if(document.getElementById){
	
	var uk; // Required in GLOBAL scope - does not overwrite any existing objects!
	if(!uk){ uk = {}; } else if(typeof uk != "object"){ eval('throw new Error("uk already exists and is not an object");'); }
	if(!uk.co){ uk.co = {}; } else if(typeof uk.co != "object"){ eval('throw new Error("uk.co already exists and is not an object");'); }
	if(!uk.co.pntestbed){ uk.co.pntestbed = {}; } else if(typeof uk.co.pntestbed != "object"){ eval('throw new Error("uk.co.pntestbed already exists and is not an object");'); }
	if(uk.co.pntestbed.core) eval('throw new Error("uk.co.pntestbed.core already exists");');
	//---------------------------------------------------------------------
	/*
		*	--------------------------
		* 	Definition Core
		*	--------------------------
	*/
	uk.co.pntestbed.core = {
		'array': {}
	,	'boolean': {}
	,	'date': {}
	,	'document': {}
	,	'event': {}
	,	'math': {}
	,	'object': {}
	,	'number': {}
	,	'regexp': {}
	,	'string': {}
	,	'window': {}
	};
	uk.co.pntestbed.core.$ = function(sId){ return document.getElementById(sId); }
	
	//---------------------------------------------------------------------
	/*
		* 	Array
	*/
	//---------------------------------------------------------------------
	uk.co.pntestbed.core.array.contains = function(a, v){ for (var i = 0; i < a.length; i++){ if(typeof v == typeof a[i] && v == a[i]){ return true; }} return false; }
	//---------------------------------------------------------------------
	
	//---------------------------------------------------------------------
	/*
		* 	Date
	*/
	//---------------------------------------------------------------------
	uk.co.pntestbed.core.date.DAYNAMES = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
	uk.co.pntestbed.core.date.MONTHNAMES = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
	uk.co.pntestbed.core.date.FORMATS = ['standard', 'short', 'mysql', 'formal', 'extended', 'compact'];
	
	uk.co.pntestbed.core.date.parseNew = function(){ var nFullYear, nMonth, nDay; if(arguments.length == 0){ return new Date(); } else if(typeof arguments[0] == 'object' && arguments[0].constructor == Date){ return arguments[0]; } else if(typeof arguments[0] == 'string'){ if(0 < arguments[0].indexOf('-')){ var a = arguments[0].split('-'); nFullYear = parseFloat(a[0]); nMonth = parseFloat(a[1]) - 1; nDay = parseFloat(a[2]); } else { var a = arguments[0].split('/'); nDay = parseFloat(a[0]); nMonth = parseFloat(a[1]) - 1; nFullYear = parseFloat(a[2]); if(arguments[0].length == 8){ nFullYear += (nFullYear < 70)? 2000: 1900;} } } else if(typeof arguments[0] == 'number'){ nFullYear = arguments[0]; nMonth = (arguments.length > 1)? 0: arguments[1] - 1; nDay = (arguments.length > 2)? 1: arguments[1] - 1; } else { return null; } if(uk.co.pntestbed.core.date.isValid(nFullYear, nMonth, nDay)){ return new Date(nFullYear, nMonth, nDay); } else { return null; } }
	
	uk.co.pntestbed.core.date.isValid = function(nFullYear, nMonth, nDay){ var d = new Date(nFullYear, nMonth, nDay); return nFullYear == d.getFullYear() && nMonth == d.getMonth() && nDay == d.getDate(); }
	uk.co.pntestbed.core.date.isWeekend = function(d){	return d.getDay() == 0 || d.getDay() == 6;	}
	uk.co.pntestbed.core.date.isLeapYear = function(d){ 	return Math.floor(d.getFullYear() / 4) == d.getFullYear() / 4;	}
	
	// Days
	// Date.getDate() - day of the month [1,...(28|29|30|31)]
	uk.co.pntestbed.core.date.getDayDecorator = function(oDate){ switch(oDate.getDate() % 10){ case 1: return 'st'; case 2: return 'nd'; case 3: return 'rd'; default: return 'th'; } }
	
	// Date.getDay() - day of the week [0 => Sun,...,6 => Sat]
	uk.co.pntestbed.core.date.getDayName = function(v, nChars, bUpperCase){ var sName = uk.co.pntestbed.core.date.DAYNAMES[(typeof v == 'number')? v: v.getDay()]; if(typeof nChars != 'undefined'){ sName = sName.substring(0, nChars); } if(typeof bUpperCase != 'undefined' && bUpperCase){ sName = sName.toUpperCase(); } return sName; }
	uk.co.pntestbed.core.date.getDayNames = function(nChars, bUpperCase){ var sNames = []; for(var nDay = 0; nDay < 7; nDay++){ sNames[nDay] = uk.co.pntestbed.core.date.getDayName(nDay, nChars, bUpperCase); } return sNames; }
	
	// Date.getMonth() - [0 => Jan,...,11 => Dec]
	uk.co.pntestbed.core.date.getMonthName = function(v, nChars, bUpperCase){ var sName = uk.co.pntestbed.core.date.MONTHNAMES[(typeof v == 'number')? v: v.getMonth()]; if(typeof nChars != 'undefined'){ sName = sName.substring(0, nChars); } if(typeof bUpperCase != 'undefined'){ sName = sName.toUpperCase(); } return sName; }
	uk.co.pntestbed.core.date.getMonthNames = function(nChars, bUpperCase){ var sNames = []; for(var nMonth = 0; nMonth < 12; nMonth++){ sNames[nMonth] = uk.co.pntestbed.core.date.getMonthName(nMonth, nChars, bUpperCase); } return sNames; }
	
	// Date.getFullYear() - 4-digit year
	
	uk.co.pntestbed.core.date.sameYear = function(d1, d2){	return d1.getFullYear() == d2.getFullYear();}
	uk.co.pntestbed.core.date.sameMonth = function(d1, d2){	return uk.co.pntestbed.core.date.sameYear(d1, d2) && d1.getMonth() == d2.getMonth();}
	uk.co.pntestbed.core.date.sameDate = function(d1, d2){	return uk.co.pntestbed.core.date.sameMonth(d1, d2) && d1.getDate() == d2.getDate();}
	uk.co.pntestbed.core.date.daysDifference = function(d1, d2){ return Math.floor(Math.abs(d1.valueOf() - d2.valueOf())/86400); }
	
	uk.co.pntestbed.core.date.getLastDayOfTheMonth = function(d){ return ([31, uk.co.pntestbed.core.date.isLeapYear(d)? 29: 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31])[d.getMonth()]; }
	uk.co.pntestbed.core.date.getLastDateOfTheMonth = function(d){ return new Date(d.getFullYear(), d.getMonth(), uk.co.pntestbed.core.date.getLastDayOfTheMonth(d)); }
	uk.co.pntestbed.core.date.getFirstDateOfTheMonth = function(d){ return new Date(d.getFullYear(), d.getMonth(), 1); }
	uk.co.pntestbed.core.date.getOffsetByDay = function(d, nDD){ var ad = new Date(d.getFullYear(), d.getMonth(), d.getDate()); ad.setDate(ad.getDate() + nDD);	return ad; }
	uk.co.pntestbed.core.date.getOffsetByMonth = function(d, nDM){ var ad = new Date(d.getFullYear(), d.getMonth(), 1); ad.setMonth(ad.getMonth() + nDM); var dLast = uk.co.pntestbed.core.date.getLastDateOfTheMonth(ad); return new Date(ad.getFullYear(), ad.getMonth(), Math.min(d.getDate(), dLast.getDate())); }
	uk.co.pntestbed.core.date.getOffsetByYear = function(d, nDY){ return uk.co.pntestbed.core.date.getOffsetByMonth(d, nDY*12); }
	
	uk.co.pntestbed.core.date.format = function(d, sFormat){ var month = '' + ((d.getMonth() + 1) < 10? '0': '') + (d.getMonth() + 1), day = '' + (d.getDate() < 10? '0': '') + d.getDate(), year = ((d.getFullYear() % 100) < 10? '0': '') + (d.getFullYear() % 100); switch(sFormat){ case 'short':	return day + '/' + month + '/' + year; case 'mysql':	return d.getFullYear() + '-' + month + '-' + day; case 'formal':	return d.getDate() + ' ' + uk.co.pntestbed.core.date.getMonthName(d) + ' ' + d.getFullYear(); case 'extended':return uk.co.pntestbed.core.date.getDayName(d) + ', ' + d.getDate() + uk.co.pntestbed.core.date.getDayDecorator(d) + ' ' + uk.co.pntestbed.core.date.getMonthName(d) + ' ' + d.getFullYear(); case 'compact':	return day + uk.co.pntestbed.core.date.getMonthName(d, 3, true) + year; default:		return day + '/' + month + '/' + d.getFullYear(); } }
	uk.co.pntestbed.core.date.detectFormat = function(sDate){ var d = uk.co.pntestbed.core.date.parseNew(sDate); if(d) for(var i = 0; i < uk.co.pntestbed.core.date.FORMATS.length; i++) if(uk.co.pntestbed.core.date.format(d, uk.co.pntestbed.core.date.FORMATS[i]) == sDate) return uk.co.pntestbed.core.date.FORMATS[i]; else return false; }
	uk.co.pntestbed.core.date.allFormats = function(d){ var aFormats = []; for(var i = 0; i < uk.co.pntestbed.core.date.FORMATS.length; i++){ var sFormat = uk.co.pntestbed.core.date.FORMATS[i]; aFormats[sFormat] = uk.co.pntestbed.core.date.format(d, sFormat); } return aFormats; }
	//---------------------------------------------------------------------
	
	//---------------------------------------------------------------------
	/*
		* 	Document
	*/
	//---------------------------------------------------------------------
	uk.co.pntestbed.core.document.getElementsByClassName = function(clsName){
	    var retVal = new Array();
		if(document.getElementsByTagName){
		    var elements = document.getElementsByTagName("*");
		    for(var i = 0; i < elements.length;i++){
		        if(elements[i].className && elements[i].className.indexOf(" ") >= 0){
		            var classes = elements[i].className.split(" ");
		            for(var j = 0; j < classes.length;j++){
		                if(classes[j] == clsName){
		                    retVal.push(elements[i]);
						}
		            }
		        } else if(elements[i].className && elements[i].className == clsName){
		            retVal.push(elements[i]);
				}
		    }
		}
	    return retVal;
	}
	uk.co.pntestbed.core.document.initialiseNodeList = function(oList){
		if(typeof oList == 'string'){
			oList = uk.co.pntestbed.core.$(oList);
		}
		var fragElements = XHTML.documentFragment();
		var fragNonElements = XHTML.documentFragment();
		while(oList.firstChild){
			if(oList.firstChild.nodeType == 1){
				fragElements.appendChild(oList.firstChild);
			} else {
				fragNonElements.appendChild(oList.firstChild);
			}
		}
		while(fragElements.firstChild){
			oList.appendChild(fragElements.firstChild);
		}
	}
	uk.co.pntestbed.core.document.moveListNode = function(oList, nFrom, nTo){
		// Eg.s 
		//	[0, 1, 2, 3, 4] from 1 to 4 => [0, 2, 3, 1, 4]
		//	[A, B, C, D, E]                [A, C, D, B, E]
		
		//	[0, 1, 2, 3, 4] from 4 to 1 => [0, 4, 1, 2, 3]
		//	[A, B, C, D, E]                [A, E, B, C, D]
		
		if(typeof oList == 'string'){
			oList = uk.co.pntestbed.core.$(oList);
		}
		
		if(nTo == nFrom || nTo == nFrom + 1) return;
		var iCycleMin = nTo < nFrom == 1? nTo: nFrom;
		var iCycleMax = nTo < nFrom == 1? nFrom	: nTo - 1;
		var oPre = XHTML.documentFragment(), oCycle = XHTML.documentFragment(), oPost = XHTML.documentFragment();
		var nNodes = oList.childNodes.length;
		for(var i = 0; i < nNodes; i++){
			(i < iCycleMin? oPre: i <= iCycleMax? oCycle: oPost).appendChild(oList.firstChild);
		}
		while(oPre.firstChild) oList.appendChild(oPre.firstChild);
		if(nTo < nFrom){
			oList.appendChild(oCycle.lastChild); // Put the last child first
			while(oCycle.firstChild) oList.appendChild(oCycle.firstChild);
		} else {
			oPre.appendChild(oCycle.firstChild); // Put the first child last
			while(oCycle.firstChild) oList.appendChild(oCycle.firstChild);
			oList.appendChild(oPre.firstChild);
		}
		while(oPost.firstChild) oList.appendChild(oPost.firstChild);
	}
	
	//---------------------------------------------------------------------
	
	//---------------------------------------------------------------------
	/*
		* 	Event
	*/
	//---------------------------------------------------------------------
	uk.co.pntestbed.core.event.getElement = function(e){
		if (!e) var e = window.event;
		if(e.srcElement){
			return e.srcElement;
		} else if(e.target){
			return e.target;
		} else {
			return null;
		}
	}
	uk.co.pntestbed.core.event.addSimple = function(sType, fn){
		if(document.attachEvent){
			document.attachEvent('on' + sType, fn); 			// probably (IE on windows)
		} else if(document.addEventListener){
			document.addEventListener(sType, fn, false); 		// could be
		} else if(window.addEventListener){
			window.addEventListener(sType, fn, false); 		// should be
		} else {											// old (not defined)
			eval('throw new Error("Modern browser required for uk.co.pntestbed.core.event.addSimple");');
		}
	}
	uk.co.pntestbed.core.event.removeSimple = function(sType, fn){
		if(document.detachEvent){
			document.detachEvent('on' + sType, fn); 			// probably (IE on windows)
		} else if(document.removeEventListener){
			document.removeEventListener(sType, fn, false); 	// could be
		} else if(window.removeEventListener){
			window.removeEventListener(sType, fn, false); 		// should be
		} else {											// old (not defined)
			eval('throw new Error("Modern browser required for uk.co.pntestbed.core.event.removeSimple");');
		}
	}
	uk.co.pntestbed.core.event.preventDefault = function(e){
		if(!e) var e = window.event;
		if(document.attachEvent){
			e.cancelBubble = true;
			e.returnValue = false;
		} else if(document.addEventListener){
			e.preventDefault();
		}
	}
	
	uk.co.pntestbed.core.event.mousePosition = function(e){
		if(!e) var e = window.event;
		var o = { x: 0, y: 0 };
		if (e.pageX || e.pageY){
			o.x = e.pageX;
			o.y = e.pageY;
		} else if (e.clientX || e.clientY){
			o.x = e.clientX + document.documentElement.scrollLeft + document.body.scrollLeft;
			o.y = e.clientY + document.documentElement.scrollTop + document.body.scrollTop;
		}
		return o;
	}
	//---------------------------------------------------------------------
	
	//---------------------------------------------------------------------
	/*
		* 	Math
	*/
	//---------------------------------------------------------------------
	uk.co.pntestbed.core.math.factorial = function(n, r){
		if(n == 0) return 1;
		if(!r){
			var r = 0;
		} else {
			r = Math.min(r, n);
		}
		var m = 1;
		for(var i = r + 1; i <= n ;i++){
			m *= i;
		} 
		return m;
	}
	//---------------------------------------------------------------------
	
	//---------------------------------------------------------------------
	/*
		* 	Number
	*/
	//---------------------------------------------------------------------
	uk.co.pntestbed.core.number.toBaseExponent = function(
		d
	,	nSigFigs		/* Significant figures of base */
	, 	nStdFormLimit	/* Numbers whose (absolute) exponents are strictly 
						   less than this limit are displayed in std form.
						   That is, if nStdFormLimit = 2 && nSigFigs = 3
						   		±0.00123456 => { ±  1.23,  -2 }
						   		±0.01234567 => { ±0.0123, 	0 }
						   		±0.12345678 => { ± 0.123, 	0 }
						   		±1.23456789 => { ±  1.23, 	0 }
								±12.3456789 => { ±  12.3, 	0 }
								±123.456789 => { ±  1.23, 	2 }
						*/
	){
		if(typeof nSigFigs == 'undefined'){
			var nSigFigs = 3; // Set default to 3 sig figs
		} else if(nSigFigs == 0){ 
			// Set base as string and zero the exponent (a bit redundant 
			// since could do this with Number.toString()). Also, js could
			// display large/small numbers in exponential form itself
			return { sBase: d.toString(), sExponent: '0' };
		}
		
		if(typeof nStdFormLimit == 'undefined'){
			var nStdFormLimit = 0; // All numbers are converted to base, exponent
		}
		var sSign = d < 0? '-': '';
		var adAbsParts = Math.abs(d).toExponential(nSigFigs - 1).toString().split('e');
		var sAbsBase = adAbsParts[0];
		var nExponent = parseInt(adAbsParts[1]);
		if(Math.abs(nExponent) <= nStdFormLimit) {
			var sAbsBaseDigits = sAbsBase.replace(/\./g, '');
			if(nExponent < 0){
				sAbsBase = '0.' + String.repeat('0', -(1 + nExponent)) + sAbsBaseDigits;
			} else if(nExponent < (nSigFigs - 1)){
				sAbsBase = uk.co.pntestbed.core.String_insertAt(sAbsBaseDigits, '.', 1 + nExponent);
			} else if(nExponent == (nSigFigs - 1)){
				sAbsBase = sAbsBaseDigits;
			} else if(nExponent > (nSigFigs - 1)){
				sAbsBase = sAbsBaseDigits + String.repeat('0', 1 + nExponent - nSigFigs);
			}
			nExponent = 0;
		}
		return { sBase: sSign + sAbsBase, sExponent: nExponent.toString() };
	}
	//---------------------------------------------------------------------
	
	//---------------------------------------------------------------------
	/*
		* 	Object
	*/
	//---------------------------------------------------------------------
	uk.co.pntestbed.core.object.getStyle = function(obj){ return obj.style? obj.style: obj; }
	uk.co.pntestbed.core.object.getPosition = function(obj){
		var pos = { left: 0, top: 0 };
		if(obj.offsetParent){
			while(obj.offsetParent){
				pos.left += obj.offsetLeft;
				pos.top += obj.offsetTop;
				obj = obj.offsetParent;
			}
		}
		return pos;
	}
	uk.co.pntestbed.core.object.fromCssSize = function(sSize){
		if(sSize.length == 0) return false;
		var o = { value: 0, dimension: '' };
		var a_sTypes = ['em', 'px', 'pt', '%'];
		for(var i = 0; i < a_sTypes.length; i++){
			var sType = a_sTypes[i];
			var re = new RegExp(sType);
			if(sSize.match(re)){
				sSize = sSize.replace(re, '');
				o.value = parseFloat(sSize);
				o.dimension = sType;
				break;
			}
		}
		return o;	
	}
	//---------------------------------------------------------------------
	
	//---------------------------------------------------------------------
	/*
		* 	String
	*/
	//---------------------------------------------------------------------
	uk.co.pntestbed.core.string.repeat = function(s, n){ var ss = ''; for(var i = 0; i < n; i++){ ss += s.toString(); } return ss; }
	uk.co.pntestbed.core.string.ucfirst = function(s){	return s.charAt(0).toUpperCase() + s.substr(1).toLowerCase();}
	uk.co.pntestbed.core.string.insertAt = function(ss, st, n){	return ss.substr(0, n) + st + ss.substr(n); }
	uk.co.pntestbed.core.string.firstIndexOf = function (sSrc, vTrg, nStart){
		// vTrg can be a single chars or an array of strings
		if(!nStart) var nStart = 0;
		var n, nFirst = sSrc.length;
		for(var i = 0; i < vTrg.length; i++){
			if((n = sSrc.indexOf(vTrg[i], nStart)) > -1){
				nFirst = Math.min(nFirst, n);
			}
		}
		return nFirst == sSrc.length? -1: nFirst;
	}
	//---------------------------------------------------------------------
	
	//---------------------------------------------------------------------
	/*
		* 	Window
	*/
	//---------------------------------------------------------------------
	uk.co.pntestbed.core.window.onload = function(fn){
		if(typeof window.addEventListener != 'undefined'){
			window.addEventListener('load', fn, false); 	// should be
		} else if(typeof document.addEventListener != 'undefined'){
			document.addEventListener('load', fn, false); 	// could be
		} else if(typeof window.attachEvent != 'undefined'){
			window.attachEvent('onload', fn); 				// probably (IE on windows)
		} else if(typeof window.onload == 'function'){
			var fnExistingOnload = onload;					// old (already defined)
			window.onload = function(){	fnExistingOnload(); fn(); };
		} else {											// old (not defined)
			window.onload = fn;
		}
	}
	//---------------------------------------------------------------------
	
}
