/* 
 * Copyright (c) Sequence Collective Ltd (www.sequence.co.uk)
 * By Sequence for Cardiff University (www.cardiff.ac.uk)
 * 
 * v.1.2 Oct 2008
 * 	addition of transition timing variable and placement of controls within slide
 * v.1.1 Sept 2008
 * 	addition of pause toggle and controls
 *
 */
if (typeof seq == "undefined" || !seq) {
    var seq = {};
}
(function() {
	// timer manages the animations but shouldn't be accessable from the outside
	var timer =  {
		running : false,
		timer : null,
		funcs : [],
		sleepd : [],
		last : 0,
		add : function(func) {
			if(!this.running)
				this.run();
			var index = this.funcs.push(func) - 1;
			return index;
		},
		remove : function(index) {
			this.sleep(index);
			if(this.sleepd[index])
				this.sleepd.splice(index, 1);
			this.funcs.splice(index, 1);
		},
		run : function() {
			var that = this;
			this.timer = window.setInterval(
				function() {
					that.update();
				}
			, 14);
			this.last = new Date().getTime();
			this.running = true;
		},
		update : function() {
			var now = new Date().getTime();
			var time= (now - this.last) / 100;
			this.last = now;
			for(var i = 0; i < this.funcs.length; i++) {
				if(!this.sleepd[i]) {
					var func = this.funcs[i];
					func(time)
				}
			}
		},
		sleep : function(index) {
			this.sleepd[index] = true;
			if(this.sleepd.length == this.funcs.length) {
				window.clearTimeout(this.timer);
				this.timer = null;
				this.running = false;
			}
		},
		awaken : function(index) {
			if(this.sleepd[index]) {
				this.run();
			}
			this.sleepd[index] = false;
		}
	}
	// generates pagination specifically for seq.cujs (aka, hacky a bit)
	var pagination = function(holder, slides, callback) {
		this.div = document.createElement("div");
		this.div.className = "pagination";
		with (this.div.style) {
			position = "absolute";
			bottom = 0;
			right = 0;
		}
		this.links = [];
		for(var i = 0, l = slides.length; i < l; i++) {
			var a = document.createElement("a");
			a.href = "#slide" + (i + 1);
			a.slideIndex = i;
			var title = slides[i].title;
			if(title) {
				a.innerHTML = "<span>" + title + "</span>";
				a.title = title;
				slides[i].title = "";
			}
			else {
				a.innerHTML = "<span>" + (i + 1) + "</span>";
			}
			var that = this;
			a.onclick = function() {
				that.active(this.slideIndex);
				if(typeof callback == "function") 
					callback(this.slideIndex)
				return false;
			}
			this.links.push(a);
			this.div.appendChild(a);
			this.current = 0;
		}
		holder.appendChild(this.div);
	}
	pagination.prototype = {
		active : function(index) {
			this.links[this.current].className = "";
			this.links[index].className = "active";
			this.current = index;
		}
	}
	seq.cujs = function(holder, subject, options) {
		if(typeof holder == "string")
			var holder = document.getElementById(holder);
		this.holder = holder;
		// These are set just incase they are not set in the stylesheet (slides are absolute)
		this.holder.style.position = "relative";
		this.holder.style.overflow = "hidden";
// Fader defaults
		this.options = {
			loop		: true,
			autostart	: false,
			paginate	: true,
			delay		: 6000,
			target	: 0,
// Button Defaults			
			enablebutton : false,
			pausetext : "Pause",
			pauseclass : "pause",
			playtext : "Play",
			playclass : "play"
		}
		// Copy new options over defualts
		if(typeof options == "object") {
			for (var i in options) {
				this.options[i] = options[i];
			}
		}
		else {
			// TODO: one-liner
		}
		this.target = this.options.target;
		this.slides = [];

		// Setup default slide styles (each slide streches to 100% of holder)
		for(i = 0, l = this.holder.childNodes.length; i < l; i++) {
			var el = this.holder.childNodes[i];
			if (el.className && el.className.match(/slide/i)) {
				with (el.style) {
					position = "absolute";
					top = 0;
					left = 0;
					width = "100%";
					height = "100%";
				}
				this.slides.push(el);
			}
		}
		// Create a new instance of subject (specific animation)
		if(typeof subject == "string")
			subject = seq.cujs.fx[subject];
		if( typeof subject == "function")
			subject = new subject();

		this.subject = subject;
		this.subject.init(this.holder, this.slides, this.target);
// Checks for links enabled status
		if (this.options.enablelinks) {
			// Loops through all the links and attach functions to them (next, prev, goto)
			var links = this.holder.getElementsByTagName("a");
			for(var i = 0, l = links.length; i < l; i++) {
				var rel = links[i].rel;
				if(rel) {
					var that = this;
					var link = links[i];
					if (rel.match(/^next$/i)) {
						link.onclick = function() {
							that.next();
							return false;
						}
					}
					else if (rel.match(/^prev$/i)) {
						link.onclick = function() {
							that.prev();
							return false;
						}
					}
					else {
						var to = rel.match(/^to\[([0-9]+)\]$/i)
						if(to[1]) {
							link.onclick = function() {
								that.to(to[1]);
								return false;
							}
						}
					}
				}
			}
		}
		if(this.options.paginate) {
			var that = this;
			this.pagination = new pagination(this.holder, this.slides,
				function(index) {
					that.to(index);
				}
			);
			if(this.options.enablebutton) {
				var button = document.createElement("a");
				button.href = "#";
				if(this.autotimer != null) {
					button.innerHTML = this.options.playtext;
					button.className = this.options.playclass;
				}
				else {
					button.innerHTML = this.options.pausetext;
					button.className = this.options.pauseclass;
				}
				this.bind(button, "toggle", {
					pausetext : this.options.pausetext,
					pauseclass : this.options.pauseclass,
					playtext : this.options.playtext,
					playclass : this.options.playclass
				});
				this.pagination.div.insertBefore(button, this.pagination.div.firstChild);
			}
			this.pagination.active(this.target);
		}
		if(this.options.autostart)
			this.start();
	}
	seq.cujs.prototype = {
		start : function() {
			if(this.running == null) {
				var that = this;
				this.running = timer.add(
					function(time) {
						that.update(time);
					}
				);
				this.to(this.options.target);
			}
			else {
				this.to(this.options.target);
			}
		},
		stop : function() {
			if(this.running != null) {
				timer.remove(this.running);
				this.running = null;
				if(this.autotimer) {
					window.clearTimeout(this.autotimer);
					this.autotimer = null;
				}
			}
		},
		pause : function() {
			if(this.autotimer != null) {
				window.clearTimeout(this.autotimer);
				this.autotimer = null;
			}
		},
		update : function(time) {
			if(!this.subject.update(this.holder, this.slides, this.target, time)) {
				timer.sleep(this.running);
				this.asleep = true;
			}
		},
		to : function(index) {
			if(this.running == null)
				this.start();
			if(this.asleep) {
				timer.awaken(this.running);
				this.asleep = false;
			}
			var length = this.slides.length;
			if(this.options.loop) {
				// Loop around, go back to the first slide
				if(index < 0) {
					index = length-1;
				}
				else if(index > length-1) {
					index = 0;
				}
			} else {
				// Stops when it reaches endpoints
				if(index < 0) {
					index = 0;
				}
				else if(index > length-1) {
					index = length-1
				}
			}
			if(this.pagination) 
				this.pagination.active(index);
			if(this.options.autostart) {
				if(this.autotimer) {
					window.clearTimeout(this.autotimer);
					this.autotimer = null;
				}
				var that = this;
				this.autotimer = window.setTimeout(
					function() {
						that.next();
					}
				, this.options.delay);
			}
			this.target = index;
		},
		next : function(count) {
			if(!count) count = 1;
			this.to(this.target + count);
		},
		prev : function(count) {
			if(!count) count = 1;
			this.to(this.target - count);
		},
		bind : function(id, action, args) {
			if(typeof id == "string")
				id = document.getElementById(id);
			if(typeof action == "string")
				action = seq.cujs.actions[action];
			var context = this;
			id.onclick = function(e) {
				action(this, context, args);
				return false;
			}
		}
	}
	/* 
		## Different Slideshow Animations ## 
	*/
	seq.cujs.fx = {}
	seq.cujs.fx.fader = function(tick) {
		if(tick == null)
			tick = 0.25;
			
		this.tick = tick;
		this.o = [];
	}
	seq.cujs.fx.fader.prototype = {
		_opacity: function(el, o) {
			el.style.opacity = o;
			el.style.MozOpacity = o;
			el.style.filter = 'alpha(opacity=' + o*100 + ')';
		},
		init : function(holder, slides, target) {
			for(var i = 0; i < slides.length; i++) {
				var el = slides[i];
				if(i == target) {
					this._opacity(el, 1);
					this.o[i] = 1;
					el.style.display = "block";
				} else {
					this._opacity(el, 0);
					this.o[i] = 0;
					el.style.display = "none";
				}
			}
		},
		update : function(holder, slides, target, time) {
			var done = true;
			for(var i = 0; i < slides.length; i++) {
				var el = slides[i];
				var o = this.o[i];
				if(i == target) {
					if(o < 1) {
						o += this.tick * time;
						this._opacity(el, o);
						this.o[i] = o;
						el.style.display = "block";
						done = false;
					} 
					else {
						this._opacity(el, 1);
						this.o[i] = 1;
						el.style.display = "block";
					}
				}
				else {
					if(o > 0) {
						o -= this.tick * time;
						this._opacity(el, o);
						this.o[i] = o;
						el.style.display = "block";
						done = false;
					}
					else {
						this._opacity(el, 0);
						this.o[i] = 0;
						el.style.display = "none";
					}
				}			
			}
			
			if(done)
				return false;
		
			return true;
		}
		
	}
	seq.cujs.fx.slider = function(accel, damp) {
		if(!accel) accel = 6;
		if(!damp) damp = 1.1;
		this.accel = accel;
		this.damp = damp;
		this.canvas = null;
		this.speed = 0;
		this.width = 0;
		this.pos = 0;
	}
	seq.cujs.fx.slider.prototype = {
		_setpos : function(p) {
			this.canvas.style.left = -Math.round(p) + "px";
			this.pos = p;
		},
		init: function(holder, slides, target) {
			this.canvas = document.createElement("div")
			this.width = holder.clientWidth;
			with(this.canvas.style) {
				position = "absolute";
				top = 0;
				left = this.width * slides.length + "px";
				width = "100%";
				height = "100%";
			}
			for(var i = 0; i < slides.length; i++) {
				var el = slides[i];
				el.style.left = this.width * i + "px";
				holder.removeChild(el);
				this.canvas.appendChild(el);
			}
			holder.appendChild(this.canvas);
			this._setpos(this.width * target);
		},
		update : function(holder, slides, target, time) {
			var tpos = target * this.width;
			var dist = tpos - this.pos;
			this.speed = (dist / this.accel + this.speed) / this.damp;
			this._setpos(this.pos + (this.speed * time));
			if(Math.abs(this.speed) <= 0.5 && this.pos == tpos) {
				return false;
			}
			return true;
		}
	}
	
	/* ## Bindable Actions ## */
	seq.cujs.actions = {
		prev : function(el, context) {
			context.next();
		},
		next : function(el, context) {
			context.next();
		},
		to : function(el, context, index) {
			if(typeof index != "number")
				index = 0;
			context.to(index);
		},
		play : function(el, context) {
			context.options.autostart = true;
			context.start();
		},
		pause : function(el, context) {
			context.options.autostart = false;
			context.pause();
		},
		toggle : function(el, context, options) {
			var o = {
				pausetext : "Pause",
				pauseclass : "pause",
				playtext : "Play",
				playclass : "play"
			}
			if(typeof options == "object") {
				for (var i in options) {
					o[i] = options[i];
				}
			}
			if(context.autotimer != null) {
				el.innerHTML = o.playtext;
				el.className = o.playclass;
				context.options.autostart = false;
				context.pause();
			}
			else {
				el.innerHTML = o.pausetext;
				el.className = o.pauseclass;
				context.options.autostart = true;
				context.start();
			}
		}
	}
})();
