UNPKG

samagra-x-enketo-core

Version:

Extensible Enketo form engine

149 lines (132 loc) 5.7 kB
/** * Table of Contents (toc) module. * * @module toc */ import { getAncestors, getSiblingElement } from './dom-utils'; export default { /** * @type {Array} * @default */ tocItems: [], /** * @type {number} * @default */ _maxTocLevel: [], /** * Generate ToC Items */ generateTocItems() { this.tocItems = []; const tocElements = [ ...this.form.view.$[ 0 ].querySelectorAll( '.question:not([role="comment"]), .or-group' ) ] .filter( tocEl => { return !tocEl.closest( '.disabled' ) && ( tocEl.matches( '.question' ) || tocEl.querySelector( '.question:not(.disabled)' ) || // or-repeat-info is only considered a page by itself if it has no sibling repeats // When there are siblings repeats, we use CSS trickery to show the + button underneath the last // repeat. ( tocEl.matches( '.or-repeat-info' ) && !getSiblingElement( tocEl, '.or-repeat' ) ) ); } ) .filter( tocEl => !tocEl.classList.contains( 'or-repeat-info' ) ); tocElements.forEach( ( element, index ) => { const groupParents = getAncestors( element, '.or-group' ); this.tocItems.push( { element: element, level: groupParents.length, parent: groupParents.length > 0 ? groupParents[ groupParents.length - 1 ] : null, tocId: index, tocParentId: null } ); } ); this._maxTocLevel = Math.max.apply( Math, this.tocItems.map( el => el.level ) ); const newTocParents = this.tocItems.filter( item => item.level < this._maxTocLevel && item.element.classList.contains( 'or-group' ) ); this.tocItems.forEach( item => { const parentItem = newTocParents.find( parent => item.parent === parent.element ); if ( parentItem ) { item.tocParentId = parentItem.tocId; } } ); }, /** * Generate ToC Html Fragment * * @return {DocumentFragment} HTML list element containing Table of Contents */ getHtmlFragment() { this.generateTocItems(); const toc = document.createDocumentFragment(); let currentTocLevel = 0; do { const currentTocLevelItems = this.tocItems.filter( item => item.level === currentTocLevel ); if ( currentTocLevel === 0 ) { this._buildTocHtmlList( currentTocLevelItems, toc ); } else { const currentLevelParentIds = [ ...new Set( currentTocLevelItems.map( item => item.tocParentId ) ) ]; currentLevelParentIds.forEach( parentId => { const tocList = document.createElement( 'ul' ); const currentLTocevelItemsWithSameIds = currentTocLevelItems.filter( item => item.tocParentId === parentId ); this._buildTocHtmlList( currentLTocevelItemsWithSameIds, tocList ); const tocParent = toc.querySelectorAll( '[tocId="' + parentId + '"]' )[ 0 ]; tocParent.appendChild( tocList ); } ); } currentTocLevel++; } while ( currentTocLevel <= this._maxTocLevel ); return toc; }, /** * Get Title of Current ToC Element * * @param {Element} el - HTML element that serves as page */ _getTitle( el ) { let tocItemText; const labelEl = el.querySelector( '.question-label.active' ); if ( labelEl ) { tocItemText = labelEl.textContent; } else { const hintEl = el.querySelector( '.or-hint.active' ); if ( hintEl ) { tocItemText = hintEl.textContent; } } tocItemText = tocItemText && tocItemText.length > 20 ? `${tocItemText.substring( 0,20 )}...` : tocItemText; return tocItemText; }, /** * Builds List of ToC Items * * @param {Array<object>} items - ToC list of items * @param {Element} appendTo - HTML Element to append ToC list to */ _buildTocHtmlList( items, appendTo ) { if ( items.length > 0 ) { items.forEach( item => { const tocListItem = document.createElement( 'li' ); if ( item.element.classList.contains( 'or-group' ) ) { const groupTocTitle = document.createElement( 'summary' ); groupTocTitle.textContent = this._getTitle( item.element ) || `[${item.tocId + 1}]`; const groupToc = document.createElement( 'details' ); groupToc.setAttribute( 'tocId', item.tocId ); if ( item.tocParentId !== null ) { groupToc.setAttribute( 'tocParentId', item.tocParentId ); } groupToc.append( groupTocTitle ); tocListItem.append( groupToc ); } else { const a = document.createElement( 'a' ); a.textContent = this._getTitle( item.element ) || `[${item.tocId + 1}]`; tocListItem.setAttribute( 'tocId', item.tocId ); tocListItem.setAttribute( 'role', 'pageLink' ); if ( item.tocParentId !== null ) { tocListItem.setAttribute( 'tocParentId', item.tocParentId ); } tocListItem.append( a ); } appendTo.append( tocListItem ); } ); } } };