/**
 * Copyright (c) 2008-2011, ASoft Ltd.
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are PROHIBITED.
 *
 * Common Helper Utilities.
 *
 */
function isFloat(val) {
	if(!val || (typeof val != "number")) {
		return(false);
	}
	var isNumber = !isNaN(val);
	if(isNumber) {
		// TODO: how locale influences this code?
		if (String(val).indexOf('.') != -1) {
			return(true);
		} else {
			return(false);
		}
	} else {
		return(false);
	}
}

// Cross-browser keyboard scan codes parser
// Supports Mozilla, IE and Opera
function GetKeyCode(event) {
	var evt = Event.extend(event || window.event);
	if (evt.keyCode != 0) {
		// not IE
		if (Prototype.Browser.Mozilla || Prototype.Browser.WebKit) {
			return {keyCode: evt.keyCode, charCode: 0};
		}
		// IE
		if (typeof(evt.which) == "undefined") {
			// stupid IE cannot give us character info, only keyCode is accessible
			return {keyCode: evt.keyCode}; // charCode is undefined in case of IE
		}
		// Opera
		if (evt.which == 0) {
			return {keyCode: evt.keyCode, charCode: 0}
		}
		else {
			return {keyCode: 0, charCode: evt.which};
		}
	}
	else {
		return {keyCode: 0, charCode: evt.charCode};
	}
	// assert (should not go here)
	return null;
}


String.prototype.isDigit = function(){
	return /^\d$/.test(this);
}

String.prototype.indicesOf = function(str){
	var result = new Array();
	for (var currentIndex = this.indexOf(str); currentIndex != -1; currentIndex = this.indexOf(str, currentIndex + 1))
		result.push(currentIndex);
	return result;
}

Element.clear = function(element){
	if (!element) return;
	$A(element.childNodes).each(Element.remove);
}

Element.getTextFast = function(element){
	if (!element) return "";
	return element.innerText || element.textContent || "";
}

Element.scrollContainerTo = function(container, element){
	if (!container || !element || !Element.descendantOf(element, container)) return;
	if (element.offsetTop < container.scrollTop) container.scrollTop = element.offsetTop;
	else if (element.offsetTop + element.offsetHeight > container.scrollTop + container.offsetHeight)
			container.scrollTop = element.offsetTop + element.offsetHeight - container.offsetHeight;
}

Event.getKeyCode = function(event){
	if (!event) return 0;
	if (typeof event.charCode != "undefined") return event.charCode;
	if (typeof event.which != "undefined") return event.which;
	return event.keyCode;
}

function Extend(oValue, nLength, sEChar) {
	var sB = "";
	var sT = oValue.toString();
	for (var i = 0; i < nLength - sT.length; ++i) sB += sEChar;
	return sB+sT;
}

function createElementNS(doc, tag, ns) {
	if(Prototype.Browser.IE) return doc.createNode(1, tag, ns);
	else return doc.createElementNS(ns, tag);
}

function createElementAI(doc, tag) {
	if(Prototype.Browser.IE) return doc.createNode(1, tag, AIURI);
	else return doc.createElementNS(AIURI, tag);
}

function getSelectionStart(element) {
	if(Prototype.Browser.IE) {
		var docRange = document.selection.createRange();
		if(element.nodeName == "TEXTAREA") {
			var eleRange = docRange.duplicate();
			eleRange.moveToElementText(element);

			eleRange.setEndPoint("EndToStart",docRange);

			return eleRange.text.length;
		}
		else {
			var eleRange=element.createTextRange();
			docRange.setEndPoint("EndToStart",eleRange);
			return docRange.text.length;
		}
	}
	else return element.selectionStart;
}

function getSelectionEnd(element) {
	if(Prototype.Browser.IE) {
		var docRange=document.selection.createRange();
		if(element.nodeName == "TEXTAREA") {
			var eleRange = docRange.duplicate();
			eleRange.moveToElementText(element);

			eleRange.setEndPoint("EndToEnd", docRange);
			return eleRange.text.length;
		}
		else {
			var eleRange=element.createTextRange();
			docRange.setEndPoint("StartToStart",eleRange);
			return docRange.text.length;
		}
	}
	else return element.selectionEnd;
}

// NOTE: does not work correctly for multiline input
function setCaretPosition(element, position) {
	if(Prototype.Browser.IE) {
		var range = element.createTextRange();
		range.collapse(true);//go to beginning of range
		range.moveEnd('character', position);
		range.moveStart('character', position);
		range.select();
	}
	else {
		element.selectionStart=position;
		element.selectionEnd=position;
	}
}

// NOTE: does not work correctly for multiline input
function setSelectionRange(element, start, end) {
	if (typeof element.setSelectionRange == "function")
		element.setSelectionRange(start, end);
	else if (element.createTextRange){
		var range = element.createTextRange();
		range.collapse(true);
		range.moveStart("character", start);
		range.moveEnd("character", end - start);
		range.select();
	}
}

function replaceSelection(element, newText, selectNewText) {
	var scrollTop = element.scrollTop;

	if (Prototype.Browser.IE) {
		var docRange = document.selection.createRange();

		var eleRange = docRange.duplicate();
		eleRange.moveToElementText(element);

		if (!eleRange.inRange(docRange)) {
			docRange = eleRange;
			docRange.collapse(false);
		}

		docRange.text = newText;

		if (!selectNewText) docRange.collapse(false);
		docRange.select();
	}
	else {
		var s = element.selectionStart;
		var e = element.selectionEnd;
		element.value = element.value.substr(0, s) + String(newText) + element.value.substr(e);

		if (selectNewText) element.setSelectionRange(s, s + newText.length);
		else element.setSelectionRange(s + newText.length, s + newText.length);
	}

	element.scrollTop = scrollTop;
}

function BindKeyDown(element, fCallback) {
	if (Prototype.Browser.IE || Prototype.Browser.WebKit) {
		element.observe('keydown', fCallback);
	}
	else {
		element.observe('keypress', fCallback);
	}
}

function UnbindKeyDown(element, fCallback) {
	if (Prototype.Browser.IE || Prototype.Browser.WebKit) {
		element.stopObserving('keydown', fCallback);
	}
	else {
		element.stopObserving('keypress', fCallback);
	}
}

function BindKeyUp(element, fCallback) {
	if (Prototype.Browser.IE || Prototype.Browser.WebKit) {
		element.observe('keyup', fCallback);
	}
	else {
		element.observe('keypress', fCallback);
	}
}

function getHexRGBColor(color) {
	color = color.replace(/\s/g,"");
	var aRGB = color.match(/^rgb\((\d{1,3}[%]?),(\d{1,3}[%]?),(\d{1,3}[%]?)\)$/i);

	if(aRGB) {
		color = '#';
		for (var i=1;  i<=3; i++) color += Math.round((aRGB[i][aRGB[i].length-1]=="%"?2.55:1)*parseInt(aRGB[i])).toString(16).replace(/^(.)$/,'0$1');
	}
	else color = color.replace(/^#?([\da-f])([\da-f])([\da-f])$/i, '$1$1$2$2$3$3');

	return color;
}


// offsetType can be either "offsetLeft" or "offsetTop"
function getAbsoluteOffset(element, offsetType) {
	var offset = 0;
	var ie7fix = (Prototype.Browser.IE && offsetType == 'offsetTop' && navigator.userAgent.indexOf('MSIE 7') != -1);

	while ( element ) {
		offset += element[offsetType];
		if (ie7fix && element.offsetParent && element.offsetParent.nodeName == 'TABLE') {
			var th = Element.up(element, 'td, th');
			if (th && th.nodeName == 'TH') offset -= th[offsetType];
		}

		element = element.offsetParent;
	}
	return offset;
}

function getRelativeOffset(element, offsetType) {
	var offset=0;

	while ( element && (
	        element.getStyle('position')!='absolute' &&
	        element.getStyle('position')!='relative' )
	  ) {
		offset += element[offsetType];
		element = element.offsetParent;
	}
	return offset;
}

var NOLOG = (document.location.hash.search('logger') == -1);
var GLoggerWindow = null;
var GLoggerGUID = null;

if (!NOLOG && !GLoggerWindow) {
	var uid = Math.uuid();
	GLoggerWindow = window.open("/logger.html", "LoggerWindow", 'location=1,status=1,scrollbars=1,width=900,height=400');
	GLoggerWindow.moveTo(100,100);
	GLoggerWindow.guid = uid;
	GLoggerGUID = uid;
	// create Logger window
	if (!GLoggerWindow) {
		alert("Failed to create Logger!");
		NOLOG = true;
	} else {
		//alert("Logger started");
	}
}

// add new line to log
function LogAdd(msg) {
	if(NOLOG) return;
	if (window.LoggerClosed && window.LoggerClosed[GLoggerGUID]) {
		NOLOG=true; // you only live twice (c)
		alert('Logger '+GLoggerGUID+' disabled');
		return;
	}
	try {
		if (!GLoggerWindow || !GLoggerWindow.document || !GLoggerWindow.document.body) throw new Error("Logger window was closed");
		var eDiv = GLoggerWindow.document.createElement('div');
		var eLine = GLoggerWindow.document.body.appendChild(eDiv);
		eLine.innerHTML = msg;
	}
	catch (exc) {}
}

// append message to last line
function LogAppend(msg) {
	if(NOLOG) return;
	if (window.LoggerClosed && window.LoggerClosed[GLoggerGUID]) {
		NOLOG=true; // you only live twice (c)
		alert('Logger '+GLoggerGUID+' disabled');
		return;
	}
	try {
		if (!GLoggerWindow || !GLoggerWindow.document || !GLoggerWindow.document.body) throw new Error("Logger window was closed");
		var body = GLoggerWindow.document.body;
		if (!body.lastChild) LogL(""); // start new line if none
		var eLine = body.lastChild;
		if (!eLine) return; // shit happened
		eLine.innerHTML = eLine.innerHTML + msg;
	}
	catch (exc) {}
}

function LogS(sMsg,sStyle) {
	if(NOLOG) return;

	var today=new Date();
	var h=Extend(today.getHours(),2,'0');
	var m=Extend(today.getMinutes(),2,'0');
	var s=Extend(today.getSeconds(),2,'0');
	var ms=Extend(today.getMilliseconds(),3,'0');
	if (typeof(sMsg) == 'undefined') sMsg = 'undefined';
	else if (sMsg === null) sMsg = 'null';
	else if (sMsg.toString) sMsg = sMsg.toString();
	var sL=AXML.EscapeEntities(sMsg);
	sL = sL.replace(/\$\$([^\$]*)\$\$/g, '<span class="LogMark">$1</span>');
	LogAdd('<span class="LoggerTime">['+h+':'+m+':'+s+'.'+ms+']&nbsp;</span><span class="'+sStyle+'">'+sL+'</span>');
}

function LogSN(sMsg,sStyle) {
	if(NOLOG) return;

	if (typeof(sMsg) == 'undefined') sMsg = 'undefined';
	else if (sMsg === null) sMsg = 'null';
	else if (sMsg.toString) sMsg = sMsg.toString();
	var sL=AXML.EscapeEntities(sMsg);
	sL = sL.replace(/\$\$([^\$]*)\$\$/g, '<span class="LogMark">$1</span>');
	LogAppend('<span class="'+sStyle+'">'+sL+'</span>');
}

function LogL(sMsg) {
	LogS(sMsg, 'LoggerText');
}

function LogE(sMsg) {
	LogS(sMsg, 'LoggerExc');
}

function Log(sMsg) {
	LogSN(sMsg, 'LoggerText');
}

// Writes all Object's properties into the log
function showObject(obj) {
	LogL("===========================================");
	if (!obj) LogL("Empty Object");
	else {
		for (var key in obj) LogL(key+": "+obj[key]);
	}
	LogL("===========================================");
}
// TODO: wrap this in logger class?

function ThrowError(sMessage) {
	LogE(sMessage);
	throw Error(sMessage);
}

var warningBox=undefined;
var warningBoxHiding=false;
function ShowTip(parent, tip) {
	var offset = Element.positionedOffset(parent);
	var x = offset[0];
	var y = offset[1];

	warningBox=document.getElementById('_warning_box_');
	if(warningBox){
		var content = Element.down(warningBox, "div.warning");
		if (content) Element.update(content, tip);
		var offsetParent = Element.getOffsetParent(parent);
		if (offsetParent != warningBox.parentNode) offsetParent.appendChild(warningBox.parentNode.removeChild(warningBox));
		var c00 = Element.down(warningBox, "td.top_left");
		Element.setStyle(warningBox, {"top": y + parseInt(parent.offsetHeight) + "px", //
				"left": x - (c00 ? c00.offsetWidth : 0) + "px"});
		return;
	}
	warningBox=new Element('div', {"id": '_warning_box_', 'class': 'warning'});

	var content=new Element('div', {'class': 'warning'}).update(tip);
	var tbl=new Element('table', {'class': 'popup', "cellpadding": "0", "cellspacing": "0"});

	var addTableRow = function(table, classnames){
		var result = new Array(classnames.length);
		var row = table.insertRow(-1);
		for (var iii = 0; iii < classnames.length; ++iii){
			result[iii] = row.insertCell(-1);
			if (classnames[iii]) result[iii].className = classnames[iii];
		}
		return result;
	}

	var cells_top = addTableRow(tbl, ["top_left", "top", "top_right"]);
	var cells_mid = addTableRow(tbl, ["left", "", "right"]);
	cells_mid[1].appendChild(content);
	addTableRow(tbl, ["bot_left", "bottom", "bot_right"]);

	Element.setStyle(warningBox, {"position": "absolute", "left": "-10000px", "top": "-10000px", "clip": 'rect(0px, 0px, 0px, 0px)'});
	warningBox.appendChild(tbl);
	Element.getOffsetParent(parent).appendChild(warningBox);
	Element.setStyle(warningBox, {"left": x - cells_top[0].offsetWidth + 'px'});

	var slide = new Animation($(warningBox), 6, undefined, undefined, Animation.prototype.decelerate);
	slide.addAnimation('clip', warningBox.scrollHeight+'px, 10000px, 10000px, 0px', '0px, 10000px, 10000px, 0px');
	slide.addAnimation('top', y+'px', y+parseInt(parent.offsetHeight)+'px');
	slide.run();

	var arise= new Animation($(warningBox), 4, undefined, undefined, Animation.prototype.decelerate);
	arise.addAnimation('opacity', '0', '1');
	arise.run();
}

function HideTip() {
	warningBox=document.getElementById('_warning_box_');
	if(warningBox && !warningBoxHiding) {
		warningBoxHiding=true;
		var x=parseInt(warningBox.offsetLeft);
		var y=parseInt(warningBox.offsetTop);

		var slide = new Animation($(warningBox), 6, undefined, undefined, Animation.prototype.decelerate);
		slide.addAnimation('clip', '0px, 10000px, 10000px, 0px', warningBox.scrollHeight+'px, 10000px, 10000px, 0px');
		slide.addAnimation('top', y+'px', y-parseInt(warningBox.scrollHeight)+'px');
		slide.onfinish=function() {
			if (warningBox) Element.remove(warningBox);
			warningBoxHiding=false;
			warningBox=undefined;
		}
		slide.run();

		var arise= new Animation($(warningBox), 7, undefined, undefined, Animation.prototype.decelerate);
		arise.addAnimation('opacity', '1', '0');
		arise.run();
	}
}

var TwoToMinus52 = Math.pow(2., -52);
var TwoToMinus28 = Math.pow(2., -28);
var TwoToMinus4 = Math.pow(2., -4);

function parseBytesAsDouble(str, i) {
	var sign = str.charCodeAt(7) & 0x80;
	var exp = (str.charCodeAt(6) >> 4) | ((str.charCodeAt(7) & 0x7f) << 4);

	if (exp == 0) {
		// We do not correctly support subnormal (2^-1022) numbers
		return (sign ? -0 : 0);
	}

	var m1 = str.charCodeAt(0) |
			(str.charCodeAt(1) << 8) |
			(str.charCodeAt(2) << 16);

	var m2 = str.charCodeAt(3) |
			(str.charCodeAt(4) << 8) |
			(str.charCodeAt(5) << 16);

	var m3 = (str.charCodeAt(6) & 0x0f) | (1 << 4);
	if ( exp == 0x7ff && (m1 != 0 || m2 != 0 || m3 != 0) ) {
		return Number.NaN;
	}
	else if ( exp == 0x7ff ) {
		return (sign ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY)
	}

	var num = m1 * TwoToMinus52 + m2 * TwoToMinus28 + m3 * TwoToMinus4;
	if (sign) num =-num;
	return num * Math.pow(2, exp - 1023);
}

var _emptyTags = {
	"IMG":   true,
	"BR":    true,
	"INPUT": true,
	"META":  true,
	"LINK":  true,
	"PARAM": true,
	"HR":    true
};

function GetOuterHTML(ths) {
	if(Prototype.Browser.IE) return ths.outerHTML;

	var attrs = ths.attributes;
	var str = "<" + ths.tagName;
	for (var i = 0; i < attrs.length; i++) {
		str += " " + attrs[i].name + "=\"" + attrs[i].value + "\"";
	}
	if (_emptyTags[ths.tagName]) return str + ">";
	return str + ">" + ths.innerHTML + "</" + ths.tagName + ">";
}

function StopEvent(evt) {
	if (!evt) return;
	if (evt.stop) evt.stop();
	else evt.stopPropagation(); // for Fx 3.6a
}

// Some test cases:
//
// 145555555.99911122
//alert ("A: " + parseBytesAsDouble("\x81\x8b\xff\xc7\x00\x5a\xa1\x41", 0));

// -1.5
//alert ("A: " + parseBytesAsDouble("\x00\x00\x00\x00\x00\x00\xf8\xbf", 0));

// 3
//alert ("A: " + parseBytesAsDouble("\x00\x00\x00\x00\x00\x00\x08\x40", 0));
//



