baguetteBox.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710
  1. /*!
  2. * baguetteBox.js
  3. * @author feimosi
  4. * @version %%INJECT_VERSION%%
  5. * @url https://github.com/feimosi/baguetteBox.js
  6. */
  7. /* global define, module */
  8. (function (root, factory) {
  9. 'use strict';
  10. if (typeof define === 'function' && define.amd) {
  11. define(factory);
  12. } else if (typeof exports === 'object') {
  13. module.exports = factory();
  14. } else {
  15. root.baguetteBox = factory();
  16. }
  17. }(this, function () {
  18. 'use strict';
  19. // SVG shapes used on the buttons
  20. var leftArrow = '<svg width="44" height="60">' +
  21. '<polyline points="30 10 10 30 30 50" stroke="rgba(255,255,255,0.5)" stroke-width="4"' +
  22. 'stroke-linecap="butt" fill="none" stroke-linejoin="round"/>' +
  23. '</svg>',
  24. rightArrow = '<svg width="44" height="60">' +
  25. '<polyline points="14 10 34 30 14 50" stroke="rgba(255,255,255,0.5)" stroke-width="4"' +
  26. 'stroke-linecap="butt" fill="none" stroke-linejoin="round"/>' +
  27. '</svg>',
  28. closeX = '<svg width="30" height="30">' +
  29. '<g stroke="rgb(160,160,160)" stroke-width="4">' +
  30. '<line x1="5" y1="5" x2="25" y2="25"/>' +
  31. '<line x1="5" y1="25" x2="25" y2="5"/>' +
  32. '</g></svg>';
  33. // Global options and their defaults
  34. var options = {},
  35. defaults = {
  36. captions: true,
  37. fullScreen: false,
  38. noScrollbars: false,
  39. titleTag: false,
  40. buttons: 'auto',
  41. async: false,
  42. preload: 2,
  43. animation: 'slideIn',
  44. afterShow: null,
  45. afterHide: null,
  46. // callback when image changes with `currentIndex` and `imagesElements.length` as parameters
  47. onChange: null,
  48. overlayBackgroundColor: 'rgba(0,0,0,.8)'
  49. };
  50. // Object containing information about features compatibility
  51. var supports = {};
  52. // DOM Elements references
  53. var overlay, slider, previousButton, nextButton, closeButton;
  54. // An array with all images in the current gallery
  55. var currentGallery = [];
  56. // Current image index inside the slider
  57. var currentIndex = 0;
  58. // Touch event start position (for slide gesture)
  59. var touch = {};
  60. // If set to true ignore touch events because animation was already fired
  61. var touchFlag = false;
  62. // Regex pattern to match image files
  63. var regex = /.+\.(gif|jpe?g|png|webp)/i;
  64. // Object of all used galleries
  65. var data = {};
  66. // Array containing temporary images DOM elements
  67. var imagesElements = [];
  68. // The last focused element before opening the overlay
  69. var documentLastFocus = null;
  70. var overlayClickHandler = function(event) {
  71. // Close the overlay when user clicks directly on the background
  72. if (event.target.id.indexOf('baguette-img') !== -1) {
  73. hideOverlay();
  74. }
  75. };
  76. var previousButtonClickHandler = function(event) {
  77. event.stopPropagation ? event.stopPropagation() : event.cancelBubble = true; // jshint ignore:line
  78. showPreviousImage();
  79. };
  80. var nextButtonClickHandler = function(event) {
  81. event.stopPropagation ? event.stopPropagation() : event.cancelBubble = true; // jshint ignore:line
  82. showNextImage();
  83. };
  84. var closeButtonClickHandler = function(event) {
  85. event.stopPropagation ? event.stopPropagation() : event.cancelBubble = true; // jshint ignore:line
  86. hideOverlay();
  87. };
  88. var touchstartHandler = function(event) {
  89. touch.count++;
  90. if (touch.count > 1) {
  91. touch.multitouch = true;
  92. }
  93. // Save x and y axis position
  94. touch.startX = event.changedTouches[0].pageX;
  95. touch.startY = event.changedTouches[0].pageY;
  96. };
  97. var touchmoveHandler = function(event) {
  98. // If action was already triggered or multitouch return
  99. if (touchFlag || touch.multitouch) {
  100. return;
  101. }
  102. event.preventDefault ? event.preventDefault() : event.returnValue = false; // jshint ignore:line
  103. var touchEvent = event.touches[0] || event.changedTouches[0];
  104. // Move at least 40 pixels to trigger the action
  105. if (touchEvent.pageX - touch.startX > 40) {
  106. touchFlag = true;
  107. showPreviousImage();
  108. } else if (touchEvent.pageX - touch.startX < -40) {
  109. touchFlag = true;
  110. showNextImage();
  111. // Move 100 pixels up to close the overlay
  112. } else if (touch.startY - touchEvent.pageY > 100) {
  113. hideOverlay();
  114. }
  115. };
  116. var touchendHandler = function() {
  117. touch.count--;
  118. if (touch.count <= 0) {
  119. touch.multitouch = false;
  120. }
  121. touchFlag = false;
  122. };
  123. var trapFocusInsideOverlay = function(event) {
  124. if (overlay.style.display === 'block' && (overlay.contains && !overlay.contains(event.target))) {
  125. event.stopPropagation();
  126. initFocus();
  127. }
  128. };
  129. // forEach polyfill for IE8
  130. // http://stackoverflow.com/a/14827443/1077846
  131. /* jshint ignore:start */
  132. if (![].forEach) {
  133. Array.prototype.forEach = function(callback, thisArg) {
  134. for (var i = 0; i < this.length; i++) {
  135. callback.call(thisArg, this[i], i, this);
  136. }
  137. };
  138. }
  139. // filter polyfill for IE8
  140. // https://gist.github.com/eliperelman/1031656
  141. if (![].filter) {
  142. Array.prototype.filter = function(a, b, c, d, e) {
  143. c = this;
  144. d = [];
  145. for (e = 0; e < c.length; e++)
  146. a.call(b, c[e], e, c) && d.push(c[e]);
  147. return d;
  148. };
  149. }
  150. /* jshint ignore:end */
  151. // Script entry point
  152. function run(selector, userOptions) {
  153. // Fill supports object
  154. supports.transforms = testTransformsSupport();
  155. supports.svg = testSVGSupport();
  156. buildOverlay();
  157. removeFromCache(selector);
  158. bindImageClickListeners(selector, userOptions);
  159. }
  160. function bindImageClickListeners(selector, userOptions) {
  161. // For each gallery bind a click event to every image inside it
  162. var galleryNodeList = document.querySelectorAll(selector);
  163. var selectorData = {
  164. galleries: [],
  165. nodeList: galleryNodeList
  166. };
  167. data[selector] = selectorData;
  168. [].forEach.call(galleryNodeList, function(galleryElement) {
  169. if (userOptions && userOptions.filter) {
  170. regex = userOptions.filter;
  171. }
  172. // Get nodes from gallery elements or single-element galleries
  173. var tagsNodeList = [];
  174. if (galleryElement.tagName === 'A') {
  175. tagsNodeList = [galleryElement];
  176. } else {
  177. tagsNodeList = galleryElement.getElementsByTagName('a');
  178. }
  179. // Filter 'a' elements from those not linking to images
  180. tagsNodeList = [].filter.call(tagsNodeList, function(element) {
  181. return regex.test(element.href);
  182. });
  183. if (tagsNodeList.length === 0) {
  184. return;
  185. }
  186. var gallery = [];
  187. [].forEach.call(tagsNodeList, function(imageElement, imageIndex) {
  188. var imageElementClickHandler = function(event) {
  189. event.preventDefault ? event.preventDefault() : event.returnValue = false; // jshint ignore:line
  190. prepareOverlay(gallery, userOptions);
  191. showOverlay(imageIndex);
  192. };
  193. var imageItem = {
  194. eventHandler: imageElementClickHandler,
  195. imageElement: imageElement
  196. };
  197. bind(imageElement, 'click', imageElementClickHandler);
  198. gallery.push(imageItem);
  199. });
  200. selectorData.galleries.push(gallery);
  201. });
  202. }
  203. function clearCachedData() {
  204. for (var selector in data) {
  205. if (data.hasOwnProperty(selector)) {
  206. removeFromCache(selector);
  207. }
  208. }
  209. }
  210. function removeFromCache(selector) {
  211. if (!data.hasOwnProperty(selector)) {
  212. return;
  213. }
  214. var galleries = data[selector].galleries;
  215. [].forEach.call(galleries, function(gallery) {
  216. [].forEach.call(gallery, function(imageItem) {
  217. unbind(imageItem.imageElement, 'click', imageItem.eventHandler);
  218. });
  219. if (currentGallery === gallery) {
  220. currentGallery = [];
  221. }
  222. });
  223. delete data[selector];
  224. }
  225. function buildOverlay() {
  226. overlay = getByID('baguetteBox-overlay');
  227. // Check if the overlay already exists
  228. if (overlay) {
  229. slider = getByID('baguetteBox-slider');
  230. previousButton = getByID('previous-button');
  231. nextButton = getByID('next-button');
  232. closeButton = getByID('close-button');
  233. return;
  234. }
  235. // Create overlay element
  236. overlay = create('div');
  237. overlay.setAttribute('role', 'dialog');
  238. overlay.id = 'baguetteBox-overlay';
  239. document.getElementsByTagName('body')[0].appendChild(overlay);
  240. // Create gallery slider element
  241. slider = create('div');
  242. slider.id = 'baguetteBox-slider';
  243. overlay.appendChild(slider);
  244. // Create all necessary buttons
  245. previousButton = create('button');
  246. previousButton.setAttribute('type', 'button');
  247. previousButton.id = 'previous-button';
  248. previousButton.setAttribute('aria-label', 'Previous');
  249. previousButton.innerHTML = supports.svg ? leftArrow : '&lt;';
  250. overlay.appendChild(previousButton);
  251. nextButton = create('button');
  252. nextButton.setAttribute('type', 'button');
  253. nextButton.id = 'next-button';
  254. nextButton.setAttribute('aria-label', 'Next');
  255. nextButton.innerHTML = supports.svg ? rightArrow : '&gt;';
  256. overlay.appendChild(nextButton);
  257. closeButton = create('button');
  258. closeButton.setAttribute('type', 'button');
  259. closeButton.id = 'close-button';
  260. closeButton.setAttribute('aria-label', 'Close');
  261. closeButton.innerHTML = supports.svg ? closeX : '&times;';
  262. overlay.appendChild(closeButton);
  263. previousButton.className = nextButton.className = closeButton.className = 'baguetteBox-button';
  264. bindEvents();
  265. }
  266. function keyDownHandler(event) {
  267. switch (event.keyCode) {
  268. case 37: // Left arrow
  269. showPreviousImage();
  270. break;
  271. case 39: // Right arrow
  272. showNextImage();
  273. break;
  274. case 27: // Esc
  275. hideOverlay();
  276. break;
  277. }
  278. }
  279. function bindEvents() {
  280. bind(overlay, 'click', overlayClickHandler);
  281. bind(previousButton, 'click', previousButtonClickHandler);
  282. bind(nextButton, 'click', nextButtonClickHandler);
  283. bind(closeButton, 'click', closeButtonClickHandler);
  284. bind(overlay, 'touchstart', touchstartHandler);
  285. bind(overlay, 'touchmove', touchmoveHandler);
  286. bind(overlay, 'touchend', touchendHandler);
  287. bind(document, 'focus', trapFocusInsideOverlay, true);
  288. }
  289. function unbindEvents() {
  290. unbind(overlay, 'click', overlayClickHandler);
  291. unbind(previousButton, 'click', previousButtonClickHandler);
  292. unbind(nextButton, 'click', nextButtonClickHandler);
  293. unbind(closeButton, 'click', closeButtonClickHandler);
  294. unbind(overlay, 'touchstart', touchstartHandler);
  295. unbind(overlay, 'touchmove', touchmoveHandler);
  296. unbind(overlay, 'touchend', touchendHandler);
  297. unbind(document, 'focus', trapFocusInsideOverlay, true);
  298. }
  299. function prepareOverlay(gallery, userOptions) {
  300. // If the same gallery is being opened prevent from loading it once again
  301. if (currentGallery === gallery) {
  302. return;
  303. }
  304. currentGallery = gallery;
  305. // Update gallery specific options
  306. setOptions(userOptions);
  307. // Empty slider of previous contents (more effective than .innerHTML = "")
  308. while (slider.firstChild) {
  309. slider.removeChild(slider.firstChild);
  310. }
  311. imagesElements.length = 0;
  312. var imagesFiguresIds = [];
  313. var imagesCaptionsIds = [];
  314. // Prepare and append images containers and populate figure and captions IDs arrays
  315. for (var i = 0, fullImage; i < gallery.length; i++) {
  316. fullImage = create('div');
  317. fullImage.className = 'full-image';
  318. fullImage.id = 'baguette-img-' + i;
  319. imagesElements.push(fullImage);
  320. imagesFiguresIds.push('baguetteBox-figure-' + i);
  321. imagesCaptionsIds.push('baguetteBox-figcaption-' + i);
  322. slider.appendChild(imagesElements[i]);
  323. }
  324. overlay.setAttribute('aria-labelledby', imagesFiguresIds.join(' '));
  325. overlay.setAttribute('aria-describedby', imagesCaptionsIds.join(' '));
  326. }
  327. function setOptions(newOptions) {
  328. if (!newOptions) {
  329. newOptions = {};
  330. }
  331. // Fill options object
  332. for (var item in defaults) {
  333. options[item] = defaults[item];
  334. if (typeof newOptions[item] !== 'undefined') {
  335. options[item] = newOptions[item];
  336. }
  337. }
  338. /* Apply new options */
  339. // Change transition for proper animation
  340. slider.style.transition = slider.style.webkitTransition = (options.animation === 'fadeIn' ? 'opacity .4s ease' :
  341. options.animation === 'slideIn' ? '' : 'none');
  342. // Hide buttons if necessary
  343. if (options.buttons === 'auto' && ('ontouchstart' in window || currentGallery.length === 1)) {
  344. options.buttons = false;
  345. }
  346. // Set buttons style to hide or display them
  347. previousButton.style.display = nextButton.style.display = (options.buttons ? '' : 'none');
  348. // Set overlay color
  349. try {
  350. overlay.style.backgroundColor = options.overlayBackgroundColor;
  351. } catch (e) {
  352. // Silence the error and continue
  353. }
  354. }
  355. function showOverlay(chosenImageIndex) {
  356. if (options.noScrollbars) {
  357. document.documentElement.style.overflowY = 'hidden';
  358. document.body.style.overflowY = 'scroll';
  359. }
  360. if (overlay.style.display === 'block') {
  361. return;
  362. }
  363. bind(document, 'keydown', keyDownHandler);
  364. currentIndex = chosenImageIndex;
  365. touch = {
  366. count: 0,
  367. startX: null,
  368. startY: null
  369. };
  370. loadImage(currentIndex, function() {
  371. preloadNext(currentIndex);
  372. preloadPrev(currentIndex);
  373. });
  374. updateOffset();
  375. overlay.style.display = 'block';
  376. if (options.fullScreen) {
  377. enterFullScreen();
  378. }
  379. // Fade in overlay
  380. setTimeout(function() {
  381. overlay.className = 'visible';
  382. if (options.afterShow) {
  383. options.afterShow();
  384. }
  385. }, 50);
  386. if (options.onChange) {
  387. options.onChange(currentIndex, imagesElements.length);
  388. }
  389. documentLastFocus = document.activeElement;
  390. initFocus();
  391. }
  392. function initFocus() {
  393. if (options.buttons) {
  394. previousButton.focus();
  395. } else {
  396. closeButton.focus();
  397. }
  398. }
  399. function enterFullScreen() {
  400. if (overlay.requestFullscreen) {
  401. overlay.requestFullscreen();
  402. } else if (overlay.webkitRequestFullscreen) {
  403. overlay.webkitRequestFullscreen();
  404. } else if (overlay.mozRequestFullScreen) {
  405. overlay.mozRequestFullScreen();
  406. }
  407. }
  408. function exitFullscreen() {
  409. if (document.exitFullscreen) {
  410. document.exitFullscreen();
  411. } else if (document.mozCancelFullScreen) {
  412. document.mozCancelFullScreen();
  413. } else if (document.webkitExitFullscreen) {
  414. document.webkitExitFullscreen();
  415. }
  416. }
  417. function hideOverlay() {
  418. if (options.noScrollbars) {
  419. document.documentElement.style.overflowY = 'auto';
  420. document.body.style.overflowY = 'auto';
  421. }
  422. if (overlay.style.display === 'none') {
  423. return;
  424. }
  425. unbind(document, 'keydown', keyDownHandler);
  426. // Fade out and hide the overlay
  427. overlay.className = '';
  428. setTimeout(function() {
  429. overlay.style.display = 'none';
  430. exitFullscreen();
  431. if (options.afterHide) {
  432. options.afterHide();
  433. }
  434. }, 500);
  435. documentLastFocus.focus();
  436. }
  437. function loadImage(index, callback) {
  438. var imageContainer = imagesElements[index];
  439. var galleryItem = currentGallery[index];
  440. // Return if the index exceeds prepared images in the overlay
  441. // or if the current gallery has been changed / closed
  442. if (imageContainer === undefined || galleryItem === undefined) {
  443. return;
  444. }
  445. // If image is already loaded run callback and return
  446. if (imageContainer.getElementsByTagName('img')[0]) {
  447. if (callback) {
  448. callback();
  449. }
  450. return;
  451. }
  452. // Get element reference, optional caption and source path
  453. var imageElement = galleryItem.imageElement;
  454. var thumbnailElement = imageElement.getElementsByTagName('img')[0];
  455. var imageCaption = typeof options.captions === 'function' ?
  456. options.captions.call(currentGallery, imageElement) :
  457. imageElement.getAttribute('data-caption') || imageElement.title;
  458. var imageSrc = getImageSrc(imageElement);
  459. // Prepare figure element
  460. var figure = create('figure');
  461. figure.id = 'baguetteBox-figure-' + index;
  462. figure.innerHTML = '<div class="baguetteBox-spinner">' +
  463. '<div class="baguetteBox-double-bounce1"></div>' +
  464. '<div class="baguetteBox-double-bounce2"></div>' +
  465. '</div>';
  466. // Insert caption if available
  467. if (options.captions && imageCaption) {
  468. var figcaption = create('figcaption');
  469. figcaption.id = 'baguetteBox-figcaption-' + index;
  470. figcaption.innerHTML = imageCaption;
  471. figure.appendChild(figcaption);
  472. }
  473. imageContainer.appendChild(figure);
  474. // Prepare gallery img element
  475. var image = create('img');
  476. image.onload = function() {
  477. // Remove loader element
  478. var spinner = document.querySelector('#baguette-img-' + index + ' .baguetteBox-spinner');
  479. figure.removeChild(spinner);
  480. if (!options.async && callback) {
  481. callback();
  482. }
  483. };
  484. image.setAttribute('src', imageSrc);
  485. image.alt = thumbnailElement ? thumbnailElement.alt || '' : '';
  486. if (options.titleTag && imageCaption) {
  487. image.title = imageCaption;
  488. }
  489. figure.appendChild(image);
  490. // Run callback
  491. if (options.async && callback) {
  492. callback();
  493. }
  494. }
  495. // Get image source location, mostly used for responsive images
  496. function getImageSrc(image) {
  497. // Set default image path from href
  498. var result = image.href;
  499. // If dataset is supported find the most suitable image
  500. if (image.dataset) {
  501. var srcs = [];
  502. // Get all possible image versions depending on the resolution
  503. for (var item in image.dataset) {
  504. if (item.substring(0, 3) === 'at-' && !isNaN(item.substring(3))) {
  505. srcs[item.replace('at-', '')] = image.dataset[item];
  506. }
  507. }
  508. // Sort resolutions ascending
  509. var keys = Object.keys(srcs).sort(function(a, b) {
  510. return parseInt(a, 10) < parseInt(b, 10) ? -1 : 1;
  511. });
  512. // Get real screen resolution
  513. var width = window.innerWidth * window.devicePixelRatio;
  514. // Find the first image bigger than or equal to the current width
  515. var i = 0;
  516. while (i < keys.length - 1 && keys[i] < width) {
  517. i++;
  518. }
  519. result = srcs[keys[i]] || result;
  520. }
  521. return result;
  522. }
  523. // Return false at the right end of the gallery
  524. function showNextImage() {
  525. var returnValue;
  526. // Check if next image exists
  527. if (currentIndex <= imagesElements.length - 2) {
  528. currentIndex++;
  529. updateOffset();
  530. preloadNext(currentIndex);
  531. returnValue = true;
  532. } else if (options.animation) {
  533. slider.className = 'bounce-from-right';
  534. setTimeout(function() {
  535. slider.className = '';
  536. }, 400);
  537. returnValue = false;
  538. }
  539. if (options.onChange) {
  540. options.onChange(currentIndex, imagesElements.length);
  541. }
  542. return returnValue;
  543. }
  544. // Return false at the left end of the gallery
  545. function showPreviousImage() {
  546. var returnValue;
  547. // Check if previous image exists
  548. if (currentIndex >= 1) {
  549. currentIndex--;
  550. updateOffset();
  551. preloadPrev(currentIndex);
  552. returnValue = true;
  553. } else if (options.animation) {
  554. slider.className = 'bounce-from-left';
  555. setTimeout(function() {
  556. slider.className = '';
  557. }, 400);
  558. returnValue = false;
  559. }
  560. if (options.onChange) {
  561. options.onChange(currentIndex, imagesElements.length);
  562. }
  563. return returnValue;
  564. }
  565. function updateOffset() {
  566. var offset = -currentIndex * 100 + '%';
  567. if (options.animation === 'fadeIn') {
  568. slider.style.opacity = 0;
  569. setTimeout(function() {
  570. /* jshint -W030 */
  571. supports.transforms ?
  572. slider.style.transform = slider.style.webkitTransform = 'translate3d(' + offset + ',0,0)'
  573. : slider.style.left = offset;
  574. slider.style.opacity = 1;
  575. }, 400);
  576. } else {
  577. /* jshint -W030 */
  578. supports.transforms ?
  579. slider.style.transform = slider.style.webkitTransform = 'translate3d(' + offset + ',0,0)'
  580. : slider.style.left = offset;
  581. }
  582. }
  583. // CSS 3D Transforms test
  584. function testTransformsSupport() {
  585. var div = create('div');
  586. return typeof div.style.perspective !== 'undefined' || typeof div.style.webkitPerspective !== 'undefined';
  587. }
  588. // Inline SVG test
  589. function testSVGSupport() {
  590. var div = create('div');
  591. div.innerHTML = '<svg/>';
  592. return (div.firstChild && div.firstChild.namespaceURI) === 'http://www.w3.org/2000/svg';
  593. }
  594. function preloadNext(index) {
  595. if (index - currentIndex >= options.preload) {
  596. return;
  597. }
  598. loadImage(index + 1, function() {
  599. preloadNext(index + 1);
  600. });
  601. }
  602. function preloadPrev(index) {
  603. if (currentIndex - index >= options.preload) {
  604. return;
  605. }
  606. loadImage(index - 1, function() {
  607. preloadPrev(index - 1);
  608. });
  609. }
  610. function bind(element, event, callback, useCapture) {
  611. if (element.addEventListener) {
  612. element.addEventListener(event, callback, useCapture);
  613. } else {
  614. // IE8 fallback
  615. element.attachEvent('on' + event, function(event) {
  616. // `event` and `event.target` are not provided in IE8
  617. event = event || window.event;
  618. event.target = event.target || event.srcElement;
  619. callback(event);
  620. });
  621. }
  622. }
  623. function unbind(element, event, callback, useCapture) {
  624. if (element.removeEventListener) {
  625. element.removeEventListener(event, callback, useCapture);
  626. } else {
  627. // IE8 fallback
  628. element.detachEvent('on' + event, callback);
  629. }
  630. }
  631. function getByID(id) {
  632. return document.getElementById(id);
  633. }
  634. function create(element) {
  635. return document.createElement(element);
  636. }
  637. function destroyPlugin() {
  638. unbindEvents();
  639. clearCachedData();
  640. unbind(document, 'keydown', keyDownHandler);
  641. document.getElementsByTagName('body')[0].removeChild(document.getElementById('baguetteBox-overlay'));
  642. data = {};
  643. currentGallery = [];
  644. currentIndex = 0;
  645. }
  646. return {
  647. run: run,
  648. destroy: destroyPlugin,
  649. showNext: showNextImage,
  650. showPrevious: showPreviousImage
  651. };
  652. }));