PluginProbe
Photonic Gallery & Lightbox for Flickr, SmugMug & Others / 3.34
Photonic Gallery & Lightbox for Flickr, SmugMug & Others v3.34
3.37 3.36 3.35 3.34 3.33 2.19 2.20 2.21 2.22 2.23 2.24 2.25 2.26 2.27 2.28 2.29 2.30 2.31 2.32 2.33 2.34 2.40 2.41 2.42 2.43 All 142 releases
photonic / include / ext / baguettebox / baguettebox.js

baguettebox.js in Photonic Gallery & Lightbox for Flickr, SmugMug & Others 3.34, at include/ext/baguettebox/baguettebox.js

884 lines 25.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /*!
2 * baguetteBox.js
3 * @author feimosi
4 * @version 1.11.1
5 * @url https://github.com/feimosi/baguetteBox.js
6 */
7
8 /* global define, module */
9
10 (function (root, factory) {
11 'use strict';
12 if (typeof define === 'function' && define.amd) {
13 define(factory);
14 }
15 else if (typeof exports === 'object') {
16 module.exports = factory();
17 }
18 else {
19 root.baguetteBox = factory();
20 }
21 }(this, function () {
22 'use strict';
23
24 // SVG shapes used on the buttons
25 var leftArrow = '<svg width="44" height="60">' +
26 '<polyline points="30 10 10 30 30 50" stroke="rgba(255,255,255,0.5)" stroke-width="4"' +
27 'stroke-linecap="butt" fill="none" stroke-linejoin="round"/>' +
28 '</svg>',
29 rightArrow = '<svg width="44" height="60">' +
30 '<polyline points="14 10 34 30 14 50" stroke="rgba(255,255,255,0.5)" stroke-width="4"' +
31 'stroke-linecap="butt" fill="none" stroke-linejoin="round"/>' +
32 '</svg>',
33 closeX = '<svg width="30" height="30">' +
34 '<g stroke="rgb(160,160,160)" stroke-width="4">' +
35 '<line x1="5" y1="5" x2="25" y2="25"/>' +
36 '<line x1="5" y1="25" x2="25" y2="5"/>' +
37 '</g></svg>';
38 // Global options and their defaults
39 var options = {},
40 defaults = {
41 captions: true,
42 buttons: 'auto',
43 fullScreen: false,
44 noScrollbars: false,
45 bodyClass: 'baguetteBox-open',
46 titleTag: false,
47 async: false,
48 preload: 2,
49 animation: 'slideIn',
50 afterShow: null,
51 afterHide: null,
52 onChange: null,
53 overlayBackgroundColor: 'rgba(0,0,0,.8)'
54 };
55 // Object containing information about features compatibility
56 var supports = {};
57 // DOM Elements references
58 var overlay, slider, previousButton, nextButton, closeButton;
59 // An array with all images in the current gallery
60 var currentGallery = [];
61 // Current image index inside the slider
62 var currentIndex = 0;
63 // Visibility of the overlay
64 var isOverlayVisible = false;
65 // Touch event start position (for slide gesture)
66 var touch = {};
67 // If set to true ignore touch events because animation was already fired
68 var touchFlag = false;
69
70 // Regex pattern to match image & video files
71 var regex = /.+\.(gif|jpe?g|png|webp|mp4|webm)/i;
72 // Pattern to match only videos
73 var videoRegex = /.+\.(mp4|webm)/i;
74
75 // Object of all used galleries
76 var data = {};
77 // Array containing temporary images DOM elements
78 var imagesElements = [];
79 // The last focused element before opening the overlay
80 var documentLastFocus = null;
81 var overlayClickHandler = function (event) {
82 // Close the overlay when user clicks directly on the background
83 if (event.target.id.indexOf('baguette-img') !== -1) {
84 hideOverlay();
85 }
86 };
87 var previousButtonClickHandler = function (event) {
88 event.stopPropagation ? event.stopPropagation() : event.cancelBubble = true; // eslint-disable-line no-unused-expressions
89 showPreviousImage();
90 };
91 var nextButtonClickHandler = function (event) {
92 event.stopPropagation ? event.stopPropagation() : event.cancelBubble = true; // eslint-disable-line no-unused-expressions
93 showNextImage();
94 };
95 var closeButtonClickHandler = function (event) {
96 event.stopPropagation ? event.stopPropagation() : event.cancelBubble = true; // eslint-disable-line no-unused-expressions
97 hideOverlay();
98 };
99 var touchstartHandler = function (event) {
100 touch.count++;
101 if (touch.count > 1) {
102 touch.multitouch = true;
103 }
104 // Save x and y axis position
105 touch.startX = event.changedTouches[0].pageX;
106 touch.startY = event.changedTouches[0].pageY;
107 };
108 var touchmoveHandler = function (event) {
109 // If action was already triggered or multitouch return
110 if (touchFlag || touch.multitouch) {
111 return;
112 }
113 event.preventDefault ? event.preventDefault() : event.returnValue = false; // eslint-disable-line no-unused-expressions
114 var touchEvent = event.touches[0] || event.changedTouches[0];
115 // Move at least 40 pixels to trigger the action
116 if (touchEvent.pageX - touch.startX > 40) {
117 touchFlag = true;
118 showPreviousImage();
119 }
120 else if (touchEvent.pageX - touch.startX < -40) {
121 touchFlag = true;
122 showNextImage();
123 // Move 100 pixels up to close the overlay
124 }
125 else if (touch.startY - touchEvent.pageY > 100) {
126 hideOverlay();
127 }
128 };
129 var touchendHandler = function () {
130 touch.count--;
131 if (touch.count <= 0) {
132 touch.multitouch = false;
133 }
134 touchFlag = false;
135 };
136 var contextmenuHandler = function () {
137 touchendHandler();
138 };
139
140 var trapFocusInsideOverlay = function (event) {
141 if (overlay.style.display === 'block' && (overlay.contains && !overlay.contains(event.target))) {
142 event.stopPropagation();
143 initFocus();
144 }
145 };
146
147 // forEach polyfill for IE8
148 // http://stackoverflow.com/a/14827443/1077846
149 /* eslint-disable */
150 if (![].forEach) {
151 Array.prototype.forEach = function (callback, thisArg) {
152 for (var i = 0; i < this.length; i++) {
153 callback.call(thisArg, this[i], i, this);
154 }
155 };
156 }
157
158 // filter polyfill for IE8
159 // https://gist.github.com/eliperelman/1031656
160 if (![].filter) {
161 Array.prototype.filter = function (a, b, c, d, e) {
162 c = this;
163 d = [];
164 for (e = 0; e < c.length; e++)
165 a.call(b, c[e], e, c) && d.push(c[e]);
166 return d;
167 };
168 }
169 /* eslint-enable */
170
171 // Script entry point
172 function run(selector, userOptions) {
173 // Fill supports object
174 supports.transforms = testTransformsSupport();
175 supports.svg = testSvgSupport();
176 supports.passiveEvents = testPassiveEventsSupport();
177
178 buildOverlay();
179 removeFromCache(selector);
180 return bindImageClickListeners(selector, userOptions);
181 }
182
183 function bindImageClickListeners(selector, userOptions) {
184 // For each gallery bind a click event to every image inside it
185 var galleryNodeList = document.querySelectorAll(selector);
186 var selectorData = {
187 galleries: [],
188 nodeList: galleryNodeList
189 };
190 data[selector] = selectorData;
191
192 [].forEach.call(galleryNodeList, function (galleryElement) {
193 if (userOptions && userOptions.filter) {
194 regex = userOptions.filter;
195 }
196
197 // Get nodes from gallery elements or single-element galleries
198 var tagsNodeList = [];
199 if (galleryElement.tagName === 'A') {
200 tagsNodeList = [galleryElement];
201 }
202 else {
203 tagsNodeList = galleryElement.getElementsByTagName('a');
204 }
205
206 // Filter 'a' elements from those not linking to images
207 tagsNodeList = [].filter.call(tagsNodeList, function (element) {
208 if (element.className.indexOf(userOptions && userOptions.ignoreClass) === -1) {
209 return regex.test(element.href) || ('image' === element.getAttribute('data-content-type') || ('video' === element.getAttribute('data-content-type')));
210 }
211 });
212 if (tagsNodeList.length === 0) {
213 return;
214 }
215
216 var gallery = [];
217 [].forEach.call(tagsNodeList, function (imageElement, imageIndex) {
218 var imageElementClickHandler = function (event) {
219 event.preventDefault ? event.preventDefault() : event.returnValue = false; // eslint-disable-line no-unused-expressions
220 prepareOverlay(gallery, userOptions);
221 showOverlay(imageIndex);
222 };
223 var imageItem = {
224 eventHandler: imageElementClickHandler,
225 imageElement: imageElement
226 };
227 bind(imageElement, 'click', imageElementClickHandler);
228 gallery.push(imageItem);
229 });
230 selectorData.galleries.push(gallery);
231 });
232
233 return selectorData.galleries;
234 }
235
236 function clearCachedData() {
237 for (var selector in data) {
238 if (data.hasOwnProperty(selector)) {
239 removeFromCache(selector);
240 }
241 }
242 }
243
244 function removeFromCache(selector) {
245 if (!data.hasOwnProperty(selector)) {
246 return;
247 }
248 var galleries = data[selector].galleries;
249 [].forEach.call(galleries, function (gallery) {
250 [].forEach.call(gallery, function (imageItem) {
251 unbind(imageItem.imageElement, 'click', imageItem.eventHandler);
252 });
253
254 if (currentGallery === gallery) {
255 currentGallery = [];
256 }
257 });
258
259 delete data[selector];
260 }
261
262 function buildOverlay() {
263 overlay = getByID('baguetteBox-overlay');
264 // Check if the overlay already exists
265 if (overlay) {
266 slider = getByID('baguetteBox-slider');
267 previousButton = getByID('previous-button');
268 nextButton = getByID('next-button');
269 closeButton = getByID('close-button');
270 return;
271 }
272 // Create overlay element
273 overlay = create('div');
274 overlay.setAttribute('role', 'dialog');
275 overlay.id = 'baguetteBox-overlay';
276 document.getElementsByTagName('body')[0].appendChild(overlay);
277 // Create gallery slider element
278 slider = create('div');
279 slider.id = 'baguetteBox-slider';
280 overlay.appendChild(slider);
281 // Create all necessary buttons
282 previousButton = create('button');
283 previousButton.setAttribute('type', 'button');
284 previousButton.id = 'previous-button';
285 previousButton.setAttribute('aria-label', 'Previous');
286 previousButton.innerHTML = supports.svg ? leftArrow : '&lt;';
287 overlay.appendChild(previousButton);
288
289 nextButton = create('button');
290 nextButton.setAttribute('type', 'button');
291 nextButton.id = 'next-button';
292 nextButton.setAttribute('aria-label', 'Next');
293 nextButton.innerHTML = supports.svg ? rightArrow : '&gt;';
294 overlay.appendChild(nextButton);
295
296 closeButton = create('button');
297 closeButton.setAttribute('type', 'button');
298 closeButton.id = 'close-button';
299 closeButton.setAttribute('aria-label', 'Close');
300 closeButton.innerHTML = supports.svg ? closeX : '&times;';
301 overlay.appendChild(closeButton);
302
303 previousButton.className = nextButton.className = closeButton.className = 'baguetteBox-button';
304
305 bindEvents();
306 }
307
308 function keyDownHandler(event) {
309 switch (event.keyCode) {
310 case 37: // Left arrow
311 showPreviousImage();
312 break;
313 case 39: // Right arrow
314 showNextImage();
315 break;
316 case 27: // Esc
317 hideOverlay();
318 break;
319 case 36: // Home
320 showFirstImage(event);
321 break;
322 case 35: // End
323 showLastImage(event);
324 break;
325 }
326 }
327
328 function bindEvents() {
329 var passiveEvent = supports.passiveEvents ? {passive: false} : null;
330 var nonPassiveEvent = supports.passiveEvents ? {passive: true} : null;
331
332 bind(overlay, 'click', overlayClickHandler);
333 bind(previousButton, 'click', previousButtonClickHandler);
334 bind(nextButton, 'click', nextButtonClickHandler);
335 bind(closeButton, 'click', closeButtonClickHandler);
336 bind(slider, 'contextmenu', contextmenuHandler);
337 bind(overlay, 'touchstart', touchstartHandler, nonPassiveEvent);
338 bind(overlay, 'touchmove', touchmoveHandler, passiveEvent);
339 bind(overlay, 'touchend', touchendHandler);
340 bind(document, 'focus', trapFocusInsideOverlay, true);
341 }
342
343 function unbindEvents() {
344 var passiveEvent = supports.passiveEvents ? {passive: false} : null;
345 var nonPassiveEvent = supports.passiveEvents ? {passive: true} : null;
346
347 unbind(overlay, 'click', overlayClickHandler);
348 unbind(previousButton, 'click', previousButtonClickHandler);
349 unbind(nextButton, 'click', nextButtonClickHandler);
350 unbind(closeButton, 'click', closeButtonClickHandler);
351 unbind(slider, 'contextmenu', contextmenuHandler);
352 unbind(overlay, 'touchstart', touchstartHandler, nonPassiveEvent);
353 unbind(overlay, 'touchmove', touchmoveHandler, passiveEvent);
354 unbind(overlay, 'touchend', touchendHandler);
355 unbind(document, 'focus', trapFocusInsideOverlay, true);
356 }
357
358 function prepareOverlay(gallery, userOptions) {
359 // If the same gallery is being opened prevent from loading it once again
360 if (currentGallery === gallery) {
361 return;
362 }
363 currentGallery = gallery;
364 // Update gallery specific options
365 setOptions(userOptions);
366 // Empty slider of previous contents (more effective than .innerHTML = "")
367 while (slider.firstChild) {
368 slider.removeChild(slider.firstChild);
369 }
370 imagesElements.length = 0;
371
372 var imagesFiguresIds = [];
373 var imagesCaptionsIds = [];
374 // Prepare and append images containers and populate figure and captions IDs arrays
375 for (var i = 0, fullImage; i < gallery.length; i++) {
376 fullImage = create('div');
377 fullImage.className = 'full-image';
378 fullImage.id = 'baguette-img-' + i;
379 imagesElements.push(fullImage);
380
381 imagesFiguresIds.push('baguetteBox-figure-' + i);
382 imagesCaptionsIds.push('baguetteBox-figcaption-' + i);
383 slider.appendChild(imagesElements[i]);
384 }
385 overlay.setAttribute('aria-labelledby', imagesFiguresIds.join(' '));
386 overlay.setAttribute('aria-describedby', imagesCaptionsIds.join(' '));
387 }
388
389 function setOptions(newOptions) {
390 if (!newOptions) {
391 newOptions = {};
392 }
393 // Fill options object
394 for (var item in defaults) {
395 options[item] = defaults[item];
396 if (typeof newOptions[item] !== 'undefined') {
397 options[item] = newOptions[item];
398 }
399 }
400 /* Apply new options */
401 // Change transition for proper animation
402 slider.style.transition = slider.style.webkitTransition = (options.animation === 'fadeIn' ? 'opacity .4s ease' :
403 options.animation === 'slideIn' ? '' : 'none');
404 // Hide buttons if necessary
405 if (options.buttons === 'auto' && ('ontouchstart' in window || currentGallery.length === 1)) {
406 options.buttons = false;
407 }
408 // Set buttons style to hide or display them
409 previousButton.style.display = nextButton.style.display = (options.buttons ? '' : 'none');
410 // Set overlay color
411 try {
412 overlay.style.backgroundColor = options.overlayBackgroundColor;
413 }
414 catch (e) {
415 // Silence the error and continue
416 }
417 }
418
419 function showOverlay(chosenImageIndex) {
420 if (options.noScrollbars) {
421 document.documentElement.style.overflowY = 'hidden';
422 document.body.style.overflowY = 'scroll';
423 }
424 if (overlay.style.display === 'block') {
425 return;
426 }
427
428 bind(document, 'keydown', keyDownHandler);
429 currentIndex = chosenImageIndex;
430 touch = {
431 count: 0,
432 startX: null,
433 startY: null
434 };
435 loadImage(currentIndex, function () {
436 preloadNext(currentIndex);
437 preloadPrev(currentIndex);
438 });
439
440 updateOffset();
441 overlay.style.display = 'block';
442 if (options.fullScreen) {
443 enterFullScreen();
444 }
445 // Fade in overlay
446 setTimeout(function () {
447 overlay.className = 'visible';
448 if (options.bodyClass && document.body.classList) {
449 document.body.classList.add(options.bodyClass);
450 }
451 if (options.afterShow) {
452 options.afterShow();
453 }
454 }, 50);
455 if (options.onChange) {
456 options.onChange(currentIndex, imagesElements.length);
457 }
458 documentLastFocus = document.activeElement;
459 initFocus();
460 isOverlayVisible = true;
461 }
462
463 function initFocus() {
464 if (options.buttons) {
465 previousButton.focus();
466 }
467 else {
468 closeButton.focus();
469 }
470 }
471
472 function enterFullScreen() {
473 if (overlay.requestFullscreen) {
474 overlay.requestFullscreen();
475 }
476 else if (overlay.webkitRequestFullscreen) {
477 overlay.webkitRequestFullscreen();
478 }
479 else if (overlay.mozRequestFullScreen) {
480 overlay.mozRequestFullScreen();
481 }
482 }
483
484 function exitFullscreen() {
485 if (document.exitFullscreen) {
486 document.exitFullscreen();
487 }
488 else if (document.mozCancelFullScreen) {
489 document.mozCancelFullScreen();
490 }
491 else if (document.webkitExitFullscreen) {
492 document.webkitExitFullscreen();
493 }
494 }
495
496 function hideOverlay() {
497 if (options.noScrollbars) {
498 document.documentElement.style.overflowY = 'auto';
499 document.body.style.overflowY = 'auto';
500 }
501 if (overlay.style.display === 'none') {
502 return;
503 }
504
505 unbind(document, 'keydown', keyDownHandler);
506 // Fade out and hide the overlay
507 overlay.className = '';
508 setTimeout(function () {
509 overlay.style.display = 'none';
510 if (document.fullscreen) {
511 exitFullscreen();
512 }
513 if (options.bodyClass && document.body.classList) {
514 document.body.classList.remove(options.bodyClass);
515 }
516 if (options.afterHide) {
517 options.afterHide();
518 }
519 pauseAnyVideoPlaying();
520
521 documentLastFocus && documentLastFocus.focus();
522 isOverlayVisible = false;
523 }, 500);
524 }
525
526 function pauseAnyVideoPlaying() {
527 [].forEach.call(imagesElements, function (imageElement) {
528 if (imageElement.getElementsByTagName('video').length > 0) {
529 imageElement.getElementsByTagName('video')[0].pause();
530 }
531 });
532 }
533
534 function loadImage(index, callback) {
535 var imageContainer = imagesElements[index];
536 var galleryItem = currentGallery[index];
537 var isVideo = false;
538 if (imageContainer !== undefined) {
539 isVideo = videoRegex.test(galleryItem.imageElement.href) || ('video' === galleryItem.imageElement.getAttribute('data-content-type'));
540 }
541
542 // Return if the index exceeds prepared images in the overlay
543 // or if the current gallery has been changed / closed
544 if (typeof imageContainer === 'undefined' || typeof galleryItem === 'undefined') {
545 return;
546 }
547
548 // If image is already loaded run callback and return
549 if ((imageContainer.getElementsByTagName('img')[0] && !isVideo) || (imageContainer.getElementsByTagName('video')[0] && isVideo)) {
550 if (callback) {
551 callback();
552 }
553 return;
554 }
555
556 // Get element reference, optional caption and source path
557 var imageElement = galleryItem.imageElement;
558 var thumbnailElement = isVideo ? imageElement.getElementsByTagName('video')[0] : imageElement.getElementsByTagName('img')[0];
559 var imageCaption = typeof options.captions === 'function' ?
560 options.captions.call(currentGallery, imageElement) :
561 imageElement.getAttribute('data-caption') || imageElement.title;
562 var imageSrc = isVideo ? getVideoSrc(imageElement) : getImageSrc(imageElement);
563
564 // Prepare figure element
565 var figure = create('figure');
566 figure.id = 'baguetteBox-figure-' + index;
567 figure.innerHTML = '<div class="baguetteBox-spinner">' +
568 '<div class="baguetteBox-double-bounce1"></div>' +
569 '<div class="baguetteBox-double-bounce2"></div>' +
570 '</div>';
571 // Insert caption if available
572 if (options.captions && imageCaption) {
573 var figcaption = create('figcaption');
574 figcaption.id = 'baguetteBox-figcaption-' + index;
575 figcaption.innerHTML = imageCaption;
576 figure.appendChild(figcaption);
577 }
578 imageContainer.appendChild(figure);
579
580 if (!isVideo) { // Prepare gallery img element
581 var image = create('img');
582 image.onload = function () {
583 // Remove loader element
584 var spinner = document.querySelector('#baguette-img-' + index + ' .baguetteBox-spinner');
585 figure.removeChild(spinner);
586 if (!options.async && callback) {
587 callback();
588 }
589 };
590 image.setAttribute('src', imageSrc);
591 image.alt = thumbnailElement ? thumbnailElement.alt || '' : '';
592 if (options.titleTag && imageCaption) {
593 image.title = imageCaption;
594 }
595 figure.appendChild(image);
596 }
597 else {
598 // Prepare gallery video element
599 var video = create('video');
600 video.addEventListener('loadeddata', function () {
601 //Remove loader element
602 var spinner = document.querySelector('#baguette-img-' + index + ' .baguetteBox-spinner');
603 figure.removeChild(spinner);
604 if (!options.async && callback) {
605 callback();
606 }
607 });
608 var source = create('source');
609 source.setAttribute('src', imageSrc);
610 video.appendChild(source);
611 if (options.titleTag && imageCaption) {
612 video.title = imageCaption;
613 }
614 figure.appendChild(video);
615 }
616
617 // Run callback
618 if (options.async && callback) {
619 callback();
620 }
621 }
622
623 // Get image source location, mostly used for responsive images
624 function getImageSrc(image) {
625 // Set default image path from href
626 var result = image.href;
627 // If dataset is supported find the most suitable image
628 if (image.dataset) {
629 var srcs = [];
630 // Get all possible image versions depending on the resolution
631 for (var item in image.dataset) {
632 if (item.substring(0, 3) === 'at-' && !isNaN(item.substring(3))) {
633 srcs[item.replace('at-', '')] = image.dataset[item];
634 }
635 }
636 // Sort resolutions ascending
637 var keys = Object.keys(srcs).sort(function (a, b) {
638 return parseInt(a, 10) < parseInt(b, 10) ? -1 : 1;
639 });
640 // Get real screen resolution
641 var width = window.innerWidth * window.devicePixelRatio;
642 // Find the first image bigger than or equal to the current width
643 var i = 0;
644 while (i < keys.length - 1 && keys[i] < width) {
645 i++;
646 }
647 result = srcs[keys[i]] || result;
648 }
649 return result;
650 }
651
652 // Get video source location, mostly used for responsive images
653 function getVideoSrc(image) {
654 // Set default image path from href
655 var result = image.href; //image.getElementsByTagName('video')[0].getElementsByTagName('source')[0].src;
656 // If dataset is supported find the most suitable image
657 if (image.dataset) {
658 var srcs = [];
659 // Get all possible image versions depending on the resolution
660 for (var item in image.dataset) {
661 if (item.substring(0, 3) === 'at-' && !isNaN(item.substring(3))) {
662 srcs[item.replace('at-', '')] = image.dataset[item];
663 }
664 }
665 // Sort resolutions ascending
666 var keys = Object.keys(srcs).sort(function (a, b) {
667 return parseInt(a, 10) < parseInt(b, 10) ? -1 : 1;
668 });
669 // Get real screen resolution
670 var width = window.innerWidth * window.devicePixelRatio;
671 // Find the first image bigger than or equal to the current width
672 var i = 0;
673 while (i < keys.length - 1 && keys[i] < width) {
674 i++;
675 }
676 result = srcs[keys[i]] || result;
677 }
678 return result;
679 }
680
681 // Return false at the right end of the gallery
682 function showNextImage() {
683 return show(currentIndex + 1);
684 }
685
686 // Return false at the left end of the gallery
687 function showPreviousImage() {
688 return show(currentIndex - 1);
689 }
690
691 // Return false at the left end of the gallery
692 function showFirstImage(event) {
693 if (event) {
694 event.preventDefault();
695 }
696 return show(0);
697 }
698
699 // Return false at the right end of the gallery
700 function showLastImage(event) {
701 if (event) {
702 event.preventDefault();
703 }
704 return show(currentGallery.length - 1);
705 }
706
707 /**
708 * Move the gallery to a specific index
709 * @param `index` {number} - the position of the image
710 * @param `gallery` {array} - gallery which should be opened, if omitted assumes the currently opened one
711 * @return {boolean} - true on success or false if the index is invalid
712 */
713 function show(index, gallery) {
714 if (!isOverlayVisible && index >= 0 && index < gallery.length) {
715 prepareOverlay(gallery, options);
716 showOverlay(index);
717 return true;
718 }
719 if (index < 0) {
720 if (options.animation) {
721 bounceAnimation('left');
722 }
723 return false;
724 }
725 if (index >= imagesElements.length) {
726 if (options.animation) {
727 bounceAnimation('right');
728 }
729 return false;
730 }
731
732 currentIndex = index;
733 loadImage(currentIndex, function () {
734 preloadNext(currentIndex);
735 preloadPrev(currentIndex);
736 });
737 updateOffset();
738
739 if (options.onChange) {
740 options.onChange(currentIndex, imagesElements.length);
741 }
742
743 return true;
744 }
745
746 /**
747 * Triggers the bounce animation
748 * @param {('left'|'right')} direction - Direction of the movement
749 */
750 function bounceAnimation(direction) {
751 slider.className = 'bounce-from-' + direction;
752 setTimeout(function () {
753 slider.className = '';
754 }, 400);
755 }
756
757 function updateOffset() {
758 pauseAnyVideoPlaying();
759
760 var offset = -currentIndex * 100 + '%';
761 if (options.animation === 'fadeIn') {
762 slider.style.opacity = 0;
763 setTimeout(function () {
764 supports.transforms ?
765 slider.style.transform = slider.style.webkitTransform = 'translate3d(' + offset + ',0,0)'
766 : slider.style.left = offset;
767 slider.style.opacity = 1;
768 }, 400);
769 }
770 else {
771 supports.transforms ?
772 slider.style.transform = slider.style.webkitTransform = 'translate3d(' + offset + ',0,0)'
773 : slider.style.left = offset;
774 }
775
776 if (imagesElements[currentIndex].getElementsByTagName('video').length > 0) {
777 imagesElements[currentIndex].getElementsByTagName('video')[0].play();
778 }
779 }
780
781 // CSS 3D Transforms test
782 function testTransformsSupport() {
783 var div = create('div');
784 return typeof div.style.perspective !== 'undefined' || typeof div.style.webkitPerspective !== 'undefined';
785 }
786
787 // Inline SVG test
788 function testSvgSupport() {
789 var div = create('div');
790 div.innerHTML = '<svg/>';
791 return (div.firstChild && div.firstChild.namespaceURI) === 'http://www.w3.org/2000/svg';
792 }
793
794 // Borrowed from https://github.com/seiyria/bootstrap-slider/pull/680/files
795 /* eslint-disable getter-return */
796 function testPassiveEventsSupport() {
797 var passiveEvents = false;
798 try {
799 var opts = Object.defineProperty({}, 'passive', {
800 get: function () {
801 passiveEvents = true;
802 }
803 });
804 window.addEventListener('test', null, opts);
805 }
806 catch (e) { /* Silence the error and continue */
807 }
808
809 return passiveEvents;
810 }
811
812 /* eslint-enable getter-return */
813
814 function preloadNext(index) {
815 if (index - currentIndex >= options.preload) {
816 return;
817 }
818 loadImage(index + 1, function () {
819 preloadNext(index + 1);
820 });
821 }
822
823 function preloadPrev(index) {
824 if (currentIndex - index >= options.preload) {
825 return;
826 }
827 loadImage(index - 1, function () {
828 preloadPrev(index - 1);
829 });
830 }
831
832 function bind(element, event, callback, options) {
833 if (element.addEventListener) {
834 element.addEventListener(event, callback, options);
835 }
836 else {
837 // IE8 fallback
838 element.attachEvent('on' + event, function (event) {
839 // `event` and `event.target` are not provided in IE8
840 event = event || window.event;
841 event.target = event.target || event.srcElement;
842 callback(event);
843 });
844 }
845 }
846
847 function unbind(element, event, callback, options) {
848 if (element.removeEventListener) {
849 element.removeEventListener(event, callback, options);
850 }
851 else {
852 // IE8 fallback
853 element.detachEvent('on' + event, callback);
854 }
855 }
856
857 function getByID(id) {
858 return document.getElementById(id);
859 }
860
861 function create(element) {
862 return document.createElement(element);
863 }
864
865 function destroyPlugin() {
866 unbindEvents();
867 clearCachedData();
868 unbind(document, 'keydown', keyDownHandler);
869 document.getElementsByTagName('body')[0].removeChild(document.getElementById('baguetteBox-overlay'));
870 data = {};
871 currentGallery = [];
872 currentIndex = 0;
873 }
874
875 return {
876 run: run,
877 show: show,
878 showNext: showNextImage,
879 showPrevious: showPreviousImage,
880 hide: hideOverlay,
881 destroy: destroyPlugin
882 };
883 }));
884