/* serializeElement.js
** Copyrigth (C) 2006 Aurelio A. Heckert <aurium@gmail.com>
** This is Free Software licenced under the GPL
** TODO: melhora esse cabecalho
*/

function serializeElement (el) {

  // A reference for the document tag will help the coding:
  var docTag = el.ownerDocument.documentElement;
  
  // Registring the the node type codes to help in the writeElem function:
  var nodeType = {
    element     : docTag.ELEMENT_NODE,
    attribute   : docTag.ATTRIBUTE_NODE,
    text        : docTag.TEXT_NODE,
    cdata       : docTag.CDATA_SECTION_NODE,
    entityRef   : docTag.ENTITY_REFERENCE_NODE,
    entity      : docTag.ENTITY_NODE,
    procInstruc : docTag.PROCESSING_INSTRUCTION_NODE,
    comment     : docTag.COMMENT_NODE,
    document    : docTag.DOCUMENT_NODE,
    docType     : docTag.DOCUMENT_TYPE_NODE,
    docFrag     : docTag.DOCUMENT_FRAGMENT_NODE,
    notation    : docTag.NOTATION_NODE
  };
  
  // Who is on the base NS don't need NS prefix. So... register this:
  var baseNS = docTag.getAttribute("xmlns");
  
  var xmlNS = {};  // to register knowed NSs
  for ( var i=0; i<docTag.attributes.length; i++ ) {
    if ( /xmlns:.+/.test( docTag.attributes[i].nodeName ) ) {
      xmlNS[ docTag.attributes[i].nodeValue ] =
         docTag.attributes[i].nodeName.replace( /xmlns:(.+)/, "$1" );
    }
  }
  /* the xmlNS key is the NS URL and the value the NS Name.
  ** It will help to put the namespace where it's needed.
  ** xmlNS = {
  **   "http://web.resource.org/cc/"  : "cc",
  **   "http://www.w3.org/1999/xlink" : "xlink",
  **   ...
  ** };
  */


  function putIndent(level){
    // Indentation bealtify the code :-)
    var indentBase = "  ";  // 2 spaces
    var indent = "";
    for (var i=0; i<level; i++) {
      indent += indentBase;
    }
    return "\n" + indent;
  }

  function xmlScape (str) {
    // Text content can't have some chars. So... Scape where need.
    str = str.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
    return str.replace(/</g, "&lt;").replace(/>/g, "&gt;")
  }

  function getChildNodes (node, indent) {
    // This function reduce the code of writeElem.
    // Is only a help to understand the code.
    // It will get the tags inside the current tag.
    var xml = "";
    for ( var i=0; i<node.childNodes.length; i++ ) {
      // call the writeElem to write it's childs. Recursion.
      xml += writeElem( node.childNodes[i], indent );
    }
    return xml;
  }

  function getAttributes( node, indent ) {
    // This function reduce the code of writeElem.
    // Is only a help to understand the code.
    // it will get the attributes of a tag and will return the code for this.
    var xml = "";
    for ( var i=0; i<node.attributes.length; i++ ) {
      if ( node.attributes[i].specified ) {
        var attName = node.attributes[i].nodeName;
        if (
             ( ! /.+:.+/.test(node.attributes[i].nodeName) )  &&  // if it don't have an NS
             ( node.attributes[i].namespaceURI != baseNS )    &&  // if it's not part of the base NS
             ( xmlNS[ node.attributes[i].namespaceURI ] )         // if it's NS is knowed
           ) {
          var attName = xmlNS[ node.attributes[i].namespaceURI ] +":"+ attName;
        }
        xml += putIndent(indent+1) + attName +'="'+
               xmlScape( node.attributes[i].nodeValue ) +'"';
      }
    }
    return xml;
  }
  
  function hasOnlyText (node) {
    // return true only if the node has only one child and it's a not blank text.
    if (
       ( node.childNodes.length == 1 ) &&
       ( node.childNodes[0].nodeType == nodeType.text ) &&
       ( node.childNodes[0].nodeValue.replace(/\n|\r|\t|\s/g,"") != "" )
       ) {
      return true;
    } else {
      return false;
    }
  }

  function writeElem (node, indent) {
    // This function identify the element and write this code.
    // It calls the getChildNodes and that call this to write it's childs. Recursion.
    // It calls the getAttributes to reduce the code and help the understing.
    var xml = "";
    
    switch (node.nodeType) {
      
      case nodeType.element:  // the element is a Tag
        xml = putIndent(indent) +"<"+ node.nodeName.toLowerCase();
        xml += getAttributes( node, indent );
        if ( node.childNodes.length == 0 ) {
          xml += " />"
        }
        else {
          xml += ">"
                 + getChildNodes( node, indent+1 )
                 + putIndent(indent) + "</"+ node.nodeName.toLowerCase() +">";
        }
        break;
      
      case nodeType.text:
        if ( node.nodeValue.replace(/\n|\r|\t|\s/g,"") == "" ) {
          xml = "";
        }
        else { xml = xmlScape( node.nodeValue ) }
        break;
      
      case nodeType.cdata:
        xml = "<![CDATA[" + node.nodeValue + "]]>";
        break;
      
      case nodeType.comment:
        xml = putIndent(indent) +"<!--" + node.nodeValue + "-->";
        break;
      
      default:
        xml = "<!-- Tipo nao tratado\n " + node
              + "\n nodeType:" + node.nodeType
              + "\n value:"    + node.nodeValue
              + "\n-->";
    }
    
    return xml;
  }

  var header = "";
  if ( el.ownerDocument.documentElement == el ) {
    header = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n'
  }
  return header + writeElem( el, 0 );

}
