// ----------------------------------------------------------------------------------- // // 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(); });