diff options
Diffstat (limited to 'docs/assets/js/baguetteBox.js')
-rw-r--r-- | docs/assets/js/baguetteBox.js | 710 |
1 files changed, 710 insertions, 0 deletions
diff --git a/docs/assets/js/baguetteBox.js b/docs/assets/js/baguetteBox.js new file mode 100644 index 0000000..5303fae --- /dev/null +++ b/docs/assets/js/baguetteBox.js @@ -0,0 +1,710 @@ +/*! + * baguetteBox.js + * @author feimosi + * @version 1.8.2 + * @url https://github.com/feimosi/baguetteBox.js + */ + +/* global define, module */ + +(function (root, factory) { + 'use strict'; + if (typeof define === 'function' && define.amd) { + define(factory); + } else if (typeof exports === 'object') { + module.exports = factory(); + } else { + root.baguetteBox = factory(); + } +}(this, function () { + 'use strict'; + + // SVG shapes used on the buttons + var leftArrow = '<svg width="44" height="60">' + + '<polyline points="30 10 10 30 30 50" stroke="rgba(255,255,255,0.5)" stroke-width="4"' + + 'stroke-linecap="butt" fill="none" stroke-linejoin="round"/>' + + '</svg>', + rightArrow = '<svg width="44" height="60">' + + '<polyline points="14 10 34 30 14 50" stroke="rgba(255,255,255,0.5)" stroke-width="4"' + + 'stroke-linecap="butt" fill="none" stroke-linejoin="round"/>' + + '</svg>', + closeX = '<svg width="30" height="30">' + + '<g stroke="rgb(160,160,160)" stroke-width="4">' + + '<line x1="5" y1="5" x2="25" y2="25"/>' + + '<line x1="5" y1="25" x2="25" y2="5"/>' + + '</g></svg>'; + // Global options and their defaults + var options = {}, + defaults = { + captions: true, + fullScreen: false, + noScrollbars: false, + titleTag: false, + buttons: 'auto', + async: false, + preload: 2, + animation: 'slideIn', + afterShow: null, + afterHide: null, + // callback when image changes with `currentIndex` and `imagesElements.length` as parameters + onChange: null, + overlayBackgroundColor: 'rgba(0,0,0,.8)' + }; + // Object containing information about features compatibility + var supports = {}; + // DOM Elements references + var overlay, slider, previousButton, nextButton, closeButton; + // An array with all images in the current gallery + var currentGallery = []; + // Current image index inside the slider + var currentIndex = 0; + // Touch event start position (for slide gesture) + var touch = {}; + // If set to true ignore touch events because animation was already fired + var touchFlag = false; + // Regex pattern to match image files + var regex = /.+\.(gif|jpe?g|png|webp)/i; + // Object of all used galleries + var data = {}; + // Array containing temporary images DOM elements + var imagesElements = []; + // The last focused element before opening the overlay + var documentLastFocus = null; + var overlayClickHandler = function(event) { + // Close the overlay when user clicks directly on the background + if (event.target.id.indexOf('baguette-img') !== -1) { + hideOverlay(); + } + }; + var previousButtonClickHandler = function(event) { + event.stopPropagation ? event.stopPropagation() : event.cancelBubble = true; // jshint ignore:line + showPreviousImage(); + }; + var nextButtonClickHandler = function(event) { + event.stopPropagation ? event.stopPropagation() : event.cancelBubble = true; // jshint ignore:line + showNextImage(); + }; + var closeButtonClickHandler = function(event) { + event.stopPropagation ? event.stopPropagation() : event.cancelBubble = true; // jshint ignore:line + hideOverlay(); + }; + var touchstartHandler = function(event) { + touch.count++; + if (touch.count > 1) { + touch.multitouch = true; + } + // Save x and y axis position + touch.startX = event.changedTouches[0].pageX; + touch.startY = event.changedTouches[0].pageY; + }; + var touchmoveHandler = function(event) { + // If action was already triggered or multitouch return + if (touchFlag || touch.multitouch) { + return; + } + event.preventDefault ? event.preventDefault() : event.returnValue = false; // jshint ignore:line + var touchEvent = event.touches[0] || event.changedTouches[0]; + // Move at least 40 pixels to trigger the action + if (touchEvent.pageX - touch.startX > 40) { + touchFlag = true; + showPreviousImage(); + } else if (touchEvent.pageX - touch.startX < -40) { + touchFlag = true; + showNextImage(); + // Move 100 pixels up to close the overlay + } else if (touch.startY - touchEvent.pageY > 100) { + hideOverlay(); + } + }; + var touchendHandler = function() { + touch.count--; + if (touch.count <= 0) { + touch.multitouch = false; + } + touchFlag = false; + }; + + var trapFocusInsideOverlay = function(event) { + if (overlay.style.display === 'block' && (overlay.contains && !overlay.contains(event.target))) { + event.stopPropagation(); + initFocus(); + } + }; + + // forEach polyfill for IE8 + // http://stackoverflow.com/a/14827443/1077846 + /* jshint ignore:start */ + if (![].forEach) { + Array.prototype.forEach = function(callback, thisArg) { + for (var i = 0; i < this.length; i++) { + callback.call(thisArg, this[i], i, this); + } + }; + } + + // filter polyfill for IE8 + // https://gist.github.com/eliperelman/1031656 + if (![].filter) { + Array.prototype.filter = function(a, b, c, d, e) { + c = this; + d = []; + for (e = 0; e < c.length; e++) + a.call(b, c[e], e, c) && d.push(c[e]); + return d; + }; + } + /* jshint ignore:end */ + + // Script entry point + function run(selector, userOptions) { + // Fill supports object + supports.transforms = testTransformsSupport(); + supports.svg = testSVGSupport(); + + buildOverlay(); + removeFromCache(selector); + bindImageClickListeners(selector, userOptions); + } + + function bindImageClickListeners(selector, userOptions) { + // For each gallery bind a click event to every image inside it + var galleryNodeList = document.querySelectorAll(selector); + var selectorData = { + galleries: [], + nodeList: galleryNodeList + }; + data[selector] = selectorData; + + [].forEach.call(galleryNodeList, function(galleryElement) { + if (userOptions && userOptions.filter) { + regex = userOptions.filter; + } + + // Get nodes from gallery elements or single-element galleries + var tagsNodeList = []; + if (galleryElement.tagName === 'A') { + tagsNodeList = [galleryElement]; + } else { + tagsNodeList = galleryElement.getElementsByTagName('a'); + } + + // Filter 'a' elements from those not linking to images + tagsNodeList = [].filter.call(tagsNodeList, function(element) { + return regex.test(element.href); + }); + if (tagsNodeList.length === 0) { + return; + } + + var gallery = []; + [].forEach.call(tagsNodeList, function(imageElement, imageIndex) { + var imageElementClickHandler = function(event) { + event.preventDefault ? event.preventDefault() : event.returnValue = false; // jshint ignore:line + prepareOverlay(gallery, userOptions); + showOverlay(imageIndex); + }; + var imageItem = { + eventHandler: imageElementClickHandler, + imageElement: imageElement + }; + bind(imageElement, 'click', imageElementClickHandler); + gallery.push(imageItem); + }); + selectorData.galleries.push(gallery); + }); + } + + function clearCachedData() { + for (var selector in data) { + if (data.hasOwnProperty(selector)) { + removeFromCache(selector); + } + } + } + + function removeFromCache(selector) { + if (!data.hasOwnProperty(selector)) { + return; + } + var galleries = data[selector].galleries; + [].forEach.call(galleries, function(gallery) { + [].forEach.call(gallery, function(imageItem) { + unbind(imageItem.imageElement, 'click', imageItem.eventHandler); + }); + + if (currentGallery === gallery) { + currentGallery = []; + } + }); + + delete data[selector]; + } + + function buildOverlay() { + overlay = getByID('baguetteBox-overlay'); + // Check if the overlay already exists + if (overlay) { + slider = getByID('baguetteBox-slider'); + previousButton = getByID('previous-button'); + nextButton = getByID('next-button'); + closeButton = getByID('close-button'); + return; + } + // Create overlay element + overlay = create('div'); + overlay.setAttribute('role', 'dialog'); + overlay.id = 'baguetteBox-overlay'; + document.getElementsByTagName('body')[0].appendChild(overlay); + // Create gallery slider element + slider = create('div'); + slider.id = 'baguetteBox-slider'; + overlay.appendChild(slider); + // Create all necessary buttons + previousButton = create('button'); + previousButton.setAttribute('type', 'button'); + previousButton.id = 'previous-button'; + previousButton.setAttribute('aria-label', 'Previous'); + previousButton.innerHTML = supports.svg ? leftArrow : '<'; + overlay.appendChild(previousButton); + + nextButton = create('button'); + nextButton.setAttribute('type', 'button'); + nextButton.id = 'next-button'; + nextButton.setAttribute('aria-label', 'Next'); + nextButton.innerHTML = supports.svg ? rightArrow : '>'; + overlay.appendChild(nextButton); + + closeButton = create('button'); + closeButton.setAttribute('type', 'button'); + closeButton.id = 'close-button'; + closeButton.setAttribute('aria-label', 'Close'); + closeButton.innerHTML = supports.svg ? closeX : '×'; + overlay.appendChild(closeButton); + + previousButton.className = nextButton.className = closeButton.className = 'baguetteBox-button'; + + bindEvents(); + } + + function keyDownHandler(event) { + switch (event.keyCode) { + case 37: // Left arrow + showPreviousImage(); + break; + case 39: // Right arrow + showNextImage(); + break; + case 27: // Esc + hideOverlay(); + break; + } + } + + function bindEvents() { + bind(overlay, 'click', overlayClickHandler); + bind(previousButton, 'click', previousButtonClickHandler); + bind(nextButton, 'click', nextButtonClickHandler); + bind(closeButton, 'click', closeButtonClickHandler); + bind(overlay, 'touchstart', touchstartHandler); + bind(overlay, 'touchmove', touchmoveHandler); + bind(overlay, 'touchend', touchendHandler); + bind(document, 'focus', trapFocusInsideOverlay, true); + } + + function unbindEvents() { + unbind(overlay, 'click', overlayClickHandler); + unbind(previousButton, 'click', previousButtonClickHandler); + unbind(nextButton, 'click', nextButtonClickHandler); + unbind(closeButton, 'click', closeButtonClickHandler); + unbind(overlay, 'touchstart', touchstartHandler); + unbind(overlay, 'touchmove', touchmoveHandler); + unbind(overlay, 'touchend', touchendHandler); + unbind(document, 'focus', trapFocusInsideOverlay, true); + } + + function prepareOverlay(gallery, userOptions) { + // If the same gallery is being opened prevent from loading it once again + if (currentGallery === gallery) { + return; + } + currentGallery = gallery; + // Update gallery specific options + setOptions(userOptions); + // Empty slider of previous contents (more effective than .innerHTML = "") + while (slider.firstChild) { + slider.removeChild(slider.firstChild); + } + imagesElements.length = 0; + + var imagesFiguresIds = []; + var imagesCaptionsIds = []; + // Prepare and append images containers and populate figure and captions IDs arrays + for (var i = 0, fullImage; i < gallery.length; i++) { + fullImage = create('div'); + fullImage.className = 'full-image'; + fullImage.id = 'baguette-img-' + i; + imagesElements.push(fullImage); + + imagesFiguresIds.push('baguetteBox-figure-' + i); + imagesCaptionsIds.push('baguetteBox-figcaption-' + i); + slider.appendChild(imagesElements[i]); + } + overlay.setAttribute('aria-labelledby', imagesFiguresIds.join(' ')); + overlay.setAttribute('aria-describedby', imagesCaptionsIds.join(' ')); + } + + function setOptions(newOptions) { + if (!newOptions) { + newOptions = {}; + } + // Fill options object + for (var item in defaults) { + options[item] = defaults[item]; + if (typeof newOptions[item] !== 'undefined') { + options[item] = newOptions[item]; + } + } + /* Apply new options */ + // Change transition for proper animation + slider.style.transition = slider.style.webkitTransition = (options.animation === 'fadeIn' ? 'opacity .4s ease' : + options.animation === 'slideIn' ? '' : 'none'); + // Hide buttons if necessary + if (options.buttons === 'auto' && ('ontouchstart' in window || currentGallery.length === 1)) { + options.buttons = false; + } + // Set buttons style to hide or display them + previousButton.style.display = nextButton.style.display = (options.buttons ? '' : 'none'); + // Set overlay color + try { + overlay.style.backgroundColor = options.overlayBackgroundColor; + } catch (e) { + // Silence the error and continue + } + } + + function showOverlay(chosenImageIndex) { + if (options.noScrollbars) { + document.documentElement.style.overflowY = 'hidden'; + document.body.style.overflowY = 'scroll'; + } + if (overlay.style.display === 'block') { + return; + } + + bind(document, 'keydown', keyDownHandler); + currentIndex = chosenImageIndex; + touch = { + count: 0, + startX: null, + startY: null + }; + loadImage(currentIndex, function() { + preloadNext(currentIndex); + preloadPrev(currentIndex); + }); + + updateOffset(); + overlay.style.display = 'block'; + if (options.fullScreen) { + enterFullScreen(); + } + // Fade in overlay + setTimeout(function() { + overlay.className = 'visible'; + if (options.afterShow) { + options.afterShow(); + } + }, 50); + if (options.onChange) { + options.onChange(currentIndex, imagesElements.length); + } + documentLastFocus = document.activeElement; + initFocus(); + } + + function initFocus() { + if (options.buttons) { + previousButton.focus(); + } else { + closeButton.focus(); + } + } + + function enterFullScreen() { + if (overlay.requestFullscreen) { + overlay.requestFullscreen(); + } else if (overlay.webkitRequestFullscreen) { + overlay.webkitRequestFullscreen(); + } else if (overlay.mozRequestFullScreen) { + overlay.mozRequestFullScreen(); + } + } + + function exitFullscreen() { + if (document.exitFullscreen) { + document.exitFullscreen(); + } else if (document.mozCancelFullScreen) { + document.mozCancelFullScreen(); + } else if (document.webkitExitFullscreen) { + document.webkitExitFullscreen(); + } + } + + function hideOverlay() { + if (options.noScrollbars) { + document.documentElement.style.overflowY = 'auto'; + document.body.style.overflowY = 'auto'; + } + if (overlay.style.display === 'none') { + return; + } + + unbind(document, 'keydown', keyDownHandler); + // Fade out and hide the overlay + overlay.className = ''; + setTimeout(function() { + overlay.style.display = 'none'; + exitFullscreen(); + if (options.afterHide) { + options.afterHide(); + } + }, 500); + documentLastFocus.focus(); + } + + function loadImage(index, callback) { + var imageContainer = imagesElements[index]; + var galleryItem = currentGallery[index]; + + // Return if the index exceeds prepared images in the overlay + // or if the current gallery has been changed / closed + if (imageContainer === undefined || galleryItem === undefined) { + return; + } + + // If image is already loaded run callback and return + if (imageContainer.getElementsByTagName('img')[0]) { + if (callback) { + callback(); + } + return; + } + + // Get element reference, optional caption and source path + var imageElement = galleryItem.imageElement; + var thumbnailElement = imageElement.getElementsByTagName('img')[0]; + var imageCaption = typeof options.captions === 'function' ? + options.captions.call(currentGallery, imageElement) : + imageElement.getAttribute('data-caption') || imageElement.title; + var imageSrc = getImageSrc(imageElement); + + // Prepare figure element + var figure = create('figure'); + figure.id = 'baguetteBox-figure-' + index; + figure.innerHTML = '<div class="baguetteBox-spinner">' + + '<div class="baguetteBox-double-bounce1"></div>' + + '<div class="baguetteBox-double-bounce2"></div>' + + '</div>'; + // Insert caption if available + if (options.captions && imageCaption) { + var figcaption = create('figcaption'); + figcaption.id = 'baguetteBox-figcaption-' + index; + figcaption.innerHTML = imageCaption; + figure.appendChild(figcaption); + } + imageContainer.appendChild(figure); + + // Prepare gallery img element + var image = create('img'); + image.onload = function() { + // Remove loader element + var spinner = document.querySelector('#baguette-img-' + index + ' .baguetteBox-spinner'); + figure.removeChild(spinner); + if (!options.async && callback) { + callback(); + } + }; + image.setAttribute('src', imageSrc); + image.alt = thumbnailElement ? thumbnailElement.alt || '' : ''; + if (options.titleTag && imageCaption) { + image.title = imageCaption; + } + figure.appendChild(image); + + // Run callback + if (options.async && callback) { + callback(); + } + } + + // Get image source location, mostly used for responsive images + function getImageSrc(image) { + // Set default image path from href + var result = image.href; + // If dataset is supported find the most suitable image + if (image.dataset) { + var srcs = []; + // Get all possible image versions depending on the resolution + for (var item in image.dataset) { + if (item.substring(0, 3) === 'at-' && !isNaN(item.substring(3))) { + srcs[item.replace('at-', '')] = image.dataset[item]; + } + } + // Sort resolutions ascending + var keys = Object.keys(srcs).sort(function(a, b) { + return parseInt(a, 10) < parseInt(b, 10) ? -1 : 1; + }); + // Get real screen resolution + var width = window.innerWidth * window.devicePixelRatio; + // Find the first image bigger than or equal to the current width + var i = 0; + while (i < keys.length - 1 && keys[i] < width) { + i++; + } + result = srcs[keys[i]] || result; + } + return result; + } + + // Return false at the right end of the gallery + function showNextImage() { + var returnValue; + // Check if next image exists + if (currentIndex <= imagesElements.length - 2) { + currentIndex++; + updateOffset(); + preloadNext(currentIndex); + returnValue = true; + } else if (options.animation) { + slider.className = 'bounce-from-right'; + setTimeout(function() { + slider.className = ''; + }, 400); + returnValue = false; + } + if (options.onChange) { + options.onChange(currentIndex, imagesElements.length); + } + return returnValue; + } + + // Return false at the left end of the gallery + function showPreviousImage() { + var returnValue; + // Check if previous image exists + if (currentIndex >= 1) { + currentIndex--; + updateOffset(); + preloadPrev(currentIndex); + returnValue = true; + } else if (options.animation) { + slider.className = 'bounce-from-left'; + setTimeout(function() { + slider.className = ''; + }, 400); + returnValue = false; + } + if (options.onChange) { + options.onChange(currentIndex, imagesElements.length); + } + return returnValue; + } + + function updateOffset() { + var offset = -currentIndex * 100 + '%'; + if (options.animation === 'fadeIn') { + slider.style.opacity = 0; + setTimeout(function() { + /* jshint -W030 */ + supports.transforms ? + slider.style.transform = slider.style.webkitTransform = 'translate3d(' + offset + ',0,0)' + : slider.style.left = offset; + slider.style.opacity = 1; + }, 400); + } else { + /* jshint -W030 */ + supports.transforms ? + slider.style.transform = slider.style.webkitTransform = 'translate3d(' + offset + ',0,0)' + : slider.style.left = offset; + } + } + + // CSS 3D Transforms test + function testTransformsSupport() { + var div = create('div'); + return typeof div.style.perspective !== 'undefined' || typeof div.style.webkitPerspective !== 'undefined'; + } + + // Inline SVG test + function testSVGSupport() { + var div = create('div'); + div.innerHTML = '<svg/>'; + return (div.firstChild && div.firstChild.namespaceURI) === 'http://www.w3.org/2000/svg'; + } + + function preloadNext(index) { + if (index - currentIndex >= options.preload) { + return; + } + loadImage(index + 1, function() { + preloadNext(index + 1); + }); + } + + function preloadPrev(index) { + if (currentIndex - index >= options.preload) { + return; + } + loadImage(index - 1, function() { + preloadPrev(index - 1); + }); + } + + function bind(element, event, callback, useCapture) { + if (element.addEventListener) { + element.addEventListener(event, callback, useCapture); + } else { + // IE8 fallback + element.attachEvent('on' + event, function(event) { + // `event` and `event.target` are not provided in IE8 + event = event || window.event; + event.target = event.target || event.srcElement; + callback(event); + }); + } + } + + function unbind(element, event, callback, useCapture) { + if (element.removeEventListener) { + element.removeEventListener(event, callback, useCapture); + } else { + // IE8 fallback + element.detachEvent('on' + event, callback); + } + } + + function getByID(id) { + return document.getElementById(id); + } + + function create(element) { + return document.createElement(element); + } + + function destroyPlugin() { + unbindEvents(); + clearCachedData(); + unbind(document, 'keydown', keyDownHandler); + document.getElementsByTagName('body')[0].removeChild(document.getElementById('baguetteBox-overlay')); + data = {}; + currentGallery = []; + currentIndex = 0; + } + + return { + run: run, + destroy: destroyPlugin, + showNext: showNextImage, + showPrevious: showPreviousImage + }; +})); |