/*
 * jQuery Galleriffic plugin
 *
 * Copyright (c) 2008 Trent Foley (http://trentacular.com)
 * Licensed under the MIT License:
 *   http://www.opensource.org/licenses/mit-license.php
 *
 * Thanks to Taku Sano (Mikage Sawatari), whose history plugin I adapted to work with Galleriffic
 * Modified by Ghismo (ghismo.com) to disable the location rewrite 
 */
;(function($) {

    // Write noscript style
    document.write("<style type='text/css'>.noscript{display:none}</style>");

    var ver = 'galleriffic-1.0';
    var galleryOffset = 0;
    var galleries = [];
    var allImages = []; 
    var historyCurrentHash;
    var historyBackStack;
    var historyForwardStack;
    var isFirst = false;
    var dontCheck = false;
    var isInitialized = false;

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

    function getHash() {
        var hash = location.hash;
        return getHashFromString(hash);
    }
       
    function getHref() {
        var href = location.href;
        href = href.replace(/#.*$/, '');
        return href;
    }
        
    function registerGallery(gallery) {
        galleries.push(gallery);

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

    function getGallery(hash) {
        for (i = 0; i < galleries.length; i++) {
            var gallery = galleries[i];
            if (hash < (gallery.data.length+gallery.offset))
                return gallery;
        }
        return 0;
    }
    
    function getIndex(gallery, hash) {
        return hash-gallery.offset;
    }
    
    function clickHandler(e, gallery, link) {
        gallery.pause();

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

    function historyCallback() {
        // Using present location.hash always (seems to work, unlike the hash argument passed to this callback)
        var hash = getHash();
        if (hash < 0) return;

        var gallery = getGallery(hash);
        if (!gallery) return;
        
        var index = hash-gallery.offset;
        gallery.goto(index);
    }
    
    function historyInit() {
        if (isInitialized) return;
        isInitialized = true; 

        var current_hash = location.hash; //(enableHistory) ? location.hash : currentIndexHash; // Ghismo

        historyCurrentHash = current_hash;
        if ($.browser.msie) {
            // To stop the callback firing twice during initilization if no hash present
            if (historyCurrentHash == '') {
                historyCurrentHash = '#';
            }
        } else if ($.browser.safari) {
            // etablish back/forward stacks
            historyBackStack = [];
            historyBackStack.length = history.length;
            historyForwardStack = [];
            isFirst = true;
        }

        setInterval(function() { historyCheck(); }, 100);
    }
    
    function historyAddHistory(hash) {
        // This makes the looping function do something
        historyBackStack.push(hash);
        historyForwardStack.length = 0; // clear forwardStack (true click occured)
        isFirst = true;
    }
    
    function historyCheck() {
        if ($.browser.safari) {
            if (!dontCheck) {
                var historyDelta = history.length - historyBackStack.length;
                
                if (historyDelta) { // back or forward button has been pushed
                    isFirst = false;
                    if (historyDelta < 0) { // back button has been pushed
                        // move items to forward stack
                        for (var i = 0; i < Math.abs(historyDelta); i++) historyForwardStack.unshift(historyBackStack.pop());
                    } else { // forward button has been pushed
                        // move items to back stack
                        for (var i = 0; i < historyDelta; i++) historyBackStack.push(historyForwardStack.shift());
                    }
                    var cachedHash = historyBackStack[historyBackStack.length - 1];
                    if (cachedHash != undefined) {
                        historyCurrentHash = location.hash; // (enableHistory) ? location.hash : currentIndexHash; // Ghismo
                        historyCallback();
                    }
                } else if (historyBackStack[historyBackStack.length - 1] == undefined && !isFirst) {
                    historyCallback();
                    isFirst = true;
                }
            }
        } else {
            // otherwise, check for location.hash
            var current_hash = location.hash; // (enableHistory) ? location.hash : currentIndexHash; // Ghismo
            if(current_hash != historyCurrentHash) {
                historyCurrentHash = current_hash;
                historyCallback();
            }
        }
    }

    var defaults = {
        delay:                  3000,
        numThumbs:              20,
        preloadAhead:           40, // Set to -1 to preload all images
        enableTopPager:         false,
        enableBottomPager:      true,
        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,
        onChange:               undefined, // accepts a delegate like such: function(prevIndex, nextIndex) { ... }
        onTransitionOut:        undefined, // accepts a delegate like such: function(callback) { ... }
        onTransitionIn:         undefined, // accepts a delegate like such: function() { ... }
        onPageTransitionOut:    undefined, // accepts a delegate like such: function(callback) { ... }
        onPageTransitionIn:     undefined  // accepts a delegate like such: function() { ... }
    };

    $.fn.galleriffic = function(thumbsContainerSel, settings) {
        //  Extend Gallery Object
        $.extend(this, {
            ver: function() {
                return ver;
            },

            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 history
                    $aThumb.attr('rel', 'history');
                    $aThumb.attr('href', getHref()+'#'+hash);
                    $aThumb.click(function(e) {
                        clickHandler(e, gallery, this);
                    });
                });
                return this;
            },

            isPreloadComplete: false,

            preloadInit: function() {
                if (this.settings.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.settings.preloadAhead >= 0 && preloadCount > this.settings.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 set timeout 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.settings.playLinkText)
                            .attr('href', getHref()+'#play')
                            .html(this.settings.playLinkText);
                    }
                } else {
                    this.ssAdvance();

                    var gallery = this;
                    this.interval = setInterval(function() {
                        gallery.ssAdvance();
                    }, this.settings.delay);
                    
                    if (this.$controlsContainer) {
                        this.$controlsContainer
                            .find('div.ss-controls a').removeClass().addClass('pause')
                            .attr('title', this.settings.pauseLinkText)
                            .attr('href', getHref()+'#pause')
                            .html(this.settings.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.settings.enableHistory)
                    location.href = getHref()+'#'+nextHash;
                else
                    this.goto(nextIndex);

                // IE we need to explicity call goto
                //if ($.browser.msie) {
                //  this.goto(nextIndex);
                //}

                return this;
            },

            goto: function(index) {
                if (index < 0) index = 0;
                else if (index >= this.data.length) index = this.data.length-1;
                
                if (this.settings.onChange)
                    this.settings.onChange(this.currentIndex, index);
                
                this.currentIndex = index;
                this.preloadRelocate(index);
                return this.refresh();
            },
            
            refresh: function() {
                var imageData = this.data[this.currentIndex];
                if (!imageData)
                    return this;
                
                // Flag we are transitioning
                var isTransitioning = true;

                var gallery = this;

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

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

                    var imageData = gallery.data[gallery.currentIndex];

                    // Replace Caption
                    if (gallery.$captionContainer) {
                        gallery.$captionContainer.empty().append(imageData.caption);
                    }

                    if (imageData.image) {
                        gallery.buildImage(imageData.image);
                    } else {
                        // Show loading container
                        if (gallery.$loadingContainer) {
                            gallery.$loadingContainer.show();
                        }
                    }
                }

                if (this.settings.onTransitionOut) {
                    this.settings.onTransitionOut(transitionOutCallback);
                } else {
                    this.$transitionContainers.hide();
                    transitionOutCallback();
                }

                if (!imageData.image) {
                    var image = new Image();
                    
                    // Wire up mainImage onload event
                    image.onload = function() {
                        imageData.image = this;

                        if (!isTransitioning) {
                            gallery.buildImage(imageData.image);
                        }
                    };

                    // 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(image) {
                if (this.$imageContainer) {
                    this.$imageContainer.empty();

                    var gallery = this;
                    var nextIndex = this.getNextIndex(this.currentIndex);

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

                    // Setup image
                    this.$imageContainer
                        .append('<span class="image-wrapper"><a class="advance-link" rel="history" href="'+getHref()+'#'+this.data[this.getNextIndex(this.currentIndex)].hash+'" title="'+image.alt+'"></a></span>')
                        .find('a')
                        .append(image)
                        .click(function(e) {
                            clickHandler(e, gallery, this);
                        });
                }

                if (this.settings.onTransitionIn)
                    this.settings.onTransitionIn();
                else
                    this.$transitionContainers.show();

                return this;
            },

            syncThumbs: function() {
                if (this.$thumbsContainer) {
                    var page = Math.floor(this.currentIndex / this.settings.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.settings.onPageTransitionIn)
                        gallery.settings.onPageTransitionIn();
                    else
                        gallery.$thumbsContainer.show();
                };

                // Transition Out the thumbsContainer
                if (this.settings.onPageTransitionOut) {
                    this.settings.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.settings.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.settings.enableTopPager) {
                    $topPager.empty();
                    this.buildPager($topPager);
                }

                // Rebuild bottom pager
                if (needsPagination && this.settings.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.settings.numThumbs;
                var stopIndex = startIndex+this.settings.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.settings.numThumbs;
                
                // Prev Page Link
                if (this.currentPage > 0) {
                    pager.append('<a rel="history" href="'+getHref()+'#0" title="'+this.settings.firstPageLinkText+'">'+this.settings.firstPageLinkText+'</a>');
                    var prevPage = startIndex - this.settings.numThumbs;
                    pager.append('<a rel="history" href="'+getHref()+'#'+this.data[prevPage].hash+'" title="'+this.settings.prevPageLinkText+'">'+this.settings.prevPageLinkText+'</a>');
                }

                // Page Index Links
                for (i=this.currentPage-4; i<=this.currentPage+4; i++) {
                    var pageNum = i+1;
                    
                    if (i == this.currentPage)
                        pager.append('<span class="current">'+pageNum+'</span>');
                    else if (i>=0 && i<this.numPages) {
                        var imageIndex = i*this.settings.numThumbs;
                        pager.append('<a rel="history" href="'+getHref()+'#'+this.data[imageIndex].hash+'" title="'+pageNum+'">'+pageNum+'</a>');
                    }
                }

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

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

                return this;
            }
        });

        // Now initialize the gallery
        this.settings = $.extend({}, defaults, settings);
        //enableHistory = this.settings.enableHistory; // Ghismo

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

        this.interval = 0;
        
        if (this.settings.imageContainerSel) this.$imageContainer = $(this.settings.imageContainerSel);
        if (this.settings.captionContainerSel) this.$captionContainer = $(this.settings.captionContainerSel);
        if (this.settings.loadingContainerSel) this.$loadingContainer = $(this.settings.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.settings.numThumbs);
        this.currentPage = -1;
        this.currentIndex = 0;
        var gallery = this;

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

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

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

        // Initialize history only once when the first gallery on the page is initialized
        historyInit();
        
        // Build image
        var hash = getHash();
        var hashGallery = (hash >= 0) ? getGallery(hash) : 0;
        var gotoIndex = (hashGallery && this == hashGallery) ? (hash-this.offset) : 0;
        this.goto(gotoIndex);

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

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

        return this;
    };
})(jQuery);

