/*
 * 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 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', '#'+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', '#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', '#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 = '#'+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', '#'+gallery.data[gallery.getPrevIndex(gallery.currentIndex)].hash).end()
              .find('div.nav-controls a.next').attr('href', '#'+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="#'+this.data[nextIndex].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) {
          var prevPage = startIndex - this.settings.numThumbs;
          pager.append('<a rel="history" href="#'+this.data[prevPage].hash+'" title="'+this.settings.prevPageLinkText+'">'+this.settings.prevPageLinkText+'</a>');
        }

        // Page Index Links
        for (i=this.currentPage-3; i<=this.currentPage+3; 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="#'+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="#'+this.data[nextPage].hash+'" title="'+this.settings.nextPageLinkText+'">'+this.settings.nextPageLinkText+'</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="#pause" class="pause" title="'+this.settings.pauseLinkText+'">'+this.settings.pauseLinkText+'</a></div>');
        } else {
          this.$controlsContainer
            .append('<div class="ss-controls"><a href="#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);

