



function getDocumentScrollLeft_withOffset(offset){
	if(!offset)
		offset = 0;
	var scrl = offset + getDocumentScrollLeft();
	//partial fix to scroll to infinity ie6 bug
	if(scrl > 1280)
		scrl = 1280 + offset;
	return ( scrl ) + 'px';
}

function getDocumentScrollTop_withOffset(offset){
	if(!offset)
		offset = 0;
	var scrl = offset + getDocumentScrollTop();
	//partial fix to scroll to infinity ie6 bug
	if(scrl > 2500)
		scrl = 2500 + offset;
	return ( scrl ) + 'px';
}


function getDocumentScrollLeft(){
	return document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft;
}

function getDocumentScrollTop(){
	return document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop;	
}

function addToDocumentScrollTop(offset){
	if(document.documentElement.scrollTop){
		document.documentElement.scrollTop =  document.documentElement.scrollTop + offset;	
	}
	else{
		document.body.scrollTop = document.body.scrollTop + offset;	
	}
}

function getScrollWidth()
{
   var w = window.pageXOffset ||
           document.body.scrollLeft ||
           document.documentElement.scrollLeft;
           
   return w ? w : 0;
} 

function getScrollHeight()
{
   var h = window.pageYOffset ||
           document.body.scrollTop ||
           document.documentElement.scrollTop;
           
   return h ? h : 0;
}

function addScrollSynchronization(fromElement, toElement, direction) {	
	var fn = function () {
		
		if (direction == "horizontal" || direction == "both")
			toElement.scrollLeft = fromElement.scrollLeft;
		if (direction == "vertical" || direction == "both")
			toElement.scrollTop = fromElement.scrollTop;
	};
	
	ie ? fromElement.attachEvent("onscroll", fn) : fromElement.addEventListener("scroll", fn, false);
}

function clearSelection() {
	var sel;
	if(document.selection && document.selection.empty){
		document.selection.empty() ;
	} 
	else 
	if(window.getSelection){
		sel = window.getSelection();
		if(sel && sel.removeAllRanges)
			sel.removeAllRanges();
	}
}

/* gets the keyCode typed*/
function getTypedKeyCode(e)
{
	var key = false;
	if(window.event) 
		key = window.event.keyCode;
	else 
	if(e) 
		key = e.which;
	
	return key;	
}


function zeroPad(s, p, before){
	s = s.toString();
	if(!s)
		return false;
		
	var tmp = "";
	if(s.length < p){
		for(var i=0; i<(p-s.length); i++)
			tmp += "0";
		before ? s = tmp + s : s = tmp + s;
	}
	return s;
}

/* allows only characters in the chars array */
function filterChars(e, chars)
{
	var key = getTypedKeyCode(e);
	if(!key) 
		return true;
	/* allow upper case */
	var keychar = String.fromCharCode(key).toLowerCase();
	
	/* allow control keys */
	if ((key==null) || (key==0) || (key==8) ||  (key==9) || (key==13) || (key==27)) 
		return true;
	/* allow chars in arg string */
	else 
	if (((chars).indexOf(keychar) > -1)) 
		return true;
	else 
		return false;
}

// If this is to work in IE 5.0, you need to include this add-on to get support for the push method on the Array object:
if (typeof(Array.prototype.push) != "function")
{
    Array.prototype.push = ArrayPush;
    function ArrayPush(value)
	{
        this[this.length] = value;
    }
}

/*
	To get all a elements in the document with a &#8220;info-links&#8221; class.
	getElementsByClassName(document, "a", "info-links");
	To get all div elements within the element named &#8220;container&#8221;, with a &#8220;col&#8221; class.
	getElementsByClassName(document.getElementById("container"), "div", "col"); 
	To get all elements within in the document with a &#8220;click-me&#8221; class.
	getElementsByClassName(document, "*", "click-me");
*/
function getElementsByClassName(oElm, strTagName, strClassName) 
{
    var arrElements = (strTagName == "*" && oElm.all) ? oElm.all : oElm.getElementsByTagName(strTagName);
    var arrReturnElements = new Array();
    strClassName = strClassName.replace(/\-/g, "\\-");
    var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$)");    	
    for (var i = 0; i < arrElements.length; i++)
	{
        var oElement = arrElements[i];      
        if (oRegExp.test(oElement.className))
		{
            arrReturnElements.push(oElement);
        }   
    }
	
    return arrReturnElements;
}

function fixEvent(e)
{
	if (!e && window.event)
		return window.event;
	return e;
}

function eventSource(e)
{
	return ie ? event.srcElement : e.target	
}

function getTargetElement(evt) 
{
    var elem;
    if (evt.target) 
	{
        elem = (evt.target.nodeType == 3) ? evt.target.parentNode : evt.target;
    } 
	else 
	{
        elem = evt.srcElement;
    }
	
    return elem;
}


function toURI(s)
{
	/* todo: other browsers */
	if (!s)
		return '';
	return encodeURIComponent(s.toString());
}

function valueToURI(o)
{
	if (!o)
		return '';
	return toURI(o.value);
}

function valueToURIById(id)
{
	return valueToURI(document.getElementById(id));
}

function valuePost(id)
{
	return id+'='+valueToURIById(id)+'&';
}

function itoURI(s)
{
	/* todo: other browsers */
	if (!s)
		return '';
	return encodeURIComponent(s.toString());
}

function innerToURI(o)
{
	if (!o)
		return '';
	return itoURI(o.innerHTML);
}

function innerToURIById(id)
{
	return innerToURI(document.getElementById(id));
}

function innerPost(id)
{
	return id+'='+innerToURIById(id)+'&';
}

function varPost(v, id)
{
	return v+'='+valueToURIById(id)+'&';
}

function varPostValue(v, value)
{
	return v+'='+toURI(value)+'&';
}

function getCssAbsolutePosition(o)
{
	if (o == null)
		return null;
	if (o.style.position != 'absolute')
		return null;
		
	var y = 0;
	if (o.style.top != '')
	{
		y = parseInt(o.style.top.replace('px', ''));
		if (isNaN(y))
			y = 0;
	}
	var x = 0;
	if (o.style.left != '')
	{
		x = parseInt(o.style.left.replace('px', ''));
		if (isNaN(x))
			x = 0;
	}
	
	return [x, y];
}

function getCssSize(o)
{
	if (o == null)
		return null;
		
	var w = 0;
	if (o.style.width != '')
	{
		w = parseInt(o.style.width.replace('px', ''), 10);
		if (isNaN(w))
			w = 0;
	}
	var h = 0;
	if (o.style.height != '')
	{
		h = parseInt(o.style.height.replace('px', ''), 10);
		if (isNaN(h))
			h = 0;
	}
	
	return [w, h];	
}


// TODO: position relative breaks
function absolutePosition(o)
{
	var absCss = getCssAbsolutePosition(o);
	if (absCss != null)
	{
		this.x = absCss[0];
		this.y = absCss[1];
		return;
	}
	
	var x = o.offsetLeft ? o.offsetLeft : 0;
	var y = o.offsetTop ? o.offsetTop : 0;
	//debugAlert('*** '+o.id+', '+o.tagName+', '+x+', '+y+', '+o.offsetTop+'<br/>');
	var p = o.offsetParent;
	while (p)
	{ 
		if (p.tagName.toLowerCase() == 'html')
			break;
		var dx = p.offsetLeft - p.scrollLeft;
		var dy = p.offsetTop - p.scrollTop;
		x += p.offsetLeft - p.scrollLeft;
		y += p.offsetTop - p.scrollTop; 
		//debugAlert('*** '+p.id+', '+p.tagName+', '+dx+', '+dy+', '+p.offsetTop+'<br/>');
		p = p.offsetParent;
		
		var absCss = getCssAbsolutePosition(p);
		if (absCss != null)
		{
			//debugAlert('mujex '+p.id+' '+p.tagName+' '+absCss+' scroll='+p.scrollTop);
			this.x = x+absCss[0]-p.scrollLeft;
			this.y = y+absCss[1]-p.scrollTop;
			return;
		}		
	}
	
	this.x = x;
	this.y = y;
}


function isTag(o, tag)
{
	return o != null && o.tagName != null && tag != null && o.tagName.toString().toLowerCase() == tag.toString().toLowerCase();
}

function getFirstElementTag(o, tag)
{
	if (!o)
		return null;
	for (var i = 0; i < o.childNodes.length; i++)
	{
		var node = o.childNodes.item(i);
		if (isTag(node, tag))
			return node;
	}
	return null;
}

function comboSelectByValue(combo, value)
{
	for (var i = 0; i < combo.options.length; i++)
	{
		var c = combo.options[i];
		if (!isTag(c, 'option'))
			continue;						
		if (c.text.toString() == value.toString())
		{
			combo.options[i].selected = true;
			return;
		}
	}	
}

function comboGetSelectedValue(combo)
{
	return combo.options[combo.selectedIndex].value;
}

function comboGetSelectedText(combo)
{
	return combo.options[combo.selectedIndex].text;
}

function comboSelectByValueAttr(combo, value)
{
	for (var i = 0; i < combo.options.length; i++)
	{
		var c = combo.options[i];
		if (!isTag(c, 'option'))
			continue;

		if (c.value.toString() == value.toString())
		{
			combo.options[i].selected=true;
			return;
		}
	}	
}

function comboDeleteOptions(combo)
{
	while (combo.options.length > 0)
			combo.remove(combo.options.length-1);	
}

function comboAddOptions(combo, arr)
{
	for (var i = 0; i < arr.length; i++)
	{
		var opt = document.createElement('OPTION');
		combo.options.add(opt);
		opt.value = arr[i][0];
		opt.innerHTML = htmlEntities(arr[i][1]);
	}
}

// This actually fetches the n'th span of a row, whether it's hidden or not 
function getRowHiddenSpan(row, index)
{
	if (!isTag(row, 'tr'))
		return null;
		
	var cell = row.cells[0];
	if (!cell)
		return null;
	var cnt = 0;
	for (var i = 0; i < cell.childNodes.length; i++)
	{
		var c = cell.childNodes[i];
		if (!isTag(c, 'span'))
			continue;
		if (cnt >= index)
			return c;
		cnt++;
	}
	
	return null;
}

function getRowHiddenSpanValue(row, index)
{
	var span = getRowHiddenSpan(row, index);
	if (!span)
		return '';
	return span.innerHTML;
}

function getRowSpanText(row, iSpan)
{
	var span = getRowHiddenSpan(row, iSpan);
	if (!span)
		return '';
	return htmlEntitiesStrip(span.innerHTML);
}

function trim(sInString) 
{
	sInString = sInString.toString().replace( /^\s+/g, "" );// strip leading
	return sInString.replace( /\s+$/g, "" );// strip trailing
}

function getElementsByIds(arrIds) 
{
	var ret = new Array();
	for (id in arrIds)
		ret.push(document.getElementById(arrIds[id]));
	return ret;
}

function safeHideElement(name) 
{
	var o = document.getElementById(name);
	if (!o)
		return;
	o.style.display = 'none';
}

function safeShowElement(name) 
{ 
	var o = document.getElementById(name);
	if (!o)
		return;
	o.style.display = '';	
}

function getTagAncestor(o, tag)
{
	if (o == null || tag == null)
		return null;
		
	var lcTag = tag.toLowerCase();
	while (o)
	{
		if (!o.tagName)
			return null;
		if (o.tagName.toLowerCase() == lcTag)
			return o;
		o = o.parentNode;
	}
		
	return null;
}

function getById(id)
{
	return document.getElementById(id);
}

function getByIds(ids)
{
	var ret = new Array();
	for (var i = 0; i < ids.length; i++)
		ret[ret.length] = getById(ids[i])
		
	return ret;
}

function newXMLHttpRequest()
{
	if (!ie) 
		return new XMLHttpRequest();

	else 
		return new ActiveXObject("Microsoft.XMLHTTP");	
}

function makeHttpGetRequest(url, callback, synch)
{
	// Make url absolute: strip last component of doc location and append target to it 
	var iSep = document.location.toString().lastIndexOf('/');
	target = document.location.toString().substring(0, iSep)+'/'+url;
	
	var xmlhttp = newXMLHttpRequest();	
	xmlhttp.open('GET', url, !synch);
	xmlhttp.onreadystatechange = function()
	{
		if (xmlhttp.readyState != 4 || (xmlhttp.status != 200 && xmlhttp.status != 304))
			return;

		if (callback && typeof(callback) == 'function')
			callback(xmlhttp.responseText);
	}
	xmlhttp.send(null);			
}

function addClass(o, c)
{
	var ac = o.className.split(" ");
	var ret = "";
	
	var lc = c.toLowerCase();
	var found = false;
	for (var i = 0; i < ac.length; i++)
	{
		if (ac[i] == "")
			continue;
		if (ac[i].toLowerCase() == lc)
			found = true;
		if (ret != "")
			ret += " ";
		ret += ac[i];
	}
	
	if (!found)
	{
		if (ret != "")
			ret += " ";
		ret += c;		
	}
	
	o.className = ret;
}

function removeClass(o, c)
{
	var ac = o.className.split(" ");
	var ret ="";
	
	var lc = c.toLowerCase();
	for (var i = 0; i < ac.length; i++)
	{
		if (ac[i] == "" || ac[i].toLowerCase() == lc)
			continue;
		if (ret != "")
			ret += " ";
		ret += ac[i];
	}
	
	o.className = ret;
}

function hasClass(className, c)
{
	var ac = className.split(" ");
	
	var lc = c.toLowerCase();
	for (var i = 0; i < ac.length; i++)
	{
		if (ac[i].toLowerCase() == lc)
			return true;
	}
	
	return false;
		
}

function setAndExecute(o, innerHTML)
{   
	o.innerHTML = innerHTML;   
	var x = o.getElementsByTagName("script");    
	for (var i = 0; i <x.length; i++) 
		eval(x[i].text);
}

function executeInnerHtml(o)
{
	var x = o.getElementsByTagName("script");    
	for (var i = 0; i <x.length; i++) 
		eval(x[i].text);
}

function makeJSONCall(target, op, args, callback, synch)
{
	// Make url absolute: strip last component of doc location and append target to it 
	var iSep = document.location.toString().lastIndexOf('/');
	target = document.location.toString().substring(0, iSep)+'/'+target;

	var xmlhttp = newXMLHttpRequest();	
	xmlhttp.open('POST', target, !synch);
	xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
	xmlhttp.onreadystatechange = function()
	{
		if (xmlhttp.readyState != 4 || (xmlhttp.status != 200 && xmlhttp.status != 304))
			return;
			
		//alert(xmlhttp.responseText);		
		debugAlert(xmlhttp.responseText);
		
		if (callback && typeof(callback) == 'function')
		{
			var obj = null;
			try
			{
				obj = String_parseJSON(xmlhttp.responseText);
				//debugAlert(nl2br(debugDump(obj)));
			}
			catch (e)
			{				
				obj = null;
				//debugAlert(xmlhttp.responseText);
			}
			callback(obj);
		}
	}
	var postData = 'op='+encodeURIComponent(op);
	if (args)
		postData += '&a='+encodeURIComponent(Object_toJSONString(args));
	debugAlert(postData);
	xmlhttp.send(postData);
}

g_htmlEntities = { '&': '&amp;', '<': '&lt;', '>': '&gt' };
g_regexp_htmlEntities = new RegExp('([&<>])', 'g');
function htmlEntities_replaceFunction($1)
{
	return g_htmlEntities[$1];
}

function htmlEntities(s)
{
	s = s.replace(/[&]/g, '&amp;');
	s = s.replace(/[<]/g, '&lt;');
	s = s.replace(/[>]/g, '&gt;');
	s = s.replace(/["]/g, '&quot;');	
	return s;
//	return s.replace(g_regexp_htmlEntities, htmlEntities_replaceFunction);
}

var g_nl2brReplace = { '\t': '&nbsp;&nbsp;&nbsp;&nbsp;', '\n': '<br/>' };
g_regexp_nl2br = new RegExp('((\\t)|(\\n))', 'g');
function nl2br_replaceFunction($1)
{
	return g_nl2brReplace[$1];
}

function nl2br(s)
{
	return s.replace(g_regexp_nl2br, nl2br_replaceFunction);
}

g_htmlEntitiesStrip = { '&amp;': '&', '&lt;': '<', '&gt;': '>' };
g_regexp_htmlEntitiesStrip = new RegExp('((&amp;)|(&lt;)|(&gt;))', 'g');
function htmlEntities_replaceFunction($1)
{
	return g_htmlEntitiesStrip[$1];
}

function htmlEntitiesStrip(s)
{
	return s.replace(g_regexp_htmlEntitiesStrip, htmlEntities_replaceFunction);
}

function createDebugDiv()
{
	if (g_debugDiv)
		return;
		
	var div = document.createElement('div');
	div.id = '__debug_div__';

	div.style.height = '480px';		
	div.style.overflowY = 'scroll';
	
	var divContainer = document.createElement('div');
	divContainer.id = '__debug_div_container__';
	divContainer.style.position = 'fixed';
	
	divContainer.style.left = '1005px';
	divContainer.style.top = '0px';
	
	divContainer.style.padding = '5px';
	
	divContainer.style.width = '500px';
	divContainer.style.height = '500px';
		
	divContainer.style.border = 'solid 2px black';
	divContainer.style.backgroundColor = 'yellow';
	
	divContainer.style.zIndex = 10000;
	
	divContainer.onclick = function() {
		var tmp = document.getElementById('G_ERROR_BUFFER');
		divContainer.style.zIndex = parseInt(tmp ? tmp.style.zIndex : divContainer.style.zIndex, 10)+2;
	};	
	
	divContainer.innerHTML = '<div style="cursor:pointer; margin-bottom:10px;"><b style="color:#006600;">JavaScript - Debugger</b> <a href="javascript:void(0);" onclick="javascript:document.getElementById(\'__debug_div__\').innerHTML = \'\';">Clear Contents</a></div>';
	divContainer.appendChild(div);
	
	document.body.appendChild(divContainer);
	
	g_debugDiv = div;
}

// Debug alert functions
// Leave these here, they depend on the registerOnLoad

var g_debugDiv = null;
var g_debugBuffer = '';
var g_debugRegisteredFunction = null;

function debugOnLoad()
{
	createDebugDiv();	
	g_debugDiv.innerHTML = g_debugBuffer;
	g_debugBuffer = '';
}

function debugAlert(s)
{
	if (!G_JS_DEBUG)
		return;
	
	if (!document.body || !document.body._loaded) {
		if (!g_debugRegisteredFunction) {
			registerOnLoadFunction(debugOnLoad);
			g_debugRegisteredFunction = true;
		}		
		g_debugBuffer += "<div>" + s + "</div>";
		
		return;
	} 
		
	createDebugDiv();		
	g_debugDiv.innerHTML += "<div>" + s + "</div>";	
}

/**
* Function : dump()
* Arguments: The data - array,hash(associative array),object
*    The level - OPTIONAL
* Returns  : The textual representation of the array.
* This function was inspired by the print_r function of PHP.
* This will accept some data as the argument and return a
* text that will be a more readable version of the
* array/hash/object that is given.
*/
function debugDump(arr, level) 
{
	var dumped_text = '';
	if (!level) 
		level = 0;
	
	//The padding given at the beginning of the line.
	var level_padding = '';
	for (var j = 0; j < level+1; j++) 
		level_padding += '\t';
		
	if (typeof(arr) == 'object') 
	{ 
		//Array/Hashes/Objects
		for (var item in arr) 
		{
			var value = arr[item];		 
			if (typeof(value) == 'object' || typeof(value) == 'function') 
			{ 
				//If it is an array,
				dumped_text += level_padding+'['+item+'] = \n';
				dumped_text += debugDump(value, level+1);
		  	} 
			else 
			{
		   		dumped_text += level_padding+'['+item+'] => ['+value+']\n';
		  	}
		}
	} 
	else if (typeof(arr) == 'function')
	{
		dumped_text = level_padding+'[code]\n';
	}
	else
	{ 
		//Stings/Chars/Numbers etc.
		dumped_text = level_padding+'['+arr+'] ('+typeof(arr)+')\n';
	}
	
	return dumped_text;
} 

function deleteTableRowsRange(t, from, to)	
{
	for (var i = to; i >= from; i--)
	{	
		if (i < t.rows.length)
			t.deleteRow(i);
	}
}

function deleteTableRows(t)
{
	while (t.rows.length > 0)
		t.deleteRow(t.rows.length-1);
}

function createShadow(shadowId, parentObj, fixPos, zIndex, width, height){
	var newDivShad = getById(shadowId);
	var append = false;
	if (newDivShad == null)
	{
		newDivShad = document.createElement('div');
		newDivShad.id = shadowId;
		append = true;
	}
	
	var ap = new absolutePosition(parentObj);
	//alert(parentObj.id+" "+ap.x+" "+ap.y);
	
	//ie6 does not have proper support for position:fixed
	if(!ie7 && ie && fixPos)
		fixPos = false;
		
	newDivShad.style.position = fixPos ? 'fixed' : 'absolute';
	newDivShad.style.backgroundColor = 'black';	
	newDivShad.style.left = (parseInt(ap.x) + 3) + 'px'; 
	newDivShad.style.top = (parseInt(ap.y) + 3) + 'px';
	newDivShad.style.width = (width ? width : parentObj.offsetWidth) + 'px';
	newDivShad.style.height = (height ? height : parentObj.offsetHeight) + 'px';
	newDivShad.className = "opaque50";
	newDivShad.style.display = '';
	newDivShad.style.zIndex = zIndex;
	
	if (append)
		document.body.appendChild(newDivShad);	
}


function getFirstElement(o)
{
	// get first element 
	var elem = o.firstChild;
	while (elem && elem.nodeType != 1)
		elem = elem.nextSibling;
	if (!elem || elem.nodeType != 1)
		return;
	
	return elem;
}

function makeHttpRequestReplaceElement(o, url)
{
	if (!o)
		return;
		
	var callback = function(text)
	{
		debugAlert(text);
		
		if (!text)
			return;
		
		// create temporary div to extract the first element from response text 
		var dummy = document.createElement('div');
		dummy.innerHTML = text;					
		var elem = getFirstElement(dummy);
		if (!elem)
			return;

		// get id and match it versus o's id
		if (!elem.id || elem.id != o.id)
			return;
			
		// get o's parent and o's index in childNodes collection
		if (!o.parentNode)
			return;
		
		// Reparent elem 
		dummy.removeChild(elem);
		o.parentNode.replaceChild(elem, o);
		
		// Execute any js 
		executeInnerHtml(elem);
	}
	
	debugAlert(url);
	makeHttpGetRequest(url, callback, false);
}

function parseMoney(v)
{
	return parseFloat(v.replace(/,/g, ""));
}

function formatMoney(v)
{
	if (isNaN(v))
		return '';
		
	var s = formatFloat(v, 2);
	var i = s.length - 3 - 3;
	while (i >= 1 && s.charAt(i-1) != '-')
	{
		s = s.substring(0, i)+','+s.substring(i);
		i -= 3;
	}
	
	return s;
}

// Array.indexOf( value, begin, strict ) - Return index of the first element that matches value
function arrayIndexOf(that, v, b, s) 
{
 	for (var i = +b || 0, l = that.length; i < l; i++ ) 
	{
		if (that[i] === v || s && that[i] == v) 
			return i; 
	}
	return -1;
}

function setBodyScrollTop(scrollTop)
{
	var scrollTop = 0;
	if (document.documentElement && document.documentElement.scrollTop != 'undefined')
		document.documentElement.scrollTop = scrollTop;
	else if (document.body)
		document.body.scrollTop = scrollTop;
}

function getBodyScrollOffsets()
{
	var scrollTop = 0;
	if (document.documentElement && document.documentElement.scrollTop != 'undefined')
		scrollTop = document.documentElement.scrollTop;
	else if (document.body)
		scrollTop = document.body.scrollTop;
		
	var scrollLeft = 0;
	if (document.documentElement && document.documentElement.scrollLeft != 'undefined')
		scrollLeft = document.documentElement.scrollLeft;
	else if (document.body)
		scrollLeft = document.body.scrollLeft;
		
	return [scrollLeft, scrollTop];
}

function getMouseXYBody(e) 
{
	if (typeof(e.clientX) != 'undefined')
	{
		var scrollOffsets = getBodyScrollOffsets();
				
		x = e.clientX + scrollOffsets[0];
		y = e.clientY + scrollOffsets[1];
	}
	else
	{
		x = e.pageX
		y = e.pageY
	}
	
	// catch possible negative values in NS4
	if (x < 0)
		x = 0;
	if (y < 0)
		y = 0;
	
	return [x, y];
}

function getDateYear(d)
{
	return ie ? d.getYear() : 1900+d.getYear();
}

//function to check valid email address 
function isValidEmail(s){ 
	var validRegExp = /^[^@]+@[^@]+.[a-z]{2,}$/i; 
	// search email text for regular exp matches 
	if (s.search(validRegExp) == -1) { 	
		return false; 
	} 
	return true; 
} 

//function to check valid postal code
function isValidPostal(code, country){ 	
	var length = code.length;
	
	switch (country) {
		case 'US':
			if ((length != 5) && (length != 9)) {
				return false;
			}
			if(!isDigits(code))
				return false;
		break;
		
		case 'CA':
			if (length != 6) {
				return false;
			}
			var validRegExp = /^[a-z][0-9][a-z][0-9][a-z][0-9]$/i; 
			if (code.search(validRegExp) == -1) { 	
				return false; 
			}
		break;
	}	
	return true; 
}

function isValidDate(dateStr, format) {
   if (format == null) { format = "MDY"; }
   format = format.toUpperCase();
   if (format.length != 3) { format = "MDY"; } 
   if ( (format.indexOf("M") == -1) || (format.indexOf("D") == -1) || (format.indexOf("Y") == -1) ) { format = "MDY"; }
   if (format.substring(0, 1) == "Y") { // If the year is first
      var reg1 = /^\d{2}(\-|\/|\.)\d{1,2}\1\d{1,2}$/
      var reg2 = /^\d{4}(\-|\/|\.)\d{1,2}\1\d{1,2}$/
   } else if (format.substring(1, 2) == "Y") { // If the year is second
      var reg1 = /^\d{1,2}(\-|\/|\.)\d{2}\1\d{1,2}$/
      var reg2 = /^\d{1,2}(\-|\/|\.)\d{4}\1\d{1,2}$/
   } else { // The year must be third
      var reg1 = /^\d{1,2}(\-|\/|\.)\d{1,2}\1\d{2}$/
      var reg2 = /^\d{1,2}(\-|\/|\.)\d{1,2}\1\d{4}$/
   }
   // If it doesn't conform to the right format (with either a 2 digit year or 4 digit year), fail
   if ( (reg1.test(dateStr) == false) && (reg2.test(dateStr) == false) ) { return false; } 
   var parts = dateStr.split(RegExp.$1); // Split into 3 parts based on what the divider was
   // Check to see if the 3 parts end up making a valid date
   if (format.substring(0, 1) == "M") { var mm = parts[0]; } else if (format.substring(1, 2) == "M") { var mm = parts[1]; } else { var mm = parts[2]; }
   if (format.substring(0, 1) == "D") { var dd = parts[0]; } else if (format.substring(1, 2) == "D") { var dd = parts[1]; } else { var dd = parts[2]; }
   if (format.substring(0, 1) == "Y") { var yy = parts[0]; } else if (format.substring(1, 2) == "Y") { var yy = parts[1]; } else { var yy = parts[2]; }
   if (parseFloat(yy) <= 50) { yy = (parseFloat(yy) + 2000).toString(); }
   if (parseFloat(yy) <= 99) { yy = (parseFloat(yy) + 1900).toString(); }
   var dt = new Date(parseFloat(yy), parseFloat(mm)-1, parseFloat(dd), 0, 0, 0, 0);
   if (parseFloat(dd) != dt.getDate()) { return false; }
   if (parseFloat(mm)-1 != dt.getMonth()) { return false; }
   return true;
}

function CRect(x, y, w, h)
{
	this.x = x;
	this.y = y;
	this.w = w;
	this.h = h;
}

function interpolatePoint(x1, y1, x2, y2, r)
{
	return [ x1+r*(x2-x1), y1+r*(y2-y1) ];
}

function interpolateRect(rect1, rect2, r)
{
	p1 = interpolatePoint(rect1.x, rect1.y, rect2.x, rect2.y, r); 
	p2 = interpolatePoint(rect1.x + rect1.w, rect1.y + rect1.h, rect2.x + rect2.w, rect2.y + rect2.h, r); 
	return new CRect(p1[0], p1[1], p2[0]-p1[0], p2[1]-p1[1]);
}

function animateElements(oElems, rects1, rects2, frames, totalTime, callback)
{	
	var fade = (ie && !ie7) || !ie; 
	
	var rects = new Array();
	if (!fade)
	{
		for (var k = 0; k < oElems.length; k++)
		{
			rects[k] = new Array();
			if (frames < 2)
				frames = 2;
			rects[k][0] = rects1[k];
			rects[k][frames-1] = rects2[k];
			for (var i = 1; i < frames-1; i++)
				rects[k][i] = interpolateRect(rects1[k], rects2[k], i/(frames-1));
		}
	}
		
	var args = [0, 0];	
	
	var fn = function()
	{
		if (args[0] >= frames)
		{
			clearInterval(args[1]);
			if (typeof(callback) == 'function')
				callback();
			return;
		}
		
		var i = args[0]++;
		
		for (var k = 0; k < oElems.length; k++)
		{
			var oElem = oElems[k];
			oElem.style.display = 'none';
			if (fade)
			{
				if (typeof(oElem._maxOpacity) != 'undefined')
					setOpacity(oElem, (i+1)/frames*oElem._maxOpacity);
				else
					setOpacity(oElem, (i+1)/frames);
			}
			else
			{
				oElem.style.left = Math.round(rects[k][i].x).toString()+'px';
				oElem.style.top = Math.round(rects[k][i].y).toString()+'px';
				oElem.style.width = Math.round(rects[k][i].w).toString()+'px';
				oElem.style.height = Math.round(rects[k][i].h).toString()+'px';
			}
			oElem.style.display = '';
		}
	}
	
	args[1] = setInterval(fn, totalTime*1000/frames);
}

function animateElement(oElem, rect1, rect2, frames, totalTime)
{
	animateElements([oElem], [rect1], [rect2], frames, totalTime);
}

var G_SCROLLBAR_WIDTH = null;
var G_SCROLLBAR_HEIGHT = null;

function getScrollBarWidth () {
	if (G_SCROLLBAR_WIDTH)
		return G_SCROLLBAR_WIDTH;
	var inner = document.createElement('p');
	inner.style.width = "100%";
	inner.style.height = "200px";
	
	var outer = document.createElement('div');
	outer.style.position = "absolute";
	outer.style.top = "0px";
	outer.style.left = "0px";
	outer.style.visibility = "hidden";
	outer.style.width = "200px";
	outer.style.height = "150px";
	outer.style.overflow = "hidden";
	outer.appendChild (inner);
	
	document.body.appendChild (outer);
	var w1 = inner.offsetWidth;
	outer.style.overflow = "scroll";
	var w2 = inner.offsetWidth;
	if (w1 == w2) w2 = outer.clientWidth;
	
	document.body.removeChild (outer);
	
	G_SCROLLBAR_WIDTH = (w1 - w2);
	return G_SCROLLBAR_WIDTH;
};

function getScrollBarHeight() {
	if (G_SCROLLBAR_HEIGHT)
		return G_SCROLLBAR_HEIGHT;
	G_SCROLLBAR_HEIGHT = getScrollBarWidth();
	return G_SCROLLBAR_HEIGHT;
}

function setOpacity(obj, o) {
	obj.style["-moz-opacity"] = obj.style["opacity"] = o;
	obj.style["filter"] = "progid:DXImageTransform.Microsoft.Alpha(opacity="+Math.round(o*100).toString()+")";
}


function floatHoursToString(h, opts) {
	var fZeroPad = typeof(opts['zeropad']) != 'undefined' && opts['zeropad'];
	var fAmPm = typeof(opts['ampm']) != 'undefined' && opts['ampm'];
	var fHideMinZero = typeof(opts['hideMinZero']) != 'undefined' && opts['hideMinZero'];
	
	var hh = Math.floor(h);
	var mm = Math.round(h*60)%60;
	if (fAmPm) {
		var suf = hh >= 12 ? ' pm' : ' am';
		var hh2 = hh%12;
		if (hh2 < 1)
			hh2 = 12;
		if (fZeroPad) {			
			return zeroPad(hh2, 2)+(fHideMinZero && mm == 0 ? '' : ':'+zeroPad(mm, 2))+suf;
		} else {
			return hh2.toString()+(fHideMinZero && mm == 0 ? '' : ':'+zeroPad(mm, 2))+suf;
		}
	} else {
		if (fZeroPad) {
			return zeroPad(hh, 2)+(fHideMinZero && mm == 0 ? '' : ':'+zeroPad(mm, 2));
		} else {
			return hh.toString()+(fHideMinZero && mm == 0 ? '' : ':'+zeroPad(mm, 2));
		}
	}
}

function floatDaysHoursToString(h) {
	var dd = Math.floor(h/24);
	
	var sdd = '';
	var shh = '';
	if (dd > 0)	
		sdd = dd.toString()+' days ';
	if (h%24 !== 0)
		shh = floatHoursToString(h%24, {'hideMinZero' : true})+' hours';
	return sdd+shh;
}

function dateFromStringMDY(s)
{
	var date = null;
	try
	{
		var y, m, d;
		var a = s.split('/');
		if (a.length != 3)
			return null;
		y = parseInt(a[2], 10);
		m = parseInt(a[0], 10)-1;
		d = parseInt(a[1], 10);
		date = new Date(y, m, d, 0, 0, 0, 0);
		if (isNaN(date))
			date = null;
	}
	catch (e)
	{
		date = null;
	}
	return date;	
}

function dateDiffDays(d1, d2)
{
 	return Math.round((d1-d2)/86400000);
}

function dateToStringMDY(d)
{
	if (d == null)
		return "";
	var ret = (d.getMonth()+1).toString()+'/'+d.getDate().toString()+'/'+getDateYear(d).toString();
	return ret;
}

function dateAddDays(d, days)
{
	var ret = new Date(d);
	ret.setDate(ret.getDate()+days);
	return ret;
}

function dateAddMonths(d, months)
{
	var ret = new Date(d);
	ret.setMonth(ret.getMonth()+months);
	return ret;
}

function charPadLeft(s, n, char)
{
	var ret = '';
	for (var i = 0; i < n; i++)
		ret += char;
	return ret+s;
}

function formatFloat(f, pos)
{
	var minus = f < 0;
	f = Math.abs(f);
	
	var fact = 1;
	for (var i = 0; i < pos; i++)
		fact *= 10;
	f2 = Math.round(f*fact);
	var sf2 = f2.toString();
	if (sf2.length <= pos) // Number too small 
		return (minus ? '-' : '')+'0.'+charPadLeft(sf2, pos-sf2.length, '0');
	else
		return (minus ? '-' : '')+sf2.substr(0, sf2.length-pos)+'.'+sf2.substr(sf2.length-pos);
}

function enableTextBox(o, en)
{
	o.readOnly = !en;
	o.disabled = !en;
	toggleClass(o, 'disabled', !en);
}

function enableDrop(o, en)
{
	o.disabled = !en;
}

function enableRadio(o, en)
{
	o.disabled = !en;
}

function enableControl(o, en)
{
	if (typeof(o) == 'string')
		o = getById(o);

	if (o.tagName.toLowerCase() == 'select')
		enableDrop(o, en);
	else if (o.tagName.toLowerCase() == 'input')
	{
		if (o.type.toLowerCase() == 'text')
			enableTextBox(o, en);
		else if (o.type.toLowerCase() == 'radio')
			enableRadio(o, en);
	}
}

function showControl(o, en)
{
	if (typeof(o) == 'string')
		o = getById(o);
	if(!o)
		return;
		
	en ? o.style.display = '' : o.style.display = 'none';
}

function toggleClass(o, cls, cond)
{
	if (cond && self.addClass)
		addClass(o, cls);
	else
	if(self.removeClass)
		removeClass(o, cls);
}


/* 
* Constants
*/
var G_MONTHS_LONG 	= new Array("January","February","March","April","May","June","July","August","September","October","November","December");
var G_MONTHS_SHORT	= new Array("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec");
var G_DAYS_LONG		= new Array("Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday");
var G_DAYS_SHORT	= new Array("Sun","Mon","Tue","Wed","Thu","Fri","Sat");

function dateGetDowName(dow)
{
	if (dow < 0 || dow > 6)
		return '';
	return G_DAYS_SHORT[dow];
}

function dateToViewLong(d, noDayName)
{
	return ( (noDayName ? "" : G_DAYS_SHORT[d.getDay()] + ", ") + G_MONTHS_SHORT[d.getMonth()] + " " + zeroPad(d.getDate(),2,true) + " '" + d.getFullYear().toString().substr(2));
}

function objectIsArray(a) {
	if (!a || typeof(a) != 'object')
		return false;
	return (new Array).constructor == a.constructor;
}

function objectIsDate(d) {
	if (!d || typeof(d) != 'object')
		return false;
	return (new Date).constructor == d.constructor;
}

function objectIsString(s) {
	if (!s)
		return false;
	if (typeof(s) == 'string')
		return true;
	if (typeof(s) != 'object')
		return false;
	return (new String).constructor == s.constructor;
}

// Does not support undefined 
function objectTypeOf(o) {
	if (o === null) 
		return 'object';
	var t = typeof(o);		
	if (t != 'object')
		return t;
	if (objectIsArray(o))
		return 'array';
	else if (objectIsDate(o))
		return 'date';
	else if (objectIsString(o))
		return 'string';
	else
		return t;
}

function isDigits(str) {
	return !/\D/.test(str); // doesn't contain non-digit
}


// par1 and par2 are url encoded
function mergeRequestParams(par1, par2) {
	var ap1 = par1.split('&');
	var ap2 = par2.split('&');
	
	var map1 = new Array();
	var map2 = new Array();
	
	for (var i = 0; i < ap1.length; i++)
	{
		if (ap1[i] == '')
			continue;
		var t = ap1[i].split('=');
		map1[t[0]] = t[1];
	}
	for (var i = 0; i < ap2.length; i++)
	{
		if (ap2[i] == '')
			continue;
		var t = ap2[i].split('=');
		map2[t[0]] = t[1];
	}
	
	var map3 = new Array();
	for (var x in map1)
		map3[x] = map1[x];
	for (var x in map2)
		map3[x] = map2[x];
		
	var i = 0;
	r = new Array();
	for (var x in map3)
		r[i++] = x+'='+map3[x];
		
	return r.join('&');
}

MU_TABLE = new Object();
MU_TABLE['length'] = [ 1, 2.54 ]; // 1 cm, 1 in
MU_TABLE['weight'] = [ 1, 0.45359237 ]; // 1 kg, 1 lbs

function syncMuFields(o1, o2, muType, muIndex)
{
	// o1 has changed --> sync o2
	var _mu_value1 = o1.getAttribute("_mu_value");
	if (o1.value == _mu_value1) // no change
		return; 
	o1.setAttribute("_mu_value", o1.value);
	
	var v1 = parseFloat(o1.value);
	if (isNaN(v1))
	{
		o2.value = '';
		o2.setAttribute("_mu_value", o2.value);
		return;
	}
	
	if (typeof(MU_TABLE[muType]) == 'undefined')
		return;
	
	var f1 = MU_TABLE[muType][muIndex];
	var f2 = MU_TABLE[muType][1-muIndex];
	var v2 = f1*v1/f2;
	
	o2.value = formatFloat(v2, 2);
	o2.setAttribute("_mu_value", o2.value);
}

// TODO: check this
function rangeSel(o,vmin,vmax){
	if(!vmin && vmin!=0)
		vmin = o.options[o.selectedIndex].value;
	if(!vmax)
		vmax =  o.options[o.selectedIndex].value;
	var l = o.id.length;
	var minmax = o.id.substr(l-3,l);
	if(minmax == 'min'){
		var o2 = document.getElementById(o.id.substr(0,l-3)+'max');
		if(!o2)	return;	
		if(parseInt(o2.options[o2.selectedIndex].value) < parseInt(o.options[o.selectedIndex].value)) comboSelectByValue(o2,vmax);		
	}
	else
	if(minmax == 'max'){
		var o2 = document.getElementById(o.id.substr(0,l-3)+'min');
		if(!o2)	return;	
		if(parseInt(o2.options[o2.selectedIndex].value) > parseInt(o.options[o.selectedIndex].value))	comboSelectByValue(o2,vmin);
	}	
	return;
}