/*!
 * XMLHelper class
 * @version 1.0
 * @author M.F.Endenburg
 * @copyright (c) Denbel Systems, 2008
 */

// load namespace
Denbel.load( 'util.XmlHelper' );

/**
 * Denbel.util.XmlHelper static class
 */
Denbel.util.XmlHelper =
{
    /**
     * NodeType conversion table
     * @var Array
     */
    nodeTypeToString: new Array
    (
        'unknown',
        'element',
        'attribute',
        'text',
        'cdata',
        'entity_ref',
        'entity',
        'process',
        'comment',
        'document',
        'doctype',
        'docfrag',
        'notation'
    ),
    
    /**
     * Converts an XML string to an XMLDocument object
     * @param string input
     * @param string type
     * @return XMLDocument
     */
    stringToXml: function( string, type )
    {
        if( !type )
        {
            type = 'text/xml';
        }
        
        if( !string || string == '' )
        {
            YAHOO.log( 'Empty string given to convert to XML!', 'error' );
            return null;
        }
        
        var doc = null;

        if( window.ActiveXObject )
        {
            doc = new ActiveXObject( 'MSXML2.DOMDocument' );
            doc.async = false;
            doc.loadXML( string );
        }
        else
        {
          if( type == 'application/xhtml+xml' )
          {
              string = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">' + string;
          }
          
            var parser = new DOMParser();
            doc = parser.parseFromString( string, type );
        }
        
        if( doc.documentElement.tagName.toLowerCase() == 'parsererror' )
        {
            throw Error( 'Unable to parse string' );
        }
        
        return doc;
    },
    
    /**
   * Creates a new XML document instance
   * @param string version XML version
   * @param string encoding of XML document
   * @return DOM object
   */
    createXmlDocument: function( version, encoding )
    {
        if( !version )
        {
            version = '1.0';
        }
        
        if( !encoding )
        {
            encoding = 'utf-8';
        }
        
        var rootTagName = null;
        
        if( document.implementation && document.implementation.createDocument )
        {
            // This is the W3C standard way to do it 
            return document.implementation.createDocument( null, null, null );
        }
        else // This is the IE way to do it
        {
            // Create an empty document as an ActiveX object
            // If there is no root element, this is all we have to do
            var doc = new ActiveXObject( 'MSXML2.DOMDocument' );
            doc.async = false;
            
            // If there is a root tag, initialize the document
            if( rootTagName )
            {
                // Look for a namespace prefix
                var prefix = '';
                var tagname = rootTagName;
                var p = rootTagName.indexOf( ':' );
                
                if( p != -1 )
                {
                    prefix = rootTagName.substring( 0, p );
                    tagname = rootTagName.substring( p + 1 );
                }
                
                // If we have a namespace, we must have a namespace prefix
                // If we don't have a namespace, we discard any prefix
                if( namespaceURL )
                {
                    if( !prefix )
                    {
                        prefix = 'a0'; // What Firefox uses
                    }
                }
                else
                {
                   prefix = '';
                }

                // Create the root element (with optional namespace) as a string of text
                var text = '<' + ( prefix ? ( prefix + ':' ) : '' ) +  tagname + ( namespaceURL ? ( ' xmlns:' + prefix + '="' + namespaceURL + '"' ) : '' ) + '/>';
                
                // And parse that text into the empty document
                doc.loadXML( text );
            }
            
            return doc;
        }
    },
    
    /**
     * Converts a DOM document to string
     * @param XMLDocument
     * @param string mime type
     * @return string
     */
    xmlToString: function( dom, type )
    {
        if( !dom )
        {
            return null;
        }
        
        if( !type )
        {
            type = 'text/xml';
        }
        
        var s = null;
        
        if( Denbel.util.BrowserDetect.browser == 'Explorer' )
        {
            s = dom.xml;
        }
        else
        {
            try
            {
              switch( type )
              {
                  case 'text/xml':
                      var xml = new XMLSerializer();
                      s = xml.serializeToString( dom );
                      break;
                  
                  default:
                      YAHOO.log( 'Unrecognized XML type to convert to string', 'error' );
                      break;
              }
            }
            catch( e )
            {
                YAHOO.log( e, 'error' );
                s = null;
            }
        }
        
        return s;
    },
    
    /**
     * Converts a DOM element to string
     * @param DOMElement el
     * @return string
     */
    elementToString: function( el )
    {
      var i = 0;
      
      var tagName = el.tagName.toLowerCase();
      var s = '<' + tagName;
      
      for( i = 0; i < el.attributes.length; i++ )
      {
        if( !el.attributes[i].name || !el.attributes[i].value || el.attributes[i].value == 'null' )
        {
          continue;
        }
        
        s += ' ' + el.attributes[i].name + '="' + el.attributes[i].value + '"';
      }
      
      s += '>';
      
      var children = el.childNodes;
      var type = null;
      
      for( i = 0; i < children.length; i++ )
      {
        type = children[i].nodeType;
        
        switch( type )
        {
          case 1:
            s += Denbel.util.XmlHelper.elementToString( children[i] );
            break;
          
          case 3:
          case 4:
            s += children[i].nodeValue;
            break;
        }
      }
      
      if( el.nodeValue )
      {
        s += el.nodeValue;
      }
      
      s += '</' + tagName + '>';
      
      return s;
    },
    
    /**
     * Returns the next child 
     * @param XMLElement start
     * @param Array with element type strings as described in the Denbel.util.XmlHelper.nodeTypeToString Array
     * @return XMLElement
     */
    getNextChildElementType: function( start, type )
    {
      if( !start || !type || !YAHOO.lang.isArray( type ) )
      {
        return null;
      }
      
      var children = start.childNodes;
      var el = null;
        
      if( !children || children.length == 0 )
      {
        return null;
      }
        
      for( var i = 0; i < children.length; i++ )
      {
        if( Denbel.util.inArray( Denbel.util.XmlHelper.nodeTypeToString[children[i].nodeType], type ) )
        {
          el = children[i];
          break;
        }
      }
        
      return el;
    },
    
    /**
     * Returns the next child XMLElement of the given XMLElement
     * @param XMLElement start
     * @return XMLElement
     */
    getNextChildElement: function( start )
    {
        return Denbel.util.XmlHelper.getNextChildElementType( start, new Array( 'element' ) );
    },
    
    /**
     * Returns the next child element of the given start element by tag name
     * @param XMLElement start
     * @param string tagName
     * @return XMLElement
     */
    getNextChildElementByTagName: function( start, tagName )
    {
        if( !start || !tagName )
        {
            return null;
        }
        
        var node = null;
        
        for( var i = 0; i < start.childNodes.length; i++ )
        {
            if( start.childNodes[i].tagName == tagName )
            {
                node = start.childNodes[i];
                break;
            }
        }
        
        return node;
    },
    
    /**
     * Returns the next child element of the given start element by a class name
     * @param XMLElement start
     * @param string className
     * @return XMLElement
     */
    getNextChildElementByClassName: function( start, className )
    {
      if( !start || !className )
      {
        return null;
      }
      
      var node = null;
      
      for( var i = 0; i < start.childNodes.length; i++ )
      {
        if( YAHOO.util.Dom.hasClass( start.childNodes[i], className ) )
        {
          node = start.childNodes[i];
          break;
        }
      }
      
      return node;
    },
    
    /**
     * Returns the textual value of the given element if any.
     * This is an alias of the getNextChildTextValue function.
     * @param XMLElement element
     * @return string
     */
    getElementValue: function( element )
    {
      var node = Denbel.util.XmlHelper.getNextChildElementType( element, ['text','cdata'] );
      
      if( node )
      {
        return node.nodeValue;
      }
      
      return '';
    },
    
    /**
     * Returns a value indicating the first node is a parent of the second node
     * @param XMLElement first
     * @param XMLElement second
     * @return Boolean
     */
    isParentOf: function( first, second )
    {
      if( second.parentNode == first )
      {
        return true;
      }
      else if( second.parentNode.parentNode )
      {
        return Denbel.util.XmlHelper.isParentOf( first, second.parentNode );
      }

      return false;
    },
    
    /**
     * Returns the first parent element of the given element
     * @param XMLElement element
     * @return XMLElement
     */
    getFirstParent: function( element )
    {
      if( !element )
      {
        return null;
      }
      
      if( element.parentNode && Denbel.util.XmlHelper.nodeTypeToString[element.parentNode.nodeType] == 'element' )
      {
        return element.parentNode;
      }
      else if( element.parentNode.parentNode && Denbel.util.XmlHelper.nodeTypeToString[element.parentNode.parentNode.nodeType] == 'element' )
      {
        return element.parentNode.parentNode;
      }
      else if( element.parentNode.parentNode.parentNode && Denbel.util.XmlHelper.nodeTypeToString[element.parentNode.parentNode.parentNode.nodeType] == 'element' )
      {
        return element.parentNode.parentNode.parentNode;
      }
      
      return null;
    },
    
    /**
     * Returns the next child TextNode or CDATASection of the given XMLElement
     * @deprecated
     * @param XMLElement start
     * @return string
     */
    getNextChildTextValue: function( start )
    {
        YAHOO.log( 'Using deprecated function getNextChildTextValue', 'warn', 'Denbel.util.XmlHelper' );
        return Denbel.util.XmlHelper.getElementValue( start );
    },
    
    /**
     * Moves a node from its current location to another
     * @param variant string node ID or Element
     * @param variant string node ID or Element
     * @return bool
     */
    moveNode: function( node, parent )
    {
      if( !node || !parent )
      {
        return false;
      }
      
      if( YAHOO.lang.isString( node ) )
      {
        node = YAHOO.util.Dom.get( node );
      }
      
      if( YAHOO.lang.isString( parent ) )
      {
        parent = YAHOO.util.Dom.get( parent );
      }
      
      var nodeString = Denbel.util.XmlHelper.elementToString( node );
      node.parentNode.removeChild( node );
      parent.appendChild( node );
    }
};
 