    
    // ========================================================================
    //  XML.ObjTree -- XML source code from/to JavaScript object like E4X
    // ========================================================================
    
    if ( typeof(XML) == 'undefined' ) XML = function() {};
    
    //  constructor
    
    XML.ObjTree = function () {
        return this;
    };
    
    //  class variables
    
    XML.ObjTree.VERSION = "0.24";
    
    //  object prototype
    
    XML.ObjTree.prototype.xmlDecl = '<?xml version="1.0" encoding="UTF-8" ?>\n';
    XML.ObjTree.prototype.attr_prefix = '-';
    XML.ObjTree.prototype.overrideMimeType = 'text/xml';
    
    //  method: parseXML( xmlsource )
    
    XML.ObjTree.prototype.parseXML = function ( xml ) {
        var root;
        if ( window.DOMParser ) {
            var xmldom = new DOMParser();
    //      xmldom.async = false;           // DOMParser is always sync-mode
            var dom = xmldom.parseFromString( xml, "application/xml" );
            if ( ! dom ) return;
            root = dom.documentElement;
        } else if ( window.ActiveXObject ) {
            xmldom = new ActiveXObject('Microsoft.XMLDOM');
            xmldom.async = false;
            xmldom.loadXML( xml );
            root = xmldom.documentElement;
        }
        if ( ! root ) return;
        return this.parseDOM( root );
    };
    
    //  method: parseHTTP( url, options, callback )
    
    XML.ObjTree.prototype.parseHTTP = function ( url, options, callback ) {
        var myopt = {};
        for( var key in options ) {
            myopt[key] = options[key];                  // copy object
        }
        if ( ! myopt.method ) {
            if ( typeof(myopt.postBody) == "undefined" &&
                 typeof(myopt.postbody) == "undefined" &&
                 typeof(myopt.parameters) == "undefined" ) {
                myopt.method = "get";
            } else {
                myopt.method = "post";
            }
        }
        if ( callback ) {
            myopt.asynchronous = true;                  // async-mode
            var __this = this;
            var __func = callback;
            var __save = myopt.onComplete;
            myopt.onComplete = function ( trans ) {
                var tree;
                if ( trans && trans.responseXML && trans.responseXML.documentElement ) {
                    tree = __this.parseDOM( trans.responseXML.documentElement );
                } else if ( trans && trans.responseText ) {
                    tree = __this.parseXML( trans.responseText );
                }
                __func( tree, trans );
                if ( __save ) __save( trans );
            };
        } else {
            myopt.asynchronous = false;                 // sync-mode
        }
        var trans;
        if ( typeof(HTTP) != "undefined" && HTTP.Request ) {
            myopt.uri = url;
            var req = new HTTP.Request( myopt );        // JSAN
            if ( req ) trans = req.transport;
        } else if ( typeof(Ajax) != "undefined" && Ajax.Request ) {
            var req = new Ajax.Request( url, myopt );   // ptorotype.js
            if ( req ) trans = req.transport;
        }
    //  if ( trans && typeof(trans.overrideMimeType) != "undefined" ) {
    //      trans.overrideMimeType( this.overrideMimeType );
    //  }
        if ( callback ) return trans;
        if ( trans && trans.responseXML && trans.responseXML.documentElement ) {
            return this.parseDOM( trans.responseXML.documentElement );
        } else if ( trans && trans.responseText ) {
            return this.parseXML( trans.responseText );
        }
    }
    
    //  method: parseDOM( documentroot )
    
    XML.ObjTree.prototype.parseDOM = function ( root ) {
        if ( ! root ) return;
    
        this.__force_array = {};
        if ( this.force_array ) {
            for( var i=0; i<this.force_array.length; i++ ) {
                this.__force_array[this.force_array[i]] = 1;
            }
        }
    
        var json = this.parseElement( root );   // parse root node
        if ( this.__force_array[root.nodeName] ) {
            json = [ json ];
        }
        if ( root.nodeType != 11 ) {            // DOCUMENT_FRAGMENT_NODE
            var tmp = {};
            tmp[root.nodeName] = json;          // root nodeName
            json = tmp;
        }
        return json;
    };
    
    //  method: parseElement( element )
    
    XML.ObjTree.prototype.parseElement = function ( elem ) {
        //  COMMENT_NODE
        if ( elem.nodeType == 7 ) {
            return;
        }
    
        //  TEXT_NODE CDATA_SECTION_NODE
        if ( elem.nodeType == 3 || elem.nodeType == 4 ) {
            var bool = elem.nodeValue.match( /[^\x00-\x20]/ );
            if ( bool == null ) return;     // ignore white spaces
            return elem.nodeValue;
        }
    
        var retval;
        var cnt = {};
    
        //  parse attributes
        if ( elem.attributes && elem.attributes.length ) {
            retval = {};
            for ( var i=0; i<elem.attributes.length; i++ ) {
                var key = elem.attributes[i].nodeName;
                if ( typeof(key) != "string" ) continue;
                var val = elem.attributes[i].nodeValue;
                if ( ! val ) continue;
                key = this.attr_prefix + key;
                if ( typeof(cnt[key]) == "undefined" ) cnt[key] = 0;
                cnt[key] ++;
                this.addNode( retval, key, cnt[key], val );
            }
        }
    
        //  parse child nodes (recursive)
        if ( elem.childNodes && elem.childNodes.length ) {
            var textonly = true;
            if ( retval ) textonly = false;        // some attributes exists
            for ( var i=0; i<elem.childNodes.length && textonly; i++ ) {
                var ntype = elem.childNodes[i].nodeType;
                if ( ntype == 3 || ntype == 4 ) continue;
                textonly = false;
            }
            if ( textonly ) {
                if ( ! retval ) retval = "";
                for ( var i=0; i<elem.childNodes.length; i++ ) {
                    retval += elem.childNodes[i].nodeValue;
                }
            } else {
                if ( ! retval ) retval = {};
                for ( var i=0; i<elem.childNodes.length; i++ ) {
                    var key = elem.childNodes[i].nodeName;
                    if ( typeof(key) != "string" ) continue;
                    var val = this.parseElement( elem.childNodes[i] );
                    if ( ! val ) continue;
                    if ( typeof(cnt[key]) == "undefined" ) cnt[key] = 0;
                    cnt[key] ++;
                    this.addNode( retval, key, cnt[key], val );
                }
            }
        }
        return retval;
    };
    
    //  method: addNode( hash, key, count, value )
    
    XML.ObjTree.prototype.addNode = function ( hash, key, cnts, val ) {
        if ( this.__force_array[key] ) {
            if ( cnts == 1 ) hash[key] = [];
            hash[key][hash[key].length] = val;      // push
        } else if ( cnts == 1 ) {                   // 1st sibling
            hash[key] = val;
        } else if ( cnts == 2 ) {                   // 2nd sibling
            hash[key] = [ hash[key], val ];
        } else {                                    // 3rd sibling and more
            hash[key][hash[key].length] = val;
        }
    };
    
    //  method: writeXML( tree )
    
    XML.ObjTree.prototype.writeXML = function ( tree ) {
        var xml = this.hash_to_xml( null, tree );
        return this.xmlDecl + xml;
    };
    
    //  method: hash_to_xml( tagName, tree )
    
    XML.ObjTree.prototype.hash_to_xml = function ( name, tree ) {
        var elem = [];
        var attr = [];
        for( var key in tree ) {
            if ( ! tree.hasOwnProperty(key) ) continue;
            var val = tree[key];
            if ( key.charAt(0) != this.attr_prefix ) {
                if ( typeof(val) == "undefined" || val == null ) {
                    elem[elem.length] = "<"+key+" />";
                } else if ( typeof(val) == "object" && val.constructor == Array ) {
                    elem[elem.length] = this.array_to_xml( key, val );
                } else if ( typeof(val) == "object" ) {
                    elem[elem.length] = this.hash_to_xml( key, val );
                } else {
                    elem[elem.length] = this.scalar_to_xml( key, val );
                }
            } else {
                attr[attr.length] = " "+(key.substring(1))+'="'+(this.xml_escape( val ))+'"';
            }
        }
        var jattr = attr.join("");
        var jelem = elem.join("");
        if ( typeof(name) == "undefined" || name == null ) {
            // no tag
        } else if ( elem.length > 0 ) {
            if ( jelem.match( /\n/ )) {
                jelem = "<"+name+jattr+">\n"+jelem+"</"+name+">\n";
            } else {
                jelem = "<"+name+jattr+">"  +jelem+"</"+name+">\n";
            }
        } else {
            jelem = "<"+name+jattr+" />\n";
        }
        return jelem;
    };
    
    //  method: array_to_xml( tagName, array )
    
    XML.ObjTree.prototype.array_to_xml = function ( name, array ) {
        var out = [];
        for( var i=0; i<array.length; i++ ) {
            var val = array[i];
            if ( typeof(val) == "undefined" || val == null ) {
                out[out.length] = "<"+name+" />";
            } else if ( typeof(val) == "object" && val.constructor == Array ) {
                out[out.length] = this.array_to_xml( name, val );
            } else if ( typeof(val) == "object" ) {
                out[out.length] = this.hash_to_xml( name, val );
            } else {
                out[out.length] = this.scalar_to_xml( name, val );
            }
        }
        return out.join("");
    };
    
    //  method: scalar_to_xml( tagName, text )
    
    XML.ObjTree.prototype.scalar_to_xml = function ( name, text ) {
        if ( name == "#text" ) {
            return this.xml_escape(text);
        } else {
            return "<"+name+">"+this.xml_escape(text)+"</"+name+">\n";
        }
    };
    
    //  method: xml_escape( text )
    
    XML.ObjTree.prototype.xml_escape = function ( text ) {
        return String(text).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
    };

