function MegaTimer4000() {
	var dublicate = this;
	var timers = {};
 
	this.timeout = function(name, fn, ms, args, context) {
		var myTimer = new Timer();
		myTimer.setFn(fn).setArgs(args).setContext(context);
		var systemTimer = window.setTimeout(myTimer.exec, ms);
		timers[name] = new TimerDataItem(systemTimer, myTimer, "timeout");
	};
 
	this.interval = function(name, fn, ms, args, context) {
		var myTimer = new Timer();
		myTimer.setFn(fn).setArgs(args).setContext(context);
		var systemTimer = window.setInterval(myTimer.exec, ms);
		timers[name] = new TimerDataItem(systemTimer, myTimer, "interval");
	};
 
	this.get = function(name) {
		return timers[name] || timers;
	}; 
 
	function Timer() {
		var dublicate = this;
		var fn = null;
		var args = [];
		var context = window;
 
		this.setFn = function(val) {
			if (typeof(val)=="function") fn = val;
			return dublicate;
		};
		this.setArgs = function(val) {
			if (val.constructor==Array) args = val;
			return dublicate;
		};
		this.setContext = function(val) {
			if (val) context = val;
			return dublicate;
		};
 
		this.exec = function() {
			try {
				fn.apply(context, args);
			} catch(er) { }
		};
	}
 
	function TimerDataItem(systemTimer, myTimer, type) {
		this.stop = function() {
			try {
				if (type=="interval") {
					window.clearInterval(systemTimer);
				} else {
					window.clearTimeout(systemTimer);
				}
				systemTimer = null;
				delete systemTimer;
			} catch(er) { }
			return myTimer;
		};
	}
}

ELS_PER_PAGE = 4;

var Scroller = {

	prev : null,
	next : null,
	scrollItems : null,
	scrolledTo : 0,
	curPos: 0,

	Scroller : function(){
		if(this.prev == null && this.next == null && this.scrollItems == null){
		this.prev = $('#toLeft');
		this.next = $('#toRight');
		this.scrollItems = $('#scrollable li.outer');
		this.prepareScrollables();
		this.prev.click(this.moveLeft);
		this.prev[0].thisObj = this;
		this.next.click(this.moveRight);
		this.next[0].thisObj = this;
		this.setDisabled();
		}else{
			throw "Scroller is already initialized"
		}
		// new:
		this.visibleWidth = $('#scroller')[0].offsetWidth;
		for (var i=0, offset=0, curOffset, l=this.scrollItems.length, item, setWidth; i<l; i++) {
			item = this.scrollItems[i];
			item.style.left = offset +"px";
			item.style.top = "0px";
			curOffset = item.getElementsByTagName("img")[0].width;
			$(".scrText", item)[0].style.width = curOffset -26 +"px";
			offset += curOffset;
			if (!setWidth && offset>this.visibleWidth) {
				setWidth = $('#scrollable').css({width: parseInt(item.style.left) -13 +"px"});
			}
		}
		for (var i=0, l=this.scrollItems.length; i<l; i++) {
			this.scrollItems[i].style.position = "absolute";
		}
		this.timers = new MegaTimer4000();
	},
	
	replaceLast: function(self, animationData) {
		// зацикливаем карусель: берем наиболее дальнего человечка и перемещаем (replaceChild) на другугю сторону :)
		var needReplace = !(animationData.toRight ? self.scrollItems[self.curPos +4] : self.scrollItems[self.curPos -1]);
		var scrollItems = [];
		for (var i=0, l=self.scrollItems.length; i<l; i++) {
			scrollItems.push(self.scrollItems[i]);
		}
		if (needReplace) {
			if (!animationData.toRight) {
				var replaceMe = scrollItems.pop();
				var replaceTo = document.createElement("div");
				var parent = replaceMe.parentNode;
				parent.insertBefore(replaceTo, parent.firstChild);
				parent.replaceChild(replaceMe, replaceTo);
				scrollItems.unshift(replaceMe);
				replaceMe.style.left = $('img', replaceMe).attr('width') *-1 +"px";
				self.curPos++;
				self.scrollItems = $('#scrollable li.outer');
			} else {
				// alert(1);
				var replaceMe = scrollItems.shift();
				var replaceTo = document.createElement("div");
				var parent = replaceMe.parentNode;
				parent.appendChild(replaceTo);
				parent.replaceChild(replaceMe, replaceTo);
				scrollItems.push(replaceMe);
				replaceMe = $(replaceMe);
				replaceMe[0].style.left = parseInt(replaceMe.prev()[0].style.left) + $('img', replaceMe).attr('width') +"px";
				self.curPos--;
				self.scrollItems = $('#scrollable li.outer');
			}
		}
	},
		
	moveLeft : function(){
		var thisObj = this.thisObj;
		thisObj.scrolledTo != 0 ? thisObj.scrolledTo-- : null;
		thisObj.setDisabled();
		thisObj.moveHelper(true);
	},

	moveRight : function(){
		var thisObj = this.thisObj;
		thisObj.scrolledTo < (thisObj.scrollItems.length - ELS_PER_PAGE) ? thisObj.scrolledTo++ : '';
		thisObj.setDisabled();
		thisObj.moveHelper(false);
	},
	
	/* Helpers */
	
	setDisabled : function(){
		
		/*
		if(this.scrolledTo == 0){
			this.prev.attr('style','opacity:0.5;filter: alpha(opacity = 50);');
			this.next.attr('style','opacity:1;filter: alpha(opacity = 100);');
		}else if(this.scrolledTo == this.scrollItems.length - ELS_PER_PAGE){
			this.prev.attr('style','opacity:1;filter: alpha(opacity = 100);');
			this.next.attr('style','opacity:0.5;filter: alpha(opacity = 50);');
		}else{
			this.prev.attr('style','opacity:1;filter: alpha(opacity = 100);');
			this.next.attr('style','opacity:1;filter: alpha(opacity = 100);');
		}
		*/
	},
	
	prepareScrollables : function(){
		/*for(var i = ELS_PER_PAGE; i < this.scrollItems.length;i++){
			this.scrollItems[i].style.right = '-'+$('img',this.scrollItems[i]).attr('width')+'px';
		}*/
	},

	moveHelper : function(isRight){
		if (this.animateNow) return;
		this.animateNow = toRight ? "toRight" : "toLeft";
		
		// настройки анимации
		var toRight = !isRight; 
		var iterations = 100; // макс. кол-во шагов анимации
		var animLengthMs = 400; // продолжительность анимации
		var ms = 1; // идеальная периочность исполнения, ms
		var nextAll = toRight ? "nextAll" : "prevAll";
		var prevAll = toRight ? "prevAll" : "nextAll";
		
		// получение основных нод
		var els = this.scrollItems;
		var leftEl = toRight ? els[this.curPos] : els[this.curPos+3]; // элемент, который будет скрыт
		var allNextEls = $(leftEl)[nextAll]("li"); // все элементы, следующие за leftEl
		var invisibleEl; // элемент, который будет показан
		var nextEls=[]; // элементы, находящиеся между leftEl и invisibleEl
		for (var i=0, l=Math.min(3, allNextEls.length); i<l; i++) {
			nextEls.push(allNextEls[i]);
		}
		invisibleEl = $(nextEls[nextEls.length-1])[nextAll]("li")[0];
		
		// проверка возможности проскроллить
		if (!invisibleEl) {
			this.animateNow = false;
			this.replaceLast(this, {toRight: toRight});
			this.moveHelper(isRight);
			return;
		}
		
		var anotherInvivisible = []; // элементы, которые надо передвинуть один раз, после анимации
		for (var i=0, arr=$(invisibleEl)[nextAll](), l=arr.length; i<l; i++) {
			// по напрвалению движения...
			anotherInvivisible.push(arr[i]);
		}
		for (var i=0, arr=$(leftEl)[prevAll](), l=arr.length; i<l; i++) {
			// ...и против
			anotherInvivisible.push(arr[i]);
		}
		// получаем данные для анимации
		var animationData = animateInit(
			iterations, 
			$('img', toRight ? leftEl : invisibleEl).attr('width') * (toRight ? 1 : -1), 
			leftEl, nextEls, invisibleEl, anotherInvivisible 
		);
		animationData.toRight = toRight;
		var startAt = (new Date()).getTime();
		var endAt = startAt +animLengthMs;
		this.timers.interval(
			"move", // имя таймера this.timers
			iterate, // исполяемая функция
			ms, // идеальная периочность исполнения, ms
			[
				animationData, 
				this.timers.get, "move", startAt, (endAt-startAt)/iterations, 
				afterAnim, this
			] // аргументы, передаваемые в функцию
		);
		
		function afterAnim(self, animationData) {
			// фукнция, исполняемая при завершении анимации
			self.animateNow = false;
			self.curPos += animationData.toRight ? 1 : -1; // изменяем номер текущего первого видимого элемента
			// и устанавливаем новую ширину ul, чтобы были видны только только нужные элементы - и только целиком
			var cur = self.scrollItems[self.curPos];
			var allNext = $(cur).nextAll("li");
			var arr = [cur]; // список видимых элементов
			for (var i=0, l=Math.min(3, allNext.length); i<l; i++) {
				arr.push(allNext[i]);
			}
			var w = 0;
			for (var i=0, l=arr.length; i<l; i++) {
				// подсчитываем их суммарную ширину
				w += $('img', arr[i]).attr('width');
			}
			self.visibleWidth = w;
			$('#scrollable').css({width: (w-13)+"px"}); // устанавливаем, 13px - padding-left
		}
		
		function iterate(obj, getTimer, timerName, startAt, stepCost, afterAnim, afterAnimArg) {
			var ms = Math.floor(((new Date()).getTime() - startAt)/stepCost); // номер шага
			if (ms > obj.curStep) {
				// если номер шага больше текущего
				obj.curStep = ms;
				if (obj.curStep > obj.steps) {
					// если номер текущего шага больше кол-ва шагов, останавливаем анимацию
					getTimer(timerName).stop(); // останавливаем периодический вызов iterate
					afterAnim(afterAnimArg, obj); // исполняем afterAnim
					obj.curStep = obj.steps; // устанавливаем номер текущего шага, как последний
					for (var i=0, arr=obj.afterAnim, l=arr.length; i<l; i++) {
						// устанавливаем не учавтсвовавшим в анимации объектам новые позиции
						arr[i][0].style.left = arr[i][1];
					}
				}
				for (var i=0, arr=obj.states, l=arr.length, c=obj.curStep; i<l; i++) {
					// устанавливаем зн-я, соотв. текущему шагу для анимируемых элементов
					arr[i][0].style.left = arr[i][c];
				}
			}
		}
		
		function animateInit(iterations, delta, leftEl, nextVisible, firstInvisible, anotherInvivisible) {
			// просчет промежуточных координат
			var iters = iterations, qIters = Math.round(iters/3), qdIters = iters-qIters; // кол-во шагов: целиком, короткое, остаток
			var ret = {
				states: [], // нода[0] и зн-я для кжадого шага[1+]
				afterAnim: [], // зн-я для элементов, не участвующих в анимации, выставляются после анимации
				curStep: -1, // текущий шаг
				steps: iters // кол-во шагов
			};
			for (var i=0, l=nextVisible.length, curLeft; i<l; i++) {
				// линейная анимация для видимых элементов
				curLeft = parseInt(nextVisible[i].style.left);
				ret.states[i] = [nextVisible[i]].concat( getStates(curLeft, curLeft-delta, iters, "px") );
			}
			// анимация для элемента, который будет скрыт:
			curLeft = parseInt(leftEl.style.left);
			ret.states.push( [leftEl].concat(
					// быстро скрываем
					getStates(curLeft, curLeft-delta, qIters, "px") 
				).concat(
					// и оставляем
					getStates(curLeft-delta, curLeft-delta, qdIters, "px") 
				)
			);
			// анимация для элемента, который будет показан:
			curLeft = parseInt(firstInvisible.style.left);
			ret.states.push( 
				[firstInvisible].concat(
					// не меняем позицию, пока leftEl не скроется
					getStates(curLeft, curLeft, qIters, "px") 
				).concat(
					// и выставляем зн-я для оставшихся шагов
					getStates(curLeft, curLeft-delta, qdIters, "px")
				)
			);
			// проставляем afterAnim
			for (var i=0, l=anotherInvivisible.length; i<l; i++) {
				ret.afterAnim.push( [anotherInvivisible[i], (parseInt( anotherInvivisible[i].style.left ) - delta) +"px"] );
			}
			return ret;
			
			function getStates(from, to, iterations, postfix) {
				// интерполяция зн-й от from до to с заданным (iterations) кол-вом шагов
				var ret = [];//new Array(iterations);
				var postfix = postfix || 0;
				var d = (to - from) / iterations;
				for (var i=0, l=iterations-1, v, lastV=from; i<l; i++) {
					lastV = lastV + d;
					v = Math.round(lastV);
					ret.push(v + postfix);
				}
				ret.push(to + postfix);
				return ret;
			}
		}
	},
	
	findFirstZeroMargin : function(elsToScroll, isReversed){
		var position = -1;
		if(isReversed){
				var firstZeroMargin = elsToScroll.length;
				for(var i = (elsToScroll.length - 1); i >= 0; i--){
					if(elsToScroll[i].style.right == "" || elsToScroll[i].style.right == "0px"){
						firstZeroMargin = i;
						break;
					}
				}
				var amount = firstZeroMargin - ELS_PER_PAGE;
				for(var i = firstZeroMargin; i > amount; i--){
					if(elsToScroll[i].style.marginRight == "" || elsToScroll[i].style.marginRight == "0px"){
						if(firstZeroMargin >= ELS_PER_PAGE){
							position = i;
							}
						break;
					}	
				}	
			}else{
				var amount = elsToScroll.length - ELS_PER_PAGE;
				for(var i = 0; i < amount; i++){
					if(elsToScroll[i].style.marginLeft == "" || elsToScroll[i].style.marginLeft == "0px"){
						position = i;
						break;
					}
				}
			}
		return position;
	}
}



$(
	function(){
		Scroller.Scroller();
	}
);