﻿var slider = function(containerId, options) {
	var that = {};
	var options = options || {};
	// Any of these options can be set by passing them in an object. 
	var fadeType = options.fadeType || 'fadeOpaque';
	var order = options.order || false; // can be 'random'
	var pauseSeconds = options.pauseSeconds !== undefined ? options.pauseSeconds : 5; // pause on content slides
	var emptySeconds = options.emptySeconds || 0; // pause on 'empty' slides
	var transSeconds = options.transSeconds || 1; // time of transition 
	var FPS = options.FPS || 24; // set lower for better performance
	var nonSlideClass = options.nonSlideClass || 'nonslide';
	var emptyClass = options.emptyClass || 'empty';
	var hoverPause = options.hoverPause !== undefined ? options.hoverPause : true;
	// Public methods below.
	that.autoRegister = function(containerId) {
		var links = document.getElementById(containerId).getElementsByTagName('a');
		var i = 0, j = 0;
		while (i < links.length) {
			while (stack.cards[j] && stack.cards[j].empty) {
				j++;
			}
			if (links[i].className.indexOf('forward') != -1) {
				links[i].onclick = this.controlNext;
			} else if (links[i].className.indexOf('back') != -1) {
				links[i].onclick = this.controlPrev;
			} else if (stack.cards[j]) {
				(function(control, card) { // not sure this little scope trick is necessary.
					var activate = function() {
						tools.addClassName('active', control);
					}
					var deactivate = function() {
						tools.removeClassName('active', control);
					}
					stack.register(card.id, activate, deactivate);
					control.onclick = function() {
						that.select(card.id);
						return false;
					}
				})(links[i], stack.cards[j]);
				j++;
			}
			i++;
		}
		stack.current().activate();
	}
	that.play = function() {
		paused = false;
		window.clearTimeout(timer);
		var time = stack.current().empty ? emptyMS : pauseMS;
		if (pauseMS > 0) {
		  timer = window.setTimeout(that.next, time);
		}
	}
	that.pause = function() {
		paused = true;
		// there's a bug with revert that causes a "blank slide" to appear. So far I haven't been able to isolate what triggers the bug.
		// if (!controlled && transition.progress > 0) {transition.revert();}
		transition.queueClear();
		window.clearTimeout(timer);
	}
	that.controlNext = function() {
		if (transition.progress == 0 || transition.progress == 100) {
			runControlledNext();
		} else {
			transition.queueAdd(runControlledNext);
		}
		return false;
	}
	var runControlledNext = function() {
		transition.speedup = true;
		controlled = true;
		that.next();
	}
	that.next = function() {
		window.clearTimeout(timer);
		transition.clearCallback();
		transition.slide = stack.current();
		stack.forward();
		if (controlled) {
			while (stack.current().empty) {
				stack.forward();
			}
		}
		if (!paused) {transition.callback = that.play;}
		transition.begin();
	}
	that.controlPrev = function() {
		if (transition.progress == 0 || transition.progress == 100) {
			runControlledPrev();
		} else {
			transition.queueAdd(runControlledPrev);
		}
		return false;
	}
	runControlledPrev = function() { // 
		transition.speedup = true;
		controlled = true;
		that.prev();
	}
	that.prev = function() {
		window.clearTimeout(timer);
		transition.clearCallback();
		transition.slide = stack.current();
		stack.backward();
		if (controlled) {
			while (stack.current().empty) {
				stack.backward();
			}
		}
		if (!paused) {transition.callback = that.play;}
		transition.begin();
	}
	that.select = function(slide) {
		var selectWithSlide = function() {
			runSelect(slide);
		}
		if (transition.progress == 0 || transition.progress == 100) {
			selectWithSlide();
		} else {
			transition.queueAdd(selectWithSlide); 
		}
	}
	var runSelect = function(slide) { // this should be private.
		if (stack.current().id == slide) {
			if (!paused) {that.play();}
		} else {
			window.clearTimeout(timer);
			transition.clearCallback();
			controlled = true;
			transition.slide = stack.current();
			stack.bringToFront(null, slide);
			if (!paused) {transition.callback = that.play;}
			transition.speedup = true;
			transition.begin();
		}
	}
	that.register = function(id, activate, deactivate) {
		stack.register(id, activate, deactivate);
	}
	// Transition methods below. Delete the ones you're not using.
	var transitions = {
		fadeOpaque : {
			once : function() {
			},
			start : function() {
				transition.slide.element.style.zIndex = 1;
				setVisible(stack.current());
			},
			run : function() {
				if (transition.progress == 0) {
					// not sure what was going to go here
				}
				var opacity = transition.progress / 100;
				web.setOpacity(transition.slide.element, 100 - transition.progress);
			},
			finish : function() {
			}
		},
		fadeTransparent : function() {
		},
		pull : {
			once : function() {
				container.style.overflow = 'hidden';
				// container.style.whiteSpace = 'nowrap'; problematic and hopefully unnecessary.
			},
			start : function() {
				if (stack.current().id == 0 || stack.current().id < transition.slide.id) {
					transition.options.rightToLeft = false;
				} else {
					transition.options.rightToLeft = true;
				}
				transition.slide.element.style.position = 'absolute';
				transition.slide.element.style.left = 0;
				transition.slide.element.style.display = 'block';
				stack.current().element.style.position = 'absolute';
				stack.current().element.style.left = transition.options.rightToLeft ? transition.slide.element.offsetWidth+'px' : '-'+stack.current().element.offsetWidth+'px';
				stack.current().element.style.display = 'block';
			},
			run : function() {
				var shift = parseInt(((20*Math.sqrt(transition.progress)-transition.progress)/100)*transition.slide.element.offsetWidth);
				transition.slide.element.style.left = transition.options.rightToLeft ? '-'+shift+'px' : shift+'px';
				stack.current().element.style.left = transition.options.rightToLeft ? (transition.slide.element.offsetWidth - shift)+'px' : '-'+(stack.current().element.offsetWidth - shift)+'px';
			},
			finish : function() {
			}
		},
		pullVertical : {
			once : function() {
				container.style.overflow = 'hidden';
				container.style.whiteSpace = 'nowrap';
			},
			start : function() {
				if (stack.current().id == 0 || stack.current().id < transition.slide.id) {
					transition.options.pullup = false;
				} else {
					transition.options.pullup = true;
				}
				transition.slide.element.style.position = 'absolute';
				transition.slide.element.style.top = 0;
				transition.slide.element.style.display = 'block';
				stack.current().element.style.position = 'absolute';
				stack.current().element.style.top = transition.options.pullup ? transition.slide.element.offsetHeight+'px' : '-'+stack.current().element.offsetHeight+'px';
				stack.current().element.style.display = 'block';
			},
			run : function() {
				var shift = parseInt(((20*Math.sqrt(transition.progress)-transition.progress)/100)*transition.slide.element.offsetHeight);
				transition.slide.element.style.top = transition.options.pullup ? '-'+shift+'px' : shift+'px';
				stack.current().element.style.top = transition.options.pullup ? (transition.slide.element.offsetHeight - shift)+'px' : '-'+(stack.current().element.offsetHeight - shift)+'px';
			},
			finish : function() {
			}
		},
		wipe : function() {
		}
	}
	var pauseMS = pauseSeconds * 1000;
	var emptyMS = emptySeconds * 1000;
	var timer;
	var container = document.getElementById(containerId);
	var controls = [];
	var transition = (function() {
		var trans = {};
		trans.timer = null;
		trans.reverse = false;
		trans.speedup = false;
		trans.options = {};
		trans.callback = function() {};
		trans.queue = [];
		trans.progress = 0;
		trans.methods = transitions[fadeType];
		trans.clearCallback = function() {
			trans.callback = function() {};
		}
		trans.queueAdd = function(method) {
			trans.queue.push(method);
		}
		trans.queueNext = function() {
			var next = trans.queue.shift();
			if (next && typeof next == 'function') {
				next();
			} else if (!paused) {
				that.play();
			}
		}
		trans.queueClear = function() {
			trans.queue = [];
		}
		trans.revert = function() {
			transition.reverse = true;
		}
		trans.begin = function() {
			transition.methods.start();
			transition.reverse = false;
			transition.progress = 0;
			transition.timer = window.setInterval(transition.animate, 1000/FPS);
			// that.animate();
		}
		trans.animate = function() {
			if ((!transition.reverse && transition.progress < 100) || (transition.reverse && transition.progress > 0)) {
				var multiplier = (transition.reverse || transition.speedup) ? 2 : 1;
				if (!transition.reverse) {
					transition.progress += parseInt(100/((FPS*transSeconds)/multiplier))
				} else {
					transition.progress -= parseInt(100/((FPS*transSeconds)/multiplier))
				}
				transition.progress = transition.progress < 0 ? 0 : transition.progress;
				transition.progress = transition.progress > 100 ? 100 : transition.progress;
				transition.methods.run();
				// transition.timer = window.setTimeout(transition.animate, 1000/FPS);
			} else {
				transition.complete();
				window.clearInterval(transition.timer);
				// window.clearTimeout(transition.timer);
			}
		}
		trans.complete = function() {
			if (transition.reverse) {
				setVisible(transition.slide);
				setHidden(stack.cards[0]);
				stack.bringToFront(null, transition.slide.id);
				transition.reverse = false;
				stack.activate(transition.slide.id);
			} else {
				transition.methods.finish();
				stack.activate(stack.cards[0].id);
				setVisible(stack.cards[0]);
				setHidden(transition.slide);
				trans.queueNext();
			}
			controlled = false;
			transition.speedup = false;
		}
		trans.slide = null; // the slide that is being faded into the front one
		return trans;
	})();
	var stack = {};
	stack.cards = [];
	stack.current = function() {
		return stack.cards[0];
	}
	stack.nextOrdinal = function() {
		if (order == 'random') {
			return Math.floor(Math.random() * (stack.cards.length - 1)) + 1;
		} else {
			return 1;
		}
	}
	stack.prevOrdinal = function() {
		return stack.cards.length - 1;
	}
	stack.bringToFront = function(ordinal, id) {
		var cards = [];
		if (typeof(id) == 'number') {
			for (var i = 0; i < stack.cards.length; i++) {
				if (stack.cards[i].id == id) { ordinal = i; }
			}
		}
		for (var i = 0, j = ordinal; i < stack.cards.length; i++) {
			cards.push(stack.cards[j]);
			j++;
			if (j == stack.cards.length) { j = 0; }
		}
		stack.cards = cards;
	}
	stack.forward = function() {
		stack.bringToFront(stack.nextOrdinal());
	}
	stack.backward = function() {
		stack.bringToFront(stack.prevOrdinal());
	}
	stack.activate = function(id) {
		for (var i = 0; i < stack.cards.length; i++) {
			if (stack.cards[i].id == id) {
				stack.cards[i].activate();
			} else {
				stack.cards[i].deactivate();
			}
		}
	}
	stack.register = function(id, activate, deactivate) {
		for (var i = 0; i < stack.cards.length; i++) {
			if (stack.cards[i].id == id) {
				stack.cards[i].activate = activate; 
				stack.cards[i].deactivate = deactivate; 
			}
		}
	}
	var refresh = function() {
		setVisible(stack.current());
		for (var i = 1; i < stack.cards.length; i++) {
			setHidden(stack.cards[i]);
		}
	}
	var setVisible = function(card) {
		web.setOpacity(card.element, 100);
		card.element.style.top = 0;
		card.element.style.left = 0;
		card.element.style.display = 'block';
	}
	var setHidden = function(card) {
		card.element.style.display = 'none';
		card.element.style.zIndex = 0;
	}
	// below is code to set up slideshow.
	var paused = false;
	var controlled = false;
	if (container) {
		for (var i = 0, j = 0; i < container.childNodes.length; i++) {
			if (container.childNodes[i].nodeType == 1 && container.childNodes[i].className.indexOf(nonSlideClass) == -1) {
				stack.cards.push((function() {
					var card = {};
					card.id = j;
					card.element = container.childNodes[i];
					card.activate = function() {};
					card.deactivate = function() {};
					card.empty = container.childNodes[i].className.indexOf(emptyClass) != -1;
					return card;
				})());
				j++;
			}
		}
		if (order == 'shuffle' || order == 'random') {
			for(var j, x, i = stack.length; i; j = parseInt(Math.random() * i), x = stack[--i], stack[i] = stack[j], stack[j] = x); // Fisher-Yates. Yow.
		}
		for (var i = 0; i < stack.cards.length; i++) {
			var card = stack.cards[i].element;
			card.style.position = 'absolute';
			switch (i) {
				case 0:
					card.style.display = 'block';
					break;
				default:
					card.style.display = 'none';
			}
		}
		if (stack.cards.length > 1) {
			container.style.position = 'relative';
			transition.methods.once();
			stack.current().activate();
			if (hoverPause) {
				container.onmouseover = function(e) {
					if (tools.checkMouseMove(e, container)) {
						that.pause();
					}
				}
				container.onmouseout = function(e) {
					if (paused && tools.checkMouseMove(e, container)) {
						that.play();
					}
				}
			}
			that.play();
		}
	}
	return that;
}



