media/js/foundation/annual-report-modal.es6.js (163 lines of code) (raw):

/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import MzpModal from '@mozilla-protocol/core/protocol/js/modal'; const modalContainers = document.getElementsByClassName('has-modal'); const content = document.querySelector('.mzp-u-modal-content'); const articleArray = document.querySelectorAll('[data-modal-id]'); const modalNextButtonFragment = `<div class="c-modal-next"> <button type="button" class="c-modal-button-next" title="Next"> Next </button> </div>`; const modalPrevButtonFragment = `<div class="c-modal-prev"> <button type="button" class="c-modal-button-prev" title="Previous"> Previous </button> </div>`; // Set to true when a listener it set, as to not set it multiple times. // Resets when the modal is closed. let kbInit = null; function keyboardListenerInit() { if (kbInit) { return; // Turn Off } document.addEventListener('keyup', keyboardNextPrev, false); kbInit = true; } function keyboardNextPrev(event) { if (!kbInit) { return; } switch (event.keyCode) { case 37: // Left arrow prevModalArticle(); break; case 38: // Up arrow prevModalArticle(); break; case 39: // Right arrow nextModalArticle(); break; case 40: // Down arrow nextModalArticle(); break; } } function modalInit() { const modalNextButton = document.querySelector('.c-modal-next'); const modalPrevButton = document.querySelector('.c-modal-prev'); modalNextButton.removeEventListener('click', nextModalArticle, false); modalPrevButton.removeEventListener('click', prevModalArticle, false); modalNextButton.addEventListener('click', nextModalArticle, false); modalPrevButton.addEventListener('click', prevModalArticle, false); keyboardListenerInit(); } function getCurrentModalIndex() { const modalContent = document.querySelector( '.mzp-u-modal-content.mzp-c-modal-overlay-contents' ); const newArticleIndex = parseInt( modalContent.getAttribute('data-current-index'), 10 ); return newArticleIndex; } function updateModalArticle(index) { const modalContent = document.querySelector( '.mzp-u-modal-content.mzp-c-modal-overlay-contents' ); const newArticleId = articleArray[index].getAttribute('data-modal-id'); const newModalContent = document .querySelector(`[data-modal-parent="${newArticleId}"]`) .cloneNode(true); const currentModalContent = modalContent.firstElementChild; window.location.hash = newArticleId; modalContent.replaceChild(newModalContent, currentModalContent); modalContent.setAttribute('data-current-index', index); modalInit(); } function nextModalArticle() { let newArticleIndex = getCurrentModalIndex(); newArticleIndex++; // If at the end of the gallery, start over if (newArticleIndex === articleArray.length) { newArticleIndex = 0; } updateModalArticle(newArticleIndex); } function prevModalArticle() { let newArticleIndex = getCurrentModalIndex(); newArticleIndex--; // If at the beginning of the gallery, start over if (newArticleIndex < 0) { newArticleIndex = articleArray.length - 1; } updateModalArticle(newArticleIndex); } for (let i = 0; i < modalContainers.length; i++) { const modalContainer = modalContainers[i]; modalContainer.setAttribute('aria-role', 'button'); modalContainer.setAttribute('data-current-index', i); modalContainer.addEventListener('click', function (e) { e.preventDefault(); const modalId = this.getAttribute('data-modal-id'); const currentIndex = parseInt( this.getAttribute('data-current-index'), 10 ); const modalContent = document .querySelector(`[data-modal-parent="${modalId}"]`) .cloneNode(true); window.location.hash = modalId; modalContent.removeAttribute('id'); modalContent.setAttribute('aria-role', 'article'); MzpModal.createModal(this, content, { allowScroll: false, closeText: window.Mozilla.Utils.trans('global-close'), onCreate: () => { content.appendChild(modalContent); content.setAttribute('data-current-index', currentIndex); const modalCloseButton = document.querySelector('.mzp-c-modal-close'); modalCloseButton.insertAdjacentHTML( 'beforebegin', modalPrevButtonFragment ); modalCloseButton.insertAdjacentHTML( 'beforebegin', modalNextButtonFragment ); // set next/prev listeners modalInit(); }, onDestroy: () => { if (window.history) { window.history.replaceState( '', '', window.location.pathname ); } kbInit = false; // Re-cache the current modal content which may have changed via next/prev buttons const modalParent = document.querySelector( '.mzp-u-modal-content.mzp-c-modal-overlay-contents' ); const currentModalContent = modalParent.firstElementChild; /** * Ensure focus is returned to opening CTA on modal close. * We have to do some manual DOM walking here as some clickable * parent elements do not normally receive focus programmatically. * Issue: https://github.com/mozilla/bedrock/issues/12346 */ const parentId = currentModalContent.getAttribute('data-modal-parent'); // Try and get the modal parent ID/ element. if (parentId) { const parentElement = document.querySelector( `[data-modal-id="${parentId}"]` ); // If the modal element contains a link or button, // set focus to the first child instance. const parentCta = parentElement.querySelector('a, button'); if (parentCta) { parentCta.focus(); } } modalParent.removeChild(currentModalContent); } }); }); } // trigger modal on page load if hash is present and matches a person with a bio if (window.location.hash) { const target = document.getElementById(window.location.hash.substr(1)); if (target) { // Query by data attribute in the event that not every element may have an ID. // Issue https://github.com/mozilla/bedrock/issues/12346 const link = document.querySelector(`[data-modal-id="${target.id}"]`); if (link && link.classList.contains('has-modal')) { link.click(); } } }