/*
 * jQuery Galleriffic plugin
 *
 * Copyright (c) 2008 Trent Foley
 * 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
 */
;(function($) {

	var ver = 'adeptGallery-0.1';

	function clickHandler(gallery) {
		gallery.pause();
		return false;
	}

	$.fn.galleriffic = {
		ver: function() {
			return ver;
		},
	
		defaults: {
			delay:                3000,
			numThumbs:            20,
			enableTopPager:       true,
			enableBottomPager:    true,
			imageContainerSel:    '',
			thumbsContainerSel:   '',
			controlsContainerSel: '',
			renderSSControls:     true,
			renderNavControls:    true,
			playLinkText:         'Play',
			pauseLinkText:        'Pause',
			prevLinkText:         'Previous',
			nextLinkText:         'Next', 
			enableDownload:       true,
			downloadLinkText:     'Download Original'
		},
		
		init: function(data, settings) {
			this.settings = $.extend({}, this.defaults, settings);
		
			if (this.interval)
				clearInterval(this.interval);

			this.interval = 0;

			this.data = data;
			if (this.settings.imageContainerSel) this.$imageContainer = $(this.settings.imageContainerSel);
			if (this.settings.thumbsContainerSel) this.$thumbsContainer = $(this.settings.thumbsContainerSel);
			this.currentPage = -1;
			this.currentIndex = 0;

			this.numPages = Math.ceil(this.data.length/this.settings.numThumbs);

			var gallery = this;

			// Setup controls
			if (this.settings.controlsContainerSel) {
				this.$controlsContainer = $(this.settings.controlsContainerSel).empty();
				
				if (this.settings.renderSSControls) {
					var gallery = this;
					this.$controlsContainer
						.append('<div class="ss-controls"><span class="play" title="Play">'+this.settings.playLinkText+'</span></div>')
						.find('div.ss-controls span')
						.click(function() { gallery.toggleSlideshow(); });
				}
			
				if (this.settings.renderNavControls) {					
					this.$controlsContainer
						.append('<div class="nav-controls"><a class="prev" rel="history" title="Previous">'+this.settings.prevLinkText+'</a><a class="next" rel="history" title="Next">'+this.settings.nextLinkText+'</a></div>')
						.find('a[@rel="history"]')
						.click(function() { clickHandler(gallery); });
				}
			}

			// Initialize history.
			// The callback is called at once by present location.hash to build the initial gallery 
			this.historyInit(function(h) {
				// Using present location.hash always (seems to work, unlike the hash argument passed to this callback)
				var hash = location.hash;
				gallery.goto(hash ? hash.replace(/^.*#/, '') : 0);
			});
			
			// Kickoff Image Preloader
			return this.setPreloadTimeout();
		},

		setPreloadTimeout: function() {
			var gallery = this;
			setTimeout(function() { gallery.preloadImages(); }, 1000);
			
			return this;
		},

		preloadImages: function() {
			if (!this.preloadComplete) {
				// Load images forward from currentIndex
				for (i = 1; i < this.data.length; i++) {
					// Check if we need to relocate where we are preloading data
					if (this.relocatePreload) {
						this.relocatePreload = false;
						this.preloadImages();
						break;
					}

					var index = this.currentIndex + i;
					if (index >= this.data.length)
						index = index - this.data.length;
					
					// If already loaded, continue
					if (this.data[index].loaded) continue;
					
					// Preload image ...
					var preloadedImage = new Image();

					// Wire up image load callback
					var gallery = this;
					preloadedImage.onload = function() {
						gallery.data[index].loaded = true;
					};

					// set src (starts the preload)
					preloadedImage.src = this.data[index].slide;
					
					// Need to finish loading current image before continuing ...
				}

				this.preloadComplete = true;
			}

			return this;
		},

		getNextIndex: function() {
			var nextIndex = this.currentIndex+1;
			if (nextIndex >= this.data.length)
				nextIndex = 0;
			return nextIndex;
		},
		
		getPrevIndex: function() {
			var prevIndex = this.currentIndex-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 span').removeClass().addClass('play')
						.attr('title', '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 span').removeClass().addClass('pause')
						.attr('title', 'Pause')
						.html(this.settings.pauseLinkText);
				}
			}

			return this;
		},

		ssAdvance: function() {
			var nextIndex = this.getNextIndex();
			// Seems to be working on both FF and Safari
			location.href = '#'+nextIndex;
			
			// IE we need to explicity call goto
			if ($.browser.msie) {
				this.goto(nextIndex);
			}
			
			return this;
		},

		goto: function(i) {
			var index = (+i);

			if (index < 0) index = 0;
			else if (index >= this.data.length) index = this.data.length-1;
			this.currentIndex = index;
			return this.refresh();
		},
		
		refresh: function() {
			if (this.$controlsContainer) {
				this.$controlsContainer
					.find('div.nav-controls a.prev').attr('href', '#'+this.getPrevIndex()).end()
					.find('div.nav-controls a.next').attr('href', '#'+this.getNextIndex());
			}
			
			if (this.$imageContainer) {
				var isFading = 1;
				var isLoaded = 0;
			
				var newImage = new Image();
			
				// show spinner
			
				var gallery = this;
				// Wire up mainImage onload event
				newImage.onload = function() {
					isLoaded = 1;
				
					if (!isFading) {
						gallery.$imageContainer.fadeIn('fast');
					}
				};
			
				// hide img
				this.$imageContainer.fadeOut('fast', function() {
					isFading = 0;
				
					gallery.buildImage();

					if (isLoaded) gallery.$imageContainer.fadeIn('fast');
				});
			
				// set src
				newImage.src = this.data[this.currentIndex].slide;
				
				// This causes the preloader (if still running) to relocate out from the currentIndex
				this.relocatePreload = true;
			}

			return this.syncThumbs();
		},
		
		buildImage: function() {
			if (this.$imageContainer) {
				this.$imageContainer.empty();

				var gallery = this;
				var imageData = this.data[this.currentIndex];
			
				// Setup image
				this.$imageContainer
					.append('<a class="advance-link" rel="history" href="#'+this.getNextIndex()+'" title="'+imageData.title+'"><img src="'+imageData.slide+'" alt="'+imageData.title+'" /></a>')
					.find('a[@rel="history"]')
					.click(function() { clickHandler(gallery); });
			
				// Download link
				if (this.settings.enableDownload)
					this.$imageContainer.append('<a class="download" href="'+imageData.original+'">'+this.settings.downloadLinkText+'</a>');
			}

			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();
				} else {
					var selectedThumb = this.currentIndex % this.settings.numThumbs;

					// Remove existing selected class and add selected class to new thumb
					this.$thumbsContainer
						.find('ul.thumbs li.selected')
						.removeClass('selected')
						.end()
						//.find('ul.thumbs a[href="#'+this.currentIndex+'"]')
						.find('ul.thumbs li').eq(selectedThumb)
						.addClass('selected');
				}
			}

			return this;
		},

		updateThumbs: function() {
			if (this.$thumbsContainer) {
				// Initialize currentPage to first page
				if (this.currentPage < 0)
					this.currentPage = 0;
			
				var startIndex = this.currentPage*this.settings.numThumbs;
		        var stopIndex = startIndex+this.settings.numThumbs-1;
		        if (stopIndex >= this.data.length)
					stopIndex = this.data.length-1;

				var needsPagination = this.data.length > this.settings.numThumbs;

				// Clear thumbs container
				this.$thumbsContainer.empty();
			
				// Rebuild top pager
				if (needsPagination && this.settings.enableTopPager) {
					this.$thumbsContainer.append('<div class="top pagination"></div>');
					this.buildPager(this.$thumbsContainer.find('div.top'));
				}

				// Rebuild thumbs
				var $ulThumbs = this.$thumbsContainer.append('<ul class="thumbs"></ul>').find('ul.thumbs');
				for (i=startIndex; i<=stopIndex; i++) {
					var selected = '';
				
					if (i==this.currentIndex)
						selected = ' class="selected"';
					
					var imageData = this.data[i];
					$ulThumbs.append('<li'+selected+'><a rel="history" href="#'+i+'" title="'+imageData.title+'"><img src="'+imageData.thumb+'" alt="'+imageData.title+'" /></a></li>');
				}

				// Rebuild bottom pager
				if (needsPagination && this.settings.enableBottomPager) {
					this.$thumbsContainer.append('<div class="bottom pagination"></div>');
					this.buildPager(this.$thumbsContainer.find('div.bottom'));
				}

				// Add click handlers
				var gallery = this;
				this.$thumbsContainer.find('a[@rel="history"]').click(function() { clickHandler(gallery); });
			}

			return this;
		},

		buildPager: function(pager) {
			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="#'+prevPage+'" title="Previous Page">&lt; Prev</a>');
			}
			
			// Page Index Links
			for (i=this.currentPage-5; i<=this.currentPage+5; i++) {
				var pageNum = i+1;
				
				if (i == this.currentPage)
					pager.append('<strong>'+pageNum+'</strong>');
				else {
					var imageIndex = i*this.settings.numThumbs;
					if (i>=0 && i<this.numPages) {
						pager.append('<a rel="history" href="#'+imageIndex+'" title="Go To Page '+pageNum+'">'+pageNum+'</a>');
					}
				}
			}
			
			// Next Page Link
			var nextPage = startIndex+this.settings.numThumbs;
			if (nextPage < this.data.length) {
				pager.append('<a rel="history" href="#'+nextPage+'" title="Next Page">Next &gt;</a>');
			}
			
			return this;
		},
		
		historyCurrentHash: undefined,
	
		historyCallback: undefined,
		
		historyInit: function(callback){
			this.historyCallback = callback;
			var current_hash = location.hash;
			
			this.historyCurrentHash = current_hash;
			if ($.browser.msie) {
				// To stop the callback firing twice during initilization if no hash present
				if (this.historyCurrentHash == '') {
					this.historyCurrentHash = '#';
				}
			
				// add hidden iframe for IE
				$("body").prepend('<iframe id="jQuery_history" style="display: none;"></iframe>');
				var ihistory = $("#jQuery_history")[0];
				var iframe = ihistory.contentWindow.document;
				iframe.open();
				iframe.close();
				iframe.location.hash = current_hash;
			} else if ($.browser.safari) {
				// etablish back/forward stacks
				this.historyBackStack = [];
				this.historyBackStack.length = history.length;
				this.historyForwardStack = [];
				
				this.isFirst = true;
			}
			this.historyCallback(current_hash.replace(/^#/, ''));
			
			var gallery = this;
			setInterval(function() { gallery.historyCheck(); }, 100);
			
			return this;
		},
		
		historyAddHistory: function(hash) {
			// This makes the looping function do something
			this.historyBackStack.push(hash);
			
			this.historyForwardStack.length = 0; // clear forwardStack (true click occured)
			this.isFirst = true;
		},
		
		historyCheck: function() {
			if ($.browser.safari) {
				if (!this.dontCheck) {
					var historyDelta = history.length - this.historyBackStack.length;
					
					if (historyDelta) { // back or forward button has been pushed
						this.isFirst = false;
						if (historyDelta < 0) { // back button has been pushed
							// move items to forward stack
							for (var i = 0; i < Math.abs(historyDelta); i++) this.historyForwardStack.unshift(this.historyBackStack.pop());
						} else { // forward button has been pushed
							// move items to back stack
							for (var i = 0; i < historyDelta; i++) this.historyBackStack.push(this.historyForwardStack.shift());
						}
						var cachedHash = this.historyBackStack[this.historyBackStack.length - 1];
						if (cachedHash != undefined) {
							this.historyCurrentHash = location.hash;
							this.historyCallback(cachedHash);
						}
					} else if (this.historyBackStack[this.historyBackStack.length - 1] == undefined && !this.isFirst) {
						// back button has been pushed to beginning and URL already pointed to hash (e.g. a bookmark)
						// document.URL doesn't change in Safari
						if (document.URL.indexOf('#') >= 0) {
							this.historyCallback(document.URL.split('#')[1]);
						} else {
							var current_hash = location.hash;
							this.historyCallback('');
						}
						this.isFirst = true;
					}
				}
			} else {
				// otherwise, check for location.hash
				var current_hash = location.hash;
				if(current_hash != this.historyCurrentHash) {
					this.historyCurrentHash = current_hash;
					this.historyCallback(current_hash.replace(/^#/, ''));
				}
			}
			
			return this;
		}

	};

})(jQuery);
