//      идея : Баранов Андрей (info@xhtml.ru)
//	автор : AKS (aksus-69@yandex.ru)
//	скрипт тестировался в IE 6, Opera 7+, gecko(navigator.productSub >= 20040113)
//	спасибо xhtml.ru за сотрудничество

//сначала - несколько вспомогательных функций, которые в дальнейшем будут 
//применяться в главном конструкторе, облегчая и осуществляя доступ к DOM

function getId(id) {										//возвращает метод идентификации элемента									
	return document.getElementById(id);
}		

function getTags(obj, tag) {									//возвращает метод получения коллекции элементов с указанным тэгом								
	return obj.getElementsByTagName(tag);  
}

function changeStyle(obj, props) {								//получая в качестве аргументов элемент и атрибуты стиля, 																		//применяет необходимый стиль к данному элементу
	for(var i in props) {									//применяет необходимый стиль к данному элементу
		obj.style[i] = props[i];
	}
}		

function attachSSEvents(el, props) {								//используя разные методы (в зависимости от браузера), привязывает к 
	if(!el.attachEvent) 									//обработчику событий необходимые функции
		el.attachEvent = function(e, fn) {
		return this.addEventListener(e.substr(2), fn, false);
	}
	for(var i in props) {
		el.attachEvent(i, props[i]);
	}
}

function getPosY(obj, prop) {									//используется для определения позиции элемента на странице
	var p = obj.offsetParent, y = obj[prop];
        while(p) {
		y += p[prop];
		p = p.offsetParent;
	}	
        return y;
}

function preventEvent(e) {									//в данном случае ипользуется для того, чтобы
	var ev = e || window.event;								//избежать выделения текстовых фрагментов панели управления слайд-шоу									 
	if (ev.preventDefault) ev.preventDefault();						
	else ev.returnValue = false;								
	return false;
}

function createSSElement(parent, tag) {								//возвращает вновь созданный элемент, при необходимости задавая для него 
	var i, elem = document.createElement(tag);						//атрибуты стиля, обработчики событий и текстовое содержимое 							
	parent.appendChild(elem);
	if(arguments[2]) {								
		changeStyle(elem, arguments[2]);		
	}
	if(arguments[3]) {
		attachSSEvents(elem, arguments[3]);
	}
	if(arguments[4]) {
		elem.appendChild(document.createTextNode(arguments[4]));
	}
	return elem;
}

//далее - главный конструктор, содержащий все свойства и методы, 
//необходимые для создания и управления слайд-шоу					

function mainSSConstructor(prnt) {										
	var thzz = this;									
	var tempSlides = [];									//массив, в который будут помещены элементы div.slide из кода страницы												
	var next = 0;										//переменная для идентификации вновь открываемого слайда  
	function getNext(bl) { 									//возвращающает инкремент и декремент переменной
		if(bl) next++; 									//next в зависимости от переданного значения
        	else  next--; 
		return next;
	}
	var active = -1;									//переменная для идентификации открытого слайда								
	var timer = null;									//таймер закрытия суб-меню
	var px = "px";	
	var openStep = 14;									//шаг открывающегося слайда в пикселях (можно установить любое разумное значение)
	var closeStep = -22;									//шаг закрывающегося слайда в пикселях (можно установить любое разумное значение)

//далее - переменные, хранящие ссылки на вновь создаваемые элементы с помощью функции createSSElement()
//таким образом, создаются все необходимые для слайд-шоу компоненты 	
					
	var topBar = createSSElement(prnt, "div", {						//элемент, в котором будет находиться элемент "меню"
		"borderBottom" : "2px solid white", "padding" : "3px" });
	prnt.insertBefore(topBar, prnt.firstChild);						//размещается в самом верху
	var navMenu = createSSElement(topBar, "span", {						//элемент "меню"				
		"cursor" : "pointer", "backgroundColor" : "white",
		"padding" : "0px 3px", "color" : "black" }, { 
		"onmouseover" : function() { changeStyle(navMenu, { 
		"backgroundColor" : "#17507F", "color" : "white", 
		"border" : "1px solid white" }); 
		thzz.showSubMenu(); }, 
		"onmouseout" : function() { thzz.hideSubMenu(); }}, 
		"МЕНЮ");
	var subMenu = createSSElement(document.body, "div", {					//суб-меню, появляющееся при наступлении события				 
		"position" : "absolute", "top" : "0px", "display" : "block" }, {		//onmouseover в элементе "меню" 
		"onmouseover" : function() { changeStyle(navMenu, { 
		"backgroundColor" : "#17507F", "color" : "white", 
		"border" : "1px solid white" }); 
		thzz.showSubMenu(); }, 
		"onmouseout" : function() { thzz.hideSubMenu(); }});	
	var subHolder = createSSElement(subMenu, "div", {					//элемент, находящийся внутри суб-меню, содержащий 
		"backgroundColor" : "white", "padding" : "3px", 				//ссылки на все слайды
		"border" : "1px solid black" });
	var navBar = createSSElement(prnt, "div", { 						//элемент для навигации по слайдам, содержащий
		"position" : "absolute", "top" : "610px", "backgroundColor" : "#17507F",        //элементы "вперед", "назад" и счетчик страниц 
		"border" : "1px solid white", "padding" : "0px 4px" }, { 
		"onclick" : preventEvent, "onmousedown" : preventEvent, 
		"onselectstart" : preventEvent });			
	var goForvard = createSSElement(navBar, "span", {					//элемент "вперед"				
		"cursor" : "pointer", "padding" : "0px 3px" }, { 		
		"onmouseup" : function() { if(slEngine[next].moving == false) 
		thzz.switchSlide(true); }, 
		"onmouseover" : function() { changeStyle(goForvard, { 
		"backgroundColor" : "white", "color" : "black" }); }, 
		"onmouseout" : function() { changeStyle(goForvard, { 
		"backgroundColor" : "transparent", "color" : "white" }); }}, 
		"вперед");
	var goBack = createSSElement(navBar, "span", {						//элемент "назад" 					
		"cursor" : "pointer", "padding" : "0px 3px" }, { 
		"onmouseup" : function() { if(slEngine[next].moving == false) 
		thzz.switchSlide(false); }, 
		"onmouseover" : function() { changeStyle(goBack, { 
		"backgroundColor" : "white", "color" : "black" }); }, 
		"onmouseout" : function() { changeStyle(goBack, { 
		"backgroundColor" : "transparent", "color" : "white" }); }}, 
		"назад");
	var counter = createSSElement(navBar, "span", {						//счетчик страниц 
		"padding" : "0px 3px", "borderLeft" : "1px solid white", "marginLeft" : "3px" }, 
		false, " ");
	var contentBlock = getId("slides");							//элемент, в котором содержатся все слайды (div.slide)							
	var getSSItems = function() {								//ищет элементы с именем класса "slide", помещая их во временный массив 
		var tempArr = [], allDivs = getTags(contentBlock, "div"), 
		i, l = allDivs.length;
			for(i = 0; i < l; i++) {
      				if(allDivs[i].className == "slide") {
        			tempArr.push(allDivs[i]); }}
				return tempArr; }
	var getSSContent = (function() {							//все слайды (div.slide) помещает в массив, 					
		tempSlides = getSSItems();		
		var top = getPosY(tempSlides[0], "offsetTop"), 					//задает необходимые атрибуты стиля с помощью функции changeStyle()
		i = 0, l = tempSlides.length;						
		for(; i < l; i++) {
			changeStyle(tempSlides[i], { 
			"position" : "absolute", "top" : top, "width" : "525px", "height" : "450px",  
			"backgroundColor" : "#10487E", "overflow" : "hidden","marginLeft" : "5px", 
			"border" : "1px solid white", "padding" : "3px" }); }})();
	var lengthSS = tempSlides.length;							//хранит количество слайдов
	var counterText = counter.firstChild;
	var contentSLMenu = (function() {							//находит заголовки во всех слайдах,						
		var originH = [], newH = [], i = 0;						//составляя из них содержимое суб-меню,
		for(; i < lengthSS; i++) {							//определяет новый стиль элементов и 
			originH[i] = getTags(tempSlides[i], "h2")[0].firstChild.nodeValue;	//обработчики событий
			newH[i] = function(j) { 					
				return createSSElement(subHolder, "div", { 
			 	"cursor" : "pointer", "fontSize" : "11px" }, { 
				"onmouseover" : function() { changeStyle(newH[j], { 
				"color" : "#f60", "textDecoration" : "underline" }); },
				"onmouseout" : function() { changeStyle(newH[j], { 
				"color" : "black", "textDecoration" : "none" }); },
				"onclick" : function() { if(slEngine[next].moving == false) {
		                        active = next;
					next = j;
					changeStyle(navMenu, { "color" : "black", 
					"backgroundColor" : "white", "border" : "none" }); 
					changeStyle(newH[j], { "color" :"black", "textDecoration" : "none" });
					slSubmenu.placeTo(0, -slSubmenu.sHeight);
					if(active != j) { thzz.displaySlide(active, j);
					thzz.showNavBar(j); }
					return next;}}}, originH[j]); }(i)}})();

//далее - методы, необходимые для управления элементами слайд-шоу

	this.showSubMenu = function() {								//при наступлении события onmouseover в элементах "меню" 						
		if(timer) clearTimeout(timer);							//или суб-меню ощищает таймер закрытия суб-меню 
		slSubmenu.showIt();								//и вызывает метод для показа суб-меню
	} 
	this.hideSubMenu = function() {								//при наступлении события onmouseout в элементах "меню" 																
		timer = setTimeout(function() { changeStyle(navMenu, { 				//или суб-меню устанавливает таймер закрытия суб-меню  
			"color" : "black", "backgroundColor" : "white", "border" : "none" }); 	//и меняет стиль элемента "меню" 
			slSubmenu.hideIt(); }, 500);
	}  
	this.switchSlide = function(bl) {							//хранит ссылку на номер открытого слайда и получает ссылку на номер слайда, 	
		active = next;									//который нужно открыть, передавая их в вызываемые 					
		next = getNext(bl);								//методы для открытия следующего слайда и закрытия открытого 									
		this.displaySlide(active, next);						//слайда, а также вызывает метод для изменения данных в 						
		this.showNavBar(next);								//элементе navBar
		return next;
	}
	this.displaySlide = function(a, n) {							//в зависимости от переданных аргументов, вызывает соответсвующие						
		if(a >= 0) slEngine[a].slideTo(-slEngine[a].sWidth - 10, closeStep);		//методы для появления нужных слайдов
		slEngine[n].slideTo(0, openStep);		
	}
	this.showNavBar = function(n) {								//метод, управляющий содержимым элемента navBar при 
		counterText.nodeValue = 							//помощи номера открытого слайда, передаваемого в 
		"стр." + ((n < 9) ? "0" + (n + 1) : n + 1) + " из " + lengthSS;			//в качестве аргумента						
		if(n <= 0) goBack.style.visibility = "hidden";
		else goBack.style.visibility = "visible";
		if(n >= lengthSS - 1) goForvard.style.visibility = "hidden";
		else goForvard.style.visibility = "visible";
	}
	function getOwnProps(obj) {
		this.style = obj.style;
		this.sWidth = obj.offsetWidth;
		this.sHeight = obj.offsetHeight;
		this.sTop = obj.offsetTop;
		this.sLeft = obj.offsetLeft;
	}

//далее - конструктор, создающий свойства и методы для элемента суб-меню

	function slideSubMenu(parent, smenu) {							
	        var self = this;
		var step = 5;									//количество пикселей, на которые передвигается суб-меню 
		var isOpen = false;								//хранит информацию о том, закрыто или открыто суб-меню

//некоторые из свойств суб-меню будут свойствами объекта slideSubMenu()

		getOwnProps.call(this, smenu);
		this.pHeight = parent.offsetHeight;

//далее - методы, которые в совокупности будут реализовать плавное скольжение суб-меню вверх и вниз

		this.clipTo = function(t, r, b, l) {						//метод, отвечающий за "обрезание" элемента суб-меню
			this.style.clip = "rect(" + t + "px, " + r + "px," + b + "px," + l + "px)";
		}
		this.moveTo = function(x, y) {							//метод, перемещающий элемент в точку с заданными координатами
			this.x = Math.floor(x);
			this.y = Math.floor(y);
			this.style.left = this.x + px; 
			this.style.top = this.y + px;
		}
		this.slideTo = function() { 							//метод, отвечающий за плавное перемещение элемента в нужную точку
			if(isOpen == true) { 
				if(this.y + step <= this.pHeight) { 
					this.clipTo(this.pHeight - this.y - step, this.sWidth, this.sHeight, 0);
					this.moveTo(this.x, this.y + step);
					setTimeout(function() { self.slideTo(); }, 10);
				}
			}
			else if(this.y >= - this.sHeight) { 
				this.clipTo(this.pHeight - this.y + step, this.sWidth, this.sHeight, 0);
				this.moveTo(this.x, this.y - step);
				setTimeout(function() { self.slideTo(); }, 10);
			}
			else return null;
		}
		this.placeTo = function(x, y) {							//применяется для размещения элемента на странице "по умолчанию"
			isOpen = false;
			this.moveTo(x, y);
		}
		this.showIt = function() {							//вызывает метод для показа суб-меню
			if(isOpen == false) {
				isOpen = true;
				this.slideTo();
			}
		}
		this.hideIt = function() {							//вызывает метод для скрытия суб-меню
			if(isOpen == true) {
				isOpen = false;
				this.slideTo();
			}
		} 
		return this; 
	}

//далее - конструктор, создающий свойства и методы, необходимые для плавной смены слайдов

	function slidesEngine(slide, num) {
		this.moving = false;								//определяет, в каком состоянии слайды (закончено движение или нет)
	
//некоторые из свойств слайдов будут свойствами объекта slidesEngine()

		getOwnProps.call(this, slide);		
		
//далее - методы, которые в совокупности будут реализовать плавную смену слайдов 

		if(num == 0) { 	
			slidesEngine.prototype.moveTo = slSubmenu.moveTo;			//метод, наследующий свойства аналогичного метода предыдущего конструктора		
	slidesEngine.prototype.slideTo = function(x, step) {			//метод, позволяющий слайдам плавно передвигаться
				var self = this, t;
				if(step > 0) {
					if(this.x < x) { 
						this.moving = true;
						this.moveTo(this.x + step, this.y);
						t = setTimeout(function() { self.slideTo(x, step); }, 10);
					}
					if(this.x + step > x) {
						clearTimeout(t);
						this.moveTo(x, this.y);
						this.moving = false;
					}
				}
				if(step < 0) {
					if(this.x > x) { 
						this.moving = true;
						this.moveTo(this.x + step, this.y);
						t = setTimeout(function() { self.slideTo(x, step); }, 10);
					}
					if(this.x + step < x) {
						clearTimeout(t);
						this.moveTo(x, this.y);
						this.moving = false;
					}
				}					
			}
		}  
		return this;
	}


//далее - создание экземпляра созданного ранее конструктора slideSubMenu()

	var slSubmenu = new slideSubMenu(topBar, subMenu);
	slSubmenu.placeTo(0, -slSubmenu.sHeight);						//размещает суб-меню (вне видимости) по указанным координатам				
	
//далее - создание экземпляра созданного ранее конструктора slidesEngine()
//создается массив переменных, каждая из которых будет отвечать за движение
//отдельно взятого слайда

	var slEngine = [];
	for(var i = 0; i < lengthSS; i++) {
		slEngine[i] = new slidesEngine(tempSlides[i], i);
		slEngine[i].moveTo(-slEngine[i].sWidth - 10, slEngine[i].sTop);
	}
	this.displaySlide(active, next);							//показывает первый из слайдов							
	this.showNavBar(next);									//устанавливает начальные параметры элемента navBar								
	return this;
}

//далее - анонимная функция для привязки к событию onload

onload = function() {										//при событии onload данная функция									
	if(document.getElementById) { 								//проверяет браузер на предмет поддержки DOM (to hell with bad browsers!),
		var obj = getId("presentation");						//определяет блок div#presentation, в котором разместится слайд-шоу и
		var slideShow = new mainSSConstructor(obj);					//создает экземпляр главного конструктора слайд-шоу, выполняющий все необходимые операции
	}											//с элементами слайд-шоу
}
