/*
     ______________________________________________________________________    
  .´Ż.---.      .----.     .  ,---------.       ,---------------------.  ,.Ż`. 
 /  /    /     /     /_____/ /           )     /_)                    / /  \  \
 |  |   (     (________   / / _____  ___/     /___ ___  _____    ___ / /   |  |
 |  |    `--------.    Ż\/ /  ) ) /  ) /     /) /)Ż.-.Ż\) .-.Ż\.´.-.Ż /    |  |
 |  |_______      /     / `--´,´ /  / /     // // /  / / `--´_/ /  / /     |  |
 |  |)     (     /     / ,´Ż) / /  / /     // // /  / / /ŻŻŻŻ/ /  / /      |  |
 |  |'._    `---´   _.´ ( .´ ( `--´ /     /´ (´ (  / (\_`-´Ż)\_`-´ (       |  |
 \  \   ŻŻŻŻŻŻŻŻŻŻŻŻ ŻŻŻŻ ŻŻŻŻŻŻŻ) /     (ŻŻŻŻŻŻŻ  ŻŻŻ  ŻŻŻŻ   ŻŻŻŻŻ       /  /
  `._`--------------------------´ ´       `------------------------------´_.´ 
     ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ    
*/
function toInt(xNumber) {
	if (typeof(xNumber) == "number") return xNumber;
	var iResult = parseInt(xNumber + "");
	return isNaN(iResult) ? 0 : iResult;
}
function toFloat(xNumber) {
	if (typeof(xNumber) == "number") return xNumber;
	var nResult = parseFloat(xNumber + "");
	return isNaN(nResult) || nResult == 0 ? toInt(xNumber) : nResult;
}
function xToString(xValue) {
	var sString = null;
	try { sString = xValue.toString(); } catch (e) {}

	switch(typeof(xValue)) {
		case "number":
			return numberToString(xValue);
		case "boolean":
			return xValue ? "TRUE" : "FALSE";
		case "string":
			return xValue;
		case "object":
			var sSubType = "";
			var sConstructor = "";
			try { sConstructor = xValue.constructor.toString(); } catch(e) {}
			var aConstructorFunction = new RegExp(
				"[\\n]?" + // STORED: A newline before "function" signals it's native. (optional)
				"function" + // The "function" keyword
				"\\s*(?://.*\\n\\s*|/\\*[\\s\\S]*\\*/\\s*)*" + // Any number of //... or /* */ comments and whitespace
				"(\\w*)" + // STORED: The function name (optional)
				"\\s*(?://.*\\n\\s*|/\\*[\\s\\S]*\\*/\\s*)*" + // Any number of //... or /* */ comments and whitespace
				"\\(" + // Argument list opening bracket
				"[\\s\\S]*" + // Arguments + code (ignored)
				"\\}" // Code closing curly bracket
			).exec(sConstructor);
			if (aConstructorFunction && aConstructorFunction.length > 1) sSubtype = aConstructorFunction[1];
			var aConstructorObject = new RegExp(
				"\[object\ (\\w*)\]"
			).exec(sConstructor);
			if (aConstructorObject && aConstructorObject.length > 1) sSubtype = aConstructorObject[1];
			
			switch(sSubType) {
				case "Error" :
					return exceptionToString(xValue);
				case "Array" :
					return arrayToString(xValue);
				default:
					var sResult = "[" + typeof(xValue);
					if (sSubType.length > 0) sResult += " " + sSubType;
					if (typeof(sString) == "string") sResult += ": " + sResult;
					return sResult + "]";
			}
		default:
			var sResult = "[" + typeof(xValue);
			if (typeof(sString) == "string") sResult += ": " + sResult;
			return sResult + "]";
	}
}

function arrayToString(aValue) {
	var aReturnValues = [];
	for (var i in aValue)
		aReturnValues.push(i.toString() + ": " + xToString(aValue[i]));
	return "[" + aReturnValues.join(", ") + "]";
}
				
function numberToString(xValue) {
	var nValue = toFloat(xValue);
	// Returns a "pretty" string representation of a number:
	//		number(1000000.5) == "1,000,000.5"
	if (nValue == 0) return "0";
	var sResult = "" + nValue;
	
	for (
		var sResult = "";
		nValue > 0;
		nValue = Math.floor(nValue / 1000)
	) {
		sResult = (nValue % 1000) +
			(sResult.length > 0 ? "," + sResult : "");
		if (nValue > 1000 && sResult.length % 4 < 3)
			sResult = "0" + sResult;
	}
	return sResult;
} 

function numberToBytesString(xValue) {
	var nValue = toFloat(xValue);
	// Returns a "pretty" string representation of a number of bytes:
	//		bytes(1000000.5) == "976.57 KB"
	var aUnits = new Array(
		"bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"
	);
	var x1024 = 0;
	while (nValue > 1024 && x1024 < aUnits.length - 1) {
		x1024++;
		nValue /= 1024;
	}
	sResult = numberToString(Math.ceil(nValue * 100) / 100) + // Two decimals
		" " + aUnits[x1024];
	return sResult;
}

function numberToTimeString(xValue, xGranularity) {
	var nValue = toFloat(xValue);
	var iGranularity = 1;
	if (arguments.length > 1)
		iGranularity = toInt(xGranularity);
	// Returns a "pretty" string representation of an elapsed number of
	// milliseconds with the given granularity:
	//		time(1000000.5) == "16m 40s 1µs"
	//		time(1000000.5, 1000) == "16m 40s"
	//		time(1000000.5, 60*1000) == "17m"
	var aUnits = new Array(
		new Array(1000,     unescape("%u03BCs")),
		new Array(60,       "s"),
		new Array(60,       "m"),
		new Array(24,       "h"),
		new Array(7,        "d"),
		new Array(52,       "w"),
		new Array(Infinity,	"y")
	);
	var sSign = (nValue < 0 ? "-" : "");
	nValue = Math.abs(nValue);
	var aResult = new Array();
	nValue = Math.round(nValue / iGranularity) * iGranularity;
	
	for(var i = 0; nValue > 0 && i < aUnits.length; i++) {
		var iSize = aUnits[i][0],
			sUnit = aUnits[i][1],
			iPartialResult = nValue % iSize;
		if (iPartialResult > 0)
			aResult.unshift(Math.round(iPartialResult) + sUnit);
		nValue = Math.floor(nValue / iSize);
	}
	if (aResult.length == 0) 
		return "0";
	else
		return sSign + aResult.join(" ");
} // time()

function numberToHexString(xInt, xType){
	var iInt = toInt(xInt);
	var sType = arguments.length > 1 ? xType.toString() : "";
	var iNibbles = 8;
	var iMask = 0xFFFFFFFF;
	if (sType) switch(sType) {
		case "DWORD": iNibbles = 8; iMask = 0xFFFFFFFF; break;
		case "WORD":  iNibbles = 4; iMask = 0xFFFF; break;
		case "BYTE":  iNibbles = 2; iMask = 0xFF; break;
		case "2 BYTES": 
			return numberToHexString(iInt, "BYTE") + " " +
				numberToHexString(iInt >> 8, "BYTE");
		case "4 BYTES": 
			return numberToHexString(iInt, "BYTE") + " " +
				numberToHexString(iInt >> 8, "BYTE") + " " +
				numberToHexString(iInt >> 16, "BYTE") + " " +
				numberToHexString(iInt >> 24, "BYTE");
		case "2 WORDS":
			return numberToHexString(iInt, "WORD") + " " +
				numberToHexString(iInt >> 8, "WORD");
		default: throw "numberToHexString: illegal sType argument";
	}
	iInt &= iMask;
	if (iInt < 0) iInt += 0x100000000;
	var sResult = iInt.toString(16);
	while (sResult.length < iNibbles) sResult = "0" + sResult;
	return sResult.toUpperCase();
}

function exceptionToString(oValue) {
	return oValue.name.toString() + " 0x" + numberToHexString(oValue.number) + ": " + oValue.message.toString();
}

function charsToHexString(xString, xType){
	var sString = xString.toString();
	var sType = xType.toString();
	if (sString.length == 0) return "";
	var iNibblesPerResult = 8;
	var iNibblesPerChar = 2;
	var iMask = 0xFFFFFFFF;
	if (sType.indexOf("BYTE") >= 0) {    iNibblesPerResult = 2; iMask = 0xFF; }
	if (sType.indexOf("WORD") >= 0) {    iNibblesPerResult = 4; iMask = 0xFFFF; }
	if (sType.indexOf("DWORD") >= 0) {   iNibblesPerResult = 8; iMask = 0xFFFFFFFF; }
	if (sType.indexOf("UNICODE") >= 0) { iNibblesPerChar = 4; }
	var aResults = [];
	var nNibbleIndex = 0;
	while ((nNibbleIndex + iNibblesPerResult) / iNibblesPerChar <= sString.length) {
		var iInt = 0;
		for (var i = 0; i < iNibblesPerResult / iNibblesPerChar; i++) {
			var iByteShift = ((nNibbleIndex / iNibblesPerChar) % 1 > 0 ? 8 : 0);
			var iValue = (sString.charCodeAt(Math.floor(nNibbleIndex / iNibblesPerChar)) >> iByteShift) & 0xFF;
			iInt +=  iValue << (4*iNibblesPerChar*i);
			nNibbleIndex++;
		}
		iInt &= iMask;
		var sResult = iInt.toString(16);
		while (sResult.length < iNibblesPerResult) sResult = "0" + sResult;
		aResults.push(sResult);
	}
	var sResult = aResults.join(" ");
	return sResult.toUpperCase() + (sString.length > 0 ? ", " + sString.length.toString() + " superflous byte" + (sString.length != 1 ? "s" : "") + ": " + charsToHexString(sString, "BYTE") : "");
}

function stringEncode(xText, xQuote) {
//	alert(xText+xQuote);
	var sText = xText.toString();
	var sReplace = "\x00-\x1F|\\|\x80-\x9F|\u0100-\uFFFF";
	if (arguments.length > 1) {
		var sQuote = xQuote.toString();
		if (sQuote.indexOf("'") >= 0) sReplace += "|'";
		if (sQuote.indexOf("\"") >= 0) sReplace += "|\"";
		if (sQuote.indexOf("*") >= 0) sReplace = "\x00-\uFFFF";
	} else {
		sReplace += "|\'|\"";
	}
	return sText.replace(
		new RegExp("[" + sReplace + "]{1}", "g"),
		function(c) {
			switch (c) {
				case "\t": return "\\t"; break;
				case "\r": return "\\r"; break;
				case "\n": return "\\n"; break;
				default:   
					if (c.charCodeAt() < 0x100)
						return "\\x" + numberToHexString(c.charCodeAt(), "BYTE");
					else
						return "\\u" + numberToHexString(c.charCodeAt(), "WORD");
				break;
			}
		}
	);
}

function htmlEncode(xText) {
	return xToString(xText).replace(
		/\r\n|[^\w]/g, 
		function (s) {
			switch(s) {
				case '\r\n': return "<BR>";
				case '\n': return "<BR>";
				case '\t': return "\t";
				case ' ':  return "&nbsp;";
				case '\<': return "&lt;";
				case '\>': return "&gt;";
				case '\"': return "&quot;";
				case '&':  return "&amp;";
				default:   return "&#" + s.charCodeAt(0) + ";";
			}
		}
	);
}
function htmlDecode(xText) {
	return xToString(xText).replace(
		/\<BR\>|\&nbsp\;|\&lt\;|\&gt\;|\&quot\;|\&amp\;|\&\#\d*\;/g,
		function (s) {
			switch(s) {
				case "<BR>":   return "\n";
				case "&nbsp;": return " ";
				case "&lt;":   return "\<";
				case "&gt;":   return "\>";
				case "&quot;": return "\"";
				case "&amp;": return "&";
				default:	   return String.fromCharCode(parseInt(s.substr(2, s.length-3)));
			}
		}
	);
}

function sleep(xSleepMilliSeconds) {
	var iSleepMilliSeconds = toInt(xSleepMilliSeconds);
	var iTargetTime = new Date().valueOf() + iSleepMilliSeconds;
	var iActions = 10000;
	while (iActions != 0 && iActions != Infinity) {
		var dStartTime = new Date().valueOf();
		new Array(iActions).reverse();
		var dEndTime = new Date().valueOf();
		// How many milliseconds did we spend per action?
		var nMilliSecondsPerAction = (dEndTime  - dStartTime ) / iActions;
		// How many actions would we need to fill the remaining time?
		// We don't want to over do it, so we'll just do half that and recalculate
		iActions = Math.floor((iTargetTime - dEndTime) / nMilliSecondsPerAction / 2);
	}
	while (new Date().valueOf() < iTargetTime) { }
}

function percentage(xPart, xTotal) {
	var nResult = Math.round(1000*toFloat(xPart)/toFloat(xTotal)) / 10;
	if (nResult % 1 == 0) return nResult.toString() + ".0";
	else return nResult.toString();
}
function htmlAlert(xHTML, xArguments) {
	return showModalDialog(
		"/Tools/write.asp?%3CHTML%3E%3CSCRIPT%3Edocument.write(window.dialogArguments.html);%3C/SCRIPT%3E%3C/HTML%3E",
		{html: xHTML, arguments: xArguments},
		"dialogWidth: 800px; dialogHeight: 600px; resizable: yes;"
	);
}

function functionCode(xFunction) {
	try {
		alert(xToString(xFunction));
		return /(?:[^\{]*)\{([\s\S]*)\}$/g.exec(xToString(xFunction))[1];
	} catch (e) {
		return "";
	}
}

function getCurrentScriptPath() {
	oLastTag = document.all[document.all.length-1];
	if (oLastTag.tagName == "SCRIPT" && oLastTag.src)
		return oLastTag.src;
	return null;
}
