(function($){
	var preloadImage = function(image, callback) {
		var self = this;
		this.file = image;
		this.callback = callback;
		this.loaded = false;
		this.aborted = false;
		this.error = false;
		this.width = 0;
		this.height = 0;
		this.src = '';
		this.alt = '';
		this.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.callback = null;
			this.image = null;
			this.dummy.remove();
		};
		
		this.imageLoaded = function() {
			var self = this;
			
			this.width = this.image.width;
			this.height = this.image.height;
			
			//create dummy <img> element to cache rendered image (not only loaded)
			this.dummy = $("<img src='' style='position:absolute;left:-10000px;top:0;visibility:hidden;' />").appendTo("body");
			
			$(this.dummy).bind("load", function() {
				if ( typeof self.callback !== "undefined" && typeof self.callback == "function") {
					self.callback.apply(self, [self, "loaded"]);
				}
				
				self.cancelEvent();
				self = null;
			});
			
			this.dummy.get(0).src = this.src;
		}
		
		//this.image.src = this.file;
		this.image = new Image();
		this.image.src = this.file;
		this.src = this.image.src;
		
		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;
				
				self.imageLoaded();
				return;
			}
			
			if (self.image && (self.image.width || self.image.height)) {
				window.clearInterval(self.autoCheckIntervalId);
				self.loaded = true;
				
				self.imageLoaded();
				return;
			}
		};
		
		this.autoCheckIntervalId = window.setInterval( self.autoCheck, 500 );
	};
	
	var preloadImages = function(images, callback) {
		this.loaded = [];
		this.objects = [];
		
		this.images = images;
		this.callback = callback;
		
		this.destroy = function() {
			this.loaded = this.objects = this.images = this.callback = null;
		}
		
		this.mycallback = function(obj, idx) {
			var b = true;
			
			this.loaded[idx] = true;
			this.objects[idx] = obj;
			
			for ( var i = 0; i < images.length; i++ ) {
				if ( typeof this.loaded[i] == "undefined" || this.loaded[i] !== true ) {
					b = false;
					break;
				}
			}
			
			if ( b ) {
				this.callback.call(this);
				this.destroy();
			}
		}
		
		for ( var i = 0; i < images.length; i++ ) {
			(function(self, idx) {
				self.loaded.push(false);
				new preloadImage(self.images[idx], function(obj, status) {
					if (status == "loaded") {
						self.mycallback(this, idx);
					}
				});
			})(this, i);
			
		}
	};
	
	var component_template = '<div class="slideshow-slides-container"><div class="slideshow-slides">#SLIDES#</div><div class="loading"></div></div>',
		slide_template = '<div class="slideshow-slide"><div class="slide-image">#SLIDE#</div></div>';
	
	$.widget("ui.slideshow", {
		options: {
			options: {
				autoRun: false,
				autoRun_timeout: 5000
			},
			slides: []
		},
		
		_create_slides: function() {
			this.element.append(component_template.replace('#SLIDES#', ''));
		},
		
		_create: function(){
			if ( $.isArray(this.options.slides) && this.options.slides.length ) {
				this.element.addClass("slideshow");
				this._create_slides();
				
				this.slidesContainer = $(".slideshow-slides-container", this.element);
				this.slidesHolder = $(".slideshow-slides", this.slidesContainer);
				this.loader = $(".loading", this.slidesContainer);
				
				this.slidesHolder.append('<div class="slideshow-back"></div>');
				
				this.slideBack = $(".slideshow-back", this.slidesHolder);
				
				this.slidesContainer.css({
					width: this.options.options.forceWidth,
					height: this.options.options.forceHeight,
				});
				
				this.slidesHolder.css({
					width: this.options.options.forceWidth,
					height: this.options.options.forceHeight,
				});
				
				this.element.css({
					width: this.options.options.forceWidth,
					height: this.options.options.forceHeight,
				});
				
				this.curSlide = -1;
				this.curVisibleSlide = -1;
				this.curIsText = false;
				
				this.autoTimeout = null;
				
				this.loadSlide(0, false);
			}
		},
		
		_hide: function(el, cb) {
			if ( el.is(":visible") ) {
				el.stop().animate({opacity:0}, {queue:false, duration:1000, complete:function(){
					$(this).hide();
					if ( typeof cb !== "undefined" && $.isFunction(cb) ) {
						cb.call(this);
					}
				}});
			} else {
				el.hide();
				if ( typeof cb !== "undefined" && $.isFunction(cb) ) {
					cb.call(el[0]);
				}
			}
		},
		
		_show: function(el, cb) {
			var opacity = el.css("opacity") || "";
		
			if ( opacity == "" || opacity > 0.99 ) {
				if ( !el.is(":visible") ) {
					el.css("opacity", 0);
				}
			}
			if ( !el.is(":visible") ) {
				el.show();
			}
			
			el.stop().animate({opacity:1}, {queue:false, duration:1000, complete:function(){
				$(this).css("opacity","");
				if ( $.browser.msie && $.browser.version < 9 ) {
					$(this).find("div").css("opacity","");
				}
				
				if ( typeof cb !== "undefined" && $.isFunction(cb) ) {
					cb.call(this);
				}
			}});
			
		},
		
		_hideSlide: function(idx) {
			var 
				opt = this.options,
				slides = opt.slides,
				self = this;
				
			if ( slides[idx].elementTitle ) {
				this._hide(slides[idx].element);
				slides[idx].elementTitle.animate({left:'100%', opacity:0}, {queue:false, duration:2000, complete:function(){
					$(this).css({
						opacity:"",
						display:'none'
					});
				}});
			} else {
				this._hide(slides[idx].element);
			}
		},
		
		_showSlide: function(idx) {
			var 
				opt = this.options,
				slides = opt.slides,
				self = this;
			
			this.curVisibleSlide = idx;
			
			if ( slides[idx].elementTitle ) {
				slides[idx].elementTitle.css({
					'left': "-100%",
					'display': 'block',
					'opacity': 0
				});
			}
			
			this._hide(this.loader);
			
			if ( slides[idx].elementTitle ) {
				this._show(slides[idx].element);
				
				window.setTimeout(function() {
					slides[idx].elementTitle.animate({left:0, opacity:1}, {queue:false, duration:2000, complete:function(){
						$(this).css({
							opacity:""
						});
						self._setAutoRun();
						self = null;
					}});
				},100);
				
			} else {
				this._show(slides[idx].element, function(){
					self._setAutoRun();
				});
			}
		},
		
		//load a slide and set as current.
		loadSlide: function(idx, w) {
			var 
				opt = this.options,
				slides = opt.slides || [],
				wait = (typeof w !== "undefined") ? w : true;
			
			if ( idx < 0 ) {
				idx = 0;
			}
			
			if ( idx > slides.length - 1 ) {
				idx = slides.length - 1;
			}
			
			if ( typeof slides[idx] !== "undefined" ) {
				this._clearAutoRun();
				
				if ( idx !== this.curSlide ) {
					this.curSlide = idx;
					this.curIsText = (typeof slides[idx].isText !== "undefined" ) ? slides[idx].isText : false;
					
					if ( typeof slides[idx].element !== "undefined" ) {
						this.showSlide(idx);
						
					} else {
						this._loadSlide(idx, wait)
					}
				}
			}
		},
		
		
		//make a slide visible, ONLY if the slide already loaded AND is current slide
		showSlide: function(idx) {
			var 
				opt = this.options,
				slides = opt.slides || [];
			
			if ( idx < 0 ) {
				idx = 0;
			}
			
			if ( idx > slides.length - 1 ) {
				idx = slides.length - 1;
			}
			
			if ( typeof slides[idx] !== "undefined" ) {
				
				if ( idx !== this.curVisibleSlide && idx == this.curSlide ) {
					//check if the slide is loaded
					if ( typeof slides[idx].element !== "undefined" ) {
						if ( typeof slides[this.curVisibleSlide] !== "undefined" && typeof slides[this.curVisibleSlide].element !== "undefined" ) {
							//this._hide( slides[this.curVisibleSlide].element );
							this._hideSlide( this.curVisibleSlide );
						}
						
						this._showSlide(idx);
						
					} else {
						//if the slide is not loaded
						this.loadSlide(idx);
					}
				}
			}
		},
		
		_loadSlide: function(idx, w) {
			var self = this,
				slide = this.options.slides[idx] || null;
			
			if ( slide ) {
				if ( !w ) {
					this._show(this.loader);
				}
				
				new preloadImage(slide.src, function(obj, status) {
					if (status == "loaded") {
						
						self._slideLoaded.call(self, obj, idx);
						self = null;
					}
				});
			}
		},
		
		_slideLoaded: function(obj, idx) {
			var opt = this.options,
				slides = opt.slides,
				slide_html = '', caption,
				slide_alt = '', alt;
				
			if ( typeof slides[idx] !== "undefined" && typeof slides[idx].element === "undefined" ) {
				slide_html = '<img src="' + obj.src + '" alt="' + slides[idx].alt + '" width="' + obj.width + '" height="' + obj.height + '" />';
				
				if ( slides[idx].link ) {
					slide_html = '<a href="' + slides[idx].link + '" title="">' + slide_html + '</a>'
				}
				
				slide_html = slide_template.replace('#SLIDE#', slide_html);
				
				caption = slides[idx].caption || "";
				if ( caption ) {
					caption = caption.replace(/&lt;(\/)?(b|i|em|strong|u)&gt;/g, function(match, m1, m2){
						
						return "<" + (m1 || "") + (m2 || "") + ">";
					});
				}
				
				slides[idx].element = $(slide_html).appendTo(this.slidesHolder).hide();
				if ( caption ) {
					slides[idx].elementTitle = $('<div class="slideshow-slide"><div class="slide-title">'+ caption +'</div></div>').appendTo(this.slidesHolder).hide();
				} else {
					slides[idx].elementTitle = null;
				}
				
				this.showSlide(idx);
			}
		},
		
		_setAutoRun: function() {
			var self = this;
			
			this._clearAutoRun();
			
			if ( this.options.options.autoRun && this.options.options.autoRun_timeout > 0 ) {
				this.autoTimeout = window.setTimeout(function(){
					self.next.call(self);
					self = null;
				}, this.options.options.autoRun_timeout);
			}
		},
		
		_clearAutoRun: function() {
			if ( this.autoTimeout ) {
				window.clearTimeout(this.autoTimeout);
			}
			
			this.autoTimeout = null;
		},
		
		next: function() {
			var idx = this.curSlide + 1;
			
			if ( idx >= this.options.slides.length ) {
				idx = 0;
			}
			
			this.loadSlide(idx);
		},
		
		destroy: function() {
			$.Widget.prototype.destroy.apply( this, arguments );
		}
	});
	
	
	var slideShows = [],
		default_settings = {
			options: {},
			slides: []
		};
	
	function pxlcreative_slideshow(id, settings) {
		this.id = id;
		this.settings = $.extend(true, {}, default_settings, settings);
		this.initialized = false;
		
		slideShows.push(this);
	}
	
	window.pxlcreative_slideshow = pxlcreative_slideshow;
	
	$(function(){
		for ( var i = 0, n = slideShows.length; i < n; i++ ) {
			$("#" + slideShows[i].id).slideshow(slideShows[i].settings);
			slideShows[i].initialized = true;
		}
		
		$(window).unload(function(){
			var n;
			
			while ( slideShows.length ) {
				if ( slideShows[0].initialize ) {
					$("#" + slideShows[0].id).slideshow("destroy");
				}
				slideShows[0] = null;
				slideShows.splice(0,1);
			}
		});
	});
	
})(jQuery);
