Module: Asciidoctor::Html::Flip
- Defined in:
- lib/asciidoctor/html/flip.rb
Overview
Flip when pagestyle is multi or presentation
Constant Summary collapse
- FLIP =
<<~JS (function() { const sectSelector = '.content-container > .section'; // Holds replaced pagination links to prev/next chapter const chapPagination = { prevChap: null, nextChap: null }; // Dictionary sect ID => [all sects until next sect with ID] const sectsById = { "page": []}; const page = document.getElementById('page'); const focusEl = document.getElementById('content-container'); const searchForm = document.getElementById('search-form'); const chapheading = page.querySelector('.chapheading'); const chaptitle = page.querySelector('.chaptitle'); const nav = document.querySelectorAll('#sidebar nav > ul > li.active > ul > li'); let currentId = "page"; document.querySelectorAll('.content-container > .chaphead, .content-container > .preamble').forEach(el => { sectsById[currentId].push(el); el.dataset.withSect = currentId; }); document.querySelectorAll(sectSelector).forEach(el => { if(el.id) { el.dataset.prevPage = currentId; currentId = el.id; sectsById[currentId] ||= []; } const prevId = el.dataset.prevPage; if(prevId) { sectsById[prevId][0].dataset.nextPage = currentId; } sectsById[currentId].push(el); el.dataset.withSect = currentId; }); for(const sects of Object.values(sectsById)) { sects[sects.length - 1].classList.add('last-section-multipage'); } function focusOnLoad() { if(searchForm) return; focusEl.focus({preventScroll: true}); } function updatePaginator(prev, next) { const paginator = document.querySelector('.paginator'); if(!paginator) return; const nextPage = paginator.lastElementChild; const prevPage = paginator.firstElementChild; if(next) { const nextH = next.querySelector(':scope > h2'); const nextLink = document.createElement('a'); const nextSectText = nextH && nextH.innerHTML; nextLink.href = '#' + next.id; nextLink.innerHTML = ` <div>${nextSectText || ''}</div> <div><i class="bi bi-chevron-compact-right"></i></div> `; chapPagination.nextChap ||= nextPage; nextPage.replaceWith(nextLink); } else if(chapPagination.nextChap) { nextPage.replaceWith(chapPagination.nextChap); } if(prev) { const prevH = prev.querySelector(':scope > h2'); const prevSectitle = prevH && (prevH.innerHTML + ' '); const prevLink = document.createElement('a'); let prevSectText = prevSectitle || chapheading && ('<span class="title-prefix">' + chapheading.textContent + '</span><br>'); if(!prevH) prevSectText += chaptitle.textContent; prevLink.href = '#' + (prev.id ? prev.id : 'page'); prevLink.innerHTML = ` <div><i class="bi bi-chevron-compact-left"></i></div> <div>${prevSectText || ''}</div> `; chapPagination.prevChap ||= prevPage; prevPage.replaceWith(prevLink); } else if(chapPagination.prevChap) { prevPage.replaceWith(chapPagination.prevChap) } paginator.classList.toggle('visible', paginator.querySelector('a')); } function flip(e) { if(!page.classList.contains('multi')) { focusOnLoad(); return; } e && e.preventDefault(); const href = location.hash; let id = href.substring(1); const target = document.getElementById(id); if(!target) id = 'page'; const isPresentation = page.classList.contains('presentation'); document.querySelector('.breadcrumb').classList.toggle('d-block', id != 'page' && !isPresentation); document.querySelector('.chapauthors').classList.toggle('d-block', isPresentation); let section = target && target.closest(sectSelector); if(section) { id = section.dataset.withSect; section = sectsById[id][0]; } for(const key in sectsById) { if(key == id) { const firstSect = sectsById[key][0]; const prev = firstSect.dataset.prevPage && sectsById[firstSect.dataset.prevPage][0]; const next = firstSect.dataset.nextPage && sectsById[firstSect.dataset.nextPage][0]; updatePaginator(prev, next); sectsById[key].forEach(el => el.classList.add('d-block')); } else { sectsById[key].forEach(el => el.classList.remove('d-block')); } } nav.forEach(el => { const a = el.querySelector('a'); const href = a && a.getAttribute('href'); el.classList.toggle('active', id == href.substring(1)); }); ADHT.nudgeMenuBtn(); if(target == section) { page.scrollTo({ top: 0, left: 0 }); } else { const rect = target.getBoundingClientRect() page.scrollTo({ top: rect.top + page.scrollTop, left: 0 }); } focusOnLoad(); } flip(); const dropdownItems = document.querySelectorAll('#viewmode-actions .dropdown-item'); const dropdownToggle = document.getElementById('btn-toggle'); function changeViewmode(viewmode) { page.classList.toggle('multi', viewmode == 'multi' || viewmode == 'presentation'); page.classList.toggle('presentation', viewmode == 'presentation'); if(viewmode == 'single') { ADHT.nudgeMenuBtn(); updatePaginator(); } else { flip(); } dropdownItems.forEach(el => { const isActive = (el.dataset.viewmode == viewmode); el.classList.toggle('active', isActive); if(isActive) dropdownToggle.textContent = el.textContent; }); } function move(direction) { const activeTopLevelLi = document.querySelector('#sidebar nav > ul > li.active'); if(!activeTopLevelLi) return; const activeSubLi = activeTopLevelLi.querySelector('ul > li.active'); let href = '#page'; if(direction == 'left') { const prevLi = activeSubLi && activeSubLi.previousElementSibling || !activeSubLi && activeTopLevelLi.previousElementSibling; if(prevLi) href = prevLi.firstElementChild.href; } else if(direction == 'right') { const nextLi = !activeSubLi && (page.classList.contains('multi') && activeTopLevelLi.querySelector('ul > li') || activeTopLevelLi.nextElementSibling) || activeSubLi && (activeSubLi.nextElementSibling || activeTopLevelLi.nextElementSibling) || activeSubLi || activeTopLevelLi; href = nextLi.firstElementChild.href; } navigation.navigate(href); } addEventListener('hashchange', flip); addEventListener('keyup', function(e) { switch(e.key) { case 'ArrowLeft': move('left'); break; case 'ArrowRight': move('right'); break; case 'e': case 'Escape': page.classList.contains('presentation') && changeViewmode('multi'); } }); dropdownItems && dropdownItems.forEach(link => link.addEventListener('click', function(e){ e.preventDefault(); const viewmode = link.dataset.viewmode; changeViewmode(viewmode); })); })(); JS