/**************
 Object: Slider
 **************/
function Slider(elem, itemWidth, itemMaxWidth, itemMargin, altersList)
{
///PROPERTIES
	var self = this;
	this.elem = elem;
	var	elemBound = elem.firstChild;
	this.items = [];

	var boundX = 0;
	var boundWidth = 0;
	var boundDiffer = 0;
	var boundPadding = 0;


	this.slidingSpeedEase = 0.4;
	this.slidingSpeedFrom = 0.5;
	this.slidingSpeedTo = 0.01;

	var intSlide = undefined;
	var intSlidePos = false;
	var lastFrontNum = undefined;

///METHODS
	/**
	 * Slides the items to a specified position
	 */
	this.slide = function(pos) {
		intSlide = clearInterval(intSlide);
		intSlide = setInterval( function(){
			var differ = Math.abs(intSlidePos - pos);
			if (differ < self.slidingSpeedTo) {
				intSlidePos = pos;
				intSlide = clearInterval(intSlide);
			} else {
				// set new speed and change slide position
				var speed = differ * self.slidingSpeedEase;
				if (speed < self.slidingSpeedTo) speed = self.slidingSpeedTo;
				if (speed > self.slidingSpeedFrom) speed = self.slidingSpeedFrom;
				if (intSlidePos > pos) intSlidePos -= speed; else intSlidePos += speed;
			}
			// display slider position
			elemBound.style.left = -Math.round( boundDiffer*intSlidePos + boundPadding*(intSlidePos-0.5) ) + 'px';
		}, 35);
	}

	/**
	 * Mouse move handler
	 */
	this.onMouseMove = function(event) {
		event = $e.event(event);
		// calculate offset
		var pos = (event.x - boundX) / boundWidth;
		if (pos < 0) pos=0;if (pos > 1) pos=1;
		// calculate number of the item for specified offset
		var num = Math.floor(pos * itemsNum);
		if (num !== lastFrontNum) {
			lastFrontNum = num;
			// front new item, back all other
			for (var i=0; i < self.items.length; i++) {
				if (i === num) self.items[i].front(); else self.items[i].back();
			}
			// slide to new item
			self.slide(num / (self.items.length - 1));
		}
		return false;
	}

	/**
	 * Mouse out handler
	 */
	this.onMouseOut = function(e) {
		e = $e.event(e);
		if ($e.elemIsOf(e.target, self.elem) && !$e.elemIsOf(e.related, self.elem)) {
			lastFrontNum = undefined;
			for (var i=0; i < self.items.length; i++) self.items[i].back();
			self.slide(0.5);
		}
	}

	/**
	 * Resizes the slider
	 */
	this.resize = function() {
		// redefine sizes
		var sizes = $e.elemSizes(this.elem);
		//elTest1.value = sizes.left;
		//elTest2.value = sizes.width;
		boundX = sizes.left;
		boundWidth = sizes.width;
		boundDiffer = itemsTotalWidth - boundWidth;
		boundPadding = itemMaxWidth - itemWidth;
		// resize every item
		for (var i=0; i<this.items.length; i++) {
			this.items[i].init();
		}
		this.slide(0.5);
	}

///CONSTRUCTOR
	// retrieve items
	var elemsItems = $e.elemsByClass('item', this.elem);
	var itemsNum = elemsItems.length;
	var itemsTotalWidth = (itemWidth + itemMargin) * itemsNum - itemMargin;
	for (var i=0; i<itemsNum; i++) {
		this.items.push( new SliderItem(elemsItems[i], altersList, i, itemWidth, itemMargin) );
	}
	this.resize();
	// set events handlers
	this.elem.onmousemove = this.onMouseMove;
	this.elem.onmouseout = this.onMouseOut;

};


/*******************
 Object: Slider Item
 *******************/
function SliderItem(elem, altersList, num, width, margin)
{
///PROPERTIES
	this.elem = elem;
	this.num = num;

	var alters = [];
	var currents = [];
	var intFront = undefined;
	var intBack = undefined;

///METHODS
	/**
	 * Initializes the item
	 */
	this.init = function() {
		// define item position, if needed
		if (width !== undefined) {
			this.elem.style.left = (num * (width + margin)) + 'px';
		}
		// set items to back
		for (var i=0; i<alters.length; i++) {
			var alter = alters[i];
			alter.elem.style[alter.prop] = '' + alter.back + alter.type;
			currents[i] = alter.back;
		}
		// reset motion
		intFront = clearInterval(intFront);
		intBack = clearInterval(intBack);
	}

	/**
	 * Fronts the item
	 */
	this.front = function() {
		intBack = clearInterval(intBack);
		if (intFront === undefined) {
			intFront = setInterval(function(){
				var alters_num = alters.length;
				// recalculate
				for (var i=0; i<alters_num; i++) {
					var alter = alters[i];
					var done = 0;
					currents[i] += alter.step;
					if (
						(alter.step > 0 && currents[i] >= alter.front) ||
						(alter.step < 0 && currents[i] <= alter.front)
					) {
						currents[i] = alter.front;
						done++;
					}
				}
				// repaint
				for (i=0; i<alters_num; i++) {
					alter = alters[i];
					alter.elem.style[alter.prop] = '' + currents[i] + alter.type;
				}
				// done?
				if (done == alters_num) intFront = clearInterval(intFront);
			}, 35);
		}
	}

	/**
	 * Backs the item
	 */
	this.back = function() {
		intFront = clearInterval(intFront);
		if (intBack === undefined) {
			intBack = setInterval(function(){
				var alters_num = alters.length;
				// recalculate
				for (var i=0; i<alters_num; i++) {
					var alter = alters[i];
					var done = 0;
					currents[i] -= alter.step;
					if (
						(alter.step > 0 && currents[i] <= alter.back) ||
						(alter.step < 0 && currents[i] >= alter.back)
					) {
						currents[i] = alter.back;
						done++;
					}
				}
				// repaint
				for (i=0; i<alters_num; i++) {
					alter = alters[i];
					alter.elem.style[alter.prop] = '' + currents[i] + alter.type;
				}
				// done?
				if (done === alters_num) intBack = clearInterval(intBack);
			}, 35);
		}
	}

///CONSTRUCTOR
	// retrieve elements for changes
	for (var i=0; i<altersList.length; i++) {
		var alter = altersList[i];
		// alter must be changing
		if (alter.back == alter.front) continue;
		// retireve elements
		var alterElem = this.elem;
		if (alter.subclass) {
			alterElem = $e.elemsByClass(alter.subclass, this.elem);
			//alert(alterElem);
			//alterElem = (alterElem instanceof Array)? alterElem[0] : undefined;
			alterElem = alterElem[0];
			if (alterElem === undefined) continue;
		}
		// store alter
		alters.push({
			elem: alterElem,
			subclass: alter.subclass,
			prop: alter.prop,
			type: alter.type,
			back: alter.back,
			front: alter.front,
			step: (alter.back < alter.front)? Math.abs(alter.step) : -Math.abs(alter.step)
		});
		// store alter current
		currents.push(alter.back);
	}

	this.init();

}


