slimbox/slimbox2.js

292 lines
9.6 KiB
JavaScript

/*
Standalone (no jQuery) lightweight Lightbox implementation with minimal CSS3 animations
Based on and mostly compatible with Slimbox2 <http://www.digitalia.be/software/slimbox2>
(c) 2012+ Vitaliy Filippov <http://yourcmc.ru/wiki>
MIT-style license.
Version 2015-05-01
*/
window.addListener = (function() {
return window.addEventListener
? function(el, type, fn) { el.addEventListener(type, fn, false); }
: function(el, type, fn) { el.attachEvent('on'+type, fn); };
})();
window.removeListener = (function() {
return window.removeEventListener
? function(el, type, fn) { el.removeEventListener(type, fn, false); }
: function(el, type, fn) { el.detachEvent('on'+type, fn); };
})();
addListener(window, 'load', function() {
// Global variables, accessible to Slimbox only
var options, images, activeImage = -1, activeURL, prevImage, nextImage, noFixed, middle, centerWidth, centerHeight,
ie6 = !window.XMLHttpRequest, hiddenElements = [], els = {}, css, keyActions,
// Preload images
preload = {}, preloadPrev = new Image(), preloadNext = new Image();
// Initialization
// Append the Slimbox HTML code at the bottom of the document
var d = document.createElement('div');
d.innerHTML = '<div id="lbOverlay" style="display: none"></div>'+
'<div id="lbCenter" style="display:none">'+
'<div id="lbWindow"><div id="lbImage"><div id="lbSizer" style="position: relative;">'+
'<a id="lbPrevLink" href="#"></a><a id="lbNextLink" href="#"></a></div></div></div>'+
'<div><div id="lbBottom">'+
'<a id="lbCloseLink" href="#"></a><div id="lbCaption"></div><div id="lbNumber"></div><div style="clear: both;" />'+
'</div></div></div>';
while (d.childNodes.length) {
document.body.appendChild(d.childNodes[0]);
}
var l = 'overlay center window image sizer prevLink nextLink bottom closeLink caption number'.split(' ');
for (var i = 0; i < l.length; i++) {
els[l[i]] = document.getElementById('lb'+l[i].substr(0, 1).toUpperCase()+l[i].substr(1));
}
els.prevLink.onclick = previous;
els.nextLink.onclick = next;
els.closeLink.onclick = close;
// Utils
function apply(a, b) {
for (var i in b) {
a[i] = b[i];
}
}
function foreach(a, f) {
for (var i in a) {
f(i, a[i]);
}
}
function curStyle(e) {
return window.getComputedStyle ? getComputedStyle(e) : e.currentStyle;
}
// API
// Open Slimbox with the specified parameters
window.slimbox = function(_images, startImage, _options) {
options = {
loop: false, // Allows to navigate between first and last images
overlayOpacity: 0.8, // 1 is opaque, 0 is completely transparent (change the color in the CSS file)
overlayFadeDuration: 400, // Duration of the overlay fade-in and fade-out animations (in milliseconds)
initialWidth: 250, // Initial width of the box (in pixels)
initialHeight: 250, // Initial height of the box (in pixels)
imageFadeDuration: 400, // Duration of the image fade-in animation (in milliseconds)
counterText: "Image {x} of {y}", // Translate or change as you wish, or set it to false to disable counter text for image groups
closeKeys: [27, 88, 67], // Array of keycodes to close Slimbox, default: Esc (27), 'x' (88), 'c' (67)
previousKeys: [37, 80], // Array of keycodes to navigate to the previous image, default: Left arrow (37), 'p' (80)
nextKeys: [39, 78] // Array of keycodes to navigate to the next image, default: Right arrow (39), 'n' (78)
};
if (_options) {
apply(options, _options);
}
css = document.createElement('style');
var D1 = options.overlayFadeDuration+'ms ease';
var D2 = options.imageFadeDuration+'ms ease';
var t1 = 'transition: opacity '+D1+';';
var t3 = 'transition: all '+D2+';';
css.type = 'text/css';
css.innerHTML =
'#lbOverlay { opacity: 0; '+t1+' -moz-'+t1+' -o-'+t1+' -webkit-'+t1+' }\n\
#lbOverlay.lbvisible { opacity: '+options.overlayOpacity+'; '+t1+' -moz-'+t1+' -o-'+t1+' -webkit-'+t1+' }\n\
#lbImage { opacity: 0; }\n\
#lbImage.lbvisible { opacity: 1; '+t1+' -moz-'+t1+' -o-'+t1+' -webkit-'+t1+' }\n\
#lbWindow, #lbCenter { '+t3+' -moz-'+t3+' -o-'+t3+' -webkit-'+t3+' }';
document.head.appendChild(css);
keyActions = {};
for (var i in options.closeKeys) {
keyActions[options.closeKeys[i]] = close;
}
for (var i in options.previousKeys) {
keyActions[options.previousKeys[i]] = previous;
}
for (var i in options.nextKeys) {
keyActions[options.nextKeys[i]] = next;
}
// The function is called for a single image, with URL and Title as first two arguments
if (typeof _images == "string") {
_images = [ { url: _images, title: startImage } ];
startImage = 0;
}
middle = ((window.innerHeight || document.documentElement.offsetHeight) / 2);
centerWidth = options.initialWidth;
centerHeight = options.initialHeight;
apply(els.center.style, {
top: Math.max(0, middle - (centerHeight / 2))+'px',
marginLeft: (-centerWidth/2)+'px',
display: ''
});
// Animate padding instead of width so all edges move synchronously
apply(els.window.style, {
paddingLeft: (centerWidth/2)+'px',
paddingRight: (centerWidth/2)+'px',
paddingTop: (centerHeight/2)+'px',
paddingBottom: (centerHeight/2)+'px'
});
var cs = curStyle(els.overlay);
noFixed = ie6 || cs && cs.position != "fixed";
if (noFixed) els.overlay.style.position = "absolute";
els.overlay.style.display = '';
setTimeout(function() {
els.overlay.className = 'lbvisible';
}, 15);
position();
setup(1);
images = _images;
options.loop = options.loop && (images.length > 1);
return changeImage(startImage);
};
// images = [ { element: clickOnWhatElement, url: imgUrl, title: titleHTML } ];
window.setSlimbox = function(images, options) {
foreach (images, function(i, img) {
if (!img.element) {
return;
}
if (img.element.nodeName == 'A') {
img.element.target = '';
}
addListener(img.element, 'click', function(ev) {
ev = ev || window.event;
ev.preventDefault();
window.slimbox(images, parseInt(''+i), options);
return false;
});
});
};
// Internal functions
function position() {
var l = document.documentElement.scrollLeft, w = document.documentElement.offsetWidth;
if (noFixed) {
apply(els.overlay.style, {
left: l+'px',
top: '0px',
width: w+'px',
height: document.documentElement.offsetHeight+'px'
});
}
}
function hide(nodes) {
for (var i = 0; i < nodes.length; i++) {
hiddenElements.push([nodes[i], nodes[i].style.visibility]);
nodes[i].style.visibility = "hidden";
}
}
function setup(open) {
if (open) {
hide(document.getElementsByTagName('object'));
hide(document.getElementsByTagName(ie6 ? 'select' : 'embed'));
} else {
for (var i in hiddenElements) {
hiddenElements[i][0].style.visibility = hiddenElements[i][1];
}
hiddenElements = [];
}
var f = (open ? addListener : removeListener);
f(window, 'scroll', position);
f(window, 'resize', position);
f(document, 'keydown', keyDown);
}
function keyDown(event) {
event = event || window.event;
var code = event.keyCode;
// Prevent default keyboard action (like navigating inside the page)
return keyActions[code] ? keyActions[code]() : false;
}
function previous() {
return changeImage(prevImage);
}
function next() {
return changeImage(nextImage);
}
function changeImage(imageIndex) {
if (imageIndex >= 0) {
activeImage = imageIndex;
activeURL = images[activeImage].url;
prevImage = (activeImage || (options.loop ? images.length : 0)) - 1;
nextImage = ((activeImage + 1) % images.length) || (options.loop ? 0 : -1);
els.center.className = "lbLoading";
els.image.className = '';
preload = new Image();
preload.onload = animateBox;
preload.src = activeURL;
}
return false;
}
function animateBox() {
els.center.className = "";
els.image.style.backgroundImage = "url(" + activeURL + ")";
els.image.style.marginLeft = (-preload.width/2)+'px';
els.image.style.marginTop = (-preload.height/2)+'px';
els.image.style.width = preload.width+'px';
els.image.style.height = preload.height+'px';
els.prevLink.style.height = preload.height+'px';
els.nextLink.style.height = preload.height+'px';
els.caption.innerHTML = images[activeImage]['title'] || "";
els.number.innerHTML = (((images.length > 1) && options.counterText) || "").replace(/{x}/, activeImage + 1).replace(/{y}/, images.length);
if (prevImage >= 0) preloadPrev.src = images[prevImage]['url'];
if (nextImage >= 0) preloadNext.src = images[nextImage]['url'];
centerWidth = preload.width;
centerHeight = preload.height;
if (els.window.clientHeight != centerHeight) {
var top = Math.max(0, middle - (centerHeight / 2));
els.window.style.paddingTop = els.window.style.paddingBottom = (centerHeight/2)+'px';
els.center.style.top = top+'px';
}
if (els.window.clientWidth != centerWidth) {
els.window.style.paddingLeft = els.window.style.paddingRight = (centerWidth/2)+'px';
els.center.style.marginLeft = (-centerWidth/2)+'px';
}
els.image.className = 'lbvisible';
if (prevImage >= 0) els.prevLink.style.display = '';
if (nextImage >= 0) els.nextLink.style.display = '';
}
function stop() {
preload.onload = null;
preload.src = preloadPrev.src = preloadNext.src = activeURL;
els.prevLink.style.display = 'none';
els.nextLink.style.display = 'none';
setup(0);
}
function close() {
if (activeImage >= 0) {
stop();
activeImage = prevImage = nextImage = -1;
els.center.style.display = 'none';
els.image.style.width = els.image.style.height = '1px';
els.overlay.className = '';
setTimeout(function() {
els.overlay.style.display = 'none';
document.head.removeChild(css);
}, options.overlayFadeDuration);
}
return false;
}
});