function $(aId){
	return document.getElementById(aId);
}

function $$(aParentNode, aTagName, aDeep){
	aDeep = (aDeep != null)?aDeep:true;

	var vNodes = [];
	var vFoundNodes = false;
	if(aDeep){
		try{
			if(aParentNode.getElementsByTagName){
				vNodes = aParentNode.getElementsByTagName(aTagName);
				vFoundNodes = true;
			}
		}
		catch(e){}
	}
	
	if(!vFoundNodes){
		var vNode;
		var vNodeName;
		for(var n=0;n<aParentNode.childNodes.length;n++){
			vNode = aParentNode.childNodes[n];

			if(vNode.nodeType == 1){	//ELEMENT_NODE
				if(vNode.nodeName.toLowerCase() == aTagName.toLowerCase()){
					vNodes.push(vNode);
				}
				if(aDeep && vNode.childNodes.length){
					vNodes = vNodes.concat($$(vNode, aTagName));
				}
			}
		}
	}
	
	return vNodes;
}

function isIE(){
	 return (navigator.appVersion.indexOf("MSIE") > -1);
}

function isNull(aVar){
	var vIsNull = true;
	if(aVar != null){
		vIsNull = false;
	}
	return vIsNull;
}

function printNode( aNode ){
	var vSerialized = String.empty;
	if (window.XMLSerializer){
		vSerialized = (new XMLSerializer()).serializeToString(aNode);
	}
	else if(aNode.xml){
		return aNode.xml;
	}
	else{
		vSerialized = "XML.serialize is not supported or can't serialize " + aNode; 
	}
	
	return vSerialized;
}

function getURL(aUrl, aCallback, aAsync){
	RD.XHTML.XMLHttpRequestHandler(aUrl, "GET", aCallback, null, null, null, aAsync);
};

function postURL(aUrl, aCallback, aAsync){
	RD.XHTML.XMLHttpRequestHandler(aUrl, "POST", aCallback, null, null, null, aAsync);
}

function parseXML(aXmlString, aAsync){
	aAsync = (aAsync != null)?aAsync:true;
	var vDocument = null;
	if (window.ActiveXObject){
		vDocument=new ActiveXObject("Microsoft.XMLDOM");
		vDocument.async = aAsync.toString();
		vDocument.loadXML(aXmlString);
	}
	else if(window.DOMParser){
		var vDomParser = new DOMParser();
		vDomParser.async = aAsync.toString();
		vDocument = vDomParser.parseFromString(aXmlString,"text/xml");
	}

	return vDocument;
}

function initWindowDimensions(){
	window._width = null;
	window._height = null;
	
	if(window.innerHeight && window.scrollMaxY) {// Firefox
		_width = window.innerWidth + window.scrollMaxX - 14;
		_height = window.innerHeight + window.scrollMaxY + 22;
	}
	else if(document.body.scrollHeight > document.body.offsetHeight){ // all but Explorer Mac
		_width = document.body.scrollWidth;
		_height = document.body.scrollHeight;
	}
	else{ // works in Explorer 6 Strict, Mozilla (not FF) and Safari
		_width = document.body.offsetWidth;
		_height = document.body.offsetHeight;
	}
	
	_height += 2;
}

//dynamically loads a javascript file
//Caveats: nothing to tell you when script is loaded
function loadScript(aUrl){
   RD.XHTML.NodeHelper.createNode("script",  $$(document, "head")[0], {
			type:"text/javascript"
			,src:aUrl
		}
	);
}

//
//	Set up String object properties/methods
//
String.empty = "";

String.customObjectDelim = "__$";

String._fillObjectFromArray = function(aObject, aArray){
	for(var n=0;n<aArray.length;n++){
		var vPropertyValue = aArray[n].split(":");
		vPropertyValue[1] = vPropertyValue[1].trim();
		if(vPropertyValue[1].indexOf("\"") < 0 && vPropertyValue[1].indexOf("'") < 0){
			vPropertyValue[1] = RD.XHTML.convertStringToObjectType(vPropertyValue[1]);
		}
		else{
			vRegExp = new RegExp("\"", "gi");
			vPropertyValue[1] = vPropertyValue[1].replace(vRegExp, String.empty);
			vRegExp = new RegExp("'", "gi");
			vPropertyValue[1] = vPropertyValue[1].replace(vRegExp, String.empty);
		}
		
		vPropertyValue[0] = vPropertyValue[0].stripSpaces();
		aObject[vPropertyValue[0]] = vPropertyValue[1];
	}
	
	return aObject;
};

String.prototype.toObject = function(aUseCustomObjectDelim){
	var vString = this.toString();
	var vRegExp = new RegExp("[\\n\\r\\t]+", "gi");
	vString =  vString.replace(vRegExp, String.empty);

	vString = vString.trim();
	//alert(vString)
	var vObject = {};
	if(vString.getCharsCount("{") != vString.getCharsCount("}")){
		vObject = "Error: curly braces do not match";
	}
	else{
		var vObjectProperties = null;
		//string the curly braces
		if(vString.substr(0, 1) == "{"){
			vString = vString.substr(1);
		}
		
		if(vString.substr(vString.length-1) == "}"){
			vString = vString.substr(0, vString.length-1);
		}
		
		var vLeftCurlyCount = 0;
		var vRightCurlyCount = 0;
		var vMatch = null;
		var vStartString = String.empty;
		var vTempString = String.empty;
		var vFoundPropertyValuePair = String.empty;
		var vStoredObjectProperty = String.empty;
		var vProperty = String.empty;
		var vValue = null;
		var vIndex = -1;
		var vRegExp = null;
		var vRegExp2 = null;
		
		vNewCount = 1;
		var vDelim = ",";
		while(vString.length){
			if(aUseCustomObjectDelim){
				vRegExp = new RegExp(".*?__\\$");
				vDelim = String.customObjectDelim;
			}
			else{
				vRegExp = new RegExp(".*?\\,");
			}
			vMatch = vString.match(vRegExp);

			//go up to the first comma;
			//if we are inside of a object we need to go to the ending curly brace for the object
			//else if the previous character ends with a right curly brace
			//then we know we have to handle a property/object pair
			
			if(!vMatch){
				vFoundPropertyValuePair = vString + vDelim;
			}
			else{
				vFoundPropertyValuePair = vMatch.join(String.empty);
			}
			vString = vString.substr(vFoundPropertyValuePair.length).trim();

			vLeftCurlyCount = vFoundPropertyValuePair.getCharsCount("{");
			vRightCurlyCount = vFoundPropertyValuePair.getCharsCount("}");
			
			//alert(vFoundPropertyValuePair)
			if(vRightCurlyCount < vLeftCurlyCount){
				//loop through the remaining string until we get to a matching right curly brace
				vRegExp = new RegExp(".*?\\}");
				var vCount = 1;
				while(vRightCurlyCount < vLeftCurlyCount){
					vMatch = vString.match(vRegExp);
					if(vMatch){
						vMatch = vMatch.join("}");
						vFoundPropertyValuePair += vMatch;
						
						vString = vString.substr(vMatch.length);
						
						vLeftCurlyCount = vFoundPropertyValuePair.getCharsCount("{");
						vRightCurlyCount = vFoundPropertyValuePair.getCharsCount("}");
						
					}
				}
			}
			else{
				vFoundPropertyValuePair = vFoundPropertyValuePair.substr(0 , vFoundPropertyValuePair.length-vDelim.length).trim();
			}
			
			if(vFoundPropertyValuePair.length){
				//alert(vFoundPropertyValuePair)
				vIndex = vFoundPropertyValuePair.indexOf(":");
				vProperty = vFoundPropertyValuePair.substr(0, vIndex);
				vValue = vFoundPropertyValuePair.substr(vIndex+1);

				if(vValue.endsWith("}")){
					//found a object.
					vObject[vProperty] = vValue.toObject();
				}
				else{
					vRegExp = new RegExp("^\"|\"$", "gi");
					vRegExp2 = new RegExp("^'|'$", "gi");
					if(vValue.match(vRegExp) || vValue.match(vRegExp2)){
						vValue = vValue.replace(vRegExp, String.empty);
						vValue = vValue.replace(vRegExp, String.empty);
						
						vObject[vProperty] = vValue;
					}
					else{
						vObject[vProperty] = RD.XHTML.convertStringToObjectType(vValue);
					}
				}
			}
		}
	}
	
	return vObject;
};

String.prototype.toFcUpperCase = function(){
	return this.replace(/\b./g, function(s){return s.toUpperCase();});
};

String.prototype.toCamelCase = function(){
	var vString = this.replace(/\b./g, function(s){return s.toUpperCase();});
	vString = vString.stripSpaces();
	return vString.substring(0, 1).toLowerCase() + vString.substring(1);
};

String.prototype.toProperName = function(){
	return this.toLowerCase().replace(/\b./g, function(s){return s.toUpperCase();});
};

String.prototype.getCharsCount = function(aChars){
	var vCount = this.length;
	var vString = this.toString();
	if(aChars){
		var vArray = vString.split(aChars);
		vCount = vArray.length - 1;
	}
	
	return vCount;
};

String.prototype.trim = function(){
	var vString = this.toString();
	if(window._isAVGInstalled){
		var vRegExp = new RegExp("^[ \\n\\t\\r\\u00A0]");
		while(vString.match(vRegExp)){
			vString = vString.replace(vRegExp, String.empty);
		}
		vRegExp = new RegExp("[ \\n\\t\\r\\u00A0]$");
		while(vString.match(vRegExp)){
			vString = vString.replace(vRegExp, String.empty);
		}
	}
	else{
		vString = vString.replace(/^\s+/, String.empty);
		vString = vString.replace(/\s+$/, String.empty);
	}
	
	return vString;
};

String.prototype.stripSpaces = function(){
	var vString = this.toString();
	var vRegExp = new RegExp("[ \\n\\r\\u00A0]", "gi");
	vString = vString.replace(vRegExp, String.empty);
	
	return vString;
};

String.prototype.stripWhitespace = function(){
	var vString = this.toString();
	var vRegExp = new RegExp("([\\t\\n\\r]+)|((  )+)", "gi");
	vString = vString.replace(vRegExp, String.empty);
	
	return vString;
};

String.prototype.startsWith = function(aSearchParam){
	var exists = false;
	
	if(aSearchParam){
		var vString = this.toString();
		var vRegExp = new RegExp(".*\/.*", "gi");
		vString.replace(vRegExp, "\/");
		vRegExp = new RegExp(".*\..*", "gi");
		vString.replace(vRegExp, "\.");

		vRegExp = new RegExp("^" + aSearchParam);
		exists = (vString.search(vRegExp) > -1 )?true:false;
	}

	return exists;
};

String.prototype.endsWith = function(aSearchParam){
	var exists = false; 
	
	if(aSearchParam){
		var vString = this.toString();
		var vRegExp = new RegExp(".*\/.*", "gi");
		vString.replace(vRegExp, "\/");
		vRegExp = new RegExp(".*\..*", "gi");
		vString.replace(vRegExp, "\.");
		
		vRegExp = new RegExp(aSearchParam + "$");
		exists = (vString.search(vRegExp) > -1 )?true:false;
	}
	
	return exists;
};

String.encodeURI = function(aString){
	aString = aString?aString:String.empty;
	
	if(window.encodeURIComponent){
		aString = encodeURIComponent(aString);
		aString = aString.replace(/%20/, "+");
		aString = aString.replace(/(.{0,3})(%0A)/g,
			function(m, p1, p2) {
				return p1 + (p1=="%0D"?"":"%0D")+p2;
			}
		);
	};
	
	return aString;
};

String.decodeURI = function(aString){
	aString = aString?aString:String.empty;

	if(window.encodeURIComponent){
		aString = aString.replace(/"%0D%0A"/g,
			function() {
				return "%0D";
			}
		);
		aString = aString.replace(/\+/, "%20");

		aString = decodeURIComponent(aString);
	};
	
	return aString;
};

Array.prototype.item = function(aIndex){
	return this[aIndex];
}

if(!Array.prototype.push){
	Array.prototype.push = function(aValue){
		this[this.length] = aValue;
	}
}
//
//	Set up Array object properties/methods
//
Array.prototype.duplicate = function(){
	return this.slice(0);
};

Array.prototype.getIndex = function(aValue){
    var vIndex = -1;
	var vLength = this.length - 1;
    for(var n=vLength;n>=0;n--){
        if(aValue == this[n]){
            vIndex = n;
            break;
        }
    }

    return vIndex;
};

Array.prototype.insertAt = function(aValue, aIndex){
    if(aIndex < 0 || aIndex > this.length){
        aIndex = this.length;
    }

    this.splice(aIndex, 0, aValue);
};

Array.prototype.removeAt = function(aIndex){
    if(aIndex < 0 || aIndex > this.length-1){
        aIndex = this.length-1;
    }

    return this.splice(aIndex, 1)[0];
};

Array.prototype.removeValue = function(aValue){
	var vIndex = this.getIndex(aValue);
	return this.removeAt(vIndex);
};

Array.prototype.swapValues = function(aIndex1, aIndex2){
	var vValue1 = this[aIndex1];
	var vValue2 = this[aIndex2];
	
	this[aIndex1] = vValue2;
	this[aIndex2] = vValue1;
};

Array.prototype.unique = function(){
    var vUniqueArry = new Array();
	var vLength = this.length - 1;
    for(var n=vLength;n>=0;n--){
        if(vUniqueArry.getIndex(this[n]) < 0){
            vUniqueArry.push(this[n]);
        }
    }

    return vUniqueArry;
};

Array.prototype.sortStrings = function(){
	this.sort(this.compareStrings);
};

Array.prototype.compareStrings = function(a, b){
	if ( a < b ) return -1;
	if ( a > b ) return 1;
	
	return 0;
};

//
//	Set up Math object properties/methods
//
Math.max3 = function(aValue1, aValue2, aValue3 ){
	var vMax = null;
	vMax = Math.max(aValue1, aValue2);
	vMax = Math.max(vMax, aValue3);
	
    return vMax;
};

Math.min3 = function(aValue1, aValue2, aValue3 ){
	var vMin = null;
	vMin = Math.min(aValue1, aValue2);
	vMin = Math.min(vMin, aValue3);
	
    return vMin;
};

Math.roundDecimal = function(aNum, aDecimal){
	aDecimal = (aDecimal!=null)?aDecimal:2;
	aNum = parseFloat(aNum);
    var result = Math.round(aNum*Math.pow(10, aDecimal))/Math.pow(10, aDecimal);
    return result;
};

Math.randomInt = function(aLow, aHigh){
	var vMax = Math.floor(Number.MAX_VALUE);
	aLow = (aLow!=null)?aLow:-vMax;
	aHigh = (aHigh!=null)?aHigh:vMax;
	var vRandom = Math.floor((aHigh - aLow + 1) * Math.random()) + aLow;

	return vRandom;
};

Math.randomFloat = function(aLow, aHigh, aNumDec){
	aNumDec = (aNumDec!=null)?parseFloat(aNumDec):0;
	var vDev = Math.pow(10, aNumDec);
	
	var strLow = Math.abs(aLow).toString();
	var vIndex = strLow.indexOf(".");
	vIndex = (vIndex> -1)?vIndex:0;
	aLow =  parseFloat(strLow.substr(0, vIndex) + strLow.substr(vIndex, aNumDec+1));

	var vDir = (aHigh<0)?-1:1;
	var strHigh = Math.abs(aHigh).toString();
	vIndex = strHigh.indexOf(".");
	vIndex = (vIndex> -1)?vIndex:0;
	aHigh = parseFloat(strHigh.substr(0, vIndex) + strHigh.substr(vIndex, aNumDec+1)) * vDir;
	
	aLow *= vDev;
	aHigh *= vDev;

	var vRandom = Math.randomInt(0)%(aLow + aHigh);
	vRandom -= aLow;
	vRandom = vRandom/vDev;

	return vRandom;
};

Math.radiansToDegrees = function(aRadians){
	return aRadians*(180/ Math.PI);
};

Math.degreesToRadians = function(aDegrees){
	aDegrees = aDegrees > 360?aDegrees%360:aDegrees;
	return aDegrees*( Math.PI/180);
}

//returns an x and y position based off of passed in degrees
Math.degreesToRD_Point = function( aDegrees ){
	aDegrees = aDegrees > 360?aDegrees%360:aDegrees;
	var vRadians = (aDegrees * Math.PI) / 180.0;
	var vPoint = new RD.XHTML.Point( Math.cos(vRadians),  Math.sin(vRadians));
	
	return vPoint;
};


//return angle (in degrees) based off of distance travled from a starting point
Math.RD_PointToDegrees = function(aStartLoc, aCurLoc){
	// calculate distance from starting point
	var delta_x =  aCurLoc.x - aStartLoc.x;
	var delta_y = aCurLoc.y - aStartLoc.y;
		
	// calculate the angle from the starting point in radians
	var radians = Math.atan2(delta_y, delta_x);

	// convert radians to degrees
	var degrees = (Math.floor(radians/(Math.PI/180)));
	if(degrees < 0 ){
		degrees = 180 + degrees;
	}
	else if(degrees > 0){
		degrees = 180 + degrees;
	}

	return degrees;
};
