// JavaScript Document
function toJSObject(xdoc) {
	if(!xdoc){ return null; }
	var tmpObj = new Object();
		tmpObj.typeOf = "JSXBObject";
	var xroot = (xdoc.nodeType == 9)?xdoc.documentElement:xdoc;
	
	if(xdoc.nodeType == 3 || xdoc.nodeType == 4) {
		return xdoc.nodeValue;
	}
	setObjects(tmpObj, xroot);
	//Set Object Nodes
	function setObjects(obj, node) {
		var elemName;
		var cnode;
		var tObj;
		var cName = "";
		if(!node) { return null; }
		if(!node.hasChildNodes()) { return null; }
		var nodeCount = node.childNodes.length - 1;	
		//Set node attributes if any
		if(node.attributes.length > 0) {
			setAttributes(obj, node);
		}
		//Process child nodes
		obj._children = new Array();
		var n = 0;
		do { //Order is irrelevant (speed-up)
			cnode = node.childNodes[n];
			switch(cnode.nodeType) {
				case 1: //Node
				elemName = formatName(cnode.tagName);
				if(cName != elemName) { obj._children.push(elemName); }
					if(node.getElementsByTagName(cnode.tagName).length > 1) {
						if(!obj[elemName]) {
							obj[elemName] = new Array(); //Create Collection
						}
						tObj = new Object();
						obj[elemName].push(tObj);
						if(cnode.attributes.length > 0) {
							setAttributes(tObj, cnode);
						}
						//Set Helper functions (contains, indexOf, sort, etc);
						if(!obj[elemName].contains) {
							setHelpers(obj[elemName]);
						}
					} else {
						obj[elemName] = new Object(); //Create Single Object
						tObj = obj[elemName];
						if(cnode.attributes.length > 0) {
							setAttributes(tObj, cnode);
						}
					}
				cName = elemName;
				if(cnode.hasChildNodes()) {
					setObjects(tObj, cnode); //Recursive Call
				}
				break;
				case 3: //Text Value
				obj.Text = cnode.nodeValue.trim();
				break;
				case 4: //CDATA
				obj.Text = (cnode.text)?cnode.text.trim():cnode.nodeValue.trim();
				break;
			}
		} while(n++ < nodeCount);
	}
	//Set collections
	function setHelpers(grpObj) {		
		grpObj.contains = function(obj, attr) {
			if(this.length > 0) {
				var maxLen = this.length -1;
				try {
					do {
						if(this[maxLen][attr] == obj) {
							return true;
						}
					} while(maxLen--);
				} catch(e) {return false;}
				return false;
			}
		}
		
		grpObj.indexOf = function(obj, attr) {
			var pos = -1;
			if(this.length > 0) {
				var maxLen = this.length -1;
				try {
					do {
						if(this[maxLen][attr] == obj) {
							pos = maxLen;
						}
					} while(maxLen--);
				} catch(e) {return -1;}
				return pos;
			}
		}
		
		grpObj.SortByAttribute = function(col, dir) {
			if(this.length) {				
				function sortFn(a, b) {
					var res;
					var tA, tB;
					if(isNumeric(a[col]) && isNumeric(b[col])) {
						tA = parseFloat(a[col]);
						tB = parseFloat(b[col]);
					} else {
						tA = a[col];
						tB = b[col];
					}
					res = (tA < tB)?-1:1;
					if(dir) {
						res = (dir.toUpperCase() == "DESC")?(0 - res):res;
					}
					return res;
				}
				this.sort(sortFn);
			}
		}
		
		grpObj.SortByValue = function(dir) {
			if(this.length) {
				function sortFn(a, b) {
					var res;
					var tA, tB;
					if(isNumeric(a.Text) && isNumeric(b.Text)) {
						tA = parseFloat(a.Text);
						tB = parseFloat(b.Text);
					} else {
						tA = a.Text;
						tB = b.Text;
					}
					res = (tA < tB)?-1:1;
					if(dir) {
						res = (dir.toUpperCase() == "DESC")?(0 - res):res;
					}
					return res;
				}
				this.sort(sortFn);
			}
		}
		grpObj.SortByNode = function(node, dir) {
			if(this.length) {
				function sortFn(a, b) {
					var res;
					var tA, tB;
					if(isNumeric(a[node].Text) && isNumeric(b[node].Text)) {
						tA = parseFloat(a[node].Text);
						tB = parseFloat(b[node].Text);
					} else {
						tA = a[node].Text;
						tB = b[node].Text;
					}
					res = (tA < tB)?-1:1;
					if(dir) {
						res = (dir.toUpperCase() == "DESC")?(0 - res):res;
					}
					return res;
				}
				this.sort(sortFn);
			}
		}
	}
	//Set Attributes of an object
	function setAttributes(obj, node) {
		if(node.attributes.length > 0) {
			var a = node.attributes.length-1;
			obj._attributes = new Array();
			do { //Order is irrelevant (speed-up)
				obj._attributes.push(formatName(node.attributes[a].name));
				obj[formatName(node.attributes[a].name)] = node.attributes[a].value.trim();
			} while(a--);
		}
	}
	//Alters attribute and collection names to comply with JS
	function formatName(name) {
		var tName = name.replace('-','_');
		return tName;
	}
	//Clean-up memmory
	xdoc = null;
	xroot = null;
	return tmpObj;
}

//Local Copy (for stand alone operation)
String.prototype.trim = function() {
     return this.replace(/^\s+|\s+$/gm,'');
}
//Checks if number is numeric
function isNumeric(str) {
	str = String(str);
	var pattern = /^((-)?([0-9]*)((\.{0,1})([0-9]+))?$)/;
	return pattern.test(str);
}
