diff --git a/common/script/mapping-tables.js b/common/script/mapping-tables.js index 64f21a8c99..449f1ac9c5 100644 --- a/common/script/mapping-tables.js +++ b/common/script/mapping-tables.js @@ -1,6 +1,7 @@ // check for require() and respec context +/* global require , mappingTables */ + if (typeof require !== 'undefined') { - /* globals require */ require(['core/pubsubhub'], function (respecEvents) { mapTables(respecEvents); }); @@ -35,8 +36,331 @@ function getElementIndex(el) { return i; } +var mappingTableInfos = []; + +function viewAsSingleTable(mappingTableInfo) { + hideElement(mappingTableInfo.detailsContainer); + // add @id to ids array and remove @id from summary + queryAll('summary', mappingTableInfo.detailsContainer).forEach(function ( + summary + ) { + summary.removeAttribute('id'); + }); + showElement(mappingTableInfo.tableContainer); + + // add relevant @id to tr + queryAll('tbody tr', mappingTableInfo.tableContainer).forEach(function ( + tr + ) { + tr.id = mappingTableInfo.ids[getElementIndex(tr)]; + }); +} + +function viewAsDetails(mappingTableInfo) { + hideElement(mappingTableInfo.tableContainer); + // add tr @id to ids array and remove @id from tr + queryAll('tbody tr', mappingTableInfo.tableContainer).forEach(function ( + tr + ) { + tr.removeAttribute('id'); + }); + showElement(mappingTableInfo.detailsContainer); + // add relevant @id to summary + queryAll('summary', mappingTableInfo.detailsContainer).forEach(function ( + summary + ) { + const details = mappingTableInfo.detailsContainer.querySelector( + 'details' + ); + summary.id = + mappingTableInfo.ids[ + // TODO: check that this works + getElementIndex(details) - getElementIndex(summary.parentNode) + ]; + }); +} + + +function expandReferredDetails(summaryFragId) { + // if details element is not open, activate click on summary + if (!summaryFragId.parentNode.open) { + summaryFragId.click(); + } +} + +function mappingTables() { + queryAll('.table-container').forEach(function (container) { + // object to store information about a mapping table. + var tableInfo = {}; + mappingTableInfos.push(tableInfo); + + // store a reference to the container and hide it + tableInfo.tableContainer = container; + hideElement(container); + + // store a reference to the table + tableInfo.table = container.querySelector('table'); + + // create a container div to hold all the details element and insert after table + tableInfo.detailsContainer = document.createElement('div'); + tableInfo.detailsContainer.className = 'details removeOnSave'; + tableInfo.id = tableInfo.table.id + '-details'; + tableInfo.tableContainer.insertAdjacentElement( + 'afterend', + tableInfo.detailsContainer + ); + + // array to store @id attributes for rows and summaries. + tableInfo.ids = []; + + // add switch to view as single table or details/summary + var viewSwitch = document.createElement('button'); + viewSwitch.className = 'switch-view removeOnSave'; + viewSwitch.innerHTML = mappingTableLabels.viewByTable; + viewSwitch.addEventListener('click', function () { + // array to store summary/tr @ids + // if current view is details/summary + if (tableInfo.detailsContainer.style.display !== 'none') { + viewAsSingleTable(tableInfo); + // toggle the viewSwitch label from view-as-single-table to view-by-X + viewSwitch.innerHTML = + mappingTableLabels.viewByLabels[tableInfo.table.id]; + } else { + viewAsDetails(tableInfo); + // toggle the viewSwitch label from view-by-X to view-as-single-table. + viewSwitch.innerHTML = mappingTableLabels.viewByTable; + } + }); + + tableInfo.tableContainer.insertAdjacentElement('beforebegin', viewSwitch); + + // store the table's column headers in array colHeaders + // TODO: figure out what browsers we have to support and replace this with Array#map if possible + var colHeaders = []; + queryAll('thead th', tableInfo.table).forEach(function (th) { + colHeaders.push(th.innerHTML); + }); + + // remove first column header from array + colHeaders.shift(); + // for each row in the table, create details/summary.. + queryAll('tbody tr', tableInfo.table).forEach(function (row) { + var caption = row.querySelector('th').innerHTML; + var summary = caption.replace(/]+>|<\/a>/g, ''); + // get the tr's @id + var id = row.id; + // store the row's @id + tableInfo.ids.push(id); + // remove the tr's @id since same id will be used in the relevant summary element + row.removeAttribute('id'); + // store the row's cells in array rowCells + var rowCells = []; + // add row cells to array rowCells for use in the details' table + queryAll('td', row).forEach(function (cell) { + rowCells.push(cell.innerHTML); + }); + // clone colHeaders array for use in details table row headers + var rowHeaders = colHeaders.slice(0); + // if attributes mapping table... + if (tableInfo.table.classList.contains('attributes')) { + // remove second column header from array + rowHeaders.shift(); + // remove and store "HTML elements" cell from rowCells array for use in details' summary and table caption + var relevantElsCaption = rowCells.shift(); + var relevantElsSummary = relevantElsCaption.replace( + /]+>|<\/a>/g, + '' + ); + } + + // create content for each
element; add row header's content to summary + var details = document.createElement('details'); + details.className = 'map removeOnSave'; + + var detailsHTML = '' + summary; + + // if attributes mapping table, append relevant elements to summary + if (tableInfo.table.classList.contains('attributes')) { + detailsHTML += ' [' + relevantElsSummary + ']'; + } + + detailsHTML += ''; + + // add table rows using appropriate header from detailsRowHead array and relevant value from rowCells array + for (var i = 0, len = rowCells.length; i < len; i++) { + detailsHTML += + ''; + } + detailsHTML += '
' + caption; + + if (tableInfo.table.classList.contains('attributes')) { + detailsHTML += ' [' + relevantElsCaption + ']'; + } + + detailsHTML += '
' + + rowHeaders[i] + + '' + + rowCells[i] + + '
'; + details.innerHTML = detailsHTML; + + // append the
element to the detailsContainer div + tableInfo.detailsContainer.appendChild(details); + }); + + // add 'expand/collapse all' functionality + var expandAllButton = document.createElement('button'); + expandAllButton.className = 'expand removeOnSave'; + expandAllButton.innerHTML = mappingTableLabels.expand; + + var collapseAllButton = document.createElement('button'); + collapseAllButton.disabled = true; + collapseAllButton.className = 'collapse removeOnSave'; + collapseAllButton.innerHTML = mappingTableLabels.collapse; + + tableInfo.detailsContainer.insertBefore( + collapseAllButton, + tableInfo.detailsContainer.firstChild + ); + tableInfo.detailsContainer.insertBefore( + expandAllButton, + tableInfo.detailsContainer.firstChild + ); + + var expandCollapseDetails = function (detCont, action) { + queryAll('details', detCont).forEach(function (details) { + var detailsSummary = details.querySelector('summary'); + var detailsNotSummary = Array.prototype.slice + .call(details.children) + .filter(function (child) { + return child !== detailsSummary; + }); + + if (action == 'collapse') { + details.classList.remove('open'); + details.open = false; + detailsSummary.setAttribute('aria-expanded', false); + detailsNotSummary.forEach(function (element) { + hideElement(element); + }); + } else { + details.classList.add('open'); + details.open = true; + detailsSummary.setAttribute('aria-expanded', true); + detailsNotSummary.forEach(function (element) { + showElement(element); + }); + } + }); + }; + + expandAllButton.addEventListener('click', function () { + expandCollapseDetails(tableInfo.detailsContainer, 'expand'); + expandAllButton.disabled = true; + tableInfo.detailsContainer + .querySelector('button.collapse') + .removeAttribute('disabled'); + }); + + collapseAllButton.addEventListener('click', function () { + expandCollapseDetails(tableInfo.detailsContainer, 'collapse'); + collapseAllButton.disabled = true; + tableInfo.detailsContainer + .querySelector('button.expand') + .removeAttribute('disabled'); + }); + + // add collapsible table columns functionality + var showHideCols = document.createElement('div'); + showHideCols.className = 'show-hide-cols removeOnSave'; + showHideCols.innerHTML = + '' + mappingTableLabels.showHideCols + ''; + + for (var i = 0, len = colHeaders.length; i < len; i++) { + var toggleLabel = colHeaders[i] + .replace(/|<\/a>/g, '') + .replace(/\[Note [0-9]+\]<\/sup>/g, ''); + + var showHideColButton = document.createElement('button'); + showHideColButton.className = 'hide-col'; + showHideColButton.setAttribute('aria-pressed', false); + showHideColButton.setAttribute( + 'title', + mappingTableLabels.hideToolTipText + ); + showHideColButton.innerHTML = + '' + + mappingTableLabels.hideActionText + + '' + + toggleLabel; + + showHideColButton.addEventListener('click', function () { + var index = getElementIndex(showHideColButton) + 1; + var wasHidden = showHideColButton.className === 'hide-col'; + + queryAll( + 'tr>th:nth-child(' + index + '), tr>td:nth-child(' + index + ')', + tableInfo.table + ).forEach(function (element) { + if (wasHidden) { + hideElement(element); + } else { + showElement(element); + } + }); + + showHideColButton.className = wasHidden ? 'show-col' : 'hide-col'; + showHideColButton.setAttribute('aria-pressed', wasHidden); + showHideColButton.setAttribute( + 'title', + wasHidden + ? mappingTableLabels.showToolTipText + : mappingTableLabels.hideToolTipText + ); + showHideColButton.querySelector('span').innerText = wasHidden + ? mappingTableLabels.showActionText + : mappingTableLabels.hideActionText; + }); + queryAll('span', showHideColButton) + .filter(function (span) { + return !span.classList.contains('action'); + }) + .forEach(function (span) { + span.parentNode.removeChild(span); + }); + showHideCols.appendChild(showHideColButton); + } + + tableInfo.tableContainer.insertBefore( + showHideCols, + tableInfo.tableContainer.firstChild + ); + }); + + // if page URL links to frag id, reset location to frag id once details/summary view is set + if (window.location.hash) { + var hash = window.location.hash; + window.location = hash; + var frag = document.querySelector(hash); + // if frag id is for a summary element, expand the parent details element + if (frag && frag.tagName === 'SUMMARY') { + expandReferredDetails(hash); + } + } + + // Add a hook to expand referred details element when whose @href is fragid of a is clicked. + queryAll('a[href^="#"]').forEach(function (a) { + var fragId = a.getAttribute('href'); + + if (fragId.tagName === 'SUMMARY') { + a.addEventListener('click', function () { + expandReferredDetails(fragId); + }); + } + }); +} + function mapTables(respecEvents) { - 'use strict'; var mappingTableInfos = []; function viewAsSingleTable(mappingTableInfo) { @@ -81,278 +405,7 @@ function mapTables(respecEvents) { }); } - function mappingTables() { - queryAll('.table-container').forEach(function (container) { - // object to store information about a mapping table. - var tableInfo = {}; - mappingTableInfos.push(tableInfo); - - // store a reference to the container and hide it - tableInfo.tableContainer = container; - hideElement(container); - - // store a reference to the table - tableInfo.table = container.querySelector('table'); - - // create a container div to hold all the details element and insert after table - tableInfo.detailsContainer = document.createElement('div'); - tableInfo.detailsContainer.className = 'details removeOnSave'; - tableInfo.id = tableInfo.table.id + '-details'; - tableInfo.tableContainer.insertAdjacentElement( - 'afterend', - tableInfo.detailsContainer - ); - - // array to store @id attributes for rows and summaries. - tableInfo.ids = []; - - // add switch to view as single table or details/summary - var viewSwitch = document.createElement('button'); - viewSwitch.className = 'switch-view removeOnSave'; - viewSwitch.innerHTML = mappingTableLabels.viewByTable; - viewSwitch.addEventListener('click', function () { - // array to store summary/tr @ids - // if current view is details/summary - if (tableInfo.detailsContainer.style.display !== 'none') { - viewAsSingleTable(tableInfo); - // toggle the viewSwitch label from view-as-single-table to view-by-X - viewSwitch.innerHTML = - mappingTableLabels.viewByLabels[tableInfo.table.id]; - } else { - viewAsDetails(tableInfo); - // toggle the viewSwitch label from view-by-X to view-as-single-table. - viewSwitch.innerHTML = mappingTableLabels.viewByTable; - } - }); - - tableInfo.tableContainer.insertAdjacentElement('beforebegin', viewSwitch); - - // store the table's column headers in array colHeaders - // TODO: figure out what browsers we have to support and replace this with Array#map if possible - var colHeaders = []; - queryAll('thead th', tableInfo.table).forEach(function (th) { - colHeaders.push(th.innerHTML); - }); - - // remove first column header from array - colHeaders.shift(); - // for each row in the table, create details/summary.. - queryAll('tbody tr', tableInfo.table).forEach(function (row) { - var caption = row.querySelector('th').innerHTML; - var summary = caption.replace(/]+>|<\/a>/g, ''); - // get the tr's @id - var id = row.id; - // store the row's @id - tableInfo.ids.push(id); - // remove the tr's @id since same id will be used in the relevant summary element - row.removeAttribute('id'); - // store the row's cells in array rowCells - var rowCells = []; - // add row cells to array rowCells for use in the details' table - queryAll('td', row).forEach(function (cell) { - rowCells.push(cell.innerHTML); - }); - // clone colHeaders array for use in details table row headers - var rowHeaders = colHeaders.slice(0); - // if attributes mapping table... - if (tableInfo.table.classList.contains('attributes')) { - // remove second column header from array - rowHeaders.shift(); - // remove and store "HTML elements" cell from rowCells array for use in details' summary and table caption - var relevantElsCaption = rowCells.shift(); - var relevantElsSummary = relevantElsCaption.replace( - /]+>|<\/a>/g, - '' - ); - } - - // create content for each
element; add row header's content to summary - var details = document.createElement('details'); - details.className = 'map removeOnSave'; - - var detailsHTML = '' + summary; - - // if attributes mapping table, append relevant elements to summary - if (tableInfo.table.classList.contains('attributes')) { - detailsHTML += ' [' + relevantElsSummary + ']'; - } - - detailsHTML += ''; - - // add table rows using appropriate header from detailsRowHead array and relevant value from rowCells array - for (var i = 0, len = rowCells.length; i < len; i++) { - detailsHTML += - ''; - } - detailsHTML += '
' + caption; - - if (tableInfo.table.classList.contains('attributes')) { - detailsHTML += ' [' + relevantElsCaption + ']'; - } - - detailsHTML += '
' + - rowHeaders[i] + - '' + - rowCells[i] + - '
'; - details.innerHTML = detailsHTML; - - // append the
element to the detailsContainer div - tableInfo.detailsContainer.appendChild(details); - }); - - // add 'expand/collapse all' functionality - var expandAllButton = document.createElement('button'); - expandAllButton.className = 'expand removeOnSave'; - expandAllButton.innerHTML = mappingTableLabels.expand; - - var collapseAllButton = document.createElement('button'); - collapseAllButton.disabled = true; - collapseAllButton.className = 'collapse removeOnSave'; - collapseAllButton.innerHTML = mappingTableLabels.collapse; - - tableInfo.detailsContainer.insertBefore( - collapseAllButton, - tableInfo.detailsContainer.firstChild - ); - tableInfo.detailsContainer.insertBefore( - expandAllButton, - tableInfo.detailsContainer.firstChild - ); - - var expandCollapseDetails = function (detCont, action) { - queryAll('details', detCont).forEach(function (details) { - var detailsSummary = details.querySelector('summary'); - var detailsNotSummary = Array.prototype.slice - .call(details.children) - .filter(function (child) { - return child !== detailsSummary; - }); - - if (action == 'collapse') { - details.classList.remove('open'); - details.open = false; - detailsSummary.setAttribute('aria-expanded', false); - detailsNotSummary.forEach(function (element) { - hideElement(element); - }); - } else { - details.classList.add('open'); - details.open = true; - detailsSummary.setAttribute('aria-expanded', true); - detailsNotSummary.forEach(function (element) { - showElement(element); - }); - } - }); - }; - - expandAllButton.addEventListener('click', function () { - expandCollapseDetails(tableInfo.detailsContainer, 'expand'); - expandAllButton.disabled = true; - tableInfo.detailsContainer - .querySelector('button.collapse') - .removeAttribute('disabled'); - }); - - collapseAllButton.addEventListener('click', function () { - expandCollapseDetails(tableInfo.detailsContainer, 'collapse'); - collapseAllButton.disabled = true; - tableInfo.detailsContainer - .querySelector('button.expand') - .removeAttribute('disabled'); - }); - - // add collapsible table columns functionality - var showHideCols = document.createElement('div'); - showHideCols.className = 'show-hide-cols removeOnSave'; - showHideCols.innerHTML = - '' + mappingTableLabels.showHideCols + ''; - - for (var i = 0, len = colHeaders.length; i < len; i++) { - var toggleLabel = colHeaders[i] - .replace(/|<\/a>/g, '') - .replace(/\[Note [0-9]+\]<\/sup>/g, ''); - - var showHideColButton = document.createElement('button'); - showHideColButton.className = 'hide-col'; - showHideColButton.setAttribute('aria-pressed', false); - showHideColButton.setAttribute( - 'title', - mappingTableLabels.hideToolTipText - ); - showHideColButton.innerHTML = - '' + - mappingTableLabels.hideActionText + - '' + - toggleLabel; - - showHideColButton.addEventListener('click', function () { - var index = getElementIndex(showHideColButton) + 1; - var wasHidden = showHideColButton.className === 'hide-col'; - - queryAll( - 'tr>th:nth-child(' + index + '), tr>td:nth-child(' + index + ')', - tableInfo.table - ).forEach(function (element) { - if (wasHidden) { - hideElement(element); - } else { - showElement(element); - } - }); - - showHideColButton.className = wasHidden ? 'show-col' : 'hide-col'; - showHideColButton.setAttribute('aria-pressed', wasHidden); - showHideColButton.setAttribute( - 'title', - wasHidden - ? mappingTableLabels.showToolTipText - : mappingTableLabels.hideToolTipText - ); - showHideColButton.querySelector('span').innerText = wasHidden - ? mappingTableLabels.showActionText - : mappingTableLabels.hideActionText; - }); - queryAll('span', showHideColButton) - .filter(function (span) { - return !span.classList.contains('action'); - }) - .forEach(function (span) { - span.parentNode.removeChild(span); - }); - showHideCols.appendChild(showHideColButton); - } - - tableInfo.tableContainer.insertBefore( - showHideCols, - tableInfo.tableContainer.firstChild - ); - }); - - // if page URL links to frag id, reset location to frag id once details/summary view is set - if (window.location.hash) { - var hash = window.location.hash; - window.location = hash; - var frag = document.querySelector(hash); - // if frag id is for a summary element, expand the parent details element - if (frag && frag.tagName === 'SUMMARY') { - expandReferredDetails(hash); - } - } - - // Add a hook to expand referred details element when whose @href is fragid of a is clicked. - queryAll('a[href^="#"]').forEach(function (a) { - var fragId = a.getAttribute('href'); - - if (fragId.tagName === 'SUMMARY') { - a.addEventListener('click', function () { - expandReferredDetails(fragId); - }); - } - }); - } - + function expandReferredDetails(summaryFragId) { // if details element is not open, activate click on summary if (!summaryFragId.parentNode.open) { diff --git a/common/script/resolveReferences.js b/common/script/resolveReferences.js index dfbb544e3a..5edb65e343 100644 --- a/common/script/resolveReferences.js +++ b/common/script/resolveReferences.js @@ -226,8 +226,16 @@ function linkCrossReferences() { } else { console.log('linkCrossReferences(): practicesURL is not defined.'); } + + // Update any terms linked using termref to be informative as all aria terms are linked informatively + Array.prototype.slice + .call(document.querySelectorAll('.termref')) + .forEach(function (item) { + item.classList.add("informative"); + }); } + function updateReferences(base) { // update references to properties // @@ -350,70 +358,7 @@ function restrictReferences(utils, content) { return base.innerHTML; } -// add a handler to come in after all the definitions are resolved -// -// New logic: If the reference is within a 'dl' element of -// class 'termlist', and if the target of that reference is -// also within a 'dl' element of class 'termlist', then -// consider it an internal reference and ignore it -- assuming -// it is not part of another included term. -require(['core/pubsubhub'], function (respecEvents) { - 'use strict'; - - respecEvents.sub('end', function (message) { - if (message === 'core/link-to-dfn') { - // all definitions are linked - Array.prototype.slice - .call(document.querySelectorAll('a.internalDFN')) - .forEach(function (item) { - var t = item.getAttribute('href'); - if (item.closest('dl.termlist')) { - if ( - document.querySelector(t) && - document.querySelector(t).closest('dl.termlist') - ) { - // Figure out the id of the glossary term which holds this - // internal reference and see if it will be pruned (i.e. - // is in the termNames array). If it is, we can ignore - // this particular internal reference. - var dd = item.closest('dd'); - var dfn = - dd && - dd.previousElementSibling && - dd.previousElementSibling.querySelector('dfn'); - var parentTermId = addId(dfn, 'dfn', getDfnTitles(dfn)[0]); - if (termNames[parentTermId]) return; - } - } - - var r = t.replace(/^#/, ''); - if (termNames[r]) { - delete termNames[r]; - } - }); - // delete any terms that were not referenced. - if (!respecConfig.definitionMap) return; - Object.keys(termNames).forEach(function (term) { - var p = document.getElementById(term); - - if (p) { - // Delete altered dfn elements and refs - p.parentNode.nextElementSibling.parentNode.removeChild( - p.parentNode.nextElementSibling - ); - p.parentNode.parentNode.removeChild(p.parentNode); - - getDfnTitles(p).forEach(function (item) { - if (respecConfig.definitionMap[item]) { - delete respecConfig.definitionMap[item]; - } - }); - } - }); - } - }); -}); // included files are brought in after proProc. Create a DOM tree // of content then call the updateReferences method above on it. Return