// Utility function function Util () {}; /* class manipulation functions */ Util.hasClass = function(el, className) { return el.classList.contains(className); }; Util.addClass = function(el, className) { var classList = className.split(' '); el.classList.add(classList[0]); if (classList.length > 1) Util.addClass(el, classList.slice(1).join(' ')); }; Util.removeClass = function(el, className) { var classList = className.split(' '); el.classList.remove(classList[0]); if (classList.length > 1) Util.removeClass(el, classList.slice(1).join(' ')); }; Util.toggleClass = function(el, className, bool) { if(bool) Util.addClass(el, className); else Util.removeClass(el, className); }; Util.setAttributes = function(el, attrs) { for(var key in attrs) { el.setAttribute(key, attrs[key]); } }; /* DOM manipulation */ Util.getChildrenByClassName = function(el, className) { var children = el.children, childrenByClass = []; for (var i = 0; i < children.length; i++) { if (Util.hasClass(children[i], className)) childrenByClass.push(children[i]); } return childrenByClass; }; Util.is = function(elem, selector) { if(selector.nodeType){ return elem === selector; } var qa = (typeof(selector) === 'string' ? document.querySelectorAll(selector) : selector), length = qa.length; while(length--){ if(qa[length] === elem){ return true; } } return false; }; /* Animate height of an element */ Util.setHeight = function(start, to, element, duration, cb, timeFunction) { var change = to - start, currentTime = null; var animateHeight = function(timestamp){ if (!currentTime) currentTime = timestamp; var progress = timestamp - currentTime; if(progress > duration) progress = duration; var val = parseInt((progress/duration)*change + start); if(timeFunction) { val = Math[timeFunction](progress, start, to - start, duration); } element.style.height = val+"px"; if(progress < duration) { window.requestAnimationFrame(animateHeight); } else { if(cb) cb(); } }; //set the height of the element before starting animation -> fix bug on Safari element.style.height = start+"px"; window.requestAnimationFrame(animateHeight); }; /* Smooth Scroll */ Util.scrollTo = function(final, duration, cb, scrollEl) { var element = scrollEl || window; var start = element.scrollTop || document.documentElement.scrollTop, currentTime = null; if(!scrollEl) start = window.scrollY || document.documentElement.scrollTop; var animateScroll = function(timestamp){ if (!currentTime) currentTime = timestamp; var progress = timestamp - currentTime; if(progress > duration) progress = duration; var val = Math.easeInOutQuad(progress, start, final-start, duration); element.scrollTo(0, val); if(progress < duration) { window.requestAnimationFrame(animateScroll); } else { cb && cb(); } }; window.requestAnimationFrame(animateScroll); }; /* Move Focus */ Util.moveFocus = function (element) { if( !element ) element = document.getElementsByTagName("body")[0]; element.focus(); if (document.activeElement !== element) { element.setAttribute('tabindex','-1'); element.focus(); } }; /* Misc */ Util.getIndexInArray = function(array, el) { return Array.prototype.indexOf.call(array, el); }; Util.cssSupports = function(property, value) { return CSS.supports(property, value); }; // merge a set of user options into plugin defaults // https://gomakethings.com/vanilla-javascript-version-of-jquery-extend/ Util.extend = function() { // Variables var extended = {}; var deep = false; var i = 0; var length = arguments.length; // Check if a deep merge if ( Object.prototype.toString.call( arguments[0] ) === '[object Boolean]' ) { deep = arguments[0]; i++; } // Merge the object into the extended object var merge = function (obj) { for ( var prop in obj ) { if ( Object.prototype.hasOwnProperty.call( obj, prop ) ) { // If deep merge and property is an object, merge properties if ( deep && Object.prototype.toString.call(obj[prop]) === '[object Object]' ) { extended[prop] = extend( true, extended[prop], obj[prop] ); } else { extended[prop] = obj[prop]; } } } }; // Loop through each object and conduct a merge for ( ; i < length; i++ ) { var obj = arguments[i]; merge(obj); } return extended; }; // Check if Reduced Motion is enabled Util.osHasReducedMotion = function() { if(!window.matchMedia) return false; var matchMediaObj = window.matchMedia('(prefers-reduced-motion: reduce)'); if(matchMediaObj) return matchMediaObj.matches; return false; // return false if not supported }; /* Animation curves */ Math.easeInOutQuad = function (t, b, c, d) { t /= d/2; if (t < 1) return c/2*t*t + b; t--; return -c/2 * (t*(t-2) - 1) + b; }; Math.easeInQuart = function (t, b, c, d) { t /= d; return c*t*t*t*t + b; }; Math.easeOutQuart = function (t, b, c, d) { t /= d; t--; return -c * (t*t*t*t - 1) + b; }; Math.easeInOutQuart = function (t, b, c, d) { t /= d/2; if (t < 1) return c/2*t*t*t*t + b; t -= 2; return -c/2 * (t*t*t*t - 2) + b; }; Math.easeOutElastic = function (t, b, c, d) { var s=1.70158;var p=d*0.7;var a=c; if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; if (a < Math.abs(c)) { a=c; var s=p/4; } else var s = p/(2*Math.PI) * Math.asin (c/a); return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b; }; /* JS Utility Classes */ // make focus ring visible only for keyboard navigation (i.e., tab key) (function() { var focusTab = document.getElementsByClassName('js-tab-focus'), shouldInit = false, outlineStyle = false, eventDetected = false; function detectClick() { if(focusTab.length > 0) { resetFocusStyle(false); window.addEventListener('keydown', detectTab); } window.removeEventListener('mousedown', detectClick); outlineStyle = false; eventDetected = true; }; function detectTab(event) { if(event.keyCode !== 9) return; resetFocusStyle(true); window.removeEventListener('keydown', detectTab); window.addEventListener('mousedown', detectClick); outlineStyle = true; }; function resetFocusStyle(bool) { var outlineStyle = bool ? '' : 'none'; for(var i = 0; i < focusTab.length; i++) { focusTab[i].style.setProperty('outline', outlineStyle); } }; function initFocusTabs() { if(shouldInit) { if(eventDetected) resetFocusStyle(outlineStyle); return; } shouldInit = focusTab.length > 0; window.addEventListener('mousedown', detectClick); }; initFocusTabs(); window.addEventListener('initFocusTabs', initFocusTabs); }()); function resetFocusTabsStyle() { window.dispatchEvent(new CustomEvent('initFocusTabs')); }; (function() { var Masonry = function(element) { this.element = element; this.list = this.element.getElementsByClassName('js-masonry__list')[0]; this.items = this.element.getElementsByClassName('js-masonry__item'); this.activeColumns = 0; this.colStartWidth = 0; this.colWidth = 0; this.colGap = 0; this.colHeights = []; this.colItems = []; getGridLayout(this); setGridLayout(this); initMasonryLayout(this); }; function getGridLayout(grid) { var itemStyle = window.getComputedStyle(grid.items[0]); if(grid.colStartWidth == 0) { grid.colStartWidth = parseFloat(itemStyle.getPropertyValue('width')); } grid.colGap = parseFloat(itemStyle.getPropertyValue('margin-right')); }; function setGridLayout(grid) { var containerWidth = parseFloat(window.getComputedStyle(grid.element).getPropertyValue('width')); grid.activeColumns = parseInt((containerWidth + grid.colGap)/(grid.colStartWidth+grid.colGap)); if(grid.activeColumns == 0) grid.activeColumns = 1; grid.colWidth = parseFloat((containerWidth - (grid.activeColumns - 1)*grid.colGap)/grid.activeColumns); for(var i = 0; i < grid.items.length; i++) { grid.items[i].style.width = grid.colWidth+'px'; } }; function initMasonryLayout(grid) { checkImgLoaded(grid); grid.element.addEventListener('masonry-resize', function(){ getGridLayout(grid); setGridLayout(grid); layItems(grid); }); grid.element.addEventListener('masonry-reset', function(event){ checkImgLoaded(grid); }); }; function layItems(grid) { Util.addClass(grid.element, 'masonry--loaded'); grid.colHeights = []; grid.colItems = []; for(var j = 0; j < grid.activeColumns; j++) { grid.colHeights.push(0); grid.colItems[j] = []; } for(var i = 0; i < grid.items.length; i++) { var minHeight = Math.min.apply(Math, grid.colHeights), index = grid.colHeights.indexOf(minHeight); if(grid.colItems[index]) grid.colItems[index].push(i); var itemHeight = grid.items[i].getBoundingClientRect().height || grid.items[i].offsetHeight || 1; grid.colHeights[index] = grid.colHeights[index] + grid.colGap + itemHeight; } var masonryHeight = Math.max.apply(Math, grid.colHeights) + 5; grid.list.style.cssText = 'height: ' + masonryHeight + 'px;'; grid.element.dispatchEvent(new CustomEvent('masonry-laid')); }; function checkImgLoaded(grid) { var imgs = grid.list.getElementsByTagName('img'); function countLoaded() { var setTimeoutOn = false; for(var i = 0; i < imgs.length; i++) { if(!imgs[i].complete) { setTimeoutOn = true; break; } else if (typeof imgs[i].naturalHeight !== "undefined" && imgs[i].naturalHeight == 0) { setTimeoutOn = true; break; } } if(!setTimeoutOn) { layItems(grid); } else { setTimeout(function(){ countLoaded(); }, 100); } }; if(imgs.length == 0) { layItems(grid); } else { countLoaded(); } }; var masonries = document.getElementsByClassName('js-masonry'), flexSupported = Util.cssSupports('flex-basis', 'auto'), masonriesArray = []; if(masonries.length > 0) { for(var i = 0; i < masonries.length; i++) { (function(i){masonriesArray.push(new Masonry(masonries[i]));})(i); } var resizingId = false, customEvent = new CustomEvent('masonry-resize'); window.addEventListener('resize', function() { clearTimeout(resizingId); resizingId = setTimeout(doneResizing, 500); }); function doneResizing() { for(var i = 0; i < masonriesArray.length; i++) { (function(i){masonriesArray[i].element.dispatchEvent(customEvent)})(i); }; }; }; })(); (function() { var Menu = function(element) { this.element = element; this.elementId = this.element.getAttribute('id'); this.menuItems = this.element.getElementsByClassName('js-menu__content'); this.trigger = document.querySelectorAll('[aria-controls="'+this.elementId+'"]'); this.selectedTrigger = false; this.menuIsOpen = false; this.initMenu(); this.initMenuEvents(); }; Menu.prototype.initMenu = function() { // init aria-labels for(var i = 0; i < this.trigger.length; i++) { Util.setAttributes(this.trigger[i], {'aria-expanded': 'false', 'aria-haspopup': 'true'}); } // init tabindex for(var i = 0; i < this.menuItems.length; i++) { this.menuItems[i].setAttribute('tabindex', '0'); } }; Menu.prototype.initMenuEvents = function() { var self = this; for(var i = 0; i < this.trigger.length; i++) {(function(i){ self.trigger[i].addEventListener('click', function(event){ event.preventDefault(); // if the menu had been previously opened by another trigger element -> close it first and reopen in the right position if(Util.hasClass(self.element, 'menu--is-visible') && self.selectedTrigger != self.trigger[i]) { self.toggleMenu(false, false); // close menu } // toggle menu self.selectedTrigger = self.trigger[i]; self.toggleMenu(!Util.hasClass(self.element, 'menu--is-visible'), true); }); })(i);} // keyboard events this.element.addEventListener('keydown', function(event) { // use up/down arrow to navigate list of menu items if( !Util.hasClass(event.target, 'js-menu__content') ) return; if( (event.keyCode && event.keyCode == 40) || (event.key && event.key.toLowerCase() == 'arrowdown') ) { self.navigateItems(event, 'next'); } else if( (event.keyCode && event.keyCode == 38) || (event.key && event.key.toLowerCase() == 'arrowup') ) { self.navigateItems(event, 'prev'); } }); }; Menu.prototype.toggleMenu = function(bool, moveFocus) { var self = this; // toggle menu visibility Util.toggleClass(this.element, 'menu--is-visible', bool); this.menuIsOpen = bool; if(bool) { this.selectedTrigger.setAttribute('aria-expanded', 'true'); Util.moveFocus(this.menuItems[0]); this.element.addEventListener("transitionend", function(event) {Util.moveFocus(self.menuItems[0]);}, {once: true}); // position the menu element this.positionMenu(); // add class to menu trigger Util.addClass(this.selectedTrigger, 'menu-control--active'); } else if(this.selectedTrigger) { this.selectedTrigger.setAttribute('aria-expanded', 'false'); if(moveFocus) Util.moveFocus(this.selectedTrigger); // remove class from menu trigger Util.removeClass(this.selectedTrigger, 'menu-control--active'); this.selectedTrigger = false; } }; Menu.prototype.positionMenu = function(event, direction) { var selectedTriggerPosition = this.selectedTrigger.getBoundingClientRect(), menuOnTop = (window.innerHeight - selectedTriggerPosition.bottom) < selectedTriggerPosition.top; // menuOnTop = window.innerHeight < selectedTriggerPosition.bottom + this.element.offsetHeight; var left = selectedTriggerPosition.left, right = (window.innerWidth - selectedTriggerPosition.right), isRight = (window.innerWidth < selectedTriggerPosition.left + this.element.offsetWidth); var horizontal = isRight ? 'right: '+right+'px;' : 'left: '+left+'px;', vertical = menuOnTop ? 'bottom: '+(window.innerHeight - selectedTriggerPosition.top)+'px;' : 'top: '+selectedTriggerPosition.bottom+'px;'; // check right position is correct -> otherwise set left to 0 if( isRight && (right + this.element.offsetWidth) > window.innerWidth) horizontal = 'left: '+ parseInt((window.innerWidth - this.element.offsetWidth)/2)+'px;'; var maxHeight = menuOnTop ? selectedTriggerPosition.top - 20 : window.innerHeight - selectedTriggerPosition.bottom - 20; this.element.setAttribute('style', horizontal + vertical +'max-height:'+Math.floor(maxHeight)+'px;'); }; Menu.prototype.navigateItems = function(event, direction) { event.preventDefault(); var index = Util.getIndexInArray(this.menuItems, event.target), nextIndex = direction == 'next' ? index + 1 : index - 1; if(nextIndex < 0) nextIndex = this.menuItems.length - 1; if(nextIndex > this.menuItems.length - 1) nextIndex = 0; Util.moveFocus(this.menuItems[nextIndex]); }; Menu.prototype.checkMenuFocus = function() { var menuParent = document.activeElement.closest('.js-menu'); if (!menuParent || !this.element.contains(menuParent)) this.toggleMenu(false, false); }; Menu.prototype.checkMenuClick = function(target) { if( !this.element.contains(target) && !target.closest('[aria-controls="'+this.elementId+'"]')) this.toggleMenu(false); }; window.Menu = Menu; //initialize the Menu objects var menus = document.getElementsByClassName('js-menu'); if( menus.length > 0 ) { var menusArray = []; var scrollingContainers = []; for( var i = 0; i < menus.length; i++) { (function(i){ menusArray.push(new Menu(menus[i])); var scrollableElement = menus[i].getAttribute('data-scrollable-element'); if(scrollableElement && !scrollingContainers.includes(scrollableElement)) scrollingContainers.push(scrollableElement); })(i); } // listen for key events window.addEventListener('keyup', function(event){ if( event.keyCode && event.keyCode == 9 || event.key && event.key.toLowerCase() == 'tab' ) { //close menu if focus is outside menu element menusArray.forEach(function(element){ element.checkMenuFocus(); }); } else if( event.keyCode && event.keyCode == 27 || event.key && event.key.toLowerCase() == 'escape' ) { // close menu on 'Esc' menusArray.forEach(function(element){ element.toggleMenu(false, false); }); } }); // close menu when clicking outside it window.addEventListener('click', function(event){ menusArray.forEach(function(element){ element.checkMenuClick(event.target); }); }); // on resize -> close all menu elements window.addEventListener('resize', function(event){ menusArray.forEach(function(element){ element.toggleMenu(false, false); }); }); // on scroll -> close all menu elements window.addEventListener('scroll', function(event){ menusArray.forEach(function(element){ if(element.menuIsOpen) element.toggleMenu(false, false); }); }); // take into account additinal scrollable containers for(var j = 0; j < scrollingContainers.length; j++) { var scrollingContainer = document.querySelector(scrollingContainers[j]); if(scrollingContainer) { scrollingContainer.addEventListener('scroll', function(event){ menusArray.forEach(function(element){ if(element.menuIsOpen) element.toggleMenu(false, false); }); }); } } } }()); (function() { var Modal = function(element) { this.element = element; this.triggers = document.querySelectorAll('[aria-controls="'+this.element.getAttribute('id')+'"]'); this.firstFocusable = null; this.lastFocusable = null; this.moveFocusEl = null; // focus will be moved to this element when modal is open this.modalFocus = this.element.getAttribute('data-modal-first-focus') ? this.element.querySelector(this.element.getAttribute('data-modal-first-focus')) : null; this.selectedTrigger = null; this.preventScrollEl = this.getPreventScrollEl(); this.showClass = "modal--is-visible"; this.initModal(); }; Modal.prototype.getPreventScrollEl = function() { var scrollEl = false; var querySelector = this.element.getAttribute('data-modal-prevent-scroll'); if(querySelector) scrollEl = document.querySelector(querySelector); return scrollEl; }; Modal.prototype.initModal = function() { var self = this; //open modal when clicking on trigger buttons if ( this.triggers ) { for(var i = 0; i < this.triggers.length; i++) { this.triggers[i].addEventListener('click', function(event) { event.preventDefault(); if(Util.hasClass(self.element, self.showClass)) { self.closeModal(); return; } self.selectedTrigger = event.currentTarget; self.showModal(); self.initModalEvents(); }); } } // listen to the openModal event -> open modal without a trigger button this.element.addEventListener('openModal', function(event){ if(event.detail) self.selectedTrigger = event.detail; self.showModal(); self.initModalEvents(); }); // listen to the closeModal event -> close modal without a trigger button this.element.addEventListener('closeModal', function(event){ if(event.detail) self.selectedTrigger = event.detail; self.closeModal(); }); // if modal is open by default -> initialise modal events if(Util.hasClass(this.element, this.showClass)) this.initModalEvents(); }; Modal.prototype.showModal = function() { var self = this; Util.addClass(this.element, this.showClass); this.getFocusableElements(); if(this.moveFocusEl) { this.moveFocusEl.focus(); // wait for the end of transitions before moving focus this.element.addEventListener("transitionend", function cb(event) { self.moveFocusEl.focus(); self.element.removeEventListener("transitionend", cb); }); } this.emitModalEvents('modalIsOpen'); // change the overflow of the preventScrollEl if(this.preventScrollEl) this.preventScrollEl.style.overflow = 'hidden'; }; Modal.prototype.closeModal = function() { if(!Util.hasClass(this.element, this.showClass)) return; Util.removeClass(this.element, this.showClass); this.firstFocusable = null; this.lastFocusable = null; this.moveFocusEl = null; if(this.selectedTrigger) this.selectedTrigger.focus(); //remove listeners this.cancelModalEvents(); this.emitModalEvents('modalIsClose'); // change the overflow of the preventScrollEl if(this.preventScrollEl) this.preventScrollEl.style.overflow = ''; }; Modal.prototype.initModalEvents = function() { //add event listeners this.element.addEventListener('keydown', this); this.element.addEventListener('click', this); }; Modal.prototype.cancelModalEvents = function() { //remove event listeners this.element.removeEventListener('keydown', this); this.element.removeEventListener('click', this); }; Modal.prototype.handleEvent = function (event) { switch(event.type) { case 'click': { this.initClick(event); } case 'keydown': { this.initKeyDown(event); } } }; Modal.prototype.initKeyDown = function(event) { if( event.keyCode && event.keyCode == 9 || event.key && event.key == 'Tab' ) { //trap focus inside modal this.trapFocus(event); } else if( (event.keyCode && event.keyCode == 13 || event.key && event.key == 'Enter') && event.target.closest('.js-modal__close')) { event.preventDefault(); this.closeModal(); // close modal when pressing Enter on close button } }; Modal.prototype.initClick = function(event) { //close modal when clicking on close button or modal bg layer if( !event.target.closest('.js-modal__close') && !Util.hasClass(event.target, 'js-modal') ) return; event.preventDefault(); this.closeModal(); }; Modal.prototype.trapFocus = function(event) { if( this.firstFocusable == document.activeElement && event.shiftKey) { //on Shift+Tab -> focus last focusable element when focus moves out of modal event.preventDefault(); this.lastFocusable.focus(); } if( this.lastFocusable == document.activeElement && !event.shiftKey) { //on Tab -> focus first focusable element when focus moves out of modal event.preventDefault(); this.firstFocusable.focus(); } } Modal.prototype.getFocusableElements = function() { //get all focusable elements inside the modal var allFocusable = this.element.querySelectorAll(focusableElString); this.getFirstVisible(allFocusable); this.getLastVisible(allFocusable); this.getFirstFocusable(); }; Modal.prototype.getFirstVisible = function(elements) { //get first visible focusable element inside the modal for(var i = 0; i < elements.length; i++) { if( isVisible(elements[i]) ) { this.firstFocusable = elements[i]; break; } } }; Modal.prototype.getLastVisible = function(elements) { //get last visible focusable element inside the modal for(var i = elements.length - 1; i >= 0; i--) { if( isVisible(elements[i]) ) { this.lastFocusable = elements[i]; break; } } }; Modal.prototype.getFirstFocusable = function() { if(!this.modalFocus || !Element.prototype.matches) { this.moveFocusEl = this.firstFocusable; return; } var containerIsFocusable = this.modalFocus.matches(focusableElString); if(containerIsFocusable) { this.moveFocusEl = this.modalFocus; } else { this.moveFocusEl = false; var elements = this.modalFocus.querySelectorAll(focusableElString); for(var i = 0; i < elements.length; i++) { if( isVisible(elements[i]) ) { this.moveFocusEl = elements[i]; break; } } if(!this.moveFocusEl) this.moveFocusEl = this.firstFocusable; } }; Modal.prototype.emitModalEvents = function(eventName) { var event = new CustomEvent(eventName, {detail: this.selectedTrigger}); this.element.dispatchEvent(event); }; function isVisible(element) { return element.offsetWidth || element.offsetHeight || element.getClientRects().length; }; window.Modal = Modal; //initialize the Modal objects var modals = document.getElementsByClassName('js-modal'); // generic focusable elements string selector var focusableElString = '[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, [tabindex]:not([tabindex="-1"]), [contenteditable], audio[controls], video[controls], summary'; if( modals.length > 0 ) { var modalArrays = []; for( var i = 0; i < modals.length; i++) { (function(i){modalArrays.push(new Modal(modals[i]));})(i); } window.addEventListener('keydown', function(event){ //close modal window on esc if(event.keyCode && event.keyCode == 27 || event.key && event.key.toLowerCase() == 'escape') { for( var i = 0; i < modalArrays.length; i++) { (function(i){modalArrays[i].closeModal();})(i); }; } }); } }()); (function() { var SwipeContent = function(element) { this.element = element; this.delta = [false, false]; this.dragging = false; this.intervalId = false; initSwipeContent(this); }; function initSwipeContent(content) { content.element.addEventListener('mousedown', handleEvent.bind(content)); content.element.addEventListener('touchstart', handleEvent.bind(content), {passive: true}); }; function initDragging(content) { //add event listeners content.element.addEventListener('mousemove', handleEvent.bind(content)); content.element.addEventListener('touchmove', handleEvent.bind(content), {passive: true}); content.element.addEventListener('mouseup', handleEvent.bind(content)); content.element.addEventListener('mouseleave', handleEvent.bind(content)); content.element.addEventListener('touchend', handleEvent.bind(content)); }; function cancelDragging(content) { //remove event listeners if(content.intervalId) { (!window.requestAnimationFrame) ? clearInterval(content.intervalId) : window.cancelAnimationFrame(content.intervalId); content.intervalId = false; } content.element.removeEventListener('mousemove', handleEvent.bind(content)); content.element.removeEventListener('touchmove', handleEvent.bind(content)); content.element.removeEventListener('mouseup', handleEvent.bind(content)); content.element.removeEventListener('mouseleave', handleEvent.bind(content)); content.element.removeEventListener('touchend', handleEvent.bind(content)); }; function handleEvent(event) { switch(event.type) { case 'mousedown': case 'touchstart': startDrag(this, event); break; case 'mousemove': case 'touchmove': drag(this, event); break; case 'mouseup': case 'mouseleave': case 'touchend': endDrag(this, event); break; } }; function startDrag(content, event) { content.dragging = true; // listen to drag movements initDragging(content); content.delta = [parseInt(unify(event).clientX), parseInt(unify(event).clientY)]; // emit drag start event emitSwipeEvents(content, 'dragStart', content.delta, event.target); }; function endDrag(content, event) { cancelDragging(content); // credits: https://css-tricks.com/simple-swipe-with-vanilla-javascript/ var dx = parseInt(unify(event).clientX), dy = parseInt(unify(event).clientY); // check if there was a left/right swipe if(content.delta && (content.delta[0] || content.delta[0] === 0)) { var s = getSign(dx - content.delta[0]); if(Math.abs(dx - content.delta[0]) > 30) { (s < 0) ? emitSwipeEvents(content, 'swipeLeft', [dx, dy]) : emitSwipeEvents(content, 'swipeRight', [dx, dy]); } content.delta[0] = false; } // check if there was a top/bottom swipe if(content.delta && (content.delta[1] || content.delta[1] === 0)) { var y = getSign(dy - content.delta[1]); if(Math.abs(dy - content.delta[1]) > 30) { (y < 0) ? emitSwipeEvents(content, 'swipeUp', [dx, dy]) : emitSwipeEvents(content, 'swipeDown', [dx, dy]); } content.delta[1] = false; } // emit drag end event emitSwipeEvents(content, 'dragEnd', [dx, dy]); content.dragging = false; }; function drag(content, event) { if(!content.dragging) return; // emit dragging event with coordinates (!window.requestAnimationFrame) ? content.intervalId = setTimeout(function(){emitDrag.bind(content, event);}, 250) : content.intervalId = window.requestAnimationFrame(emitDrag.bind(content, event)); }; function emitDrag(event) { emitSwipeEvents(this, 'dragging', [parseInt(unify(event).clientX), parseInt(unify(event).clientY)]); }; function unify(event) { // unify mouse and touch events return event.changedTouches ? event.changedTouches[0] : event; }; function emitSwipeEvents(content, eventName, detail, el) { var trigger = false; if(el) trigger = el; // emit event with coordinates var event = new CustomEvent(eventName, {detail: {x: detail[0], y: detail[1], origin: trigger}}); content.element.dispatchEvent(event); }; function getSign(x) { if(!Math.sign) { return ((x > 0) - (x < 0)) || +x; } else { return Math.sign(x); } }; window.SwipeContent = SwipeContent; //initialize the SwipeContent objects var swipe = document.getElementsByClassName('js-swipe-content'); if( swipe.length > 0 ) { for( var i = 0; i < swipe.length; i++) { (function(i){new SwipeContent(swipe[i]);})(i); } } }()); (function() { var Sidebar = function(element) { this.element = element; this.triggers = document.querySelectorAll('[aria-controls="'+this.element.getAttribute('id')+'"]'); this.firstFocusable = null; this.lastFocusable = null; this.selectedTrigger = null; this.showClass = "sidebar--is-visible"; this.staticClass = "sidebar--static"; this.customStaticClass = ""; this.readyClass = "sidebar--loaded"; this.contentReadyClass = "sidebar-loaded:show"; this.layout = false; // this will be static or mobile this.preventScrollEl = getPreventScrollEl(this); getCustomStaticClass(this); // custom classes for static version initSidebar(this); }; function getPreventScrollEl(element) { var scrollEl = false; var querySelector = element.element.getAttribute('data-sidebar-prevent-scroll'); if(querySelector) scrollEl = document.querySelector(querySelector); return scrollEl; }; function getCustomStaticClass(element) { var customClasses = element.element.getAttribute('data-static-class'); if(customClasses) element.customStaticClass = ' '+customClasses; }; function initSidebar(sidebar) { initSidebarResize(sidebar); // handle changes in layout -> mobile to static and viceversa if ( sidebar.triggers ) { // open sidebar when clicking on trigger buttons - mobile layout only for(var i = 0; i < sidebar.triggers.length; i++) { sidebar.triggers[i].addEventListener('click', function(event) { event.preventDefault(); toggleSidebar(sidebar, event.target); }); } } // use the 'openSidebar' event to trigger the sidebar sidebar.element.addEventListener('openSidebar', function(event) { toggleSidebar(sidebar, event.detail); }); }; function toggleSidebar(sidebar, target) { if(Util.hasClass(sidebar.element, sidebar.showClass)) { sidebar.selectedTrigger = target; closeSidebar(sidebar); return; } sidebar.selectedTrigger = target; showSidebar(sidebar); initSidebarEvents(sidebar); }; function showSidebar(sidebar) { // mobile layout only Util.addClass(sidebar.element, sidebar.showClass); getFocusableElements(sidebar); Util.moveFocus(sidebar.element); // change the overflow of the preventScrollEl if(sidebar.preventScrollEl) sidebar.preventScrollEl.style.overflow = 'hidden'; }; function closeSidebar(sidebar) { // mobile layout only Util.removeClass(sidebar.element, sidebar.showClass); sidebar.firstFocusable = null; sidebar.lastFocusable = null; if(sidebar.selectedTrigger) sidebar.selectedTrigger.focus(); sidebar.element.removeAttribute('tabindex'); //remove listeners cancelSidebarEvents(sidebar); // change the overflow of the preventScrollEl if(sidebar.preventScrollEl) sidebar.preventScrollEl.style.overflow = ''; }; function initSidebarEvents(sidebar) { // mobile layout only //add event listeners sidebar.element.addEventListener('keydown', handleEvent.bind(sidebar)); sidebar.element.addEventListener('click', handleEvent.bind(sidebar)); }; function cancelSidebarEvents(sidebar) { // mobile layout only //remove event listeners sidebar.element.removeEventListener('keydown', handleEvent.bind(sidebar)); sidebar.element.removeEventListener('click', handleEvent.bind(sidebar)); }; function handleEvent(event) { // mobile layout only switch(event.type) { case 'click': { initClick(this, event); } case 'keydown': { initKeyDown(this, event); } } }; function initKeyDown(sidebar, event) { // mobile layout only if( event.keyCode && event.keyCode == 27 || event.key && event.key == 'Escape' ) { //close sidebar window on esc closeSidebar(sidebar); } else if( event.keyCode && event.keyCode == 9 || event.key && event.key == 'Tab' ) { //trap focus inside sidebar trapFocus(sidebar, event); } }; function initClick(sidebar, event) { // mobile layout only //close sidebar when clicking on close button or sidebar bg layer if( !event.target.closest('.js-sidebar__close-btn') && !Util.hasClass(event.target, 'js-sidebar') ) return; event.preventDefault(); closeSidebar(sidebar); }; function trapFocus(sidebar, event) { // mobile layout only if( sidebar.firstFocusable == document.activeElement && event.shiftKey) { //on Shift+Tab -> focus last focusable element when focus moves out of sidebar event.preventDefault(); sidebar.lastFocusable.focus(); } if( sidebar.lastFocusable == document.activeElement && !event.shiftKey) { //on Tab -> focus first focusable element when focus moves out of sidebar event.preventDefault(); sidebar.firstFocusable.focus(); } }; function initSidebarResize(sidebar) { // custom event emitted when window is resized - detect only if the sidebar--static@{breakpoint} class was added var beforeContent = getComputedStyle(sidebar.element, ':before').getPropertyValue('content'); if(beforeContent && beforeContent !='' && beforeContent !='none') { checkSidebarLayout(sidebar); sidebar.element.addEventListener('update-sidebar', function(event){ checkSidebarLayout(sidebar); }); } // check if there a main element to show var mainContent = document.getElementsByClassName(sidebar.contentReadyClass); if(mainContent.length > 0) Util.removeClass(mainContent[0], sidebar.contentReadyClass); Util.addClass(sidebar.element, sidebar.readyClass); }; function checkSidebarLayout(sidebar) { var layout = getComputedStyle(sidebar.element, ':before').getPropertyValue('content').replace(/\'|"/g, ''); if(layout == sidebar.layout) return; sidebar.layout = layout; if(layout != 'static') Util.addClass(sidebar.element, 'rz4-hide'); Util.toggleClass(sidebar.element, sidebar.staticClass + sidebar.customStaticClass, layout == 'static'); if(layout != 'static') setTimeout(function(){Util.removeClass(sidebar.element, 'rz4-hide')}); // reset element role (layout == 'static') ? sidebar.element.removeAttribute('role', 'alertdialog') : sidebar.element.setAttribute('role', 'alertdialog'); // reset mobile behaviour if(layout == 'static' && Util.hasClass(sidebar.element, sidebar.showClass)) closeSidebar(sidebar); }; function getFocusableElements(sidebar) { //get all focusable elements inside the drawer var allFocusable = sidebar.element.querySelectorAll('[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, [tabindex]:not([tabindex="-1"]), [contenteditable], audio[controls], video[controls], summary'); getFirstVisible(sidebar, allFocusable); getLastVisible(sidebar, allFocusable); }; function getFirstVisible(sidebar, elements) { //get first visible focusable element inside the sidebar for(var i = 0; i < elements.length; i++) { if( elements[i].offsetWidth || elements[i].offsetHeight || elements[i].getClientRects().length ) { sidebar.firstFocusable = elements[i]; return true; } } }; function getLastVisible(sidebar, elements) { //get last visible focusable element inside the sidebar for(var i = elements.length - 1; i >= 0; i--) { if( elements[i].offsetWidth || elements[i].offsetHeight || elements[i].getClientRects().length ) { sidebar.lastFocusable = elements[i]; return true; } } }; window.Sidebar = Sidebar; //initialize the Sidebar objects var sidebar = document.getElementsByClassName('js-sidebar'); if( sidebar.length > 0 ) { for( var i = 0; i < sidebar.length; i++) { (function(i){new Sidebar(sidebar[i]);})(i); } // switch from mobile to static layout var customEvent = new CustomEvent('update-sidebar'); window.addEventListener('resize', function(event){ (!window.requestAnimationFrame) ? setTimeout(function(){resetLayout();}, 250) : window.requestAnimationFrame(resetLayout); }); (window.requestAnimationFrame) // init sidebar layout ? window.requestAnimationFrame(resetLayout) : resetLayout(); function resetLayout() { for( var i = 0; i < sidebar.length; i++) { (function(i){sidebar[i].dispatchEvent(customEvent)})(i); }; }; } }()); (function() { var MenuBar = function(element) { this.element = element; this.items = Util.getChildrenByClassName(this.element, 'menu-bar__item'); this.mobHideItems = this.element.getElementsByClassName('menu-bar__item--hide'); this.moreItemsTrigger = this.element.getElementsByClassName('js-menu-bar__trigger'); initMenuBar(this); }; function initMenuBar(menu) { setMenuTabIndex(menu); // set correct tabindexes for menu item initMenuBarMarkup(menu); // create additional markup checkMenuLayout(menu); // set menu layout Util.addClass(menu.element, 'menu-bar--loaded'); // reveal menu // custom event emitted when window is resized menu.element.addEventListener('update-menu-bar', function(event){ checkMenuLayout(menu); if(menu.menuInstance) menu.menuInstance.toggleMenu(false, false); // close dropdown }); // keyboard events // open dropdown when pressing Enter on trigger element if(menu.moreItemsTrigger.length > 0) { menu.moreItemsTrigger[0].addEventListener('keydown', function(event) { if( (event.keyCode && event.keyCode == 13) || (event.key && event.key.toLowerCase() == 'enter') ) { if(!menu.menuInstance) return; menu.menuInstance.selectedTrigger = menu.moreItemsTrigger[0]; menu.menuInstance.toggleMenu(!Util.hasClass(menu.subMenu, 'menu--is-visible'), true); } }); // close dropdown on esc menu.subMenu.addEventListener('keydown', function(event) { if((event.keyCode && event.keyCode == 27) || (event.key && event.key.toLowerCase() == 'escape')) { // close submenu on esc if(menu.menuInstance) menu.menuInstance.toggleMenu(false, true); } }); } // navigate menu items using left/right arrows menu.element.addEventListener('keydown', function(event) { if( (event.keyCode && event.keyCode == 39) || (event.key && event.key.toLowerCase() == 'arrowright') ) { navigateItems(menu.items, event, 'next'); } else if( (event.keyCode && event.keyCode == 37) || (event.key && event.key.toLowerCase() == 'arrowleft') ) { navigateItems(menu.items, event, 'prev'); } }); }; function setMenuTabIndex(menu) { // set tabindexes for the menu items to allow keyboard navigation var nextItem = false; for(var i = 0; i < menu.items.length; i++ ) { if(i == 0 || nextItem) menu.items[i].setAttribute('tabindex', '0'); else menu.items[i].setAttribute('tabindex', '-1'); if(i == 0 && menu.moreItemsTrigger.length > 0) nextItem = true; else nextItem = false; } }; function initMenuBarMarkup(menu) { if(menu.mobHideItems.length == 0 ) { // no items to hide on mobile - remove trigger if(menu.moreItemsTrigger.length > 0) menu.element.removeChild(menu.moreItemsTrigger[0]); return; } if(menu.moreItemsTrigger.length == 0) return; // create the markup for the Menu element var content = ''; menu.menuControlId = 'submenu-bar-'+Date.now(); for(var i = 0; i < menu.mobHideItems.length; i++) { var item = menu.mobHideItems[i].cloneNode(true), svg = item.getElementsByTagName('svg')[0], label = item.getElementsByClassName('menu-bar__label')[0]; svg.setAttribute('class', 'icon menu__icon'); content = content + '
  • '+svg.outerHTML+''+label.innerHTML+'
  • '; } Util.setAttributes(menu.moreItemsTrigger[0], {'role': 'button', 'aria-expanded': 'false', 'aria-controls': menu.menuControlId, 'aria-haspopup': 'true'}); var subMenu = document.createElement('menu'), customClass = menu.element.getAttribute('data-menu-class'); Util.setAttributes(subMenu, {'id': menu.menuControlId, 'class': 'menu js-menu '+customClass}); subMenu.innerHTML = content; document.body.appendChild(subMenu); menu.subMenu = subMenu; menu.subItems = subMenu.getElementsByTagName('li'); menu.menuInstance = new Menu(menu.subMenu); // this will handle the dropdown behaviour }; function checkMenuLayout(menu) { // switch from compressed to expanded layout and viceversa var layout = getComputedStyle(menu.element, ':before').getPropertyValue('content').replace(/\'|"/g, ''); Util.toggleClass(menu.element, 'menu-bar--collapsed', layout == 'collapsed'); }; function navigateItems(list, event, direction, prevIndex) { // keyboard navigation among menu items event.preventDefault(); var index = (typeof prevIndex !== 'undefined') ? prevIndex : Util.getIndexInArray(list, event.target), nextIndex = direction == 'next' ? index + 1 : index - 1; if(nextIndex < 0) nextIndex = list.length - 1; if(nextIndex > list.length - 1) nextIndex = 0; // check if element is visible before moving focus (list[nextIndex].offsetParent === null) ? navigateItems(list, event, direction, nextIndex) : Util.moveFocus(list[nextIndex]); }; function checkMenuClick(menu, target) { // close dropdown when clicking outside the menu element if(menu.menuInstance && !menu.moreItemsTrigger[0].contains(target) && !menu.subMenu.contains(target)) menu.menuInstance.toggleMenu(false, false); }; // init MenuBars objects var menuBars = document.getElementsByClassName('js-menu-bar'); if( menuBars.length > 0 ) { var j = 0, menuBarArray = []; for( var i = 0; i < menuBars.length; i++) { var beforeContent = getComputedStyle(menuBars[i], ':before').getPropertyValue('content'); if(beforeContent && beforeContent !='' && beforeContent !='none') { (function(i){menuBarArray.push(new MenuBar(menuBars[i]));})(i); j = j + 1; } } if(j > 0) { var resizingId = false, customEvent = new CustomEvent('update-menu-bar'); // update Menu Bar layout on resize window.addEventListener('resize', function(event){ clearTimeout(resizingId); resizingId = setTimeout(doneResizing, 150); }); // close menu when clicking outside it window.addEventListener('click', function(event){ menuBarArray.forEach(function(element){ checkMenuClick(element, event.target); }); }); function doneResizing() { for( var i = 0; i < menuBars.length; i++) { (function(i){menuBars[i].dispatchEvent(customEvent)})(i); }; }; } } }()); (function() { var Slideshow = function(opts) { this.options = Util.extend(Slideshow.defaults , opts); this.element = this.options.element; this.items = this.element.getElementsByClassName('js-slideshow__item'); this.controls = this.element.getElementsByClassName('js-slideshow__control'); this.selectedSlide = 0; this.autoplayId = false; this.autoplayPaused = false; this.navigation = false; this.navCurrentLabel = false; this.ariaLive = false; this.moveFocus = false; this.animating = false; this.supportAnimation = Util.cssSupports('transition'); this.animationOff = (!Util.hasClass(this.element, 'slideshow--transition-fade') && !Util.hasClass(this.element, 'slideshow--transition-slide') && !Util.hasClass(this.element, 'slideshow--transition-prx')); this.animationType = Util.hasClass(this.element, 'slideshow--transition-prx') ? 'prx' : 'slide'; this.animatingClass = 'slideshow--is-animating'; initSlideshow(this); initSlideshowEvents(this); initAnimationEndEvents(this); }; Slideshow.prototype.showNext = function() { showNewItem(this, this.selectedSlide + 1, 'next'); }; Slideshow.prototype.showPrev = function() { showNewItem(this, this.selectedSlide - 1, 'prev'); }; Slideshow.prototype.showItem = function(index) { showNewItem(this, index, false); }; Slideshow.prototype.startAutoplay = function() { var self = this; if(this.options.autoplay && !this.autoplayId && !this.autoplayPaused) { self.autoplayId = setInterval(function(){ self.showNext(); }, self.options.autoplayInterval); } }; Slideshow.prototype.pauseAutoplay = function() { var self = this; if(this.options.autoplay) { clearInterval(self.autoplayId); self.autoplayId = false; } }; function initSlideshow(slideshow) { // basic slideshow settings // if no slide has been selected -> select the first one if(slideshow.element.getElementsByClassName('slideshow__item--selected').length < 1) Util.addClass(slideshow.items[0], 'slideshow__item--selected'); slideshow.selectedSlide = Util.getIndexInArray(slideshow.items, slideshow.element.getElementsByClassName('slideshow__item--selected')[0]); // create an element that will be used to announce the new visible slide to SR var srLiveArea = document.createElement('div'); Util.setAttributes(srLiveArea, {'class': 'sr-only js-slideshow__aria-live', 'aria-live': 'polite', 'aria-atomic': 'true'}); slideshow.element.appendChild(srLiveArea); slideshow.ariaLive = srLiveArea; }; function initSlideshowEvents(slideshow) { // if slideshow navigation is on -> create navigation HTML and add event listeners if(slideshow.options.navigation) { // check if navigation has already been included if(slideshow.element.getElementsByClassName('js-slideshow__navigation').length == 0) { var navigation = document.createElement('ol'), navChildren = ''; var navClasses = slideshow.options.navigationClass+' js-slideshow__navigation'; if(slideshow.items.length <= 1) { navClasses = navClasses + ' is-hidden'; } navigation.setAttribute('class', navClasses); for(var i = 0; i < slideshow.items.length; i++) { var className = (i == slideshow.selectedSlide) ? 'class="'+slideshow.options.navigationItemClass+' '+slideshow.options.navigationItemClass+'--selected js-slideshow__nav-item"' : 'class="'+slideshow.options.navigationItemClass+' js-slideshow__nav-item"', navCurrentLabel = (i == slideshow.selectedSlide) ? 'Current Item' : ''; navChildren = navChildren + '
  • '; } navigation.innerHTML = navChildren; slideshow.element.appendChild(navigation); } slideshow.navCurrentLabel = slideshow.element.getElementsByClassName('js-slideshow__nav-current-label')[0]; slideshow.navigation = slideshow.element.getElementsByClassName('js-slideshow__nav-item'); var dotsNavigation = slideshow.element.getElementsByClassName('js-slideshow__navigation')[0]; dotsNavigation.addEventListener('click', function(event){ navigateSlide(slideshow, event, true); }); dotsNavigation.addEventListener('keyup', function(event){ navigateSlide(slideshow, event, (event.key.toLowerCase() == 'enter')); }); } // slideshow arrow controls if(slideshow.controls.length > 0) { // hide controls if one item available if(slideshow.items.length <= 1) { Util.addClass(slideshow.controls[0], 'is-hidden'); Util.addClass(slideshow.controls[1], 'is-hidden'); } slideshow.controls[0].addEventListener('click', function(event){ event.preventDefault(); slideshow.showPrev(); updateAriaLive(slideshow); }); slideshow.controls[1].addEventListener('click', function(event){ event.preventDefault(); slideshow.showNext(); updateAriaLive(slideshow); }); } // swipe events if(slideshow.options.swipe) { //init swipe new SwipeContent(slideshow.element); slideshow.element.addEventListener('swipeLeft', function(event){ slideshow.showNext(); }); slideshow.element.addEventListener('swipeRight', function(event){ slideshow.showPrev(); }); } // autoplay if(slideshow.options.autoplay) { slideshow.startAutoplay(); // pause autoplay if user is interacting with the slideshow if(!slideshow.options.autoplayOnHover) { slideshow.element.addEventListener('mouseenter', function(event){ slideshow.pauseAutoplay(); slideshow.autoplayPaused = true; }); slideshow.element.addEventListener('mouseleave', function(event){ slideshow.autoplayPaused = false; slideshow.startAutoplay(); }); } if(!slideshow.options.autoplayOnFocus) { slideshow.element.addEventListener('focusin', function(event){ slideshow.pauseAutoplay(); slideshow.autoplayPaused = true; }); slideshow.element.addEventListener('focusout', function(event){ slideshow.autoplayPaused = false; slideshow.startAutoplay(); }); } } // detect if external buttons control the slideshow var slideshowId = slideshow.element.getAttribute('id'); if(slideshowId) { var externalControls = document.querySelectorAll('[data-controls="'+slideshowId+'"]'); for(var i = 0; i < externalControls.length; i++) { (function(i){externalControlSlide(slideshow, externalControls[i]);})(i); } } // custom event to trigger selection of a new slide element slideshow.element.addEventListener('selectNewItem', function(event){ // check if slide is already selected if(event.detail) { if(event.detail - 1 == slideshow.selectedSlide) return; showNewItem(slideshow, event.detail - 1, false); } }); // keyboard navigation slideshow.element.addEventListener('keydown', function(event){ if(event.keyCode && event.keyCode == 39 || event.key && event.key.toLowerCase() == 'arrowright') { slideshow.showNext(); } else if(event.keyCode && event.keyCode == 37 || event.key && event.key.toLowerCase() == 'arrowleft') { slideshow.showPrev(); } }); }; function navigateSlide(slideshow, event, keyNav) { // user has interacted with the slideshow navigation -> update visible slide var target = ( Util.hasClass(event.target, 'js-slideshow__nav-item') ) ? event.target : event.target.closest('.js-slideshow__nav-item'); if(keyNav && target && !Util.hasClass(target, 'slideshow__nav-item--selected')) { slideshow.showItem(Util.getIndexInArray(slideshow.navigation, target)); slideshow.moveFocus = true; updateAriaLive(slideshow); } }; function initAnimationEndEvents(slideshow) { // remove animation classes at the end of a slide transition for( var i = 0; i < slideshow.items.length; i++) { (function(i){ slideshow.items[i].addEventListener('animationend', function(){resetAnimationEnd(slideshow, slideshow.items[i]);}); slideshow.items[i].addEventListener('transitionend', function(){resetAnimationEnd(slideshow, slideshow.items[i]);}); })(i); } }; function resetAnimationEnd(slideshow, item) { setTimeout(function(){ // add a delay between the end of animation and slideshow reset - improve animation performance if(Util.hasClass(item,'slideshow__item--selected')) { if(slideshow.moveFocus) Util.moveFocus(item); emitSlideshowEvent(slideshow, 'newItemVisible', slideshow.selectedSlide); slideshow.moveFocus = false; } Util.removeClass(item, 'slideshow__item--'+slideshow.animationType+'-out-left slideshow__item--'+slideshow.animationType+'-out-right slideshow__item--'+slideshow.animationType+'-in-left slideshow__item--'+slideshow.animationType+'-in-right'); item.removeAttribute('aria-hidden'); slideshow.animating = false; Util.removeClass(slideshow.element, slideshow.animatingClass); }, 100); }; function showNewItem(slideshow, index, bool) { if(slideshow.items.length <= 1) return; if(slideshow.animating && slideshow.supportAnimation) return; slideshow.animating = true; Util.addClass(slideshow.element, slideshow.animatingClass); if(index < 0) index = slideshow.items.length - 1; else if(index >= slideshow.items.length) index = 0; // skip slideshow item if it is hidden if(bool && Util.hasClass(slideshow.items[index], 'is-hidden')) { slideshow.animating = false; index = bool == 'next' ? index + 1 : index - 1; showNewItem(slideshow, index, bool); return; } // index of new slide is equal to index of slide selected item if(index == slideshow.selectedSlide) { slideshow.animating = false; return; } var exitItemClass = getExitItemClass(slideshow, bool, slideshow.selectedSlide, index); var enterItemClass = getEnterItemClass(slideshow, bool, slideshow.selectedSlide, index); // transition between slides if(!slideshow.animationOff) Util.addClass(slideshow.items[slideshow.selectedSlide], exitItemClass); Util.removeClass(slideshow.items[slideshow.selectedSlide], 'slideshow__item--selected'); slideshow.items[slideshow.selectedSlide].setAttribute('aria-hidden', 'true'); //hide to sr element that is exiting the viewport if(slideshow.animationOff) { Util.addClass(slideshow.items[index], 'slideshow__item--selected'); } else { Util.addClass(slideshow.items[index], enterItemClass+' slideshow__item--selected'); } // reset slider navigation appearance resetSlideshowNav(slideshow, index, slideshow.selectedSlide); slideshow.selectedSlide = index; // reset autoplay slideshow.pauseAutoplay(); slideshow.startAutoplay(); // reset controls/navigation color themes resetSlideshowTheme(slideshow, index); // emit event emitSlideshowEvent(slideshow, 'newItemSelected', slideshow.selectedSlide); if(slideshow.animationOff) { slideshow.animating = false; Util.removeClass(slideshow.element, slideshow.animatingClass); } }; function getExitItemClass(slideshow, bool, oldIndex, newIndex) { var className = ''; if(bool) { className = (bool == 'next') ? 'slideshow__item--'+slideshow.animationType+'-out-right' : 'slideshow__item--'+slideshow.animationType+'-out-left'; } else { className = (newIndex < oldIndex) ? 'slideshow__item--'+slideshow.animationType+'-out-left' : 'slideshow__item--'+slideshow.animationType+'-out-right'; } return className; }; function getEnterItemClass(slideshow, bool, oldIndex, newIndex) { var className = ''; if(bool) { className = (bool == 'next') ? 'slideshow__item--'+slideshow.animationType+'-in-right' : 'slideshow__item--'+slideshow.animationType+'-in-left'; } else { className = (newIndex < oldIndex) ? 'slideshow__item--'+slideshow.animationType+'-in-left' : 'slideshow__item--'+slideshow.animationType+'-in-right'; } return className; }; function resetSlideshowNav(slideshow, newIndex, oldIndex) { if(slideshow.navigation) { Util.removeClass(slideshow.navigation[oldIndex], 'slideshow__nav-item--selected'); Util.addClass(slideshow.navigation[newIndex], 'slideshow__nav-item--selected'); slideshow.navCurrentLabel.parentElement.removeChild(slideshow.navCurrentLabel); slideshow.navigation[newIndex].getElementsByTagName('button')[0].appendChild(slideshow.navCurrentLabel); } }; function resetSlideshowTheme(slideshow, newIndex) { var dataTheme = slideshow.items[newIndex].getAttribute('data-theme'); if(dataTheme) { if(slideshow.navigation) slideshow.navigation[0].parentElement.setAttribute('data-theme', dataTheme); if(slideshow.controls[0]) slideshow.controls[0].parentElement.setAttribute('data-theme', dataTheme); } else { if(slideshow.navigation) slideshow.navigation[0].parentElement.removeAttribute('data-theme'); if(slideshow.controls[0]) slideshow.controls[0].parentElement.removeAttribute('data-theme'); } }; function emitSlideshowEvent(slideshow, eventName, detail) { var event = new CustomEvent(eventName, {detail: detail}); slideshow.element.dispatchEvent(event); }; function updateAriaLive(slideshow) { slideshow.ariaLive.innerHTML = 'Item '+(slideshow.selectedSlide + 1)+' of '+slideshow.items.length; }; function externalControlSlide(slideshow, button) { // control slideshow using external element button.addEventListener('click', function(event){ var index = button.getAttribute('data-index'); if(!index || index == slideshow.selectedSlide + 1) return; event.preventDefault(); showNewItem(slideshow, index - 1, false); }); }; Slideshow.defaults = { element : '', navigation : true, autoplay : false, autoplayOnHover: false, autoplayOnFocus: false, autoplayInterval: 5000, navigationItemClass: 'slideshow__nav-item', navigationClass: 'slideshow__navigation', swipe: false }; window.Slideshow = Slideshow; //initialize the Slideshow objects var slideshows = document.getElementsByClassName('js-slideshow'); if( slideshows.length > 0 ) { for( var i = 0; i < slideshows.length; i++) { (function(i){ var navigation = (slideshows[i].getAttribute('data-navigation') && slideshows[i].getAttribute('data-navigation') == 'off') ? false : true, autoplay = (slideshows[i].getAttribute('data-autoplay') && slideshows[i].getAttribute('data-autoplay') == 'on') ? true : false, autoplayOnHover = (slideshows[i].getAttribute('data-autoplay-hover') && slideshows[i].getAttribute('data-autoplay-hover') == 'on') ? true : false, autoplayOnFocus = (slideshows[i].getAttribute('data-autoplay-focus') && slideshows[i].getAttribute('data-autoplay-focus') == 'on') ? true : false, autoplayInterval = (slideshows[i].getAttribute('data-autoplay-interval')) ? slideshows[i].getAttribute('data-autoplay-interval') : 5000, swipe = (slideshows[i].getAttribute('data-swipe') && slideshows[i].getAttribute('data-swipe') == 'on') ? true : false, navigationItemClass = slideshows[i].getAttribute('data-navigation-item-class') ? slideshows[i].getAttribute('data-navigation-item-class') : 'slideshow__nav-item', navigationClass = slideshows[i].getAttribute('data-navigation-class') ? slideshows[i].getAttribute('data-navigation-class') : 'slideshow__navigation'; new Slideshow({element: slideshows[i], navigation: navigation, autoplay : autoplay, autoplayOnHover: autoplayOnHover, autoplayOnFocus: autoplayOnFocus, autoplayInterval : autoplayInterval, swipe : swipe, navigationItemClass: navigationItemClass, navigationClass: navigationClass}); })(i); } } }()); (function() { var ExpGallery = function(element) { this.element = element; this.slideshow = this.element.getElementsByClassName('js-exp-lightbox__body')[0]; this.slideshowList = this.element.getElementsByClassName('js-exp-lightbox__slideshow')[0]; this.slideshowId = this.element.getAttribute('id') this.gallery = document.querySelector('[data-controls="'+this.slideshowId+'"]'); this.galleryItems = this.gallery.getElementsByClassName('js-exp-gallery__item'); this.lazyload = this.gallery.getAttribute('data-placeholder'); this.animationRunning = false; // menu bar this.menuBar = this.element.getElementsByClassName('js-menu-bar'); initNewContent(this); initLightboxMarkup(this); lazyLoadLightbox(this); initSlideshow(this); initModal(this); initModalEvents(this); }; function initNewContent(gallery) { // if the gallery uses the infinite load - make sure to update the modal gallery when new content is loaded gallery.infiniteScrollParent = gallery.gallery.closest('[data-container]'); if(!gallery.infiniteScrollParent && Util.hasClass(gallery.gallery, 'js-infinite-scroll')) { gallery.infiniteScrollParent = gallery.gallery; } if(gallery.infiniteScrollParent) { gallery.infiniteScrollParent.addEventListener('content-loaded', function(event){ initLightboxMarkup(gallery); initSlideshow(gallery); }); } }; function initLightboxMarkup(gallery) { // create items inside lightbox - modal slideshow var slideshowContent = ''; for(var i = 0; i < gallery.galleryItems.length; i++) { var image = gallery.galleryItems[i].getElementsByTagName('img')[0]; if (!image) continue; // skip this iteration if no image found var caption = gallery.galleryItems[i].getElementsByClassName('js-exp-gallery__caption'); // details var src = image.getAttribute('data-src') || image.getAttribute('data-modal-src') || image.getAttribute('src'); var altAttr = image.getAttribute('alt'); altAttr = altAttr ? 'alt="'+altAttr+'"' : ''; var draggable = gallery.slideshow.getAttribute('data-swipe') == 'on' ? 'draggable="false" ondragstart="return false;"' : ''; var imgBlock = gallery.lazyload ? '' : ''; var captionBlock = caption.length > 0 ? '
    '+caption[0].textContent+'
    ' : ''; slideshowContent += '
  • '+imgBlock+'
    '+captionBlock+'
  • '; } gallery.slideshowList.innerHTML = slideshowContent; gallery.slides = gallery.slideshowList.getElementsByClassName('js-slideshow__item'); // append the morphing image - we will animate it from the selected slide to the final position (and viceversa) var imgMorph = document.createElement("div"); Util.setAttributes(imgMorph, {'aria-hidden': 'true', 'class': 'exp-lightbox__clone-img-wrapper js-exp-lightbox__clone-img-wrapper', 'data-exp-morph': gallery.slideshowId}); imgMorph.innerHTML = ''; document.body.appendChild(imgMorph); gallery.imgMorph = document.querySelector('.js-exp-lightbox__clone-img-wrapper[data-exp-morph="'+gallery.slideshowId+'"]'); gallery.imgMorphSVG = gallery.imgMorph.getElementsByTagName('svg')[0]; gallery.imgMorphRect = gallery.imgMorph.getElementsByTagName('rect')[0]; gallery.imgMorphImg = gallery.imgMorph.getElementsByTagName('image')[0]; // append image for zoom in effect if(gallery.slideshow.getAttribute('data-zoom') == 'on') { var zoomImg = document.createElement("div"); Util.setAttributes(zoomImg, {'aria-hidden': 'true', 'class': 'exp-lightbox__zoom exp-lightbox__zoom--no-transition js-exp-lightbox__zoom'}); zoomImg.innerHTML = ''; gallery.element.appendChild(zoomImg); gallery.zoomImg = gallery.element.getElementsByClassName('js-exp-lightbox__zoom')[0]; } }; function lazyLoadLightbox(gallery) { // lazyload media of selected slide/prev slide/next slide gallery.slideshow.addEventListener('newItemSelected', function(event){ // 'newItemSelected' is emitted by the Slideshow object when a new slide is selected gallery.selectedSlide = event.detail; lazyLoadSlide(gallery); // menu element - trigger new slide event triggerMenuEvent(gallery); }); }; function lazyLoadSlide(gallery) { setSlideMedia(gallery, gallery.selectedSlide); setSlideMedia(gallery, gallery.selectedSlide + 1); setSlideMedia(gallery, gallery.selectedSlide - 1); }; function setSlideMedia(gallery, index) { if(index < 0) index = gallery.slides.length - 1; if(index > gallery.slides.length - 1) index = 0; var imgs = gallery.slides[index].querySelectorAll('img[data-src]'); for(var i = 0; i < imgs.length; i++) { imgs[i].src = imgs[i].getAttribute('data-src'); } }; function initSlideshow(gallery) { // reset slideshow navigation resetSlideshowControls(gallery); gallery.slideshowNav = gallery.element.getElementsByClassName('js-slideshow__control'); if(gallery.slides.length <= 1) { toggleSlideshowElements(gallery, true); return; } var swipe = (gallery.slideshow.getAttribute('data-swipe') && gallery.slideshow.getAttribute('data-swipe') == 'on') ? true : false; gallery.slideshowObj = new Slideshow({element: gallery.slideshow, navigation: false, autoplay : false, swipe : swipe}); }; function resetSlideshowControls(gallery) { var arrowControl = gallery.element.getElementsByClassName('js-slideshow__control'); if(arrowControl.length == 0) return; var controlsWrapper = arrowControl[0].parentElement; if(!controlsWrapper) return; controlsWrapper.innerHTML = controlsWrapper.innerHTML; }; function toggleSlideshowElements(gallery, bool) { // hide slideshow controls if gallery is composed by one item only if(gallery.slideshowNav.length > 0) { for(var i = 0; i < gallery.slideshowNav.length; i++) { bool ? Util.addClass(gallery.slideshowNav[i], 'is-hidden') : Util.removeClass(gallery.slideshowNav[i], 'is-hidden'); } } }; function initModal(gallery) { Util.addClass(gallery.element, 'exp-lightbox--no-transition'); // add no-transition class to lightbox - used to select the first visible slide gallery.element.addEventListener('modalIsClose', function(event){ // add no-transition class Util.addClass(gallery.element, 'exp-lightbox--no-transition'); gallery.imgMorph.style = ''; }); // trigger modal lightbox gallery.gallery.addEventListener('click', function(event){ openModalLightbox(gallery, event); }); }; function initModalEvents(gallery) { if(gallery.zoomImg) { // image zoom gallery.slideshow.addEventListener('click', function(event){ if(event.target.tagName.toLowerCase() == 'img' && event.target.closest('.js-slideshow__item') && !gallery.modalSwiping) modalZoomImg(gallery, event.target); }); gallery.zoomImg.addEventListener('click', function(event){ modalZoomImg(gallery, false); }); gallery.element.addEventListener('modalIsClose', function(event){ modalZoomImg(gallery, false); // close zoom-in image if open closeModalAnimation(gallery); }); } if(!gallery.slideshowObj) return; if(gallery.slideshowObj.options.swipe) { // close gallery when you swipeUp/SwipeDown gallery.slideshowObj.element.addEventListener('swipeUp', function(event){ closeModal(gallery); }); gallery.slideshowObj.element.addEventListener('swipeDown', function(event){ closeModal(gallery); }); } if(gallery.zoomImg && gallery.slideshowObj.options.swipe) { gallery.slideshowObj.element.addEventListener('swipeLeft', function(event){ gallery.modalSwiping = true; }); gallery.slideshowObj.element.addEventListener('swipeRight', function(event){ gallery.modalSwiping = true; }); gallery.slideshowObj.element.addEventListener('newItemVisible', function(event){ gallery.modalSwiping = false; }); } }; function openModalLightbox(gallery, event) { var item = event.target.closest('.js-exp-gallery__item'); if(!item) return; // reset slideshow items visibility resetSlideshowItemsVisibility(gallery); gallery.selectedSlide = Util.getIndexInArray(gallery.galleryItems, item); setSelectedItem(gallery); lazyLoadSlide(gallery); if(animationSupported) { // start expanding animation window.requestAnimationFrame(function(){ animateSelectedImage(gallery); openModal(gallery, item); }); } else { // no expanding animation -> show modal openModal(gallery, item); Util.removeClass(gallery.element, 'exp-lightbox--no-transition'); } // menu element - trigger new slide event triggerMenuEvent(gallery); }; function resetSlideshowItemsVisibility(gallery) { var index = 0; for(var i = 0; i < gallery.galleryItems.length; i++) { var itemVisible = isVisible(gallery.galleryItems[i]); if(itemVisible) { index = index + 1; Util.removeClass(gallery.slides[i], 'is-hidden'); } else { Util.addClass(gallery.slides[i], 'is-hidden'); } } toggleSlideshowElements(gallery, index < 2); }; function setSelectedItem(gallery) { // if a specific slide was selected -> make sure to show that item first var lastSelected = gallery.slideshow.getElementsByClassName('slideshow__item--selected'); if(lastSelected.length > 0 ) Util.removeClass(lastSelected[0], 'slideshow__item--selected'); Util.addClass(gallery.slides[gallery.selectedSlide], 'slideshow__item--selected'); if(gallery.slideshowObj) gallery.slideshowObj.selectedSlide = gallery.selectedSlide; }; function openModal(gallery, item) { gallery.element.dispatchEvent(new CustomEvent('openModal', {detail: item})); gallery.modalSwiping = false; }; function closeModal(gallery) { gallery.modalSwiping = true; modalZoomImg(gallery, false); gallery.element.dispatchEvent(new CustomEvent('closeModal')); }; function closeModalAnimation(gallery) { // modal is already closing -> start image closing animation gallery.selectedSlide = gallery.slideshowObj ? gallery.slideshowObj.selectedSlide : 0; // on close - make sure last selected image (of the gallery) is in the viewport var boundingRect = gallery.galleryItems[gallery.selectedSlide].getBoundingClientRect(); if(boundingRect.top < 0 || boundingRect.top > window.innerHeight) { var windowScrollTop = window.scrollY || document.documentElement.scrollTop; window.scrollTo(0, boundingRect.top + windowScrollTop); } // animate on close animateSelectedImage(gallery, true); }; function modalZoomImg(gallery, img) { // toggle zoom-in image if(!gallery.zoomImg) return; var bool = false; if(img) { // open zoom-in image gallery.originImg = img; gallery.zoomImg.children[0].setAttribute('src', img.getAttribute('src')); bool = true; } (animationSupported) ? requestAnimationFrame(function(){animateZoomImg(gallery, bool)}) : Util.toggleClass(gallery.zoomImg, 'exp-lightbox__zoom--is-visible', bool); }; function animateZoomImg(gallery, bool) { if(!gallery.originImg) return; var originImgPosition = gallery.originImg.getBoundingClientRect(), originStyle = 'translateX('+originImgPosition.left+'px) translateY('+(originImgPosition.top + gallery.zoomImg.scrollTop)+'px) scale('+ originImgPosition.width/gallery.zoomImg.scrollWidth+')', finalStyle = 'scale(1)'; if(bool) { gallery.zoomImg.children[0].style.transform = originStyle; } else { gallery.zoomImg.addEventListener('transitionend', function cb(){ Util.addClass(gallery.zoomImg, 'exp-lightbox__zoom--no-transition'); gallery.zoomImg.scrollTop = 0; gallery.zoomImg.removeEventListener('transitionend', cb); }); } setTimeout(function(){ Util.removeClass(gallery.zoomImg, 'exp-lightbox__zoom--no-transition'); Util.toggleClass(gallery.zoomImg, 'exp-lightbox__zoom--is-visible', bool); gallery.zoomImg.children[0].style.transform = (bool) ? finalStyle : originStyle; }, 50); }; function animateSelectedImage(gallery, bool) { // create morphing image effect var imgInit = gallery.galleryItems[gallery.selectedSlide].getElementsByTagName('img')[0], imgInitPosition = imgInit.getBoundingClientRect(), imgFinal = gallery.slides[gallery.selectedSlide].getElementsByTagName('img')[0], imgFinalPosition = imgFinal.getBoundingClientRect(); if(bool) { runAnimation(gallery, imgInit, imgInitPosition, imgFinal, imgFinalPosition, bool); } else { imgFinal.style.visibility = 'hidden'; gallery.animationRunning = false; var image = new Image(); image.onload = function () { if(gallery.animationRunning) return; imgFinalPosition = imgFinal.getBoundingClientRect(); runAnimation(gallery, imgInit, imgInitPosition, imgFinal, imgFinalPosition, bool); } image.src = imgFinal.getAttribute('data-src') ? imgFinal.getAttribute('data-src') : imgFinal.getAttribute('src'); if(image.complete) { gallery.animationRunning = true; imgFinalPosition = imgFinal.getBoundingClientRect(); runAnimation(gallery, imgInit, imgInitPosition, imgFinal, imgFinalPosition, bool); } } }; function runAnimation(gallery, imgInit, imgInitPosition, imgFinal, imgFinalPosition, bool) { // retrieve all animation params var scale = imgFinalPosition.width > imgFinalPosition.height ? imgFinalPosition.height/imgInitPosition.height : imgFinalPosition.width/imgInitPosition.width; var initHeight = imgFinalPosition.width > imgFinalPosition.height ? imgInitPosition.height : imgFinalPosition.height/scale, initWidth = imgFinalPosition.width > imgFinalPosition.height ? imgFinalPosition.width/scale : imgInitPosition.width; var initTranslateY = (imgInitPosition.height - initHeight)/2, initTranslateX = (imgInitPosition.width - initWidth)/2, initTop = imgInitPosition.top + initTranslateY, initLeft = imgInitPosition.left + initTranslateX; // get final states var translateX = imgFinalPosition.left - imgInitPosition.left, translateY = imgFinalPosition.top - imgInitPosition.top; var finTranslateX = translateX - initTranslateX, finTranslateY = translateY - initTranslateY; var initScaleX = imgInitPosition.width/initWidth, initScaleY = imgInitPosition.height/initHeight, finScaleX = 1, finScaleY = 1; if(bool) { // update params if this is a closing animation scale = 1/scale; finScaleX = initScaleX; finScaleY = initScaleY; initScaleX = 1; initScaleY = 1; finTranslateX = -1*finTranslateX; finTranslateY = -1*finTranslateY; initTop = imgFinalPosition.top; initLeft = imgFinalPosition.left; initHeight = imgFinalPosition.height; initWidth = imgFinalPosition.width; } if(!bool) { imgFinal.style.visibility = ''; // reset visibility } // set initial status gallery.imgMorph.setAttribute('style', 'height: '+initHeight+'px; width: '+initWidth+'px; top: '+initTop+'px; left: '+initLeft+'px;'); gallery.imgMorphSVG.setAttribute('viewbox', '0 0 '+initWidth+' '+initHeight); Util.setAttributes(gallery.imgMorphImg, {'xlink:href': imgInit.getAttribute('src'), 'href': imgInit.getAttribute('src')}); Util.setAttributes(gallery.imgMorphRect, {'style': 'height: '+initHeight+'px; width: '+initWidth+'px;', 'transform': 'translate('+(initWidth/2)*(1 - initScaleX)+' '+(initHeight/2)*(1 - initScaleY)+') scale('+initScaleX+','+initScaleY+')'}); // reveal image and start animation Util.addClass(gallery.imgMorph, 'exp-lightbox__clone-img-wrapper--is-visible'); Util.addClass(gallery.slideshowList, 'slideshow__content--is-hidden'); Util.addClass(gallery.galleryItems[gallery.selectedSlide], 'exp-gallery-item-hidden'); gallery.imgMorph.addEventListener('transitionend', function cb(event){ // reset elements once animation is over if(event.propertyName.indexOf('transform') < 0) return; Util.removeClass(gallery.element, 'exp-lightbox--no-transition'); Util.removeClass(gallery.imgMorph, 'exp-lightbox__clone-img-wrapper--is-visible'); Util.removeClass(gallery.slideshowList, 'slideshow__content--is-hidden'); gallery.imgMorph.removeAttribute('style'); gallery.imgMorphRect.removeAttribute('style'); gallery.imgMorphRect.removeAttribute('transform'); gallery.imgMorphImg.removeAttribute('href'); gallery.imgMorphImg.removeAttribute('xlink:href'); Util.removeClass(gallery.galleryItems[gallery.selectedSlide], 'exp-gallery-item-hidden'); gallery.imgMorph.removeEventListener('transitionend', cb); }); // trigger expanding/closing animation gallery.imgMorph.style.transform = 'translateX('+finTranslateX+'px) translateY('+finTranslateY+'px) scale('+scale+')'; animateRectScale(gallery.imgMorphRect, initScaleX, initScaleY, finScaleX, finScaleY, initWidth, initHeight); }; function animateRectScale(rect, scaleX, scaleY, finScaleX, finScaleY, width, height) { var currentTime = null, duration = parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--exp-gallery-animation-duration'))*1000 || 300; var animateScale = function(timestamp){ if (!currentTime) currentTime = timestamp; var progress = timestamp - currentTime; if(progress > duration) progress = duration; var valX = easeOutQuad(progress, scaleX, finScaleX-scaleX, duration), valY = easeOutQuad(progress, scaleY, finScaleY-scaleY, duration); rect.setAttribute('transform', 'translate('+(width/2)*(1 - valX)+' '+(height/2)*(1 - valY)+') scale('+valX+','+valY+')'); if(progress < duration) { window.requestAnimationFrame(animateScale); } }; function easeOutQuad(t, b, c, d) { t /= d; return -c * t*(t-2) + b; }; window.requestAnimationFrame(animateScale); }; function keyboardNavigateLightbox(gallery, direction) { if(!Util.hasClass(gallery.element, 'modal--is-visible')) return; if(!document.activeElement.closest('.js-exp-lightbox__body') && document.activeElement.closest('.js-modal')) return; if(!gallery.slideshowObj) return; (direction == 'next') ? gallery.slideshowObj.showNext() : gallery.slideshowObj.showPrev(); }; function triggerMenuEvent(gallery) { if(gallery.menuBar.length < 1) return; var event = new CustomEvent('update-menu', {detail: { index: gallery.selectedSlide, item: gallery.slides[gallery.selectedSlide] }}); gallery.menuBar[0].dispatchEvent(event); }; function isVisible(element) { return (element.offsetWidth || element.offsetHeight || element.getClientRects().length); }; window.ExpGallery = ExpGallery; // init ExpGallery objects var expGalleries = document.getElementsByClassName('js-exp-lightbox'), animationSupported = window.requestAnimationFrame && !Util.osHasReducedMotion(); if( expGalleries.length > 0 ) { var expGalleriesArray = []; for( var i = 0; i < expGalleries.length; i++) { (function(i){ expGalleriesArray.push(new ExpGallery(expGalleries[i]));})(i); // Lightbox gallery navigation with keyboard window.addEventListener('keydown', function(event){ if(event.keyCode && event.keyCode == 39 || event.key && event.key.toLowerCase() == 'arrowright') { updateLightbox('next'); } else if(event.keyCode && event.keyCode == 37 || event.key && event.key.toLowerCase() == 'arrowleft') { updateLightbox('prev'); } }); function updateLightbox(direction) { for( var i = 0; i < expGalleriesArray.length; i++) { (function(i){keyboardNavigateLightbox(expGalleriesArray[i], direction);})(i); }; }; } } }());