// GLOBAL VARIABLES

var fps = 25;				// frames per second for animations
var dt = Math.round(1000/fps);
var dots = [];				// collection of margin dot objects
var wigglewidth = 20;		// sets amplitude of wiggling (in pixels)
var flying = false;			// flag for when fly-in or fly-out are in progress
var flytimer;
var pc = 100;				// used in flyindots and flyoutdots functions
var atease = true;			// set to false after 'attention' dot behaviour
var wigglearray = [];

// ADD METHODS TO JAVASCRIPT

document.getElementsByClassName = function(cl) {
	var retnode = [];
	var myclass = new RegExp('\\b'+cl+'\\b');
	var elem = this.getElementsByTagName('*');
	for (var i = 0; i < elem.length; i++) {
		var classes = elem[i].className;
		if (myclass.test(classes)) {
			retnode.push(elem[i]);
		}
	}
	return retnode;
};

// GENERAL FUNCTIONS

function mod(n, m) {	//mathmatical modulus function; returns 0 =< p < m such that p+km = n for some integer k; assumes m >0
	if (n < 0 ) {
		while (n < 0) {
			n += m;
		}
	} else {
		while (n >= m) {
			n -= m;
		}
	}
	return n;
}

function rawurlencode(str) {			// JS equivalent for PHP rawurlencode
	str = encodeURIComponent(str); 
	str = str.replace(/!/g, "%21");		// resolve differences between Javascript encodeURIComponent() and PHP rawurlencode()
	str = str.replace(/'/g, "%27");
	str = str.replace(/\(/g, "%28");
	str = str.replace(/\)/g, "%29");
	str = str.replace(/~/g, "%7E");
	str = str.replace(/%C2%A3/g, "%A3");	// Â£ -> £
	return str;
}

function getWindowHeight() {
	var height = 0;
	if( typeof( window.innerHeight ) == 'number' ) {
		//Non-IE
		height = window.innerHeight;
	} else if( document.documentElement && document.documentElement.clientHeight ) {
		//IE 6+ in 'standards compliant mode'
		height = document.documentElement.clientHeight;
	} else if( document.body && document.body.clientHeight ) {
		//IE 4 compatible
		height = document.body.clientHeight;
	}
	return height;
}


function getOpacity(el) {
	var compstyle, op;

	if (window.getComputedStyle) {								// standard
		compstyle = window.getComputedStyle(el, null);
	} else if (el.currentStyle) {								// IE
		compstyle = el.currentStyle;
	}
	
	if (compstyle.filters && compstyle.filters.alpha && compstyle.filters.alpha.opacity) {	// IE
		op = compstyle.filters.alpha.opacity*0.01;
	} else {					//everything else
		op = (compstyle.opacity || compstyle.MozOpacity || compstyle.KhtmlOpacity);
	}
	return Number(op);
}


function setOpacity(el, op) {
	el.style.filter = "alpha(opacity=" + op*100 + ")"; // for IE5 and earlier - screws up IE6 and later
	el.style.opacity = op;
	el.style.MozOpacity = op;
	el.style.KhtmlOpacity = op;
}


// BUILDTIME FUNCTIONS

function makedot(alignment, dotn) {
	var x = Math.round(100 * Math.random());
	var y = Math.round(100 * Math.random());
	var mapText = "<map id =\"dotmap" + dotn + "\" name=\"dotmap" + dotn + "\"><area shape=\"circle\" coords=\"20,20,10\" onclick=\"javascript: dotclicked(" + dotn + ");\" onmouseover=\"javascript: wiggle(" + dotn + ");\" alt=\"\" /></map>";
	var dotText = "<img src=\"images/red_dot_hover.gif\" id=\"dot" + dotn + "\"name=\"dot\" alt=\"\" usemap=\"#dotmap" + dotn + "\" style=\"position: absolute; top: " + y + "%; " + alignment + ": " + x + "%;\" />";
	document.writeln(mapText);
	document.writeln(dotText);
	dots[dotn] = document.getElementById("dot"+dotn);
	dots[dotn].alignment = alignment;
	dots[dotn].wiggling = false;
	dots[dotn].nowiggle = false;
}


function buildmargin(marg) {
	var intImgWidth = 40;        // width of margin image
	var intImgHeight = 40;       // height of margin image
	var intRepeatHeight = 100;

	var windowHeight = getWindowHeight();
	var outerHeight = document.getElementById("outer").offsetHeight;
	var innerHeight = document.getElementById("inner").offsetHeight;
	var headernavHeight = document.getElementById("header").offsetHeight + document.getElementById("nav").offsetHeight;

	var mainHeight;
	if (document.getElementById("main") === null) {
		mainHeight = 0;
	} else {
		mainHeight = Math.max(document.getElementById("main").offsetHeight, document.getElementById("sidebar").offsetHeight);
	}

	var p = Math.max(windowHeight, outerHeight);
	var q = Math.max(innerHeight + headernavHeight, mainHeight + headernavHeight);

	var intPageHeight = Math.max(p, q);
	var intMarginWidth = document.getElementById(marg).offsetWidth;
	var intBlockWidth = intMarginWidth - intImgWidth - wigglewidth;
	var intBlockHeight = intRepeatHeight - intImgHeight;

	var n = Math.ceil(intPageHeight/intRepeatHeight);
	var offset = "-100";		//prepare offset for fly-in 

	var alignment;
	var idoffset;
	if (marg == "leftmargin") {
		alignment = "left";
		idoffset = 0;
	} else if (marg == "rightmargin"){
		alignment = "right";
		idoffset = n;
	}				// set alignment and id number offset used below

	var divname = marg + "carrier";
	var i = 0;
	while (i<n) {
		var j = i + idoffset;	
		document.writeln("<div class=\"marginblock\" style=\"top: " + (i*intRepeatHeight) + "px; height: " + intRepeatHeight + "px;\">");
		document.writeln("<div name=\"" + divname + "\" id=\"" + divname + "\" style=\"position: absolute; " + alignment + ": " + offset + "%; width: " + intBlockWidth + "px; height: " + intBlockHeight + "px;\">");
		makedot(alignment, j);
		document.writeln("</div>");
		document.writeln("</div>");
		if (alignment == "left") {
			dots[j].setoffset = function(x) {
				this.parentNode.style.left = x;
			}
			dots[j].getoffset = function() {
				return this.parentNode.style.left;
			}
		} else {
			dots[j].setoffset = function(x) {
				this.parentNode.style.right = x;
			}
			dots[j].getoffset = function() {
				return this.parentNode.style.right;
			}			
		}
		i++;
	}	
}

// RUNTIME FUNCTIONS

// --  fades --

function fade(id, time, target) {
	var el = document.getElementById(id);

	function fadestep() {
		setOpacity(el, el.fadearray[el.step]);
		el.step++;
		if (el.step > el.steps ) {
			window.clearInterval(el.fadetimer);
		}
	}

	el.steps = Math.ceil(time/dt);
	el.fadearray = [];
	var initialvalue = getOpacity(el) 
	el.fadearray[0] = initialvalue;
	el.fadearray[el.steps] = target;
	var increment = el.steps*(target - el.fadearray[0]);
	for (var i = 1; i < el.steps; i++) {
		el.fadearray[i] = Math.round((initialvalue + i/increment)*100)/100;
	}
	el.step = 0;	
	if (el.fadetimer) {
		window.clearInterval(el.fadetimer);
	}
	el.fadetimer = window.setInterval(fadestep, dt);
}

// -- dot animations --

function flyindots() {
	flying=true;
	var n = dots.length/2;
	if (pc > 100) {
		pc = 100;
	}		// initial offset

	function flyinstep() {
		pc*=0.80;		// scaling factor
		var j;
		for (j=0; j<n; j++) {
			dots[j].setoffset(-pc + "%")
			dots[j + n].setoffset(-pc + "%")
		}

		if (pc < 0.5) {
			for (j=0; j<n; j++) {
				dots[j].setoffset("0px");
				dots[j + n].setoffset("0px");
			}
			flying = false;
			window.clearInterval(flytimer);
		}
	}

	window.clearInterval(flytimer);
	flytimer = window.setInterval(flyinstep, dt);
}


function flyoutdots(url) {
	flying=true;
	var n = dots.length/2;
	if (pc < 0.5) {
		pc = 0.5;
	}					// initial offset

	function flyoutstep() {
		pc*=2;					// scaling factor; higher value makes faster flyout
		for (var j=0; j<n; j++) {
			dots[j].setoffset(-pc + "%")
			dots[j+n].setoffset(-pc + "%")
		}
		if(pc > 110) {
			window.clearInterval(flytimer);
			flying = false;
			window.location.assign(url);
		}
	}

	for (var j=0; j<dots.length; j++) {
		dots[j].wiggling=false;		// stop wiggling if in progress
	}
	window.clearInterval(flytimer);
	flytimer = window.setInterval(flyoutstep, dt); 
}


function wiggle(dotn) {
	if (!dots[dotn].wiggling && !flying && !dots[dotn].nowiggle) {
		dots[dotn].t = 0;

		function wigglestep() {
			dots[dotn].setoffset(wigglearray[dots[dotn].t]);
			dots[dotn].t++;
			if (dots[dotn].t >= wigglearray.length || dots[dotn].wiggling === false) {
				window.clearInterval(dots[dotn].wiggletimer);	
				dots[dotn].wiggling = false;
			}
		}

		dots[dotn].wiggling = true;
		dots[dotn].wiggletimer = window.setInterval(wigglestep, dt);
	}
}

// RANDOM onclick FUNCTIONS FOR DOTS

function randomiseandflyin() {
	var j;
	var n = dots.length/2;
	var dotstyle;
	for (j = 0; j < n; j++) {
		dotstyle = dots[j].style;
		dotstyle.top = Math.round(100 * Math.random()) + "%";
		dotstyle.left = Math.round(100 * Math.random()) + "%";
	}
	for (j = n; j < dots.length; j++) {
		dotstyle = dots[j].style;
		dotstyle.top = Math.round(100 * Math.random()) + "%";
		dotstyle.right = Math.round(100 * Math.random()) + "%";
	}
	window.setTimeout(flyindots, 300);			// wait 300ms before fly in - gives more realistic effect
}


function dotsoutin() {					// CASE 1
	flyoutdots("javascript: randomiseandflyin()");
}


function mexicanwiggle(dotn) {				// CASE 2: mexican wave wiggling starts at dot n
	var j, k;
	if (Math.random()<0.5) { k = 1;} else { k = -1;}	// does wave go forward or backward?

	var lefttop = 0;
	var leftbottom = dots.length / 2 - 1;
	var righttop = dots.length / 2;
	var rightbottom = dots.length - 1;
	for (j = 0; j < dots.length; j++) {
		dots[dotn].mexwigt = -j;
		if (dotn == lefttop && k == -1) {dotn = righttop; k=1;}
		else if (dotn == leftbottom && k == 1) {dotn = rightbottom; k = -1;}
		else if (dotn == righttop && k == -1) {dotn = lefttop; k = 1;}
		else if (dotn == rightbottom && k == 1) {dotn = leftbottom; k = -1;}
		else {dotn += k;}
	}

	function mexicanwigglestep() {
		for (j = 0; j < dots.length; j++) {
		var m = dots[j].mexwigt;
		if (m == 0) {
				window.clearInterval(dots[j].wiggletimer)
				dots[j].wiggling = true;
				dots[j].setoffset(wigglearray[0]);
			} else if (m > 0 && m < wigglearray.length) {
				dots[j].setoffset(wigglearray[m]);
			} else if (m = wigglearray.length) {
				dots[j].wiggling = false;
			}
		dots[j].mexwigt++;
		}
		if (dots[dotn].mexwigt >= wigglearray.length + dots.length || flying == true) {
			window.clearInterval(document.mexwigtimer);
		}
	}
	window.clearInterval(document.mexwigtimer);
	document.mexwigtimer = window.setInterval(mexicanwigglestep, dt);
}


function letmeoutgo(dotn, vx, vy, k) {
	var el = dots[dotn].style;
	var x;
	var y;
	var elx;
	var ely;

	if (dots[dotn].alignment == "left") {
		elx = el.left;
		x = Number(elx.substring(0, elx.length - 1));
		x += vx;
		if (x < 0) {x = -x; vx = -vx;}
		if (x > 100) {x = 200 - x; vx = -vx;}	// bounce off sides of parent div
		el.left = Math.round(x * 100) / 100 + "%";
	} else {
		elx = el.right;
		x = Number(elx.substring(0, elx.length - 1));
		x += vx;
		if (x < 0) {x = -x; vx = -vx;}
		if (x > 100) {x = 200 - x; vx = -vx;}	// bounce off sides of parent div
		el.right = Math.round(x * 100) / 100 + "%";
	}
	ely = el.top;
	y = Number(ely.substring(0, ely.length - 1));

	y += vy;
	if (y < 0) {y = -y; vy = -vy;}
	if (y > 100) {y = 200 - y; vy = -vy;}		// bounce off sides of parent div
	el.top = Math.round(y * 100) / 100 + "%";

	vx *= k;
	vy *= k;

	if (Math.abs(vx) > 0.1 || Math.abs(vy) > 0.1) {
		window.setTimeout("letmeoutgo(" + dotn + ", " + vx + ", " + vy + ", " + k + ");", dt);
	} else {
		window.setTimeout("dots[" + dotn + "].nowiggle = false;", 200);
	}
}


function letmeout(dotn) {					// CASE 3
	dots[dotn].nowiggle = true;
	var theta = Math.random()*Math.PI*2;
	var v = 50;				// velocity in px per frame;
	var vx = v * Math.sin(theta);
	var vy = v * Math.cos(theta);
	letmeoutgo(dotn, vx, vy, 0.9);	// last number is damping factor 0<k<1; higher k means effect lasts longer
}


function letusallout(dotn) {					// CASE 4
	var i;
	var j = dotn;
	var dj = -1;
	var k = dotn + 1;
	var dk = 1;

	var lefttop = 0;
	var leftbottom = dots.length / 2 - 1;
	var righttop = dots.length / 2;
	var rightbottom = dots.length - 1;

	for (i=0; i < righttop; i++) {
		window.setTimeout("letmeout(" + j + ");", i*100);
		window.setTimeout("letmeout(" + k + ");", (i+1)*100);

		if (j == lefttop && dj == -1) {j = righttop; dj = 1;}
		else if (j == righttop && dj == -1) {j = lefttop; dj = 1;}
		else {j = j + dj;}

		if (k == leftbottom && dk == 1) {k = rightbottom; dk = -1;}
		else if (k == rightbottom && dk == 1) {k = leftbottom; dk = -1;}
		else {k = k + dk;}
	}
}


function attentiongo(dotn, k) {
	var el = dots[dotn].style;
	var x;
	var y;
	var elx;
	var ely;

	if (dots[dotn].alignment == "left") {
		elx = el.left;
		x = Number(elx.substring(0, elx.length - 1));
		x = 50 + (x - 50) * k;
		el.left = Math.round(x * 100) / 100 + "%";
	} else {
		elx = el.right;
		x = Number(elx.substring(0, elx.length - 1));
		x = 50 + (x - 50) * k;
		el.right = Math.round(x * 100) / 100 + "%";
	}
	ely = el.top;
	y = Number(ely.substring(0, ely.length - 1));

	y = 50 + (y - 50) * k;
	el.top = Math.round(y * 100) / 100 + "%";

	k*=0.8;

	if ( Math.abs(x - 50) > 0.5 || Math.abs(y - 50) > 0.5 ) {
		window.setTimeout("attentiongo(" + dotn + ", " + k + ");", dt);
	}
}


function attention() {				// CASE 5
	for (var j = 0; j < dots.length; j++) {
		attentiongo(j, 0.99);		// 2nd argument is damping factor
	}
}


function dotclicked(dotn) {			// chooses action of dots on click
	var x;
	if (atease) {x = Math.ceil(Math.random() * 5 )}	// last number should be number of cases in switch statement below
	else {x = Math.ceil(Math.random() * 4 );}		// do not trigger 2nd attention() before dots are messed up again
	switch(x) {
		case 1:						// functions that move all dots to new places
			dotsoutin();
			atease = true;
			break;
		case 2:
			letusallout(dotn);
			atease = true;
			break;
		case 3:						// functions that move a single dot or a few dots to a new place
			letmeout(dotn);
			break;
		case 4:						// functions that leave dots in position
			mexicanwiggle(dotn);
			break;
		case 5:						// functions that line up dots in regular patterns
			attention(dotn);
			atease = false;
			break;
		default:
			dotsoutin();
  }
}

// INITIALISATION

function replacelinks() {					// add onclick property to links to trigger flyout
	var loc = "/" + location.hostname + "/";
	for (var i = 0; i < document.links.length; i++) {
		var url = document.links[i].href;
		if (url && url.substring(0,4) == "http" && url.search(loc) != -1 ) {	// don't want to replace non-html and off-site links
			document.links[i].onclick = function() {
				flyoutdots(this.href);
				return false;
			};
		}
	}
}


function wiggleinit() {
	var k = 0.90;		// damping factor
	var f = 2;			// frequency in Hertz
	var t = 0;
	var amp = wigglewidth;
	while (amp > 0.5) {
		wigglearray[t] = Math.round(amp*Math.sin(2*Math.PI*f*t/fps)) + "px";
		amp *= k;
		t++;
	} 
	wigglearray[t] = "0px";
}


function initialise() {
	replacelinks();
	wiggleinit();
	flyindots();
}


function test() {
	alert("test called");
}

// *** AJAX ***

function newXMLHttpObject() {
	if (window.XMLHttpRequest) {
		// code for IE7, Firefox, Opera, etc.
		return new XMLHttpRequest();
	} else if (window.ActiveXObject) {
		// code for IE6, IE5
		return new ActiveXObject("Microsoft.XMLHTTP");
	} else {
		return false;
	}
}

// *** TWITTER ***

function tweetxmlstatechangehandler() {
	if (this.readyState==4) {	// 4 = "loaded"
		if (this.status==200) {	// 200 = "OK"
			var tweetXMLdoc = this.responseXML;
			if (tweetXMLdoc) {
				var tweetnode = tweetXMLdoc.getElementsByTagName("tweet");
				var j=0;
				while(tweetnode[j]) {
					tweet[j] = tweetnode[j].childNodes[0].nodeValue;
					j++;
				}
				numtweets = j;
			}
		}
	}
}

function nexttweet() {
	fade("tweet", 500, 0);
	tweetnum++;

	if (tweetnum == numtweets - 1) {
		// refresh tweet array  with XMLHttpRequest
		var tweetxml = newXMLHttpObject();
		tweetxml.onreadystatechange = tweetxmlstatechangehandler;
		tweetxml.open("GET", "gettweetsxml.php", true);
		tweetxml.send(null);
	}

	if (tweetnum >= numtweets) {
		tweetnum = 0;
	}
	setTimeout("document.getElementById('tweet').innerHTML = tweet[tweetnum]; fade(\"tweet\", 500, 1)", 500);
}
