Subversion Repositories munaweb

Rev

Blame | Last modification | View Log | RSS feed

/**
 * XMLWriter - XML generator for Javascript, based on .NET's XMLTextWriter.
 * Copyright (c) 2008-2014 Ariel Flesler - aflesler [a] gmail [d] com | http://flesler.blogspot.com
 * Licensed under BSD (https://raw2.github.com/flesler/XMLWriter/master/LICENSE)
 * @version 1.0.2
 * @author Ariel Flesler
 * http://flesler.blogspot.com/2008/03/xmlwriter-for-javascript.html
 */
 
function XMLWriter( encoding, version ){
        if( encoding )
                this.encoding = encoding;
        if( version )
                this.version = version;
};
(function(){
        
var proto = XMLWriter.prototype = {
        /* These are the defaults, you don't need to change them on the original code */
        encoding:'UTF-8',// what is the encoding
        version:'1.0', //what xml version to use
        formatting: 'indented', //how to format the output (indented/none)  ?
        indentChar:'\t', //char to use for indent
        indentation: 1, //how many indentChar to add per level
        newLine: '\n', //character to separate nodes when formatting
        /* */
        //start a new document, cleanup if we are reusing
        writeStartDocument:function( standalone ){
                this.close();//cleanup
                this.stack = [ ];
                this.standalone = standalone;
        },
        //get back to the root
        writeEndDocument:function(){
                this.active = this.root;
                this.stack = [ ];
        },
        //set the text of the doctype
        writeDocType:function( dt ){
                this.doctype = dt;
        },
        //start a new node with this name, and an optional namespace
        writeStartElement:function( name, ns ){
                if( ns )//namespace
                        name = ns + ':' + name;
                
                var node = { n:name, a:{ }, c: [ ] };//(n)ame, (a)ttributes, (c)hildren
                
                if( this.active ){
                        this.active.c.push(node);
                        this.stack.push(this.active);
                }else
                        this.root = node;
                this.active = node;
        },
        //go up one node, if we are in the root, ignore it
        writeEndElement:function(){
                this.active = this.stack.pop() || this.root;
        },
        //add an attribute to the active node
        writeAttributeString:function( name, value ){
                if( this.active )
                        this.active.a[name] = value;
        },
        //add a text node to the active node
        writeString:function( text ){
                if( this.active )
                        this.active.c.push(text);
        },
        //add plain xml content to the active node without any further checks and escaping
        writeXML:function( text ){
                if( this.active )
                        this.active.c.push(text);
        },
        //shortcut, open an element, write the text and close
        writeElementString:function( name, text, ns ){
                this.writeStartElement( name, ns ) //take advantage of the chaining
                        .writeString( text )
                        .writeEndElement();
        },
        //add a text node wrapped with CDATA
        writeCDATA:function( text ){
                // keep nested CDATA
                text = text.replace(/>>]/g, "]]><![CDATA[>");
                this.writeString( '<![CDATA[' + text + ']]>' );
        },
        //add a text node wrapped in a comment
        writeComment:function( text ){
                this.writeString('<!-- ' + text + ' -->');
        },
        //generate the xml string, you can skip closing the last nodes
        flush:function(){               
                this.writeEndDocument();//ensure it's closed
                
                var 
                        chr = '', num = this.indentation,
                        formatting = this.formatting.toLowerCase() == 'indented',
                        buffer = ['<?xml version="'+this.version+'" encoding="'+this.encoding+'"'];
                        
                if( this.standalone !== undefined )
                        buffer[0] += ' standalone="'+!!this.standalone+'"';
                
                buffer[0] += ' ?>';
                
                if( this.doctype && this.root )
                        buffer.push('<!DOCTYPE '+ this.root.n + ' ' + this.doctype+'>'); 
                
                if( formatting ){
                        while( num-- )
                                chr += this.indentChar;
                }
                
                if( this.root )//skip if no element was added
                        format( this.root, '', chr, buffer );
                
                return buffer.join( formatting ? this.newLine : '' );
        },
        //cleanup, don't use again without calling startDocument
        close:function(){
                if( this.root )
                        clean( this.root );
                this.active = this.root = this.stack = null;
        },
        getDocument: window.ActiveXObject 
                ? function(){ //MSIE
                        var doc = new ActiveXObject('Microsoft.XMLDOM');
                        doc.async = false;
                        doc.loadXML(this.flush());
                        return doc;
                }
                : function(){//W3C
                        return (new DOMParser()).parseFromString(this.flush(),'text/xml');
        }
};

//utility, you don't need it
function clean( node ){
        var l = node.c.length;
        while( l-- ){
                if( typeof node.c[l] == 'object' )
                        clean( node.c[l] );
        }
        node.n = node.a = node.c = null;        
};

//utility, you don't need it
function format( node, indent, chr, buffer ){
        var 
                xml = indent + '<' + node.n,
                nc = node.c.length,
                attr, child, i = 0;
                
        for( attr in node.a )
                xml += ' ' + attr + '="' + node.a[attr] + '"';
        
        xml += nc ? '>' : ' />';

        buffer.push( xml );
                
        if( nc ){
                do{
                        child = node.c[i++];
                        if( typeof child == 'string' ){
                                if( nc == 1 )//single text node
                                        return buffer.push( buffer.pop() + child + '</'+node.n+'>' );                                   
                                else //regular text node
                                        buffer.push( indent+chr+child );
                        }else if( typeof child == 'object' ) //element node
                                format(child, indent+chr, chr, buffer);
                }while( i < nc );
                buffer.push( indent + '</'+node.n+'>' );
        }
};

//add chaining! :)
for( var method in proto ){
        if( typeof proto[method] == 'function' && !/flush|getDocument/.test(method) ){
                var original = proto[method];
                proto[method] = function(){
                        arguments.callee._o_.apply(this,arguments);
                        return this;
                };
                proto[method]._o_ = original;
        }
}

})();