/* *****************************************
    ATHENA STANDARD JAVASCRIPT
    Revision: 1.2.0
   ***************************************** */

addEvent(window, 'load', athenaInit);
var uriPos = window.location.href.indexOf('?');
var uri = uriPos > -1 ? window.location.href.slice(0, uriPos) : window.location.href;
var formChanged = false;
var statusSeed = 0;
var contextEl = null;
var pfMode = 0;
var _usr = null;
var isMSIE = /*@cc_on!@*/false;

/*
 * Prototypes
 */

String.prototype.trim = function()		{ return this.replace(/^\s+|\s+$/g,""); }
String.prototype.urlSafe = function()	{ return escape(this).replace(/\+/g, '%2B'); }
String.prototype.frmSafe = function()	{ return this.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;'); }
Math.roundP = function(x, precision)	{ return this.round(x * this.pow(10, precision)) / this.pow(10, precision); }

Number.prototype.toTime = function() {
	var secs = this;
	if (secs >= 60) {
		var mins = Math.floor(secs / 60);
		secs = secs % 60;
		if (mins >= 60) {
			var hours = Math.floor(mins / 60);
			mins = mins % 60;
			if (hours >= 24) {
				var days = Math.floor(mins / 24);
				hours = hours % 24;
				return days + ' days, ' + hours + ' hours, ' + mins + ' minutes, ' + secs + ' seconds';
			} else {
				return hours + ' hours, ' + mins + ' minutes, ' + secs + ' seconds';
			}
		} else {
			return mins + ' minutes, ' + secs + ' seconds';
		}
	} else {
		return secs + ' seconds';
	}
}

Number.prototype.toSize = function(precision) {
	if (typeof(precision) == 'undefined') precision = 1;
	var x = this;
	var suffix = ' bytes';
	if (x >= 1024) { x = x / 1024; suffix = ' KB'; }
	if (x >= 1024) { x = x / 1024; suffix = ' MB'; }
	if (x >= 1024) { x = x / 1024; suffix = ' GB'; }
	if (x >= 1024) { x = x / 1024; suffix = ' TB'; }
	return Math.roundP(x, precision)+suffix;
}

/*
 * Functions
 */

function setDefault(v, value) {
	if (typeof(v) == "undefined") v = value;
	return v;
}

// Form saving functions
// To add checking: addEvent(window, 'beforeunload', checkSave);
function frmChange() 	{ formChanged = true; }
function frmSave() 		{ formChanged = false; }

function checkSave(e) {
	if (!formChanged) return;
	var msg = 'You have unsaved changes.';
	if (!e) e = window.event;
	e.returnValue = msg;
	return msg;
}

// Athena standard initialisation
function athenaInit() {
	// IE fixes - 
	activateMenu('dropdownmenu'); 
	if (typeof(mcImageManager) != "undefined") mcImageManager.baseURL = '/core/api/tiny_mce/plugins/imagemanager/';
	if (typeof(mcFileManager) != "undefined") mcFileManager.baseURL = '/core/api/tiny_mce/plugins/filemanager/';
	
	// Window fixes -
	var el = document.getElementById("windowGrip");
	if (el) {
		el.parentNode.removeChild(el);
		document.body.appendChild(el);
	}
	// Look for other examples that might need to be moved
	// TODO:

	// Preload images -
	var preload = document.getElementById('imgPreload');
	if (preload) {
		var img = new Image();
		var imgs = preload.value.split(" ");
		for (var i = 0; i < imgs.length; i++) img.src = imgs[i];
	}
	
	// Auto-load comments
	if (window.location.href.indexOf('#comments') > -1) {
		el = document.getElementById('comments');
		if (el) showComments(el.title);
	}
	
	// Auto-refresh forum brief
	el = document.getElementById('forumTips');
	if (el) setTimeout('updateForumTips()', 10000);
}

// Generic AddEvent Function
function addEvent(obj, evType, fn){ 
	if (obj.addEventListener) {
		obj.addEventListener(evType, fn, false); 
		return true;
	} else if (obj.attachEvent) {
		var r = obj.attachEvent("on"+evType, fn);
		return r;
	} else {
		return false;
	} 
}

// Suckerfish menu fix for IE6
function activateMenu(nav) {
	// currentStyle restricts the Javascript to IE only
	var navroot = document.getElementById(nav);  
	if (document.all && navroot && navroot.currentStyle) {          
		// Get all the list items within the menu 
		var lis = navroot.getElementsByTagName("LI");  
		for (i=0; i < lis.length; i++) {		
			// If the LI has another menu level
			if (lis[i].lastChild && (lis[i].lastChild.tagName == "UL")) {
				// assign the function to the LI
				lis[i].onmouseover = function() {					
					this.lastChild.style.display = "block";
				}
				lis[i].onmouseout = function() {                       
					this.lastChild.style.display = "none";
				}
			}
		}	// end for
	}	// end if
}

// Trim whitespace
function trim(str) { return str.trim(); }

// Standard status alert - requires a block with id "statusTip" to be hidden in the body
function setStatus(txt, expire) {
	var t = document.getElementById('aedStatus');		// AED takes precidence
	if (!t) t = document.getElementById('statusTip');
	if (t) {
		t.innerHTML = txt;
		t.style.display = 'block';
		if ((typeof(expire) != "undefined") && expire) {
			if ((typeof(expire) == "numeric") || (typeof(expire) == "number")) {
				// Custom defined expire
				statusExpire(expire);
			} else {
				// Default expire
				statusExpire(3000);
			}
		}
	} else {
		alert(txt);
	}
}

function clearStatus(_seed) {
	if (typeof(_seed) != "undefined" && (statusSeed != _seed)) return;
	var t = document.getElementById('aedStatus');		// AED takes precidence
	if (!t) t = document.getElementById('statusTip');
	if (t) {
		t.style.display = 'none';
		t.innerHTML = '&nbsp;';
	}
}

function statusExpire(t) {
	if (++statusSeed > 32000) statusSeed = 0;
	setTimeout('clearStatus('+statusSeed+')', t);
}

// Browser detection
var BrowserDetect = {
	init: function () {
		this.browser = this.searchString(this.dataBrowser) || "An unknown browser";
		this.version = this.searchVersion(navigator.userAgent)
			|| this.searchVersion(navigator.appVersion)
			|| "an unknown version";
		this.OS = this.searchString(this.dataOS) || "an unknown OS";
	},
	searchString: function (data) {
		for (var i=0;i<data.length;i++)	{
			var dataString = data[i].string;
			var dataProp = data[i].prop;
			this.versionSearchString = data[i].versionSearch || data[i].identity;
			if (dataString) {
				if (dataString.indexOf(data[i].subString) != -1)
					return data[i].identity;
			}
			else if (dataProp)
				return data[i].identity;
		}
	},
	searchVersion: function (dataString) {
		var index = dataString.indexOf(this.versionSearchString);
		if (index == -1) return;
		return parseFloat(dataString.substring(index+this.versionSearchString.length+1));
	},
	dataBrowser: [
		{ 	string: navigator.userAgent,
			subString: "OmniWeb",
			versionSearch: "OmniWeb/",
			identity: "OmniWeb"
		},
		{
			string: navigator.vendor,
			subString: "Apple",
			identity: "Safari"
		},
		{
			prop: window.opera,
			identity: "Opera"
		},
		{
			string: navigator.vendor,
			subString: "iCab",
			identity: "iCab"
		},
		{
			string: navigator.vendor,
			subString: "KDE",
			identity: "Konqueror"
		},
		{
			string: navigator.userAgent,
			subString: "Firefox",
			identity: "Firefox"
		},
		{
			string: navigator.vendor,
			subString: "Camino",
			identity: "Camino"
		},
		{		// for newer Netscapes (6+)
			string: navigator.userAgent,
			subString: "Netscape",
			identity: "Netscape"
		},
		{
			string: navigator.userAgent,
			subString: "MSIE",
			identity: "Explorer",
			versionSearch: "MSIE"
		},
		{
			string: navigator.userAgent,
			subString: "Gecko",
			identity: "Mozilla",
			versionSearch: "rv"
		},
		{ 		// for older Netscapes (4-)
			string: navigator.userAgent,
			subString: "Mozilla",
			identity: "Netscape",
			versionSearch: "Mozilla"
		}
	],
	dataOS : [
		{
			string: navigator.platform,
			subString: "Win",
			identity: "Windows"
		},
		{
			string: navigator.platform,
			subString: "Mac",
			identity: "Mac"
		},
		{
			string: navigator.platform,
			subString: "Linux",
			identity: "Linux"
		}
	]

};
BrowserDetect.init();

// Initialises a single thread AJAX object
function initAjax() { 
	var ajaxObj = null;
	if (window.XMLHttpRequest) {
		ajaxObj = new XMLHttpRequest();
	} else if (window.ActiveXObject) {
		ajaxObj = new ActiveXObject("Microsoft.XMLHTTP");
	}	
	return ajaxObj;
}

// Quick "ready" function for AJAX callbacks
function ajaxReady(ajaxObj) { return (ajaxObj.readyState == 4 || ajaxObj.readyState == "complete"); }

/*
 * Perform an AJAX call
 * 
 * 'postData' is optional, if present (even an empty string) then the call
 * will be a POST call, else GET
 * 
 * 'callBack' can be a function name or a string, if you call it as a function name then the method will use
 * a global "ajax" variable and the function will be used as a the callback. If you specify a string then it
 * will be executed as a function with a single sting argument being the response.
 */
function ajaxCall(url, callback, postData, x) {
	if (typeof(callback) == "string") {
		// Self-controlled AJAX object
		var ajaxObj = initAjax();
		if (ajaxObj == null) return false;

        ajaxObj.onreadystatechange = function() {
			if (!ajaxReady(ajaxObj)) return;
			if (ajaxObj.status >= 400) {
				setStatus('(AJAX Error) Server returned '+ajaxObj.status+' '+ajaxObj.statusText, 10000);
			} else {
				eval(callback + "(ajaxObj.getResponseHeader('Content-Type').substr(0, 8).toLowerCase() == 'text/xml' ? ajaxObj.responseXML : ajaxObj.responseText, x);");
			}
        }
		
		if (typeof(postData) != "undefined") {
			ajaxObj.open("POST", url, true);
			ajaxObj.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
			ajaxObj.send(postData);
		} else {
			ajaxObj.open("GET", url, true);
			ajaxObj.send(null);
		}	
	} else {
		// Use an existing AJAX object - global `ajax`
		if (typeof(ajax) == "undefined" || ajax == null) ajax = initAjax();
		if (ajax == null) return false;
		
		ajax.onreadystatechange = callback;
		if (typeof(postData) != "undefined") {
			ajax.open("POST", url, true);
			ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
			ajax.send(postData);
		} else {
			ajax.open("GET", url, true);
			ajax.send(null);
		}
	}
	return true;
}

// Page Finder
function showFinder(_pfMode) {
	if (typeof(_pfMode) == "undefined") var _pfMode = 0;
	pfMode = _pfMode;
	openAthenaWindow('pfWindow');
	var txt = document.getElementById('pfSearchTxt');
	txt.value = '/';
	txt.focus();
	pfSearch();
}

function pfSearch() {
	q = document.getElementById('pfSearchTxt').value.urlSafe();
	ajaxCall("/_std.ajax?action=6&search="+q, 'pf_callback');
}

function pf_callback(xml) {
	var root = xml.getElementsByTagName('pagedata')[0];
	var code = root.getAttribute("code");
	if (code == "ok") {
		// Show results
		var itemRoot = xml.getElementsByTagName('searchItems')[0];
		var items = itemRoot.getElementsByTagName("item");
		document.getElementById("pfResults").innerHTML = "";
		for (var i = 0 ; i < items.length ; i++) {
			var currentItem = items[i];
			var toc;
			var title;
			var url;
			var id = currentItem.getElementsByTagName("id")[0].firstChild.nodeValue;
			if (currentItem.getElementsByTagName("toc")[0].firstChild) {
				toc = currentItem.getElementsByTagName("toc")[0].firstChild.nodeValue;	
			} else {
				toc = "[No TOC]"
			}
			title = currentItem.getElementsByTagName("title")[0].firstChild.nodeValue;
			url = currentItem.getElementsByTagName("url")[0].firstChild.nodeValue;
			document.getElementById("pfResults").innerHTML += '\
			<div class="pfItem" onClick="pfClick('+id+')">\
				<div class="pfID">'+id+' - <span class="pfTOC" id="pfToc'+id+'">'+toc+'</span></div>\
				<div class="pfTitle">'+title+'</div>\
				<div class="pfURL" id="pfURL'+id+'">'+url+'</div>\
			</div>';
		}
	} else {
		// No access
		document.getElementById("pfResults").innerHTML = '\
			<div class="pfItem">\
				<div class="pfID">No Access</div>\
				<div class="pfURL">You do not have the required access to list pages.</div>\
			</div>';			
	}
}

function pfClick(id) {
	link = document.getElementById('pfLink');
	if (pfMode) {
		link.innerHTML = id;
	} else {
		toc = document.getElementById('pfToc' + id);
		if (toc.innerHTML == "[No TOC]") {
			link.innerHTML = document.getElementById('pfURL' + id).innerHTML;
		} else {
			link.innerHTML = '{' + toc.innerHTML + '}';
		}
	}
	autoSelect(link);
}

// Selects an object
function autoSelect(el) {
	if (el && (el.tagName == "TEXTAREA" || (el.tagName == "INPUT" && el.type == "text"))) {
		el.select();
	} else if (el && window.getSelection) {
		// DOM browsers
		var sel = window.getSelection();
		var range = document.createRange();
		range.selectNodeContents(el);
		sel.removeAllRanges();
		sel.addRange(range);
	} else if (el) {
		// IE
		document.selection.empty();
		var range = document.body.createTextRange();
		range.moveToElementText(el);
		range.select();
	}
}

// Returns a string containing all of a forms elements ready for sending via AJAX
function frmToPost(form) {
	var data = '';
	for (var i = 0; i < form.elements.length; i++) {
		x = form.elements[i];
		if (x.type == "radio" && !x.checked) continue;
		
		if (data) data += '&';
		data += x.name + "=";
		
		if (x.type == "checkbox") {
			// Checkbox
			data += x.checked ? '1' : '0';
		} else {
			// Anything else
			data += x.value.urlSafe();
		}
	}
	return data;
}

// WYSIWYG Controls
function showWC() {
	winList['wcWindow'].open();
	wcCheck();
}

// Check the WC status and return it to the page, AJAX
function wcCheck() {
	wcAjax = initAjax();
	wcAjax.onreadystatechange = wc_callback;
	wcAjax.open( "GET", "/_std.ajax?action=7", true );
	wcAjax.send( null );	
}

function wcSetStatus(status) {
	wcAjax = initAjax();
	wcAjax.onreadystatechange = wc_callback;
	wcAjax.open( "GET", "/_std.ajax?action=7&wc="+status, true );
	wcAjax.send( null );	
	btn = document.getElementById('wcSetBtn');
	btn.value = "Standby";
	btn.disabled = true;
}

function wc_callback() {
	if (!(wcAjax.readyState==4 || wcAjax.readyState=="complete")) return;
	btn = document.getElementById('wcSetBtn');
	btn.disabled = false;
	if (wcAjax.responseText == 'true') {
		document.getElementById('wcSettings').innerHTML = 'Disallow';
		btn.value = 'Allow';
		btn.onclick = function() { wcSetStatus('0'); }
	} else {
		document.getElementById('wcSettings').innerHTML = 'Permit';
		btn.value = 'Disallow';
		btn.onclick = function() { wcSetStatus('1'); }
	}
}


/*
 * BBC Functions
 */

function toggleSpoiler(e) {
	if (!e) var e = window.event;
	var targ = e.target ? e.target : e.srcElement;
	if (targ.nodeType == 3) targ = targ.parentNode; // defeat Safari bug
	
	var spoilerBox = targ.parentNode;
	for (i = 0; i < spoilerBox.childNodes.length; i++) {
		if (spoilerBox.childNodes[i].className == "spoilerBody") {
			var spoilerBody = spoilerBox.childNodes[i];
			if (spoilerBody.opened) {
				spoilerBody.opened = false;
				spoilerBody.style.display = 'none';
			} else {
				spoilerBody.opened = true;
				spoilerBody.style.display = 'block';
			}
		}
	}
	targ.blur();
	return false;
}

function contextClick(e) {
	if (!e) var e = window.event;
	var targ = e.target ? e.target : e.srcElement;
	if (targ.nodeType == 3) targ = targ.parentNode; // defeat Safari bug
	targ.blur();
	contextEl = targ;
	contextAction(0);
}

function contextAction(action) {
	switch (action) {
		default:
		case 0:	// Turn off
			contextEl.className = "contextOff";
			setTimeout("contextAction(1)", 70);
			break;
		case 1:	// Turn on
			contextEl.className = "contextOn";
			setTimeout("contextAction(2)", 90);
			break;
		case 2:	// Turn off
			contextEl.className = "contextOff";
			setTimeout("contextAction(3)", 70);
			break;
		case 3:	// Turn on
			contextEl.className = "contextOn";
			break;
	}
}


/*
 * Standard Menu/Button Controls
 */
function enableBtn(btn) {
	btnA = document.getElementById(btn);
	if (btnA && (btnA.className == 'stdBtnDisabled')) {
		btnA.className = 'stdBtn';
		var btnI = btnA.getElementsByTagName('img')[0];
		if (btnI) btnI.src = btnI.src.substr(0, btnI.src.length-13)+".png";
	}	
}

function disableBtn(btn) {
	btnA = document.getElementById(btn);
	if (btnA && (btnA.className == 'stdBtn')) {
		btnA.className = 'stdBtnDisabled';
		var btnI = btnA.getElementsByTagName('img')[0];
		if (btnI) btnI.src = btnI.src.substr(0, btnI.src.length-4)+"_disabled.png";
	}
}

function showBtn(btn) {
	document.getElementById(btn).style.display = 'block';
}

function hideBtn(btn) {
	document.getElementById(btn).style.display = 'none';
}

function btnEnabled(btn) {
	btnA = document.getElementById(btn);
	return btnA && ((btnA.className == 'stdBtn') || (btnA.className == 'stdBtnActive'));
}

// Quick hint set function - intended for form hints
function setHint(id, hint, style) {
	el = document.getElementById(id);
	if (!el) return;
	if (typeof(hint) == 'undefined' || hint == '') {
		el.style.display = 'none';
		el.innerHTML = '';
	} else {
		el.style.display = 'block';
		if (typeof(style) == 'undefined' ) {
			el.innerHTML = hint;
		} else {
			el.innerHTML = '<span class="'+style+'">'+hint+'</span>';
		}
	}
}

// Strips a form field down to a selected char range
function stripChars(el, validChars) {
	validChars = setDefault(validChars, "abcdefghijklmnopqrstuvwxyz-_.@0123456789");
	var val = el.value.toLowerCase();
	var rep = '';
	var c;
	for (var i = 0; i < val.length; i++) {
		c = val.substr(i, 1);
		if (validChars.indexOf(c) > -1) rep += c;
	}
	if (rep != el.value) {
		var pos = getCaretPosition(el, true);
		el.value = rep;
		setCaretPosition(el, pos, true);
	}
}

/*
 * Athena Windowing System
 */

var dragStatus = 0;
var dragSrc = null;
var dragX, dragY, dragIt = 0;
var windowZ = 1000;

function dragInit(e) {
	if (!e) var e = window.event;
	var targ = e.target ? e.target : e.srcElement;
	if (targ.nodeType == 3) targ = targ.parentNode; // defeat Safari bug
	
	dragSrc = targ.parentNode;
	while (dragSrc.className != 'athenaWindow') {
		if (dragSrc.parentNode) {
			dragSrc = dragSrc.parentNode;
		} else {
			return; // error
		}
	}
	
	if (typeof(dragSrc.style.pixelLeft) == "undefined") {
		dragX = e.clientX - dragSrc.offsetLeft;
		dragY = e.clientY - dragSrc.offsetTop;
	} else {
		dragX = e.clientX - dragSrc.style.pixelLeft;
		dragY = e.clientY - dragSrc.style.pixelTop;
	}
	
	if (dragStatus == 0) {
		addEvent(document, 'mousemove', dragMove);
		addEvent(document, 'mouseup', dragEnd);
	}
	dragStatus = 2;
	
	if (e.preventDefault) e.preventDefault();
	e.returnValue = false;
}

function dragMove(e) {
	if (!e) var e = window.event;
	// High-res mice will slow this down, we can counter by only redrawing every x samples
	if (++dragIt < 2) {
		return false;
	} else {
		dragIt = 0;
	}
	if (dragStatus == 2){
		if (typeof(dragSrc.style.pixelLeft) == "undefined") {
			dragSrc.style.left = (e.clientX - dragX) + "px";
			dragSrc.style.top = (e.clientY - dragY) + "px";
		} else {
			dragSrc.style.pixelLeft = e.clientX - dragX;
			dragSrc.style.pixelTop = e.clientY - dragY;
		}
		
		return false;
	}
}

function dragEnd(e) {
	dragStatus = 1;
}

function updateWindow(e) {
	if (!e) var e = window.event;
	var targ = e.target ? e.target : e.srcElement;
	if (targ.nodeType == 3) targ = targ.parentNode; // defeat Safari bug

	while (targ.className != 'athenaWindow') {
		if (targ.parentNode) {
			targ = targ.parentNode;
		} else {
			return;	// error
		}
	}
	
	targ.style.zIndex = ++windowZ;
}

function openAthenaWindow(id) {
	var win = document.getElementById(id);
	if (!win) return;
	win.style.display = 'block';
	win.style.zIndex = ++windowZ;
}

function closeAthenaWindow(e) {
	var targ = null;
	if (typeof(e) == 'string') {
		targ = document.getElementById(e);
	} else {
		if (!e) var e = window.event;
		targ = e.target ? e.target : e.srcElement;
		if (targ.nodeType == 3) targ = targ.parentNode; // defeat Safari bug
		while (targ.className != 'athenaWindow') {
			if (targ.parentNode) {
				targ = targ.parentNode;
			} else {
				return false; // error
			}
		}
	}
	
	targ.style.display = 'none';
	return false;
}

// Put an element in the top-left of a window
function positionWindow(id, x, y) {
	var lw = document.getElementById(id);
	if (lw) {
		if (typeof(x) == "undefined") x = 50;
		if (typeof(y) == "undefined") y = 50;
		var sTop = typeof(window.pageYOffset) != "undefined" ? window.pageYOffset : document.documentElement.scrollTop;
		lw.style.top = (sTop + y) + "px";
		lw.style.left = x + "px";
	}
}

function centerWindow(id){
	centerElement(document.getElementById(id));
}

/*
 * Text functions
 */

function surroundText(text1, text2, textarea) {
	// Can a text range be created?
	if (typeof(textarea.caretPos) != "undefined" && textarea.createTextRange) {
		var caretPos = textarea.caretPos, temp_length = caretPos.text.length;
		caretPos.text = caretPos.text.charAt(caretPos.text.length - 1) == ' ' ? text1 + caretPos.text + text2 + ' ' : text1 + caretPos.text + text2;

		if (temp_length == 0) {
			caretPos.moveStart("character", -text2.length);
			caretPos.moveEnd("character", -text2.length);
			caretPos.select();
		} else {
			textarea.focus(caretPos);
		}
	} else if (typeof(textarea.selectionStart) != "undefined") {
		// Mozilla text range wrap.
		var begin = textarea.value.substr(0, textarea.selectionStart);
		var selection = textarea.value.substr(textarea.selectionStart, textarea.selectionEnd - textarea.selectionStart);
		var end = textarea.value.substr(textarea.selectionEnd);
		var newCursorPos = textarea.selectionStart;
		var scrollPos = textarea.scrollTop;

		textarea.value = begin + text1 + selection + text2 + end;

		if (textarea.setSelectionRange) {
			if (selection.length == 0) {
				textarea.setSelectionRange(newCursorPos + text1.length, newCursorPos + text1.length);
			} else {
				textarea.setSelectionRange(newCursorPos, newCursorPos + text1.length + selection.length + text2.length);
			}
			textarea.focus();
		}
		textarea.scrollTop = scrollPos;
	} else {
		// Just put them on the end, then.
		textarea.value += text1 + text2;
		textarea.focus(textarea.value.length - 1);
	}
}

function formatTA(e, textarea) {
	if (!e) var e = window.event;
	var format = document.getElementById('format').value;
	if (format == '0') format = 0;
	if (e.keyCode == 13 && e.shiftKey) {
		if (format == 0) {
			surroundText('<br />', '', textarea);
		} else {
			surroundText('[p]', '[/p]', textarea);
		}
	}
	if (e.keyCode == 9 && e.shiftKey) {
		surroundText('	', '', textarea);
		e.returnValue = false;
		setTimeout("document.getElementById('"+textarea.id+"').focus();",0);
	} else if (e.keyCode == 9) {
		surroundText('   ', '', textarea);
		e.returnValue = false;
		setTimeout("document.getElementById('"+textarea.id+"').focus();",0);
	}
}

function getCaretPosition(el, mustFocus) {
	mustFocus = setDefault(mustFocus, false);
	var caretPos = 0;

	if (document.selection) {
		// --  IE -- //
		if (mustFocus) el.focus();
		
		var sel = document.selection.createRange();
		sel.moveStart('character', -el.value.length);
		caretPos = sel.text.length;
	} else {
		// -- DOM -- //
		caretPos = el.selectionStart;
	}
	
	// Return results
	return caretPos;
}

/*
**  Sets the caret (cursor) position of the specified text field.
**  Valid positions are 0-oField.length.
*/
function setCaretPosition(el, pos, mustFocus) {
	mustFocus = setDefault(mustFocus, false);
	
	if (document.selection) { 
		// -- IE -- //
		if (mustFocus) el.focus();

		var sel = document.selection.createRange();
		sel.moveEnd('character', -el.value.length);
		sel.moveStart('character', -el.value.length);
		sel.moveEnd('character', pos);
		sel.moveStart('character', pos);
		sel.select();
	} else {
		// -- DOM -- //
		el.selectionStart = pos;
		el.selectionEnd = pos;
	}
}

/*
 * Element Max-Length Constraint
 * Works on any text field and uses the elAtt() function to warn when length hit
 * ---------------------------------
 * Revision: 1.0.0
 */

function checkLength(el, len) {
	if (el.value.length > len) {
		el.value = el.value.substr(0, len);
		elAtt(el);
		return false;
	} else {
		return true;
	}
}


/*
 * Element Attention
 * Highlights an element and it fades back to normal
 * ---------------------------------
 * Revision: 1.0.0
 */

var fadeData = new Array();
function elAtt(el, msec, cola, colb) {
	var id;
	if (typeof(el) == "string") {
		id = el;
		el = document.getElementById(id);
	} else {
		id = el.id;
	}
	
	if (typeof(msec) == "undefined") msec = 2500;
	if (typeof(cola) == "undefined") cola = "rgb(255,0,0)";
	if (typeof(colb) == "undefined") colb = "rgb(255,200,200)";
	
	if (typeof(fadeData[id]) == "undefined") {
		// First run
		fadeData[id] = {
			t: msec,
			a: cola,
			b: colb,
			x: getStyle(el, "border-top-color"),
			y: getStyle(el, "background-color"),
			s: Math.random()
		};
		
		// Fix colour codes
		if (fadeData[id].a.substr(0,1) == '#') fadeData[id].a = convertColour(fadeData[id].a);
		if (fadeData[id].b.substr(0,1) == '#') fadeData[id].b = convertColour(fadeData[id].b);
		if (fadeData[id].x.substr(0,1) == '#') fadeData[id].x = convertColour(fadeData[id].x);
		if (fadeData[id].y.substr(0,1) == '#') fadeData[id].y = convertColour(fadeData[id].y);
	} else {
		// Rerun
		fadeData[id].t = msec;
		fadeData[id].a = cola;
		fadeData[id].b = colb;
		fadeData[id].s = Math.random();
		if (fadeData[id].a[0] == '#') fadeData[id].a = convertColour(fadeData[id].a);
		if (fadeData[id].b[0] == '#') fadeData[id].b = convertColour(fadeData[id].b);
	}
	
	el.style.borderColor = fadeData[id].a;
	el.style.backgroundColor = fadeData[id].b;
	
	var d = new Date();
	setTimeout("elAttStat('"+id+"', "+d.getTime()+", "+msec+", "+fadeData[id].s+")", 10);
}

// convert from 6-digit hex syntax to RGB syntax
function convertColour(c) {
	var red = parseInt(c.substr(1,2), 16);
	var green = parseInt(c.substr(3,2), 16);
	var blue = parseInt(c.substr(5,2), 16);
	return 'rgb('+red+', '+green+', '+blue+')';	
}

function elAttStat(id, startTime, animTime, seed) {
	var att = fadeData[id];
	var el = document.getElementById(id);
	if (att.s != seed) return; // State changed, terminate this thread
	
	var d = new Date();
	var t = d.getTime();
	if (t >= startTime + animTime) {
		// Last run, finish up
		el.style.borderColor = att.x;
		el.style.backgroundColor = att.y;
	} else {
		// Progressive update
		var progress = (t - startTime) / animTime;
		var regex = /rgb\(([0-9]+)\,\s*([0-9]+)\,\s*([0-9]+)\)/
		var cola = regex.exec(att.a);
		var colb = regex.exec(att.b);
		
		var colx = regex.exec(att.x);
		var coly = regex.exec(att.y);
		
		el.style.borderColor = mixCol(cola, colx, progress);
		el.style.backgroundColor = mixCol(colb, coly, progress);
		
		// Queue next frame
		setTimeout("elAttStat('"+id+"', "+startTime+", "+animTime+", "+seed+")", 10);
	}
}

function mixCol(cola, colb, progress) {
	var ch1 = parseInt(cola[1]) + Math.floor((colb[1] - cola[1]) * progress);
	var ch2 = parseInt(cola[2]) + Math.floor((colb[2] - cola[2]) * progress);
	var ch3 = parseInt(cola[3]) + Math.floor((colb[3] - cola[3]) * progress);
	return 'rgb('+ch1+', '+ch2+', '+ch3+')';
}

function getStyle(oElm, strCssRule) {
	var strValue = "";
	if(document.defaultView && document.defaultView.getComputedStyle){
		strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule);
	} else if (oElm.currentStyle){
		strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1) {
			return p1.toUpperCase();
		});
		strValue = oElm.currentStyle[strCssRule];
	}
	return strValue;
}


/*
 * Modal Dialogue
 * Revision: 1.0.3
 */

function openModalDialogue(id) {
	var el = document.getElementById(id);
	var modal = document.getElementById('modalBox');
	if (!modal) {
		// Modal box doesn't exist, create it at the bottom of the <body> element
		document.body.innerHTML += '<div id="modalBox">&nbsp;</div>';
		modal = document.getElementById('modalBox');
	}
	if (el) {
		// Nuke opacity and set visible
		changeOpac(id, 0);
		changeOpac('modalBox', 0);
		el.style.display = 'block';
		modal.style.display = 'block';
		
		// Center Modal Dialogue
		centerElement(el);
		
		// Set modal cover to full-screen
		// Non-IE browsers include the vertical scroll-bar here, so this normally adds a horizonal one too
		var viewport = getViewportDimensions();
		modal.style.width = Math.max(document.body.offsetWidth, document.body.parentNode.offsetWidth, viewport.width)+'px';
		modal.style.height = Math.max(document.body.offsetHeight, document.body.parentNode.offsetHeight, viewport.height)+'px';
		
		// Fade in dialoge and modal cover
		var d = new Date();
		setTimeout("modalProgress('"+id+"', "+d.getTime()+", 150, false)", 10); 
	}
}

function closeModalDialogue(id){
	// Fade out dialoge and modal cover
	var d = new Date();
	setTimeout("modalProgress('"+id+"', "+d.getTime()+", 150, true)", 1);
}

// Special function to do two elements in one
function modalProgress(id, startTime, animTime, reverse) {
	var d = new Date();
	var t = d.getTime();
	if (t >= startTime + animTime) {
		// Last call - finish off
		if (reverse) {
			document.getElementById(id).style.display = 'none';
			document.getElementById('modalBox').style.display = 'none';
		} else {
			changeOpac(id, 1);
			changeOpac('modalBox', 0.5);
		}
	} else {
		var progress = (t - startTime) / animTime;
		if (reverse) {
			changeOpac(id, 1-progress);
			changeOpac('modalBox', 0.5-(progress / 2));
		} else {
			changeOpac(id, progress);
			changeOpac('modalBox', progress / 2);
		}
		// Queue up next frame
		setTimeout("modalProgress('"+id+"', "+startTime+", "+animTime+", "+reverse+")", 10); 
	}
}

// Change the opacity for all browsers 
function changeOpac(id, opacity) { 
	var object = document.getElementById(id).style; 
	object.opacity = opacity; 
	object.MozOpacity = opacity; 
	object.KhtmlOpacity = opacity; 
	object.filter = "alpha(opacity=" + Math.ceil(opacity * 100)+ ")"; 
}

// Centers an element on the screen (kind'a)
function centerElement(elem) {
	var viewport = getViewportDimensions();
	var scrollTop = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop;
	var scrollLeft = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft;
	var left = (viewport.width == 0) ? 50 : parseInt((viewport.width - elem.offsetWidth) / 2  + scrollLeft, 10);
	var top = (viewport.height == 0) ? 50 : parseInt((viewport.height - elem.offsetHeight) / 2  + scrollTop, 10);
	
	elem.style.left = left + 'px';
	elem.style.top = top + 'px';
}

// Grabs the viewport dimensions
function getViewportDimensions() {
	var intH = 0, intW = 0;
	
	if (self.innerHeight) {
		intH = window.innerHeight;
		intW = window.innerWidth;
	} else if (document.documentElement && document.documentElement.clientHeight) {
		intH = document.documentElement.clientHeight;
		intW = document.documentElement.clientWidth;
	} else if (document.body) {
		intH = document.body.clientHeight;
		intW = document.body.clientWidth;
	}
	
	return {
		height: parseInt(intH, 10),
		width: parseInt(intW, 10)
	};
}

// Very simple JSON encoder
function json_encode(arr) {
	var json = '';
	
	/*
	 * Associative arrays are really hash objects, however some browsers return the same object type for either real arrays
	 * or hash objects. Most browsers can identify the array type with:
	 *   (Object.prototype.toString.apply(arr) === '[object Array]');
	 */
	var isArray = false;
	
	for (var key in arr) {
		if (json) json += ",";
		
		var value = arr[key];
		//Custom handling for arrays
		if (typeof(value) == "object") { 
			// Recurse
			if (isArray) {
				json += json_encode(value);
			} else {
				json += '"' + key + '":' + json_encode(value);
			}
		} else {
			// Custom handling for multiple data types
			var str = '';
			if (typeof(value) == "number") {
				str += value;
			} else if (value === false) {
				str += 'false';
			} else 	if (value === true) {
				str += 'true';
			} else {
				str += '"' + value.replace(/\"/g, '\\"') + '"'; 
			}
			if (isArray) {
				json += str;
			} else {
				json += '"' + key + '":' + str;
			}
		}
	}
	
	if (isArray) {
		// Enumerated
		return '[' + json + ']';
	} else {
		// Associative array
		return '{' + json + '}';
	}
}

function usr() {
	if (_usr) {
		return _usr;
	} else {
		var el = document.getElementById('usr');
		_usr = el ? el.value : 'usr/common/';
		return _usr;
	}
}

/*
 * Page Comments
 */

function showComments(id) {
	var com = document.getElementById('commentsBox');
	if (com) {
		ajaxCall('/_std.ajax?action=21&id='+id, 'commentsCallback');
		com.innerHTML = '<div class="comLoad"><p>Loading Comments..</p><img src="/usr/common/images/loading.gif" /></div>';
	}
}

function commentsCallback(txt) {
	var com = document.getElementById('commentsBox');
	if (com) com.innerHTML = txt;
}

/*
 * Forum Tips
 */

function updateForumTips() {
	ajaxCall('/_std.ajax?action=23', 'forumsCallback');
}

function forumsCallback(txt) {
	el = document.getElementById('forumTips');
	el.innerHTML = txt;
	setTimeout('updateForumTips()', 10000);
}

function forumsCatchUp(board) {
	ajaxCall('/_std.ajax?action=24&board='+board, 'catchupCallback');
	var el = document.getElementById('board-'+board);
	el.className = "forumBoard removing";
	el = document.getElementById('boardLink-'+board);
	el.parentNode.removeChild(el);
}

function catchupCallback(txt) {
	// Done, update display
	ajaxCall('/_std.ajax?action=23', 'forumsCallback_single');
}

function forumsCallback_single(txt) {
	var el = document.getElementById('forumTips');
	el.innerHTML = txt;
}

/*
 * Voting on polls
 */

function vote(item, direction) {
	ajaxCall('/_std.ajax?action=25&item='+item+'&direction='+direction, 'voteCallback');
}

function voteCallback(json) {
	var stat;
	try {
		stat = eval("("+json+")");
	} catch(e) {
		setStatus("Unknown response received from server", true);
	}
	if (stat['err']) {
		setStatus(stat['body'], true);
	} else {
		var el = document.getElementById('poll-'+stat['id']);
		if (el) {
			el.innerHTML = stat['body'];
		}
	}
}

