// MediaWiki JavaScript support functions

var clientPC = navigator.userAgent.toLowerCase(); // Get client info
var is_gecko = ((clientPC.indexOf('gecko')!=-1) && (clientPC.indexOf('spoofer')==-1)
 && (clientPC.indexOf('khtml') == -1) && (clientPC.indexOf('netscape/7.0')==-1));
var is_safari = ((clientPC.indexOf('applewebkit')!=-1) && (clientPC.indexOf('spoofer')==-1));
var is_khtml = (navigator.vendor == 'KDE' || ( document.childNodes && !document.all && !navigator.taintEnabled ));
// For accesskeys
var is_ff2_win = (clientPC.indexOf('firefox/2')!=-1 || clientPC.indexOf('minefield/3')!=-1) && clientPC.indexOf('windows')!=-1;
var is_ff2_x11 = (clientPC.indexOf('firefox/2')!=-1 || clientPC.indexOf('minefield/3')!=-1) && clientPC.indexOf('x11')!=-1;
if (clientPC.indexOf('opera') != -1) {
	var is_opera = true;
	var is_opera_preseven = (window.opera && !document.childNodes);
	var is_opera_seven = (window.opera && document.childNodes);
	var is_opera_95 = ( _proxy_jslib_handle(clientPC, 'search', '', 1, 0)(/opera\/(9.[5-9]|[1-9][0-9])/)!=-1);
}

// Global external objects used by this script.
/*extern ta, stylepath, skin */

// add any onload functions in this hook (please don't hard-code any events in the xhtml source)
var doneOnloadHook;

if (!window.onloadFuncts) {
	var onloadFuncts = [];
}

function addOnloadHook(hookFunct) {
	// Allows add-on scripts to add onload functions
	 _proxy_jslib_assign('', onloadFuncts, (onloadFuncts.length), '=', ( hookFunct));
}

function hookEvent(hookName, hookFunct) {
	if (window.addEventListener) {
		window.addEventListener(hookName, hookFunct, false);
	} else if (window.attachEvent) {
		window.attachEvent("on" + hookName, hookFunct);
	}
}

// document.write special stylesheet links
if (typeof stylepath != 'undefined' && typeof skin != 'undefined') {
	if (is_opera_preseven) {
		 _proxy_jslib_handle(document, 'write', '', 1, 0)('<link rel="stylesheet" type="text/css" href="'+stylepath+'/'+skin+'/Opera6Fixes.css">');
	} else if (is_opera_seven && !is_opera_95) {
		 _proxy_jslib_handle(document, 'write', '', 1, 0)('<link rel="stylesheet" type="text/css" href="'+stylepath+'/'+skin+'/Opera7Fixes.css">');
	} else if (is_opera_95) {
		 _proxy_jslib_handle(document, 'write', '', 1, 0)('<link rel="stylesheet" type="text/css" href="'+stylepath+'/'+skin+'/Opera95Fixes.css">');
	} else if (is_khtml) {
		 _proxy_jslib_handle(document, 'write', '', 1, 0)('<link rel="stylesheet" type="text/css" href="'+stylepath+'/'+skin+'/KHTMLFixes.css">');
	}
}

if (wgBreakFrames) {
	// Un-trap us from framesets
	if ( _proxy_jslib_handle(window, 'top', '', 0, 0) != window) {
		 _proxy_jslib_assign('',  _proxy_jslib_handle(window, 'top', '', 0, 0), 'location', '=', (  _proxy_jslib_handle(window, 'location', '', 0, 0)));
	}
}

// for enhanced RecentChanges
function toggleVisibility(_levelId, _otherId, _linkId) {
	var thisLevel =  _proxy_jslib_handle(document, 'getElementById', '', 1, 0)(_levelId);
	var otherLevel =  _proxy_jslib_handle(document, 'getElementById', '', 1, 0)(_otherId);
	var linkLevel =  _proxy_jslib_handle(document, 'getElementById', '', 1, 0)(_linkId);
	if (thisLevel.style.display == 'none') {
		thisLevel.style.display = 'block';
		otherLevel.style.display = 'none';
		linkLevel.style.display = 'inline';
	} else {
		thisLevel.style.display = 'none';
		otherLevel.style.display = 'inline';
		linkLevel.style.display = 'none';
	}
}

function historyRadios(parent) {
	var inputs =  _proxy_jslib_handle( _proxy_jslib_handle(null, 'parent', parent, 0, 0), 'getElementsByTagName', '', 1, 0)('input');
	var radios = [];
	for (var i = 0; i < inputs.length; i++) {
		if ( _proxy_jslib_handle(inputs, (i), 0, 0).name == "diff" ||  _proxy_jslib_handle(inputs, (i), 0, 0).name == "oldid") {
			 _proxy_jslib_assign('', radios, (radios.length), '=', (  _proxy_jslib_handle(inputs, (i), 0, 0)));
		}
	}
	return radios;
}

// check selection and tweak visibility/class onclick
function diffcheck() {
	var dli = false; // the li where the diff radio is checked
	var oli = false; // the li where the oldid radio is checked
	var hf =  _proxy_jslib_handle(document, 'getElementById', '', 1, 0)('pagehistory');
	if (!hf) {
		return true;
	}
	var lis =  _proxy_jslib_handle(hf, 'getElementsByTagName', '', 1, 0)('li');
	for (var i=0;i<lis.length;i++) {
		var inputs = historyRadios( _proxy_jslib_handle(lis, (i), 0, 0));
		if (inputs[1] && inputs[0]) {
			if (inputs[1].checked || inputs[0].checked) { // this row has a checked radio button
				if (inputs[1].checked && inputs[0].checked &&  _proxy_jslib_handle(inputs[0], 'value', '', 0, 0) ==  _proxy_jslib_handle(inputs[1], 'value', '', 0, 0)) {
					return false;
				}
				if (oli) { // it's the second checked radio
					if (inputs[1].checked) {
						oli.className = "selected";
						return false;
					}
				} else if (inputs[0].checked) {
					return false;
				}
				if (inputs[0].checked) {
					dli =  _proxy_jslib_handle(lis, (i), 0, 0);
				}
				if (!oli) {
					inputs[0].style.visibility = 'hidden';
				}
				if (dli) {
					inputs[1].style.visibility = 'hidden';
				}
				 _proxy_jslib_handle(lis, (i), 0, 0).className = "selected";
				oli =  _proxy_jslib_handle(lis, (i), 0, 0);
			}  else { // no radio is checked in this row
				if (!oli) {
					inputs[0].style.visibility = 'hidden';
				} else {
					inputs[0].style.visibility = 'visible';
				}
				if (dli) {
					inputs[1].style.visibility = 'hidden';
				} else {
					inputs[1].style.visibility = 'visible';
				}
				 _proxy_jslib_handle(lis, (i), 0, 0).className = "";
			}
		}
	}
	return true;
}

// page history stuff
// attach event handlers to the input elements on history page
function histrowinit() {
	var hf =  _proxy_jslib_handle(document, 'getElementById', '', 1, 0)('pagehistory');
	if (!hf) {
		return;
	}
	var lis =  _proxy_jslib_handle(hf, 'getElementsByTagName', '', 1, 0)('li');
	for (var i = 0; i < lis.length; i++) {
		var inputs = historyRadios( _proxy_jslib_handle(lis, (i), 0, 0));
		if (inputs[0] && inputs[1]) {
			inputs[0].onclick = diffcheck;
			inputs[1].onclick = diffcheck;
		}
	}
	diffcheck();
}

// generate toc from prefs form, fold sections
// XXX: needs testing on IE/Mac and safari
// more comments to follow
function tabbedprefs() {
	var prefform =  _proxy_jslib_handle(document, 'getElementById', '', 1, 0)('preferences');
	if (!prefform || !document.createElement) {
		return;
	}
	if (prefform.nodeName.toLowerCase() == 'a') {
		return; // Occasional IE problem
	}
	prefform.className = prefform.className + 'jsprefs';
	var sections = [];
	var children = prefform.childNodes;
	var seci = 0;
	for (var i = 0; i < children.length; i++) {
		if ( _proxy_jslib_handle(children, (i), 0, 0).nodeName.toLowerCase() == 'fieldset') {
			 _proxy_jslib_handle(children, (i), 0, 0).id = 'prefsection-' + seci;
			 _proxy_jslib_handle(children, (i), 0, 0).className = 'prefsection';
			if (is_opera || is_khtml) {
				 _proxy_jslib_handle(children, (i), 0, 0).className = 'prefsection operaprefsection';
			}
			var legends =  _proxy_jslib_handle( _proxy_jslib_handle(children, (i), 0, 0), 'getElementsByTagName', '', 1, 0)('legend');
			 _proxy_jslib_assign('', sections, (seci), '=', ( {}));
			legends[0].className = 'mainLegend';
			if (legends[0] &&  _proxy_jslib_handle(legends[0].firstChild, 'nodeValue', '', 0, 0)) {
				 _proxy_jslib_handle(sections, (seci), 0, 0).text =  _proxy_jslib_handle(legends[0].firstChild, 'nodeValue', '', 0, 0);
			} else {
				 _proxy_jslib_handle(sections, (seci), 0, 0).text = '# ' + seci;
			}
			 _proxy_jslib_handle(sections, (seci), 0, 0).secid =  _proxy_jslib_handle(children, (i), 0, 0).id;
			seci++;
			if (sections.length != 1) {
				 _proxy_jslib_handle(children, (i), 0, 0).style.display = 'none';
			} else {
				var selectedid =  _proxy_jslib_handle(children, (i), 0, 0).id;
			}
		}
	}
	var toc = document.createElement('ul');
	toc.id = 'preftoc';
	toc.selectedid = selectedid;
	for (i = 0; i < sections.length; i++) {
		var li = document.createElement('li');
		if (i === 0) {
			li.className = 'selected';
		}
		var a = document.createElement('a');
		 _proxy_jslib_assign('', a, 'href', '=', ( '#' +  _proxy_jslib_handle(sections, (i), 0, 0).secid));
		a.onmousedown = a.onclick = uncoversection;
		a.appendChild(document.createTextNode( _proxy_jslib_handle(sections, (i), 0, 0).text));
		a.secid =  _proxy_jslib_handle(sections, (i), 0, 0).secid;
		li.appendChild(a);
		toc.appendChild(li);
	}
	prefform.parentNode.insertBefore(toc, prefform.parentNode.childNodes[0]);
	 _proxy_jslib_handle(document, 'getElementById', '', 1, 0)('prefsubmit').id = 'prefcontrol';
}

function uncoversection() {
	var oldsecid = this.parentNode.parentNode.selectedid;
	var newsec =  _proxy_jslib_handle(document, 'getElementById', '', 1, 0)(this.secid);
	if (oldsecid != this.secid) {
		var ul =  _proxy_jslib_handle(document, 'getElementById', '', 1, 0)('preftoc');
		 _proxy_jslib_handle(document, 'getElementById', '', 1, 0)(oldsecid).style.display = 'none';
		newsec.style.display = 'block';
		ul.selectedid = this.secid;
		var lis =  _proxy_jslib_handle(ul, 'getElementsByTagName', '', 1, 0)('li');
		for (var i = 0; i< lis.length; i++) {
			 _proxy_jslib_handle(lis, (i), 0, 0).className = '';
		}
		this.parentNode.className = 'selected';
	}
	return false;
}

// Timezone stuff
// tz in format [+-]HHMM
function checkTimezone(tz, msg) {
	var localclock = new (Date)();
	// returns negative offset from GMT in minutes
	var tzRaw = localclock.getTimezoneOffset();
	var tzHour = Math.floor( Math.abs(tzRaw) / 60);
	var tzMin = Math.abs(tzRaw) % 60;
	var tzString = ((tzRaw >= 0) ? "-" : "+") + ((tzHour < 10) ? "0" : "") + tzHour + ((tzMin < 10) ? "0" : "") + tzMin;
	if (tz != tzString) {
		var junk = msg.split('$1');
		 _proxy_jslib_handle(document, 'write', '', 1, 0)(junk[0] + "UTC" + tzString + junk[1]);
	}
}

function unhidetzbutton() {
	var tzb =  _proxy_jslib_handle(document, 'getElementById', '', 1, 0)('guesstimezonebutton');
	if (tzb) {
		tzb.style.display = 'inline';
	}
}

// in [-]HH:MM format...
// won't yet work with non-even tzs
function fetchTimezone() {
	// FIXME: work around Safari bug
	var localclock = new (Date)();
	// returns negative offset from GMT in minutes
	var tzRaw = localclock.getTimezoneOffset();
	var tzHour = Math.floor( Math.abs(tzRaw) / 60);
	var tzMin = Math.abs(tzRaw) % 60;
	var tzString = ((tzRaw >= 0) ? "-" : "") + ((tzHour < 10) ? "0" : "") + tzHour +
		":" + ((tzMin < 10) ? "0" : "") + tzMin;
	return tzString;
}

function guessTimezone(box) {
	 _proxy_jslib_assign('', document.getElementsByName("wpHourDiff")[0], 'value', '=', ( fetchTimezone()));
}

function showTocToggle() {
	if (document.createTextNode) {
		// Uses DOM calls to avoid document.write + XHTML issues

		var linkHolder =  _proxy_jslib_handle(document, 'getElementById', '', 1, 0)('toctitle');
		if (!linkHolder) {
			return;
		}

		var outerSpan = document.createElement('span');
		outerSpan.className = 'toctoggle';

		var toggleLink = document.createElement('a');
		toggleLink.id = 'togglelink';
		toggleLink.className = 'internal';
		 _proxy_jslib_assign('', toggleLink, 'href', '=', ( 'javascript:toggleToc()'));
		toggleLink.appendChild(document.createTextNode(tocHideText));

		outerSpan.appendChild(document.createTextNode('['));
		outerSpan.appendChild(toggleLink);
		outerSpan.appendChild(document.createTextNode(']'));

		linkHolder.appendChild(document.createTextNode(' '));
		linkHolder.appendChild(outerSpan);

		var cookiePos =  _proxy_jslib_handle(document, 'cookie', '', 0, 0).indexOf("hidetoc=");
		if (cookiePos > -1 &&  _proxy_jslib_handle(document, 'cookie', '', 0, 0).charAt(cookiePos + 8) == 1) {
			toggleToc();
		}
	}
}

function changeText(el, newText) {
	// Safari work around
	if (el.innerText) {
		el.innerText = newText;
	} else if (el.firstChild &&  _proxy_jslib_handle(el.firstChild, 'nodeValue', '', 0, 0)) {
		 _proxy_jslib_assign('', el.firstChild, 'nodeValue', '=', ( newText));
	}
}

function toggleToc() {
	var toc =  _proxy_jslib_handle( _proxy_jslib_handle(document, 'getElementById', '', 1, 0)('toc'), 'getElementsByTagName', '', 1, 0)('ul')[0];
	var toggleLink =  _proxy_jslib_handle(document, 'getElementById', '', 1, 0)('togglelink');

	if (toc && toggleLink && toc.style.display == 'none') {
		changeText(toggleLink, tocHideText);
		toc.style.display = 'block';
		 _proxy_jslib_assign('', document, 'cookie', '=', ( "hidetoc=0"));
	} else {
		changeText(toggleLink, tocShowText);
		toc.style.display = 'none';
		 _proxy_jslib_assign('', document, 'cookie', '=', ( "hidetoc=1"));
	}
}

var mwEditButtons = [];
var mwCustomEditButtons = []; // eg to add in MediaWiki:Common.js

// this function generates the actual toolbar buttons with localized text
// we use it to avoid creating the toolbar where javascript is not enabled
function addButton(imageFile, speedTip, tagOpen, tagClose, sampleText, imageId) {
	// Don't generate buttons for browsers which don't fully
	// support it.
	 _proxy_jslib_assign('', mwEditButtons, (mwEditButtons.length), '=', (
		{"imageId": imageId,
		 "imageFile": imageFile,
		 "speedTip": speedTip,
		 "tagOpen": tagOpen,
		 "tagClose": tagClose,
		 "sampleText": sampleText}));
}

// this function generates the actual toolbar buttons with localized text
// we use it to avoid creating the toolbar where javascript is not enabled
function mwInsertEditButton(parent, item) {
	var image = document.createElement("img");
	image.width = 23;
	image.height = 22;
	image.className = "mw-toolbar-editbutton";
	if (item.imageId) image.id = item.imageId;
	 _proxy_jslib_assign('', image, 'src', '=', ( item.imageFile));
	image.border = 0;
	image.alt = item.speedTip;
	image.title = item.speedTip;
	 _proxy_jslib_assign('', image.style, 'cursor', '=', ( "pointer"));
	image.onclick = function() {
		insertTags(item.tagOpen, item.tagClose, item.sampleText);
		return false;
	};

	 _proxy_jslib_handle(null, 'parent', parent, 0, 0).appendChild(image);
	return true;
}

function mwSetupToolbar() {
	var toolbar =  _proxy_jslib_handle(document, 'getElementById', '', 1, 0)('toolbar');
	if (!toolbar) { return false; }

	var textbox =  _proxy_jslib_handle(document, 'getElementById', '', 1, 0)('wpTextbox1');
	if (!textbox) { return false; }

	// Don't generate buttons for browsers which don't fully
	// support it.
	if (!(document.selection && document.selection.createRange)
 && textbox.selectionStart === null) {
		return false;
	}

	for (var i = 0; i < mwEditButtons.length; i++) {
		mwInsertEditButton(toolbar,  _proxy_jslib_handle(mwEditButtons, (i), 0, 0));
	}
	for (var i = 0; i < mwCustomEditButtons.length; i++) {
		mwInsertEditButton(toolbar,  _proxy_jslib_handle(mwCustomEditButtons, (i), 0, 0));
	}
	return true;
}

function escapeQuotes(text) {
	var re = new (RegExp)("'","g");
	text =  _proxy_jslib_handle(text, 'replace', '', 1, 0)(re,"\\'");
	re = new (RegExp)("\\n","g");
	text =  _proxy_jslib_handle(text, 'replace', '', 1, 0)(re,"\\n");
	return escapeQuotesHTML(text);
}

function escapeQuotesHTML(text) {
	var re = new (RegExp)('&',"g");
	text =  _proxy_jslib_handle(text, 'replace', '', 1, 0)(re,"&amp;");
	re = new (RegExp)('"',"g");
	text =  _proxy_jslib_handle(text, 'replace', '', 1, 0)(re,"&quot;");
	re = new (RegExp)('<',"g");
	text =  _proxy_jslib_handle(text, 'replace', '', 1, 0)(re,"&lt;");
	re = new (RegExp)('>',"g");
	text =  _proxy_jslib_handle(text, 'replace', '', 1, 0)(re,"&gt;");
	return text;
}

// apply tagOpen/tagClose to selection in textarea,
// use sampleText instead of selection if there is none
function insertTags(tagOpen, tagClose, sampleText) {
	var txtarea;
	if (document.editform) {
		txtarea = document.editform.wpTextbox1;
	} else {
		// some alternate form? take the first one we can find
		var areas =  _proxy_jslib_handle(document, 'getElementsByTagName', '', 1, 0)('textarea');
		txtarea = areas[0];
	}
	var selText, isSample = false;

	if (document.selection && document.selection.createRange) { // IE/Opera

		//save window scroll position
		if (document.documentElement && document.documentElement.scrollTop)
			var winScroll = document.documentElement.scrollTop
		else if ( _proxy_jslib_handle(document, 'body', '', 0, 0))
			var winScroll =  _proxy_jslib_handle(document, 'body', '', 0, 0).scrollTop;
		//get current selection  
		txtarea.focus();
		var range = document.selection.createRange();
		selText = range.text;
		//insert tags
		checkSelectedText();
		range.text = tagOpen + selText + tagClose;
		//mark sample text as selected
		if (isSample && range.moveStart) {
			if (window.opera)
				tagClose =  _proxy_jslib_handle(tagClose, 'replace', '', 1, 0)(/\n/g,'');
			range.moveStart('character', - tagClose.length - selText.length); 
			range.moveEnd('character', - tagClose.length); 
		}
		range.select();   
		//restore window scroll position
		if (document.documentElement && document.documentElement.scrollTop)
			document.documentElement.scrollTop = winScroll
 else if ( _proxy_jslib_handle(document, 'body', '', 0, 0))
			 _proxy_jslib_handle(document, 'body', '', 0, 0).scrollTop = winScroll;

	} else if (txtarea.selectionStart || txtarea.selectionStart == '0') { // Mozilla

		//save textarea scroll position
		var textScroll = txtarea.scrollTop;
		//get current selection
		txtarea.focus();
		var startPos = txtarea.selectionStart;
		var endPos = txtarea.selectionEnd;
		selText =  _proxy_jslib_handle(txtarea, 'value', '', 0, 0).substring(startPos, endPos);
		//insert tags
		checkSelectedText();
		 _proxy_jslib_assign('', txtarea, 'value', '=', (  _proxy_jslib_handle(txtarea, 'value', '', 0, 0).substring(0, startPos)
 + tagOpen + selText + tagClose
 +  _proxy_jslib_handle(txtarea, 'value', '', 0, 0).substring(endPos,  _proxy_jslib_handle(txtarea, 'value', '', 0, 0).length)));
		//set new selection
		if (isSample) {
			txtarea.selectionStart = startPos + tagOpen.length;
			txtarea.selectionEnd = startPos + tagOpen.length + selText.length;
		} else {
			txtarea.selectionStart = startPos + tagOpen.length + selText.length + tagClose.length;
			txtarea.selectionEnd = txtarea.selectionStart;
		}
		//restore textarea scroll position
		txtarea.scrollTop = textScroll;
	} 

	function checkSelectedText() {
		if (!selText) {
			selText = sampleText;
			isSample = true;
		} else if (selText.charAt(selText.length - 1) == ' ') { //exclude ending space char
			selText = selText.substring(0, selText.length - 1);
			tagClose += ' '
 } 
	}

}


/**
 * Set the accesskey prefix based on browser detection.
 */
var tooltipAccessKeyPrefix = 'alt-';
if (is_opera) {
	tooltipAccessKeyPrefix = 'shift-esc-';
} else if (is_safari
 || navigator.userAgent.toLowerCase().indexOf('mac') != -1
 || navigator.userAgent.toLowerCase().indexOf('konqueror') != -1 ) {
	tooltipAccessKeyPrefix = 'ctrl-';
} else if (is_ff2_x11 || is_ff2_win) {
	tooltipAccessKeyPrefix = 'alt-shift-';
}
var tooltipAccessKeyRegexp = /\[(ctrl-)?(alt-)?(shift-)?(esc-)?.\]$/;

/**
 * Add the appropriate prefix to the accesskey shown in the tooltip.
 * If the nodeList parameter is given, only those nodes are updated;
 * otherwise, all the nodes that will probably have accesskeys by
 * default are updated.
 *
 * @param Array nodeList -- list of elements to update
 */
function updateTooltipAccessKeys( nodeList ) {
	if ( !nodeList ) {
		// skins without a "column-one" element don't seem to have links with accesskeys either
		var columnOne =  _proxy_jslib_handle(document, 'getElementById', '', 1, 0)("column-one");
		if ( columnOne )
			updateTooltipAccessKeys(  _proxy_jslib_handle(columnOne, 'getElementsByTagName', '', 1, 0)("a") );
		// these are rare enough that no such optimization is needed
		updateTooltipAccessKeys(  _proxy_jslib_handle(document, 'getElementsByTagName', '', 1, 0)("input") );
		updateTooltipAccessKeys(  _proxy_jslib_handle(document, 'getElementsByTagName', '', 1, 0)("label") );
		return;
	}

	for ( var i = 0; i < nodeList.length; i++ ) {
		var element =  _proxy_jslib_handle(nodeList, (i), 0, 0);
		var tip = element.getAttribute("title");
		var key = element.getAttribute("accesskey");
		if ( key && tooltipAccessKeyRegexp.exec(tip) ) {
			tip =  _proxy_jslib_handle(tip, 'replace', '', 1, 0)(tooltipAccessKeyRegexp,
					  "["+tooltipAccessKeyPrefix+key+"]");
			 _proxy_jslib_handle(element, 'setAttribute', '', 1, 0)("title", tip );
		}
	}
}

/**
 * Add a link to one of the portlet menus on the page, including:
 *
 * p-cactions: Content actions (shown as tabs above the main content in Monobook)
 * p-personal: Personal tools (shown at the top right of the page in Monobook)
 * p-navigation: Navigation
 * p-tb: Toolbox
 *
 * This function exists for the convenience of custom JS authors.  All
 * but the first three parameters are optional, though providing at
 * least an id and a tooltip is recommended.
 *
 * By default the new link will be added to the end of the list.  To
 * add the link before a given existing item, pass the DOM node of
 * that item (easily obtained with document.getElementById()) as the
 * nextnode parameter; to add the link _after_ an existing item, pass
 * the node's nextSibling instead.
 *
 * @param String portlet -- id of the target portlet ("p-cactions", "p-personal", "p-navigation" or "p-tb")
 * @param String href -- link URL
 * @param String text -- link text (will be automatically lowercased by CSS for p-cactions in Monobook)
 * @param String id -- id of the new item, should be unique and preferably have the appropriate prefix ("ca-", "pt-", "n-" or "t-")
 * @param String tooltip -- text to show when hovering over the link, without accesskey suffix
 * @param String accesskey -- accesskey to activate this link (one character, try to avoid conflicts)
 * @param Node nextnode -- the DOM node before which the new item should be added, should be another item in the same list
 *
 * @return Node -- the DOM node of the new item (an LI element) or null
 */
function addPortletLink(portlet, href, text, id, tooltip, accesskey, nextnode) {
	var node =  _proxy_jslib_handle(document, 'getElementById', '', 1, 0)(portlet);
	if ( !node ) return null;
	node =  _proxy_jslib_handle(node, 'getElementsByTagName', '', 1, 0)( "ul" )[0];
	if ( !node ) return null;

	var link = document.createElement( "a" );
	link.appendChild( document.createTextNode( text ) );
	 _proxy_jslib_assign('', link, 'href', '=', (  _proxy_jslib_handle(null, 'href', href, 0, 0)));

	var item = document.createElement( "li" );
	item.appendChild( link );
	if ( id ) item.id = id;

	if ( accesskey ) {
		 _proxy_jslib_handle(link, 'setAttribute', '', 1, 0)( "accesskey", accesskey );
		tooltip += " ["+accesskey+"]";
	}
	if ( tooltip ) {
		 _proxy_jslib_handle(link, 'setAttribute', '', 1, 0)( "title", tooltip );
	}
	if ( accesskey && tooltip ) {
		updateTooltipAccessKeys( new (Array)( link ) );
	}

	if ( nextnode && nextnode.parentNode == node )
		node.insertBefore( item, nextnode );
	else
		node.appendChild( item );  // IE compatibility (?)

	return item;
}


/**
 * Set up accesskeys/tooltips from the deprecated ta array.  If doId
 * is specified, only set up for that id.  Note that this function is
 * deprecated and will not be supported indefinitely -- use
 * updateTooltipAccessKey() instead.
 *
 * @param mixed doId string or null
 */
function akeytt( doId ) {
	// A lot of user scripts (and some of the code below) break if
	// ta isn't defined, so we make sure it is.  Explictly using
	// window.ta avoids a "ta is not defined" error.
	if (!window.ta) window.ta = new (Array);

	// Make a local, possibly restricted, copy to avoid clobbering
	// the original.
	var ta;
	if ( doId ) {
		ta = [doId];
	} else {
		ta = window.ta;
	}

	// Now deal with evil deprecated ta
	var watchCheckboxExists =  _proxy_jslib_handle(document, 'getElementById', '', 1, 0)( 'wpWatchthis' ) ? true : false;
	for (var id in ta) {
		var n =  _proxy_jslib_handle(document, 'getElementById', '', 1, 0)(id);
		if (n) {
			var a = null;
			var ak = '';
			// Are we putting accesskey in it
			if ( _proxy_jslib_handle(ta, (id), 0, 0)[0].length > 0) {
				// Is this object a object? If not assume it's the next child.

				if (n.nodeName.toLowerCase() == "a") {
					a = n;
				} else {
					a = n.childNodes[0];
				}
			 	// Don't add an accesskey for the watch tab if the watch
			 	// checkbox is also available.
				if (a && ((id != 'ca-watch' && id != 'ca-unwatch') || !watchCheckboxExists)) {
					a.accessKey =  _proxy_jslib_handle(ta, (id), 0, 0)[0];
					ak = ' ['+tooltipAccessKeyPrefix+ _proxy_jslib_handle(ta, (id), 0, 0)[0]+']';
				}
			} else {
				// We don't care what type the object is when assigning tooltip
				a = n;
				ak = '';
			}

			if (a) {
				a.title =  _proxy_jslib_handle(ta, (id), 0, 0)[1]+ak;
			}
		}
	}
}

function setupRightClickEdit() {
	if ( _proxy_jslib_handle(document, 'getElementsByTagName', '', 0, 0)) {
		var spans =  _proxy_jslib_handle(document, 'getElementsByTagName', '', 1, 0)('span');
		for (var i = 0; i < spans.length; i++) {
			var el =  _proxy_jslib_handle(spans, (i), 0, 0);
			if(el.className == 'editsection') {
				addRightClickEditHandler(el);
			}
		}
	}
}

function addRightClickEditHandler(el) {
	for (var i = 0; i < el.childNodes.length; i++) {
		var link =  _proxy_jslib_handle(el.childNodes, (i), 0, 0);
		if (link.nodeType == 1 && link.nodeName.toLowerCase() == 'a') {
			var editHref = link.getAttribute('href');
			// find the enclosing (parent) header
			var prev = el.parentNode;
			if (prev && prev.nodeType == 1 &&
			prev.nodeName.match(/^[Hh][1-6]$/)) {
				prev.oncontextmenu = function(e) {
					if (!e) { e = window.event; }
					// e is now the event in all browsers
					var targ;
					if (e.target) { targ = e.target; }
					else if (e.srcElement) { targ = e.srcElement; }
					if (targ.nodeType == 3) { // defeat Safari bug
						targ = targ.parentNode;
					}
					// targ is now the target element

					// We don't want to deprive the noble reader of a context menu
					// for the section edit link, do we?  (Might want to extend this
					// to all <a>'s?)
					if (targ.nodeName.toLowerCase() != 'a'
 || targ.parentNode.className != 'editsection') {
						 _proxy_jslib_assign('', document, 'location', '=', ( editHref));
						return false;
					}
					return true;
				};
			}
		}
	}
}

var checkboxes;
var lastCheckbox;

function setupCheckboxShiftClick() {
	checkboxes = [];
	lastCheckbox = null;
	var inputs =  _proxy_jslib_handle(document, 'getElementsByTagName', '', 1, 0)('input');
	addCheckboxClickHandlers(inputs);
}

function addCheckboxClickHandlers(inputs, start) {
	if ( !start) start = 0;

	var finish = start + 250;
	if ( finish > inputs.length )
		finish = inputs.length;

	for ( var i = start; i < finish; i++ ) {
		var cb =  _proxy_jslib_handle(inputs, (i), 0, 0);
		if ( !cb.type || cb.type.toLowerCase() != 'checkbox' )
			continue;
		var end = checkboxes.length;
		 _proxy_jslib_assign('', checkboxes, (end), '=', ( cb));
		cb.index = end;
		cb.onclick = checkboxClickHandler;
	}

	if ( finish < inputs.length ) {
		 _proxy_jslib_handle(null, 'setTimeout', setTimeout, 1, 0)( function () {
			addCheckboxClickHandlers(inputs, finish);
		}, 200 );
	}
}

function checkboxClickHandler(e) {
	if (typeof e == 'undefined') {
		e = window.event;
	}
	if ( !e.shiftKey || lastCheckbox === null ) {
		lastCheckbox = this.index;
		return true;
	}
	var endState = this.checked;
	var start, finish;
	if ( this.index < lastCheckbox ) {
		start = this.index + 1;
		finish = lastCheckbox;
	} else {
		start = lastCheckbox;
		finish = this.index - 1;
	}
	for (var i = start; i <= finish; (i= _proxy_jslib_assign_rval('++', 'i', '', '', i)) ) {
		 _proxy_jslib_handle(checkboxes, (i), 0, 0).checked = endState;
	}
	lastCheckbox = this.index;
	return true;
}

function toggle_element_activation(ida,idb) {
	if (! _proxy_jslib_handle(document, 'getElementById', '', 0, 0)) {
		return;
	}
	 _proxy_jslib_handle(document, 'getElementById', '', 1, 0)(ida).disabled=true;
	 _proxy_jslib_handle(document, 'getElementById', '', 1, 0)(idb).disabled=false;
}

function toggle_element_check(ida,idb) {
	if (! _proxy_jslib_handle(document, 'getElementById', '', 0, 0)) {
		return;
	}
	 _proxy_jslib_handle(document, 'getElementById', '', 1, 0)(ida).checked=true;
	 _proxy_jslib_handle(document, 'getElementById', '', 1, 0)(idb).checked=false;
}

/**
 * Restore the edit box scroll state following a preview operation,
 * and set up a form submission handler to remember this state
 */
function scrollEditBox() {
	var editBox =  _proxy_jslib_handle(document, 'getElementById', '', 1, 0)( 'wpTextbox1' );
	var scrollTop =  _proxy_jslib_handle(document, 'getElementById', '', 1, 0)( 'wpScrolltop' );
	var editForm =  _proxy_jslib_handle(document, 'getElementById', '', 1, 0)( 'editform' );
	if( editBox && scrollTop ) {
		if(  _proxy_jslib_handle(scrollTop, 'value', '', 0, 0) )
			editBox.scrollTop =  _proxy_jslib_handle(scrollTop, 'value', '', 0, 0);
		addHandler( editForm, 'submit', function() {
			 _proxy_jslib_assign('',  _proxy_jslib_handle(document, 'getElementById', '', 1, 0)( 'wpScrolltop' ), 'value', '=', (  _proxy_jslib_handle(document, 'getElementById', '', 1, 0)( 'wpTextbox1' ).scrollTop)); 
		} );
	}
}
hookEvent( 'load', scrollEditBox );

var allmessages_nodelist = false;
var allmessages_modified = false;
var allmessages_timeout = false;
var allmessages_running = false;

function allmessagesmodified() {
	allmessages_modified = !allmessages_modified;
	allmessagesfilter();
}

function allmessagesfilter() {
	if ( allmessages_timeout )
		window.clearTimeout( allmessages_timeout );

	if ( !allmessages_running )
		allmessages_timeout =  _proxy_jslib_handle(window, 'setTimeout', '', 1, 0)( 'allmessagesfilter_do();', 500 );
}

function allmessagesfilter_do() {
	if ( !allmessages_nodelist )
		return;

	var text =  _proxy_jslib_handle( _proxy_jslib_handle(document, 'getElementById', '', 1, 0)('allmessagesinput'), 'value', '', 0, 0);
	var nodef = allmessages_modified;

	allmessages_running = true;

	for ( var name in allmessages_nodelist ) {
		var nodes =  _proxy_jslib_handle(allmessages_nodelist, (name), 0, 0);
		var display = ( name.indexOf( text ) == -1 ? 'none' : '' );

		for ( var i = 0; i < nodes.length; i++)
			 _proxy_jslib_handle(nodes, (i), 0, 0).style.display =
				(  _proxy_jslib_handle(nodes, (i), 0, 0).className == "def" && nodef
 ? 'none' : display );
	}

	if ( text !=  _proxy_jslib_handle( _proxy_jslib_handle(document, 'getElementById', '', 1, 0)('allmessagesinput'), 'value', '', 0, 0) ||
	     nodef != allmessages_modified )
		allmessagesfilter_do();  // repeat

	allmessages_running = false;
}

function allmessagesfilter_init() {
	if ( allmessages_nodelist )
		return;

	var nodelist = new (Array)();
	var templist = new (Array)();

	var table =  _proxy_jslib_handle(document, 'getElementById', '', 1, 0)('allmessagestable');
	if ( !table ) return;

	var rows =  _proxy_jslib_handle(document, 'getElementsByTagName', '', 1, 0)('tr');
	for ( var i = 0; i < rows.length; i++ ) {
		var id =  _proxy_jslib_handle(rows, (i), 0, 0).getAttribute('id')
		if ( id && id.substring(0,16) != 'sp-allmessages-r' ) continue;
		 _proxy_jslib_assign('', templist, ( id ), '=', (  _proxy_jslib_handle(rows, (i), 0, 0)));
	}

	var spans =  _proxy_jslib_handle(table, 'getElementsByTagName', '', 1, 0)('span');
	for ( var i = 0; i < spans.length; i++ ) {
		var id =  _proxy_jslib_handle(spans, (i), 0, 0).getAttribute('id')
		if ( id && id.substring(0,17) != 'sp-allmessages-i-' ) continue;
		if ( ! _proxy_jslib_handle(spans, (i), 0, 0).firstChild ||  _proxy_jslib_handle(spans, (i), 0, 0).firstChild.nodeType != 3 ) continue;

		var nodes = new (Array)();
		var row1 =  _proxy_jslib_handle(templist, (  _proxy_jslib_handle(id, 'replace', '', 1, 0)('i', 'r1') ), 0, 0);
		var row2 =  _proxy_jslib_handle(templist, (  _proxy_jslib_handle(id, 'replace', '', 1, 0)('i', 'r2') ), 0, 0);

		if ( row1 )  _proxy_jslib_assign('', nodes, (nodes.length), '=', ( row1));
		if ( row2 )  _proxy_jslib_assign('', nodes, (nodes.length), '=', ( row2));
		 _proxy_jslib_assign('', nodelist, (  _proxy_jslib_handle( _proxy_jslib_handle(spans, (i), 0, 0).firstChild, 'nodeValue', '', 0, 0) ), '=', ( nodes));
	}

	var k =  _proxy_jslib_handle(document, 'getElementById', '', 1, 0)('allmessagesfilter');
	if (k) { k.style.display = ''; }

	allmessages_nodelist = nodelist;
}

hookEvent( "load", allmessagesfilter_init );

/*
	Written by Jonathan Snook, http://www.snook.ca/jonathan
	Add-ons by Robert Nyman, http://www.robertnyman.com
	Author says "The credit comment is all it takes, no license. Go crazy with it!:-)"
	From http://www.robertnyman.com/2005/11/07/the-ultimate-getelementsbyclassname/
*/
function getElementsByClassName(oElm, strTagName, oClassNames) {
	var arrElements = (strTagName == "*" && oElm.all)? oElm.all :  _proxy_jslib_handle(oElm, 'getElementsByTagName', '', 1, 0)(strTagName);
	var arrReturnElements = new (Array)();
	var arrRegExpClassNames = new (Array)();
	if(typeof oClassNames == "object"){
		for(var i=0; i<oClassNames.length; i++){
			 _proxy_jslib_assign('', arrRegExpClassNames, (arrRegExpClassNames.length), '=', (
				new (RegExp)("(^|\\s)" +  _proxy_jslib_handle( _proxy_jslib_handle(oClassNames, (i), 0, 0), 'replace', '', 1, 0)(/\-/g, "\\-") + "(\\s|$)")));
		}
	}
	else{
		 _proxy_jslib_assign('', arrRegExpClassNames, (arrRegExpClassNames.length), '=', (
			new (RegExp)("(^|\\s)" +  _proxy_jslib_handle(oClassNames, 'replace', '', 1, 0)(/\-/g, "\\-") + "(\\s|$)")));
	}
	var oElement;
	var bMatchesAll;
	for(var j=0; j<arrElements.length; j++){
		oElement =  _proxy_jslib_handle(arrElements, (j), 0, 0);
		bMatchesAll = true;
		for(var k=0; k<arrRegExpClassNames.length; k++){
			if(! _proxy_jslib_handle(arrRegExpClassNames, (k), 0, 0).test(oElement.className)){
				bMatchesAll = false;
				break;
			}
		}
		if(bMatchesAll){
			 _proxy_jslib_assign('', arrReturnElements, (arrReturnElements.length), '=', ( oElement));
		}
	}
	return (arrReturnElements)
}

function redirectToFragment(fragment) {
	var match = navigator.userAgent.match(/AppleWebKit\/(\d+)/);
	if (match) {
		var webKitVersion = parseInt(match[1]);
		if (webKitVersion < 420) {
			// Released Safari w/ WebKit 418.9.1 messes up horribly
			// Nightlies of 420+ are ok
			return;
		}
	}
	if (is_gecko) {
		// Mozilla needs to wait until after load, otherwise the window doesn't scroll
		addOnloadHook(function () {
			if ( _proxy_jslib_handle(window, 'location', '', 0, 0).hash == "")
				 _proxy_jslib_handle(window, 'location', '', 0, 0).hash = fragment;
		});
	} else {
		if ( _proxy_jslib_handle(window, 'location', '', 0, 0).hash == "")
			 _proxy_jslib_handle(window, 'location', '', 0, 0).hash = fragment;
	}
}

/*
 * Table sorting script  by Joost de Valk, check it out at http://www.joostdevalk.nl/code/sortable-table/.
 * Based on a script from http://www.kryogenix.org/code/browser/sorttable/.
 * Distributed under the MIT license: http://www.kryogenix.org/code/browser/licence.html .
 *
 * Copyright (c) 1997-2006 Stuart Langridge, Joost de Valk.
 *
 * @todo don't break on colspans/rowspans (bug 8028)
 * @todo language-specific digit grouping/decimals (bug 8063)
 * @todo support all accepted date formats (bug 8226)
 */

var ts_image_path = stylepath+"/common/images/";
var ts_image_up = "sort_up.gif";
var ts_image_down = "sort_down.gif";
var ts_image_none = "sort_none.gif";
var ts_europeandate = wgContentLanguage != "en"; // The non-American-inclined can change to "true"
var ts_alternate_row_colors = true;
var SORT_COLUMN_INDEX;

function sortables_init() {
	var idnum = 0;
	// Find all tables with class sortable and make them sortable
	var tables = getElementsByClassName(document, "table", "sortable");
	for (var ti = 0; ti < tables.length ; ti++) {
		if (! _proxy_jslib_handle(tables, (ti), 0, 0).id) {
			 _proxy_jslib_handle( _proxy_jslib_handle(tables, (ti), 0, 0), 'setAttribute', '', 1, 0)('id','sortable_table_id_'+idnum);
			(idnum= _proxy_jslib_assign_rval('++', 'idnum', '', '', idnum));
		}
		ts_makeSortable( _proxy_jslib_handle(tables, (ti), 0, 0));
	}
}

function ts_makeSortable(table) {
	var firstRow;
	if (table.rows && table.rows.length > 0) {
		if (table.tHead && table.tHead.rows.length > 0) {
			firstRow =  _proxy_jslib_handle(table.tHead.rows, (table.tHead.rows.length-1), 0, 0);
		} else {
			firstRow = table.rows[0];
		}
	}
	if (!firstRow) return;

	// We have a first row: assume it's the header, and make its contents clickable links
	for (var i = 0; i < firstRow.cells.length; i++) {
		var cell =  _proxy_jslib_handle(firstRow.cells, (i), 0, 0);
		if ((" "+cell.className+" ").indexOf(" unsortable ") == -1) {
			 _proxy_jslib_assign('', cell, 'innerHTML', '+=', ( '&nbsp;&nbsp;<a href="#" class="sortheader" onclick="ts_resortTable(this);return false;"><span class="sortarrow"><img src="'+ ts_image_path + ts_image_none + '" alt="&darr;"/></span></a>'));
		}
	}
	if (ts_alternate_row_colors) {
		ts_alternate(table);
	}
}

function ts_getInnerText(el) {
	if (typeof el == "string") return el;
	if (typeof el == "undefined") { return el };
	if (el.textContent) return el.textContent; // not needed but it is faster
	if (el.innerText) return el.innerText;     // IE doesn't have textContent
	var str = "";

	var cs = el.childNodes;
	var l = cs.length;
	for (var i = 0; i < l; i++) {
		switch ( _proxy_jslib_handle(cs, (i), 0, 0).nodeType) {
			case 1: //ELEMENT_NODE
				str += ts_getInnerText( _proxy_jslib_handle(cs, (i), 0, 0));
				break;
			case 3:	//TEXT_NODE
				str +=  _proxy_jslib_handle( _proxy_jslib_handle(cs, (i), 0, 0), 'nodeValue', '', 0, 0);
				break;
		}
	}
	return str;
}

function ts_resortTable(lnk) {
	// get the span
	var span =  _proxy_jslib_handle(lnk, 'getElementsByTagName', '', 1, 0)('span')[0];

	var td = lnk.parentNode;
	var tr = td.parentNode;
	var column = td.cellIndex;

	var table = tr.parentNode;
	while (table && !(table.tagName && table.tagName.toLowerCase() == 'table'))
		table = table.parentNode;
	if (!table) return;

	// Work out a type for the column
	if (table.rows.length <= 1) return;

	// Skip the first row if that's where the headings are
	var rowStart = (table.tHead && table.tHead.rows.length > 0 ? 0 : 1);

	var itm = "";
	for (var i = rowStart; i < table.rows.length; i++) {
		if ( _proxy_jslib_handle(table.rows, (i), 0, 0).cells.length > column) {
			itm = ts_getInnerText( _proxy_jslib_handle( _proxy_jslib_handle(table.rows, (i), 0, 0).cells, (column), 0, 0));
			itm =  _proxy_jslib_handle( _proxy_jslib_handle(itm, 'replace', '', 1, 0)(/^[\s\xa0]+/, ""), 'replace', '', 1, 0)(/[\s\xa0]+$/, "");
			if (itm != "") break;
		}
	}

	sortfn = ts_sort_caseinsensitive;
	if (itm.match(/^\d\d[\/. -][a-zA-Z]{3}[\/. -]\d\d\d\d$/))
		sortfn = ts_sort_date;
	if (itm.match(/^\d\d[\/.-]\d\d[\/.-]\d\d\d\d$/))
		sortfn = ts_sort_date;
	if (itm.match(/^\d\d[\/.-]\d\d[\/.-]\d\d$/))
		sortfn = ts_sort_date;
	if (itm.match(/^[\u00a3$\u20ac]/)) // pound dollar euro
		sortfn = ts_sort_currency;
	if (itm.match(/^[\d.,]+\%?$/))
		sortfn = ts_sort_numeric;

	var reverse = (span.getAttribute("sortdir") == 'down');

	var newRows = new (Array)();
	for (var j = rowStart; j < table.rows.length; j++) {
		var row =  _proxy_jslib_handle(table.rows, (j), 0, 0);
		var keyText = ts_getInnerText( _proxy_jslib_handle(row.cells, (column), 0, 0));
		var oldIndex = (reverse ? -j : j);

		 _proxy_jslib_assign('', newRows, (newRows.length), '=', ( new (Array)(row, keyText, oldIndex)));
	}

	newRows.sort(sortfn);

	var arrowHTML;
	if (reverse) {
			arrowHTML = '<img src="'+ ts_image_path + ts_image_down + '" alt="&darr;"/>';
			newRows.reverse();
			 _proxy_jslib_handle(span, 'setAttribute', '', 1, 0)('sortdir','up');
	} else {
			arrowHTML = '<img src="'+ ts_image_path + ts_image_up + '" alt="&uarr;"/>';
			 _proxy_jslib_handle(span, 'setAttribute', '', 1, 0)('sortdir','down');
	}

	// We appendChild rows that already exist to the tbody, so it moves them rather than creating new ones
	// don't do sortbottom rows
	for (var i = 0; i < newRows.length; i++) {
		if ((" "+ _proxy_jslib_handle(newRows, (i), 0, 0)[0].className+" ").indexOf(" sortbottom ") == -1)
			table.tBodies[0].appendChild( _proxy_jslib_handle(newRows, (i), 0, 0)[0]);
	}
	// do sortbottom rows only
	for (var i = 0; i < newRows.length; i++) {
		if ((" "+ _proxy_jslib_handle(newRows, (i), 0, 0)[0].className+" ").indexOf(" sortbottom ") != -1)
			table.tBodies[0].appendChild( _proxy_jslib_handle(newRows, (i), 0, 0)[0]);
	}

	// Delete any other arrows there may be showing
	var spans = getElementsByClassName(tr, "span", "sortarrow");
	for (var i = 0; i < spans.length; i++) {
		 _proxy_jslib_assign('',  _proxy_jslib_handle(spans, (i), 0, 0), 'innerHTML', '=', ( '<img src="'+ ts_image_path + ts_image_none + '" alt="&darr;"/>'));
	}
	 _proxy_jslib_assign('', span, 'innerHTML', '=', ( arrowHTML));

	ts_alternate(table);		
}

function ts_dateToSortKey(date) {	
	// y2k notes: two digit years less than 50 are treated as 20XX, greater than 50 are treated as 19XX
	if (date.length == 11) {
		switch (date.substr(3,3).toLowerCase()) {
			case "jan": var month = "01"; break;
			case "feb": var month = "02"; break;
			case "mar": var month = "03"; break;
			case "apr": var month = "04"; break;
			case "may": var month = "05"; break;
			case "jun": var month = "06"; break;
			case "jul": var month = "07"; break;
			case "aug": var month = "08"; break;
			case "sep": var month = "09"; break;
			case "oct": var month = "10"; break;
			case "nov": var month = "11"; break;
			case "dec": var month = "12"; break;
			// default: var month = "00";
		}
		return date.substr(7,4)+month+date.substr(0,2);
	} else if (date.length == 10) {
		if (ts_europeandate == false) {
			return date.substr(6,4)+date.substr(0,2)+date.substr(3,2);
		} else {
			return date.substr(6,4)+date.substr(3,2)+date.substr(0,2);
		}
	} else if (date.length == 8) {
		yr = date.substr(6,2);
		if (parseInt(yr) < 50) { 
			yr = '20'+yr; 
		} else { 
			yr = '19'+yr; 
		}
		if (ts_europeandate == true) {
			return yr+date.substr(3,2)+date.substr(0,2);
		} else {
			return yr+date.substr(0,2)+date.substr(3,2);
		}
	}
	return "00000000";
}

function ts_parseFloat(num) {
	if (!num) return 0;
	num = parseFloat( _proxy_jslib_handle(num, 'replace', '', 1, 0)(/,/, ""));
	return (isNaN(num) ? 0 : num);
}

function ts_sort_date(a,b) {
	var aa = ts_dateToSortKey(a[1]);
	var bb = ts_dateToSortKey(b[1]);
	return (aa < bb ? -1 : aa > bb ? 1 : a[2] - b[2]);
}

function ts_sort_currency(a,b) {
	var aa = ts_parseFloat( _proxy_jslib_handle(a[1], 'replace', '', 1, 0)(/[^0-9.]/g,''));
	var bb = ts_parseFloat( _proxy_jslib_handle(b[1], 'replace', '', 1, 0)(/[^0-9.]/g,''));
	return (aa != bb ? aa - bb : a[2] - b[2]);
}

function ts_sort_numeric(a,b) {
	var aa = ts_parseFloat(a[1]);
	var bb = ts_parseFloat(b[1]);
	return (aa != bb ? aa - bb : a[2] - b[2]);
}

function ts_sort_caseinsensitive(a,b) {
	var aa = a[1].toLowerCase();
	var bb = b[1].toLowerCase();
	return (aa < bb ? -1 : aa > bb ? 1 : a[2] - b[2]);
}

function ts_sort_default(a,b) {
	return (a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : a[2] - b[2]);
}

function ts_alternate(table) {
	// Take object table and get all it's tbodies.
	var tableBodies =  _proxy_jslib_handle(table, 'getElementsByTagName', '', 1, 0)("tbody");
	// Loop through these tbodies
	for (var i = 0; i < tableBodies.length; i++) {
		// Take the tbody, and get all it's rows
		var tableRows =  _proxy_jslib_handle( _proxy_jslib_handle(tableBodies, (i), 0, 0), 'getElementsByTagName', '', 1, 0)("tr");
		// Loop through these rows
		// Start at 1 because we want to leave the heading row untouched
		for (var j = 0; j < tableRows.length; j++) {
			// Check if j is even, and apply classes for both possible results
			var oldClasses =  _proxy_jslib_handle(tableRows, (j), 0, 0).className.split(" ");
			var newClassName = "";
			for (var k = 0; k < oldClasses.length; k++) {
				if ( _proxy_jslib_handle(oldClasses, (k), 0, 0) != "" &&  _proxy_jslib_handle(oldClasses, (k), 0, 0) != "even" &&  _proxy_jslib_handle(oldClasses, (k), 0, 0) != "odd")
					newClassName +=  _proxy_jslib_handle(oldClasses, (k), 0, 0) + " ";
			}
			 _proxy_jslib_handle(tableRows, (j), 0, 0).className = newClassName + (j % 2 == 0 ? "even" : "odd");
		}
	}
}

/*
 * End of table sorting code
 */
 
 
/**
 * Add a cute little box at the top of the screen to inform the user of
 * something, replacing any preexisting message.
 *
 * @param String message HTML to be put inside the right div
 * @param String className   Used in adding a class; should be different for each
 *   call to allow CSS/JS to hide different boxes.  null = no class used.
 * @return Boolean       True on success, false on failure
 */
function jsMsg( message, className ) {
	if ( ! _proxy_jslib_handle(document, 'getElementById', '', 0, 0) ) {
		return false;
	}
	// We special-case skin structures provided by the software.  Skins that
	// choose to abandon or significantly modify our formatting can just define
	// an mw-js-message div to start with.
	var messageDiv =  _proxy_jslib_handle(document, 'getElementById', '', 1, 0)( 'mw-js-message' );
	if ( !messageDiv ) {
		messageDiv = document.createElement( 'div' );
		if (  _proxy_jslib_handle(document, 'getElementById', '', 1, 0)( 'column-content' )
 &&  _proxy_jslib_handle(document, 'getElementById', '', 1, 0)( 'content' ) ) {
			// MonoBook, presumably
			 _proxy_jslib_handle(document, 'getElementById', '', 1, 0)( 'content' ).insertBefore(
				messageDiv,
				 _proxy_jslib_handle(document, 'getElementById', '', 1, 0)( 'content' ).firstChild
 );
		} else if (  _proxy_jslib_handle(document, 'getElementById', '', 1, 0)('content')
 &&  _proxy_jslib_handle(document, 'getElementById', '', 1, 0)( 'article' ) ) {
			// Non-Monobook but still recognizable (old-style)
			 _proxy_jslib_handle(document, 'getElementById', '', 1, 0)( 'article').insertBefore(
				messageDiv,
				 _proxy_jslib_handle(document, 'getElementById', '', 1, 0)( 'article' ).firstChild
 );
		} else {
			return false;
		}
	}

	 _proxy_jslib_handle(messageDiv, 'setAttribute', '', 1, 0)( 'id', 'mw-js-message' );
	if( className ) {
		 _proxy_jslib_handle(messageDiv, 'setAttribute', '', 1, 0)( 'class', 'mw-js-message-'+className );
	}
	 _proxy_jslib_assign('', messageDiv, 'innerHTML', '=', ( message));
	return true;
}

/**
 * Inject a cute little progress spinner after the specified element
 *
 * @param element Element to inject after
 * @param id Identifier string (for use with removeSpinner(), below)
 */
function injectSpinner( element, id ) {
	var spinner = document.createElement( "img" );
	spinner.id = "mw-spinner-" + id;
	 _proxy_jslib_assign('', spinner, 'src', '=', ( stylepath + "/common/images/spinner.gif"));
	spinner.alt = spinner.title = "...";
	if( element.nextSibling ) {
		element.parentNode.insertBefore( spinner, element.nextSibling );
	} else {
		element.parentNode.appendChild( spinner );
	}
}

/**
 * Remove a progress spinner added with injectSpinner()
 *
 * @param id Identifier string
 */
function removeSpinner( id ) {
	var spinner =  _proxy_jslib_handle(document, 'getElementById', '', 1, 0)( "mw-spinner-" + id );
	if( spinner ) {
		spinner.parentNode.removeChild( spinner );
	}
}

function runOnloadHook() {
	// don't run anything below this for non-dom browsers
	if (doneOnloadHook || !( _proxy_jslib_handle(document, 'getElementById', '', 0, 0) &&  _proxy_jslib_handle(document, 'getElementsByTagName', '', 0, 0))) {
		return;
	}

	// set this before running any hooks, since any errors below
	// might cause the function to terminate prematurely
	doneOnloadHook = true;

	histrowinit();
	unhidetzbutton();
	tabbedprefs();
	updateTooltipAccessKeys( null );
	akeytt( null );
	scrollEditBox();
	setupCheckboxShiftClick();
	sortables_init();

	// Run any added-on functions
	for (var i = 0; i < onloadFuncts.length; i++) {
		 _proxy_jslib_handle(onloadFuncts, (i), 1, 0)();
	}
}

/**
 * Add an event handler to an element
 *
 * @param Element element Element to add handler to
 * @param String attach Event to attach to
 * @param callable handler Event handler callback
 */
function addHandler( element, attach, handler ) {
	if( window.addEventListener ) {
		element.addEventListener( attach, handler, false );
	} else if( window.attachEvent ) {
		element.attachEvent( 'on' + attach, handler );
	}
}

/**
 * Add a click event handler to an element
 *
 * @param Element element Element to add handler to
 * @param callable handler Event handler callback
 */
function addClickHandler( element, handler ) {
	addHandler( element, 'click', handler );
}
//note: all skins should call runOnloadHook() at the end of html output,
//      so the below should be redundant. It's there just in case.
hookEvent("load", runOnloadHook);
hookEvent("load", mwSetupToolbar);
 ;
_proxy_jslib_flush_write_buffers() ;