/*! * baguettebox.js * @author feimosi * @version 0.7.0 * @url https://github.com/feimosi/baguettebox.js */ var baguettebox = (function() { // svg shapes used in buttons var leftarrow = '' + '<' + '', rightarrow = '' + '>' + '', closex = '' + '' + '' + '' + 'x'; // main id names var overlayid = 'baguettebox-overlay'; var sliderid = 'baguettebox-slider'; // global options and their defaults var options = {}, defaults = { captions: true, buttons: 'auto', async: false, preload: 2, animation: 'slidein' }; // dom elements references var overlay, slider, previousbutton, nextbutton, closebutton; // current image index inside the slider and displayed gallery index var currentindex = 0, currentgallery = -1; // touch event start position (for slide gesture) var touchstartx; // if set to true ignore touch events because animation was already fired var touchflag = false; // array of all used galleries (dom elements) var galleries = []; // 2d array of galleries and images inside them var imagesmap = []; // array containing temporary images dom elements var imagesarray = []; // foreach polyfill for ie8 if(!array.prototype.foreach) { array.prototype.foreach = function(callback, thisarg) { var len = this.length; for(var i = 0; i < len; i++) { callback.call(thisarg, this[i], i, this); } }; } // script entry point function run(selector, useroptions) { buildoverlay(); // for each gallery bind a click event to every image inside it galleries = document.queryselectorall(selector); [].foreach.call( galleries, function (galleryelement, galleryindex) { var galleryid = imagesmap.length; imagesmap.push(galleryelement.getelementsbytagname('a')); imagesmap[galleryid].options = useroptions; [].foreach.call( imagesmap[galleryid], function (imageelement, imageindex) { bind(imageelement, 'click', function(event) { /*jshint -w030 */ event.preventdefault ? event.preventdefault() : event.returnvalue = false; prepareoverlay(galleryid); showoverlay(imageindex); }); } ); } ); defaults.transforms = testtransformssupport(); } function buildoverlay() { overlay = document.getelementbyid(overlayid); // check if the overlay already exists if(overlay) { slider = document.getelementbyid(sliderid); previousbutton = document.getelementbyid('previous-button'); nextbutton = document.getelementbyid('next-button'); closebutton = document.getelementbyid('close-button'); return; } // create overlay element overlay = document.createelement('div'); overlay.id = overlayid; document.getelementsbytagname('body')[0].appendchild(overlay); // create gallery slider element slider = document.createelement('div'); slider.id = sliderid; overlay.appendchild(slider); // create all necessary buttons previousbutton = document.createelement('button'); previousbutton.id = 'previous-button'; previousbutton.innerhtml = leftarrow; overlay.appendchild(previousbutton); nextbutton = document.createelement('button'); nextbutton.id = 'next-button'; nextbutton.innerhtml = rightarrow; overlay.appendchild(nextbutton); closebutton = document.createelement('button'); closebutton.id = 'close-button'; closebutton.innerhtml = closex; overlay.appendchild(closebutton); previousbutton.classname = nextbutton.classname = closebutton.classname = 'baguettebox-button'; bindevents(); } function bindevents() { // when clicked on the overlay (outside displayed image) close it bind(overlay, 'click', function(event) { if(event.target && event.target.nodename !== "img") hideoverlay(); }); // add event listeners for buttons bind(document.getelementbyid('previous-button'), 'click', function(event) { /*jshint -w030 */ event.stoppropagation ? event.stoppropagation() : event.cancelbubble = true; showpreviousimage(); }); bind(document.getelementbyid('next-button'), 'click', function(event) { /*jshint -w030 */ event.stoppropagation ? event.stoppropagation() : event.cancelbubble = true; shownextimage(); }); bind(document.getelementbyid('close-button'), 'click', function(event) { /*jshint -w030 */ event.stoppropagation ? event.stoppropagation() : event.cancelbubble = true; hideoverlay(); }); // add touch events bind(overlay, 'touchstart', function(event) { // save x axis position touchstartx = event.changedtouches[0].pagex; }); bind(overlay, 'touchmove', function(event) { if(touchflag) return; /*jshint -w030 */ event.preventdefault ? event.preventdefault() : event.returnvalue = false; touch = event.touches[0] || event.changedtouches[0]; if(touch.pagex - touchstartx > 40) { touchflag = true; showpreviousimage(); } else if (touch.pagex - touchstartx < -40) { touchflag = true; shownextimage(); } }); bind(overlay, 'touchend', function(event) { touchflag = false; }); // activate keyboard shortcuts bind(document, 'keydown', function(event) { switch(event.keycode) { case 37: // left arrow showpreviousimage(); break; case 39: // right arrow shownextimage(); break; case 27: // esc hideoverlay(); break; } }); } function prepareoverlay(galleryindex) { // if the same gallery is being opened prevent from loading it once again if(currentgallery === galleryindex) return; currentgallery = galleryindex; // update gallery specific options setoptions(imagesmap[galleryindex].options); // empty slider of previous contents (more effective than .innerhtml = "") while(slider.firstchild) slider.removechild(slider.firstchild); imagesarray.length = 0; // prepare and append images containers for(var i = 0; i < imagesmap[galleryindex].length; i++) { imagesarray.push(returnimagecontainer()); slider.appendchild(imagesarray[i]); } } function setoptions(newoptions) { if(!newoptions) newoptions = {}; 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' : ''; // hide buttons if necessary if(options.buttons === 'auto' && ('ontouchstart' in window || imagesmap[currentgallery].length === 1)) options.buttons = false; // set buttons style to hide or display them previousbutton.style.display = nextbutton.style.display = options.buttons ? '' : 'none'; } // return dom element for image container
...
function returnimagecontainer() { var fullimage = document.createelement('div'); fullimage.classname = 'full-image'; return fullimage; } function showoverlay(index) { // return if overlay is already visible if(overlay.style.display === 'block') return; // set current index to a new value and show proper image currentindex = index; loadimage(currentindex, function() { preloadnext(currentindex); preloadprev(currentindex); }); updateoffset(); overlay.style.display = 'block'; // fade in overlay settimeout(function() { overlay.classname = 'visible'; }, 50); } function hideoverlay() { // return if overlay is already hidden if(overlay.style.display === 'none') return; // fade out and hide the overlay overlay.classname = ''; settimeout(function() { overlay.style.display = 'none'; }, 500); } function loadimage(index, callback) { var imagecontainer = imagesarray[index]; // if index is invalid return if(typeof imagecontainer === '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 imageelement = imagesmap[currentgallery][index]; imagecaption = imageelement.getattribute('data-caption') || imageelement.title; imagesrc = getimagesrc(imageelement); // prepare image container elements var figure = document.createelement('figure'); var image = document.createelement('img'); var figcaption = document.createelement('figcaption'); imagecontainer.appendchild(figure); // add loader element figure.innerhtml = '
' + '
' + '
' + '
'; // set callback function when image loads image.onload = function() { // remove loader element var spinner = this.parentnode.queryselector('.spinner'); this.parentnode.removechild(spinner); if(!options.async && callback) callback(); }; image.setattribute('src', imagesrc); figure.appendchild(image); // insert caption if available if(options.captions && imagecaption) { figcaption.innerhtml = imagecaption; figure.appendchild(figcaption); } // run callback if(options.async && callback) callback(); } function getimagesrc(image) { // set dafult image path from href var result = imageelement.getattribute('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 keys = object.keys(srcs).sort(function(a, b) { return parseint(a) < parseint(b) ? -1 : 1; }); // get real screen resolution var width = window.innerwidth * window.devicepixelratio; // find first image bigger than or equal to the current width for(var i = 0; i < keys.length; i++) { if(keys[i] >= width) { result = srcs[keys[i]]; break; } result = srcs[keys[i]]; } } return result; } function shownextimage() { if(currentindex <= imagesarray.length - 2) { currentindex++; updateoffset(); preloadnext(currentindex); } else { slider.classname = 'bounce-from-right'; settimeout(function() { slider.classname = ''; }, 400); } } function showpreviousimage() { if(currentindex >= 1) { currentindex--; updateoffset(); preloadprev(currentindex); } else { slider.classname = 'bounce-from-left'; settimeout(function() { slider.classname = ''; }, 400); } } function updateoffset() { var offset = -currentindex * 100 + '%'; if(options.animation === 'fadein') { slider.style.opacity = 0; settimeout(function() { /*jshint -w030 */ options.transforms ? slider.style.transform = slider.style.webkittransform = 'translate3d(' + offset + ',0,0)' : slider.style.left = offset; slider.style.opacity = 1; }, 400); } else { /*jshint -w030 */ options.transforms ? slider.style.transform = slider.style.webkittransform = 'translate3d(' + offset + ',0,0)' : slider.style.left = offset; } } function testtransformssupport() { var div = document.createelement('div'), support = false; support = typeof div.style.perspective !== 'undefined' || typeof div.style.webkitperspective !== 'undefined'; return support; } 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) { if(element.addeventlistener) element.addeventlistener(event, callback, false); else element.attachevent('on' + event, callback); } return { run: run }; })();