1 // -----------------------------------------------------------------------------------
4 // by Lokesh Dhakar - http://www.huddletogether.com
7 // For more information on this script, visit:
8 // http://huddletogether.com/projects/lightbox2/
10 // Licensed under the Creative Commons Attribution 2.5 License - http://creativecommons.org/licenses/by/2.5/
12 // Credit also due to those who have helped, inspired, and made their code available to the public.
13 // Including: Scott Upton(uptonic.com), Peter-Paul Koch(quirksmode.com), Thomas Fuchs(mir.aculo.us), and others.
16 // -----------------------------------------------------------------------------------
24 Extending Built-in Objects
25 - Object.extend(Element)
26 - Array.prototype.removeDuplicates()
27 - Array.prototype.empty()
29 Lightbox Class Declaration
34 - resizeImageContainer()
39 - disableKeyboardNav()
41 - preloadNeighborImages()
44 Miscellaneous Functions
57 - addLoadEvent(initLightbox)
60 // -----------------------------------------------------------------------------------
65 var fileLoadingImage
= "lightbox/images/loading.gif";
66 var fileBottomNavCloseImage
= "lightbox/images/closelabel.gif";
68 var overlayOpacity
= 0.8; // controls transparency of shadow overlay
70 var animate
= true; // toggles resizing animations
71 var resizeSpeed
= 8; // controls the speed of the image resizing animations (1=slowest and 10=fastest)
73 var borderSize
= 10; //if you adjust the padding in the CSS, you will need to update this variable
75 // -----------------------------------------------------------------------------------
80 var imageArray
= new Array
;
84 overlayDuration
= 0.2; // shadow fade in/out duration
85 if(resizeSpeed
> 10){ resizeSpeed
= 10;}
86 if(resizeSpeed
< 1){ resizeSpeed
= 1;}
87 resizeDuration
= (11 - resizeSpeed
) * 0.15;
93 // -----------------------------------------------------------------------------------
96 // Additional methods for Element added by SU, Couloir
97 // - further additions by Lokesh Dhakar (huddletogether.com)
99 Object
.extend(Element
, {
100 getWidth: function(element
) {
101 element
= $(element
);
102 return element
.offsetWidth
;
104 setWidth: function(element
,w
) {
105 element
= $(element
);
106 element
.style
.width
= w
+"px";
108 setHeight: function(element
,h
) {
109 element
= $(element
);
110 element
.style
.height
= h
+"px";
112 setTop: function(element
,t
) {
113 element
= $(element
);
114 element
.style
.top
= t
+"px";
116 setLeft: function(element
,l
) {
117 element
= $(element
);
118 element
.style
.left
= l
+"px";
120 setSrc: function(element
,src
) {
121 element
= $(element
);
124 setHref: function(element
,href
) {
125 element
= $(element
);
128 setInnerHTML: function(element
,content
) {
129 element
= $(element
);
130 element
.innerHTML
= content
;
134 // -----------------------------------------------------------------------------------
137 // Extending built-in Array object
138 // - array.removeDuplicates()
141 Array
.prototype.removeDuplicates = function () {
142 for(i
= 0; i
< this.length
; i
++){
143 for(j
= this.length
-1; j
>i
; j
--){
144 if(this[i
][0] == this[j
][0]){
151 // -----------------------------------------------------------------------------------
153 Array
.prototype.empty = function () {
154 for(i
= 0; i
<= this.length
; i
++){
159 // -----------------------------------------------------------------------------------
162 // Lightbox Class Declaration
166 // - resizeImageContainer()
170 // - enableKeyboardNav()
171 // - disableKeyboardNav()
172 // - keyboardNavAction()
173 // - preloadNeighborImages()
176 // Structuring of code inspired by Scott Upton (http://www.uptonic.com/)
178 var Lightbox
= Class
.create();
180 Lightbox
.prototype = {
183 // Constructor runs on completion of the DOM loading. Calls updateImageList and then
184 // the function inserts html at the bottom of the page which is used to display the shadow
185 // overlay and the image container.
187 initialize: function() {
189 this.updateImageList();
191 // Code inserts html at the bottom of the page that looks similar to this:
193 // <div id="overlay"></div>
194 // <div id="lightbox">
195 // <div id="outerImageContainer">
196 // <div id="imageContainer">
197 // <img id="lightboxImage">
198 // <div style="" id="hoverNav">
199 // <a href="#" id="prevLink"></a>
200 // <a href="#" id="nextLink"></a>
202 // <div id="loading">
203 // <a href="#" id="loadingLink">
204 // <img src="images/loading.gif">
209 // <div id="imageDataContainer">
210 // <div id="imageData">
211 // <div id="imageDetails">
212 // <span id="caption"></span>
213 // <span id="numberDisplay"></span>
215 // <div id="bottomNav">
216 // <a href="#" id="bottomNavClose">
217 // <img src="images/close.gif">
225 var objBody
= document
.getElementsByTagName("body").item(0);
227 var objOverlay
= document
.createElement("div");
228 objOverlay
.setAttribute('id','overlay');
229 objOverlay
.style
.display
= 'none';
230 objOverlay
.onclick = function() { myLightbox
.end(); }
231 objBody
.appendChild(objOverlay
);
233 var objLightbox
= document
.createElement("div");
234 objLightbox
.setAttribute('id','lightbox');
235 objLightbox
.style
.display
= 'none';
236 objLightbox
.onclick = function(e
) { // close Lightbox is user clicks shadow overlay
237 if (!e
) var e
= window
.event
;
238 var clickObj
= Event
.element(e
).id
;
239 if ( clickObj
== 'lightbox') {
243 objBody
.appendChild(objLightbox
);
245 var objOuterImageContainer
= document
.createElement("div");
246 objOuterImageContainer
.setAttribute('id','outerImageContainer');
247 objLightbox
.appendChild(objOuterImageContainer
);
249 // When Lightbox starts it will resize itself from 250 by 250 to the current image dimension.
250 // If animations are turned off, it will be hidden as to prevent a flicker of a
251 // white 250 by 250 box.
253 Element
.setWidth('outerImageContainer', 250);
254 Element
.setHeight('outerImageContainer', 250);
256 Element
.setWidth('outerImageContainer', 1);
257 Element
.setHeight('outerImageContainer', 1);
260 var objImageContainer
= document
.createElement("div");
261 objImageContainer
.setAttribute('id','imageContainer');
262 objOuterImageContainer
.appendChild(objImageContainer
);
264 var objLightboxImage
= document
.createElement("img");
265 objLightboxImage
.setAttribute('id','lightboxImage');
266 objImageContainer
.appendChild(objLightboxImage
);
268 var objHoverNav
= document
.createElement("div");
269 objHoverNav
.setAttribute('id','hoverNav');
270 objImageContainer
.appendChild(objHoverNav
);
272 var objPrevLink
= document
.createElement("a");
273 objPrevLink
.setAttribute('id','prevLink');
274 objPrevLink
.setAttribute('href','#');
275 objHoverNav
.appendChild(objPrevLink
);
277 var objNextLink
= document
.createElement("a");
278 objNextLink
.setAttribute('id','nextLink');
279 objNextLink
.setAttribute('href','#');
280 objHoverNav
.appendChild(objNextLink
);
282 var objLoading
= document
.createElement("div");
283 objLoading
.setAttribute('id','loading');
284 objImageContainer
.appendChild(objLoading
);
286 var objLoadingLink
= document
.createElement("a");
287 objLoadingLink
.setAttribute('id','loadingLink');
288 objLoadingLink
.setAttribute('href','#');
289 objLoadingLink
.onclick = function() { myLightbox
.end(); return false; }
290 objLoading
.appendChild(objLoadingLink
);
292 var objLoadingImage
= document
.createElement("img");
293 objLoadingImage
.setAttribute('src', fileLoadingImage
);
294 objLoadingLink
.appendChild(objLoadingImage
);
296 var objImageDataContainer
= document
.createElement("div");
297 objImageDataContainer
.setAttribute('id','imageDataContainer');
298 objLightbox
.appendChild(objImageDataContainer
);
300 var objImageData
= document
.createElement("div");
301 objImageData
.setAttribute('id','imageData');
302 objImageDataContainer
.appendChild(objImageData
);
304 var objImageDetails
= document
.createElement("div");
305 objImageDetails
.setAttribute('id','imageDetails');
306 objImageData
.appendChild(objImageDetails
);
308 var objCaption
= document
.createElement("span");
309 objCaption
.setAttribute('id','caption');
310 objImageDetails
.appendChild(objCaption
);
312 var objNumberDisplay
= document
.createElement("span");
313 objNumberDisplay
.setAttribute('id','numberDisplay');
314 objImageDetails
.appendChild(objNumberDisplay
);
316 var objBottomNav
= document
.createElement("div");
317 objBottomNav
.setAttribute('id','bottomNav');
318 objImageData
.appendChild(objBottomNav
);
320 var objBottomNavCloseLink
= document
.createElement("a");
321 objBottomNavCloseLink
.setAttribute('id','bottomNavClose');
322 objBottomNavCloseLink
.setAttribute('href','#');
323 objBottomNavCloseLink
.onclick = function() { myLightbox
.end(); return false; }
324 objBottomNav
.appendChild(objBottomNavCloseLink
);
326 var objBottomNavCloseImage
= document
.createElement("img");
327 objBottomNavCloseImage
.setAttribute('src', fileBottomNavCloseImage
);
328 objBottomNavCloseLink
.appendChild(objBottomNavCloseImage
);
334 // Loops through anchor tags looking for 'lightbox' references and applies onclick
335 // events to appropriate links. You can rerun after dynamically adding images w/ajax.
337 updateImageList: function() {
338 if (!document
.getElementsByTagName
){ return; }
339 var anchors
= document
.getElementsByTagName('a');
340 var areas
= document
.getElementsByTagName('area');
342 // loop through all anchor tags
343 for (var i
=0; i
<anchors
.length
; i
++){
344 var anchor
= anchors
[i
];
346 var relAttribute
= String(anchor
.getAttribute('rel'));
348 // use the string.match() method to catch 'lightbox' references in the rel attribute
349 if (anchor
.getAttribute('href') && (relAttribute
.toLowerCase().match('lightbox'))){
350 anchor
.onclick = function () {myLightbox
.start(this); return false;}
354 // loop through all area tags
355 // todo: combine anchor & area tag loops
356 for (var i
=0; i
< areas
.length
; i
++){
359 var relAttribute
= String(area
.getAttribute('rel'));
361 // use the string.match() method to catch 'lightbox' references in the rel attribute
362 if (area
.getAttribute('href') && (relAttribute
.toLowerCase().match('lightbox'))){
363 area
.onclick = function () {myLightbox
.start(this); return false;}
371 // Display overlay and lightbox. If image is part of a set, add siblings to imageArray.
373 start: function(imageLink
) {
378 // stretch overlay to fill page and fade in
379 var arrayPageSize
= getPageSize();
380 Element
.setWidth('overlay', arrayPageSize
[0]);
381 Element
.setHeight('overlay', arrayPageSize
[1]);
383 new Effect
.Appear('overlay', { duration: overlayDuration
, from: 0.0, to: overlayOpacity
});
388 if (!document
.getElementsByTagName
){ return; }
389 var anchors
= document
.getElementsByTagName( imageLink
.tagName
);
391 // if image is NOT part of a set..
392 if((imageLink
.getAttribute('rel') == 'lightbox')){
393 // add single image to imageArray
394 imageArray
.push(new Array(imageLink
.getAttribute('href'), imageLink
.getAttribute('title')));
396 // if image is part of a set..
398 // loop through anchors, find other images in set, and add them to imageArray
399 for (var i
=0; i
<anchors
.length
; i
++){
400 var anchor
= anchors
[i
];
401 if (anchor
.getAttribute('href') && (anchor
.getAttribute('rel') == imageLink
.getAttribute('rel'))){
402 imageArray
.push(new Array(anchor
.getAttribute('href'), anchor
.getAttribute('title')));
405 imageArray
.removeDuplicates();
406 while(imageArray
[imageNum
][0] != imageLink
.getAttribute('href')) { imageNum
++;}
409 // calculate top and left offset for the lightbox
410 var arrayPageScroll
= getPageScroll();
411 var lightboxTop
= arrayPageScroll
[1] + (arrayPageSize
[3] / 10);
412 var lightboxLeft
= arrayPageScroll
[0];
413 Element
.setTop('lightbox', lightboxTop
);
414 Element
.setLeft('lightbox', lightboxLeft
);
416 Element
.show('lightbox');
418 this.changeImage(imageNum
);
423 // Hide most elements and preload image in preparation for resizing image container.
425 changeImage: function(imageNum
) {
427 activeImage
= imageNum
; // update global var
429 // hide elements during transition
430 if(animate
){ Element
.show('loading');}
431 Element
.hide('lightboxImage');
432 Element
.hide('hoverNav');
433 Element
.hide('prevLink');
434 Element
.hide('nextLink');
435 Element
.hide('imageDataContainer');
436 Element
.hide('numberDisplay');
438 imgPreloader
= new Image();
440 // once image is preloaded, resize image container
441 imgPreloader
.onload=function(){
442 Element
.setSrc('lightboxImage', imageArray
[activeImage
][0]);
443 myLightbox
.resizeImageContainer(imgPreloader
.width
, imgPreloader
.height
);
445 imgPreloader
.onload=function(){}; // clear onLoad, IE behaves irratically with animated gifs otherwise
447 imgPreloader
.src
= imageArray
[activeImage
][0];
451 // resizeImageContainer()
453 resizeImageContainer: function( imgWidth
, imgHeight
) {
455 // get curren width and height
456 this.widthCurrent
= Element
.getWidth('outerImageContainer');
457 this.heightCurrent
= Element
.getHeight('outerImageContainer');
459 // get new width and height
460 var widthNew
= (imgWidth
+ (borderSize
* 2));
461 var heightNew
= (imgHeight
+ (borderSize
* 2));
463 // scalars based on change from old to new
464 this.xScale
= ( widthNew
/ this.widthCurrent
) * 100;
465 this.yScale
= ( heightNew
/ this.heightCurrent
) * 100;
467 // calculate size difference between new and old image, and resize if necessary
468 wDiff
= this.widthCurrent
- widthNew
;
469 hDiff
= this.heightCurrent
- heightNew
;
471 if(!( hDiff
== 0)){ new Effect
.Scale('outerImageContainer', this.yScale
, {scaleX: false, duration: resizeDuration
, queue: 'front'}); }
472 if(!( wDiff
== 0)){ new Effect
.Scale('outerImageContainer', this.xScale
, {scaleY: false, delay: resizeDuration
, duration: resizeDuration
}); }
474 // if new and old image are same size and no scaling transition is necessary,
475 // do a quick pause to prevent image flicker.
476 if((hDiff
== 0) && (wDiff
== 0)){
477 if (navigator
.appVersion
.indexOf("MSIE")!=-1){ pause(250); } else { pause(100);}
480 Element
.setHeight('prevLink', imgHeight
);
481 Element
.setHeight('nextLink', imgHeight
);
482 Element
.setWidth( 'imageDataContainer', widthNew
);
489 // Display image and begin preloading neighbors.
491 showImage: function(){
492 Element
.hide('loading');
493 new Effect
.Appear('lightboxImage', { duration: resizeDuration
, queue: 'end', afterFinish: function(){ myLightbox
.updateDetails(); } });
494 this.preloadNeighborImages();
499 // Display caption, image number, and bottom nav.
501 updateDetails: function() {
503 // if caption is not null
504 if(imageArray
[activeImage
][1]){
505 Element
.show('caption');
506 Element
.setInnerHTML( 'caption', imageArray
[activeImage
][1]);
509 // if image is part of set display 'Image x of x'
510 if(imageArray
.length
> 1){
511 Element
.show('numberDisplay');
512 Element
.setInnerHTML( 'numberDisplay', "Image " + eval(activeImage
+ 1) + " of " + imageArray
.length
);
516 [ new Effect
.SlideDown( 'imageDataContainer', { sync: true, duration: resizeDuration
, from: 0.0, to: 1.0 }),
517 new Effect
.Appear('imageDataContainer', { sync: true, duration: resizeDuration
}) ],
518 { duration: resizeDuration
, afterFinish: function() {
519 // update overlay size and update nav
520 var arrayPageSize
= getPageSize();
521 Element
.setHeight('overlay', arrayPageSize
[1]);
522 myLightbox
.updateNav();
530 // Display appropriate previous and next hover navigation.
532 updateNav: function() {
534 Element
.show('hoverNav');
536 // if not first image in set, display prev image button
537 if(activeImage
!= 0){
538 Element
.show('prevLink');
539 document
.getElementById('prevLink').onclick = function() {
540 myLightbox
.changeImage(activeImage
- 1); return false;
544 // if not last image in set, display next image button
545 if(activeImage
!= (imageArray
.length
- 1)){
546 Element
.show('nextLink');
547 document
.getElementById('nextLink').onclick = function() {
548 myLightbox
.changeImage(activeImage
+ 1); return false;
552 this.enableKeyboardNav();
556 // enableKeyboardNav()
558 enableKeyboardNav: function() {
559 document
.onkeydown
= this.keyboardAction
;
563 // disableKeyboardNav()
565 disableKeyboardNav: function() {
566 document
.onkeydown
= '';
572 keyboardAction: function(e
) {
573 if (e
== null) { // ie
574 keycode
= event
.keyCode
;
578 escapeKey
= e
.DOM_VK_ESCAPE
;
581 key
= String
.fromCharCode(keycode
).toLowerCase();
583 if((key
== 'x') || (key
== 'o') || (key
== 'c') || (keycode
== escapeKey
)){ // close lightbox
585 } else if((key
== 'p') || (keycode
== 37)){ // display previous image
586 if(activeImage
!= 0){
587 myLightbox
.disableKeyboardNav();
588 myLightbox
.changeImage(activeImage
- 1);
590 } else if((key
== 'n') || (keycode
== 39)){ // display next image
591 if(activeImage
!= (imageArray
.length
- 1)){
592 myLightbox
.disableKeyboardNav();
593 myLightbox
.changeImage(activeImage
+ 1);
600 // preloadNeighborImages()
601 // Preload previous and next images.
603 preloadNeighborImages: function(){
605 if((imageArray
.length
- 1) > activeImage
){
606 preloadNextImage
= new Image();
607 preloadNextImage
.src
= imageArray
[activeImage
+ 1][0];
610 preloadPrevImage
= new Image();
611 preloadPrevImage
.src
= imageArray
[activeImage
- 1][0];
620 this.disableKeyboardNav();
621 Element
.hide('lightbox');
622 new Effect
.Fade('overlay', { duration: overlayDuration
});
628 // -----------------------------------------------------------------------------------
632 // Returns array with x,y page scroll values.
633 // Core code from - quirksmode.com
635 function getPageScroll(){
637 var xScroll
, yScroll
;
639 if (self
.pageYOffset
) {
640 yScroll
= self
.pageYOffset
;
641 xScroll
= self
.pageXOffset
;
642 } else if (document
.documentElement
&& document
.documentElement
.scrollTop
){ // Explorer 6 Strict
643 yScroll
= document
.documentElement
.scrollTop
;
644 xScroll
= document
.documentElement
.scrollLeft
;
645 } else if (document
.body
) {// all other Explorers
646 yScroll
= document
.body
.scrollTop
;
647 xScroll
= document
.body
.scrollLeft
;
650 arrayPageScroll
= new Array(xScroll
,yScroll
)
651 return arrayPageScroll
;
654 // -----------------------------------------------------------------------------------
658 // Returns array with page width, height and window width, height
659 // Core code from - quirksmode.com
660 // Edit for Firefox by pHaez
662 function getPageSize(){
664 var xScroll
, yScroll
;
666 if (window
.innerHeight
&& window
.scrollMaxY
) {
667 xScroll
= window
.innerWidth
+ window
.scrollMaxX
;
668 yScroll
= window
.innerHeight
+ window
.scrollMaxY
;
669 } else if (document
.body
.scrollHeight
> document
.body
.offsetHeight
){ // all but Explorer Mac
670 xScroll
= document
.body
.scrollWidth
;
671 yScroll
= document
.body
.scrollHeight
;
672 } else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
673 xScroll
= document
.body
.offsetWidth
;
674 yScroll
= document
.body
.offsetHeight
;
677 var windowWidth
, windowHeight
;
679 // console.log(self.innerWidth);
680 // console.log(document.documentElement.clientWidth);
682 if (self
.innerHeight
) { // all except Explorer
683 if(document
.documentElement
.clientWidth
){
684 windowWidth
= document
.documentElement
.clientWidth
;
686 windowWidth
= self
.innerWidth
;
688 windowHeight
= self
.innerHeight
;
689 } else if (document
.documentElement
&& document
.documentElement
.clientHeight
) { // Explorer 6 Strict Mode
690 windowWidth
= document
.documentElement
.clientWidth
;
691 windowHeight
= document
.documentElement
.clientHeight
;
692 } else if (document
.body
) { // other Explorers
693 windowWidth
= document
.body
.clientWidth
;
694 windowHeight
= document
.body
.clientHeight
;
697 // for small pages with total height less then height of the viewport
698 if(yScroll
< windowHeight
){
699 pageHeight
= windowHeight
;
701 pageHeight
= yScroll
;
704 // console.log("xScroll " + xScroll)
705 // console.log("windowWidth " + windowWidth)
707 // for small pages with total width less then width of the viewport
708 if(xScroll
< windowWidth
){
711 pageWidth
= windowWidth
;
713 // console.log("pageWidth " + pageWidth)
715 arrayPageSize
= new Array(pageWidth
,pageHeight
,windowWidth
,windowHeight
)
716 return arrayPageSize
;
719 // -----------------------------------------------------------------------------------
723 // Gets keycode. If 'x' is pressed then it hides the lightbox.
726 if (e
== null) { // ie
727 keycode
= event
.keyCode
;
731 key
= String
.fromCharCode(keycode
).toLowerCase();
737 // -----------------------------------------------------------------------------------
742 function listenKey () { document
.onkeypress
= getKey
; }
744 // ---------------------------------------------------
746 function showSelectBoxes(){
747 var selects
= document
.getElementsByTagName("select");
748 for (i
= 0; i
!= selects
.length
; i
++) {
749 selects
[i
].style
.visibility
= "visible";
753 // ---------------------------------------------------
755 function hideSelectBoxes(){
756 var selects
= document
.getElementsByTagName("select");
757 for (i
= 0; i
!= selects
.length
; i
++) {
758 selects
[i
].style
.visibility
= "hidden";
762 // ---------------------------------------------------
764 function showFlash(){
765 var flashObjects
= document
.getElementsByTagName("object");
766 for (i
= 0; i
< flashObjects
.length
; i
++) {
767 flashObjects
[i
].style
.visibility
= "visible";
770 var flashEmbeds
= document
.getElementsByTagName("embed");
771 for (i
= 0; i
< flashEmbeds
.length
; i
++) {
772 flashEmbeds
[i
].style
.visibility
= "visible";
776 // ---------------------------------------------------
778 function hideFlash(){
779 var flashObjects
= document
.getElementsByTagName("object");
780 for (i
= 0; i
< flashObjects
.length
; i
++) {
781 flashObjects
[i
].style
.visibility
= "hidden";
784 var flashEmbeds
= document
.getElementsByTagName("embed");
785 for (i
= 0; i
< flashEmbeds
.length
; i
++) {
786 flashEmbeds
[i
].style
.visibility
= "hidden";
792 // ---------------------------------------------------
795 // pause(numberMillis)
796 // Pauses code execution for specified time. Uses busy code, not good.
797 // Help from Ran Bar-On [ran2103@gmail.com]
801 var date
= new Date();
803 do{var curDate
= new Date();}
804 while( curDate
- date
< ms
);
807 function pause(numberMillis) {
808 var curently = new Date().getTime() + sender;
809 while (new Date().getTime();
812 // ---------------------------------------------------
813 var myLightbox
= null
814 function initLightbox() { myLightbox
= new Lightbox(); }
815 Event
.observe(window
, 'load', initLightbox
, false);