// -----------------------------------------------------------------------------------
//
// lightbox v2.04
// by lokesh dhakar - http://www.lokeshdhakar.com
// last modification: 2/9/08
//
// for more information, visit:
// http://lokeshdhakar.com/projects/lightbox2/
//
// licensed under the creative commons attribution 2.5 license - http://creativecommons.org/licenses/by/2.5/
// - free for use in both personal and commercial projects
// - attribution requires leaving author name, author link, and the license info intact.
//
// thanks: scott upton(uptonic.com), peter-paul koch(quirksmode.com), and thomas fuchs(mir.aculo.us) for ideas, libs, and snippets.
// artemy tregubenko (arty.name) for cleanup and help in updating to latest ver of proto-aculous.
//
// -----------------------------------------------------------------------------------
/*
table of contents
-----------------
configuration
lightbox class declaration
- initialize()
- updateimagelist()
- start()
- changeimage()
- resizeimagecontainer()
- showimage()
- updatedetails()
- updatenav()
- enablekeyboardnav()
- disablekeyboardnav()
- keyboardaction()
- preloadneighborimages()
- end()
function calls
- document.observe()
*/
// -----------------------------------------------------------------------------------
//
// configurationl
//
lightboxoptions = object.extend({
fileloadingimage: 'plugins/lightbox/images/loading.gif',
filebottomnavcloseimage: 'plugins/lightbox/images/closelabel_cn.gif',
overlayopacity: 0.8, // controls transparency of shadow overlay
animate: true, // toggles resizing animations
resizespeed: 7, // controls the speed of the image resizing animations (1=slowest and 10=fastest)
bordersize: 10, //if you adjust the padding in the css, you will need to update this variable
// when grouping images this is used to write: image # of #.
// change it for non-english localization
labelimage: "image",
labelof: "of"
}, window.lightboxoptions || {});
// -----------------------------------------------------------------------------------
var lightbox = class.create();
lightbox.prototype = {
imagearray: [],
activeimage: undefined,
// initialize()
// constructor runs on completion of the dom loading. calls updateimagelist and then
// the function inserts html at the bottom of the page which is used to display the shadow
// overlay and the image container.
//
initialize: function() {
this.updateimagelist();
this.keyboardaction = this.keyboardaction.bindaseventlistener(this);
if (lightboxoptions.resizespeed > 10) lightboxoptions.resizespeed = 10;
if (lightboxoptions.resizespeed < 1) lightboxoptions.resizespeed = 1;
this.resizeduration = lightboxoptions.animate ? ((11 - lightboxoptions.resizespeed) * 0.15) : 0;
this.overlayduration = lightboxoptions.animate ? 0.2 : 0; // shadow fade in/out duration
// when lightbox starts it will resize itself from 250 by 250 to the current image dimension.
// if animations are turned off, it will be hidden as to prevent a flicker of a
// white 250 by 250 box.
var size = (lightboxoptions.animate ? 250 : 1) + 'px';
// code inserts html at the bottom of the page that looks similar to this:
//
//
//
//
//
//
//
//
//
//
//
//
var objbody = $$('body')[0];
objbody.appendchild(builder.node('div',{id:'overlay'}));
objbody.appendchild(builder.node('div',{id:'lightbox'}, [
builder.node('div',{id:'outerimagecontainer'},
builder.node('div',{id:'imagecontainer'}, [
builder.node('img',{id:'lightboximage'}),
builder.node('div',{id:'hovernav'}, [
builder.node('a',{id:'prevlink', href: '#' }),
builder.node('a',{id:'nextlink', href: '#' })
]),
builder.node('div',{id:'loading'},
builder.node('a',{id:'loadinglink', href: '#' },
builder.node('img', {src: lightboxoptions.fileloadingimage})
)
)
])
),
builder.node('div', {id:'imagedatacontainer'},
builder.node('div',{id:'imagedata'}, [
builder.node('div',{id:'imagedetails'}, [
builder.node('span',{id:'caption'}),
builder.node('span',{id:'numberdisplay'})
]),
builder.node('div',{id:'bottomnav'},
builder.node('a',{id:'bottomnavclose', href: '#' },
builder.node('img', { src: lightboxoptions.filebottomnavcloseimage })
)
)
])
)
]));
$('overlay').hide().observe('click', (function() { this.end(); }).bind(this));
$('lightbox').hide().observe('click', (function(event) { if (event.element().id == 'lightbox') this.end(); }).bind(this));
$('outerimagecontainer').setstyle({ width: size, height: size });
$('prevlink').observe('click', (function(event) { event.stop(); this.changeimage(this.activeimage - 1); }).bindaseventlistener(this));
$('nextlink').observe('click', (function(event) { event.stop(); this.changeimage(this.activeimage + 1); }).bindaseventlistener(this));
$('loadinglink').observe('click', (function(event) { event.stop(); this.end(); }).bind(this));
$('bottomnavclose').observe('click', (function(event) { event.stop(); this.end(); }).bind(this));
var th = this;
(function(){
var ids =
'overlay lightbox outerimagecontainer imagecontainer lightboximage hovernav prevlink nextlink loading loadinglink ' +
'imagedatacontainer imagedata imagedetails caption numberdisplay bottomnav bottomnavclose';
$w(ids).each(function(id){ th[id] = $(id); });
}).defer();
},
//
// updateimagelist()
// loops through anchor tags looking for 'lightbox' references and applies onclick
// events to appropriate links. you can rerun after dynamically adding images w/ajax.
//
updateimagelist: function() {
this.updateimagelist = prototype.emptyfunction;
document.observe('click', (function(event){
var target = event.findelement('a[rel^=lightbox]') || event.findelement('area[rel^=lightbox]');
if (target) {
event.stop();
this.start(target);
}
}).bind(this));
},
//
// start()
// display overlay and lightbox. if image is part of a set, add siblings to imagearray.
//
start: function(imagelink) {
$$('select', 'object', 'embed').each(function(node){ node.style.visibility = 'hidden' });
// stretch overlay to fill page and fade in
var arraypagesize = this.getpagesize();
$('overlay').setstyle({ width: arraypagesize[0] + 'px', height: arraypagesize[1] + 'px' });
new effect.appear(this.overlay, { duration: this.overlayduration, from: 0.0, to: lightboxoptions.overlayopacity });
this.imagearray = [];
var imagenum = 0;
if ((imagelink.rel == 'lightbox')){
// if image is not part of a set, add single image to imagearray
this.imagearray.push([imagelink.href, imagelink.title]);
} else {
// if image is part of a set..
this.imagearray =
$$(imagelink.tagname + '[href][rel="' + imagelink.rel + '"]').
collect(function(anchor){ return [anchor.href, anchor.title]; }).
uniq();
while (this.imagearray[imagenum][0] != imagelink.href) { imagenum++; }
}
// calculate top and left offset for the lightbox
var arraypagescroll = document.viewport.getscrolloffsets();
var lightboxtop = arraypagescroll[1] + (document.viewport.getheight() / 10);
var lightboxleft = arraypagescroll[0];
this.lightbox.setstyle({ top: lightboxtop + 'px', left: lightboxleft + 'px' }).show();
this.changeimage(imagenum);
},
//
// changeimage()
// hide most elements and preload image in preparation for resizing image container.
//
changeimage: function(imagenum) {
this.activeimage = imagenum; // update global var
// hide elements during transition
if (lightboxoptions.animate) this.loading.show();
this.lightboximage.hide();
this.hovernav.hide();
this.prevlink.hide();
this.nextlink.hide();
// hack: opera9 does not currently support scriptaculous opacity and appear fx
this.imagedatacontainer.setstyle({opacity: .0001});
this.numberdisplay.hide();
var imgpreloader = new image();
// once image is preloaded, resize image container
imgpreloader.onload = (function(){
this.lightboximage.src = this.imagearray[this.activeimage][0];
this.resizeimagecontainer(imgpreloader.width, imgpreloader.height);
}).bind(this);
imgpreloader.src = this.imagearray[this.activeimage][0];
},
//
// resizeimagecontainer()
//
resizeimagecontainer: function(imgwidth, imgheight) {
// get current width and height
var widthcurrent = this.outerimagecontainer.getwidth();
var heightcurrent = this.outerimagecontainer.getheight();
// get new width and height
var widthnew = (imgwidth + lightboxoptions.bordersize * 2);
var heightnew = (imgheight + lightboxoptions.bordersize * 2);
// scalars based on change from old to new
var xscale = (widthnew / widthcurrent) * 100;
var yscale = (heightnew / heightcurrent) * 100;
// calculate size difference between new and old image, and resize if necessary
var wdiff = widthcurrent - widthnew;
var hdiff = heightcurrent - heightnew;
if (hdiff != 0) new effect.scale(this.outerimagecontainer, yscale, {scalex: false, duration: this.resizeduration, queue: 'front'});
if (wdiff != 0) new effect.scale(this.outerimagecontainer, xscale, {scaley: false, duration: this.resizeduration, delay: this.resizeduration});
// if new and old image are same size and no scaling transition is necessary,
// do a quick pause to prevent image flicker.
var timeout = 0;
if ((hdiff == 0) && (wdiff == 0)){
timeout = 100;
if (prototype.browser.ie) timeout = 250;
}
(function(){
this.prevlink.setstyle({ height: imgheight + 'px' });
this.nextlink.setstyle({ height: imgheight + 'px' });
this.imagedatacontainer.setstyle({ width: widthnew + 'px' });
this.showimage();
}).bind(this).delay(timeout / 1000);
},
//
// showimage()
// display image and begin preloading neighbors.
//
showimage: function(){
this.loading.hide();
new effect.appear(this.lightboximage, {
duration: this.resizeduration,
queue: 'end',
afterfinish: (function(){ this.updatedetails(); }).bind(this)
});
this.preloadneighborimages();
},
//
// updatedetails()
// display caption, image number, and bottom nav.
//
updatedetails: function() {
// if caption is not null
if (this.imagearray[this.activeimage][1] != ""){
this.caption.update(this.imagearray[this.activeimage][1]).show();
}
// if image is part of set display 'image x of x'
if (this.imagearray.length > 1){
this.numberdisplay.update( lightboxoptions.labelimage + ' ' + (this.activeimage + 1) + ' ' + lightboxoptions.labelof + ' ' + this.imagearray.length).show();
}
new effect.parallel(
[
new effect.slidedown(this.imagedatacontainer, { sync: true, duration: this.resizeduration, from: 0.0, to: 1.0 }),
new effect.appear(this.imagedatacontainer, { sync: true, duration: this.resizeduration })
],
{
duration: this.resizeduration,
afterfinish: (function() {
// update overlay size and update nav
var arraypagesize = this.getpagesize();
this.overlay.setstyle({ height: arraypagesize[1] + 'px' });
this.updatenav();
}).bind(this)
}
);
},
//
// updatenav()
// display appropriate previous and next hover navigation.
//
updatenav: function() {
this.hovernav.show();
// if not first image in set, display prev image button
if (this.activeimage > 0) this.prevlink.show();
// if not last image in set, display next image button
if (this.activeimage < (this.imagearray.length - 1)) this.nextlink.show();
this.enablekeyboardnav();
},
//
// enablekeyboardnav()
//
enablekeyboardnav: function() {
document.observe('keydown', this.keyboardaction);
},
//
// disablekeyboardnav()
//
disablekeyboardnav: function() {
document.stopobserving('keydown', this.keyboardaction);
},
//
// keyboardaction()
//
keyboardaction: function(event) {
var keycode = event.keycode;
var escapekey;
if (event.dom_vk_escape) { // mozilla
escapekey = event.dom_vk_escape;
} else { // ie
escapekey = 27;
}
var key = string.fromcharcode(keycode).tolowercase();
if (key.match(/x|o|c/) || (keycode == escapekey)){ // close lightbox
this.end();
} else if ((key == 'p') || (keycode == 37)){ // display previous image
if (this.activeimage != 0){
this.disablekeyboardnav();
this.changeimage(this.activeimage - 1);
}
} else if ((key == 'n') || (keycode == 39)){ // display next image
if (this.activeimage != (this.imagearray.length - 1)){
this.disablekeyboardnav();
this.changeimage(this.activeimage + 1);
}
}
},
//
// preloadneighborimages()
// preload previous and next images.
//
preloadneighborimages: function(){
var preloadnextimage, preloadprevimage;
if (this.imagearray.length > this.activeimage + 1){
preloadnextimage = new image();
preloadnextimage.src = this.imagearray[this.activeimage + 1][0];
}
if (this.activeimage > 0){
preloadprevimage = new image();
preloadprevimage.src = this.imagearray[this.activeimage - 1][0];
}
},
//
// end()
//
end: function() {
this.disablekeyboardnav();
this.lightbox.hide();
new effect.fade(this.overlay, { duration: this.overlayduration });
$$('select', 'object', 'embed').each(function(node){ node.style.visibility = 'visible' });
},
//
// getpagesize()
//
getpagesize: function() {
var xscroll, yscroll;
if (window.innerheight && window.scrollmaxy) {
xscroll = window.innerwidth + window.scrollmaxx;
yscroll = window.innerheight + window.scrollmaxy;
} else if (document.body.scrollheight > document.body.offsetheight){ // all but explorer mac
xscroll = document.body.scrollwidth;
yscroll = document.body.scrollheight;
} else { // explorer mac...would also work in explorer 6 strict, mozilla and safari
xscroll = document.body.offsetwidth;
yscroll = document.body.offsetheight;
}
var windowwidth, windowheight;
if (self.innerheight) { // all except explorer
if(document.documentelement.clientwidth){
windowwidth = document.documentelement.clientwidth;
} else {
windowwidth = self.innerwidth;
}
windowheight = self.innerheight;
} else if (document.documentelement && document.documentelement.clientheight) { // explorer 6 strict mode
windowwidth = document.documentelement.clientwidth;
windowheight = document.documentelement.clientheight;
} else if (document.body) { // other explorers
windowwidth = document.body.clientwidth;
windowheight = document.body.clientheight;
}
// for small pages with total height less then height of the viewport
if(yscroll < windowheight){
pageheight = windowheight;
} else {
pageheight = yscroll;
}
// for small pages with total width less then width of the viewport
if(xscroll < windowwidth){
pagewidth = xscroll;
} else {
pagewidth = windowwidth;
}
return [pagewidth,pageheight];
}
}
document.observe('dom:loaded', function () { new lightbox(); });