var WebElements;

if(!WebElements) WebElements = {};

WebElements.SlidePanel = function(element, options) {
	
	this.element = element;			// container that holds the panel items
	this.slideitems = null;			// array of slide items to be displayed - only for internal use
									// use image_names for specifying images or leave it empty to use the slide items that are already in the html code
	
	this.image_names = null;		// array of image names
	this.image_path = "";			// path to the images
	
	this.slideprevious = null;		// element that forces slide panel to move backward
	this.slidenext = null;			// element that forces slide panel to move forward
	this.slidewindow = null;		// container that frames the visible window in which the moving slide items should be visible
	this.slide = null;				// container that sits in the slide window and holds the slide items
	this.slideitems_classname = "";	// class name of the slide items in the slide panel
	
	this.name = "slidepanel";		// name of the imageshifter instance for cookies etc.
	this.activeitem = 0;			// the currently active item (the item at the very left of the visible part of items)
	this.nextitem = 0;				// the next item
	this.previousitem = 0;			// the previous item
	this.displayeditems = 1;		// number of visible items in the slidewindow (for advanced sliding)
	this.slideby = 1;				// number of items to slide per animation (for advanced sliding)
	this.fade = false;				// if fade is true, an item, that is moved out of the slide window, is also faded out (for advanced sliding)
	this.direction = "forward";		// direction of array traversion ("forward" | "backward")
	this.layout = "horizontal";		// layout of the slide panel ("horizontal" or "vertical" sliding)
	this.mode = "slide";			// animation mode "slide" for simple slide and "slideby" for advanced sliding
	this.counter = 0;				// internal counter for advanced sliding
	
	this.load_message = "loading data...";
	
	this.use_cookies = true;
	this.cookie_path = "";
	
	this.slidewindow_height = null;	// height of the slide window
	this.slidewindow_width = null;	// width of the slide window
	this.slide_height = null;		// height of the slide
	this.slide_width = null;		// width of the slide
	this.slideitem_spacing = null;	// space between two slide items
	this.unit = "px";				// unit of the items' position to be animated 
	this.fps = 60;					// frames per second of the animation
	this.duration = 300;			// duration of an itmes' movement
	this.form = "linear";
	this.callback = null;
	this.animator = null;
	
	this.setOptions(options);
	
	this.init();
	
	this.attachBehaviors();
}

WebElements.SlidePanel.prototype.setOptions = function(options) {
	
	if(options)
	{
		for(var optionname in options)
		{
			this[optionname] = options[optionname];
		}
		
		return true;
	}
	
	return false;
}

WebElements.SlidePanel.prototype.loadImages = function() {
	
	var self = this;
	var loaded = 0;
	var images = this.slide.getElementsByTagName("img");
	
	// preload all the images in the slide
	// check, if images are loaded completely
	for(var i = 0; i < images.length; i++)
	{
		if(images[i].complete) loaded++;
	}
	
	// if images are not loaded completely
	// set timeout and try again
	if(loaded < images.length)
	{
		setTimeout(function(){ self.loadImages(); }, 0);
	}
	// if images are loaded go back to init() in section 1
	else
	{
		setTimeout(function(){ self.init(1); }, 0);
	}
}

WebElements.SlidePanel.prototype.init = function(section) {
	
	if(section == null) section = 0;
	
	// standard styles for correct panel display
	this.slidewindow.style.overflow = "hidden";
	this.slidewindow.style.position = "relative";
	
	// section 0 for initial setup and image loading
	if(section == 0)
	{
		// hide the slide while the content is still loading
		// use visibility, to preserve widths and spacings of the items for calculations
		this.slide.style.visibility = "hidden";
		
		// get the slide items and put them into the array
		if(this.image_names == null)
		{
			// no image names, so retrieve items via classname
			this.slideitems = WebElements.Utils.getElementsByClassName(this.slide, "*", this.slideitems_classname);
		}
		else
		{
			this.slideitems = new Array();
			
			// create image tags from the image names
			for(var i = 0; i < this.image_names.length; i++)
			{
				var imagetag = document.createElement("img");
				
				imagetag.src = this.image_path + this.image_names[i];
				
				this.slide.appendChild(imagetag);
				
				this.slideitems.push(imagetag);
			}
		}
		
		// create a load message and display it in the slide window
		var load_prompt = document.createElement("div");
	
		load_prompt.id = "load_message";
		load_prompt.className = "load_message";
		load_prompt.innerHTML = this.load_message;
		
		this.slidewindow.appendChild(load_prompt);
		
		// preload all the images
		var self = this;
		setTimeout(function(){ self.loadImages(); }, 0);
		
		return;
	}
	
	// get slidewindow's width after loading the content
	// otherwise IE will have the wrong height and width
	if(this.slidewindow_height == null)
	{
		this.slidewindow_height = this.slidewindow.offsetHeight;
	}
	if(this.slidewindow_width == null)
	{
		this.slidewindow_width = this.slidewindow.offsetWidth;
	}
	
	this.slidewindow.style.height = this.slidewindow_height + this.unit;
	this.slidewindow.style.width = this.slidewindow_width + this.unit;
	
	// calculate slide items' spacing
	if(this.slideitem_spacing == null)
	{
		if(this.layout == "horizontal")
		{
			if(this.slideitems.length > 1)
				this.slideitem_spacing = this.slideitems[1].offsetLeft - this.slideitems[0].offsetLeft - this.slideitems[0].offsetWidth;
			else
				this.slideitem_spacing = 0;
		}
		else if(this.layout == "vertical")
		{
			if(this.slideitems.length > 1)
				this.slideitem_spacing = this.slideitems[1].offsetTop - this.slideitems[0].offsetTop - this.slideitems[0].offsetHeight;
			else
				this.slideitem_spacing = 0;
		}
	}
	
	// calculate proper width or height of the slide
	if(this.layout == "horizontal")
	{
		if(this.slide_width == null)
		{
			this.slide_width = 0;
			
			for(var i = 0; i < this.slideitems.length; i++)
			{
				// sum up the width of the slide items and their spacing to the left element
				this.slide_width += (this.slideitems[i].offsetWidth + this.slideitem_spacing);
			}
			// clear spacing for last item
			if(this.slideitems.length > 0)
				this.slideitems[i-1].style.margin = "0" + this.unit + " 0" + this.unit + " 0" + this.unit + " 0" + this.unit;
			
			// subtract the last items' right spacing from the slide's width (the last -2 is not clear, but it breaks otherwise)
			this.slide_width -= (this.slideitem_spacing - 2);
		}
		
		this.slide.style.width = this.slide_width + this.unit;
		this.slide.style.position = "relative";
		this.slide.style.left = 0 + this.unit;
	}
	else if(this.layout == "vertical")
	{
		if(this.slide_height == null)
		{
			this.slide_height = 0;
			
			for(var i = 0; i < this.slideitems.length; i++)
			{
				// sum up the height of the slide items and their spacing to the top element
				this.slide_height += (this.slideitems[i].offsetHeight + this.slideitem_spacing);
			}
			// clear spacing for last item
			if(this.slideitems.length > 0)
				this.slideitems[i-1].style.margin = "0" + this.unit + " 0" + this.unit + " 0" + this.unit + " 0" + this.unit;
			
			// subtract the last items' bottom spacing from the slide's width
			this.slide_height -= (this.slideitem_spacing);
		}
		
		this.slide.style.height = this.slide_height + this.unit;
		this.slide.style.position = "relative";
		this.slide.style.top = 0 + this.unit;
	}
	
	
	// set default styles for slide items (advanced mode)
	if(this.mode == "slideby")
	{
		var currentwidth = 0;
		var trailingspace = 0;
		
		for(var i = 0; i < this.slideitems.length; i++)
		{
			// set an absolute width, otherwise filter:alpha() will not work in IE
			this.slideitems[i].style.width = this.slideitems[i].offsetWidth + this.unit;
			
			if(i < this.activeitem)
			{
				this.slideitems[i].style.opacity = "0";
				this.slideitems[i].style["-moz-opacity"] = "0";
				this.slideitems[i].style["-khtml-opacity"] = "0";
				this.slideitems[i].style.filter = "alpha(opacity=0)";
			}
			else
			if(i >= this.activeitem + this.displayeditems)
			{
				this.slideitems[i].style.opacity = "0";
				this.slideitems[i].style["-moz-opacity"] = "0";
				this.slideitems[i].style["-khtml-opacity"] = "0";
				this.slideitems[i].style.filter = "alpha(opacity=0)";
			}
			else
			{
				currentwidth += this.slideitems[i].offsetWidth + this.slideitem_spacing;
			}
		}
		
		currentwidth -= this.slideitem_spacing;
		
		if(this.slideitems.length > 0)
			trailingspace = this.slideitems[0].offsetLeft;
		
		// center the visible slide items in the slide window
		this.slide.style.left = Math.round((this.slidewindow_width / 2) - (currentwidth / 2) - trailingspace) + this.unit;
	}
	
	// images are loaded and the slide has the correct width, so remove the load message and display the slide
	this.slidewindow.removeChild(document.getElementById("load_message"));
	this.slide.style.visibility = "";
	
	// update the items' indexes
	this.updateItems("current");
	
	// check if slide position was saved in cookie and restore position
	// only for mode 'slide' at the moment
	if(this.use_cookies && this.mode == "slide")
	{
		var position = WebElements.Utils.getCookie(this.name + "_position");
		
		if(position != null) this.slideItem(this.direction, position);
	}
}

WebElements.SlidePanel.prototype.attachBehaviors = function() {
	
	var self = this;
	
	if(this.mode == "slide")
	{
		if(this.slideprevious)
		{
			WebElements.Utils.addEventListener(this.slideprevious, "mousedown", function(e) { self.previousItem(); });
			WebElements.Utils.addEventListener(this.slideprevious, "mouseup", function(e) { self.stopSlide(); });
		}
		if(this.slidenext)
		{
			WebElements.Utils.addEventListener(this.slidenext, "mousedown", function(e) { self.nextItem(); });
			WebElements.Utils.addEventListener(this.slidenext, "mouseup", function(e) { self.stopSlide(); });
		}
	}
	
	if(this.mode == "slideby")
	{
		if(this.slideprevious)
		{
			WebElements.Utils.addEventListener(this.slideprevious, "click", function(e) { self.previousItem(); });
		}
		if(this.slidenext)
		{
			WebElements.Utils.addEventListener(this.slidenext, "click", function(e) { self.nextItem(); });
		}
	}
}

WebElements.SlidePanel.prototype.updateItems = function(mode) {
	
	switch(mode)
	{
		case "current":
					// do nothing - only let nextitem and previousitem be calculated
					break;
					
		case "next":
					this.activeitem++;
					
					if(this.activeitem >= (this.slideitems.length - this.displayeditems))
					{
						this.activeitem = this.slideitems.length - this.displayeditems;
					}
					break;
					
		case "previous":
					this.activeitem--;
					
					if(this.activeitem < 0)
					{
						this.activeitem = 0;
					}
					break;
	}
	
	this.nextitem = this.activeitem + 1;
	this.previousitem = this.activeitem - 1;
	
	if(this.nextitem >= (this.slideitems.length - this.displayeditems)) this.nextitem = this.slideitems.length - this.displayeditems;
	if(this.previousitem < 0) this.previousitem = 0;
	
	/*
	this.debugOutput("activeitem: " + this.activeitem);
	this.debugOutput("nextitem: " + this.nextitem);
	this.debugOutput("previousitem: " + this.previousitem);
	*/
}

WebElements.SlidePanel.prototype.slideItem = function(direction, position) {
	
	if(!this.animator || !this.animator.running)
	{
		var self = this;
		
		// create animator object with initial animation chain (empty),
		// defined frames per second and a callback function
		this.animator = new WebElements.Animator(new WebElements.AnimationChain(), this.fps, function() { if(self.use_cookies) self.savePosition(); });
		
		// calculate slide position and limits
		// round the current position, otherwise animation will not work
		if(this.layout == "horizontal")
		{
			var current = Math.round(this.slide.style.left.replace("px", ""));
			var max = (position != null) ? position : (-(this.slide.offsetWidth - this.slidewindow_width));
			var min = 0;
		}
		else if(this.layout == "vertical")
		{
			var current = Math.round(this.slide.style.top.replace("px", ""));
			var max = (position != null) ? position : (-(this.slide.offsetHeight - this.slidewindow_height));
			var min = 0;
		}
		
		// calculate the relative slide position to adjust the time for the animation
		var progress = current / max;
		
		// create animations to be executed
		// create groups for animation objects and put them into the animation chain
		if(direction == "forward" && current > max)
		{
			if(this.layout == "horizontal")
			{
				var animation_1 = new WebElements.AnimationObject(this.slide, "left", current, max, this.unit, this.duration * (1 - progress), null, this.form);
			}
			else if(this.layout == "vertical")
			{
				var animation_1 = new WebElements.AnimationObject(this.slide, "top", current, max, this.unit, this.duration * (1 - progress), null, this.form);
			}
			
			this.animator.animation_chain.addAnimationGroup(new WebElements.AnimationGroup(animation_1));
			
			this.animator.start();
		}
		if(direction == "backward" && current < min)
		{
			if(this.layout == "horizontal")
			{
				var animation_1 = new WebElements.AnimationObject(this.slide, "left", current, min, this.unit, this.duration * progress, null, this.form);
			}
			else if(this.layout == "vertical")
			{
				var animation_1 = new WebElements.AnimationObject(this.slide, "top", current, min, this.unit, this.duration * progress, null, this.form);
			}
			
			this.animator.animation_chain.addAnimationGroup(new WebElements.AnimationGroup(animation_1));
			
			this.animator.start();
		}
		
		return true;
	}
	
	return false;
}

WebElements.SlidePanel.prototype.shiftItem = function(direction) {
	
	if(!this.animator || !this.animator.running)
	{
		var self = this;
		
		// create animator object with initial animation chain (empty),
		// defined frames per second and a callback function
		this.animator = new WebElements.Animator(new WebElements.AnimationChain(), this.fps, function() { self.updateItems((direction == "forward") ? "next" : "previous"); });
		
		// prepare looping
		this.counter++;
		
		if(this.counter < this.slideby)
		{
			this.animator.addCallback(function() { self.shiftItem(direction); });
		}
		else
		{
			this.counter = 0;
		}
		
		// calculate slide position and limits - round the current position, otherwise animation will not work
		var current = Math.round(this.slide.style.left.replace("px", ""));
		
		// create animations to be executed
		// create groups for animation objects and put them into the animation chain
		if(direction == "forward" && this.activeitem != this.nextitem)
		{
			var leftitem = this.slideitems[this.activeitem];
			var rightitem = this.slideitems[this.activeitem + this.displayeditems];
			
			var to = Math.round((current - leftitem.offsetWidth - this.slideitem_spacing) - ((rightitem.offsetWidth - leftitem.offsetWidth) / 2));
			
			var animation_1 = new WebElements.AnimationObject(this.slide, "left", current, to, this.unit, this.duration, null, this.form);
			var animation_2 = new WebElements.AnimationObject(leftitem, "opacity", 1, 0, "", this.duration, null, this.form);
			var animation_3 = new WebElements.AnimationObject(rightitem, "opacity", 0, 1, "", this.duration, null, this.form);
			
			this.animator.animation_chain.addAnimationGroup(new WebElements.AnimationGroup(new Array(animation_1, animation_2, animation_3)));
		}
		if(direction == "backward" && this.activeitem != this.previousitem)
		{
			var leftitem = this.slideitems[this.previousitem];
			var rightitem = this.slideitems[this.activeitem + this.displayeditems - 1];
			
			var to = Math.round((current + rightitem.offsetWidth + this.slideitem_spacing) + ((leftitem.offsetWidth - rightitem.offsetWidth) / 2));
			
			var animation_1 = new WebElements.AnimationObject(this.slide, "left", current, to, this.unit, this.duration, null, this.form);
			var animation_2 = new WebElements.AnimationObject(leftitem, "opacity", 0, 1, "", this.duration, null, this.form);
			var animation_3 = new WebElements.AnimationObject(rightitem, "opacity", 1, 0, "", this.duration, null, this.form);
			
			this.animator.animation_chain.addAnimationGroup(new WebElements.AnimationGroup(new Array(animation_1, animation_2, animation_3)));
		}
		
		this.animator.start();
		
		return true;
	}
	
	return false;
}

WebElements.SlidePanel.prototype.nextItem = function() {
	
	if(!this.animator || !this.animator.running)
	{
		switch(this.mode)
		{
			case "slide":
							this.slideItem("forward");
							break;
			
			case "slideby":
							this.shiftItem("forward");
							break;
		}
	}
	
	return false;
}

WebElements.SlidePanel.prototype.previousItem = function() {
	
	if(!this.animator || !this.animator.running)
	{
		switch(this.mode)
		{
			case "slide":
							this.slideItem("backward");
							break;
			
			case "slideby":
							this.shiftItem("backward");
							break;
		}
	}
	
	return false;
}

WebElements.SlidePanel.prototype.stopSlide = function() {
	
	if(this.animator && this.animator.running)
	{
		this.animator.stop();
		
		return true;
	}
	
	return false;
}

WebElements.SlidePanel.prototype.savePosition = function() {
	
	var position;
	
	if(this.layout == "vertical") position = Math.round(this.slide.style.top.replace("px", ""));
	if(this.layout == "horizontal") position = Math.round(this.slide.style.left.replace("px", ""));
	
	WebElements.Utils.setCookie(this.name + "_position", position, null, this.cookie_path);
}



WebElements.SlidePanel.prototype.debugOutput = function(message) {
	
	var debug_node = document.getElementById("slidepanel_debug");
	
	if(debug_node == null)
	{
		debug_node = document.createElement("div");
		
		debug_node.id = "slidepanel_debug";
		debug_node.style.padding = "10px";
		debug_node.style.width = "200px";
		debug_node.style.styleFloat = "none";
		debug_node.style.clear = "both";
		debug_node.style.position = "relative";
		debug_node.style.top = "10px";
		debug_node.style.fontSize = "10px";
		debug_node.style.color = "#666";
		debug_node.style.border = "1px solid #ccc";
		debug_node.style.backgroundColor = "#fff";
		debug_node.style.opacity = "0.9";
		debug_node.innerHTML = message;
	
		this.element.appendChild(debug_node);
	}
	else
	{
		debug_node.innerHTML += ("<br />" + message);
	}
}