diff --git a/fec/fec/static/js/pages/dom-datatableblock2.js b/fec/fec/static/js/pages/dom-datatableblock2.js
new file mode 100644
index 0000000000..bbd38a0b79
--- /dev/null
+++ b/fec/fec/static/js/pages/dom-datatableblock2.js
@@ -0,0 +1,197 @@
+'use strict';
+
+//TEMPORARY ESLINT FIXES
+/* eslint-disable no-debugger, no-console */
+// $ not defined ERROR WITHOUT THIS GOBAL...NEED TO RESEARCH ALTERNATIVES
+/* global $ */
+
+//**IT IS ALSO PREFERRABLE TO `dom-datatableblock` BECAUE IT DOES NOT NEED THE TEMPLATETAG ADDED TO THE PAGE...
+//...BECAUSE IT GET `sort_info` from `partials/tableblock_to_datatable.html` USING DJANGO {{}} TAGS...
+//...AND CREATES `th_array` FROM EACH TABLES HEADERS ON THE PAGE.
+
+//var tables = require('../modules/tables');
+//require('../modules/import-dtables'); //ANOTHER OPTOION THAT COULD BE A SEPATATE IMPORT-SCRIPT
+
+//var $ = require('jquery');
+
+//require('datatables.net');
+require('datatables.net-responsive');
+
+var convert_for_sort = function(type, value) {
+//function convert_for_sort(type, value) {
+
+ if (type == 'date') {
+ //Assumes mm/dd/yyyy format is being supplied
+ //pattern = /\d{2}\/\d{2}\/\d{4}/i; //not using this line
+ var dateString = value.replace(/(\d{2})\/(\d{2})\/(\d{4})/, `$2/$1/$3` );
+ console.log('dateString:'+ dateString);
+
+ var dateParts = dateString.split('/');
+
+ // month is 0-based, that's why we need dataParts[1] - 1
+ var date = new Date(+dateParts[2], dateParts[1] - 1, +dateParts[0]);
+
+ //var timestampInMs = date.getTime();
+
+ var unixTimestamp = Math.floor(date.getTime() / 1000);
+
+ console.log(unixTimestamp); //
+
+ return unixTimestamp;
+
+ }
+
+ else if (type == 'currency') {
+ //Assumes format: '$1,234.56'
+ //pattern = /^\$|,/i //Remove $ and commas
+ var amount_converted = value.replace(/^\$|,/,'');
+
+ //does this need to be a float or integer ot string?
+ return amount_converted;
+ }
+
+ //type == 'numeric or alphabetical')
+ else {
+
+ return value;
+
+ }
+
+ };
+
+$(function() {
+
+//How `sort_info` comes from Python templatetag, but building it below (if exists) in JS instead, using Django {{}} in partials/tableblock_to_datatable.html
+//sort_info =
+// [
+// [{'column': 'Date', 'sort_format': 'date'}, {'column': 'Number of Pages', 'sort_format': 'numeric'}]
+// ,
+// [{}],
+// [{'column': 'Document Date', 'sort_format': 'date'}, {'column': 'Amount', 'sort_format': 'currency'}]
+// ]
+
+//`sort_column_object` built below for each table using `sort_info`
+//EXAMPLE:
+// {
+// "1": "numeric",
+// "2": "date"
+// }
+
+//{}
+
+// {
+// "1": "date",
+// "2": "currency"
+// }
+
+// In jQuery , 'this' is the table, 'index' is the iterator (0, 1, 2 etc...)
+$('.block-datatable_block table').each(function(index){
+
+ console.log ('index', index);
+ console.log ('(this):', this);
+
+//Create the `sort_info`` array of from the user-sumitted Sort fields for this table (if any was submutted).
+ let sort_info;
+ //Find the sort info script tag for this table, if exists
+ let sort_script = $(this).closest('.block-datatable_block').find('.sorting')[0];
+ //If user submitted Sort fields in Wagtail...
+ if (sort_script) {
+ let sort_txt = sort_script.innerHTML;
+ sort_info = JSON.parse(sort_txt);
+ }
+ //...else just use an empty list
+ else {
+ sort_info= [{}];
+ }
+
+ console.log ('sort_info:', sort_info);
+
+ let th_array = [];
+ //Iterate the cells in first row (header row) of current table, i.e. (this).
+ for(let c=0; c < (this).rows[0].cells.length; c++) {
+
+ //Create an array the current tables header text
+ let th_text = this.rows[0].cells[c].innerText;
+ console.log ('th_text:', th_text);
+ th_array.push(th_text);
+ }
+
+/////////////////START APPLY HTML5 data-order ATTR TO CELLS USING `sort_info`////////////////
+
+//***TODO: COULD JUST ITERATE ROWS[0].CELLS ONCE, FINDING A SORT INFO ITEME, MAKING THE CONVERSION AND SET DATA-ATTR...
+//... THEN ITERATE THE NEXT ONE RATHER THAN ITERATING ROWS AGAIN BELOW ??
+let sort_columns_object = {};
+let initial_sort_column;
+let initial_sort_order;
+
+//TODO: CAN I JUST ITERATE `th_array.length` HERE NOW INSTEAD OF `(this).rows[0].cells.length`` ?
+//Iterate the cells in first row (headers) of current table (index)
+for(let i=0; i < (this).rows[0].cells.length; i++) {
+
+ //Now,iterate sort_info for current table to determine sort columns
+ for(let j=0; j < sort_info.length; j++) {
+
+ //Create a sort_columns_object to use below to decide which cells get a converted data-order attr.
+ if ((this).rows[0].cells[i].innerText == sort_info[j].column) {
+ let column_index = th_array.indexOf(sort_info[j].column);
+ let sort_format = sort_info[j]['sort_format'];
+ sort_columns_object[column_index] = sort_format;
+
+ }
+ }
+
+}
+console.log( 'sort_columns_object:', sort_columns_object);
+
+ //Determine the initial_sort column and initial sort order using sort_info object
+
+ //Get the index position of column in the first item submitted by user in Wagtail Sort fields
+ let order_index = th_array.indexOf(sort_info[0]['column']);
+ //If the index is -1(non-existent), use default; otherwise, return it
+ initial_sort_column = order_index == -1 ? 0 : order_index;
+ console.log( 'initial_sort_column:', initial_sort_column);
+ //Get the initial sort order from order value in the first item submitted by user in Wagtail Sort fields
+ initial_sort_order = sort_info[0]['order'] || 'asc';
+ console.log( 'initial_sort_order:', initial_sort_order);
+
+//TODO: COULD SET CURRENT CELL AS A VAR SO I DONT HAVE TO KEEP SAYING '(this).rows[k].cells[l]'
+
+//iterate all rows in current table
+for(let k=0; k < (this).rows.length; k++) {
+
+ //iterate all cells in current row
+ for(let l=0; l < (this).rows[k].cells.length; l++) {
+
+ //If the number of the current iterator (l) is in the `sort_columns_object`
+ if(Object.prototype.hasOwnProperty.call(sort_columns_object, l)) {
+ //if (sort_columns_object.hasOwnProperty(l)) { //(This gets ESLINT error, so using above)
+ let sort_type = sort_columns_object[l];
+ let sort_value = (this).rows[k].cells[l].innerText; //or use col_text var above?
+ let number_for_sort = convert_for_sort(sort_type, sort_value);
+ console.log('number_for_sort:', number_for_sort);
+
+ //NOTE: THIS WAS BROKEN B/C OF (this) INSTEAD OF this (parens broke it) IF USING PARENS '(this)', ALWAYS US SEMICOLON ON PREV LINE
+ //Set the data-order(sort) attr for cells that require it in sort_columns_object
+ (this).rows[k].cells[l].setAttribute('data-order',number_for_sort);
+
+ }
+
+ }
+
+}
+
+/////////////////END APPLY HTML5 data-order ATTR TO CELLS USING `sort_info`////////////////
+
+ (this).id = `dtable-block-${index}`;
+ (this).classList.add('data-table', 'data-table--heading-borders', 'scrollX', 'simple-table' , 'u-no-border');
+
+ //THIS ONE JUST APPLIES jQuery datatables to the existing html table on page and give its intial ordering
+
+ $(`#dtable-block-${index}`).DataTable({
+ order: [[initial_sort_column ,initial_sort_order]] //from js above
+ });
+
+ }); // /end $('.block-datatable_block table').each(function
+
+}); //end $(document.ready function.
+
diff --git a/fec/fec/static/js/pages/legal-to-datatable.js b/fec/fec/static/js/pages/legal-to-datatable.js
new file mode 100644
index 0000000000..cbe21b483a
--- /dev/null
+++ b/fec/fec/static/js/pages/legal-to-datatable.js
@@ -0,0 +1,196 @@
+'use strict';
+
+/* eslint-disable no-debugger, no-console */
+/* global $ */
+
+//var $ = require('jquery');
+//var tables = require('../modules/tables');;
+
+//require('datatables.net');
+require('datatables.net-responsive');
+
+//Sorting and ordering definitions for legal cononacal tables
+const legal_docs_sorting =
+{
+ 'af-disposition': {
+ '1': 'currency',
+ '2': 'date',
+ ordering: {
+ column: '2',
+ direction: 'desc'
+ }
+ },
+ 'af-documents': {
+ '0': 'date',
+ ordering: {
+ column: '0',
+ direction: 'desc'
+ }
+ },
+ 'ao-final-opinion': {
+ '0': 'date',
+ ordering: {
+ column: '0',
+ direction: 'desc'
+ }
+ },
+ 'ao-entities': {
+ ordering: {
+ column: '1',
+ direction: 'asc'
+ }
+ },
+ //CAN'T DO THESE WITHOUT POSSIBLY USING THE ROW-GROUP PLUGIN
+ // 'archived-mur-documents': {
+
+ // },
+ // 'archived-mur-participants': {
+
+ // },
+ // 'current-mur-disposition': {
+ //1 : 'currency',
+ // 'ordering': {
+ // 'column': '1',
+ // 'direction':'asc'
+ // },
+ //},
+ //'current-mur-documents': {
+ //1:'date',
+ // 'ordering': {
+ // 'column': '1',
+ // 'direction':'asc'
+ // },
+ //},
+ // 'current-mur-participants': {
+
+ // },
+ // 'adr-disposition:': {
+ // 1 : 'currency',
+ // 'ordering': {
+ // 'column': '1',
+ // 'direction':'asc'
+ // },
+ // },
+ // 'adr-documents': {
+
+ // },
+ 'adr-participants': {
+ ordering: {
+ column: '1',
+ direction: 'asc'
+ }
+
+ }
+};
+
+const covert_for_sort = function(type, value) {
+
+ if (type == 'date') {
+ //Assumes mm/dd/yyyy format is being supplied
+ //pattern = /\d{2}\/\d{2}\/\d{4}/i; ne
+ var dateString = value.replace(/(\d{2})\/(\d{2})\/(\d{4})/, `$2/$1/$3` );
+ console.log('dateString:'+ dateString);
+
+ var dateParts = dateString.split('/');
+
+ // month is 0-based, that's why we need dataParts[1] - 1
+ var date = new Date(+dateParts[2], dateParts[1] - 1, +dateParts[0]);
+
+ //var timestampInMs = date.getTime();
+
+ var unixTimestamp = Math.floor(date.getTime() / 1000);
+
+ console.log(unixTimestamp); //
+
+ return unixTimestamp;
+
+ }
+
+ else if (type == 'currency') {
+ //Assumes format: '$1,234.56'
+ //pattern = /^\$|,/i //Remove $ and commas
+ var amount_converted = value.replace(/^\$|,/,'');
+
+ //does this need to be a float or integer ot string?
+ return amount_converted;
+ }
+
+ //type == 'numeric')
+ else {
+
+ return value;
+
+ }
+
+ };
+
+$(function() {
+
+///////////////START HTML5 DATE SORT SUPPORT////////////////
+
+//THIS FUNCTION ONLY WORKS FOR APPLYING DATATABLES DIRECTLY TO TABLE)
+
+//const tables = $('table')
+
+const tables = document.getElementsByTagName('table');
+console.log ('Tables:', tables);
+
+let table_id;
+//First iterate all tables on page:
+Array.from(tables).forEach(table => {
+ //$('table').each(function(index, value){
+ table_id = table.getAttribute('id');
+
+ console.log('table_id' + table_id);
+
+ //****TO DO: SHOULD I DO AN IF ELSE RETURN SO IT DOES NOT HAVE TO ITERATE A TABLE WITHOUT SORT VALS?
+ if(Object.prototype.hasOwnProperty.call(legal_docs_sorting, table_id)) {
+ //if (legal_docs_sorting.hasOwnProperty(table_id)) { //(This gets ESLINT error, so using above)
+
+ //Inside this iteration(table), now iterate all rows
+ for(let i=0; i < table.rows.length; i++) {
+
+ for(let j=0; j < table.rows[i].cells.length; j++) {
+ //***TODO: COULD ALSO JUST LEAVE OUT TABLE IDS THAT DONT HAVE SORT IN ABOVE IF STMT, DO I NEED BOTH?
+ console.log ('legal_docs_sorting[table_id]:'+ legal_docs_sorting[table_id]);
+ if(Object.prototype.hasOwnProperty.call(legal_docs_sorting[table_id], j)) {
+ //if (legal_docs_sorting[table_id].hasOwnProperty(j)) { //(This gets ESLINT error, so using above)
+
+ let sort_type = legal_docs_sorting[table_id][j];
+ let sort_value = table.rows[i].cells[j].textContent;
+ let number_for_sort = covert_for_sort(sort_type,sort_value);
+
+ table.rows[i].cells[j].setAttribute('data-order',number_for_sort);
+ }
+
+ }
+
+ }
+
+ let order_col;
+ let order_dir;
+ if(Object.prototype.hasOwnProperty.call(legal_docs_sorting[table_id], 'ordering')) {
+ //if (legal_docs_sorting[table_id].hasOwnProperty('ordering')) { //(This gets ESLINT error, so using above)
+ order_col = legal_docs_sorting[table_id].ordering.column;//['ordering']['column']
+ order_dir = legal_docs_sorting[table_id].ordering.direction;//['ordering']['direction']
+
+ }
+ else {
+ order_col = '0';
+ order_dir = 'asc';
+ }
+
+ //$(myTable).addClass("data-table")
+ //myTable.classList.add("data-table", "data-table--heading-borders", "scrollX", "simple-table" , "u-no-border")
+
+//THIS ONE JUST APPLIES jQuery datatables to the existing table
+ $(`#${table_id}`).DataTable({
+ order: [[order_col, order_dir]]
+
+ });
+
+//document.getElementsByClassName('block-table')[0].remove()
+ } //EMD if (legal_docs_sorting.hasOwnProperty(table_id))...
+ });
+});//end $(document.ready function.
+
diff --git a/fec/fec/static/js/pages/mutli-inplace-datatable.js b/fec/fec/static/js/pages/mutli-inplace-datatable.js
new file mode 100644
index 0000000000..a138872522
--- /dev/null
+++ b/fec/fec/static/js/pages/mutli-inplace-datatable.js
@@ -0,0 +1,81 @@
+'use strict';
+
+/* eslint-disable no-debugger, no-console */
+/* global $ */
+
+//***NOTE: THIS ONE JUST CREATES A DATATABLE FROM ANY GENERIC `tableblocks` ON PAGE TARGETTED BY `$('.block-table table')`...
+/// USED FOR FOR EXISTNNG TABLES ON audit-reports PAGES THAT HAVE SPECIFIC TABLE STRUCTURE.
+
+//var $ = require('jquery');
+require('datatables.net-responsive');
+
+$(function() {
+
+/////////////////START HTML5 SORT SUPPORT////////////////
+
+//Determine which col is the 'Date' col to add the `data-sort`` attr in next function
+//TODO: This could be a named function that returns the index number of date col
+var theTable = document.querySelector('.block-table table'); //gets only the first element
+console.log ('theTable:', theTable);
+$('.block-table table').each(function(index){
+
+console.log ('index', index);
+ console.log ('(this):', this);
+ console.log ('(this):'+ this);
+
+let date_col;
+for(let i=0; i < (this).rows[0].cells.length; i++) {
+
+ if ((this).rows[0].cells[i].innerText == 'Date') {
+ date_col = i;
+ console.log ('date_col:'+ date_col);
+
+ }
+}
+
+//Set the data-order(sort) attr for the date cells with UNIX timestamp. Assumes mm/dd/yyyy format
+for(let i=0; i < (this).rows.length; i++) {
+
+ var dateText = (this).rows[i].cells[date_col].textContent;
+
+ //Assumes mm/dd/yyyy format is being supplied
+ //Pattern = /\d{2}\/\d{2}\/\d{4}/i; //not using this line
+ var dateString = dateText.replace(/(\d{2})\/(\d{2})\/(\d{4})/, `$2/$1/$3` );
+ console.log('dateString:'+ dateString);
+
+ var dateParts = dateString.split('/');
+
+ // month is 0-based, that's why we need dataParts[1] - 1
+ var date = new Date(+dateParts[2], dateParts[1] - 1, +dateParts[0]);
+
+ //var timestampInMs = date.getTime();
+
+ var unixTimestamp = Math.floor(date.getTime() / 1000);
+ console.log(unixTimestamp); //
+
+ (this).rows[i].cells[date_col].setAttribute('data-order',unixTimestamp);
+}
+
+ (this).id = `dtable-${index}`;
+ (this).classList.add('data-table', 'data-table--heading-borders', 'scrollX', 'simple-table' , 'u-no-border');
+
+/////////////////END HTML5 DATE SUPPORT////////////////
+
+ // JUST APPLY jQuery datatables to the existing table
+ //$('.block-table table').DataTable({
+ $(`#dtable-${index}`).DataTable({
+ order: [[date_col, 'asc']]
+ });
+
+ }); // /end theTable.each(function(){
+
+//This is a bit much, I think. Probably just add some CSS/SCSS to existing stylesheets
+// Create an empty "constructed" stylesheet
+const sheet = new CSSStyleSheet();
+// Apply a rule to the sheet
+sheet.replaceSync(' .dataTables_length { width: 20%; display: inline-block; float: left; padding: 1rem 1rem 2rem 0 !important;} .dataTables_filter { width: 80%; float: left; display: inline-block; padding: 1rem 0rem 4.5rem 0 !important; }');
+
+// Apply the stylesheet to a document
+document.adoptedStyleSheets = [sheet];
+
+}); //end $(document.ready function.
diff --git a/fec/home/blocks.py b/fec/home/blocks.py
index 81d6fcf119..2b3e5c5b06 100644
--- a/fec/home/blocks.py
+++ b/fec/home/blocks.py
@@ -187,6 +187,49 @@ class Meta:
icon = 'table'
+class DataTableBlock(blocks.StructBlock):
+ """A Datatable built with Wagtail TableBlock
+ """
+ datatable_options = {
+ 'startRows': 7,
+ 'startCols': 4,
+ 'colHeaders': True,
+ 'rowHeaders': False,
+ 'height': 108,
+ 'language': 'en',
+ 'renderer': 'html'
+ }
+
+ # TODO: MAKE SORT FIELDS REQUIRED, OR FIND WAY TO MAKE SURE AN EMPTY SORT FIELDS BLOCK DOES NOT BREAK AS IT DOES NOW
+ data_table = blocks.StreamBlock([
+ ('title', blocks.CharBlock(required=False, icon='title')),
+ ('table', TableBlock(table_options=datatable_options)),
+ ('sort_fields', blocks.ListBlock(blocks.StructBlock([
+ ('column', blocks.CharBlock(required=True, help_text="Put text exactly as it appears in the column head")),
+ ('sort_format', blocks.ChoiceBlock(choices=[
+ ('date', 'Date (mm/dd/yyyy)'),
+ ('numeric', 'Numeric'),
+ ('currency', 'Currency($1,2345.67, no spaces between commas)'),
+ ('alphabetical', 'Alphabetical'),
+ ], required=True, help_text='Set custom sorting for a pecific column. If no sort fields are specified, \
+ table defaults to ordering by first column/alphabetically.')),
+ ('order', blocks.ChoiceBlock(choices=[
+ ('asc', 'Ascending'),
+ ('desc', 'Descending')], required=True, help_text='Ascending is largest to smallest. \
+ Descending is smallest to largest')),
+ ]), required=True, help_text='
Sort fields is not reqired, but please remove this block if not using it
\
+
The table will be ordered initially by the first column you specify as a sort field below.
\
+
If no sort fields are specified, table defaults to order by first column/alphabetically.
'))
+ ], block_counts={
+ 'table': {'max_num': 1},
+ 'sort_fields': {'max_num': 1}}
+ )
+
+ class Meta:
+ template = 'blocks/data-table-block.html'
+ icon = 'table'
+
+
class ExampleImage(blocks.StructBlock):
"""Creates an example module with an image and a caption, side-by-side
Typically used for showing reporting Examples
@@ -249,6 +292,7 @@ class ResourceBlock(blocks.StructBlock):
('image', ImageChooserBlock()),
('example_image', ExampleImage()),
('example_paragraph', ExampleParagraph()),
+ ('datatable_block', DataTableBlock()),
])
aside = blocks.StreamBlock([
diff --git a/fec/home/templates/blocks/data-table-block.html b/fec/home/templates/blocks/data-table-block.html
new file mode 100644
index 0000000000..ceff38f6c9
--- /dev/null
+++ b/fec/home/templates/blocks/data-table-block.html
@@ -0,0 +1,49 @@
+{% load wagtailcore_tags %}
+
+{% for block in self.data_table %}
+ {% if block.block_type == "title" %}
+