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