// ===============================================================
// CBkort version 2.x, copyright Carl Bro GIS&IT, 2006
// ===============================================================
// $Archive: /Products/CBKort2/development/2.5/standard/wwwroot/js/standard/cbhttp.js $ 
// $Date: 27-01-09 11:40 $
// $Revision: 17 $ 
// $Author: Kpo $
// =============================================================== 
 

// **********************************************************
//  CBHTTP request
//
//  Denne javascript bruges til at kalde CBkort (CBInfo)
//  WEB services med en URL. Det forventede response 
//  fra den kaldte service er PCollections på XML format.
//  Den modtagne pcollection (i form af et DOM objekt), bliver
//  wrappet i javascript klasser som modsvarer java PCollections 
//
//  Niels Mørck, Carl Bro GIS & IT
//
//  Afhængigheder
//     ingen
//
//  Der er følgende klasser i denne fil.
//
//  --------------------------------------------------------------------
//  Klasse CBhttp  
//    Kald af CBinfo
//
//  Metoder 
//    executeUrl (url : string, showErrors : boolean) return PCollection  
//
//    url skal være en fuldstændig url inkl. parametre.
//    
//    Hvis showErrors er sand, vises en alertboks, hvis der modtages andet end
//    en pcollection fra serveren. Hvis den er falsk kastes en exception.   
//
//  --------------------------------------------------------------------  
//  Klasse PCollection 
//    Abstract superklasse for PComposite, RowList, Row og Column
//
//  Metoder 
//    getName      () return String     : returnerer indholdet af "name"   attributten
//    getId        () return String     : returnerer indholdet af "pcolid" attributten
//    isPComposite () return boolean    : sand hvis elementet er en <pcomposite> 
//    isRowList    () return boolean    : sand hvis elementet er en <rowlist> 
//    isRow        () return boolean    : sand hvis elementet er en <row> 
//    isColumn     () return boolean    : sand hvis elementet er en <column> 
//
//  --------------------------------------------------------------------  
//  Klasse PComposite
//    Repæsentation af et XML <pcomposite> element.
//  Metoder 
//    get  (index : integer)  return PCollection   : returnere subelementet ud fra et indeks
//    get  (name  : string)   return PCollection   : returnere subelementet ud fra et navn
//    size ()                 return integer       : antallet af subelementer
//
//  --------------------------------------------------------------------  
//  Klasse RowList
//    Repæsentation af et XML <rowlist> element.
//
//  Metoder 
//    row  (index : integer)  return Row        : returnerer subrækken udfra et index
//    size ()                 return integer    : antallet af rækker
//
//  --------------------------------------------------------------------  
//  Klasse Row
//    Repæsentation af XML <row> element.
//
//  Metoder 
//    column  (index : integer)  return Column     : returnerer kolonnen udfra et index
//    column  (name  : string)   return Column     : returnerer kolonnen udfra et navn
//    size ()                    return integer    : antallet af kolonner
//
//  --------------------------------------------------------------------  
//  Klasse Column
//    Repæsentation af XML <col> element.
//
//  Metoder 
//    getValue ()  return string   : returnerer kolonnens værdi
//
// 
//  --------------------------------------------------------------------  
//  Eksempel
//
//
//  try
//  {  
//     var cbHttp  = new CBhttp();
//
//     // -- Udfør request - der returneres altid en "envelope", som er en PComposite
//
//     var pcomp = cbHttp.executeUrl ("http://webkort:8080/cbkort?page=get-rowlist", false);
//
//     // -- Tag første - og i dette tilfælde det eneste - element ud af pcomp.
//     var rowList = pcomp.get(0);
//      
//     if (rowList == null || ! rowList.isRowList() )
//        {  alert ("Forkert response - rowlist forventet");
//        }
//     else
//        {  var str = "";
//
//           // Iterer gennem alle rows
//           for (var rowIx=0; rowIx<rowList.size(); rowIx++)
//           {  
//              // -- Tag næste row 
//              var row = rowList.row(rowIx);
//
//              str += "Row " + rowIx + ":\n"; 
//
//              // -- Iterer gennem alle columns 
//              for (var colIx=0; colIx<row.size(); colIx++)
//              {  
//                 // -- Tag næste colomn
//                 var column = row.column(colIx);
//
//                 // -- Udskriv column navn og værdi
//                 var name   = column.getName();
//                 var value  = column.getValue();
//
//                 str += " " + name + "=" + value + "\n";  
//              }
//           }  
//
//           alert (str);
//        }
//  }
//  catch (e)
//  {  if (e instanceof Error)
//     {  alert (e.message);
//     }
//     else
//     {  alert (e);
//     }
//  } 
//
// **********************************************************



// *************************************************
//  CBhttp
// *************************************************

function CBhttp () 
{
    this.responsePCollection = null;
    this.responseDOM = null;
    this.xmlHttp = cbhttp_createXMLHttpRequestObject ();
    this.errorText = null;
    this.method = "post";
}

CBhttp.prototype.setMethod = function (method)
{
    this.method = method;
}

CBhttp.prototype.executeUrl = function (url_arg, showErrors)
{     
    var strRand = "&jdh=" +Math.random();
    var url = url_arg + strRand;
    this.responseDOM = null;
    this.errorText   = null;
    
    if (showErrors == null)
    {  showErrors = true;
    }

    if (this.method == "get")
    {  this.xmlHttp.open ("get", url, false);
       this.xmlHttp.send(null);
    }
    else
    {  url = encodeURI (url);
       var a = url.split("?");
       var data = null;
       url = a[0];
    
       if (a.length>1)
       {  
          data = a[1];
       }
       this.xmlHttp.open ("post", url, false);  
       this.xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
       this.xmlHttp.send(data);
    }
    
    if (this.xmlHttp.readyState == 4) 
    {
        if (ie)
        {  
           this.responseDOM = this.xmlHttp.responseXML;
           this.responseDOM.validateOnParse = false;
        }  
        else 
        {  
           var objDOMParser = new DOMParser();
           this.responseDOM = objDOMParser.parseFromString(this.xmlHttp.responseText, "text/xml");
        }
        
        if (this.responseDOM!=null)
        {   
          this.responsePCollection = cbhttp_docToPCollection(this.responseDOM);
        }

        if (this.responsePCollection==null)
        {  
           var s = cbhttp_getError (this.responseDOM);
           if (s!=null && s!="")
           {   this.errorText = s;
               if (showErrors)
                  alert (cbInfo.getString("standard.error.wrong_response_from", "server", "\nUrl: " + url + "\n" + this.xmlHttp.responseText));
               else
                  throw new Error (this.errorText);
           }
        }
        else
        {  return this.responsePCollection;
        }
     }
} 


CBhttp.prototype.executeUrlAsync = function (url, showErrors, responseHandler)
{  
	var xmlHttpObj = cbhttp_createXMLHttpRequestObject (); 
	var responseDOM = null;
	var errorText   = null;

	if (showErrors == null)
	{  
		showErrors = true;
	}

    if(responseHandler)
    {
    	xmlHttpObj.onreadystatechange=function() 
    	{
    		if (xmlHttpObj.readyState == 0)		{ /* uninitialized */ }
    		else if (xmlHttpObj.readyState == 1)	{ /* loading */}			
    		else if (xmlHttpObj.readyState == 2)	{ /* loaded */ }
    		else if (xmlHttpObj.readyState == 3)	{ /* interactive */ }		
    		else if (xmlHttpObj.readyState == 4)   	  /* complete */
    		{
    			if (ie)
    			{  
    				responseDOM = xmlHttpObj.responseXML;
    				responseDOM.validateOnParse = false;
    			}  
    			else 
    			{  
    				var objDOMParser = new DOMParser();
    				responseDOM = objDOMParser.parseFromString(xmlHttpObj.responseText, "text/xml");
    			}        
    
    			if (responseDOM != null)
    			{   
    				CBhttp.responsePCollection = cbhttp_docToPCollection(responseDOM);
    			}
    
    			if (CBhttp.responsePCollection==null)
    			{  
                   if (showErrors)
                   {  
                      alert (cbInfo.getString("standard.error.wrong_response_from", "server", "\nUrl: " + url + "\n" + this.xmlHttpObj.responseText));
                   }
                   else
                   {  errorText = cbhttp_getError (responseDOM);
                      throw new Error (errorText);
                   }
                }    
    
    			responseHandler(CBhttp.responsePCollection);
    		}
    	}
    }

    if (this.method == "get")
    {  xmlHttpObj.open ("get", url, true);
       xmlHttpObj.send(null);
    }
    else
    {  url = encodeURI (url);
       var a = url.split("?");
       var data = null;
       url = a[0];
    
       if (a.length>1)
       {  
          data = a[1];
       }
       xmlHttpObj.open ("post", url, true);  
       xmlHttpObj.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
       xmlHttpObj.send(data);
    }
} 


CBhttp.prototype.getResponsePCollection = function ()
{  return this.responsePCollection;
}

CBhttp.prototype.getResponsetext = function ()
{  return this.xmlHttp.responseText;
}

CBhttp.prototype.getResponseDOM = function ()
{  return this.xmlHttp.responseDOM;
}

CBhttp.prototype.getErrorText = function ()
{  return this.xmlHttp.errorText;
}


// *************************************************
//  PCollection
// *************************************************

function PCollection ()
{   this.node = null;
}

PCollection.prototype.getName = function ()
{ if (this.node!=null) 
     return this.node.getAttribute("name").toLowerCase();
  else
     return null;    
}

PCollection.prototype.getType = function ()
{  this.node.tagName;
}

PCollection.prototype.getId = function ()
{  if (this.node!=null) 
      return this.node.getAttribute("pcolid");
   else
      return null;    
}

PCollection.prototype.setNode = function (node)
{  this.node = node;
}

PCollection.prototype.getNode = function ()
{  return this.node;
}

PCollection.prototype.isPComposite = function ()
{  return this.node.tagName.toLowerCase() == "pcomposite";
}

PCollection.prototype.isRowList = function ()
{  return this.node.tagName.toLowerCase() == "rowlist";
}

PCollection.prototype.isRow = function ()
{  return this.node.tagName.toLowerCase() == "row";
}

PCollection.prototype.isColumn = function ()
{  return this.node.tagName.toLowerCase() == "col";
}

PCollection.prototype.toString = function ()
{  return "PCollection '" + this.getName() + "'";
}

// *************************************************
//   Pcomposite
// *************************************************

PComposite.prototype = new PCollection();
PComposite.prototype.constructor=PComposite;

function PComposite (node)
{       
   	PCollection.prototype.setNode.call(this, node);
    this.elements = cbhttp_getPCollectionChilds (node);
}

PComposite.prototype.size = function ()
{   
    return this.elements.length;
}

PComposite.prototype.get = function (nameOrIndex)
{   
    if (! isNaN(nameOrIndex))
    {   var n = this.elements[nameOrIndex];
    
        if (n==null)
        {  return null;
        }
    
        return cbhttp_toPCollection (n);
    }
    else
    {  var size = this.size();
       nameOrIndex = nameOrIndex.toLowerCase();
       for (var pc_i2=0; pc_i2<size; pc_i2++)
       {

           var n = this.elements[pc_i2]; //Get the node

           if (n!=null)
           {  var nodeName = n.getAttribute("name").toLowerCase(); //Get name
              // Compare
              if (nodeName == nameOrIndex)
              {  return this.get(pc_i2);     // Convert to PCollection and return
              } 
           }
         
          // Old 
          // var n = this.get(pc_i2);
          // if (n!=null && n.getName() == nameOrIndex)
          // {  return n;
          // }
       }
    
       return null;
    }      
}

PComposite.prototype.toString = function ()
{  var str = "BEGIN PComposite '" + this.getName() + "'\n";
   
   for (var pc_i1=0; pc_i1<this.size(); pc_i1++)
   {   str += this.get(pc_i1).toString() + "\n";
   }
   
   str += "END PComposite '" + this.getName() + "'\n";
   
   return str;
}


// *************************************************
//   RowList
// *************************************************

RowList.prototype = new PCollection();
RowList.prototype.constructor=RowList;

function RowList (node)
{  
   	PCollection.prototype.setNode.call(this, node);
    this.elements = cbhttp_getPCollectionChilds (node);   	
}

RowList.prototype.size = function ()
{   
    return this.elements.length;
}


RowList.prototype.row = function (index)
{   
    var n = this.elements[index];   
    
    if (n!=null)
       return new Row(n);
    else
       return null;   
}

RowList.prototype.toString = function ()
{  var str = "BEGIN RowList '" + this.getName() + "'\n";
   
   for (var r_i1=0; r_i1<this.size(); r_i1++)
   {  str += this.row(r_i1).toString() + "\n";
   }
   
   str += "END RowList '" + this.getName() + "'\n";
   
   return str;
}


// *************************************************
//   Row
// *************************************************

Row.prototype = new PCollection();
Row.prototype.constructor=Row;

function Row (node)
{  
   	PCollection.prototype.setNode.call(this, node);
    this.elements = cbhttp_getPCollectionChilds (node);   	
}

Row.prototype.size = function ()
{   
    return this.elements.length;
}

Row.prototype.column = function (nameOrIndex)
{   
    if (! isNaN(nameOrIndex))
    {  var n = this.elements[nameOrIndex];   
    
       if (n!=null)
          return new Column(n);
       else
          return null;   
    }
    else
    {
       var size = this.size();
       nameOrIndex = nameOrIndex.toLowerCase();    
       for (var c_i1=0; c_i1<size; c_i1++)
       {

           var n = this.elements[c_i1]; //Get the node

           if (n!=null)
           {  var nodeName = n.getAttribute("name").toLowerCase(); //Get name
              // Compare names              
              if (nodeName == nameOrIndex)
              {  return this.column(c_i1);     // Convert to PCollection and return
              } 
           }


          // Old
          // var n = this.column(c_i1);
          // if (n!=null && n.getName() == nameOrIndex)
          // {  return n;
          // }
       }
    
       return null;
    }      
}

Row.prototype.toString = function ()
{  var str = "BEGIN Row '" + this.getName() + "'\n";
   
   str += "Columns: ";
   for (var i=0;i<this.size(); i++)
   {  str += this.column(i).toString() + ", ";
   }
   
   str += "\nEND Row '" + this.getName() + "'\n";

   return str;
}


// *************************************************
//   Column
// *************************************************

Column.prototype = new PCollection();
Column.prototype.constructor=Column;

function Column (node)
{  
   PCollection.prototype.setNode.call(this, node);
}

Column.prototype.getValue = function  ()
{  var n = this.getNode();

   if (n!=null && n.firstChild!=null)
      return n.firstChild.nodeValue;
   else      
      return null;   
}

Column.prototype.toString = function ()
{  return  this.getName() + "='" + this.getValue() + "'";
}


// *************************************************
//  Utility funtioner
// *************************************************


function cbhttp_docToPCollection (doc)
{
    var rootElm = doc.getElementsByTagName("pcollection").item(0)
    
    var pcol = cbhttp_toPCollection (rootElm);
    
    return pcol;
}       
  
function cbhttp_toPCollection (node)
{
    var pcol = null;
 
    if (node!=null)
    {  
       if (node.tagName == "pcollection")
          pcol = cbhttp_toPCollection (cbhttp_getPCollectionChilds (node)[0]);
       else if (node.tagName == "pcomposite")
          pcol = new PComposite(node);
       else if (node.tagName == "rowlist")
          pcol = new RowList(node);
       else if (node.tagName == "row")
          pcol = new Row(node);
       else if (node.tagName == "col")
          pcol = new Column(node);
    }      
    
    return pcol;
}     

function cbhttp_getPCollectionChilds (node)
{
   var result = new Array();
   var count = 0;
   
   var nl = node.childNodes;
   for (var i=0; node!=null && i<nl.length; i++)
   { 
      var n = nl.item(i);
      var tagName = n.tagName; 


      if (tagName=="pcollection" || tagName=="pcomposite" || tagName=="rowlist" || tagName=="row" || tagName=="col")
      {  result[count] = n;
         count++;
      }
   }

   return result;
}   

function cbhttp_createXMLHttpRequestObject ()
{
    var xmlhttpreq = false;
    try
    {
        if(window.XMLHttpRequest)
        {
           xmlhttpreq = new XMLHttpRequest(); 
        }
        else if(window.ActiveXObject)
        {
            try 
            {  xmlhttpreq = new ActiveXObject("Msxml2.XMLHTTP.3.0"); 
            }
            catch(e)
            {  xmlhttpreq = new ActiveXObject("Microsoft.XMLHTTP"); 
            }
        }
    }
    catch (e)
    {
       throw new Error (cbInfo.getString("standard.error.could_not_create_object", "XMLHTTP"));    
    }

    return xmlhttpreq;    
}

function cbhttp_getError (responseDOM) 
{
   var msg = "";

   if (responseDOM==null)
   {  msg = cbInfo.getString("standard.error.wrong_response_from", "server", "");
   }
   else 
   {     
      var node = responseDOM.getElementsByTagName('error');
      
      if (node!=null)
      {   
         var e = "";
         var m = "";

         var eNode = responseDOM.getElementsByTagName('exception');
         if (eNode!=null && eNode.length > 0)
         {  e = eNode[0].firstChild.nodeValue;
         } 

         var mNode  = responseDOM.getElementsByTagName('message');
         if (mNode != null && mNode.length > 0)
         {  
            for (i=0; i<mNode.length; i++) 
            {  if (m=="")
               {  m = m + ". ";
               }   
               m = m + mNode[i].firstChild.nodeValue;
            }   
         } 
      
         if (m != "")
         {  msg = m;
            if (e != "")
            {  msg = msg + " (" + e + ")";  
            }            
         }
         else
         {  msg = e;
         }
      }
      else
      {  msg = cbInfo.getString("standard.error.wrong_response_from", "server", ""); 
      }
   }   
   
   return msg;
}



// *************************************************
//   Additional functions (kpo)
// *************************************************

//returns the dom - if responseHandler pressent, then run async
function cbhttp_getRequestDom(url,get,responseHandler)
{
    var xmlhttpreq = cbhttp_createXMLHttpRequestObject();
    var async = false;
    if(responseHandler)
    {
        async = true;
        xmlhttpreq.onreadystatechange=function() 
        {
            if (xmlhttpreq.readyState == 0)         { /* uninitialized */ }
            else if (xmlhttpreq.readyState == 1)    { /* loading */}            
            else if (xmlhttpreq.readyState == 2)    { /* loaded */ }
            else if (xmlhttpreq.readyState == 3)    { /* interactive */ }       
            else if (xmlhttpreq.readyState == 4)      /* complete */
            {
                if (ie)
                {  
                    responseDOM = xmlhttpreq.responseXML;
                    responseDOM.validateOnParse = false;
                }  
                else 
                {  
                    var objDOMParser = new DOMParser();
                    responseDOM = objDOMParser.parseFromString(xmlhttpreq.responseText, "text/xml");
                }        
                responseHandler(responseDOM);
            }
        }
    }
    try
    {
        if(get)
        {
            xmlhttpreq.open('GET', url, async);
            xmlhttpreq.send(null);
        }
        else
        {
            var baseurl = url.split('?');
            xmlhttpreq.open('POST', baseurl[0], async);
            xmlhttpreq.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
            xmlhttpreq.send(baseurl[1]);
        }
        if(!async)
            return xmlhttpreq.responseXML;
    }
    catch (e)
    {
        alert(cbInfo.getString("standard.error.an_error_occurred")+ ": " + e);
        return null;
    }
}
function cbhttp_getRequestJSON(url,get,responseHandler)
{
    var xmlhttpreq = cbhttp_createXMLHttpRequestObject();
    var async = false;
    if(responseHandler)
    {
        async = true;
        xmlHttpObj.onreadystatechange=function() 
        {
            if (xmlHttpObj.readyState == 0)         { /* uninitialized */ }
            else if (xmlHttpObj.readyState == 1)    { /* loading */}            
            else if (xmlHttpObj.readyState == 2)    { /* loaded */ }
            else if (xmlHttpObj.readyState == 3)    { /* interactive */ }       
            else if (xmlHttpObj.readyState == 4)      /* complete */
            {
                responseHandler(XMLObjectifier.xmlToJSON(xmlhttpreq.responseXML));
            }
        }
    }
    try
    {
        if(get)
        {
            xmlhttpreq.open('GET', url, false);
            xmlhttpreq.send(null);
        }
        else
        {
            var baseurl = url.split('?');
            xmlhttpreq.open('POST', baseurl[0], false);
            xmlhttpreq.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
            xmlhttpreq.send(baseurl[1]);
        }
        if(!async)
            return XMLObjectifier.xmlToJSON(xmlhttpreq.responseXML);
    }
    catch (e)
    {
        alert(cbInfo.getString("standard.error.an_error_occurred")+ ": " + e);
        return null;
    }
}

//returns number of childnodes (not grandchilds and so on)
function cbhttp_getLength(node)
{
    var l=0;
    var lnode = cbhttp_getFirstChild(node);
    if(!lnode)
        return l;
    for (var inode = 0; inode < 99999; inode++) 
    {
        if(lnode.nodeType!=3)
            l++;
        lnode = lnode.nextSibling;
        if(!lnode)
            inode = 100000;
    }
    return l;
}
//returns the first child of a node
function cbhttp_getFirstChild(node)
{
    if(ie)
    {
        if(node)
        {
            if(!node.hasChildNodes())
                return null;
        }
        else
            return null;
    }
    var l = 99999;
    var returnnode = node.firstChild;
    for (var inode=0;inode<l;inode++) 
    {
        if(returnnode.nodeType!=3)
            return returnnode;
        returnnode = returnnode.nextSibling;
        if(!returnnode)
            inode = l+1;
    }
    return null;
}

//returns the next sisternode
function cbhttp_getNextNode(node)
{
    var l = 99999;
    var returnnode = node.nextSibling;
    if(!returnnode)
        return null;
    for (var inode=0;inode<l;inode++) 
    {
        if(returnnode.nodeType!=3)
            return returnnode;
        returnnode = returnnode.nextSibling;
        if(!returnnode)
            inode = l+1;
    }
    return null;
}

//returns a node at the next level with a specific attribute name and value
function cbhttp_getNodeByAttributeName(node,attributeName,attributeValue)
{
    var l = 99999;
    var returnnode = cbhttp_getFirstChild(node);
    if(!returnnode)
        return null;

    for (var inode=0;inode<l;inode++) 
    {
        if(returnnode.getAttribute(attributeName) == attributeValue)
            return returnnode;
        returnnode = cbhttp_getNextNode(returnnode);
        if(!returnnode)
            inode = l+1;
    }
    return null;
}


// Returns the value of the embedded textnode
function cbhttp_getNodeValue  (node)
{  
   if (node!=null && node.firstChild!=null)
      return node.firstChild.nodeValue;
   else      
      return null;   
}


/*
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
var XMLObjectifier = (function() {
    var _clone = function(obj){
        if(!!obj && typeof(obj)==="object"){
            function F(){}
            F.prototype = obj;
            return new F();
        }       
    };
    //Is Numeric check
    var isNumeric = function(s) {
        var testStr = "";
        if(!!s && typeof(s) === "string") { testStr = s; }
        var pattern = /^((-)?([0-9]*)((\.{0,1})([0-9]+))?$)/;
        return pattern.test(testStr);
    };
    var _self = {
    xmlToJSON: function(xdoc) {
        try {
            if(!xdoc){ return null; }
            var tmpObj = {};
                tmpObj.typeOf = "JSXBObject";
            var xroot = (xdoc.nodeType == 9)?xdoc.documentElement:xdoc;
                tmpObj.RootName = xroot.nodeName || "";
            if(xdoc.nodeType == 3 || xdoc.nodeType == 4) {
                return xdoc.nodeValue;
            }
            //Trim function
            function trim(s) {
                return s.replace(/^\s+|\s+$/gm,'');
            }                       
            //Alters attribute and collection names to comply with JS
            function formatName(name) {
                var regEx = /-/g;
                var tName = String(name).replace(regEx,"_");
                return tName;
            }
            //Set Attributes of an object
            function setAttributes(obj, node) {
                if(node.attributes.length > 0) {
                    var a = node.attributes.length-1;
                    var attName;
                    obj._attributes = [];
                    do { //Order is irrelevant (speed-up)
                        attName = String(formatName(node.attributes[a].name));
                        obj._attributes.push(attName);              
                        obj[attName] = trim(node.attributes[a].value);
                    } while(a--);
                }
            }
            
            //Node Prototype
            var _node = (function() {
                    var _self = {
                        activate: function() {
                            var nodes = [];
                            if(!!nodes) {
                                    nodes.getNodesByAttribute = function(attr, obj) {
                                        if(!!nodes && nodes.length > 0) {
                                            var out = [];
                                            var cNode;
                                            var maxLen = nodes.length -1;
                                            try {
                                                do {
                                                    cNode = nodes[maxLen];
                                                    if(cNode[attr] === obj) {
                                                        out.push(cNode);
                                                    }
                                                } while(maxLen--);
                                                out.reverse();
                                                return out;
                                            } catch(e) {return null;}
                                            return null;
                                        }
                                    };
                                    nodes.getNodeByAttribute = function(attr, obj) {
                                        if(!!nodes && nodes.length > 0) {
                                            var cNode;
                                            var maxLen = nodes.length -1;
                                            try {
                                                do {
                                                    cNode = nodes[maxLen];
                                                    if(cNode[attr] === obj) {
                                                        return cNode;
                                                    }
                                                } while(maxLen--);
                                            } catch(e) {return null;}
                                            return null;
                                        }
                                    };
                                    nodes.getNodesByValue = function(obj) {
                                        if(!!nodes && nodes.length > 0) {
                                            var out = [];
                                            var cNode;
                                            var maxLen = nodes.length -1;
                                            try {
                                                do {
                                                    cNode = nodes[maxLen];
                                                    if(!!cNode.Text && cNode.Text === obj) {
                                                        out.push(cNode);
                                                    }
                                                } while(maxLen--);
                                                return out;
                                            } catch(e) {return null;}
                                            return null;
                                        }
                                    };
                                    nodes.contains = function(attr, obj) {
                                        if(!!nodes && nodes.length > 0) {
                                            var maxLen = nodes.length -1;
                                            try {
                                                do {
                                                    if(nodes[maxLen][attr] === obj) {
                                                        return true;
                                                    }
                                                } while(maxLen--);
                                            } catch(e) {return false;}
                                            return false;
                                        }
                                    };
                                    nodes.indexOf = function(attr, obj) {
                                        var pos = -1;
                                        if(!!nodes && nodes.length > 0) {
                                            var maxLen = nodes.length -1;
                                            try {
                                                do {
                                                    if(nodes[maxLen][attr] === obj) {
                                                        pos = maxLen;
                                                    }
                                                } while(maxLen--);
                                            } catch(e) {return -1;}
                                            return pos;
                                        }
                                    };
                                    nodes.SortByAttribute = function(col, dir) {
                                        if(!!nodes && nodes.length > 0) {               
                                            function getValue(pair, idx) {
                                                var out = pair[idx];
                                                out = (bam.validation.isNumeric(out))?parseFloat(out):out;
                                                return out;
                                            }
                                            function sortFn(a, b) {
                                                var tA, tB;
                                                tA = getValue(a, col);
                                                tB = getValue(b, col);
                                                var res = (tA<tB)?-1:(tB<tA)?1:0;
                                                if(!!dir) {
                                                    res = (dir.toUpperCase() === "DESC")?(0 - res):res;
                                                }
                                                return res;
                                            }
                                            nodes.sort(sortFn);
                                        }
                                    };
                                    nodes.SortByValue = function(dir) {
                                        if(!!nodes && nodes.length > 0) {
                                            function getValue(pair) {
                                                var out = pair.Text;
                                                out = (bam.validation.isNumeric(out))?parseFloat(out):out;
                                                return out;
                                            }
                                            function sortFn(a, b) {
                                                var tA, tB;
                                                tA = getValue(a);
                                                tB = getValue(b);
                                                var res = (tA<tB)?-1:(tB<tA)?1:0;
                                                if(!!dir) {
                                                    res = (dir.toUpperCase() === "DESC")?(0 - res):res;
                                                }
                                                return res;
                                            }
                                            nodes.sort(sortFn);
                                        }
                                    };
                                    nodes.SortByNode = function(node, dir) {
                                        if(!!nodes && nodes.length > 0) {
                                            function getValue(pair, node) {
                                                var out = pair[node][0].Text;
                                                out = (bam.validation.isNumeric(out))?parseFloat(out):out;
                                                return out;
                                            }
                                            function sortFn(a, b) {                                     
                                                var tA, tB;
                                                tA = getValue(a, node);
                                                tB = getValue(b, node);
                                                var res = (tA<tB)?-1:(tB<tA)?1:0;
                                                if(!!dir) {
                                                    res = (dir.toUpperCase() === "DESC")?(0 - res):res;
                                                }
                                                return res;
                                            }
                                            nodes.sort(sortFn);
                                        }
                                  };
                            }
                            return nodes;
                        }
                    };
                    return _self;
            })();
            //Makes a new node of type _node;
            var makeNode = function() {
                var _fn = _clone(_node);                    
                return _fn.activate();
            }
            //Set collections
            function setHelpers(grpObj) {
                //Selects a node withing array where attribute = value
                grpObj.getNodeByAttribute = function(attr, obj) {
                    if(this.length > 0) {
                        var cNode;
                        var maxLen = this.length -1;
                        try {
                            do {
                                cNode = this[maxLen];
                                if(cNode[attr] == obj) {
                                    return cNode;
                                }
                            } while(maxLen--);
                        } catch(e) {return false;}
                        return false;
                    }
                };
                
                grpObj.contains = function(attr, obj) {
                    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(attr, obj) {
                    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 getValue(pair, idx) {
                            var out = pair[idx];
                            out = (isNumeric(out))?parseFloat(out):out;
                            return out;
                        }
                        function sortFn(a, b) {
                            var res = 0;
                            var tA, tB;                     
                            tA = getValue(a, col);
                            tB = getValue(b, col);
                            if(tA < tB) { res = -1; } else if(tB < tA) { res = 1; }
                            if(dir) {
                                res = (dir.toUpperCase() == "DESC")?(0 - res):res;
                            }
                            return res;
                        }
                        this.sort(sortFn);
                    }
                };
                
                grpObj.SortByValue = function(dir) {
                    if(this.length) {
                        function getValue(pair) {
                            var out = pair.Text;
                            out = (isNumeric(out))?parseFloat(out):out;
                            return out;
                        }
                        function sortFn(a, b) {
                            var res = 0;
                            var tA, tB;
                            tA = getValue(a);
                            tB = getValue(b);
                            if(tA < tB) { res = -1; } else if(tB < tA) { res = 1; }
                            if(dir) {
                                res = (dir.toUpperCase() == "DESC")?(0 - res):res;
                            }
                            return res;
                        }
                        this.sort(sortFn);
                    }
                };
                
                grpObj.SortByNode = function(node, dir) {
                    if(this.length) {
                        function getValue(pair, node) {
                            var out = pair[node][0].Text;
                            out = (isNumeric(out))?parseFloat(out):out;
                            return out;
                        }
                        function sortFn(a, b) {
                            var res = 0;
                            var tA, tB;
                            tA = getValue(a, node);
                            tB = getValue(b, node);
                            if(tA < tB) { res = -1; } else if(tB < tA) { res = 1; }
                            if(dir) {
                                res = (dir.toUpperCase() == "DESC")?(0 - res):res;
                            }
                            return res;
                        }
                        this.sort(sortFn);
                    }
                };
            }
            //Recursive JSON Assembler
            //Set Object Nodes
            function setObjects(obj, node) {
                var elemName;   //Element name
                var cnode;  //Current Node
                var tObj;   //New subnode
                var cName = "";
                if(!node) { return null; }              
                //Set node attributes if any
                if(node.attributes.length > 0){setAttributes(obj, node);}               
                obj.Text = "";
                if(node.hasChildNodes()) {
                    var nodeCount = node.childNodes.length - 1; 
                    var n = 0;
                    do { //Order is irrelevant (speed-up)
                        cnode = node.childNodes[n];
                        switch(cnode.nodeType) {
                            case 1: //Node
                            //Process child nodes
                            obj._children = [];
                            //SOAP XML FIX to remove namespaces (i.e. soapenv:)
                            elemName = (cnode.localName)?cnode.localName:cnode.baseName;
                            elemName = formatName(elemName);
                            if(cName != elemName) { obj._children.push(elemName); }
                                //Create sub elemns array
                                if(!obj[elemName]) {
                                    obj[elemName] = []; //Create Collection
                                }
                                tObj = {};
                                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]);
                                }   
                            cName = elemName;
                            if(cnode.hasChildNodes()) {
                                setObjects(tObj, cnode); //Recursive Call
                            }
                            break;
                            case 3: //Text Value
                            obj.Text += trim(cnode.nodeValue);
                            break;
                            case 4: //CDATA
                            obj.Text += (cnode.text)?trim(cnode.text):trim(cnode.nodeValue);
                            break;
                        }
                    } while(n++ < nodeCount);
                }
            }           
            //RUN
            setObjects(tmpObj, xroot);
            //Clean-up memmory
            xdoc = null;
            xroot = null;
            return tmpObj;  
        } catch(e) {
                return null;    
        }   
    },

    //Converts Text to XML DOM
    textToXML: function(strXML) {
        var xmlDoc = null;
        try {
            xmlDoc = (document.all)?new ActiveXObject("Microsoft.XMLDOM"):new DOMParser();
            xmlDoc.async = false;
        } catch(e) {throw new Error("XML Parser could not be instantiated");}
        var out;
        try {
            if(document.all) {
                out = (xmlDoc.loadXML(strXML))?xmlDoc:false;
            } else {        
                out = xmlDoc.parseFromString(strXML, "text/xml");
            }
        } catch(e) { throw new Error("Error parsing XML string"); }
        return out;
    }
    };
    return _self;
})();
