(function($){
	$.preloadImage = function(image, callback) {
		var self = this;
		this.file = image;
		this.callback = callback;
		this.loaded = false;
		this.aborted = false;
		this.error = false;
		this.image = new Image();
		
		this.cancelEvent = function() {
			if (this.image && this.image.onload) {
				this.image.onload = null;
			}
			if (this.image && this.image.onabort) {
				this.image.onabort = null;
			}
			if (this.image && this.image.onerror) {
				this.image.onerror = null;
			}
		};
		
		this.on_load = function() {
			self.loaded = true;
			if (self.callback && typeof self.callback == "function") {
				self.callback.apply(self, ["loaded"]);
			}
			self.cancelEvent();
		};
		
		this.on_error = function() {
			self.error = true;
			if (self.callback && typeof self.callback == "function") {
				self.callback.apply(self, ["error"]);
			}
			self.cancelEvent();
		};
		
		this.on_abort = function() {
			self.aborted = true;
			if (self.callback && typeof self.callback == "function") {
				self.callback.apply(self, ["aborted"]);
			}
			self.cancelEvent();
		};
		
		this.image.src = this.file;
		
		this.autoCheckIntervalId = 0;
		this.autoCheck = function() {
			if (self.loaded || self.aborted || self.error) {
				window.clearInterval(self.autoCheckIntervalId);
				return;
			}
			
			if (self.image && self.image.complete) {
				window.clearInterval(self.autoCheckIntervalId);
				self.loaded = true;
				if (self.callback && typeof self.callback == "function") {
					self.callback.apply(self, ["loaded"]);
				}
				self.cancelEvent();
				return;
			}
			
			if (self.image && (self.image.width || self.image.height)) {
				window.clearInterval(self.autoCheckIntervalId);
				self.loaded = true;
				if (self.callback && typeof self.callback == "function") {
					self.callback.apply(self, ["loaded"]);
				}
				self.cancelEvent();
				return;
			}
		};
		
		this.autoCheckIntervalId = window.setInterval( self.autoCheck, 500 );
	};
	
	$.fn.carousel = function(options) {
		var isMethodCall = (typeof options == "string") || false;
		var args = arguments;
			
		$(this).each(function() {
			var carousel = $(this).data("carousel");
			
			if (isMethodCall && carousel) {
				if ( carousel[options] && $.isFunction(carousel[options]) ) {
					carousel[options].apply(carousel, $.makeArray(args).slice(1));
				}
			} else if (!isMethodCall) {
				if ( !carousel ) {
					carousel = new $.carousel(this, options);
					$(this).data("carousel", carousel);
				} else {
					carousel.setOptions(options);
				}
			}
		});
		
		return this;
	}
	
	$.carousel = function(elem, options) {
		this.elem = elem;
		this.options = $.extend( {}, $.carousel.defaults, options );
		this.init();
	};
	
	$.extend( $.carousel, {
		defaults: {
			loadingImage: "images/ajax-loader.gif",
			activeIndex: 0,
			slideDuration: 1200
		},
		prototype: {
			init: function() {
				this.slides_container = $(".carousel_images>ul", this.elem);
				this.slides = $(">li>a", this.slides_container);
				
				for (var i=0,n=this.slides.length; i < n; i++) {
					if ( $(this.slides[i]).attr('rel') != "" ) {
						$("<img />").attr("src", this.options.loadingImage).addClass("loading").appendTo(this.slides[i]);
					}
					this.slides[i] = {
						image: $(this.slides[i]).attr('rel') ? $(this.slides[i]).attr('rel') : "",
						loaded: false,
						loading: false,
						elem: this.slides[i],
						parentId: $(this.slides[i]).parent().attr("id") || "",
						title: $(this.slides[i]).attr("title")
					};
				}
				
				if ( this.options.activeIndex < 0 || this.options.activeIndex > this.slides.length-1 ) {
					this.options.activeIndex = 0;
				}
				
				if (this.slides.length > 0) {
					this.nav = $(".carousel_nav", this.elem);
					this.next = $(".carousel_nav a.next", this.elem);
					this.prev = $(".carousel_nav a.prev", this.elem);
					this.legend = $(".carousel_nav .legend", this.elem);
					
					if ( this.next || this.prev ) {
						this.setupNav();
					}
					
					this.nav_height = $(this.nav).innerHeight();
					this.nav.css({bottom:-this.nav_height+"px"});
					
					this.slide_width = $(">li:eq(0)", this.slides_container).outerWidth(true);
					
					this.slides_container.css({
						width: (this.slide_width * this.slides.length)+"px"
					});
					
					this.moveTo(this.options.activeIndex);
				}
			},
			
			setupNav: function(){
				var self = this;
				if ( this.next ) {
					this.next.bind("click", function(){
						this.blur();
						self.moveTo(self.current_index+1);
						return false;
					});
				}
				if ( this.prev ) {
					this.prev.bind("click", function(){
						this.blur();
						self.moveTo(self.current_index-1);
						return false;
					});
				}
				
				$(this.elem).bind("mouseover", function(){
					self.nav.stop();
					self.nav.animate({bottom:0}, {queue:false,easing:"easeInOutExpo",duration:600});
				});
				
				$(this.elem).bind("mouseout", function(){
					self.nav.stop();
					self.nav.animate({bottom:-self.nav_height+"px"}, {queue:false,easing:"easeInOutExpo",duration:600});
				});
			},
			
			moveTo: function(idx) {
				if ( idx < 0 || idx > this.slides.length-1 ) return;
				
				this.slides_container.stop();
				this.slides_container.animate({left: (-1 * this.slide_width * idx) + "px" }, {
					queue:false,
					easing:"easeInOutExpo",
					duration:this.options.slideDuration
				});
				
				if (!this.slides[idx].loaded) {
					if ( this.slides[idx].image != "" ) {
						this.slides[idx].loading = true;
						var loading = this.slides[idx];
						new $.preloadImage( this.slides[idx].image, function() {
							$("<img />").attr("src", loading.image).attr("alt", loading.title).css({opacity:0,display:"block"}).appendTo(loading.elem);
							$("img.loading", loading.elem).remove();
							$("img", loading.elem).animate({opacity:1},{queue:false,duration:800,complete:function(){
								$(this).css({opacity:""});
							}});
							loading.loaded = true;
							loading.loading = false;
						});
					}
				}
				
				this.current_index = idx;
				
				if ( idx > 0 && this.prev ) {
					this.prev.attr("title", this.slides[idx-1].title).attr("href", "#" + this.slides[idx-1].parentId).removeClass("prev_disabled");
				} else {
					this.prev.attr("title", "").attr("href", "#").addClass("prev_disabled");
				}
				
				if ( idx < this.slides.length-1 && this.next ) {
					this.next.attr("title", this.slides[idx+1].title).attr("href", "#" + this.slides[idx+1].parentId).removeClass("prev_disabled");
				} else {
					this.next.attr("title", "").attr("href", "#").addClass("prev_disabled");
				}
				
				if ( this.legend ) {
					this.legend.html("Photo " + [idx+1] + " of " + this.slides.length);
				}
			}
		}
	});
	
	$().ready(function(){
		$(".carousel").carousel();
	});
})(jQuery);