;(function($) {
	
	$.widget("ui.rotate", {
			 
		_shuffle: function(arr) {
			for(
				var j, x, i = arr.length; i;
				j = parseInt(Math.random() * i),
				x = arr[--i], arr[i] = arr[j], arr[j] = x
			);
			return arr;
		},
		
		_init: function() {
			var self = this;
			if (!this.options.total || this.options.total <= 0) {
				throw 'jQuery ' + this.namespace + "." + this.widgetName + ": The 'total' option is not a positive number.";
			}
			else if (typeof this.options.getFullURL != "function") {
				throw 'jQuery ' + this.namespace + "." + this.widgetName + ": The 'getFullURL' option is not a function.";
			}
			else if (!this.element.is("div")) {
				throw 'jQuery ' + this.namespace + "." + this.widgetName + ": Can only be used on DIVs.";
			}
			else {
				this.element
					.addClass(this.widgetBaseClass)
					.css('position', 'relative');
				
				this.stop();
				
				this._imgs = [];
				
				// The order of the images
				this._imageOrder = [];
				for (var i = 0; i < this.options.total; i++) this._imageOrder[i] = i + 1;
				
				// Randomize the order of the images, if requested.
				if (this.options.randomizeOrder) {
					this._imageOrder = this._shuffle(this._imageOrder);
				}
				
				// Determine the starting num.
				this._num = 
					// Randomize the starting num, if requested.
					typeof this.options.start == "string" && this.options.start.toUpperCase() == "RANDOM"
					? Math.round( (Math.random() * (this.options.total - 1)) + 1)
					: this.options.start;
				
				// Preload the first image
				this._preload(this._num);
				
				if (this.options.fade) {
					this._fadeState = "INIT";
					this.options.fadeImg = this.options.fadeImg ? $(this.options.fadeImg) : $('img:first', this.element);
					this.options.fadeImg.fadeTo(0, 0, function() {
						self.options.onFadeComplete && self.options.onFadeComplete(this._imageOrder[self._num]);
					});
					
					this.element.addClass(this.widgetBaseClass + '-fade');
					this.element.css({
						'background-position': this.element.css('padding-left') + ' ' + this.element.css('padding-top'),
						'background-repeat': 'no-repeat'
					});
				}
				
				if (this.options.clickNext) {
					$(this.element)
						.css('cursor', 'pointer')
						.click(function(e) {
							self.stop();
							self.gotoNext(true);
						});
				}
				
				if (this.options.tabs) {
					this._tabs = $('<div>')
						.addClass(this.widgetBaseClass + '-tabs')
						.css({'position': 'absolute', 'cursor': 'pointer'})
						.appendTo(this.element);
					
					if (this.options.tabs == "right") {
						this._tabs
							.css(this.options.tabs, '0')
							.css(this.options.tabs == "right" || this.options.tabs == "left" ? "top" : "left", this.options.tabStart + 'px');
					}
					
					for (var i = 1; i <= this.options.total; i++) {
						(function(index){
							$('<div></div>')
								.addClass(self.widgetBaseClass + '-tab ' + self.widgetBaseClass + '-tab' + index)
								.css({
									'z-index': self.options.total - index,
									'top': (self.options.tabSpacing * (index - 1)) + 'px'
								})
								.click(function(e){
									self.stop();
									self.gotoNum(index, true);
									e.stopPropagation();
								})
								.append($('<div/>'))
								.appendTo(self._tabs);
						})(i);
					}
					
					this.element.bind(this.widgetEventPrefix + 'onChange', function(e, num) {
						// Update tabs
						$('.' + self.widgetBaseClass + '-activetab', self._tabs).each(function() {
							$(this)
								.removeClass(self.widgetBaseClass + '-activetab')
								.css('background-image', 'url(/js/jquery/images/jquery.ui.rotate/tabs/right/inactive.png)');
						});
						
						$('.' + self.widgetBaseClass + '-tab' + self._num, self._tabs)
							.addClass(self.widgetBaseClass + '-activetab')
							.css('background-image', 'url(/js/jquery/images/jquery.ui.rotate/tabs/right/active.png)');
					});
				}
				
				if (this.options.buttons) {
					this._buttons = $('<div>').addClass(this.widgetBaseClass + '-buttons').appendTo(this.element);
					
					// Previous Button
					this._prevButton = $('<div>')
						.addClass(this.widgetBaseClass + '-button-prev')
						.css('background-image', 'url(' + this.options.buttonBG + ')')
						.appendTo(this._buttons);
					
					$('<div>').appendTo(this._prevButton)
						.css('background-image', 'url(' + this.options.prevButton + ')')
						.click(function() { self.stop(); self.gotoPrevious(true); });
					
					// Page Count
					this._pageCount = $('<div>')
						.addClass(this.widgetBaseClass + '-button-page')
						.css('background-image', 'url(' + this.options.buttonBG + ')')
						.text(this.options.total + ' / ' + this.options.total)
						.appendTo(this._buttons);
					
					// Measuer the estimated max size of the page count
					this._pageCount.width(this._pageCount.width() + 18);
					
					// Next Button
					this._nextButton = $('<div>')
						.addClass(this.widgetBaseClass + '-button-next')
						.css({
							'background-image': 'url(' + this.options.buttonBG + ')'
						})
						.appendTo(this._buttons);
					
					$('<div>').appendTo(this._nextButton)
						.css({
							'background-image': 'url(' + this.options.nextButton + ')'
						})
						.click(function() { self.stop(); self.gotoNext(true); });
					
					// Set the width of the buttons container.
					this._buttons.width(this._prevButton.outerWidth() + this._pageCount.outerWidth() + this._nextButton.outerWidth() + 6);
					
					this._buttons.css({
						'left': (this.element.width() - this._buttons.outerWidth()) / 2,
						'top': this.element.height() - this._buttons.outerHeight() - 6
					});
					
					this._buttons.hide();
					
					this.element.hover(
						function() {
							self._buttons.show();
						},
						function() {
							self._buttons.hide();
						}
					);
				}
				
				if (this.options.autoStart) this.start();
				else this.gotoNum(this._num);
			}
		},
		
		list: function() {
			return this._imageOrder;
		},
		
		start: function() {
			this._state = "START";
			this.gotoNum(this._num);
		},
		
		stop: function() {
			this._state = "STOP";
			if (this._tid) clearTimeout(this._tid);
		},
		
		nextNum: function(from) {
			var num = (typeof from != "undefined" ? from : this._num);
			if (num >= this.options.total && this.options.loop) {
				return 1;
			}
			else if (num < this.options.total) {
				return num + 1;
			}
		},
		
		previousNum: function(from) {
			var num = (typeof from != "undefined" ? from : this._num);
			if (num <= 1 && this.options.loop) {
				return this.options.total;
			}
			else if (num > 1) {
				return num - 1;
			}
		},
		
		gotoNext: function(skipFade) {
			this.gotoNum(this.nextNum(), skipFade);
		},
		
		gotoPrevious: function(skipFade) {
			this.gotoNum(this.previousNum(), skipFade);
		},
		
		gotoNum: function(num, skipFade) {
			var self = this;
			
			this._num = num;
			
			if (this.options.buttons) this._pageCount.text(num + ' / ' + this.options.total);
			
			if (this.options.fade) {
				if (this._fadeState == "INIT") {
					this.element.css('background-image', 'url(' + this.options.getFullURL(this.options.randomizeOrder ? this._imageOrder[num - 1] : num) + ')');
					this._fadeState = "BG";
				}
				else if (this._fadeState == "BG") {
					this.options.fadeImg
						.attr('src', this.options.getFullURL(this.options.randomizeOrder ? this._imageOrder[num - 1] : num))
						.stop(false, true).fadeTo(skipFade ? 0 : this.options.fadeTime, 1, function() {
							self.options.onFadeComplete && self.options.onFadeComplete(self._num);
						});
					
					this._fadeState = "IMG";
				}
				else if (this._fadeState == "IMG") {
					this.element.css('background-image', 'url(' + this.options.getFullURL(this.options.randomizeOrder ? this._imageOrder[num - 1] : num) + ')');
					this.options.fadeImg.stop(false, true).fadeTo(skipFade ? 0 : this.options.fadeTime, 0, function() {
						self.options.onFadeComplete && self.options.onFadeComplete(self._num);
					});
					this._fadeState = "BG";
				}
			}
			else {
				this.element.attr('src', this.options.getFullURL(this.options.randomizeOrder ? this._imageOrder[num - 1] : num));
			}
			
			this._trigger('onChange', {}, [ this.options.randomizeOrder ? this._imageOrder[num - 1] : num ]);
			
			// Preload the next image.
			if (this.options.preload == "NEXT") {
				this._preload(this.options.direction == "NEXT" ? this.nextNum() : this.previousNum());
			}
			
			this._tid = setTimeout(function(){
				if (self._state == "START") {
					self.options.direction == "NEXT"
						? self.gotoNext()
						: self.gotoPrevious();
				}
			}, this.options.seconds * 1000);
		},
		
		currentNum: function() {
			return this.options.randomizeOrder ? this._imageOrder[this._num - 1] : this._num;
		},
		
		// Preload an image.
		_preload: function(num, loadFn, errorFn) {
			var self = this, url = this.options.getFullURL(this.options.randomizeOrder ? this._imageOrder[num - 1] : num);
			
			// Skip if already loaded.
			if (self._imgs[url]) {
				loadFn && loadFn();
				return;
			}
			else {
				var img = $(new Image());
				
				img.load(function(e) {
					self._imgs[url] = img;
					loadFn && loadFn();
				});
				
				if (errorFn) img.error(errorFn);
				
				img.attr('src', url);
			}
		}
		
	});
	
	$.extend($.ui.rotate, {
		getter: "options currentNum previousNum nextNum list",
		defaults: { 
			seconds: 10,
			fade: false,
			fadeImg: null,
			fadeTime: 500, // Miliseconds
			start: 1, // a number or "RANDOM"
			total: null,
			randomizeOrder: false,
			getFullURL: null,
			onChange: null,
			onFadeComplete: null,
			preload: "NEXT", // NO, NEXT, or ALL
			autoStart: false,
			clickNext: false,
			loop: true,
			direction: "NEXT", // NEXT or PREVIOUS
			buttons: false,
			nextButton: 'data:image/gif;base64,R0lGODlhBgAHAIABAP///////yH5BAEAAAEALAAAAAAGAAcAAAIKRA5pockNTUOOFgA7',
			prevButton: 'data:image/gif;base64,R0lGODlhBgAHAIABAP///////yH5BAEAAAEALAAAAAAGAAcAAAILBIJ2oddvWEsKsQIAOw==',
			buttonBG: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA9JREFUeNpiYGBgKAYIMAAAeAB0g+OgCQAAAABJRU5ErkJggg==',
			tabs: null,
			tabSpacing: 40, // pixels
			tabStart: 7 // pixels
		}
	});
	
})(jQuery);
