292 lines
9.6 KiB
JavaScript
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;
|
|
}
|
|
|
|
});
|