/** * A lightweight light box script. * * @author Matthias Kittsteiner * @package rh-60 */ var lightBoxBox = null; var lightBoxContainer = null; var lightBoxContent = null; var lightBoxGallery = ''; var lightBoxGalleryCurrentItem = 0; var lightBoxIcon = null; var lightBoxLoading = null; var lightBoxNavigation = null; var lightBoxOverlay = null; var lightBoxType = 'normal'; var focussedElement = null; document.addEventListener( 'DOMContentLoaded', function( event ) { var lightBoxes = document.getElementsByClassName( 'lightbox' ); // event listeners for ( var i = 0; i < lightBoxes.length; i++ ) { lightBoxes[ i ].addEventListener( 'click', click ); } // image gallery load more functionality adds additional images const clickObserver = new MutationObserver( ( mutations, observer ) => { for ( const mutation of mutations ) { if ( ! mutation.target.classList.contains( 'rh-image-gallery-container' ) ) { continue; } if ( ! mutation.addedNodes ) { continue; } for ( const node of mutation.addedNodes ) { if ( node.getAttribute( 'data-target' ) !== 'lightbox' ) { continue; } node.addEventListener( 'click', click ); const accessibilityButton = node.querySelector( '.rh-block-image-gallery__link-button' ); if ( accessibilityButton ) { accessibilityButton.addEventListener( 'click', click ); } } } } ); clickObserver.observe( document.getElementById( 'main' ), { childList: true, subtree: true, } ); document.onkeydown = function( event ) { if ( event.code === 'Escape' ) { removeContent( event ); } if ( event.code === 'ArrowLeft' ) { var leftEvent = new CustomEvent( 'click', { 'bubbles': true, 'cancelable': true } ); var leftElement = document.querySelector( '.lightBoxLink.prev' ); if ( leftElement ) { leftElement.dispatchEvent( leftEvent ); } } if ( event.code === 'ArrowRight' ) { var rightEvent = new CustomEvent( 'click', { 'bubbles': true, 'cancelable': true } ); var rightElement = document.querySelector( '.lightBoxLink.next' ); if ( rightElement ) { rightElement.dispatchEvent( rightEvent ); } } }; if ( lightBoxes !== undefined && lightBoxes.length ) { init(); } else { return false; } /** * Initialize the lightBox container. */ function init() { // don’t rearrange lightBoxOverlay = createElement( 'div', 'lightBoxOverlay', document.body ); lightBoxContainer = createElement( 'div', 'lightBoxContainer', lightBoxOverlay ); lightBoxBox = createElement( 'div', 'lightBoxBox', lightBoxContainer ); lightBoxIcon = createElement( 'a', 'lightBoxIcon', lightBoxContainer ); lightBoxContent = createElement( 'div', 'lightBoxContent', lightBoxBox ); lightBoxLoading = createElement( 'div', 'lightBoxLoading', lightBoxOverlay ); lightBoxContainer.setAttribute( 'role', 'dialog' ); lightBoxContainer.setAttribute( 'aria-hidden', true ); lightBoxContainer.setAttribute( 'aria-labelledby', rhLightBox.areaLabels.title ); lightBoxOverlay.addEventListener( 'click', removeContent ); lightBoxIcon.addEventListener( 'click', removeContent ); lightBoxIcon.href = ''; lightBoxIcon.role = 'button'; lightBoxIcon.tabIndex = 2; lightBoxIcon.setAttribute( 'aria-label', rhLightBox.areaLabels.close ); document.body.addEventListener( 'click', removeContent ); } /** * Add a gallery navigation item. * * @param {Element} item * @param {String} type */ function addGalleryNavigationItem( item, type ) { // make sure to create the list element for styling purposes var listElement = createElement( 'li', '', lightBoxNavigation ); if ( document.body.contains( item ) ) { var link = createElement( 'a', 'lightBoxLink', listElement ); link.setAttribute( 'data-lightbox-gallery', item.getAttribute( 'data-lightbox-gallery' ) ); link.setAttribute( 'data-lightbox-item', item.getAttribute( 'data-lightbox-item' ) ); link.href = item.getAttribute( 'href' ) || item.getAttribute( 'data-link' ); link.role = 'button'; link.tabIndex = 1; link.classList.add( type ); link.setAttribute( 'aria-label', rhLightBox.areaLabels[ type ] ); link.addEventListener( 'click', click ); } } /** * Click on a link with class `.lightbox`. * * @param {Event} event */ function click( event ) { event.preventDefault(); event.stopPropagation(); var currentTarget = event.target || event.srcElement; var linkItem = currentTarget.closest( 'a' ); const textContainer = document.createElement( 'div' ); if ( linkItem && linkItem.href.match( /\.(jpeg|jpg|gif|png)$/ ) === null ) { lightBoxType = 'iframe'; } else { lightBoxType = 'normal'; let cover = currentTarget.closest( '.wp-block-cover' ); // try getting cover from navigation clicks if ( ! cover ) { cover = document.querySelector( '.wp-block-cover[data-lightbox-gallery="' + currentTarget.getAttribute( 'data-lightbox-gallery' ) + '"][data-lightbox-item="' + currentTarget.getAttribute( 'data-lightbox-item' ) + '"]' ); } if ( cover && ( cover.classList.contains( 'is-light-box-type-block' ) || cover.classList.contains( 'is-light-box-type-media' ) ) ) { let textContentElement; if ( cover.classList.contains( 'is-light-box-type-block' ) ) { textContentElement = cover.querySelector( '.wp-block-cover__inner-container' ); } else if ( cover.classList.contains( 'is-light-box-type-media' ) ) { textContentElement = cover.querySelector( '.rh-light-box__description' ); } if ( textContentElement ) { textContainer.innerHTML = textContentElement.innerHTML; } textContainer.classList.add( 'rh-lightbox__text-container' ); } } // slider/gallery support, as links are not in the DOM if ( ! linkItem ) { linkItem = event.currentTarget; if ( linkItem.href && linkItem.href.match( /\.(jpeg|jpg|gif|png)$/ ) === null || linkItem.getAttribute( 'data-link' ).match( /\.(jpeg|jpg|gif|png)$/ ) === null ) { lightBoxType = 'iframe'; } else { lightBoxType = 'normal'; } } lightBoxGallery = linkItem.getAttribute( 'data-lightbox-gallery' ) ? linkItem.getAttribute( 'data-lightbox-gallery' ) : ''; lightBoxGalleryCurrentItem = linkItem.getAttribute( 'data-lightbox-item' ) ? linkItem.getAttribute( 'data-lightbox-item' ) : 0; var nextItem = null; var prevItem = null; // get gallery items if ( lightBoxGallery && lightBoxGalleryCurrentItem ) { nextItem = getNextGalleryItem(); prevItem = getPrevGalleryItem(); } // remove navigation if ( lightBoxNavigation !== null ) { lightBoxNavigation.remove(); } removeContent( event ); focussedElement = document.activeElement; getContent( currentTarget, nextItem, prevItem, event ); if ( textContainer ) { lightBoxContent.appendChild( textContainer ); } } /** * Create the navigation. * * @param {Element} nextItem * @param {Element} prevItem */ function createNavigation( nextItem, prevItem ) { if ( ( nextItem !== null || prevItem !== null ) && document.body.contains( nextItem ) || document.body.contains( prevItem ) ) { lightBoxNavigation = createElement( 'ul', 'lightBoxNavigation', lightBoxContainer ); lightBoxContainer.classList.add( 'has-navigation' ); // add next|prev links addGalleryNavigationItem( prevItem, 'prev' ); addGalleryNavigationItem( nextItem, 'next' ); } } /** * Disable focus on elements outside the lightbox. */ function disableFocus() { const consentManager = document.getElementById( 'consentManager' ); const consentManagerLink = document.querySelector( '.cookie-manager-link' ); const page = document.getElementById( 'page' ); const wpadminbar = document.getElementById( 'wpadminbar' ); const focusableSelector = 'a[*|href], a[onclick], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])'; const focusable = getFocusableElements( page.querySelectorAll( focusableSelector ) ); for ( const element of focusable ) { element.setAttribute( 'data-lightbox-tabindex', element.tabIndex ); element.tabIndex = '-1'; } if ( consentManager ) { const consentManagerFocusable = getFocusableElements( consentManager.querySelectorAll( focusableSelector ) ); for ( const element of consentManagerFocusable ) { element.setAttribute( 'data-lightbox-tabindex', element.tabIndex ); element.tabIndex = '-1'; } } if ( consentManagerLink ) { consentManagerLink.setAttribute( 'data-lightbox-tabindex', consentManagerLink.tabIndex ); consentManagerLink.tabIndex = '-1'; } if ( wpadminbar ) { const wpadminbarFocusable = getFocusableElements( wpadminbar.querySelectorAll( focusableSelector ) ); for ( const element of wpadminbarFocusable ) { element.setAttribute( 'data-lightbox-tabindex', element.tabIndex ); element.tabIndex = '-1'; } } } /** * Enable focus outside the lightbox. */ function enableFocus() { const disabledFocusable = document.querySelectorAll( '[data-lightbox-tabindex]' ); for ( const focusable of disabledFocusable ) { if ( focusable.getAttribute( 'data-lightbox-tabindex' ) !== '0' ) { focusable.tabIndex = focusable.getAttribute( 'data-lightbox-tabindex' ); } else { focusable.removeAttribute( 'tabindex' ); } focusable.removeAttribute( 'data-lightbox-tabindex' ); } if ( focussedElement ) { focussedElement.focus(); focussedElement = null; } } /** * Get all focusable elements. * * @param {*} elements A list of elements * @returns {Array} A list of focusable elements */ function getFocusableElements( elements ) { return Array.from( elements ).filter( function( element ) { return ! element.hasAttribute( 'disabled' ) && ! element.getAttribute( 'aria-hidden' ); } ); } /** * Show the light box content. * * @param {Element} currentTarget * @param {Element} nextItem * @param {Element} prevItem * @param {Event} event */ function getContent( currentTarget, nextItem, prevItem, event ) { if ( ! linkItem ) { var linkItem = event.currentTarget; if ( linkItem.href ) { var href = linkItem.href; } else { var href = linkItem.getAttribute( 'data-link' ); } if ( ! href ) { var linkItem = currentTarget.closest( 'a' ); var href = linkItem.getAttribute( 'href' ); } } else { var linkItem = currentTarget.closest( 'a' ); var href = linkItem.getAttribute( 'href' ); } disableFocus(); lightBoxOverlay.classList.add( 'visible' ); lightBoxContainer.setAttribute( 'aria-hidden', false ); // add navigation createNavigation( nextItem, prevItem ); if ( lightBoxType === 'iframe' ) { lightBoxContainer.classList.add( 'lightBoxIframe' ); var element = createElement( 'iframe', 'iframe-content', lightBoxContent ); element.src = href; // allow fullscreen applications element.setAttribute( 'allowfullscreen', true ); if ( href.match( /\.pdf$/ ) ) { // handle pdf element.src += '#view=fit'; lightBoxContainer.classList.add( 'pdf-content' ); lightBoxContainer.classList.add( 'visible' ); lightBoxIcon.focus(); positionElement( lightBoxContainer ); } else { element.onload = function() { lightBoxContainer.classList.add( 'visible' ); lightBoxIcon.focus(); positionElement( lightBoxContainer ); }; } } else { var img = new Image(); lightBoxContent.innerHTML = ''; img.onload = function() { lightBoxContainer.classList.add( 'visible' ); positionElement( lightBoxContainer ); // focus gallery navigation or close button if ( lightBoxContainer.querySelector( '.lightBoxLink.next' ) ) { lightBoxContainer.querySelector( '.lightBoxLink.next' ).focus(); } else { lightBoxIcon.focus(); } }; img.src = href; } document.body.classList.add( 'lightbox-open' ); } /** * Get next item of a gallery. * * @returns {Element|Null} */ function getNextGalleryItem() { var nextGalleryItemID = parseInt( lightBoxGalleryCurrentItem ) + 1; var nextGalleryItem = document.querySelector( '[data-lightbox-gallery="' + lightBoxGallery + '"][data-lightbox-item="' + nextGalleryItemID + '"]' ); if ( document.body.contains( nextGalleryItem ) ) { return nextGalleryItem; } else { return null; } } /** * Get previous item of a gallery. * * @returns {Element|Null} */ function getPrevGalleryItem() { var prevGalleryItemID = parseInt( lightBoxGalleryCurrentItem ) - 1; var prevGalleryItem = document.querySelector( '[data-lightbox-gallery="' + lightBoxGallery + '"][data-lightbox-item="' + prevGalleryItemID + '"]' ); if ( document.body.contains( prevGalleryItem ) ) { return prevGalleryItem; } else { return null; } } /** * Remove content of the light box. * * @param {Event} event */ function removeContent( event ) { var currentTarget = event.target; var isNode = currentTarget instanceof Node; if ( currentTarget.classList.contains( 'lightBoxIcon' ) ) { event.preventDefault(); } if ( isNode && lightBoxContainer !== null && lightBoxContainer.contains( currentTarget ) && ! currentTarget.classList.contains( 'lightBoxIcon' ) && ! currentTarget.classList.contains( 'lightBoxLink' ) ) { return; } if ( lightBoxContent !== null ) { lightBoxContent.innerHTML = ''; lightBoxContent.removeAttribute( 'style' ); lightBoxBox.removeAttribute( 'style' ); } if ( lightBoxNavigation !== null ) { lightBoxNavigation.remove(); } if ( lightBoxOverlay !== null ) { lightBoxOverlay.classList.remove( 'visible' ); } if ( lightBoxContainer !== null ) { lightBoxContainer.classList.remove( 'has-navigation' ); lightBoxContainer.classList.remove( 'lightBoxIframe' ); lightBoxContainer.classList.remove( 'too-big' ); lightBoxContainer.classList.remove( 'visible' ); lightBoxContainer.removeAttribute( 'style' ); lightBoxContainer.setAttribute( 'aria-hidden', true ); } document.body.classList.remove( 'lightbox-open' ); enableFocus(); } } ); /** * Create a new HTML element. * * @param {String} name * @param {String} newClass * @param {Element} parent * @returns {Element} */ function createElement( name, newClass, parent ) { var element = document.createElement( name ); if ( newClass.length ) { element.classList.add( newClass ); } parent.appendChild( element ); return element; } /** * Position an element at the center of the browser window. * * @param {Element} element */ function positionElement( element ) { var bodyHeight = window.innerHeight; var bodyWidth = window.innerWidth; var content = element.querySelector( '.lightBoxContent' ); const lightBoxText = content.querySelector( '.rh-lightbox__text-container' ); const textContainerHeight = lightBoxText ? lightBoxText.offsetHeight : 0; // reset element.style.opacity = 0; element.classList.remove( 'too-high' ); element.classList.remove( 'too-wide' ); content.removeAttribute( 'style' ); var height = element.offsetHeight + textContainerHeight; var width = element.offsetWidth; // if light box is too high if ( bodyHeight <= height + 60 ) { element.classList.add( 'too-high' ); } // if light box is too wide if ( bodyWidth < width + 60 && lightBoxType !== 'iframe' ) { element.classList.add( 'too-wide' ); } // horizontal centering if ( window.matchMedia( '(min-width: 840px)' ).matches ) { if ( bodyWidth >= width + 20 && ! element.classList.contains( 'too-wide' ) || lightBoxType === 'iframe' ) { setTimeout( () => { var contentImage = lightBoxContainer.querySelector( '.lightBoxContent > img' ); var contentHeight = content.offsetHeight + textContainerHeight; var contentWidth = content.offsetWidth; console.log( {contentHeight, contentWidth} ); // set height manually for non-Webkit browsers if ( contentImage ) { if ( ! element.classList.contains( 'too-high' ) ) { lightBoxBox.style.height = contentHeight + 'px'; } lightBoxBox.style.width = contentWidth + 'px'; } // check if container height and width match the element height and width // due to the fact that large image are getting resized if ( height !== contentHeight) height = contentHeight + 6; if ( width !== contentWidth ) width = contentWidth + 6; element.style.left = ( bodyWidth - width ) / 2 + 'px'; element.style.right = 'auto'; }, 10 ); } } // if element is too high and too wide and contains an image // calculate if the resized height or width is the limit var image = lightBoxContainer.querySelector( '.lightBoxContent > img' ); if ( image ) { let imageHeight = image.offsetHeight; var imageWidth = image.offsetWidth; if ( height > imageHeight + textContainerHeight ) { setTimeout( () => lightBoxBox.style.height = ( imageHeight + textContainerHeight ) + 'px', 10 ); } if ( element.classList.contains( 'too-high' ) && element.classList.contains( 'too-wide' ) ) { image.style.maxHeight = 'calc(100% - ' + textContainerHeight + 'px)'; imageHeight = image.offsetHeight + textContainerHeight; imageWidth = image.offsetWidth; setTimeout( () => { lightBoxBox.style.removeProperty( 'height' ); lightBoxBox.style.removeProperty( 'width' ); }, 10 ); if ( bodyWidth >= imageWidth + 60 ) { element.classList.remove( 'too-high' ); element.classList.remove( 'too-wide' ); // center again element.style.left = ( bodyWidth - imageWidth ) / 2 + 'px'; element.style.right = ( bodyWidth - imageWidth ) / 2 + 'px'; } else if ( bodyHeight > imageHeight + 60 ) { element.classList.remove( 'too-high' ); } } else if ( element.classList.contains( 'too-high' ) && ! element.classList.contains( 'too-wide' ) ) { image.style.maxHeight = 'calc(100% - ' + textContainerHeight + 'px)'; // recalculate width after defining maximum height imageWidth = image.offsetWidth; setTimeout( () => { lightBoxBox.style.removeProperty( 'height' ); lightBoxBox.style.removeProperty( 'width' ); }, 10 ); // center again element.style.left = ( bodyWidth - image.offsetWidth ) / 2 + 'px'; element.style.right = ( bodyWidth - image.offsetWidth ) / 2 + 'px'; } // set width manually for non-Webkit browsers lightBoxContent.style.width = imageWidth + 'px'; } setTimeout( function() { element.style.removeProperty( 'opacity' ); }, 10 ); // fix wrong image width in Chrome document.body.style.zoom = 1.0000001; setTimeout( function() { document.body.style.zoom = 1; }, 50 ); // allow some time to flush the css buffer }