
/* - jq.galleriffic.js - */
/*
 * jQuery Galleriffic plugin
 *
 * Copyright (c) 2008 Trent Foley (http://trentacular.com)
 * Licensed under the MIT License:
 *   http://www.opensource.org/licenses/mit-license.php
 *
 * Much thanks to primary contributer Ponticlaro (http://www.ponticlaro.com)
 */
;(function($) {

        // Hide elements with the noscript class
        $('.noscript').hide();

        var ver = 'galleriffic-1.1';
        var galleryOffset = 0;
        var galleries = [];
        var allImages = [];

        $.galleriffic = {
                goto: function(hash) {
                        hash = getHashFromString(hash);
                        var gallery = getGalleryForHash(hash);
                        if (!gallery) return;

                        var index = hash-gallery.offset;
                        gallery.goto(index);
                }
        };

        function getGalleryForHash(hash) {
                for (i = 0; i < galleries.length; i++) {
                        var gallery = galleries[i];
                        if (hash < (gallery.data.length+gallery.offset))
                                return gallery;
                }
                return 0;
        }

        function getHashFromString(hash) {
                if (typeof hash == 'number')
                        return hash;
           
                if (!hash) return -1;
           
                hash = hash.replace(/^.*#/, '');

                if (isNaN(hash)) return -1;
                return (+hash);
        }

        function registerGallery(gallery) {
                galleries.push(gallery);

                // update the global offset value
                galleryOffset += gallery.data.length;
        }      

        var defaults = {
                delay:                     3000,
                numThumbs:                 20,
                preloadAhead:              40, // Set to -1 to preload all images
                enableTopPager:            false,
                enableBottomPager:         true,
                maxPagesToShow:            7,
                imageContainerSel:         '',
                captionContainerSel:       '',
                controlsContainerSel:      '',
                loadingContainerSel:       '',
                renderSSControls:          true,
                renderNavControls:         true,
                playLinkText:              'Play',
                pauseLinkText:             'Pause',
                prevLinkText:              'Previous',
                nextLinkText:              'Next',
                nextPageLinkText:          'Next &rsaquo;',
                prevPageLinkText:          '&lsaquo; Prev',
                enableHistory:             false,
                autoStart:                 false,
                syncTransitions:           false,
                defaultTransitionDuration: 1000,
                onSlideChange:             undefined, // accepts a delegate like such: function(prevIndex, nextIndex) { ... }
                onTransitionOut:           undefined, // accepts a delegate like such: function(slide, caption, isSync, callback) { ... }
                onTransitionIn:            undefined, // accepts a delegate like such: function(slide, caption, isSync) { ... }
                onPageTransitionOut:       undefined, // accepts a delegate like such: function(callback) { ... }
                onPageTransitionIn:        undefined,  // accepts a delegate like such: function() { ... }
                // lightbox
                // Configuration related to overlay
    			overlayBgColor: 		'#000',		// (string) Background color to overlay; inform a hexadecimal value like: #RRGGBB. Where RR, GG, and BB are the hexadecimal values for the red, green, and blue values of the color.
    			overlayOpacity:			0.8,		// (integer) Opacity value to overlay; inform: 0.X. Where X are number from 0 to 9
    			// Configuration related to navigation
    			fixedNavigation:		false,		// (boolean) Boolean that informs if the navigation (next and prev button) will be fixed or not in the interface.
    			// Configuration related to images
    			imageLoading:			'lightbox-ico-loading.gif',		// (string) Path and the name of the loading icon
    			imageBtnPrev:			'lightbox-btn-prev.gif',			// (string) Path and the name of the prev button image
    			imageBtnNext:			'lightbox-btn-next.gif',			// (string) Path and the name of the next button image
    			imageBtnClose:			'lightbox-btn-close.gif',		// (string) Path and the name of the close btn
    			imageBlank:				'lightbox-blank.gif',			// (string) Path and the name of a blank image (one pixel)
    			// Configuration related to container image box
    			containerBorderSize:	10,			// (integer) If you adjust the padding in the CSS for the container, #lightbox-container-image-box, you will need to update this value
    			containerResizeSpeed:	400,		// (integer) Specify the resize duration of container image. These number are miliseconds. 400 is default.
    			// Configuration related to texts in caption. For example: Image 2 of 8. You can alter either "Image" and "of" texts.
    			txtImage:				'Image',	// (string) Specify text "Image"
    			txtOf:					'of',		// (string) Specify text "of"
    			// Configuration related to keyboard navigation
    			keyToClose:				'c',		// (string) (c = close) Letter to close the jQuery lightBox interface. Beyond this letter, the letter X and the SCAPE key is used to.
    			keyToPrev:				'p',		// (string) (p = previous) Letter to show the previous image
    			keyToNext:				'n',		// (string) (n = next) Letter to show the next image.
    			// Don´t alter these variables in any way
    			imageArray:				[],
    			activeImage:			0
        };
    
        // lightbox specific functions

    	function buildLightBox(image,gallery,current) {

    		gallery.pause();
            
            var current_image_src = gallery.data[current].slideUrl.replace('/image_preview', '/image_large');
    		// Hide some elements to avoid conflict with overlay in IE. These elements appear above the overlay.
    		$('embed, object, select').css({ 'visibility' : 'hidden' });
    		// Call the function to create the markup structure; style some elements; assign events in some elements.
    		_set_interface();
    		// Unset total images in imageArray
    		defaults.imageArray.length = 0;
    		// Unset image active information
    		defaults.activeImage = 0;

    		if ( gallery.data.length == 1 ) {
    			defaults.imageArray.push(new Array(current_image_src,gallery.data[current].title));
    		} else {
    			// Add an Array (as many as we have), with href and title atributes, inside the Array that storage the images references		
    			for ( var i = 0; i < gallery.data.length; i++ ) {
    				defaults.imageArray.push(new Array(gallery.data[i].slideUrl.replace('/image_preview', '/image_large'),gallery.data[i].title));
    			}
    		}
    		while ( defaults.imageArray[defaults.activeImage][0] != current_image_src ) {
    			defaults.activeImage++;
    		}
    		// Call the function that prepares image exibition
    		_set_image_to_view();
    	}

    	/**
    	 * Create the jQuery lightBox plugin interface
    	 *
    	 * The HTML markup will be like that:
    		<div id="jquery-overlay"></div>
    		<div id="jquery-lightbox">
    			<div id="lightbox-container-image-box">
    				<div id="lightbox-container-image">
    					<img src="../fotos/XX.jpg" id="lightbox-image">
    					<div id="lightbox-nav">
    						<a href="#" id="lightbox-nav-btnPrev"></a>
    						<a href="#" id="lightbox-nav-btnNext"></a>
    					</div>
    					<div id="lightbox-loading">
    						<a href="#" id="lightbox-loading-link">
    							<img src="../images/lightbox-ico-loading.gif">
    						</a>
    					</div>
    				</div>
    			</div>
    			<div id="lightbox-container-image-data-box">
    				<div id="lightbox-container-image-data">
    					<div id="lightbox-image-details">
    						<span id="lightbox-image-details-caption"></span>
    						<span id="lightbox-image-details-currentNumber"></span>
    					</div>
    					<div id="lightbox-secNav">
    						<a href="#" id="lightbox-secNav-btnClose">
    							<img src="../images/lightbox-btn-close.gif">
    						</a>
    					</div>
    				</div>
    			</div>
    		</div>
    	 *
    	 */
    	function _set_interface() {
    		// Apply the HTML markup into body tag
    		$('body').append('<div id="jquery-overlay"></div><div id="jquery-lightbox"><div id="lightbox-container-image-box"><div id="lightbox-container-image"><img id="lightbox-image"><div style="" id="lightbox-nav"><a href="#" id="lightbox-nav-btnPrev"></a><a href="#" id="lightbox-nav-btnNext"></a></div><div id="lightbox-loading"><a href="#" id="lightbox-loading-link"><img src="' + defaults.imageLoading + '"></a></div></div></div><div id="lightbox-container-image-data-box"><div id="lightbox-container-image-data"><div id="lightbox-image-details"><span id="lightbox-image-details-caption"></span><span id="lightbox-image-details-currentNumber"></span></div><div id="lightbox-secNav"><a href="#" id="lightbox-secNav-btnClose"><img src="' + defaults.imageBtnClose + '"></a></div></div></div></div>');	
    		// Get page sizes
    		var arrPageSizes = ___getPageSize();
    		// Style overlay and show it
    		$('#jquery-overlay').css({
    			backgroundColor:	defaults.overlayBgColor,
    			opacity:			defaults.overlayOpacity,
    			width:				arrPageSizes[0],
    			height:				arrPageSizes[1]
    		}).fadeIn();
    		// Get page scroll
    		var arrPageScroll = ___getPageScroll();
    		// Calculate top and left offset for the jquery-lightbox div object and show it
    		$('#jquery-lightbox').css({
    			top:	arrPageScroll[1] + (arrPageSizes[3] / 10),
    			left:	arrPageScroll[0]
    		}).show();
    		// Assigning click events in elements to close overlay
    		$('#jquery-overlay,#jquery-lightbox').click(function() {
    			_finish();									
    		});
    		// Assign the _finish function to lightbox-loading-link and lightbox-secNav-btnClose objects
    		$('#lightbox-loading-link,#lightbox-secNav-btnClose').click(function() {
    			_finish();
    			return false;
    		});
    		// If window was resized, calculate the new overlay dimensions
    		$(window).resize(function() {
    			// Get page sizes
    			var arrPageSizes = ___getPageSize();
    			// Style overlay and show it
    			$('#jquery-overlay').css({
    				width:		arrPageSizes[0],
    				height:		arrPageSizes[1]
    			});
    			// Get page scroll
    			var arrPageScroll = ___getPageScroll();
    			// Calculate top and left offset for the jquery-lightbox div object and show it
    			$('#jquery-lightbox').css({
    				top:	arrPageScroll[1] + (arrPageSizes[3] / 10),
    				left:	arrPageScroll[0]
    			});
    		});
    	}


    	/**
    	 * Prepares image exibition; doing a image´s preloader to calculate it´s size
    	 *
    	 */
    	function _set_image_to_view() { // show the loading
    		// Show the loading
    		$('#lightbox-loading').show();
    		if ( defaults.fixedNavigation ) {
    			$('#lightbox-image,#lightbox-container-image-data-box,#lightbox-image-details-currentNumber').hide();
    		} else {
    			// Hide some elements
    			$('#lightbox-image,#lightbox-nav,#lightbox-nav-btnPrev,#lightbox-nav-btnNext,#lightbox-container-image-data-box,#lightbox-image-details-currentNumber').hide();
    		}
    		// Image preload process
    		var objImagePreloader = new Image();
    		objImagePreloader.onload = function() {
    			$('#lightbox-image').attr('src',defaults.imageArray[defaults.activeImage][0]);
    			// Perfomance an effect in the image container resizing it
    			_resize_container_image_box(objImagePreloader.width,objImagePreloader.height);
    			//	clear onLoad, IE behaves irratically with animated gifs otherwise
    			objImagePreloader.onload=function(){};
    		};
    		objImagePreloader.src = defaults.imageArray[defaults.activeImage][0];
    	};

    	/**
    	 * Perfomance an effect in the image container resizing it
    	 *
    	 * @param integer intImageWidth The image´s width that will be showed
    	 * @param integer intImageHeight The image´s height that will be showed
    	 */
    	function _resize_container_image_box(intImageWidth,intImageHeight) {
    		// Get current width and height
    		var intCurrentWidth = $('#lightbox-container-image-box').width();
    		var intCurrentHeight = $('#lightbox-container-image-box').height();
    		// Get the width and height of the selected image plus the padding
    		var intWidth = (intImageWidth + (defaults.containerBorderSize * 2)); // Plus the image´s width and the left and right padding value
    		var intHeight = (intImageHeight + (defaults.containerBorderSize * 2)); // Plus the image´s height and the left and right padding value
    		// Diferences
    		var intDiffW = intCurrentWidth - intWidth;
    		var intDiffH = intCurrentHeight - intHeight;
    		// Perfomance the effect
    		$('#lightbox-container-image-box').animate({ width: intWidth, height: intHeight },defaults.containerResizeSpeed,function() { _show_image(); });
    		if ( ( intDiffW == 0 ) && ( intDiffH == 0 ) ) {
    			if ( $.browser.msie ) {
    				___pause(250);
    			} else {
    				___pause(100);	
    			}
    		} 
    		$('#lightbox-container-image-data-box').css({ width: intImageWidth });
    		$('#lightbox-nav-btnPrev,#lightbox-nav-btnNext').css({ height: intImageHeight + (defaults.containerBorderSize * 2) });
    	};

    	/**
    	 * Show the prepared image
    	 *
    	 */
    	function _show_image() {
    		$('#lightbox-loading').hide();
    		$('#lightbox-image').fadeIn(function() {
    			_show_image_data();
    			_set_navigation();
    		});
    		_preload_neighbor_images();
    	};

    	/**
    	 * Show the image information
    	 *
    	 */
    	function _show_image_data() {
    		$('#lightbox-container-image-data-box').slideDown('fast');
    		$('#lightbox-image-details-caption').hide();
    		if ( defaults.imageArray[defaults.activeImage][1] ) {
    			$('#lightbox-image-details-caption').html(defaults.imageArray[defaults.activeImage][1]).show();
    		}
    		// If we have a image set, display 'Image X of X'
    		if ( defaults.imageArray.length > 1 ) {
    			$('#lightbox-image-details-currentNumber').html(defaults.txtImage + ' ' + ( defaults.activeImage + 1 ) + ' ' + defaults.txtOf + ' ' + defaults.imageArray.length).show();
    		}		
    	}

    	// handles the reloaction of the URL hash variable when the previous/next buttons are clicked within the lightbox UI.
    	function changeThumbnail(activeImg) {
    		location.hash = '#'+activeImg;
    		// IE we need to explicity call goto
    		/*if ($.browser.msie) {
    			this.goto(activeImg);
    		}*/
    	}

    	/**
    	 * Display the button navigations
    	 *
    	 */
    	function _set_navigation() {
    		$('#lightbox-nav').show();

    		// Instead to define this configuration in CSS file, we define here. And it´s need to IE. Just.
    		$('#lightbox-nav-btnPrev,#lightbox-nav-btnNext').css({ 'background' : 'transparent url(' + defaults.imageBlank + ') no-repeat' });

    		// Show the prev button, if not the first image in set
    		if ( defaults.activeImage != 0 ) {
    			if ( defaults.fixedNavigation ) {
    				$('#lightbox-nav-btnPrev').css({ 'background' : 'url(' + defaults.imageBtnPrev + ') left 15% no-repeat' })
    					.unbind()
    					.bind('click',function(e) {
    						defaults.activeImage = defaults.activeImage - 1;
                                                changeThumbnail();
    						_set_image_to_view();
    						return false;
    					});
    			} else {
    				// Show the images button for Next buttons
    				$('#lightbox-nav-btnPrev').unbind().hover(function() {
    					$(this).css({ 'background' : 'url(' + defaults.imageBtnPrev + ') left 15% no-repeat' });
    				},function() {
    					$(this).css({ 'background' : 'transparent url(' + defaults.imageBlank + ') no-repeat' });
    				}).show().bind('click',function(e) {
    					defaults.activeImage = defaults.activeImage - 1;
                                        changeThumbnail(defaults.activeImage);
    					_set_image_to_view();
    					return false;
    				});
    			}
    		}

    		// Show the next button, if not the last image in set
    		if ( defaults.activeImage != ( defaults.imageArray.length -1 ) ) {
    			if ( defaults.fixedNavigation ) {
    				$('#lightbox-nav-btnNext').css({ 'background' : 'url(' + defaults.imageBtnNext + ') right 15% no-repeat' })
    					.unbind()
    					.bind('click',function(e) {
    						defaults.activeImage = defaults.activeImage + 1;
                                                changeThumbnail(defaults.activeImage);
    						_set_image_to_view();
    						return false;
    					});
    			} else {
    				// Show the images button for Next buttons
    				$('#lightbox-nav-btnNext').unbind().hover(function() {
    					$(this).css({ 'background' : 'url(' + defaults.imageBtnNext + ') right 15% no-repeat' });
    				},function() {
    					$(this).css({ 'background' : 'transparent url(' + defaults.imageBlank + ') no-repeat' });
    				}).show().bind('click',function(e) {
    					defaults.activeImage = defaults.activeImage + 1;
    					changeThumbnail(defaults.activeImage);
    					_set_image_to_view();
    					return false;
    				});
    			}
    		}
    		// Enable keyboard navigation
    		_enable_keyboard_navigation();
    	}

    	/**
    	 * Enable a support to keyboard navigation
    	 *
    	 */
    	function _enable_keyboard_navigation() {
    		$(document).keydown(function(objEvent) {
    			_keyboard_action(objEvent);
    		});
    	}
    	/**
    	 * Disable the support to keyboard navigation
    	 *
    	 */
    	function _disable_keyboard_navigation() {
    		$(document).unbind();
    	}


    	/**
    	 * Perform the keyboard actions
    	 *
    	 */
    	function _keyboard_action(objEvent) {
    		// To ie
    		if ( objEvent == null ) {
    			keycode = event.keyCode;
    			escapeKey = 27;
    		// To Mozilla
    		} else {
    			keycode = objEvent.keyCode;
    			escapeKey = objEvent.DOM_VK_ESCAPE;
    		}
    		// Get the key in lower case form
    		key = String.fromCharCode(keycode).toLowerCase();
    		// Verify the keys to close the ligthBox
    		if ( ( key == defaults.keyToClose ) || ( key == 'x' ) || ( keycode == escapeKey ) ) {
    			_finish();
    		}
    		// Verify the key to show the previous image
    		if ( ( key == defaults.keyToPrev ) || ( keycode == 37 ) ) {
    			// If we´re not showing the first image, call the previous
    			if ( defaults.activeImage != 0 ) {
    				defaults.activeImage = defaults.activeImage - 1;
    				changeThumbnail(defaults.activeImage);
    				_set_image_to_view();
    				_disable_keyboard_navigation();
    			}
    		}
    		// Verify the key to show the next image
    		if ( ( key == defaults.keyToNext ) || ( keycode == 39 ) ) {
    			// If we´re not showing the last image, call the next
    			if ( defaults.activeImage != ( defaults.imageArray.length - 1 ) ) {
    				defaults.activeImage = defaults.activeImage + 1;
    				changeThumbnail(defaults.activeImage);
    				_set_image_to_view();
    				_disable_keyboard_navigation();
    			}
    		}
    	}
    	/**
    	 * Preload prev and next images being showed
    	 *
    	 */
    	function _preload_neighbor_images() {
    		if ( (defaults.imageArray.length -1) > defaults.activeImage ) {
    			objNext = new Image();
    			objNext.src = defaults.imageArray[defaults.activeImage + 1][0];
    		}
    		if ( defaults.activeImage > 0 ) {
    			objPrev = new Image();
    			objPrev.src = defaults.imageArray[defaults.activeImage -1][0];
    		}
    	}
    	/**
    	 * Remove jQuery lightBox plugin HTML markup
    	 *
    	 */
    	function _finish() {
    		$('#jquery-lightbox').remove();
    		$('#jquery-overlay').fadeOut(function() { $('#jquery-overlay').remove(); });
    		// Show some elements to avoid conflict with overlay in IE. These elements appear above the overlay.
    		$('embed, object, select').css({ 'visibility' : 'visible' });
    	}
    	/**
    	 / THIRD FUNCTION
    	 * getPageSize() by quirksmode.com
    	 *
    	 * @return Array Return an array with page width, height and window width, height
    	 */
    	function ___getPageSize() {
    		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;
    		}
    		arrayPageSize = new Array(pageWidth,pageHeight,windowWidth,windowHeight);
    		return arrayPageSize;
    	};
    	/**
    	 / THIRD FUNCTION
    	 * getPageScroll() by quirksmode.com
    	 *
    	 * @return Array Return an array with x,y page scroll values.
    	 */
    	function ___getPageScroll() {
    		var xScroll, yScroll;
    		if (self.pageYOffset) {
    			yScroll = self.pageYOffset;
    			xScroll = self.pageXOffset;
    		} else if (document.documentElement && document.documentElement.scrollTop) {	 // Explorer 6 Strict
    			yScroll = document.documentElement.scrollTop;
    			xScroll = document.documentElement.scrollLeft;
    		} else if (document.body) {// all other Explorers
    			yScroll = document.body.scrollTop;
    			xScroll = document.body.scrollLeft;	
    		}
    		arrayPageScroll = new Array(xScroll,yScroll);
    		return arrayPageScroll;
    	};
    	 /**
    	  * Stop the code execution from a escified time in milisecond
    	  *
    	  */
    	 function ___pause(ms) {
    		var date = new Date(); 
    		curDate = null;
    		do { var curDate = new Date(); }
    		while ( curDate - date < ms);
    	 };
    
        // end ligthbox functions 
    
        $.fn.galleriffic = function(thumbsContainerSel, settings) {
                //  Extend Gallery Object
                $.extend(this, {
                        ver: function() {
                                return ver;
                        },

                        getIndex: function(hash) {
                                return hash-this.offset;
                        },

                        clickHandler: function(e, link) {
                                this.pause();

                                if (!this.enableHistory) {
                                        var hash = getHashFromString(link.href);
                                        if (hash >= 0) {
                                                var index = this.getIndex(hash);
                                                if (index >= 0)
                                                        this.goto(index);
                                        }
                                        e.preventDefault();
                                }
                        },

                        initializeThumbs: function() {
                                this.data = [];
                                var gallery = this;
                           
                                this.$thumbsContainer.find('ul.thumbs > li').each(function(i) {
                                        var $li = $(this);
                                        var $aThumb = $li.find('a.thumb');
                                        var hash = gallery.offset+i;

                                        gallery.data.push({
                                                title:$aThumb.attr('title'),
                                                slideUrl:$aThumb.attr('href'),
                                                caption:$li.find('.caption').remove(),
                                                hash:hash
                                        });

                                        // Setup attributes and click event handler
                                        $aThumb.attr('rel', 'history')
                                                .attr('href', '#'+hash)
                                                .click(function(e) {
                                                        gallery.clickHandler(e, this);
                                                });
                                });
                                return this;
                        },

                        isPreloadComplete: false,

                        preloadInit: function() {
                                if (this.preloadAhead == 0) return this;
                           
                                this.preloadStartIndex = this.currentIndex;
                                var nextIndex = this.getNextIndex(this.preloadStartIndex);
                                return this.preloadRecursive(this.preloadStartIndex, nextIndex);
                        },
                   
                        preloadRelocate: function(index) {
                                // By changing this startIndex, the current preload script will restart
                                this.preloadStartIndex = index;
                                return this;
                        },

                        preloadRecursive: function(startIndex, currentIndex) {
                                // Check if startIndex has been relocated
                                if (startIndex != this.preloadStartIndex) {
                                        var nextIndex = this.getNextIndex(this.preloadStartIndex);
                                        return this.preloadRecursive(this.preloadStartIndex, nextIndex);
                                }

                                var gallery = this;

                                // Now check for preloadAhead count
                                var preloadCount = currentIndex - startIndex;
                                if (preloadCount < 0)
                                        preloadCount = this.data.length-1-startIndex+currentIndex;
                                if (this.preloadAhead >= 0 && preloadCount > this.preloadAhead) {
                                        // Do this in order to keep checking for relocated start index
                                        setTimeout(function() { gallery.preloadRecursive(startIndex, currentIndex); }, 500);
                                        return this;
                                }

                                var imageData = this.data[currentIndex];
                                if (!imageData)
                                        return this;

                                // If already loaded, continue
                                if (imageData.image)
                                        return this.preloadNext(startIndex, currentIndex);
                           
                                // Preload the image
                                var image = new Image();
                           
                                image.onload = function() {
                                        imageData.image = this;
                                        gallery.preloadNext(startIndex, currentIndex);
                                };

                                image.alt = imageData.title;
                                image.src = imageData.slideUrl;

                                return this;
                        },
                   
                        preloadNext: function(startIndex, currentIndex) {
                                var nextIndex = this.getNextIndex(currentIndex);
                                if (nextIndex == startIndex) {
                                        this.isPreloadComplete = true;
                                } else {
                                        // Use setTimeout to free up thread
                                        var gallery = this;
                                        setTimeout(function() { gallery.preloadRecursive(startIndex, nextIndex); }, 100);
                                }
                                return this;
                        },


                        getNextIndex: function(index) {
                                var nextIndex = index+1;
                                if (nextIndex >= this.data.length)
                                        nextIndex = 0;
                                return nextIndex;
                        },
                   
                        getPrevIndex: function(index) {
                                var prevIndex = index-1;
                                if (prevIndex < 0)
                                        prevIndex = this.data.length-1;
                                return prevIndex;
                        },

                        pause: function() {
                                if (this.interval)
                                        this.toggleSlideshow();
                           
                                return this;
                        },

                        play: function() {
                                if (!this.interval)
                                        this.toggleSlideshow();
                           
                                return this;
                        },

                        toggleSlideshow: function() {
                                if (this.interval) {
                                        clearInterval(this.interval);
                                        this.interval = 0;
                                   
                                        if (this.$controlsContainer) {
                                                this.$controlsContainer
                                                        .find('div.ss-controls a').removeClass().addClass('play')
                                                        .attr('title', this.playLinkText)
                                                        .attr('href', '#play')
                                                        .html(this.playLinkText);
                                        }
                                } else {
                                        var gallery = this;
                                        this.interval = setInterval(function() {
                                                gallery.ssAdvance();
                                        }, this.delay);
                                   
                                        if (this.$controlsContainer) {
                                                this.$controlsContainer
                                                        .find('div.ss-controls a').removeClass().addClass('pause')
                                                        .attr('title', this.pauseLinkText)
                                                        .attr('href', '#pause')
                                                        .html(this.pauseLinkText);
                                        }
                                }

                                return this;
                        },

                        ssAdvance: function() {
                                var nextIndex = this.getNextIndex(this.currentIndex);
                                var nextHash = this.data[nextIndex].hash;

                                // Seems to be working on both FF and Safari
                                if (this.enableHistory)
                                        $.historyLoad(String(nextHash));  // At the moment, historyLoad only accepts string arguments
                                else
                                        this.goto(nextIndex);

                                return this;
                        },

                        next: function() {
                                this.pause();
                                goto(this.getNextIndex(this.currentIndex));
                        },

                        previous: function() {
                                this.pause();
                                goto(this.getPrevIndex(this.currentIndex));
                        },

                        goto: function(index) {
                                if (index < 0) index = 0;
                                else if (index >= this.data.length) index = this.data.length-1;
                           
                                if (this.onSlideChange)
                                        this.onSlideChange(this.currentIndex, index);
                           
                                this.currentIndex = index;
                                this.preloadRelocate(index);
                                return this.refresh();
                        },
                   
                        getDefaultTransitionDuration: function(isSync) {
                                if (isSync)
                                        return this.defaultTransitionDuration;
                                return this.defaultTransitionDuration / 2;
                        },
                   
                        refresh: function() {
                                var imageData = this.data[this.currentIndex];
                                if (!imageData)
                                        return this;

                                // Update Controls
                                if (this.$controlsContainer) {
                                        this.$controlsContainer
                                                .find('div.nav-controls a.prev').attr('href', '#'+this.data[this.getPrevIndex(this.currentIndex)].hash).end()
                                                .find('div.nav-controls a.next').attr('href', '#'+this.data[this.getNextIndex(this.currentIndex)].hash);
                                }
                                // update lightbox
                                $('#lightbox-nav-btnPrev').attr('href', '#'+this.data[this.getPrevIndex(this.currentIndex)].hash).end();
                                $('#lightbox-nav-btnNext').attr('href', '#'+this.data[this.getNextIndex(this.currentIndex)].hash);

                                var previousSlide = this.$imageContainer.find('span.current').addClass('previous').removeClass('current');
                                var previousCaption = 0;

                                if (this.$captionContainer) {
                                        previousCaption = this.$captionContainer.find('span.current').addClass('previous').removeClass('current');
                                }

                                // Perform transitions simultaneously if syncTransitions is true and the next image is already preloaded
                                var isSync = this.syncTransitions && imageData.image;
                           
                                // Flag we are transitioning
                                var isTransitioning = true;
                                var gallery = this;

                                var transitionOutCallback = function() {
                                        // Flag that the transition has completed
                                        isTransitioning = false;

                                        // Remove the old slide
                                        previousSlide.remove();

                                        // Remove old caption
                                        if (previousCaption)
                                                previousCaption.remove();

                                        if (!isSync)
                                        {
                                                if (imageData.image && imageData.hash == gallery.data[gallery.currentIndex].hash) {
                                                        gallery.buildImage(imageData, isSync);
                                                } else {
                                                        // Show loading container
                                                        if (gallery.$loadingContainer) {
                                                                gallery.$loadingContainer.show();
                                                        }
                                                }
                                        }
                                };

                                if (previousSlide.length == 0) {
                                        // For the first slide, the previous slide will be empty, so we will call the callback immediately
                                        transitionOutCallback();
                                } else {
                                        if (this.onTransitionOut) {
                                                this.onTransitionOut(previousSlide, previousCaption, isSync, transitionOutCallback);
                                        } else {
                                                previousSlide.fadeTo(this.getDefaultTransitionDuration(isSync), 0.0, transitionOutCallback);
                                                if (previousCaption)
                                                        previousCaption.fadeTo(this.getDefaultTransitionDuration(isSync), 0.0);
                                        }
                                }

                                // Go ahead and begin transition in of next image
                                if (isSync)
                                        this.buildImage(imageData, isSync);
                   
                                if (!imageData.image) {
                                        var image = new Image();
                                   
                                        // Wire up mainImage onload event
                                        image.onload = function() {
                                                imageData.image = this;

                                                // Only build image if the out transition has completed and we are still on the same image hash
                                                if (!isTransitioning && imageData.hash == gallery.data[gallery.currentIndex].hash) {
                                                        gallery.buildImage(imageData, isSync);
                                                }
                                        };

                                        // set alt and src
                                        image.alt = imageData.title;
                                        image.src = imageData.slideUrl;
                                }

                                // This causes the preloader (if still running) to relocate out from the currentIndex
                                this.relocatePreload = true;

                                return this.syncThumbs();
                        },
                   
                        buildImage: function(imageData, isSync) {
                                var gallery = this;
                                var nextIndex = this.getNextIndex(this.currentIndex);

                                // Construct new hidden span for the image
                                //.append('<span class="image-wrapper"><a class="advance-link" rel="history" href="#'+this.data[this.getNextIndex(this.currentIndex)].hash+'" title="'+imageData.image.alt+'"></a></span>')
                                //.append('<span class="image-wrapper current"><a class="advance-link" href="'+imageData.image.src.replace('/image_preview', '/image_large')+'" title="'+imageData.title+'"> </a></span>')        
                                var newSlide = this.$imageContainer
                                        .empty()
                                        .append('<span class="image-wrapper"><a class="advance-link" rel="history" title="'+imageData.image.alt+'"></a></span>');
                                        //.find('span.current').css('opacity', '0');
                                var currentImageIndex = this.currentIndex;
                                newSlide.find('a')
                                        .click(function() { buildLightBox(imageData.image,gallery,currentImageIndex); })
                                        .append(imageData.image);
                           
                                var newCaption = 0;
                                if (this.$captionContainer) {
                                        // Construct new hidden caption for the image
                                        newCaption = this.$captionContainer
                                                .append('<span class="image-caption current"></span>')
                                                .find('span.current').css('opacity', '0')
                                                .append(imageData.caption);
                                }


                                // Hide the loading conatiner
                                if (this.$loadingContainer) {
                                        this.$loadingContainer.hide();
                                }

                                // Transition in the new image
                                if (this.onTransitionIn) {
                                        this.onTransitionIn(newSlide, newCaption, isSync);
                                } else {
                                        newSlide.fadeTo(this.getDefaultTransitionDuration(isSync), 1.0);
                                        if (newCaption)
                                                newCaption.fadeTo(this.getDefaultTransitionDuration(isSync), 1.0);
                                }

                                return this;
                        },

                        syncThumbs: function() {
                                if (this.$thumbsContainer) {
                                        var page = Math.floor(this.currentIndex / this.numThumbs);
                                        if (page != this.currentPage) {
                                                this.currentPage = page;
                                                this.updateThumbs();
                                        }

                                        // Remove existing selected class and add selected class to new thumb
                                        var $thumbs = this.$thumbsContainer.find('ul.thumbs').children();
                                        $thumbs.filter('.selected').removeClass('selected');
                                        $thumbs.eq(this.currentIndex).addClass('selected');
                                }

                                return this;
                        },

                        updateThumbs: function() {
                                var gallery = this;
                                var transitionOutCallback = function() {
                                        gallery.rebuildThumbs();

                                        // Transition In the thumbsContainer
                                        if (gallery.onPageTransitionIn)
                                                gallery.onPageTransitionIn();
                                        else
                                                gallery.$thumbsContainer.show();
                                };

                                // Transition Out the thumbsContainer
                                if (this.onPageTransitionOut) {
                                        this.onPageTransitionOut(transitionOutCallback);
                                } else {
                                        this.$thumbsContainer.hide();
                                        transitionOutCallback();
                                }

                                return this;
                        },

                        rebuildThumbs: function() {
                                // Initialize currentPage to first page
                                if (this.currentPage < 0)
                                        this.currentPage = 0;
                           
                                var needsPagination = this.data.length > this.numThumbs;

                                // Rebuild top pager
                                var $topPager = this.$thumbsContainer.find('div.top');
                                if ($topPager.length == 0)
                                        $topPager = this.$thumbsContainer.prepend('<div class="top pagination"></div>').find('div.top');

                                if (needsPagination && this.enableTopPager) {
                                        $topPager.empty();
                                        this.buildPager($topPager);
                                }

                                // Rebuild bottom pager
                                if (needsPagination && this.enableBottomPager) {
                                        var $bottomPager = this.$thumbsContainer.find('div.bottom');
                                        if ($bottomPager.length == 0)
                                                $bottomPager = this.$thumbsContainer.append('<div class="bottom pagination"></div>').find('div.bottom');
                                        else
                                                $bottomPager.empty();

                                        this.buildPager($bottomPager);
                                }

                                var startIndex = this.currentPage*this.numThumbs;
                                var stopIndex = startIndex+this.numThumbs-1;
                                if (stopIndex >= this.data.length)
                                        stopIndex = this.data.length-1;

                                // Show/Hide thumbs
                                var $thumbsUl = this.$thumbsContainer.find('ul.thumbs');
                                $thumbsUl.find('li').each(function(i) {
                                        var $li = $(this);
                                        if (i >= startIndex && i <= stopIndex) {
                                                $li.show();
                                        } else {
                                                $li.hide();
                                        }
                                });

                                // Remove the noscript class from the thumbs container ul
                                $thumbsUl.removeClass('noscript');
                           
                                return this;
                        },

                        buildPager: function(pager) {
                                var gallery = this;
                                var startIndex = this.currentPage*this.numThumbs;
                                var pagesRemaining = this.maxPagesToShow - 1;
                           
                                var pageNum = this.currentPage - Math.floor((this.maxPagesToShow - 1) / 2) + 1;
                                if (pageNum > 0) {
                                        var remainingPageCount = this.numPages - pageNum;
                                        if (remainingPageCount < pagesRemaining) {
                                                pageNum = pageNum - (pagesRemaining - remainingPageCount);
                                        }
                                }

                                if (pageNum < 0) {
                                        pageNum = 0;
                                }

                                // Prev Page Link
                                if (this.currentPage > 0) {
                                        var prevPage = startIndex - this.numThumbs;
                                        pager.append('<a rel="history" href="#'+this.data[prevPage].hash+'" title="'+this.prevPageLinkText+'">'+this.prevPageLinkText+'</a>');
                                }

                                // Create First Page link if needed
                                if (pageNum > 0) {
                                        this.buildPageLink(pager, 0);
                                        if (pageNum > 1)
                                                pager.append('<span class="ellipsis">&hellip;</span>');
                                   
                                        pagesRemaining--;
                                }

                                // Page Index Links
                                while (pagesRemaining > 0) {
                                        this.buildPageLink(pager, pageNum);
                                        pagesRemaining--;
                                        pageNum++;
                                }

                                // Create Last Page link if needed
                                if (pageNum < this.numPages) {
                                        var lastPageNum = this.numPages - 1;
                                        if (pageNum < lastPageNum)
                                                pager.append('<span class="ellipsis">&hellip;</span>');

                                        this.buildPageLink(pager, lastPageNum);
                                }

                                // Next Page Link
                                var nextPage = startIndex+this.numThumbs;
                                if (nextPage < this.data.length) {
                                        pager.append('<a rel="history" href="#'+this.data[nextPage].hash+'" title="'+this.nextPageLinkText+'">'+this.nextPageLinkText+'</a>');
                                }

                                pager.find('a').click(function(e) {
                                        gallery.clickHandler(e, this);
                                });

                                return this;
                        },
                   
                        buildPageLink: function(pager, pageNum) {
                                var pageLabel = pageNum + 1;

                                if (pageNum == this.currentPage)
                                        pager.append('<span class="current">'+pageLabel+'</span>');
                                else if (pageNum < this.numPages) {
                                        var imageIndex = pageNum*this.numThumbs;
                                        pager.append('<a rel="history" href="#'+this.data[imageIndex].hash+'" title="'+pageLabel+'">'+pageLabel+'</a>');
                                }
                        }
                });

                // Now initialize the gallery
                $.extend(this, defaults, settings);

                if (this.interval)
                        clearInterval(this.interval);

                this.interval = 0;
           
                // Verify the history plugin is available
                if (this.enableHistory && !$.historyInit)
                        this.enableHistory = false;
           
                // Select containers
                if (this.imageContainerSel) this.$imageContainer = $(this.imageContainerSel);
                if (this.captionContainerSel) this.$captionContainer = $(this.captionContainerSel);
                if (this.loadingContainerSel) this.$loadingContainer = $(this.loadingContainerSel);

                // Setup the jQuery object holding each container that will be transitioned
                this.$transitionContainers = $([]);
                if (this.$imageContainer)
                        this.$transitionContainers = this.$transitionContainers.add(this.$imageContainer);
                if (this.$captionContainer)
                        this.$transitionContainers = this.$transitionContainers.add(this.$captionContainer);
           
                // Set the hash index offset for this gallery
                this.offset = galleryOffset;


                this.$thumbsContainer = $(thumbsContainerSel);
                this.initializeThumbs();

                // Add this gallery to the global galleries array
                registerGallery(this);

                this.numPages = Math.ceil(this.data.length/this.numThumbs);
           
                if (this.maxPagesToShow < 3)
                        this.maxPagesToShow = 3;

                this.currentPage = -1;
                this.currentIndex = 0;
                var gallery = this;

                // Hide the loadingContainer
                if (this.$loadingContainer)
                        this.$loadingContainer.hide();

                // Setup controls
                if (this.controlsContainerSel) {
                        this.$controlsContainer = $(this.controlsContainerSel).empty();
                   
                        if (this.renderSSControls) {
                                if (this.autoStart) {
                                        this.$controlsContainer
                                                .append('<div class="ss-controls"><a href="#pause" class="pause" title="'+this.pauseLinkText+'">'+this.pauseLinkText+'</a></div>');
                                } else {
                                        this.$controlsContainer
                                                .append('<div class="ss-controls"><a href="#play" class="play" title="'+this.playLinkText+'">'+this.playLinkText+'</a></div>');
                                }

                                this.$controlsContainer.find('div.ss-controls a')
                                        .click(function(e) {
                                                gallery.toggleSlideshow();
                                                e.preventDefault();
                                                return false;
                                        });
                        }
           
                        if (this.renderNavControls) {
                                this.$controlsContainer
                                        .append('<div class="nav-controls"><a class="prev" rel="history" title="'+this.prevLinkText+'">'+this.prevLinkText+'</a><a class="next" rel="history" title="'+this.nextLinkText+'">'+this.nextLinkText+'</a></div>')
                                        .find('div.nav-controls a')
                                        .click(function(e) {
                                                gallery.clickHandler(e, this);
                                        });
                        }
                }

                // Setup gallery to show the first image
                if (!this.enableHistory || !location.hash) {
                        this.goto(0);
                }

                if (this.autoStart) {
                        setTimeout(function() { gallery.play(); }, this.delay);
                }

                // Kickoff Image Preloader after 1 second
                setTimeout(function() { gallery.preloadInit(); }, 1000);

                return this;
        };
})(jQuery);

