function jsTypeOf(value) {
    var s = typeof value;
    if (s === 'object') {
        if (value) {
            if (typeof value.length === 'number' &&
                    !(value.propertyIsEnumerable('length')) &&
                    typeof value.splice === 'function') {
                s = 'array';
            }
        } else {
            s = 'null';
        }
    }
    return s;
}

function urlDecode(str){
    str=str.replace(new RegExp('\\+','g'),' ');
    return unescape(str);
}
function urlEncode(str){
    str=escape(str);
    str=str.replace(new RegExp('\\+','g'),'%2B');
    return str.replace(new RegExp('%20','g'),'+');
}

var END_OF_INPUT = -1;

var base64Chars = new Array(
    'A','B','C','D','E','F','G','H',
    'I','J','K','L','M','N','O','P',
    'Q','R','S','T','U','V','W','X',
    'Y','Z','a','b','c','d','e','f',
    'g','h','i','j','k','l','m','n',
    'o','p','q','r','s','t','u','v',
    'w','x','y','z','0','1','2','3',
    '4','5','6','7','8','9','+','/'
);

var reverseBase64Chars = new Array();
for (var i=0; i < base64Chars.length; i++){
    reverseBase64Chars[base64Chars[i]] = i;
}

// TODO - not threadsafe
var base64Str;
var base64Count;
function setBase64Str(str){
    base64Str = str;
    base64Count = 0;
}
function readBase64(){    
    if (!base64Str) return END_OF_INPUT;
    if (base64Count >= base64Str.length) return END_OF_INPUT;
    var c = base64Str.charCodeAt(base64Count) & 0xff;
    base64Count++;
    return c;
}
function encodeBase64(str){
    setBase64Str(str);
    var result = '';
    var inBuffer = new Array(3);
    var lineCount = 0;
    var done = false;
    while (!done && (inBuffer[0] = readBase64()) != END_OF_INPUT){
        inBuffer[1] = readBase64();
        inBuffer[2] = readBase64();
        result += (base64Chars[ inBuffer[0] >> 2 ]);
        if (inBuffer[1] != END_OF_INPUT){
            result += (base64Chars [(( inBuffer[0] << 4 ) & 0x30) | (inBuffer[1] >> 4) ]);
            if (inBuffer[2] != END_OF_INPUT){
                result += (base64Chars [((inBuffer[1] << 2) & 0x3c) | (inBuffer[2] >> 6) ]);
                result += (base64Chars [inBuffer[2] & 0x3F]);
            } else {
                result += (base64Chars [((inBuffer[1] << 2) & 0x3c)]);
                result += ('=');
                done = true;
            }
        } else {
            result += (base64Chars [(( inBuffer[0] << 4 ) & 0x30)]);
            result += ('=');
            result += ('=');
            done = true;
        }
        lineCount += 4;
        if (false && lineCount >= 76){
            result += ('\n');
            lineCount = 0;
        }
    }
    return result;
}
function readReverseBase64(){   
    if (!base64Str) return END_OF_INPUT;
    while (true){      
        if (base64Count >= base64Str.length) return END_OF_INPUT;
        var nextCharacter = base64Str.charAt(base64Count);
        base64Count++;
        if (reverseBase64Chars[nextCharacter]){
            return reverseBase64Chars[nextCharacter];
        }
        if (nextCharacter == 'A') return 0;
    }
    return END_OF_INPUT;
}

function ntos(n){
    n=n.toString(16);
    if (n.length == 1) n="0"+n;
    n="%"+n;
    return unescape(n);
}

function decodeBase64(str){
    setBase64Str(str);
    var result = "";
    var inBuffer = new Array(4);
    var done = false;
    while (!done && (inBuffer[0] = readReverseBase64()) != END_OF_INPUT
        && (inBuffer[1] = readReverseBase64()) != END_OF_INPUT){
        inBuffer[2] = readReverseBase64();
        inBuffer[3] = readReverseBase64();
        result += ntos((((inBuffer[0] << 2) & 0xff)| inBuffer[1] >> 4));
        if (inBuffer[2] != END_OF_INPUT){
            result +=  ntos((((inBuffer[1] << 4) & 0xff)| inBuffer[2] >> 2));
            if (inBuffer[3] != END_OF_INPUT){
                result +=  ntos((((inBuffer[2] << 6)  & 0xff) | inBuffer[3]));
            } else {
                done = true;
            }
        } else {
            done = true;
        }
    }
    return result;
}

var digitArray = new Array('0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f');
function toHex(n){
    var result = ''
    var start = true;
    for (var i=32; i>0;){
        i-=4;
        var digit = (n>>i) & 0xf;
        if (!start || digit != 0){
            start = false;
            result += digitArray[digit];
        }
    }
    return (result==''?'0':result);
}

function pad(str, len, pad){
    var result = str;
    for (var i=str.length; i<len; i++){
        result = pad + result;
    }
    return result;
}

function encodeHex(str){
    var result = "";
    for (var i=0; i<str.length; i++){
        result += pad(toHex(str.charCodeAt(i)&0xff),2,'0');
    }
    return result;
}

var hexv = {
  "00":0,"01":1,"02":2,"03":3,"04":4,"05":5,"06":6,"07":7,"08":8,"09":9,"0A":10,"0B":11,"0C":12,"0D":13,"0E":14,"0F":15,
  "10":16,"11":17,"12":18,"13":19,"14":20,"15":21,"16":22,"17":23,"18":24,"19":25,"1A":26,"1B":27,"1C":28,"1D":29,"1E":30,"1F":31,
  "20":32,"21":33,"22":34,"23":35,"24":36,"25":37,"26":38,"27":39,"28":40,"29":41,"2A":42,"2B":43,"2C":44,"2D":45,"2E":46,"2F":47,
  "30":48,"31":49,"32":50,"33":51,"34":52,"35":53,"36":54,"37":55,"38":56,"39":57,"3A":58,"3B":59,"3C":60,"3D":61,"3E":62,"3F":63,
  "40":64,"41":65,"42":66,"43":67,"44":68,"45":69,"46":70,"47":71,"48":72,"49":73,"4A":74,"4B":75,"4C":76,"4D":77,"4E":78,"4F":79,
  "50":80,"51":81,"52":82,"53":83,"54":84,"55":85,"56":86,"57":87,"58":88,"59":89,"5A":90,"5B":91,"5C":92,"5D":93,"5E":94,"5F":95,
  "60":96,"61":97,"62":98,"63":99,"64":100,"65":101,"66":102,"67":103,"68":104,"69":105,"6A":106,"6B":107,"6C":108,"6D":109,"6E":110,"6F":111,
  "70":112,"71":113,"72":114,"73":115,"74":116,"75":117,"76":118,"77":119,"78":120,"79":121,"7A":122,"7B":123,"7C":124,"7D":125,"7E":126,"7F":127,
  "80":128,"81":129,"82":130,"83":131,"84":132,"85":133,"86":134,"87":135,"88":136,"89":137,"8A":138,"8B":139,"8C":140,"8D":141,"8E":142,"8F":143,
  "90":144,"91":145,"92":146,"93":147,"94":148,"95":149,"96":150,"97":151,"98":152,"99":153,"9A":154,"9B":155,"9C":156,"9D":157,"9E":158,"9F":159,
  "A0":160,"A1":161,"A2":162,"A3":163,"A4":164,"A5":165,"A6":166,"A7":167,"A8":168,"A9":169,"AA":170,"AB":171,"AC":172,"AD":173,"AE":174,"AF":175,
  "B0":176,"B1":177,"B2":178,"B3":179,"B4":180,"B5":181,"B6":182,"B7":183,"B8":184,"B9":185,"BA":186,"BB":187,"BC":188,"BD":189,"BE":190,"BF":191,
  "C0":192,"C1":193,"C2":194,"C3":195,"C4":196,"C5":197,"C6":198,"C7":199,"C8":200,"C9":201,"CA":202,"CB":203,"CC":204,"CD":205,"CE":206,"CF":207,
  "D0":208,"D1":209,"D2":210,"D3":211,"D4":212,"D5":213,"D6":214,"D7":215,"D8":216,"D9":217,"DA":218,"DB":219,"DC":220,"DD":221,"DE":222,"DF":223,
  "E0":224,"E1":225,"E2":226,"E3":227,"E4":228,"E5":229,"E6":230,"E7":231,"E8":232,"E9":233,"EA":234,"EB":235,"EC":236,"ED":237,"EE":238,"EF":239,
  "F0":240,"F1":241,"F2":242,"F3":243,"F4":244,"F5":245,"F6":246,"F7":247,"F8":248,"F9":249,"FA":250,"FB":251,"FC":252,"FD":253,"FE":254,"FF":255
};

function decodeHex(str){
    str = str.toUpperCase().replace(new RegExp("s/[^0-9A-Z]//g"));
    var result = "";
    var nextchar = "";
    for (var i=0; i<str.length; i++){
        nextchar += str.charAt(i);
        if (nextchar.length == 2){
            result += ntos(hexv[nextchar]);
            nextchar = "";
        }
    }
    return result;
    
}

function tableAddRow(tabname, cols)
{
	var table = document.getElementById(tabname);
	var newrow = table.insertRow(table.rows.length);
	for(i = 0; i < cols.length; i++)
	{
		var newcol = newrow.insertCell(i);
		newcol.innerHTML = cols[i];
	}
}

function tableClearRowsNotHeader(tabname)
{
	var table = document.getElementById(tabname);
	if(table)
	{
		while(table.rows.length > 1)
			table.deleteRow(table.rows.length - 1);
	}
}

function tableClearRows(tabname) {
	var table = document.getElementById(tabname);
	if(table) {
		while(table.rows.length > 0)
			table.deleteRow(table.rows.length - 1);
	}	
}

function tog2(id1, id2) {
	tog(id1);
	tog(id2);
}

function hide(id) {
	return setDisplay(id, "none");
}

function show(id) {
	return setDisplay(id, "");
}

function setDisplay(id, style) {
	var div = document.getElementById(id);
	if(div) {
		div.style.display = style;
	}
	return div;
}

function tog(id) {
	var div = document.getElementById(id);
	if(div) {
		var s = div.style.display;
		if(s == '') s = 'none'; else s = '';
			div.style.display = s;
	}
	return div;
}

function getInpVal(inpid)
{
	var o = document.getElementById(inpid);
	if(o)
		return o.value;
	else
		alert("not found: " + inpid);
	return null;
}

function setInpVal(inpid, val)
{
	var o = document.getElementById(inpid);
	if(o)
		o.value = val;
	else
		alert("not found: " + inpid);
	return val;
}

function seth(id, s)
{
	var d = document.getElementById(id);
	if(d != null)
		d.innerHTML = s;
	else
		alert("not found: " + id);
	return s;
}

function jsinfo(report)
{
	var info = "Browser: " + navigator.appName;
	seth('divjsinfo', info);
	if(report)
		document.location = report + "info=" + info;
}

function makeElem(tagname, attr) {
	var elem = document.createElement(tagname);
	for(var i in attr) {
		elem[i] = attr[i]; // TODO - is there a single assoc array merge function?
	}
	return elem;
}

// ugly string stuff

function tag(t, a, s)
{
	var buffer = [];
	buffer.push("<");
	buffer.push(t);
	if(a)
		buffer.push(a);
	buffer.push(">");
	if(s)
		buffer.push(s);
	buffer.push("</");
	buffer.push(t);
	buffer.push(">");
	return buffer.join("");
}
function isThere(s) {if(s) return s; return "";}
function tableb(s) { return tag("table", ' border=1 cellpadding=0 cellspacing=0', s); }
function table(s) { return tag("table", ' border=0 cellpadding=0 cellspacing=0', s); }
function tr(s) { return tag("tr", ' valign=top', s); }
function tdx(s) { return td("&nbsp;"+s+"&nbsp;"); }
function td(s) { return tag("td", ' align=left', s); }
function tdr(s) { return tag("td", ' align=right', s); }
function tdrx(s) { return tag("td", ' align=right', "&nbsp;"+s+"&nbsp;"); }
function th(s) { return tag("th", ' align=left', s); }
function mkin(id, ttype, a)
{
	if(ttype == null || ttype == "text")
		ttype = "";
	else
		ttype = ' type="'+ttype+'"';
	var buffer = [];
	buffer.push("<input");
	buffer.push(ttype);
	if(a)
		buffer.push(a);
	buffer.push(' id="');
	buffer.push(id);
	buffer.push('" name="');
	buffer.push(id);
	buffer.push('" />');
	return buffer.join("");
}

function lnk(l, c) { return "<a href=\""+l+"\">"+c+"</a>"; }
function lnkn(l, c) { return "<a target=\"_new\" href=\""+l+"\">"+c+"</a>"; }
function jlnk(l, c) { return lnk("javascript:"+l, c); }

var logcount = 0;
function log(s) {
	var divlog = document.getElementById('divlog');
	if(divlog) {
		divlog.innerHTML += "<br />"+s;
		logcount += 1;
		if(logcount > 90)
		{
			divlog.innerHTML = '';
			logcount = 0;
		}
	}
}
// var newDictIfNone = parseQueryStringInto(document.location.toString(), dict);
function parseQueryStringInto(url, dict)
{
	if(! dict)
		dict = {};
	var qs = url.split("?");
	if(qs.length > 1)
	{
		qs = qs[1];
		var nvs = qs.split("&");
		for(var i = 0; i < nvs.length; i++)
		{
			var nv = nvs[i].split("=");
			if(nv.length == 2)
			{
				dict[nv[0]] = nv[1];
			}
			else if(nv.length == 1)
			{
				dict[nv[0]] = null;
			}				
		}
	}
	return dict;
}

function gE(id) { /* log(id); */ return document.getElementById(id); }

function dumpHash(h) {
	var s = "";
	for(var i in h) {
		s += i + " = " + h[i] + "<br />\n";
	}
	return s;
}

function dumpChildNodes(elem) {
	var s = "";
	for(var i = 0; i < elem.children.length; i++) {
		s += elem.children[i].nodeName + ": " + elem.children[i].id + "<br />\n";
	}
	return s;
}

function debugbodyhtml() {
    var ta = document.getElementById("debugbodyhtml");
    if(ta)
	    document.body.removeChild(ta);
	    
    var html = document.body.innerHTML;
    
    ta = document.createElement('textarea');
    ta.id = "debugbodyhtml";
    ta.innerText = html;
    document.body.appendChild(ta);
    return true;
}

function isVisible(obj) {
    if (obj == document) return true
    if (!obj) return false
    if (!obj.parentNode) return false
    if (obj.style) {
        if (obj.style.display == 'none') return false
        if (obj.style.visibility == 'hidden') return false
    }
    
    //Try the computed style in a standard way
    if (window.getComputedStyle) {
        var style = window.getComputedStyle(obj, "")
        if (style.display == 'none') return false
        if (style.visibility == 'hidden') return false
    }
    
    //Or get the computed style using IE's silly proprietary way
    var style = obj.currentStyle
    if (style) {
        if (style['display'] == 'none') return false
        if (style['visibility'] == 'hidden') return false
    }
    
    return true;
}

function getHiddens(elem, iden) {
	var sub;
	var id = elem.id || "no-id";
	var sss = id+":"+isVisible(elem)+"\n";
	for(var i in elem.childNodes) {
		sub = elem.childNodes[i];
		sss += getHiddens(sub, iden + " ");
	}
	return sss;
}

    
// http://www.codeproject.com/KB/scripting/replace_url_in_ajax_chat.aspx
function chat_string_create_urls(input)
{
    return input.replace(/(ftp|http|https|file):\/\/[\S]+(\b|$)/gim,'<a href="$&" rel="nofollow" target="_blank">$&</a>').replace(/([^\/])(www[\S]+(\b|$))/gim,'$1<a href="http://$2" rel="nofollow" target="_blank">$2</a>');
}


