/*

 * 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);

