diff --git a/fec/fec/static/js/legal-search-ao.js b/fec/fec/static/js/legal-search-ao.js
index 9e0500b409..59b8a1b008 100644
--- a/fec/fec/static/js/legal-search-ao.js
+++ b/fec/fec/static/js/legal-search-ao.js
@@ -279,6 +279,9 @@ LegalSearchAo.prototype.getResults = function(e) {
// Set the sort param value according to this.sortOrder
serializedFilters.sort = this.sortOrder == 'asc' ? 'ao_no' : '-ao_no';
+ // If we're getting new results, let's reset the page offset (go back to page 1)
+ serializedFilters['offset'] = 0;
+
// Then update the URL with currently params
updateQuery(serializedFilters, filterFields);
@@ -295,7 +298,23 @@ LegalSearchAo.prototype.getResults = function(e) {
delete fetchParams[param];
if (param == 'search') {
- fetchParams['q'] = fetchParams['search'];
+ // We need to divide any 'search' value into q and q_exclude, split on ` -`
+ const qStrings = [];
+ const qExcludeStrings = [];
+ if (fetchParams['search'].indexOf(' -') >= 0) {
+ const allTerms = fetchParams['search'].split(' ');
+ allTerms.forEach(term => {
+ if (term.startsWith('-'))
+ qExcludeStrings.push(term.substring(1));
+ else qStrings.push(term);
+ });
+ if (qStrings.length > 0)
+ fetchParams['q'] = qStrings.join(' ');
+ if (qExcludeStrings.length > 0)
+ fetchParams['q_exclude'] = qExcludeStrings.join(' ');
+ } else
+ fetchParams['q'] = fetchParams['search'];
+
delete fetchParams['search'];
}
}
@@ -604,7 +623,7 @@ LegalSearchAo.prototype.updatePagination = function(resultsCount) {
if (totalNumberOfPages <= maxButtonsOnScreen) {
// Yay! We can just use every button
for (let i = 0; i < totalNumberOfPages; i++) {
- pageNumbers.push(i + 1);
+ pageNumbers.push(i);
}
} else {
const buttonsBeforeCurrent = Math.floor(maxButtonsOnScreen / 2);
diff --git a/fec/fec/static/js/pages/election-reporting-dates-tables.js b/fec/fec/static/js/pages/election-reporting-dates-tables.js
deleted file mode 100644
index 5559239165..0000000000
--- a/fec/fec/static/js/pages/election-reporting-dates-tables.js
+++ /dev/null
@@ -1,564 +0,0 @@
-const states_dropdown_template = `
-
-
-
`;
-
-const header_notes_modal_partial = `
-
-
-
-
-
-
-`;
-
-function ReportingDates() {
-
- this.dates_table = document.getElementsByClassName('election-dates-table')[0];
-
- this.buildStaticElements(); // build header_notes dialog and states dropdown
-
- this.convertFootnotes(); //converts number or symbol following "~" to footnote html, in-place
-
- this.addStateClass(); // adds state abbr classes to rows
-
- this.addFootnotes(); //adds hidden footnote rows
-
- this.stripeByState(); //zebra strip by state
-
- //it only runs this logic if the page has an `.election-dates-table` on it, TODO: might not e necessary for this trmplate
- if (this.dates_table) {
- //get all acnhor links in TDs)
- this.anchors = this.dates_table.querySelectorAll('td a[href^=\'#\']');
-
- //disable default jump behavior for anchor links(#) but keep links for accessibility
- for (const anchor of this.anchors) {
- anchor.addEventListener('click', e => {
- e.preventDefault();
- });
- }
-
- const header_sups = document.querySelectorAll(
- 'tr:first-child th a[href^="#"]'
- );
-
- //add data attribute to the header sups to open AY11 dialog and disable default jump behavior for anchor links(#) but keep links for accessibility
- for (const header_sup of header_sups) {
- header_sup.setAttribute('data-a11y-dialog-show', 'header_notes_modal');
- header_sup.addEventListener('click', e => {
- e.preventDefault();
- });
- }
-
- //Define media query
- const mql = window.matchMedia('screen and (max-width: 650px)');
-
- //call listener function explicitly at run time
- this.mediaQueryResponse(mql);
-
- //attach listener function to listen in on state changes
- mql.addListener(this.mediaQueryResponse); // TODO: .addListener() has been deprecated
-
- //show footnotes on click of a link that wraps the superscripts in cells
- for (const anchor of this.anchors) {
- anchor.addEventListener('click', this.showFootnotes.bind(this));
- }
-
- //handle changes on states dropdown to filter by state
- this.states = document.getElementById('states');
- this.states.addEventListener('change', this.handleStateChange.bind(this));
-
- //Define nexUntil() function for use in ShowFootnotes()
- this.nextUntil = function(elem, selector, filter) {
- // Setup siblings array
- const siblings = [];
-
- // Get the next sibling element
- elem = elem.nextElementSibling;
-
- // As long as a sibling exists
- while (elem) {
- // If we've reached our match, bail
- if (elem.matches(selector)) break;
-
- // If filtering by a selector, check if the sibling matches
- if (filter && !elem.matches(filter)) {
- elem = elem.nextElementSibling;
- continue;
- }
-
- // Otherwise, push it to the siblings array
- siblings.push(elem);
-
- // Get the next sibling element
- elem = elem.nextElementSibling;
- }
-
- return siblings;
- };
- }
-
-}
-
-//create and insert states-dropdown, static header-notes-list, and dialog
-ReportingDates.prototype.buildStaticElements = function() {
- //Add states dropdown template to page
- const dropdown_wrapper = document.createElement('div');
- dropdown_wrapper.innerHTML = states_dropdown_template;
-
- const table_parent = this.dates_table.parentNode;
-
- table_parent.insertBefore(dropdown_wrapper, this.dates_table);
-
- //Create header note list for modal dialogue
- let hdr_str = '';
-
- //Get the '#header_notes' script tag created in the template with json_script
- const header_notes = document.getElementById('header_notes');
- if (header_notes) {
- const header_notes_json = JSON.parse(document.getElementById('header_notes').textContent);
-
- if (typeof header_notes_json == 'object') {
- hdr_str = `
Header notes
`;
- for (const note of header_notes_json.footnote) {
- hdr_str += `
`;
- }
-
- if (typeof header_notes_json == 'object') {
- //Create A11Y modal dialog for header_notes popup and add innerHTML
- const dialog = document.createElement('div');
- //Must add these three classes separately for IE :-(
- dialog.classList.add('js-modal');
- dialog.classList.add('modal');
- dialog.classList.add('modal__content');
- dialog.setAttribute('aria-hidden', 'true');
- dialog.id = 'header_notes_modal';
- document.body.appendChild(dialog);
- dialog.innerHTML = header_notes_modal_partial;
- //Populate dialog with all header notes
- const dialog_p = document.querySelector('.modal p');
- dialog_p.innerHTML = `${hdr_str}`;
-
- }
- }
-};
-
-// Adds state classes to rows
-ReportingDates.prototype.addStateClass = function() {
- const all_tr = document.querySelectorAll('tr');
- const states_select = document.getElementById('states');
- Array.from(all_tr).forEach(row => {
-
- let state_election_name_str = row.cells[0].textContent;
- // Remove extra spaces, set to lowercase to normalize human input errors
- let state_election_name = state_election_name_str.replace(/\s+/g,' ').trim().toLowerCase();
-
- // Match state name in the full election name with states select option to get the state abbreviation
- Array.from(states_select.options).forEach(opt => {
- // Use '^' to match the full state-name at beginning of string, to lowercase
- let regex = `^${opt.textContent.toLowerCase()}.*$`;
- // Match state-name in full state_election_string
- if (state_election_name.match(regex)) {
-
- row.classList.add(opt.value.toLowerCase());
- }
- });
-
- });
-};
-
-// Show chosen state rows, hide others
-ReportingDates.prototype.handleStateChange = function() {
- const state = this.states.value.toLowerCase();
-
- //TODO: Should this be `this.dates_table.querySelectorAll` ?
- const tr = document.querySelectorAll('tr');
- const ftnt = document.querySelectorAll('tr.footnote_row');
-
- if (state != 'states') {
- const ones = document.querySelectorAll(
- `table tr.${state}:not(.footnote_row), tr:first-child`
- );
- const not_ones = document.querySelectorAll(`table tr:not(.${state})`);
-
- for (const not_one of not_ones) {
- not_one.style.display = 'none';
- //use class to handle Safari's lack of support for visibiity:visible/collapse
- not_one.classList.remove('row_display');
- }
-
- for (const one of ones) {
- one.style.display = 'table-row';
- //use class to handle Safari's lack of support for visibiity:visible/collapse
- one.classList.add('row_display');
- }
- } else {
- for (const t of tr) {
- t.style.display = 'table-row';
- t.classList.add('row_display');
- }
-
- for (const f of ftnt) {
- f.style.display = 'none';
- f.classList.remove('row_display');
- }
- }
-};
-
-// Add footnote rows based on existence of superscript number created by convertFootnotes()
-ReportingDates.prototype.addFootnotes = function() {
- //Get the '#footnotes' script tag created in the template with json_script
- const footnotes = document.getElementById('footnotes');
- if (footnotes) {
- const footnotes_json = JSON.parse(document.getElementById('footnotes').textContent);
- const footnotes_array = footnotes_json.footnote;
-
- const date_sups = document.querySelectorAll('td sup');
-
- if (typeof footnotes_array == 'object') {
- Array.from(date_sups)
- .reverse()
- .forEach(node => {
- const indx = node.innerText; //should this be textContent?
- //Only put period after numeric footnotes
- const dot = /^\d+$/.test(indx) ? '.' : '';
- const state_class = node.closest('tr').className;
- const ftnt_colspan = node.closest('tr').cells.length - 1;
- let current_text;
-
- for (let note of footnotes_array){
- if(note.value.footnote_number == indx){
-
- current_text = note.value.footnote_text;
-
- }
- }
-
- const ftnt_row = `
-
-
- ${indx}${dot} ${current_text}
-
`;
- node.closest('tr').insertAdjacentHTML('afterend', ftnt_row);
- });
- }
- //hide footnotes rows initially
- const footnote_rows = document.querySelectorAll('.footnote_row');
- for (const footnote_row of footnote_rows) {
- footnote_row.style.display = 'none';
- }
- }
-};
-//Prepend header to cells in mobile ONLY, also add/remove them on resize between mobile/desktop
-ReportingDates.prototype.mediaQueryResponse = function(mql) {
- //get all non-footnote row cells for mobile
- const all_tds = document.querySelectorAll('tr:not(.footnote_row) td');
- //get all table header cells
- const all_th = document.querySelectorAll('th');
-
- //If mobile
- if (mql.matches) {
- //Iterate over non-foonote-row cells
- Array.from(all_tds).forEach(cell => {
- //get the header html for each cell index
- const th_append = Array.from(all_th)[cell.cellIndex].outerHTML;
-
- //prepend it to the cell in a span
- cell.insertAdjacentHTML(
- 'afterbegin',
- `${th_append}`
- );
-
- //get all the appended header anchor links
- const appended_anchors = cell.querySelectorAll('.th_append a[href^="#"]');
- //get the modal and modal-close
- const jsmodal = document.getElementsByClassName('js-modal');
- const jsmodalClose = document.querySelectorAll('.modal__close');
-
- //Re-stablish A11Y data-attributes to the appended header's anchor if it was removed and re-added
- for (const anchor of appended_anchors) {
- anchor.setAttribute('data-a11y-dialog-show', 'header_notes_modal');
- anchor.addEventListener('click', e => {
- jsmodal[0].setAttribute('aria-hidden', 'false');
- e.preventDefault();
- });
- }
-
- for (const close of jsmodalClose) {
- close.addEventListener('click', () => {
- jsmodal[0].setAttribute('aria-hidden', 'true');
- close.focus(); // TODO: jQuery deprecation
- });
- }
- });
- }
- //If not mobile
- else {
- //Iterate over non-foonote-row cells
- Array.from(all_tds).forEach(cell => {
- //get prepended header --if it exists-- and remove it
- cell.getElementsByTagName('span');
- const th_appended = cell.getElementsByTagName('span'); //cell.getElementsByClassname('th_append');
- if (th_appended.length) {
- th_appended[0].remove();
- }
- });
- }
-};
-
-//Show chosen footnote or append it in Mobile view
-ReportingDates.prototype.showFootnotes = function(e) {
- //escape symbols and invalid CSS selectors in indx (ie: '*', '**', etc.)
- const indx = CSS.escape(e.target.textContent);
- //get the string representaton of indx to use elsewhere
- const clean_indx = e.target.textContent;
-
- const el = e.target;
- const notftnt = 'tr:not(.footnote_row)';
- const ftnt = `.footnote_${indx}`;
- const ntlvftnt = `tr:not(.footnote_${indx})`;
- const current_row = el.closest('tr');
- const el_td = el.closest('td');
-
- //get background color of parent row so footnotes can match parent without zebra striping
- const style = window.getComputedStyle(current_row, '');
- const bgColor = style.getPropertyValue('background-color');
- //create new no-transparency color based on background color of current row to change(hide) the btm border on selected cell
- const newColor =
- bgColor == 'rgba(0, 0, 0, 0)'
- ? 'rgba(255,255,255,1)'
- : 'rgba(248,248,248,1)';
-
- //get footnote row corresponding to the one clicked
- const live_note = this.nextUntil(current_row, notftnt, ftnt);
- //get all other footnote rows
- const not_live_note = this.nextUntil(current_row, notftnt, ntlvftnt);
-
- //Change border under clicked footnote in Desktop view
-
- const tds = current_row.cells;
-
- //First set borderBottom on all TDs on parent row, then below we change
- //border on only the one clicked
- //have to use 'Array.from' here for Safari, not sure why only in this for/of stmt and not others
- for (const td of Array.from(tds)) {
- td.style.borderBottom = '1px solid #ddd';
- }
-
- //Show/hide footnote and change border color under cell
- if (live_note[0].style.display == 'none') {
- //remove bottom border on first TD (state cell)
- current_row.cells[0].style.borderBottom = `1px solid ${newColor}`;
- //show chosen footnote
- live_note[0].style.display = 'table-row';
- //set bg color on footnote rows to match parent rows
- live_note[0].style.backgroundColor = bgColor;
- //hide bottom border under cell in which the footnote sup was clicked
- // or left border for first-column cells
- if (el_td.cellIndex == '0') {
- current_row.parentNode.rows[
- current_row.rowIndex + 1
- ].cells[0].setAttribute(
- 'style',
- `border-right:1px solid ${newColor} !important`
- );
- } else {
- el_td.style.borderBottom = `1px solid ${newColor}`;
- }
-
- //hide all but the footnote clicked
- for (const not of not_live_note) {
- not.style.display = 'none';
- }
- } else {
- live_note[0].style.display = 'none';
- el_td.style.borderBottom = '1px solid #ddd';
- }
-
- //Dynamically add footnotes in Mobile view (under each cell, per click)
-
- //Add footnote innerHTML in span under clicked cell(display:block) in mobile
-
- //get innerHTML from chosen footnote using the results of the nextUntil function (i.e live_note const)
- const live_note_text = live_note[0].cells[1].innerHTML;
-
- //define any existing responsive_footnote for just the current row(i.e. current_row const)
- const resp_exists = current_row.querySelector(`.append${indx}`);
-
- //For toggling on/off the same one using superscript
- if (!resp_exists) {
- //if it does not already exist, add it with a class of the clicked index
- el_td.insertAdjacentHTML(
- 'beforeend',
- `${live_note_text}`
- );
- }
- //else remove it
- else {
- resp_exists.remove();
- }
-
- //get all '.ftnt_append' classes per row
- const resp_ftnts = current_row.getElementsByClassName('ftnt_append');
-
- //For going from one footnote to another within each row
- //Iterate over them and remove the ones that are not the clicked index number
- Array.from(resp_ftnts).forEach(nt => {
- if (nt.classList.contains(`append${clean_indx}`)) {
- nt.style.display = 'block';
- } else {
- nt.remove();
- }
- });
-
- //Extra close('x') button for mobile footnotes
- if (resp_ftnts[0]) {
- const close_btn = document.createElement('button');
- close_btn.classList.add('ftnt-close');
- close_btn.classList.add('button--close--primary');
- close_btn.textContent = 'x';
- resp_ftnts[0].appendChild(close_btn);
- //get ftnt-close for just this row and activate it
- const closer = current_row.querySelectorAll('.ftnt-close');
- closer[0].addEventListener('click', ex => {
- const x = ex.target;
- const this_span = x.closest('span');
- this_span.remove();
- });
- }
-};
-
-ReportingDates.prototype.stripeByState = function() {
- const bg = 'rgba(241,241,241,.5)';
- const state_rows = this.dates_table.getElementsByTagName('tr');
- let state_class = [];
- for (const tr of state_rows) {
- state_class.push(tr.classList.item(0));
- }
- let unique = [...new Set(state_class)];
- for (let x = 0; x < unique.length; x += 2) {
- const unique_row = this.dates_table.getElementsByClassName(unique[x]);
- for (const un of unique_row) {
- un.style.backgroundColor = bg;
- }
- }
-};
-
-ReportingDates.prototype.convertFootnotes = function() {
-
-const all_hdr = this.dates_table.getElementsByTagName('th');
- Array.from(all_hdr).forEach(cell => {
- const txt = cell.textContent;
-
- if (/~/.test(txt)) {
- let txt_array = txt.split('~');
- let hdr_txt = txt_array.shift();
- let appended_hdr_notes = txt_array;
-
- let hdr_note_html_array = [];
- for (let note of appended_hdr_notes) {
- let hdr_note_html = `${note}`;
- hdr_note_html_array.push(hdr_note_html);
-
- }
-
- cell.innerHTML = `${hdr_txt}${hdr_note_html_array}`;
-
- }
-
- });
-
- //get all non-footnote/non-header row cells
- const all_td = this.dates_table.querySelectorAll('tr:not(.footnote_row) td');
-
- Array.from(all_td).forEach(cell => {
-
- const txt = cell.innerHTML;
-
- if (/~/.test(txt)) {
- //Create an array from the string split on the tilda(s)
- let txt_array = txt.split('~');
- ///The first item is the date text, return that as a var. Now txt_array only includes footnotes.
- let date_txt = txt_array.shift();
- //Creeate a new varialble for clarity
- let appended_footnotes = txt_array;
-
- let footnote_html_array = [];
- for (let note of appended_footnotes) {
- let footnote_html = `${note}`;
-
- footnote_html_array.push(footnote_html);
- }
-
- cell.innerHTML = `${date_txt}${footnote_html_array}`;
-
- }
-
- });
-
-};
-
-new ReportingDates();
diff --git a/fec/fec/static/js/pages/reporting-dates-tables.js b/fec/fec/static/js/pages/reporting-dates-tables.js
index 8c9f2a48e5..848d2a3661 100644
--- a/fec/fec/static/js/pages/reporting-dates-tables.js
+++ b/fec/fec/static/js/pages/reporting-dates-tables.js
@@ -11,7 +11,7 @@ const states_dropdown_template = `
-
+
@@ -72,39 +72,43 @@ const header_notes_modal_partial = `
{
- e.preventDefault();
- });
- }
+ this.addFootnotes(); // adds hidden footnote rows
+
+ this.stripeByState(); // zebra strip by state
- this.buildStaticElements();
+ // only runs this logic if the page has an `.election-dates-table` on it.
+ if (this.dates_table) {
+ // get all acnhor links in TDs)
+ this.anchors = this.dates_table.querySelectorAll('td a[href^=\'#\']');
+
+ // disable default jump behavior for anchor links(#) but keep links for accessibility
+ for (const anchor of this.anchors) {
+ anchor.addEventListener('click', e => {
+ e.preventDefault();
+ });
+ }
- this.addFootnotes();
+ const header_sups = document.querySelectorAll(
+ 'tr:first-child th a[href^="#"]'
+ );
- this.stripeByState();
+ // add data attribute to the header sups to open AY11 dialog and disable default jump behavior for anchor links(#) but keep links for accessibility
+ for (const header_sup of header_sups) {
+ header_sup.setAttribute('data-a11y-dialog-show', 'header_notes_modal');
+ header_sup.addEventListener('click', e => {
+ e.preventDefault();
+ });
+ }
- //Define media query
+ // define media query
const mql = window.matchMedia('screen and (max-width: 650px)');
// call listener function explicitly at run time
@@ -113,38 +117,38 @@ function ReportingDates() {
// attach listener function to listen in on state changes
mql.addListener(this.mediaQueryResponse); // TODO: .addListener() has been deprecated
- //show footnotes on click of a link that wraps the superscripts in cells
+ // show footnotes on click of a link that wraps the superscripts in cells
for (const anchor of this.anchors) {
anchor.addEventListener('click', this.showFootnotes.bind(this));
}
- //handle changes on states dropdown
+ // handle changes on states dropdown to filter by state
this.states = document.getElementById('states');
this.states.addEventListener('change', this.handleStateChange.bind(this));
- //Define nexUntil() function for use in ShowFootnotes()
+ // define nexUntil() function for use in ShowFootnotes()
this.nextUntil = function(elem, selector, filter) {
- // Setup siblings array
+ // setup siblings array
const siblings = [];
- // Get the next sibling element
+ // get the next sibling element
elem = elem.nextElementSibling;
- // As long as a sibling exists
+ // as long as a sibling exists
while (elem) {
- // If we've reached our match, bail
+ // if we've reached our match, bail
if (elem.matches(selector)) break;
- // If filtering by a selector, check if the sibling matches
+ // if filtering by a selector, check if the sibling matches
if (filter && !elem.matches(filter)) {
elem = elem.nextElementSibling;
continue;
}
- // Otherwise, push it to the siblings array
+ // otherwise, push it to the siblings array
siblings.push(elem);
- // Get the next sibling element
+ // get the next sibling element
elem = elem.nextElementSibling;
}
@@ -154,62 +158,42 @@ function ReportingDates() {
}
-//create and insert states-dropdown, static footnote/header-notes-list , and dialog
+// create and insert states-dropdown, static header-notes-list, and dialog
ReportingDates.prototype.buildStaticElements = function() {
- //Add states dropdown template to page
+ // add states dropdown template to page
const dropdown_wrapper = document.createElement('div');
dropdown_wrapper.innerHTML = states_dropdown_template;
+ if (this.dates_table.dataset.hideStateDropdown == 'true') {
+ dropdown_wrapper.style.display='none';
+ }
const table_parent = this.dates_table.parentNode;
table_parent.insertBefore(dropdown_wrapper, this.dates_table);
- //Create static footnote/header note list
-
+ // create header note list for modal dialogue
let hdr_str = '';
- //build static list from header notes object if it exists
- if (typeof header_notes == 'object') {
+
+ // get the '#header_notes' script tag created in the template with json_script
+ const header_notes = document.getElementById('header_notes');
+ if (header_notes) {
+ const header_notes_json = JSON.parse(document.getElementById('header_notes').textContent);
+
+ if (typeof header_notes_json == 'object') {
hdr_str = `
Header notes
`;
- for (const key in header_notes) {
+ for (const note of header_notes_json.footnote) {
hdr_str += `
`;
- }
-
- //create div for all notes if either foot or header notes exist
- if (hdr_str || ftnt_str) {
- const static_notes = document.createElement('div');
- static_notes.id = 'static_notes';
-
- //add combibed header_notes, footnotes list to collapsible div
- static_notes.innerHTML = `${hdr_str}${ftnt_str}`;
-
- //insert it after table
- table_parent.insertBefore(static_notes, this.dates_table.nextSibling);
- }
-
- if (typeof header_notes == 'object') {
- //Create A11Y modal dialog for header_notes popup and add innerHTML
+ if (typeof header_notes_json == 'object') {
+ // create A11Y modal dialog for header_notes popup and add innerHTML
const dialog = document.createElement('div');
- //Must add these three classes separately for IE :-(
+ // must add these three classes separately for IE :-(
dialog.classList.add('js-modal');
dialog.classList.add('modal');
dialog.classList.add('modal__content');
@@ -217,25 +201,54 @@ ReportingDates.prototype.buildStaticElements = function() {
dialog.id = 'header_notes_modal';
document.body.appendChild(dialog);
dialog.innerHTML = header_notes_modal_partial;
- //Populate dialog with all header notes
+ // populate dialog with all header notes
const dialog_p = document.querySelector('.modal p');
dialog_p.innerHTML = `${hdr_str}`;
- const header_sups = document.querySelectorAll(
- 'tr:first-child th a[href^="#"]'
- );
+ }
+ }
+};
+
+// adds state classes to rows
+ReportingDates.prototype.addStateClass = function() {
+ const all_tr = document.querySelectorAll('tr');
+ const states_select = document.getElementById('states');
+ Array.from(all_tr).forEach(row => {
+ /* For more granular control over zebra-striping, Users can add an additional class to a row in the Wagtail table block
+ by wrapping the election title text in the first column in a custom html tag with a clas attribute.
+ Example(includiing a footnote tilda): Florida 1st Congressional District Special Primary ~*
+ This will add the class from the election tag to that row in addition to the state appreviation class added by default.
+ So the resultiing class on this row would be "fl1 fl".
+ */
+ // before adding state classes, add any user-created classes to rows
+ let election_cell = row.cells[0];
+ // find any rows that have custom tags added by user
+ let election_tag = election_cell.getElementsByTagName('election')[0];
+ // add the user-created class to the row
+ if (election_tag) {
+ row.classList.add(election_tag.classList[0]);
+ }
+
+ // add the state class to each row:
+ let state_election_name_str = row.cells[0].textContent;
+ // remove extra spaces, set to lowercase to normalize human input errors
+ let state_election_name = state_election_name_str.replace(/\s+/g,' ').trim().toLowerCase();
+
+ // match state name in the full election name with states select option to get the state abbreviation
+ Array.from(states_select.options).forEach(opt => {
+ // use '^' to match the full state-name at beginning of string, to lowercase
+ let regex = `^${opt.textContent.toLowerCase()}.*$`;
+ // match state-name in full state_election_string
+ if (state_election_name.match(regex)) {
+
+ row.classList.add(opt.value.toLowerCase());
+ }
+ });
- //add data attribute to the header sups to open AY11 dialog
- for (const header_sup of header_sups) {
- header_sup.setAttribute('data-a11y-dialog-show', 'header_notes_modal');
- header_sup.addEventListener('click', e => {
- e.preventDefault();
});
- }
- }
};
-//Show chosen state rows, hide others
+// show chosen state rows, hide others
ReportingDates.prototype.handleStateChange = function() {
const state = this.states.value.toLowerCase();
@@ -250,13 +263,13 @@ ReportingDates.prototype.handleStateChange = function() {
for (const not_one of not_ones) {
not_one.style.display = 'none';
- //use class to handle Safari's lack of support for visibiity:visible/collapse
+ // use class to handle Safari's lack of support for visibiity:visible/collapse
not_one.classList.remove('row_display');
}
for (const one of ones) {
one.style.display = 'table-row';
- //use class to handle Safari's lack of support for visibiity:visible/collapse
+ // use class to handle Safari's lack of support for visibiity:visible/collapse
one.classList.add('row_display');
}
} else {
@@ -272,59 +285,77 @@ ReportingDates.prototype.handleStateChange = function() {
}
};
-// Add footnote rows based on existence of superscript number on page load
+// add footnote rows based on existence of superscript number created by convertFootnotes()
ReportingDates.prototype.addFootnotes = function() {
+ // get the '#footnotes' script tag created in the template with json_script
+ const footnotes = document.getElementById('footnotes');
+ if (footnotes) {
+ const footnotes_json = JSON.parse(document.getElementById('footnotes').textContent);
+ const footnotes_array = footnotes_json.footnote;
+
const date_sups = document.querySelectorAll('td sup');
+ if (typeof footnotes_array == 'object') {
Array.from(date_sups)
.reverse()
.forEach(node => {
- const indx = node.innerText; //should this be textContent?
+ const indx = node.innerText; // should this be textContent?
+ // only put period after numeric footnotes
+ const dot = /^\d+$/.test(indx) ? '.' : '';
const state_class = node.closest('tr').className;
const ftnt_colspan = node.closest('tr').cells.length - 1;
+ let current_text;
+
+ for (let note of footnotes_array) {
+ if(note.value.footnote_number == indx) {
+
+ current_text = note.value.footnote_text;
+
+ }
+ }
const ftnt_row = `
- ${indx}. ${footnotes[indx]}
+ ${indx}${dot} ${current_text}
`;
node.closest('tr').insertAdjacentHTML('afterend', ftnt_row);
});
-
- //hide footnotes rows initially
+ }
+ // hide footnotes rows initially
const footnote_rows = document.querySelectorAll('.footnote_row');
for (const footnote_row of footnote_rows) {
footnote_row.style.display = 'none';
}
+ }
};
-
-//Prepend header to cells in mobile ONLY, also add/remove them on resize between mobile/desktop
+// prepend header to cells in mobile ONLY, also add/remove them on resize between mobile/desktop
ReportingDates.prototype.mediaQueryResponse = function(mql) {
- //get all non-footnote row cells for mobile
+ // get all non-footnote row cells for mobile
const all_tds = document.querySelectorAll('tr:not(.footnote_row) td');
- //get all table header cells
+ // get all table header cells
const all_th = document.querySelectorAll('th');
- //If mobile
+ // if mobile
if (mql.matches) {
- //Iterate over non-foonote-row cells
+ // iterate over non-foonote-row cells
Array.from(all_tds).forEach(cell => {
- //get the header html for each cell index
+ // get the header html for each cell index
const th_append = Array.from(all_th)[cell.cellIndex].outerHTML;
- //prepend it to the cell in a span
+ // prepend it to the cell in a span
cell.insertAdjacentHTML(
'afterbegin',
`${th_append}`
);
- //get all the appended header anchor links
+ // get all the appended header anchor links
const appended_anchors = cell.querySelectorAll('.th_append a[href^="#"]');
- //get the modal and modal-close
+ // get the modal and modal-close
const jsmodal = document.getElementsByClassName('js-modal');
const jsmodalClose = document.querySelectorAll('.modal__close');
- //Re-stablish A11Y data-attributes to the appended header's anchor if it was removed and re-added
+ // re-stablish A11Y data-attributes to the appended header's anchor if it was removed and re-added
for (const anchor of appended_anchors) {
anchor.setAttribute('data-a11y-dialog-show', 'header_notes_modal');
anchor.addEventListener('click', e => {
@@ -341,13 +372,13 @@ ReportingDates.prototype.mediaQueryResponse = function(mql) {
}
});
}
- //If not mobile
+ // if not mobile
else {
- //Iterate over non-foonote-row cells
+ // iterate over non-foonote-row cells
Array.from(all_tds).forEach(cell => {
- //get prepended header --if it exists-- and remove it
+ // get prepended header --if it exists-- and remove it
cell.getElementsByTagName('span');
- const th_appended = cell.getElementsByTagName('span'); //cell.getElementsByClassname('th_append');
+ const th_appended = cell.getElementsByTagName('span');
if (th_appended.length) {
th_appended[0].remove();
}
@@ -355,11 +386,11 @@ ReportingDates.prototype.mediaQueryResponse = function(mql) {
}
};
-//Show chosen footnote or append it in Mobile view
+// show chosen footnote or append it in Mobile view
ReportingDates.prototype.showFootnotes = function(e) {
- //escape symbols and invalid CSS selectors in indx (ie: '*', '**', etc.)
+ // escape symbols and invalid CSS selectors in indx (ie: '*', '**', etc.)
const indx = CSS.escape(e.target.textContent);
- //get the string representaton of indx to use elsewhere
+ // get the string representaton of indx to use elsewhere
const clean_indx = e.target.textContent;
const el = e.target;
@@ -369,40 +400,39 @@ ReportingDates.prototype.showFootnotes = function(e) {
const current_row = el.closest('tr');
const el_td = el.closest('td');
- //get background color of parent row so footnotes can match parent without zebra striping
+ // get background color of parent row so footnotes can match parent without zebra striping
const style = window.getComputedStyle(current_row, '');
const bgColor = style.getPropertyValue('background-color');
- //create new no-transparency color based on background color of current row to change(hide) the btm border on selected cell
+ // create new no-transparency color based on background color of current row to change(hide) the btm border on selected cell
const newColor =
bgColor == 'rgba(0, 0, 0, 0)'
? 'rgba(255,255,255,1)'
: 'rgba(248,248,248,1)';
- //get footnote row corresponding to the one clicked
+ // get footnote row corresponding to the one clicked
const live_note = this.nextUntil(current_row, notftnt, ftnt);
- //get all other footnote rows
+ // get all other footnote rows
const not_live_note = this.nextUntil(current_row, notftnt, ntlvftnt);
- //Change border under clicked footnote in Desktop view
+ // change border under clicked footnote in Desktop view
const tds = current_row.cells;
- //First set borderBottom on all TDs on parent row, then below we change
- //border on only the one clicked
- //have to use 'Array.from' here for Safari, not sure why only in this for/of stmt and not others
+ // first set borderBottom on all TDs on parent row, then below we change border on only the one clicked
+ // have to use 'Array.from' here for Safari, not sure why only in this for/of stmt and not others
for (const td of Array.from(tds)) {
td.style.borderBottom = '1px solid #ddd';
}
- //Show/hide footnote and change border color under cell
+ // show/hide footnote and change border color under cell
if (live_note[0].style.display == 'none') {
- //remove bottom border on first TD (state cell)
+ // remove bottom border on first TD (state cell)
current_row.cells[0].style.borderBottom = `1px solid ${newColor}`;
- //show chosen footnote
+ // show chosen footnote
live_note[0].style.display = 'table-row';
- //set bg color on footnote rows to match parent rows
+ // set bg color on footnote rows to match parent rows
live_note[0].style.backgroundColor = bgColor;
- //hide bottom border under cell in which the footnote sup was clicked
+ // hide bottom border under cell in which the footnote sup was clicked
// or left border for first-column cells
if (el_td.cellIndex == '0') {
current_row.parentNode.rows[
@@ -415,7 +445,7 @@ ReportingDates.prototype.showFootnotes = function(e) {
el_td.style.borderBottom = `1px solid ${newColor}`;
}
- //hide all but the footnote clicked
+ // hide all but the footnote clicked
for (const not of not_live_note) {
not.style.display = 'none';
}
@@ -424,34 +454,34 @@ ReportingDates.prototype.showFootnotes = function(e) {
el_td.style.borderBottom = '1px solid #ddd';
}
- //Dynamically add footnotes in Mobile view (under each cell, per click)
+ // dynamically add footnotes in Mobile view (under each cell, per click)
- //Add footnote innerHTML in span under clicked cell(display:block) in mobile
+ // add footnote innerHTML in span under clicked cell(display:block) in mobile
- //get innerHTML from chosen footnote using the results of the nextUntil function (i.e live_note const)
+ // get innerHTML from chosen footnote using the results of the nextUntil function (i.e live_note const)
const live_note_text = live_note[0].cells[1].innerHTML;
- //define any existing responsive_footnote for just the current row(i.e. current_row const)
+ // define any existing responsive_footnote for just the current row(i.e. current_row const)
const resp_exists = current_row.querySelector(`.append${indx}`);
- //For toggling on/off the same one using superscript
+ // for toggling on/off the same one using superscript
if (!resp_exists) {
- //if it does not already exist, add it with a class of the clicked index
+ // if it does not already exist, add it with a class of the clicked index
el_td.insertAdjacentHTML(
'beforeend',
`${live_note_text}`
);
}
- //else remove it
+ // else remove it
else {
resp_exists.remove();
}
- //get all '.ftnt_append' classes per row
+ // get all '.ftnt_append' classes per row
const resp_ftnts = current_row.getElementsByClassName('ftnt_append');
- //For going from one footnote to another within each row
- //Iterate over them and remove the ones that are not the clicked index number
+ // for going from one footnote to another within each row
+ // iterate over them and remove the ones that are not the clicked index number
Array.from(resp_ftnts).forEach(nt => {
if (nt.classList.contains(`append${clean_indx}`)) {
nt.style.display = 'block';
@@ -460,14 +490,14 @@ ReportingDates.prototype.showFootnotes = function(e) {
}
});
- //Extra close('x') button for mobile footnotes
+ // extra close('x') button for mobile footnotes
if (resp_ftnts[0]) {
const close_btn = document.createElement('button');
close_btn.classList.add('ftnt-close');
close_btn.classList.add('button--close--primary');
close_btn.textContent = 'x';
resp_ftnts[0].appendChild(close_btn);
- //get ftnt-close for just this row and activate it
+ // get ftnt-close for just this row and activate it
const closer = current_row.querySelectorAll('.ftnt-close');
closer[0].addEventListener('click', ex => {
const x = ex.target;
@@ -493,4 +523,58 @@ ReportingDates.prototype.stripeByState = function() {
}
};
+ReportingDates.prototype.convertFootnotes = function() {
+
+const all_hdr = this.dates_table.getElementsByTagName('th');
+ Array.from(all_hdr).forEach(cell => {
+ const txt = cell.textContent;
+
+ if (/~/.test(txt)) {
+ let txt_array = txt.split('~');
+ let hdr_txt = txt_array.shift();
+ let appended_hdr_notes = txt_array;
+
+ let hdr_note_html_array = [];
+ for (let note of appended_hdr_notes) {
+ let hdr_note_html = `${note}`;
+ hdr_note_html_array.push(hdr_note_html);
+
+ }
+
+ cell.innerHTML = `${hdr_txt}${hdr_note_html_array}`;
+
+ }
+
+ });
+
+ // get all non-footnote/non-header row cells
+ const all_td = this.dates_table.querySelectorAll('tr:not(.footnote_row) td');
+
+ Array.from(all_td).forEach(cell => {
+
+ const txt = cell.innerHTML;
+
+ if (/~/.test(txt)) {
+ // create an array from the string split on the tilda(s)
+ let txt_array = txt.split('~');
+ // the first item is the date text, return that as a var. Now txt_array only includes footnotes.
+ let date_txt = txt_array.shift();
+ // creeate a new varialble for clarity
+ let appended_footnotes = txt_array;
+
+ let footnote_html_array = [];
+ for (let note of appended_footnotes) {
+ let footnote_html = `${note}`;
+
+ footnote_html_array.push(footnote_html);
+ }
+
+ cell.innerHTML = `${date_txt}${footnote_html_array}`;
+
+ }
+
+ });
+
+};
+
new ReportingDates();
diff --git a/fec/home/blocks.py b/fec/home/blocks.py
index 747ff9e8ad..b96e8477b9 100644
--- a/fec/home/blocks.py
+++ b/fec/home/blocks.py
@@ -213,7 +213,8 @@ class ReportingTableBlock(blocks.StructBlock):
'renderer': 'html',
'renderAllRows': True,
}
-
+
+ hide_state_dropdown = blocks.BooleanBlock(required=False, help_text='Hide state dropdown on this table?')
table = TableBlock(table_options=reporting_table_options)
class Meta:
diff --git a/fec/home/migrations/0138_alter_reportingdatestable_reporting_dates_table.py b/fec/home/migrations/0138_alter_reportingdatestable_reporting_dates_table.py
new file mode 100644
index 0000000000..b94275bd53
--- /dev/null
+++ b/fec/home/migrations/0138_alter_reportingdatestable_reporting_dates_table.py
@@ -0,0 +1,21 @@
+# Generated by Django 4.2.17 on 2025-01-10 19:59
+
+from django.db import migrations
+import wagtail.blocks
+import wagtail.contrib.table_block.blocks
+import wagtail.fields
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('home', '0137_commissionerpage_picture_download'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='reportingdatestable',
+ name='reporting_dates_table',
+ field=wagtail.fields.StreamField([('paragraph', wagtail.blocks.RichTextBlock(blank=True)), ('html', wagtail.blocks.RawHTMLBlock()), ('internal_button', wagtail.blocks.StructBlock([('internal_page', wagtail.blocks.PageChooserBlock()), ('text', wagtail.blocks.CharBlock())])), ('external_button', wagtail.blocks.StructBlock([('url', wagtail.blocks.URLBlock()), ('text', wagtail.blocks.CharBlock())])), ('dates_table', wagtail.blocks.StructBlock([('hide_state_dropdown', wagtail.blocks.BooleanBlock(help_text='Hide state dropdown on this table?', required=False)), ('table', wagtail.contrib.table_block.blocks.TableBlock(table_options={'colHeaders': False, 'height': 108, 'language': 'en', 'renderAllRows': True, 'renderer': 'html', 'rowHeaders': False, 'startCols': 6, 'startRows': 1}))], blank=True, form_classname='title', required=False))], blank=True, null=True, use_json_field=True),
+ ),
+ ]
diff --git a/fec/home/models.py b/fec/home/models.py
index 8a3946a7e9..2924669ffc 100644
--- a/fec/home/models.py
+++ b/fec/home/models.py
@@ -1429,7 +1429,10 @@ class ReportingDatesTable(Page):
use_json_field=True)
content_panels = Page.content_panels + [
- FieldPanel('reporting_dates_table'),
+ FieldPanel('reporting_dates_table', help_text='Zebra-striping tip: To add additional row classes for more granular control over zebra stripes, wrap \
+ the election name text in first column/first cell with an html tag. \
+ Put any footnote tildes at the very end. \
+ Example (including a footnote tilde): Florida 1st Congressional District Special Primary ~*'),
FieldPanel('footnotes'),
FieldPanel('citations')
]
diff --git a/fec/home/templates/blocks/reporting-dates-table-block.html b/fec/home/templates/blocks/reporting-dates-table-block.html
index fadf7dabc6..a7748e444d 100644
--- a/fec/home/templates/blocks/reporting-dates-table-block.html
+++ b/fec/home/templates/blocks/reporting-dates-table-block.html
@@ -1,7 +1,7 @@
{% load table_block_tags %}
{% load wagtailcore_tags %}
-
+
{% for row in self.table.data %}
{% if forloop.first %}