
/*
how to use it?
if have any bug please send email to me:
MAIL:242772@qq.com

var request=new Ajax.Request("aa.xml?getdate="+(new Date().getDate()),{
method: 'get',
onSuccess:function(response)
{
        //here create new object and pass response partten
       var xml2json=new Xml2JSON(response,"");
       //xml2json.Convert this function return a json object
        var json=xml2json.Convert();
        
        alert(json.rows.row[0]["@rid"]);
}
});
*/

var Xml2JSON=Class.create();
Xml2JSON.prototype={
    initialize:function(response){
    this.xmlDoc=this.getDom(response);
    },
    getDom:function(response){
        var dom = null;
        if(response.responseText){
            if (window.DOMParser){ //support firefox
          try {dom = (new DOMParser()).parseFromString(response.responseText, "text/xml");} 
          catch(e){dom = null;}
           }
           else if(window.ActiveXObject){
          try{
         dom = new ActiveXObject('Microsoft.XMLDOM');
         dom.async = false;
         if(!dom.loadXML(response.responseText)) // parse error ..
            window.alert(dom.parseError.reason + dom.parseError.srcText);
              }catch(e){ dom = null; }
            }
           else
          alert("cannot parse xml string!");
        }
        if (dom.nodeType == 9) // document node
      dom = dom.documentElement;
      
      return dom;
    },
    toObj: function(xml){
        var o = {};
         if (xml.nodeType==1) {   // element node ..
            if (xml.attributes.length)   // element with attributes  ..
               for (var i=0; i<xml.attributes.length; i++)
                  o["@"+xml.attributes[i].nodeName] = (xml.attributes[i].nodeValue||"").toString();
            if (xml.firstChild) { // element has child nodes ..
               var textChild=0, cdataChild=0, hasElementChild=false;
               for (var n=xml.firstChild; n; n=n.nextSibling) {
                  if (n.nodeType==1) hasElementChild = true;
                  else if (n.nodeType==3 && n.nodeValue.match(/[^ \f\n\r\t\v]/)) textChild++; // non-whitespace text
                  else if (n.nodeType==4) cdataChild++; // cdata section node
               }
               if (hasElementChild) {
                  if (textChild < 2 && cdataChild < 2) { // structured element with evtl. a single text or/and cdata node ..
                     this.removeWhite(xml);
                     for (var n=xml.firstChild; n; n=n.nextSibling) {
                        if (n.nodeType == 3)  // text node
                           o["#text"] = this.escape(n.nodeValue);
                        else if (n.nodeType == 4)  // cdata node 
                           o["#cdata"] = this.escape(n.nodeValue);
                        else if (o[n.nodeName]) {  // multiple occurence of element ..
                           if (o[n.nodeName] instanceof Array)
                              o[n.nodeName][o[n.nodeName].length] = this.toObj(n);
                           else
                              o[n.nodeName] = [o[n.nodeName], this.toObj(n)];
                        }
                        else  // first occurence of element..
                           o[n.nodeName] = this.toObj(n);
                     }
                  }
                  else { // mixed content
                     if (!xml.attributes.length)
                        o = this.escape(this.innerXml(xml));
                     else
                        o["#text"] = this.escape(this.innerXml(xml));
                  }
               }
               else if (textChild) { // pure text
                  if (!xml.attributes.length)
                     o = this.escape(this.innerXml(xml));
                  else
                     o["#text"] = this.escape(this.innerXml(xml));
               }
               else if (cdataChild) { // cdata
                  if (cdataChild > 1) {
                     o = this.escape(this.innerXml(xml));
                  } else
                     
                     for (var n=xml.firstChild; n; n=n.nextSibling) {
//                        console.log("name="+n.parentNode.nodeName);
                        o["CDATA"] = this.escape(n.nodeValue);
//                        o[n.parentNode.nodeName] = this.escape(n.nodeValue);
                     }
	
               }
            }
            if (!xml.attributes.length && !xml.firstChild) o = null;
         }
         else if (xml.nodeType==9) { // document.node
            o = this.toObj(xml.documentElement);
         }
         else
            alert("unhandled node type: " + xml.nodeType);
         return o;
    },
    toJson: function(o, name, ind) {
        
        var json = name ? ("\""+name+"\"") : "";
         if (o instanceof Array) {
            for (var i=0,n=o.length; i<n; i++)
               o[i] = this.toJson(o[i], "", ind+"\t");
            json += (name?":[":"[") + (o.length > 1 ? ("\n"+ind+"\t"+o.join(",\n"+ind+"\t")+"\n"+ind) : o.join("")) + "]";
         }
         else if (o == null)
            json += (name&&":") + "null";
         else if (typeof(o) == "object") {
            var arr = [];
            for (var m in o)
               arr[arr.length] = this.toJson(o[m], m, ind+"\t");
            json += (name?":{":"{") + (arr.length > 1 ? ("\n"+ind+"\t"+arr.join(",\n"+ind+"\t")+"\n"+ind) : arr.join("")) + "}";
         }
         else if (typeof(o) == "string")
            json += (name&&":") + "\"" + o.toString() + "\"";
         else
            json += (name&&":") + o.toString();
         return json;
    },
    innerXml: function(node) {
        var s = ""
         if ("innerHTML" in node)
            s = node.innerHTML;
         else {
            var asXml = function(n) {
               var s = "";
               if (n.nodeType == 1) {
                  s += "<" + n.nodeName;
                  for (var i=0; i<n.attributes.length;i++)
                     s += " " + n.attributes[i].nodeName + "=\"" + (n.attributes[i].nodeValue||"").toString() + "\"";
                  if (n.firstChild) {
                     s += ">";
                     for (var c=n.firstChild; c; c=c.nextSibling)
                        s += asXml(c);
                     s += "</"+n.nodeName+">";
                  }
                  else
                     s += "/>";
               }
               else if (n.nodeType == 3)
                  s += n.nodeValue;
               else if (n.nodeType == 4)
                  s += "<![CDATA[" + n.nodeValue + "]]>";
               return s;
            };
            for (var c=node.firstChild; c; c=c.nextSibling)
               s += asXml(c);
         }
         return s;
    },
    escape: function(txt){
        return txt.replace(/[\\]/g, "\\\\")
                   .replace(/[\"]/g, '\\"')
                   .replace(/[\n]/g, '\\n')
                   .replace(/[\r]/g, '\\r');
    },
    removeWhite: function(e) {
        e.normalize();
         for (var n = e.firstChild; n; ) {
            if (n.nodeType == 3) {  // text node
               if (!n.nodeValue.match(/[^ \f\n\r\t\v]/)) { // pure whitespace text node
                  var nxt = n.nextSibling;
                  e.removeChild(n);
                  n = nxt;
               }
               else
                  n = n.nextSibling;
            }
            else if (n.nodeType == 1) {  // element node
               this.removeWhite(n);
               n = n.nextSibling;
            }
            else                      // any other node
               n = n.nextSibling;
         }
         return e;
    },
    Convert:function(){
        var o=this;
        var tab="";
        var json = o.toJson(o.toObj(o.removeWhite(o.xmlDoc)), o.xmlDoc.nodeName, "\t");
       var jsonStr= "{\n" + tab + (tab ? json.replace(/\t/g, tab) : json.replace(/\t|\n/g, "")) + "\n}";
       return jsonStr.evalJSON();
    },
    toGoogleFeedApi:function(json){
var items = null;
if(json.rss.channel.item.length!=null)
  items = json.rss.channel.item;
else {
  items = new Array();
  items[0] = json.rss.channel.item;
}
        if (items!=null) {
          json.rss.channel.entries = new Array();
	  for (var i = 0; i < items.length; i++) {
            var entry = items[i];
            var newEntry = new Object();

            //  TODO handle content and summary.
            if(entry.description!=null) {
             if(entry.description.CDATA!=null) {
              newEntry.content = entry.description.CDATA;
             }else
              newEntry.content = entry.description;
            }
            if(entry.title!=null)
              newEntry.title = entry.title;
            if(entry.link!=null)
  	      newEntry.link = entry.link;
            //  TODO handle publishedDate (published/pubDate) & categories
            if(entry.pubDate!=null)
               newEntry.publishedDate = entry.pubDate;
            if(entry.published!=null)
               newEntry.publishedDate = entry.published;
            if(entry.categories!=null)
               newEntry.categories = entry.categories;
            json.rss.channel.entries[json.rss.channel.entries.length] = newEntry;
          }
        }
       return json;
    }
}