﻿// =========================================================================
//
// xmlw3cdom.js - a W3C compliant DOM parser for XML for <SCRIPT>
//
// version 3.1
//
// =========================================================================
//
// Copyright (C) 2002, 2003, 2004 Jon van Noort (jon@webarcana.com.au), David Joham (djoham@yahoo.com) and Scott Severtson
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// visit the XML for <SCRIPT> home page at xmljs.sourceforge.net
//
// Contains text (used within comments to methods) from the
//  XML Path Language (XPath) Version 1.0 W3C Recommendation
//  Copyright ? 16 November 1999 World Wide Web Consortium,
//  (Massachusetts Institute of Technology,
//  European Research Consortium for Informatics and Mathematics, Keio University).
//  All Rights Reserved.
//  (see: http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/)

/**
* @function addClass - add new className to classCollection
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  classCollectionStr : string - list of existing class names
*   (separated and top and tailed with '|'s)
* @param  newClass           : string - new class name to add
*
* @return : string - the new classCollection, with new className appended,
*   (separated and top and tailed with '|'s)
*/
function addClass(classCollectionStr, newClass)
{
    /// <summary>addClass - add new className to classCollection</summary>
    /// <param name="classCollectionStr" type="String">list of existing class names (separated and top and tailed with '|'s)</param>
    /// <param name="newClass" type="String">new class name to add</param>
    /// <returns type="String">the new classCollection, with new className appended, (separated and top and tailed with '|'s)</returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    if (classCollectionStr)
    {
        if (classCollectionStr.indexOf("|" + newClass + "|") < 0)
        {
            classCollectionStr += newClass + "|";
        }
    }
    else
    {
        classCollectionStr = "|" + newClass + "|";
    }

    return classCollectionStr;
}

/**
* @class  DOMException - raised when an operation is impossible to perform
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  code : int - the exception code (one of the DOMException constants)
*/
DOMException = function(code)
{
    /// <summary>DOMException - raised when an operation is impossible to perform</summary>
    /// <param name="code" type="int">the exception code (one of the DOMException constants)</param>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    this._class = addClass(this._class, "DOMException");

    this.code = code;
};

// DOMException constants
// Introduced in DOM Level 1:
DOMException.INDEX_SIZE_ERR = 1;
DOMException.DOMSTRING_SIZE_ERR = 2;
DOMException.HIERARCHY_REQUEST_ERR = 3;
DOMException.WRONG_DOCUMENT_ERR = 4;
DOMException.INVALID_CHARACTER_ERR = 5;
DOMException.NO_DATA_ALLOWED_ERR = 6;
DOMException.NO_MODIFICATION_ALLOWED_ERR = 7;
DOMException.NOT_FOUND_ERR = 8;
DOMException.NOT_SUPPORTED_ERR = 9;
DOMException.INUSE_ATTRIBUTE_ERR = 10;

// Introduced in DOM Level 2:
DOMException.INVALID_STATE_ERR = 11;
DOMException.SYNTAX_ERR = 12;
DOMException.INVALID_MODIFICATION_ERR = 13;
DOMException.NAMESPACE_ERR = 14;
DOMException.INVALID_ACCESS_ERR = 15;


/**
* @class  DOMImplementation - provides a number of methods for performing operations
*   that are independent of any particular instance of the document object model.
*
* @author Jon van Noort (jon@webarcana.com.au)
*/
DOMImplementation = function()
{
    /// <summary>DOMImplementation - provides a number of methods for performing operations that are independent of any particular instance of the document object model.</summary>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    this._class = addClass(this._class, "DOMImplementation");
    this._p = null;

    this.preserveWhiteSpace = false;  // by default, ignore whitespace
    this.namespaceAware = true;       // by default, handle namespaces
    this.errorChecking = true;       // by default, test for exceptions
};


/**
* @method DOMImplementation.escapeString - escape special characters
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  str : string - The string to be escaped
*
* @return : string - The escaped string
*/
DOMImplementation.prototype.escapeString = function DOMNode__escapeString(str)
{
    /// <summary>DOMImplementation.escapeString - escape special characters</summary>
    /// <param name="str" type="String">The string to be escaped</param>
    /// <returns type="String">The escaped string</returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>



    //the sax processor already has this function. Just wrap it
    return __escapeString(str);
};

/**
* @method DOMImplementation.unescapeString - unescape special characters
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  str : string - The string to be unescaped
*
* @return : string - The unescaped string
*/
DOMImplementation.prototype.unescapeString = function DOMNode__unescapeString(str)
{
    /// <summary>DOMImplementation.unescapeString - unescape special characters</summary>
    /// <param name="str" type="String">The string to be unescaped</param>
    /// <returns type="String">The unescaped string</returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>



    //the sax processor already has this function. Just wrap it
    return __unescapeString(str);
};

/**
* @method DOMImplementation.hasFeature - Test if the DOM implementation implements a specific feature
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  feature : string - The package name of the feature to test. the legal only values are "XML" and "CORE" (case-insensitive).
* @param  version : string - This is the version number of the package name to test. In Level 1, this is the string "1.0".
*
* @return : boolean
*/
DOMImplementation.prototype.hasFeature = function DOMImplementation_hasFeature(feature, version)
{
    /// <summary>DOMImplementation.hasFeature - Test if the DOM implementation implements a specific feature</summary>
    /// <param name="feature" type="String">The package name of the feature to test. the legal only values are "XML" and "CORE" (case-insensitive).</param>
    /// <param name="version" type="String">This is the version number of the package name to test. In Level 1, this is the string "1.0".</param>
    /// <returns type="Boolean"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>



    var ret = false;
    if (feature.toLowerCase() == "xml")
    {
        ret = (!version || (version == "1.0") || (version == "2.0"));
    }
    else if (feature.toLowerCase() == "core")
    {
        ret = (!version || (version == "2.0"));
    }

    return ret;
};

/**
* @method DOMImplementation.loadXML - parse XML string
*
* @author Jon van Noort (jon@webarcana.com.au), David Joham (djoham@yahoo.com) and Scott Severtson
*
* @param  xmlStr : string - the XML string
*
* @return : DOMDocument
*/
DOMImplementation.prototype.loadXML = function DOMImplementation_loadXML(xmlStr)
{
    /// <summary>DOMImplementation.loadXML - parse XML string</summary>
    /// <param name="xmlStr" type="String">the XML string</param>
    /// <returns type="DOMDocument"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au), David Joham (djoham@yahoo.com) and Scott Severtson</remarks>


    // create SAX Parser
    var parser;

    try
    {
        parser = new XMLP(xmlStr);
    }
    catch (e)
    {
        alert("Error Creating the SAX Parser. Did you include xmlsax.js or tinyxmlsax.js in your web page?\nThe SAX parser is needed to populate XML for <SCRIPT>'s W3C DOM Parser with data.");
    }

    // create DOM Document
    var doc = new DOMDocument(this);

    // populate Document with Parsed Nodes
    this._parseLoop(doc, parser);

    // set parseComplete flag, (Some validation Rules are relaxed if this is false)
    doc._parseComplete = true;

    return doc;
};


/**
* @method DOMImplementation.translateErrCode - convert DOMException Code
*   to human readable error message;
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  code : int - the DOMException code
*
* @return : string - the human readbale error message
*/
DOMImplementation.prototype.translateErrCode = function DOMImplementation_translateErrCode(code)
{
    /// <summary>DOMImplementation.translateErrCode - convert DOMException Code to human readable error message;</summary>
    /// <param name="code" type="int">the DOMException code</param>
    /// <returns type="String">the human readbale error message</returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    var msg = "";

    switch (code)
    {
        case DOMException.INDEX_SIZE_ERR:                // 1
            msg = "INDEX_SIZE_ERR: Index out of bounds";
            break;

        case DOMException.DOMSTRING_SIZE_ERR:            // 2
            msg = "DOMSTRING_SIZE_ERR: The resulting string is too long to fit in a DOMString";
            break;

        case DOMException.HIERARCHY_REQUEST_ERR:         // 3
            msg = "HIERARCHY_REQUEST_ERR: The Node can not be inserted at this location";
            break;

        case DOMException.WRONG_DOCUMENT_ERR:            // 4
            msg = "WRONG_DOCUMENT_ERR: The source and the destination Documents are not the same";
            break;

        case DOMException.INVALID_CHARACTER_ERR:         // 5
            msg = "INVALID_CHARACTER_ERR: The string contains an invalid character";
            break;

        case DOMException.NO_DATA_ALLOWED_ERR:           // 6
            msg = "NO_DATA_ALLOWED_ERR: This Node / NodeList does not support data";
            break;

        case DOMException.NO_MODIFICATION_ALLOWED_ERR:   // 7
            msg = "NO_MODIFICATION_ALLOWED_ERR: This object cannot be modified";
            break;

        case DOMException.NOT_FOUND_ERR:                 // 8
            msg = "NOT_FOUND_ERR: The item cannot be found";
            break;

        case DOMException.NOT_SUPPORTED_ERR:             // 9
            msg = "NOT_SUPPORTED_ERR: This implementation does not support function";
            break;

        case DOMException.INUSE_ATTRIBUTE_ERR:           // 10
            msg = "INUSE_ATTRIBUTE_ERR: The Attribute has already been assigned to another Element";
            break;

        // Introduced in DOM Level 2: 
        case DOMException.INVALID_STATE_ERR:             // 11
            msg = "INVALID_STATE_ERR: The object is no longer usable";
            break;

        case DOMException.SYNTAX_ERR:                    // 12
            msg = "SYNTAX_ERR: Syntax error";
            break;

        case DOMException.INVALID_MODIFICATION_ERR:      // 13
            msg = "INVALID_MODIFICATION_ERR: Cannot change the type of the object";
            break;

        case DOMException.NAMESPACE_ERR:                 // 14
            msg = "NAMESPACE_ERR: The namespace declaration is incorrect";
            break;

        case DOMException.INVALID_ACCESS_ERR:            // 15
            msg = "INVALID_ACCESS_ERR: The object does not support this function";
            break;

        default:
            msg = "UNKNOWN: Unknown Exception Code (" + code + ")";
    }

    return msg;
}

/**
* @method DOMImplementation._parseLoop - process SAX events
*
* @author Jon van Noort (jon@webarcana.com.au), David Joham (djoham@yahoo.com) and Scott Severtson
*
* @param  doc : DOMDocument - the Document to contain the parsed XML string
* @param  p   : XMLP        - the SAX Parser
*
* @return : DOMDocument
*/
DOMImplementation.prototype._parseLoop = function DOMImplementation__parseLoop(doc, p)
{
    /// <summary>DOMImplementation._parseLoop - process SAX events</summary>
    /// <param name="doc" type="DOMDocument">the Document to contain the parsed XML string</param>
    /// <param name="p" type="XMLP">the SAX Parser</param>
    /// <returns type="DOMDocument"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au), David Joham (djoham@yahoo.com) and Scott Severtson</remarks>


    var iEvt, iNode, iAttr, strName;
    iNodeParent = doc;

    var el_close_count = 0;

    var entitiesList = new Array();
    var textNodesList = new Array();

    // if namespaceAware, add default namespace
    if (this.namespaceAware)
    {
        var iNS = doc.createNamespace(""); // add the default-default namespace
        iNS.setValue("http://www.w3.org/2000/xmlns/");
        doc._namespaces.setNamedItem(iNS);
    }

    // loop until SAX parser stops emitting events
    while (true)
    {
        // get next event
        iEvt = p.next();

        if (iEvt == XMLP._ELM_B)
        {                      // Begin-Element Event
            var pName = p.getName();                      // get the Element name
            pName = trim(pName, true, true);              // strip spaces from Element name

            if (!this.namespaceAware)
            {
                iNode = doc.createElement(p.getName());     // create the Element

                // add attributes to Element
                for (var i = 0; i < p.getAttributeCount(); i++)
                {
                    strName = p.getAttributeName(i);          // get Attribute name
                    iAttr = iNode.getAttributeNode(strName);  // if Attribute exists, use it

                    if (!iAttr)
                    {
                        iAttr = doc.createAttribute(strName);   // otherwise create it
                    }

                    iAttr.setValue(p.getAttributeValue(i));   // set Attribute value
                    iNode.setAttributeNode(iAttr);            // attach Attribute to Element
                }
            }
            else
            {  // Namespace Aware
                // create element (with empty namespaceURI,
                //  resolve after namespace 'attributes' have been parsed)
                iNode = doc.createElementNS("", p.getName());

                // duplicate ParentNode's Namespace definitions
                iNode._namespaces = iNodeParent._namespaces._cloneNodes(iNode);

                // add attributes to Element
                for (var i = 0; i < p.getAttributeCount(); i++)
                {
                    strName = p.getAttributeName(i);          // get Attribute name

                    // if attribute is a namespace declaration
                    if (this._isNamespaceDeclaration(strName))
                    {
                        // parse Namespace Declaration
                        var namespaceDec = this._parseNSName(strName);

                        if (strName != "xmlns")
                        {
                            iNS = doc.createNamespace(strName);   // define namespace
                        }
                        else
                        {
                            iNS = doc.createNamespace("");        // redefine default namespace
                        }
                        iNS.setValue(p.getAttributeValue(i));   // set value = namespaceURI

                        iNode._namespaces.setNamedItem(iNS);    // attach namespace to namespace collection
                    }
                    else
                    {  // otherwise, it is a normal attribute
                        iAttr = iNode.getAttributeNode(strName);        // if Attribute exists, use it

                        if (!iAttr)
                        {
                            iAttr = doc.createAttributeNS("", strName);   // otherwise create it
                        }

                        iAttr.setValue(p.getAttributeValue(i));         // set Attribute value
                        iNode.setAttributeNodeNS(iAttr);                // attach Attribute to Element

                        if (this._isIdDeclaration(strName))
                        {
                            iNode.id = p.getAttributeValue(i);    // cache ID for getElementById()
                        }
                    }
                }

                // resolve namespaceURIs for this Element
                if (iNode._namespaces.getNamedItem(iNode.prefix))
                {
                    iNode.namespaceURI = iNode._namespaces.getNamedItem(iNode.prefix).value;
                }

                //  for this Element's attributes
                for (var i = 0; i < iNode.attributes.length; i++)
                {
                    if (iNode.attributes.item(i).prefix != "")
                    {  // attributes do not have a default namespace
                        if (iNode._namespaces.getNamedItem(iNode.attributes.item(i).prefix))
                        {
                            iNode.attributes.item(i).namespaceURI = iNode._namespaces.getNamedItem(iNode.attributes.item(i).prefix).value;
                        }
                    }
                }
            }

            // if this is the Root Element
            if (iNodeParent.nodeType == DOMNode.DOCUMENT_NODE)
            {
                iNodeParent.documentElement = iNode;        // register this Element as the Document.documentElement
            }

            iNodeParent.appendChild(iNode);               // attach Element to parentNode
            iNodeParent = iNode;                          // descend one level of the DOM Tree
        }

        else if (iEvt == XMLP._ELM_E)
        {                  // End-Element Event
            iNodeParent = iNodeParent.parentNode;         // ascend one level of the DOM Tree
        }

        else if (iEvt == XMLP._ELM_EMP)
        {                // Empty Element Event
            pName = p.getName();                          // get the Element name
            pName = trim(pName, true, true);              // strip spaces from Element name

            if (!this.namespaceAware)
            {
                iNode = doc.createElement(pName);           // create the Element

                // add attributes to Element
                for (var i = 0; i < p.getAttributeCount(); i++)
                {
                    strName = p.getAttributeName(i);          // get Attribute name
                    iAttr = iNode.getAttributeNode(strName);  // if Attribute exists, use it

                    if (!iAttr)
                    {
                        iAttr = doc.createAttribute(strName);   // otherwise create it
                    }

                    iAttr.setValue(p.getAttributeValue(i));   // set Attribute value
                    iNode.setAttributeNode(iAttr);            // attach Attribute to Element
                }
            }
            else
            {  // Namespace Aware
                // create element (with empty namespaceURI,
                //  resolve after namespace 'attributes' have been parsed)
                iNode = doc.createElementNS("", p.getName());

                // duplicate ParentNode's Namespace definitions
                iNode._namespaces = iNodeParent._namespaces._cloneNodes(iNode);

                // add attributes to Element
                for (var i = 0; i < p.getAttributeCount(); i++)
                {
                    strName = p.getAttributeName(i);          // get Attribute name

                    // if attribute is a namespace declaration
                    if (this._isNamespaceDeclaration(strName))
                    {
                        // parse Namespace Declaration
                        var namespaceDec = this._parseNSName(strName);

                        if (strName != "xmlns")
                        {
                            iNS = doc.createNamespace(strName);   // define namespace
                        }
                        else
                        {
                            iNS = doc.createNamespace("");        // redefine default namespace
                        }
                        iNS.setValue(p.getAttributeValue(i));   // set value = namespaceURI

                        iNode._namespaces.setNamedItem(iNS);    // attach namespace to namespace collection
                    }
                    else
                    {  // otherwise, it is a normal attribute
                        iAttr = iNode.getAttributeNode(strName);        // if Attribute exists, use it

                        if (!iAttr)
                        {
                            iAttr = doc.createAttributeNS("", strName);   // otherwise create it
                        }

                        iAttr.setValue(p.getAttributeValue(i));         // set Attribute value
                        iNode.setAttributeNodeNS(iAttr);                // attach Attribute to Element

                        if (this._isIdDeclaration(strName))
                        {
                            iNode.id = p.getAttributeValue(i);    // cache ID for getElementById()
                        }
                    }
                }

                // resolve namespaceURIs for this Element
                if (iNode._namespaces.getNamedItem(iNode.prefix))
                {
                    iNode.namespaceURI = iNode._namespaces.getNamedItem(iNode.prefix).value;
                }

                //  for this Element's attributes
                for (var i = 0; i < iNode.attributes.length; i++)
                {
                    if (iNode.attributes.item(i).prefix != "")
                    {  // attributes do not have a default namespace
                        if (iNode._namespaces.getNamedItem(iNode.attributes.item(i).prefix))
                        {
                            iNode.attributes.item(i).namespaceURI = iNode._namespaces.getNamedItem(iNode.attributes.item(i).prefix).value;
                        }
                    }
                }
            }

            // if this is the Root Element
            if (iNodeParent.nodeType == DOMNode.DOCUMENT_NODE)
            {
                iNodeParent.documentElement = iNode;        // register this Element as the Document.documentElement
            }

            iNodeParent.appendChild(iNode);               // attach Element to parentNode
        }
        else if (iEvt == XMLP._TEXT || iEvt == XMLP._ENTITY)
        {                   // TextNode and entity Events
            // get Text content
            var pContent = p.getContent().substring(p.getContentBegin(), p.getContentEnd());

            if (!this.preserveWhiteSpace)
            {
                if (trim(pContent, true, true) == "")
                {
                    pContent = ""; //this will cause us not to create the text node below
                }
            }

            if (pContent.length > 0)
            {                    // ignore empty TextNodes
                var textNode = doc.createTextNode(pContent);
                iNodeParent.appendChild(textNode); // attach TextNode to parentNode

                //the sax parser breaks up text nodes when it finds an entity. For
                //example hello&lt;there will fire a text, an entity and another text
                //this sucks for the dom parser because it looks to us in this logic
                //as three text nodes. I fix this by keeping track of the entity nodes
                //and when we're done parsing, calling normalize on their parent to
                //turn the multiple text nodes into one, which is what DOM users expect
                //the code to do this is at the bottom of this function
                if (iEvt == XMLP._ENTITY)
                {
                    entitiesList[entitiesList.length] = textNode;
                }
                else
                {
                    //I can't properly decide how to handle preserve whitespace
                    //until the siblings of the text node are built due to 
                    //the entitiy handling described above. I don't know that this
                    //will be all of the text node or not, so trimming is not appropriate
                    //at this time. Keep a list of all the text nodes for now
                    //and we'll process the preserve whitespace stuff at a later time.
                    textNodesList[textNodesList.length] = textNode;
                }
            }
        }
        else if (iEvt == XMLP._PI)
        {                     // ProcessingInstruction Event
            // attach ProcessingInstruction to parentNode
            iNodeParent.appendChild(doc.createProcessingInstruction(p.getName(), p.getContent().substring(p.getContentBegin(), p.getContentEnd())));
        }
        else if (iEvt == XMLP._CDATA)
        {                  // CDATA Event
            // get CDATA data
            pContent = p.getContent().substring(p.getContentBegin(), p.getContentEnd());

            if (!this.preserveWhiteSpace)
            {
                pContent = trim(pContent, true, true);      // trim whitespace
                pContent.replace(/ +/g, ' ');               // collapse multiple spaces to 1 space
            }

            if (pContent.length > 0)
            {                    // ignore empty CDATANodes
                iNodeParent.appendChild(doc.createCDATASection(pContent)); // attach CDATA to parentNode
            }
        }
        else if (iEvt == XMLP._COMMENT)
        {                // Comment Event
            // get COMMENT data
            var pContent = p.getContent().substring(p.getContentBegin(), p.getContentEnd());

            if (!this.preserveWhiteSpace)
            {
                pContent = trim(pContent, true, true);      // trim whitespace
                pContent.replace(/ +/g, ' ');               // collapse multiple spaces to 1 space
            }

            if (pContent.length > 0)
            {                    // ignore empty CommentNodes
                iNodeParent.appendChild(doc.createComment(pContent));  // attach Comment to parentNode
            }
        }
        else if (iEvt == XMLP._DTD)
        {                    // ignore DTD events
        }
        else if (iEvt == XMLP._ERROR)
        {
            throw (new DOMException(DOMException.SYNTAX_ERR));
            // alert("Fatal Error: " + p.getContent() + "\nLine: " + p.getLineNumber() + "\nColumn: " + p.getColumnNumber() + "\n");
            // break;
        }
        else if (iEvt == XMLP._NONE)
        {                   // no more events
            if (iNodeParent == doc)
            {                     // confirm that we have recursed back up to root
                break;
            }
            else
            {
                throw (new DOMException(DOMException.SYNTAX_ERR));  // one or more Tags were not closed properly
            }
        }
    }

    //normalize any entities in the DOM to a single textNode
    var intCount = entitiesList.length;
    for (intLoop = 0; intLoop < intCount; intLoop++)
    {
        var entity = entitiesList[intLoop];
        //its possible (if for example two entities were in the
        //same domnode, that the normalize on the first entitiy
        //will remove the parent for the second. Only do normalize
        //if I can find a parent node
        var parentNode = entity.getParentNode();
        if (parentNode)
        {
            parentNode.normalize();

            //now do whitespace (if necessary)
            //it was not done for text nodes that have entities
            if (!this.preserveWhiteSpace)
            {
                var children = parentNode.getChildNodes();
                var intCount2 = children.getLength();
                for (intLoop2 = 0; intLoop2 < intCount2; intLoop2++)
                {
                    var child = children.item(intLoop2);
                    if (child.getNodeType() == DOMNode.TEXT_NODE)
                    {
                        var childData = child.getData();
                        childData = trim(childData, true, true);
                        childData.replace(/ +/g, ' ');
                        child.setData(childData);
                    }
                }
            }
        }
    }

    //do the preserve whitespace processing on the rest of the text nodes
    //It's possible (due to the processing above) that the node will have been
    //removed from the tree. Only do whitespace checking if parentNode is not null.
    //This may duplicate the whitespace processing for some nodes that had entities in them
    //but there's no way around that
    if (!this.preserveWhiteSpace)
    {
        var intCount = textNodesList.length;
        for (intLoop = 0; intLoop < intCount; intLoop++)
        {
            var node = textNodesList[intLoop];
            if (node.getParentNode() != null)
            {
                var nodeData = node.getData();
                nodeData = trim(nodeData, true, true);
                nodeData.replace(/ +/g, ' ');
                node.setData(nodeData);
            }
        }

    }
};

/**
* @method DOMImplementation._isNamespaceDeclaration - Return true, if attributeName is a namespace declaration
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  attributeName : string - the attribute name
*
* @return : boolean
*/
DOMImplementation.prototype._isNamespaceDeclaration = function DOMImplementation__isNamespaceDeclaration(attributeName)
{
    /// <summary>DOMImplementation._isNamespaceDeclaration - Return true, if attributeName is a namespace declaration</summary>
    /// <param name="attributeName" type="String">the attribute name</param>
    /// <returns type="Boolean"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // test if attributeName is 'xmlns'
    return (attributeName.indexOf('xmlns') > -1);
}

/**
* @method DOMImplementation._isIdDeclaration - Return true, if attributeName is an id declaration
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  attributeName : string - the attribute name
*
* @return : boolean
*/
DOMImplementation.prototype._isIdDeclaration = function DOMImplementation__isIdDeclaration(attributeName)
{
    /// <summary>DOMImplementation._isIdDeclaration - Return true, if attributeName is an id declaration</summary>
    /// <param name="attributeName" type="String">the attribute name</param>
    /// <returns type="Boolean"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // test if attributeName is 'id' (case insensitive)
    return (attributeName.toLowerCase() == 'id');
}

/**
* @method DOMImplementation._isValidName - Return true,
*   if name contains no invalid characters
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  name : string - the candidate name
*
* @return : boolean
*/
DOMImplementation.prototype._isValidName = function DOMImplementation__isValidName(name)
{
    /// <summary>DOMImplementation._isValidName - Return true, if name contains no invalid characters</summary>
    /// <param name="name" type="String">the candidate name</param>
    /// <returns type="Boolean"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // test if name contains only valid characters
    return name.match(re_validName);
}
re_validName = /^[a-zA-Z_:][a-zA-Z0-9\.\-_:]*$/;

/**
* @method DOMImplementation._isValidString - Return true, if string does not contain any illegal chars
*  All of the characters 0 through 31 and character 127 are nonprinting control characters.
*  With the exception of characters 09, 10, and 13, (Ox09, Ox0A, and Ox0D)
*  Note: different from _isValidName in that ValidStrings may contain spaces
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  name : string - the candidate string
*
* @return : boolean
*/
DOMImplementation.prototype._isValidString = function DOMImplementation__isValidString(name)
{
    /// <summary>DOMImplementation._isValidString - Return true, if string does not contain any illegal chars All of the characters 0 through 31 and character 127 are nonprinting control characters. With the exception of characters 09, 10, and 13, (Ox09, Ox0A, and Ox0D) Note: different from _isValidName in that ValidStrings may contain spaces</summary>
    /// <param name="name" type="String">the candidate string</param>
    /// <returns type="Boolean"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // test that string does not contains invalid characters
    return (name.search(re_invalidStringChars) < 0);
}
re_invalidStringChars = /\x01|\x02|\x03|\x04|\x05|\x06|\x07|\x08|\x0B|\x0C|\x0E|\x0F|\x10|\x11|\x12|\x13|\x14|\x15|\x16|\x17|\x18|\x19|\x1A|\x1B|\x1C|\x1D|\x1E|\x1F|\x7F/

/**
* @method DOMImplementation._parseNSName - parse the namespace name.
*  if there is no colon, the
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  qualifiedName : string - The qualified name
*
* @return : NSName - [
*                     .prefix        : string - The prefix part of the qname
*                     .namespaceName : string - The namespaceURI part of the qname
*                    ]
*/
DOMImplementation.prototype._parseNSName = function DOMImplementation__parseNSName(qualifiedName)
{
    /// <summary>DOMImplementation._parseNSName - parse the namespace name. if there is no colon, the</summary>
    /// <param name="qualifiedName" type="String">The qualified name</param>
    /// <returns type="NSName">[ .prefix : string - The prefix part of the qname .namespaceName : string - The namespaceURI part of the qname ]</returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    var resultNSName = new Object();

    resultNSName.prefix = qualifiedName;  // unless the qname has a namespaceName, the prefix is the entire String
    resultNSName.namespaceName = "";

    // split on ':'
    delimPos = qualifiedName.indexOf(':');

    if (delimPos > -1)
    {
        // get prefix
        resultNSName.prefix = qualifiedName.substring(0, delimPos);

        // get namespaceName
        resultNSName.namespaceName = qualifiedName.substring(delimPos + 1, qualifiedName.length);
    }

    return resultNSName;
}

/**
* @method DOMImplementation._parseQName - parse the qualified name
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  qualifiedName : string - The qualified name
*
* @return : QName
*/
DOMImplementation.prototype._parseQName = function DOMImplementation__parseQName(qualifiedName)
{
    /// <summary>DOMImplementation._parseQName - parse the qualified name</summary>
    /// <param name="qualifiedName" type="String">The qualified name</param>
    /// <returns type="QName"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    var resultQName = new Object();

    resultQName.localName = qualifiedName;  // unless the qname has a prefix, the local name is the entire String
    resultQName.prefix = "";

    // split on ':'
    delimPos = qualifiedName.indexOf(':');

    if (delimPos > -1)
    {
        // get prefix
        resultQName.prefix = qualifiedName.substring(0, delimPos);

        // get localName
        resultQName.localName = qualifiedName.substring(delimPos + 1, qualifiedName.length);
    }

    return resultQName;
}

/**
* @class  DOMNodeList - provides the abstraction of an ordered collection of nodes
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  ownerDocument : DOMDocument - the ownerDocument
* @param  parentNode    : DOMNode - the node that the DOMNodeList is attached to (or null)
*/
DOMNodeList = function(ownerDocument, parentNode)
{
    /// <summary>DOMNodeList - provides the abstraction of an ordered collection of nodes</summary>
    /// <param name="ownerDocument" type="DOMDocument">the ownerDocument</param>
    /// <param name="parentNode" type="DOMNode">the node that the DOMNodeList is attached to (or null)</param>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    this._class = addClass(this._class, "DOMNodeList");
    this._nodes = new Array();

    this.length = 0;
    this.parentNode = parentNode;
    this.ownerDocument = ownerDocument;

    this._readonly = false;
};

/**
* @method DOMNodeList.getLength - Java style gettor for .length
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : int
*/
DOMNodeList.prototype.getLength = function DOMNodeList_getLength()
{
    /// <summary>DOMNodeList.getLength - Java style gettor for .length</summary>
    /// <returns type="int"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    return this.length;
};

/**
* @method DOMNodeList.item - Returns the indexth item in the collection.
*   If index is greater than or equal to the number of nodes in the list, this returns null.
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  index : int - Index into the collection.
*
* @return : DOMNode - The node at the indexth position in the NodeList, or null if that is not a valid index
*/
DOMNodeList.prototype.item = function DOMNodeList_item(index)
{
    /// <summary>DOMNodeList.item - Returns the indexth item in the collection. If index is greater than or equal to the number of nodes in the list, this returns null.</summary>
    /// <param name="index" type="int">Index into the collection.</param>
    /// <returns type="DOMNode">The node at the indexth position in the NodeList, or null if that is not a valid index</returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    var ret = null;

    if ((index >= 0) && (index < this._nodes.length))
    { // bounds check
        ret = this._nodes[index];                    // return selected Node
    }

    return ret;                                    // if the index is out of bounds, default value null is returned
};

/**
* @method DOMNodeList._findItemIndex - find the item index of the node with the specified internal id
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  id : int - unique internal id
*
* @return : int
*/
DOMNodeList.prototype._findItemIndex = function DOMNodeList__findItemIndex(id)
{
    /// <summary>DOMNodeList._findItemIndex - find the item index of the node with the specified internal id</summary>
    /// <param name="id" type="int">unique internal id</param>
    /// <returns type="int"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    var ret = -1;

    // test that id is valid
    if (id > -1)
    {
        for (var i = 0; i < this._nodes.length; i++)
        {
            // compare id to each node's _id
            if (this._nodes[i]._id == id)
            {            // found it!
                ret = i;
                break;
            }
        }
    }

    return ret;                                    // if node is not found, default value -1 is returned
};

/**
* @method DOMNodeList._insertBefore - insert the specified Node into the NodeList before the specified index
*   Used by DOMNode.insertBefore(). Note: DOMNode.insertBefore() is responsible for Node Pointer surgery
*   DOMNodeList._insertBefore() simply modifies the internal data structure (Array).
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  newChild      : DOMNode - the Node to be inserted
* @param  refChildIndex : int     - the array index to insert the Node before
*/
DOMNodeList.prototype._insertBefore = function DOMNodeList__insertBefore(newChild, refChildIndex)
{
    /// <summary>DOMNodeList._insertBefore - insert the specified Node into the NodeList before the specified index Used by DOMNode.insertBefore(). Note: DOMNode.insertBefore() is responsible for Node Pointer surgery DOMNodeList._insertBefore() simply modifies the internal data structure (Array).</summary>
    /// <param name="newChild" type="DOMNode">the Node to be inserted</param>
    /// <param name="refChildIndex" type="int">the array index to insert the Node before</param>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    if ((refChildIndex >= 0) && (refChildIndex < this._nodes.length))
    { // bounds check
        // get array containing children prior to refChild
        var tmpArr = new Array();
        tmpArr = this._nodes.slice(0, refChildIndex);

        if (newChild.nodeType == DOMNode.DOCUMENT_FRAGMENT_NODE)
        {  // node is a DocumentFragment
            // append the children of DocumentFragment
            tmpArr = tmpArr.concat(newChild.childNodes._nodes);
        }
        else
        {
            // append the newChild
            tmpArr[tmpArr.length] = newChild;
        }

        // append the remaining original children (including refChild)
        this._nodes = tmpArr.concat(this._nodes.slice(refChildIndex));

        this.length = this._nodes.length;            // update length
    }
};

/**
* @method DOMNodeList._replaceChild - replace the specified Node in the NodeList at the specified index
*   Used by DOMNode.replaceChild(). Note: DOMNode.replaceChild() is responsible for Node Pointer surgery
*   DOMNodeList._replaceChild() simply modifies the internal data structure (Array).
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  newChild      : DOMNode - the Node to be inserted
* @param  refChildIndex : int     - the array index to hold the Node
*/
DOMNodeList.prototype._replaceChild = function DOMNodeList__replaceChild(newChild, refChildIndex)
{
    /// <summary>DOMNodeList._replaceChild - replace the specified Node in the NodeList at the specified index Used by DOMNode.replaceChild(). Note: DOMNode.replaceChild() is responsible for Node Pointer surgery DOMNodeList._replaceChild() simply modifies the internal data structure (Array).</summary>
    /// <param name="newChild" type="DOMNode">the Node to be inserted</param>
    /// <param name="refChildIndex" type="int">the array index to hold the Node</param>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    var ret = null;

    if ((refChildIndex >= 0) && (refChildIndex < this._nodes.length))
    { // bounds check
        ret = this._nodes[refChildIndex];            // preserve old child for return

        if (newChild.nodeType == DOMNode.DOCUMENT_FRAGMENT_NODE)
        {  // node is a DocumentFragment
            // get array containing children prior to refChild
            var tmpArr = new Array();
            tmpArr = this._nodes.slice(0, refChildIndex);

            // append the children of DocumentFragment
            tmpArr = tmpArr.concat(newChild.childNodes._nodes);

            // append the remaining original children (not including refChild)
            this._nodes = tmpArr.concat(this._nodes.slice(refChildIndex + 1));
        }
        else
        {
            // simply replace node in array (links between Nodes are made at higher level)
            this._nodes[refChildIndex] = newChild;
        }
    }

    return ret;                                   // return replaced node
};

/**
* @method DOMNodeList._removeChild - remove the specified Node in the NodeList at the specified index
*   Used by DOMNode.removeChild(). Note: DOMNode.removeChild() is responsible for Node Pointer surgery
*   DOMNodeList._replaceChild() simply modifies the internal data structure (Array).
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  refChildIndex : int - the array index holding the Node to be removed
*/
DOMNodeList.prototype._removeChild = function DOMNodeList__removeChild(refChildIndex)
{
    /// <summary>DOMNodeList._removeChild - remove the specified Node in the NodeList at the specified index Used by DOMNode.removeChild(). Note: DOMNode.removeChild() is responsible for Node Pointer surgery DOMNodeList._replaceChild() simply modifies the internal data structure (Array).</summary>
    /// <param name="refChildIndex" type="int">the array index holding the Node to be removed</param>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    var ret = null;

    if (refChildIndex > -1)
    {                              // found it!
        ret = this._nodes[refChildIndex];                    // return removed node

        // rebuild array without removed child
        var tmpArr = new Array();
        tmpArr = this._nodes.slice(0, refChildIndex);
        this._nodes = tmpArr.concat(this._nodes.slice(refChildIndex + 1));

        this.length = this._nodes.length;            // update length
    }

    return ret;                                   // return removed node
};

/**
* @method DOMNodeList._appendChild - append the specified Node to the NodeList
*   Used by DOMNode.appendChild(). Note: DOMNode.appendChild() is responsible for Node Pointer surgery
*   DOMNodeList._appendChild() simply modifies the internal data structure (Array).
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  newChild      : DOMNode - the Node to be inserted
*/
DOMNodeList.prototype._appendChild = function DOMNodeList__appendChild(newChild)
{
    /// <summary>DOMNodeList._appendChild - append the specified Node to the NodeList Used by DOMNode.appendChild(). Note: DOMNode.appendChild() is responsible for Node Pointer surgery DOMNodeList._appendChild() simply modifies the internal data structure (Array).</summary>
    /// <param name="newChild" type="DOMNode">the Node to be inserted</param>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>



    if (newChild.nodeType == DOMNode.DOCUMENT_FRAGMENT_NODE)
    {  // node is a DocumentFragment
        // append the children of DocumentFragment
        this._nodes = this._nodes.concat(newChild.childNodes._nodes);
    }
    else
    {
        // simply add node to array (links between Nodes are made at higher level)
        this._nodes[this._nodes.length] = newChild;
    }

    this.length = this._nodes.length;              // update length
};

/**
* @method DOMNodeList._cloneNodes - Returns a NodeList containing clones of the Nodes in this NodeList
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  deep : boolean - If true, recursively clone the subtree under each of the nodes;
*   if false, clone only the nodes themselves (and their attributes, if it is an Element).
* @param  parentNode : DOMNode - the new parent of the cloned NodeList
*
* @return : DOMNodeList - NodeList containing clones of the Nodes in this NodeList
*/
DOMNodeList.prototype._cloneNodes = function DOMNodeList__cloneNodes(deep, parentNode)
{
    /// <summary>DOMNodeList._cloneNodes - Returns a NodeList containing clones of the Nodes in this NodeList</summary>
    /// <param name="deep" type="Boolean">If true, recursively clone the subtree under each of the nodes; if false, clone only the nodes themselves (and their attributes, if it is an Element).</param>
    /// <param name="parentNode" type="DOMNode">the new parent of the cloned NodeList</param>
    /// <returns type="DOMNodeList">NodeList containing clones of the Nodes in this NodeList</returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    var cloneNodeList = new DOMNodeList(this.ownerDocument, parentNode);

    // create list containing clones of each child
    for (var i = 0; i < this._nodes.length; i++)
    {
        cloneNodeList._appendChild(this._nodes[i].cloneNode(deep));
    }

    return cloneNodeList;
};

/**
* @method DOMNodeList.toString - Serialize this NodeList into an XML string
*
* @author Jon van Noort (jon@webarcana.com.au) and David Joham (djoham@yahoo.com)
*
* @return : string
*/
DOMNodeList.prototype.toString = function DOMNodeList_toString()
{
    /// <summary>DOMNodeList.toString - Serialize this NodeList into an XML string</summary>
    /// <returns type="String"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au) and David Joham (djoham@yahoo.com)</remarks>


    var ret = "";

    // create string containing the concatenation of the string values of each child
    for (var i = 0; i < this.length; i++)
    {
        ret += this._nodes[i].toString();
    }

    return ret;
};

/**
* @class  DOMNamedNodeMap - used to represent collections of nodes that can be accessed by name
*  typically a set of Element attributes
*
* @extends DOMNodeList - note W3C spec says that this is not the case,
*   but we need an item() method identicle to DOMNodeList's, so why not?
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  ownerDocument : DOMDocument - the ownerDocument
* @param  parentNode    : DOMNode - the node that the DOMNamedNodeMap is attached to (or null)
*/
DOMNamedNodeMap = function(ownerDocument, parentNode)
{
    /// <summary>DOMNamedNodeMap - used to represent collections of nodes that can be accessed by name typically a set of Element attributes</summary>
    /// <param name="ownerDocument" type="DOMDocument">the ownerDocument</param>
    /// <param name="parentNode" type="DOMNode">the node that the DOMNamedNodeMap is attached to (or null)</param>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>
    /// <extends>DOMNodeList - note W3C spec says that this is not the case, but we need an item() method identicle to DOMNodeList's, so why not?</extends>


    this._class = addClass(this._class, "DOMNamedNodeMap");
    this.DOMNodeList = DOMNodeList;
    this.DOMNodeList(ownerDocument, parentNode);
};
DOMNamedNodeMap.prototype = new DOMNodeList;

/**
* @method DOMNamedNodeMap.getNamedItem - Retrieves a node specified by name
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  name : string - Name of a node to retrieve
*
* @return : DOMNode
*/
DOMNamedNodeMap.prototype.getNamedItem = function DOMNamedNodeMap_getNamedItem(name)
{
    /// <summary>DOMNamedNodeMap.getNamedItem - Retrieves a node specified by name</summary>
    /// <param name="name" type="String">Name of a node to retrieve</param>
    /// <returns type="DOMNode"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    var ret = null;

    // test that Named Node exists
    var itemIndex = this._findNamedItemIndex(name);

    if (itemIndex > -1)
    {                          // found it!
        ret = this._nodes[itemIndex];                // return NamedNode
    }

    return ret;                                    // if node is not found, default value null is returned
};

/**
* @method DOMNamedNodeMap.setNamedItem - Adds a node using its nodeName attribute
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  arg : DOMNode - A node to store in a named node map.
*   The node will later be accessible using the value of the nodeName attribute of the node.
*   If a node with that name is already present in the map, it is replaced by the new one.
*
* @throws : DOMException - WRONG_DOCUMENT_ERR: Raised if arg was created from a different document than the one that created this map.
* @throws : DOMException - NO_MODIFICATION_ALLOWED_ERR: Raised if this NamedNodeMap is readonly.
* @throws : DOMException - INUSE_ATTRIBUTE_ERR: Raised if arg is an Attr that is already an attribute of another Element object.
*  The DOM user must explicitly clone Attr nodes to re-use them in other elements.
*
* @return : DOMNode - If the new Node replaces an existing node with the same name the previously existing Node is returned,
*   otherwise null is returned
*/
DOMNamedNodeMap.prototype.setNamedItem = function DOMNamedNodeMap_setNamedItem(arg)
{
    /// <summary>DOMNamedNodeMap.setNamedItem - Adds a node using its nodeName attribute</summary>
    /// <param name="arg" type="DOMNode">A node to store in a named node map. The node will later be accessible using the value of the nodeName attribute of the node. If a node with that name is already present in the map, it is replaced by the new one.</param>
    /// <returns type="DOMNode">If the new Node replaces an existing node with the same name the previously existing Node is returned, otherwise null is returned</returns>
    /// <exception cref="DOMException">WRONG_DOCUMENT_ERR: Raised if arg was created from a different document than the one that created this map.</exception>
    /// <exception cref="DOMException">NO_MODIFICATION_ALLOWED_ERR: Raised if this NamedNodeMap is readonly.</exception>
    /// <exception cref="DOMException">INUSE_ATTRIBUTE_ERR: Raised if arg is an Attr that is already an attribute of another Element object. The DOM user must explicitly clone Attr nodes to re-use them in other elements.</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // test for exceptions
    if (this.ownerDocument.implementation.errorChecking)
    {
        // throw Exception if arg was not created by this Document
        if (this.ownerDocument != arg.ownerDocument)
        {
            throw (new DOMException(DOMException.WRONG_DOCUMENT_ERR));
        }

        // throw Exception if DOMNamedNodeMap is readonly
        if (this._readonly || (this.parentNode && this.parentNode._readonly))
        {
            throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
        }

        // throw Exception if arg is already an attribute of another Element object
        if (arg.ownerElement && (arg.ownerElement != this.parentNode))
        {
            throw (new DOMException(DOMException.INUSE_ATTRIBUTE_ERR));
        }
    }

    // get item index
    var itemIndex = this._findNamedItemIndex(arg.name);
    var ret = null;

    if (itemIndex > -1)
    {                          // found it!
        ret = this._nodes[itemIndex];                // use existing Attribute

        // throw Exception if DOMAttr is readonly
        if (this.ownerDocument.implementation.errorChecking && ret._readonly)
        {
            throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
        }
        else
        {
            this._nodes[itemIndex] = arg;                // over-write existing NamedNode
        }
    }
    else
    {
        this._nodes[this.length] = arg;              // add new NamedNode
    }

    this.length = this._nodes.length;              // update length

    arg.ownerElement = this.parentNode;            // update ownerElement

    return ret;                                    // return old node or null
};

/**
* @method DOMNamedNodeMap.removeNamedItem - Removes a node specified by name.
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  name : string - The name of a node to remove
*
* @throws : DOMException - NOT_FOUND_ERR: Raised if there is no node named name in this map.
* @throws : DOMException - NO_MODIFICATION_ALLOWED_ERR: Raised if this NamedNodeMap is readonly.
*
* @return : DOMNode - The node removed from the map or null if no node with such a name exists.
*/
DOMNamedNodeMap.prototype.removeNamedItem = function DOMNamedNodeMap_removeNamedItem(name)
{
    /// <summary>DOMNamedNodeMap.removeNamedItem - Removes a node specified by name.</summary>
    /// <param name="name" type="String">The name of a node to remove</param>
    /// <returns type="DOMNode">The node removed from the map or null if no node with such a name exists.</returns>
    /// <exception cref="DOMException">NOT_FOUND_ERR: Raised if there is no node named name in this map.</exception>
    /// <exception cref="DOMException">NO_MODIFICATION_ALLOWED_ERR: Raised if this NamedNodeMap is readonly.</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    var ret = null;
    // test for exceptions
    // throw Exception if DOMNamedNodeMap is readonly
    if (this.ownerDocument.implementation.errorChecking && (this._readonly || (this.parentNode && this.parentNode._readonly)))
    {
        throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
    }

    // get item index
    var itemIndex = this._findNamedItemIndex(name);

    // throw Exception if there is no node named name in this map
    if (this.ownerDocument.implementation.errorChecking && (itemIndex < 0))
    {
        throw (new DOMException(DOMException.NOT_FOUND_ERR));
    }

    // get Node
    var oldNode = this._nodes[itemIndex];

    // throw Exception if Node is readonly
    if (this.ownerDocument.implementation.errorChecking && oldNode._readonly)
    {
        throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
    }

    // return removed node
    return this._removeChild(itemIndex);
};

/**
* @method DOMNamedNodeMap.getNamedItemNS - Retrieves a node specified by name
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  namespaceURI : string - the namespace URI of the required node
* @param  localName    : string - the local name of the required node
*
* @return : DOMNode
*/
DOMNamedNodeMap.prototype.getNamedItemNS = function DOMNamedNodeMap_getNamedItemNS(namespaceURI, localName)
{
    /// <summary>DOMNamedNodeMap.getNamedItemNS - Retrieves a node specified by name</summary>
    /// <param name="namespaceURI" type="String">the namespace URI of the required node</param>
    /// <param name="localName" type="String">the local name of the required node</param>
    /// <returns type="DOMNode"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    var ret = null;

    // test that Named Node exists
    var itemIndex = this._findNamedItemNSIndex(namespaceURI, localName);

    if (itemIndex > -1)
    {                          // found it!
        ret = this._nodes[itemIndex];                // return NamedNode
    }

    return ret;                                    // if node is not found, default value null is returned
};

/**
* @method DOMNamedNodeMap.setNamedItemNS - Adds a node using
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  arg : string - A node to store in a named node map.
*   The node will later be accessible using the value of the nodeName attribute of the node.
*   If a node with that name is already present in the map, it is replaced by the new one.
*
* @throws : DOMException - NO_MODIFICATION_ALLOWED_ERR: Raised if this NamedNodeMap is readonly.
* @throws : DOMException - WRONG_DOCUMENT_ERR: Raised if arg was created from a different document than the one that created this map.
* @throws : DOMException - INUSE_ATTRIBUTE_ERR: Raised if arg is an Attr that is already an attribute of another Element object.
*   The DOM user must explicitly clone Attr nodes to re-use them in other elements.
*
* @return : DOMNode - If the new Node replaces an existing node with the same name the previously existing Node is returned,
*   otherwise null is returned
*/
DOMNamedNodeMap.prototype.setNamedItemNS = function DOMNamedNodeMap_setNamedItemNS(arg)
{
    /// <summary>DOMNamedNodeMap.setNamedItemNS - Adds a node using</summary>
    /// <param name="arg" type="String">A node to store in a named node map. The node will later be accessible using the value of the nodeName attribute of the node. If a node with that name is already present in the map, it is replaced by the new one.</param>
    /// <returns type="DOMNode">If the new Node replaces an existing node with the same name the previously existing Node is returned, otherwise null is returned</returns>
    /// <exception cref="DOMException">NO_MODIFICATION_ALLOWED_ERR: Raised if this NamedNodeMap is readonly.</exception>
    /// <exception cref="DOMException">WRONG_DOCUMENT_ERR: Raised if arg was created from a different document than the one that created this map.</exception>
    /// <exception cref="DOMException">INUSE_ATTRIBUTE_ERR: Raised if arg is an Attr that is already an attribute of another Element object. The DOM user must explicitly clone Attr nodes to re-use them in other elements.</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // test for exceptions
    if (this.ownerDocument.implementation.errorChecking)
    {
        // throw Exception if DOMNamedNodeMap is readonly
        if (this._readonly || (this.parentNode && this.parentNode._readonly))
        {
            throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
        }

        // throw Exception if arg was not created by this Document
        if (this.ownerDocument != arg.ownerDocument)
        {
            throw (new DOMException(DOMException.WRONG_DOCUMENT_ERR));
        }

        // throw Exception if arg is already an attribute of another Element object
        if (arg.ownerElement && (arg.ownerElement != this.parentNode))
        {
            throw (new DOMException(DOMException.INUSE_ATTRIBUTE_ERR));
        }
    }

    // get item index
    var itemIndex = this._findNamedItemNSIndex(arg.namespaceURI, arg.localName);
    var ret = null;

    if (itemIndex > -1)
    {                          // found it!
        ret = this._nodes[itemIndex];                // use existing Attribute
        // throw Exception if DOMAttr is readonly
        if (this.ownerDocument.implementation.errorChecking && ret._readonly)
        {
            throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
        }
        else
        {
            this._nodes[itemIndex] = arg;                // over-write existing NamedNode
        }
    }
    else
    {
        this._nodes[this.length] = arg;              // add new NamedNode
    }

    this.length = this._nodes.length;              // update length

    arg.ownerElement = this.parentNode;


    return ret;                                    // return old node or null
};

/**
* @method DOMNamedNodeMap.removeNamedItemNS - Removes a node specified by name.
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  namespaceURI : string - the namespace URI of the required node
* @param  localName    : string - the local name of the required node
*
* @throws : DOMException - NOT_FOUND_ERR: Raised if there is no node with the specified namespaceURI and localName in this map.
* @throws : DOMException - NO_MODIFICATION_ALLOWED_ERR: Raised if this NamedNodeMap is readonly.
*
* @return : DOMNode - The node removed from the map or null if no node with such a name exists.
*/
DOMNamedNodeMap.prototype.removeNamedItemNS = function DOMNamedNodeMap_removeNamedItemNS(namespaceURI, localName)
{
    /// <summary>DOMNamedNodeMap.removeNamedItemNS - Removes a node specified by name.</summary>
    /// <param name="namespaceURI" type="String">the namespace URI of the required node</param>
    /// <param name="localName" type="String">the local name of the required node</param>
    /// <returns type="DOMNode">The node removed from the map or null if no node with such a name exists.</returns>
    /// <exception cref="DOMException">NOT_FOUND_ERR: Raised if there is no node with the specified namespaceURI and localName in this map.</exception>
    /// <exception cref="DOMException">NO_MODIFICATION_ALLOWED_ERR: Raised if this NamedNodeMap is readonly.</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    var ret = null;

    // test for exceptions
    // throw Exception if DOMNamedNodeMap is readonly
    if (this.ownerDocument.implementation.errorChecking && (this._readonly || (this.parentNode && this.parentNode._readonly)))
    {
        throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
    }

    // get item index
    var itemIndex = this._findNamedItemNSIndex(namespaceURI, localName);

    // throw Exception if there is no matching node in this map
    if (this.ownerDocument.implementation.errorChecking && (itemIndex < 0))
    {
        throw (new DOMException(DOMException.NOT_FOUND_ERR));
    }

    // get Node
    var oldNode = this._nodes[itemIndex];

    // throw Exception if Node is readonly
    if (this.ownerDocument.implementation.errorChecking && oldNode._readonly)
    {
        throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
    }

    return this._removeChild(itemIndex);             // return removed node
};

/**
* @method DOMNamedNodeMap._findNamedItemIndex - find the item index of the node with the specified name
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  name : string - the name of the required node
*
* @return : int
*/
DOMNamedNodeMap.prototype._findNamedItemIndex = function DOMNamedNodeMap__findNamedItemIndex(name)
{
    /// <summary>DOMNamedNodeMap._findNamedItemIndex - find the item index of the node with the specified name</summary>
    /// <param name="name" type="String">the name of the required node</param>
    /// <returns type="int"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    var ret = -1;

    // loop through all nodes
    for (var i = 0; i < this._nodes.length; i++)
    {
        // compare name to each node's nodeName
        if (this._nodes[i].name == name)
        {         // found it!
            ret = i;
            break;
        }
    }

    return ret;                                    // if node is not found, default value -1 is returned
};

/**
* @method DOMNamedNodeMap._findNamedItemNSIndex - find the item index of the node with the specified namespaceURI and localName
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  namespaceURI : string - the namespace URI of the required node
* @param  localName    : string - the local name of the required node
*
* @return : int
*/
DOMNamedNodeMap.prototype._findNamedItemNSIndex = function DOMNamedNodeMap__findNamedItemNSIndex(namespaceURI, localName)
{
    /// <summary>DOMNamedNodeMap._findNamedItemNSIndex - find the item index of the node with the specified namespaceURI and localName</summary>
    /// <param name="namespaceURI" type="String">the namespace URI of the required node</param>
    /// <param name="localName" type="String">the local name of the required node</param>
    /// <returns type="int"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    var ret = -1;

    // test that localName is not null
    if (localName)
    {
        // loop through all nodes
        for (var i = 0; i < this._nodes.length; i++)
        {
            // compare name to each node's namespaceURI and localName
            if ((this._nodes[i].namespaceURI == namespaceURI) && (this._nodes[i].localName == localName))
            {
                ret = i;                                 // found it!
                break;
            }
        }
    }

    return ret;                                    // if node is not found, default value -1 is returned
};

/**
* @method DOMNamedNodeMap._hasAttribute - Returns true if specified node exists
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  name : string - the name of the required node
*
* @return : boolean
*/
DOMNamedNodeMap.prototype._hasAttribute = function DOMNamedNodeMap__hasAttribute(name)
{
    /// <summary>DOMNamedNodeMap._hasAttribute - Returns true if specified node exists</summary>
    /// <param name="name" type="String">the name of the required node</param>
    /// <returns type="Boolean"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    var ret = false;

    // test that Named Node exists
    var itemIndex = this._findNamedItemIndex(name);

    if (itemIndex > -1)
    {                          // found it!
        ret = true;                                  // return true
    }

    return ret;                                    // if node is not found, default value false is returned
}

/**
* @method DOMNamedNodeMap._hasAttributeNS - Returns true if specified node exists
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  namespaceURI : string - the namespace URI of the required node
* @param  localName    : string - the local name of the required node
*
* @return : boolean
*/
DOMNamedNodeMap.prototype._hasAttributeNS = function DOMNamedNodeMap__hasAttributeNS(namespaceURI, localName)
{
    /// <summary>DOMNamedNodeMap._hasAttributeNS - Returns true if specified node exists</summary>
    /// <param name="namespaceURI" type="String">the namespace URI of the required node</param>
    /// <param name="localName" type="String">the local name of the required node</param>
    /// <returns type="Boolean"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    var ret = false;

    // test that Named Node exists
    var itemIndex = this._findNamedItemNSIndex(namespaceURI, localName);

    if (itemIndex > -1)
    {                          // found it!
        ret = true;                                  // return true
    }

    return ret;                                    // if node is not found, default value false is returned
}

/**
* @method DOMNamedNodeMap._cloneNodes - Returns a NamedNodeMap containing clones of the Nodes in this NamedNodeMap
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  parentNode : DOMNode - the new parent of the cloned NodeList
*
* @return : DOMNamedNodeMap - NamedNodeMap containing clones of the Nodes in this DOMNamedNodeMap
*/
DOMNamedNodeMap.prototype._cloneNodes = function DOMNamedNodeMap__cloneNodes(parentNode)
{
    /// <summary>DOMNamedNodeMap._cloneNodes - Returns a NamedNodeMap containing clones of the Nodes in this NamedNodeMap</summary>
    /// <param name="parentNode" type="DOMNode">the new parent of the cloned NodeList</param>
    /// <returns type="DOMNamedNodeMap">NamedNodeMap containing clones of the Nodes in this DOMNamedNodeMap</returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    var cloneNamedNodeMap = new DOMNamedNodeMap(this.ownerDocument, parentNode);

    // create list containing clones of all children
    for (var i = 0; i < this._nodes.length; i++)
    {
        cloneNamedNodeMap._appendChild(this._nodes[i].cloneNode(false));
    }

    return cloneNamedNodeMap;
};

/**
* @method DOMNamedNodeMap.toString - Serialize this NodeMap into an XML string
*
* @author Jon van Noort (jon@webarcana.com.au) and David Joham (djoham@yahoo.com)
*
* @return : string
*/
DOMNamedNodeMap.prototype.toString = function DOMNamedNodeMap_toString()
{
    /// <summary>DOMNamedNodeMap.toString - Serialize this NodeMap into an XML string</summary>
    /// <returns type="String"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au) and David Joham (djoham@yahoo.com)</remarks>


    var ret = "";

    // create string containing concatenation of all (but last) Attribute string values (separated by spaces)
    for (var i = 0; i < this.length - 1; i++)
    {
        ret += this._nodes[i].toString() + " ";
    }

    // add last Attribute to string (without trailing space)
    if (this.length > 0)
    {
        ret += this._nodes[this.length - 1].toString();
    }

    return ret;
};

/**
* @class  DOMNamespaceNodeMap - used to represent collections of namespace nodes that can be accessed by name
*  typically a set of Element attributes
*
* @extends DOMNamedNodeMap
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  ownerDocument : DOMDocument - the ownerDocument
* @param  parentNode    : DOMNode - the node that the DOMNamespaceNodeMap is attached to (or null)
*/
DOMNamespaceNodeMap = function(ownerDocument, parentNode)
{
    /// <summary>DOMNamespaceNodeMap - used to represent collections of namespace nodes that can be accessed by name typically a set of Element attributes</summary>
    /// <param name="ownerDocument" type="DOMDocument">the ownerDocument</param>
    /// <param name="parentNode" type="DOMNode">the node that the DOMNamespaceNodeMap is attached to (or null)</param>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>
    /// <extends>DOMNamedNodeMap</extends>


    this._class = addClass(this._class, "DOMNamespaceNodeMap");
    this.DOMNamedNodeMap = DOMNamedNodeMap;
    this.DOMNamedNodeMap(ownerDocument, parentNode);
};
DOMNamespaceNodeMap.prototype = new DOMNamedNodeMap;

/**
* @method DOMNamespaceNodeMap._findNamedItemIndex - find the item index of the node with the specified localName
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  localName : string - the localName of the required node
*
* @return : int
*/
DOMNamespaceNodeMap.prototype._findNamedItemIndex = function DOMNamespaceNodeMap__findNamedItemIndex(localName)
{
    /// <summary>DOMNamespaceNodeMap._findNamedItemIndex - find the item index of the node with the specified localName</summary>
    /// <param name="localName" type="String">the localName of the required node</param>
    /// <returns type="int"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    var ret = -1;

    // loop through all nodes
    for (var i = 0; i < this._nodes.length; i++)
    {
        // compare name to each node's nodeName
        if (this._nodes[i].localName == localName)
        {         // found it!
            ret = i;
            break;
        }
    }

    return ret;                                    // if node is not found, default value -1 is returned
};


/**
* @method DOMNamespaceNodeMap._cloneNodes - Returns a NamespaceNodeMap containing clones of the Nodes in this NamespaceNodeMap
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  parentNode : DOMNode - the new parent of the cloned NodeList
*
* @return : DOMNamespaceNodeMap - NamespaceNodeMap containing clones of the Nodes in this NamespaceNodeMap
*/
DOMNamespaceNodeMap.prototype._cloneNodes = function DOMNamespaceNodeMap__cloneNodes(parentNode)
{
    /// <summary>DOMNamespaceNodeMap._cloneNodes - Returns a NamespaceNodeMap containing clones of the Nodes in this NamespaceNodeMap</summary>
    /// <param name="parentNode" type="DOMNode">the new parent of the cloned NodeList</param>
    /// <returns type="DOMNamespaceNodeMap">NamespaceNodeMap containing clones of the Nodes in this NamespaceNodeMap</returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    var cloneNamespaceNodeMap = new DOMNamespaceNodeMap(this.ownerDocument, parentNode);

    // create list containing clones of all children
    for (var i = 0; i < this._nodes.length; i++)
    {
        cloneNamespaceNodeMap._appendChild(this._nodes[i].cloneNode(false));
    }

    return cloneNamespaceNodeMap;
};

/**
* @method DOMNamespaceNodeMap.toString - Serialize this NamespaceNodeMap into an XML string
*
* @author Jon van Noort (jon@webarcana.com.au) and David Joham (djoham@yahoo.com)
*
* @return : string
*/
DOMNamespaceNodeMap.prototype.toString = function DOMNamespaceNodeMap_toString()
{
    /// <summary>DOMNamespaceNodeMap.toString - Serialize this NamespaceNodeMap into an XML string</summary>
    /// <returns type="String"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au) and David Joham (djoham@yahoo.com)</remarks>


    var ret = "";

    // identify namespaces declared local to this Element (ie, not inherited)
    for (var ind = 0; ind < this._nodes.length; ind++)
    {
        // if namespace declaration does not exist in the containing node's, parentNode's namespaces
        var ns = null;
        try
        {
            var ns = this.parentNode.parentNode._namespaces.getNamedItem(this._nodes[ind].localName);
        }
        catch (e)
        {
            //breaking to prevent default namespace being inserted into return value
            break;
        }
        if (!(ns && ("" + ns.nodeValue == "" + this._nodes[ind].nodeValue)))
        {
            // display the namespace declaration
            ret += this._nodes[ind].toString() + " ";
        }
    }

    return ret;
};

/**
* @class  DOMNode - The Node interface is the primary datatype for the entire Document Object Model.
*   It represents a single node in the document tree.
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  ownerDocument : DOMDocument - The Document object associated with this node.
*/
DOMNode = function(ownerDocument)
{
    /// <summary>DOMNode - The Node interface is the primary datatype for the entire Document Object Model. It represents a single node in the document tree.</summary>
    /// <param name="ownerDocument" type="DOMDocument">The Document object associated with this node.</param>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    this._class = addClass(this._class, "DOMNode");

    if (ownerDocument)
    {
        this._id = ownerDocument._genId();           // generate unique internal id
    }

    this.namespaceURI = "";                        // The namespace URI of this node (Level 2)
    this.prefix = "";                        // The namespace prefix of this node (Level 2)
    this.localName = "";                        // The localName of this node (Level 2)

    this.nodeName = "";                            // The name of this node
    this.nodeValue = "";                           // The value of this node
    this.nodeType = 0;                             // A code representing the type of the underlying object

    // The parent of this node. All nodes, except Document, DocumentFragment, and Attr may have a parent.
    // However, if a node has just been created and not yet added to the tree, or if it has been removed from the tree, this is null
    this.parentNode = null;

    // A NodeList that contains all children of this node. If there are no children, this is a NodeList containing no nodes.
    // The content of the returned NodeList is "live" in the sense that, for instance, changes to the children of the node object
    // that it was created from are immediately reflected in the nodes returned by the NodeList accessors;
    // it is not a static snapshot of the content of the node. This is true for every NodeList, including the ones returned by the getElementsByTagName method.
    this.childNodes = new DOMNodeList(ownerDocument, this);

    this.firstChild = null;                   // The first child of this node. If there is no such node, this is null
    this.lastChild = null;                   // The last child of this node. If there is no such node, this is null.
    this.previousSibling = null;                   // The node immediately preceding this node. If there is no such node, this is null.
    this.nextSibling = null;                   // The node immediately following this node. If there is no such node, this is null.

    this.attributes = new DOMNamedNodeMap(ownerDocument, this);   // A NamedNodeMap containing the attributes of this node (if it is an Element) or null otherwise.
    this.ownerDocument = ownerDocument;          // The Document object associated with this node
    this._namespaces = new DOMNamespaceNodeMap(ownerDocument, this);  // The namespaces in scope for this node

    this._readonly = false;
};

// nodeType constants
DOMNode.ELEMENT_NODE = 1;
DOMNode.ATTRIBUTE_NODE = 2;
DOMNode.TEXT_NODE = 3;
DOMNode.CDATA_SECTION_NODE = 4;
DOMNode.ENTITY_REFERENCE_NODE = 5;
DOMNode.ENTITY_NODE = 6;
DOMNode.PROCESSING_INSTRUCTION_NODE = 7;
DOMNode.COMMENT_NODE = 8;
DOMNode.DOCUMENT_NODE = 9;
DOMNode.DOCUMENT_TYPE_NODE = 10;
DOMNode.DOCUMENT_FRAGMENT_NODE = 11;
DOMNode.NOTATION_NODE = 12;
DOMNode.NAMESPACE_NODE = 13;

/**
* @method DOMNode.hasAttributes
*
* @author Jon van Noort (jon@webarcana.com.au) & David Joham (djoham@yahoo.com)
*
* @return : boolean
*/
DOMNode.prototype.hasAttributes = function DOMNode_hasAttributes()
{
    /// <summary>DOMNode.hasAttributes</summary>
    /// <returns type="Boolean"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au) &amp; David Joham (djoham@yahoo.com)</remarks>


    if (this.attributes.length == 0)
    {
        return false;
    }
    else
    {
        return true;
    }
};

/**
* @method DOMNode.getNodeName - Java style gettor for .nodeName
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : string
*/
DOMNode.prototype.getNodeName = function DOMNode_getNodeName()
{
    /// <summary>DOMNode.getNodeName - Java style gettor for .nodeName</summary>
    /// <returns type="String"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    return this.nodeName;
};

/**
* @method DOMNode.getNodeValue - Java style gettor for .NodeValue
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : string
*/
DOMNode.prototype.getNodeValue = function DOMNode_getNodeValue()
{
    /// <summary>DOMNode.getNodeValue - Java style gettor for .NodeValue</summary>
    /// <returns type="String"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    return this.nodeValue;
};

/**
* @method DOMNode.setNodeValue - Java style settor for .NodeValue
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  nodeValue : string - unique internal id
*/
DOMNode.prototype.setNodeValue = function DOMNode_setNodeValue(nodeValue)
{
    /// <summary>DOMNode.setNodeValue - Java style settor for .NodeValue</summary>
    /// <param name="nodeValue" type="String">unique internal id</param>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // throw Exception if DOMNode is readonly
    if (this.ownerDocument.implementation.errorChecking && this._readonly)
    {
        throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
    }

    this.nodeValue = nodeValue;
};

/**
* @method DOMNode.getNodeType - Java style gettor for .nodeType
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : int
*/
DOMNode.prototype.getNodeType = function DOMNode_getNodeType()
{
    /// <summary>DOMNode.getNodeType - Java style gettor for .nodeType</summary>
    /// <returns type="int"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    return this.nodeType;
};

/**
* @method DOMNode.getParentNode - Java style gettor for .parentNode
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : DOMNode
*/
DOMNode.prototype.getParentNode = function DOMNode_getParentNode()
{
    /// <summary>DOMNode.getParentNode - Java style gettor for .parentNode</summary>
    /// <returns type="DOMNode"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    return this.parentNode;
};

/**
* @method DOMNode.getChildNodes - Java style gettor for .childNodes
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : DOMNodeList
*/
DOMNode.prototype.getChildNodes = function DOMNode_getChildNodes()
{
    /// <summary>DOMNode.getChildNodes - Java style gettor for .childNodes</summary>
    /// <returns type="DOMNodeList"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    return this.childNodes;
};

/**
* @method DOMNode.getFirstChild - Java style gettor for .firstChild
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : DOMNode
*/
DOMNode.prototype.getFirstChild = function DOMNode_getFirstChild()
{
    /// <summary>DOMNode.getFirstChild - Java style gettor for .firstChild</summary>
    /// <returns type="DOMNode"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    return this.firstChild;
};

/**
* @method DOMNode.getLastChild - Java style gettor for .lastChild
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : DOMNode
*/
DOMNode.prototype.getLastChild = function DOMNode_getLastChild()
{
    /// <summary>DOMNode.getLastChild - Java style gettor for .lastChild</summary>
    /// <returns type="DOMNode"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    return this.lastChild;
};

/**
* @method DOMNode.getPreviousSibling - Java style gettor for .previousSibling
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : DOMNode
*/
DOMNode.prototype.getPreviousSibling = function DOMNode_getPreviousSibling()
{
    /// <summary>DOMNode.getPreviousSibling - Java style gettor for .previousSibling</summary>
    /// <returns type="DOMNode"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    return this.previousSibling;
};

/**
* @method DOMNode.getNextSibling - Java style gettor for .nextSibling
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : DOMNode
*/
DOMNode.prototype.getNextSibling = function DOMNode_getNextSibling()
{
    /// <summary>DOMNode.getNextSibling - Java style gettor for .nextSibling</summary>
    /// <returns type="DOMNode"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    return this.nextSibling;
};

/**
* @method DOMNode.getAttributes - Java style gettor for .attributes
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : DOMNamedNodeList
*/
DOMNode.prototype.getAttributes = function DOMNode_getAttributes()
{
    /// <summary>DOMNode.getAttributes - Java style gettor for .attributes</summary>
    /// <returns type="DOMNamedNodeList"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    return this.attributes;
};

/**
* @method DOMNode.getOwnerDocument - Java style gettor for .ownerDocument
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : DOMDocument
*/
DOMNode.prototype.getOwnerDocument = function DOMNode_getOwnerDocument()
{
    /// <summary>DOMNode.getOwnerDocument - Java style gettor for .ownerDocument</summary>
    /// <returns type="DOMDocument"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    return this.ownerDocument;
};

/**
* @method DOMNode.getNamespaceURI - Java style gettor for .namespaceURI
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : String
*/
DOMNode.prototype.getNamespaceURI = function DOMNode_getNamespaceURI()
{
    /// <summary>DOMNode.getNamespaceURI - Java style gettor for .namespaceURI</summary>
    /// <returns type="String"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    return this.namespaceURI;
};

/**
* @method DOMNode.getPrefix - Java style gettor for .prefix
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : String
*/
DOMNode.prototype.getPrefix = function DOMNode_getPrefix()
{
    /// <summary>DOMNode.getPrefix - Java style gettor for .prefix</summary>
    /// <returns type="String"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    return this.prefix;
};

/**
* @method DOMNode.setPrefix - Java style settor for .prefix
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param   prefix : String
*
* @throws : DOMException - NO_MODIFICATION_ALLOWED_ERR: Raised if this Node is readonly.
* @throws : DOMException - INVALID_CHARACTER_ERR: Raised if the string contains an illegal character
* @throws : DOMException - NAMESPACE_ERR: Raised if the Namespace is invalid
*
*/
DOMNode.prototype.setPrefix = function DOMNode_setPrefix(prefix)
{
    /// <summary>DOMNode.setPrefix - Java style settor for .prefix</summary>
    /// <param name="prefix" type="String"></param>
    /// <exception cref="DOMException">NO_MODIFICATION_ALLOWED_ERR: Raised if this Node is readonly.</exception>
    /// <exception cref="DOMException">INVALID_CHARACTER_ERR: Raised if the string contains an illegal character</exception>
    /// <exception cref="DOMException">NAMESPACE_ERR: Raised if the Namespace is invalid</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // test for exceptions
    if (this.ownerDocument.implementation.errorChecking)
    {
        // throw Exception if DOMNode is readonly
        if (this._readonly)
        {
            throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
        }

        // throw Exception if the prefix string contains an illegal character
        if (!this.ownerDocument.implementation._isValidName(prefix))
        {
            throw (new DOMException(DOMException.INVALID_CHARACTER_ERR));
        }

        // throw Exception if the Namespace is invalid;
        //  if the specified prefix is malformed,
        //  if the namespaceURI of this node is null,
        //  if the specified prefix is "xml" and the namespaceURI of this node is
        //   different from "http://www.w3.org/XML/1998/namespace",
        if (!this.ownerDocument._isValidNamespace(this.namespaceURI, prefix + ":" + this.localName))
        {
            throw (new DOMException(DOMException.NAMESPACE_ERR));
        }

        // throw Exception if we are trying to make the attribute look like a namespace declaration;
        //  if this node is an attribute and the specified prefix is "xmlns"
        //   and the namespaceURI of this node is different from "http://www.w3.org/2000/xmlns/",
        if ((prefix == "xmlns") && (this.namespaceURI != "http://www.w3.org/2000/xmlns/"))
        {
            throw (new DOMException(DOMException.NAMESPACE_ERR));
        }

        // throw Exception if we are trying to make the attribute look like a default namespace declaration;
        //  if this node is an attribute and the qualifiedName of this node is "xmlns" [Namespaces].
        if ((prefix == "") && (this.localName == "xmlns"))
        {
            throw (new DOMException(DOMException.NAMESPACE_ERR));
        }
    }

    // update prefix
    this.prefix = prefix;

    // update nodeName (QName)
    if (this.prefix != "")
    {
        this.nodeName = this.prefix + ":" + this.localName;
    }
    else
    {
        this.nodeName = this.localName;  // no prefix, therefore nodeName is simply localName
    }
};

/**
* @method DOMNode.getLocalName - Java style gettor for .localName
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : String
*/
DOMNode.prototype.getLocalName = function DOMNode_getLocalName()
{
    /// <summary>DOMNode.getLocalName - Java style gettor for .localName</summary>
    /// <returns type="String"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    return this.localName;
};

/**
* @method DOMNode.insertBefore - Inserts the node newChild before the existing child node refChild.
*   If refChild is null, insert newChild at the end of the list of children.
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  newChild : DOMNode - The node to insert.
* @param  refChild : DOMNode - The reference node, i.e., the node before which the new node must be inserted
*
* @throws : DOMException - HIERARCHY_REQUEST_ERR: Raised if the node to insert is one of this node's ancestors
* @throws : DOMException - WRONG_DOCUMENT_ERR: Raised if arg was created from a different document than the one that created this map.
* @throws : DOMException - NO_MODIFICATION_ALLOWED_ERR: Raised if this Node is readonly.
* @throws : DOMException - NOT_FOUND_ERR: Raised if there is no node named name in this map.
*
* @return : DOMNode - The node being inserted.
*/
DOMNode.prototype.insertBefore = function DOMNode_insertBefore(newChild, refChild)
{
    /// <summary>DOMNode.insertBefore - Inserts the node newChild before the existing child node refChild. If refChild is null, insert newChild at the end of the list of children.</summary>
    /// <param name="newChild" type="DOMNode">The node to insert.</param>
    /// <param name="refChild" type="DOMNode">The reference node, i.e., the node before which the new node must be inserted</param>
    /// <returns type="DOMNode">The node being inserted.</returns>
    /// <exception cref="DOMException">HIERARCHY_REQUEST_ERR: Raised if the node to insert is one of this node's ancestors</exception>
    /// <exception cref="DOMException">WRONG_DOCUMENT_ERR: Raised if arg was created from a different document than the one that created this map.</exception>
    /// <exception cref="DOMException">NO_MODIFICATION_ALLOWED_ERR: Raised if this Node is readonly.</exception>
    /// <exception cref="DOMException">NOT_FOUND_ERR: Raised if there is no node named name in this map.</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    var prevNode;

    // test for exceptions
    if (this.ownerDocument.implementation.errorChecking)
    {
        // throw Exception if DOMNode is readonly
        if (this._readonly)
        {
            throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
        }

        // throw Exception if newChild was not created by this Document
        if (this.ownerDocument != newChild.ownerDocument)
        {
            throw (new DOMException(DOMException.WRONG_DOCUMENT_ERR));
        }

        // throw Exception if the node is an ancestor
        if (this._isAncestor(newChild))
        {
            throw (new DOMException(DOMException.HIERARCHY_REQUEST_ERR));
        }
    }

    if (refChild)
    {                                // if refChild is specified, insert before it
        // find index of refChild
        var itemIndex = this.childNodes._findItemIndex(refChild._id);

        // throw Exception if there is no child node with this id
        if (this.ownerDocument.implementation.errorChecking && (itemIndex < 0))
        {
            throw (new DOMException(DOMException.NOT_FOUND_ERR));
        }

        // if the newChild is already in the tree,
        var newChildParent = newChild.parentNode;
        if (newChildParent)
        {
            // remove it
            newChildParent.removeChild(newChild);
        }

        // insert newChild into childNodes
        this.childNodes._insertBefore(newChild, this.childNodes._findItemIndex(refChild._id));

        // do node pointer surgery
        prevNode = refChild.previousSibling;

        // handle DocumentFragment
        if (newChild.nodeType == DOMNode.DOCUMENT_FRAGMENT_NODE)
        {
            if (newChild.childNodes._nodes.length > 0)
            {
                // set the parentNode of DocumentFragment's children
                for (var ind = 0; ind < newChild.childNodes._nodes.length; ind++)
                {
                    newChild.childNodes._nodes[ind].parentNode = this;
                }

                // link refChild to last child of DocumentFragment
                refChild.previousSibling = newChild.childNodes._nodes[newChild.childNodes._nodes.length - 1];
            }
        }
        else
        {
            newChild.parentNode = this;                // set the parentNode of the newChild
            refChild.previousSibling = newChild;       // link refChild to newChild
        }
    }
    else
    {                                         // otherwise, append to end
        prevNode = this.lastChild;
        this.appendChild(newChild);
    }

    if (newChild.nodeType == DOMNode.DOCUMENT_FRAGMENT_NODE)
    {
        // do node pointer surgery for DocumentFragment
        if (newChild.childNodes._nodes.length > 0)
        {
            if (prevNode)
            {
                prevNode.nextSibling = newChild.childNodes._nodes[0];
            }
            else
            {                                         // this is the first child in the list
                this.firstChild = newChild.childNodes._nodes[0];
            }

            newChild.childNodes._nodes[0].previousSibling = prevNode;
            newChild.childNodes._nodes[newChild.childNodes._nodes.length - 1].nextSibling = refChild;
        }
    }
    else
    {
        // do node pointer surgery for newChild
        if (prevNode)
        {
            prevNode.nextSibling = newChild;
        }
        else
        {                                         // this is the first child in the list
            this.firstChild = newChild;
        }

        newChild.previousSibling = prevNode;
        newChild.nextSibling = refChild;
    }

    return newChild;
};

/**
* @method DOMNode.replaceChild - Replaces the child node oldChild with newChild in the list of children,
*   and returns the oldChild node.
*   If the newChild is already in the tree, it is first removed.
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  newChild : DOMNode - The node to insert.
* @param  oldChild : DOMNode - The node being replaced in the list.
*
* @throws : DOMException - HIERARCHY_REQUEST_ERR: Raised if the node to insert is one of this node's ancestors
* @throws : DOMException - WRONG_DOCUMENT_ERR: Raised if arg was created from a different document than the one that created this map.
* @throws : DOMException - NO_MODIFICATION_ALLOWED_ERR: Raised if this Node is readonly.
* @throws : DOMException - NOT_FOUND_ERR: Raised if there is no node named name in this map.
*
* @return : DOMNode - The node that was replaced
*/
DOMNode.prototype.replaceChild = function DOMNode_replaceChild(newChild, oldChild)
{
    /// <summary>DOMNode.replaceChild - Replaces the child node oldChild with newChild in the list of children, and returns the oldChild node. If the newChild is already in the tree, it is first removed.</summary>
    /// <param name="newChild" type="DOMNode">The node to insert.</param>
    /// <param name="oldChild" type="DOMNode">The node being replaced in the list.</param>
    /// <returns type="DOMNode">The node that was replaced</returns>
    /// <exception cref="DOMException">HIERARCHY_REQUEST_ERR: Raised if the node to insert is one of this node's ancestors</exception>
    /// <exception cref="DOMException">WRONG_DOCUMENT_ERR: Raised if arg was created from a different document than the one that created this map.</exception>
    /// <exception cref="DOMException">NO_MODIFICATION_ALLOWED_ERR: Raised if this Node is readonly.</exception>
    /// <exception cref="DOMException">NOT_FOUND_ERR: Raised if there is no node named name in this map.</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    var ret = null;

    // test for exceptions
    if (this.ownerDocument.implementation.errorChecking)
    {
        // throw Exception if DOMNode is readonly
        if (this._readonly)
        {
            throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
        }

        // throw Exception if newChild was not created by this Document
        if (this.ownerDocument != newChild.ownerDocument)
        {
            throw (new DOMException(DOMException.WRONG_DOCUMENT_ERR));
        }

        // throw Exception if the node is an ancestor
        if (this._isAncestor(newChild))
        {
            throw (new DOMException(DOMException.HIERARCHY_REQUEST_ERR));
        }
    }

    // get index of oldChild
    var index = this.childNodes._findItemIndex(oldChild._id);

    // throw Exception if there is no child node with this id
    if (this.ownerDocument.implementation.errorChecking && (index < 0))
    {
        throw (new DOMException(DOMException.NOT_FOUND_ERR));
    }

    // if the newChild is already in the tree,
    var newChildParent = newChild.parentNode;
    if (newChildParent)
    {
        // remove it
        newChildParent.removeChild(newChild);
    }

    // add newChild to childNodes
    ret = this.childNodes._replaceChild(newChild, index);


    if (newChild.nodeType == DOMNode.DOCUMENT_FRAGMENT_NODE)
    {
        // do node pointer surgery for Document Fragment
        if (newChild.childNodes._nodes.length > 0)
        {
            for (var ind = 0; ind < newChild.childNodes._nodes.length; ind++)
            {
                newChild.childNodes._nodes[ind].parentNode = this;
            }

            if (oldChild.previousSibling)
            {
                oldChild.previousSibling.nextSibling = newChild.childNodes._nodes[0];
            }
            else
            {
                this.firstChild = newChild.childNodes._nodes[0];
            }

            if (oldChild.nextSibling)
            {
                oldChild.nextSibling.previousSibling = newChild;
            }
            else
            {
                this.lastChild = newChild.childNodes._nodes[newChild.childNodes._nodes.length - 1];
            }

            newChild.childNodes._nodes[0].previousSibling = oldChild.previousSibling;
            newChild.childNodes._nodes[newChild.childNodes._nodes.length - 1].nextSibling = oldChild.nextSibling;
        }
    }
    else
    {
        // do node pointer surgery for newChild
        newChild.parentNode = this;

        if (oldChild.previousSibling)
        {
            oldChild.previousSibling.nextSibling = newChild;
        }
        else
        {
            this.firstChild = newChild;
        }
        if (oldChild.nextSibling)
        {
            oldChild.nextSibling.previousSibling = newChild;
        }
        else
        {
            this.lastChild = newChild;
        }
        newChild.previousSibling = oldChild.previousSibling;
        newChild.nextSibling = oldChild.nextSibling;
    }
    return ret;
};

/**
* @method DOMNode.removeChild - Removes the child node indicated by oldChild from the list of children, and returns it.
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  oldChild : DOMNode - The node being removed.
*
* @throws : DOMException - NO_MODIFICATION_ALLOWED_ERR: Raised if this Node is readonly.
* @throws : DOMException - NOT_FOUND_ERR: Raised if there is no node named name in this map.
*
* @return : DOMNode - The node being removed.
*/
DOMNode.prototype.removeChild = function DOMNode_removeChild(oldChild)
{
    /// <summary>DOMNode.removeChild - Removes the child node indicated by oldChild from the list of children, and returns it.</summary>
    /// <param name="oldChild" type="DOMNode">The node being removed.</param>
    /// <returns type="DOMNode">The node being removed.</returns>
    /// <exception cref="DOMException">NO_MODIFICATION_ALLOWED_ERR: Raised if this Node is readonly.</exception>
    /// <exception cref="DOMException">NOT_FOUND_ERR: Raised if there is no node named name in this map.</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // throw Exception if DOMNamedNodeMap is readonly
    if (this.ownerDocument.implementation.errorChecking && (this._readonly || oldChild._readonly))
    {
        throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
    }

    // get index of oldChild
    var itemIndex = this.childNodes._findItemIndex(oldChild._id);

    // throw Exception if there is no child node with this id
    if (this.ownerDocument.implementation.errorChecking && (itemIndex < 0))
    {
        throw (new DOMException(DOMException.NOT_FOUND_ERR));
    }

    // remove oldChild from childNodes
    this.childNodes._removeChild(itemIndex);

    // do node pointer surgery
    oldChild.parentNode = null;

    if (oldChild.previousSibling)
    {
        oldChild.previousSibling.nextSibling = oldChild.nextSibling;
    }
    else
    {
        this.firstChild = oldChild.nextSibling;
    }
    if (oldChild.nextSibling)
    {
        oldChild.nextSibling.previousSibling = oldChild.previousSibling;
    }
    else
    {
        this.lastChild = oldChild.previousSibling;
    }

    oldChild.previousSibling = null;
    oldChild.nextSibling = null;
    return oldChild;
};

/**
* @method DOMNode.appendChild - Adds the node newChild to the end of the list of children of this node.
*   If the newChild is already in the tree, it is first removed.
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  newChild : DOMNode - The node to add
*
* @throws : DOMException - HIERARCHY_REQUEST_ERR: Raised if the node to insert is one of this node's ancestors
* @throws : DOMException - WRONG_DOCUMENT_ERR: Raised if arg was created from a different document than the one that created this map.
* @throws : DOMException - NO_MODIFICATION_ALLOWED_ERR: Raised if this Node is readonly.
*
* @return : DOMNode - The node added
*/
DOMNode.prototype.appendChild = function DOMNode_appendChild(newChild)
{
    /// <summary>DOMNode.appendChild - Adds the node newChild to the end of the list of children of this node. If the newChild is already in the tree, it is first removed.</summary>
    /// <param name="newChild" type="DOMNode">The node to add</param>
    /// <returns type="DOMNode">The node added</returns>
    /// <exception cref="DOMException">HIERARCHY_REQUEST_ERR: Raised if the node to insert is one of this node's ancestors</exception>
    /// <exception cref="DOMException">WRONG_DOCUMENT_ERR: Raised if arg was created from a different document than the one that created this map.</exception>
    /// <exception cref="DOMException">NO_MODIFICATION_ALLOWED_ERR: Raised if this Node is readonly.</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // test for exceptions
    if (this.ownerDocument.implementation.errorChecking)
    {
        // throw Exception if Node is readonly
        if (this._readonly)
        {
            throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
        }

        // throw Exception if arg was not created by this Document
        if (this.ownerDocument != newChild.ownerDocument)
        {
            throw (new DOMException(DOMException.WRONG_DOCUMENT_ERR));
        }

        // throw Exception if the node is an ancestor
        if (this._isAncestor(newChild))
        {
            throw (new DOMException(DOMException.HIERARCHY_REQUEST_ERR));
        }
    }

    // if the newChild is already in the tree,
    var newChildParent = newChild.parentNode;
    if (newChildParent)
    {
        // remove it
        newChildParent.removeChild(newChild);
    }

    // add newChild to childNodes
    this.childNodes._appendChild(newChild);

    if (newChild.nodeType == DOMNode.DOCUMENT_FRAGMENT_NODE)
    {
        // do node pointer surgery for DocumentFragment
        if (newChild.childNodes._nodes.length > 0)
        {
            for (var ind = 0; ind < newChild.childNodes._nodes.length; ind++)
            {
                newChild.childNodes._nodes[ind].parentNode = this;
            }

            if (this.lastChild)
            {
                this.lastChild.nextSibling = newChild.childNodes._nodes[0];
                newChild.childNodes._nodes[0].previousSibling = this.lastChild;
                this.lastChild = newChild.childNodes._nodes[newChild.childNodes._nodes.length - 1];
            }
            else
            {
                this.lastChild = newChild.childNodes._nodes[newChild.childNodes._nodes.length - 1];
                this.firstChild = newChild.childNodes._nodes[0];
            }
        }
    }
    else
    {
        // do node pointer surgery for newChild
        newChild.parentNode = this;
        if (this.lastChild)
        {
            this.lastChild.nextSibling = newChild;
            newChild.previousSibling = this.lastChild;
            this.lastChild = newChild;
        }
        else
        {
            this.lastChild = newChild;
            this.firstChild = newChild;
        }
    }

    return newChild;
};

/**
* @method DOMNode.hasChildNodes - This is a convenience method to allow easy determination of whether a node has any children.
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : boolean - true if the node has any children, false if the node has no children
*/
DOMNode.prototype.hasChildNodes = function DOMNode_hasChildNodes()
{
    /// <summary>DOMNode.hasChildNodes - This is a convenience method to allow easy determination of whether a node has any children.</summary>
    /// <returns type="Boolean">true if the node has any children, false if the node has no children</returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    return (this.childNodes.length > 0);
};

/**
* @method DOMNode.cloneNode - Returns a duplicate of this node, i.e., serves as a generic copy constructor for nodes.
*   The duplicate node has no parent (parentNode returns null.).
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  deep : boolean - If true, recursively clone the subtree under the specified node;
*   if false, clone only the node itself (and its attributes, if it is an Element).
*
* @return : DOMNode
*/
DOMNode.prototype.cloneNode = function DOMNode_cloneNode(deep)
{
    /// <summary>DOMNode.cloneNode - Returns a duplicate of this node, i.e., serves as a generic copy constructor for nodes. The duplicate node has no parent (parentNode returns null.).</summary>
    /// <param name="deep" type="Boolean">If true, recursively clone the subtree under the specified node; if false, clone only the node itself (and its attributes, if it is an Element).</param>
    /// <returns type="DOMNode"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // use importNode to clone this Node
    //do not throw any exceptions
    try
    {
        return this.ownerDocument.importNode(this, deep);
    }
    catch (e)
    {
        //there shouldn't be any exceptions, but if there are, return null
        return null;
    }
};

/**
* @method DOMNode.normalize - Puts all Text nodes in the full depth of the sub-tree underneath this Element into a "normal" form
*   where only markup (e.g., tags, comments, processing instructions, CDATA sections, and entity references) separates Text nodes,
*   i.e., there are no adjacent Text nodes.
*
* @author Jon van Noort (jon@webarcana.com.au), David Joham (djoham@yahoo.com) and Scott Severtson
*/
DOMNode.prototype.normalize = function DOMNode_normalize()
{
    /// <summary>DOMNode.normalize - Puts all Text nodes in the full depth of the sub-tree underneath this Element into a "normal" form where only markup (e.g., tags, comments, processing instructions, CDATA sections, and entity references) separates Text nodes, i.e., there are no adjacent Text nodes.</summary>
    /// <remarks>Jon van Noort (jon@webarcana.com.au), David Joham (djoham@yahoo.com) and Scott Severtson</remarks>


    var inode;
    var nodesToRemove = new DOMNodeList();

    if (this.nodeType == DOMNode.ELEMENT_NODE || this.nodeType == DOMNode.DOCUMENT_NODE)
    {
        var adjacentTextNode = null;

        // loop through all childNodes
        for (var i = 0; i < this.childNodes.length; i++)
        {
            inode = this.childNodes.item(i);

            if (inode.nodeType == DOMNode.TEXT_NODE)
            { // this node is a text node
                if (inode.length < 1)
                {                  // this text node is empty
                    nodesToRemove._appendChild(inode);      // add this node to the list of nodes to be remove
                }
                else
                {
                    if (adjacentTextNode)
                    {                // if previous node was also text
                        adjacentTextNode.appendData(inode.data);     // merge the data in adjacent text nodes
                        nodesToRemove._appendChild(inode);    // add this node to the list of nodes to be removed
                    }
                    else
                    {
                        adjacentTextNode = inode;              // remember this node for next cycle
                    }
                }
            }
            else
            {
                adjacentTextNode = null;                 // (soon to be) previous node is not a text node
                inode.normalize();                       // normalise non Text childNodes
            }
        }

        // remove redundant Text Nodes
        for (var i = 0; i < nodesToRemove.length; i++)
        {
            inode = nodesToRemove.item(i);
            inode.parentNode.removeChild(inode);
        }
    }
};

/**
* @method DOMNode.isSupported - Test if the DOM implementation implements a specific feature
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  feature : string - The package name of the feature to test. the legal only values are "XML" and "CORE" (case-insensitive).
* @param  version : string - This is the version number of the package name to test. In Level 1, this is the string "1.0".
*
* @return : boolean
*/
DOMNode.prototype.isSupported = function DOMNode_isSupported(feature, version)
{
    /// <summary>DOMNode.isSupported - Test if the DOM implementation implements a specific feature</summary>
    /// <param name="feature" type="String">The package name of the feature to test. the legal only values are "XML" and "CORE" (case-insensitive).</param>
    /// <param name="version" type="String">This is the version number of the package name to test. In Level 1, this is the string "1.0".</param>
    /// <returns type="Boolean"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // use Implementation.hasFeature to determin if this feature is supported
    return this.ownerDocument.implementation.hasFeature(feature, version);
}

/**
* @method DOMNode.getElementsByTagName - Returns a NodeList of all the Elements with a given tag name
*   in the order in which they would be encountered in a preorder traversal of the Document tree.
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  tagname : string - The name of the tag to match on. The special value "*" matches all tags
*
* @return : DOMNodeList
*/
DOMNode.prototype.getElementsByTagName = function DOMNode_getElementsByTagName(tagname)
{
    /// <summary>DOMNode.getElementsByTagName - Returns a NodeList of all the Elements with a given tag name in the order in which they would be encountered in a preorder traversal of the Document tree.</summary>
    /// <param name="tagname" type="String">The name of the tag to match on. The special value " " matches all tags</param>
    /// <returns type="DOMNodeList"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // delegate to _getElementsByTagNameRecursive
    return this._getElementsByTagNameRecursive(tagname, new DOMNodeList(this.ownerDocument));
};

/**
* @method DOMNode._getElementsByTagNameRecursive - implements getElementsByTagName()
*
* @author Jon van Noort (jon@webarcana.com.au), David Joham (djoham@yahoo.com) and Scott Severtson
*
* @param  tagname  : string      - The name of the tag to match on. The special value "*" matches all tags
* @param  nodeList : DOMNodeList - The accumulating list of matching nodes
*
* @return : DOMNodeList
*/
DOMNode.prototype._getElementsByTagNameRecursive = function DOMNode__getElementsByTagNameRecursive(tagname, nodeList)
{
    /// <summary>DOMNode._getElementsByTagNameRecursive - implements getElementsByTagName()</summary>
    /// <param name="tagname" type="String">The name of the tag to match on. The special value " " matches all tags</param>
    /// <param name="nodeList" type="DOMNodeList">The accumulating list of matching nodes</param>
    /// <returns type="DOMNodeList"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au), David Joham (djoham@yahoo.com) and Scott Severtson</remarks>


    if (this.nodeType == DOMNode.ELEMENT_NODE || this.nodeType == DOMNode.DOCUMENT_NODE)
    {

        if ((this.nodeName == tagname) || (tagname == "*"))
        {
            nodeList._appendChild(this);               // add matching node to nodeList
        }

        // recurse childNodes
        for (var i = 0; i < this.childNodes.length; i++)
        {
            nodeList = this.childNodes.item(i)._getElementsByTagNameRecursive(tagname, nodeList);
        }
    }

    return nodeList;
};

/**
* @method DOMNode.getXML - Returns the String XML of the node and all of its children
*
* @author Jon van Noort (jon@webarcana.com.au) and David Joham (djoham@yahoo.com)
*
* @return : string - XML String of the XML of the node and all of its children
*/
DOMNode.prototype.getXML = function DOMNode_getXML()
{
    /// <summary>DOMNode.getXML - Returns the String XML of the node and all of its children</summary>
    /// <returns type="String">XML String of the XML of the node and all of its children</returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au) and David Joham (djoham@yahoo.com)</remarks>


    return this.toString();
}


/**
* @method DOMNode.getElementsByTagNameNS - Returns a NodeList of all the Elements with a given namespaceURI and localName
*   in the order in which they would be encountered in a preorder traversal of the Document tree.
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  namespaceURI : string - the namespace URI of the required node
* @param  localName    : string - the local name of the required node
*
* @return : DOMNodeList
*/
DOMNode.prototype.getElementsByTagNameNS = function DOMNode_getElementsByTagNameNS(namespaceURI, localName)
{
    /// <summary>DOMNode.getElementsByTagNameNS - Returns a NodeList of all the Elements with a given namespaceURI and localName in the order in which they would be encountered in a preorder traversal of the Document tree.</summary>
    /// <param name="namespaceURI" type="String">the namespace URI of the required node</param>
    /// <param name="localName" type="String">the local name of the required node</param>
    /// <returns type="DOMNodeList"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // delegate to _getElementsByTagNameNSRecursive
    return this._getElementsByTagNameNSRecursive(namespaceURI, localName, new DOMNodeList(this.ownerDocument));
};

/**
* @method DOMNode._getElementsByTagNameNSRecursive - implements getElementsByTagName()
*
* @author Jon van Noort (jon@webarcana.com.au), David Joham (djoham@yahoo.com) and Scott Severtson
*
* @param  namespaceURI : string - the namespace URI of the required node
* @param  localName    : string - the local name of the required node
* @param  nodeList     : DOMNodeList - The accumulating list of matching nodes
*
* @return : DOMNodeList
*/
DOMNode.prototype._getElementsByTagNameNSRecursive = function DOMNode__getElementsByTagNameNSRecursive(namespaceURI, localName, nodeList)
{
    /// <summary>DOMNode._getElementsByTagNameNSRecursive - implements getElementsByTagName()</summary>
    /// <param name="namespaceURI" type="String">the namespace URI of the required node</param>
    /// <param name="localName" type="String">the local name of the required node</param>
    /// <param name="nodeList" type="DOMNodeList">The accumulating list of matching nodes</param>
    /// <returns type="DOMNodeList"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au), David Joham (djoham@yahoo.com) and Scott Severtson</remarks>


    if (this.nodeType == DOMNode.ELEMENT_NODE || this.nodeType == DOMNode.DOCUMENT_NODE)
    {

        if (((this.namespaceURI == namespaceURI) || (namespaceURI == "*")) && ((this.localName == localName) || (localName == "*")))
        {
            nodeList._appendChild(this);               // add matching node to nodeList
        }

        // recurse childNodes
        for (var i = 0; i < this.childNodes.length; i++)
        {
            nodeList = this.childNodes.item(i)._getElementsByTagNameNSRecursive(namespaceURI, localName, nodeList);
        }
    }

    return nodeList;
};

/**
* @method DOMNode._isAncestor - returns true if node is ancestor of this
*
* @author Jon van Noort (jon@webarcana.com.au), David Joham (djoham@yahoo.com) and Scott Severtson
*
* @param  node         : DOMNode - The candidate ancestor node
*
* @return : boolean
*/
DOMNode.prototype._isAncestor = function DOMNode__isAncestor(node)
{
    /// <summary>DOMNode._isAncestor - returns true if node is ancestor of this</summary>
    /// <param name="node" type="DOMNode">The candidate ancestor node</param>
    /// <returns type="Boolean"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au), David Joham (djoham@yahoo.com) and Scott Severtson</remarks>


    // if this node matches, return true,
    // otherwise recurse up (if there is a parentNode)
    return ((this == node) || ((this.parentNode) && (this.parentNode._isAncestor(node))));
}

/**
* @method DOMNode.importNode - Imports a node from another document to this document.
*   The returned node has no parent; (parentNode is null).
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  importedNode : Node - The Node to be imported
* @param  deep         : boolean - If true, recursively clone the subtree under the specified node;
*   if false, clone only the node itself (and its attributes, if it is an Element).
*
* @return : DOMNode
*/
DOMNode.prototype.importNode = function DOMNode_importNode(importedNode, deep)
{
    /// <summary>DOMNode.importNode - Imports a node from another document to this document. The returned node has no parent; (parentNode is null).</summary>
    /// <param name="importedNode" type="Node">The Node to be imported</param>
    /// <param name="deep" type="Boolean">If true, recursively clone the subtree under the specified node; if false, clone only the node itself (and its attributes, if it is an Element).</param>
    /// <returns type="DOMNode"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    var importNode;

    //there is no need to perform namespace checks since everything has already gone through them
    //in order to have gotten into the DOM in the first place. The following line
    //turns namespace checking off in ._isValidNamespace
    this.getOwnerDocument()._performingImportNodeOperation = true;

    try
    {
        if (importedNode.nodeType == DOMNode.ELEMENT_NODE)
        {
            if (!this.ownerDocument.implementation.namespaceAware)
            {
                // create a local Element (with the name of the importedNode)
                importNode = this.ownerDocument.createElement(importedNode.tagName);

                // create attributes matching those of the importedNode
                for (var i = 0; i < importedNode.attributes.length; i++)
                {
                    importNode.setAttribute(importedNode.attributes.item(i).name, importedNode.attributes.item(i).value);
                }
            }
            else
            {
                // create a local Element (with the name & namespaceURI of the importedNode)
                importNode = this.ownerDocument.createElementNS(importedNode.namespaceURI, importedNode.nodeName);

                // create attributes matching those of the importedNode
                for (var i = 0; i < importedNode.attributes.length; i++)
                {
                    importNode.setAttributeNS(importedNode.attributes.item(i).namespaceURI, importedNode.attributes.item(i).name, importedNode.attributes.item(i).value);
                }

                // create namespace definitions matching those of the importedNode
                for (var i = 0; i < importedNode._namespaces.length; i++)
                {
                    importNode._namespaces._nodes[i] = this.ownerDocument.createNamespace(importedNode._namespaces.item(i).localName);
                    importNode._namespaces._nodes[i].setValue(importedNode._namespaces.item(i).value);
                }
            }
        }
        else if (importedNode.nodeType == DOMNode.ATTRIBUTE_NODE)
        {
            if (!this.ownerDocument.implementation.namespaceAware)
            {
                // create a local Attribute (with the name of the importedAttribute)
                importNode = this.ownerDocument.createAttribute(importedNode.name);
            }
            else
            {
                // create a local Attribute (with the name & namespaceURI of the importedAttribute)
                importNode = this.ownerDocument.createAttributeNS(importedNode.namespaceURI, importedNode.nodeName);

                // create namespace definitions matching those of the importedAttribute
                for (var i = 0; i < importedNode._namespaces.length; i++)
                {
                    importNode._namespaces._nodes[i] = this.ownerDocument.createNamespace(importedNode._namespaces.item(i).localName);
                    importNode._namespaces._nodes[i].setValue(importedNode._namespaces.item(i).value);
                }
            }

            // set the value of the local Attribute to match that of the importedAttribute
            importNode.setValue(importedNode.value);
        }
        else if (importedNode.nodeType == DOMNode.DOCUMENT_FRAGMENT)
        {
            // create a local DocumentFragment
            importNode = this.ownerDocument.createDocumentFragment();
        }
        else if (importedNode.nodeType == DOMNode.NAMESPACE_NODE)
        {
            // create a local NamespaceNode (with the same name & value as the importedNode)
            importNode = this.ownerDocument.createNamespace(importedNode.nodeName);
            importNode.setValue(importedNode.value);
        }
        else if (importedNode.nodeType == DOMNode.TEXT_NODE)
        {
            // create a local TextNode (with the same data as the importedNode)
            importNode = this.ownerDocument.createTextNode(importedNode.data);
        }
        else if (importedNode.nodeType == DOMNode.CDATA_SECTION_NODE)
        {
            // create a local CDATANode (with the same data as the importedNode)
            importNode = this.ownerDocument.createCDATASection(importedNode.data);
        }
        else if (importedNode.nodeType == DOMNode.PROCESSING_INSTRUCTION_NODE)
        {
            // create a local ProcessingInstruction (with the same target & data as the importedNode)
            importNode = this.ownerDocument.createProcessingInstruction(importedNode.target, importedNode.data);
        }
        else if (importedNode.nodeType == DOMNode.COMMENT_NODE)
        {
            // create a local Comment (with the same data as the importedNode)
            importNode = this.ownerDocument.createComment(importedNode.data);
        }
        else
        {  // throw Exception if nodeType is not supported
            throw (new DOMException(DOMException.NOT_SUPPORTED_ERR));
        }

        if (deep)
        {                                    // recurse childNodes
            for (var i = 0; i < importedNode.childNodes.length; i++)
            {
                importNode.appendChild(this.ownerDocument.importNode(importedNode.childNodes.item(i), true));
            }
        }

        //reset _performingImportNodeOperation
        this.getOwnerDocument()._performingImportNodeOperation = false;
        return importNode;
    }
    catch (eAny)
    {
        //reset _performingImportNodeOperation
        this.getOwnerDocument()._performingImportNodeOperation = false;

        //re-throw the exception
        throw eAny;
    } //djotemp
};

/**
* @method DOMNode.escapeString - escape special characters
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  str : string - The string to be escaped
*
* @return : string - The escaped string
*/
DOMNode.prototype.__escapeString = function DOMNode__escapeString(str)
{
    /// <summary>DOMNode.escapeString - escape special characters</summary>
    /// <param name="str" type="String">The string to be escaped</param>
    /// <returns type="String">The escaped string</returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>



    //the sax processor already has this function. Just wrap it
    return __escapeString(str);
};

/**
* @method DOMNode.unescapeString - unescape special characters
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  str : string - The string to be unescaped
*
* @return : string - The unescaped string
*/
DOMNode.prototype.__unescapeString = function DOMNode__unescapeString(str)
{
    /// <summary>DOMNode.unescapeString - unescape special characters</summary>
    /// <param name="str" type="String">The string to be unescaped</param>
    /// <returns type="String">The unescaped string</returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>



    //the sax processor already has this function. Just wrap it
    return __unescapeString(str);
};



/**
* @class  DOMDocument - The Document interface represents the entire HTML or XML document.
*   Conceptually, it is the root of the document tree, and provides the primary access to the document's data.
*
* @extends DOMNode
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  implementation : DOMImplementation - the creator Implementation
*/
DOMDocument = function(implementation)
{
    /// <summary>DOMDocument - The Document interface represents the entire HTML or XML document. Conceptually, it is the root of the document tree, and provides the primary access to the document's data.</summary>
    /// <param name="implementation" type="DOMImplementation">the creator Implementation</param>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>
    /// <extends>DOMNode</extends>


    this._class = addClass(this._class, "DOMDocument");
    this.DOMNode = DOMNode;
    this.DOMNode(this);

    this.doctype = null;                           // The Document Type Declaration (see DocumentType) associated with this document
    this.implementation = implementation;          // The DOMImplementation object that handles this document.
    this.documentElement = null;                   // This is a convenience attribute that allows direct access to the child node that is the root element of the document
    this.all = new Array();                       // The list of all Elements

    this.nodeName = "#document";
    this.nodeType = DOMNode.DOCUMENT_NODE;
    this._id = 0;
    this._lastId = 0;
    this._parseComplete = false;                   // initially false, set to true by parser

    this.ownerDocument = this;

    this._performingImportNodeOperation = false;
};
DOMDocument.prototype = new DOMNode;

/**
* @method DOMDocument.getDoctype - Java style gettor for .doctype
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : DOMDocument
*/
DOMDocument.prototype.getDoctype = function DOMDocument_getDoctype()
{
    /// <summary>DOMDocument.getDoctype - Java style gettor for .doctype</summary>
    /// <returns type="DOMDocument"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    return this.doctype;
};

/**
* @method DOMDocument.getImplementation - Java style gettor for .implementation
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : DOMImplementation
*/
DOMDocument.prototype.getImplementation = function DOMDocument_implementation()
{
    /// <summary>DOMDocument.getImplementation - Java style gettor for .implementation</summary>
    /// <returns type="DOMImplementation"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    return this.implementation;
};

/**
* @method DOMDocument.getDocumentElement - Java style gettor for .documentElement
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : DOMDocumentElement
*/
DOMDocument.prototype.getDocumentElement = function DOMDocument_getDocumentElement()
{
    /// <summary>DOMDocument.getDocumentElement - Java style gettor for .documentElement</summary>
    /// <returns type="DOMDocumentElement"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    return this.documentElement;
};

/**
* @method DOMDocument.createElement - Creates an element of the type specified.
*   Note that the instance returned implements the Element interface,
*   so attributes can be specified directly on the returned object.
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  tagName : string - The name of the element type to instantiate.
*
* @throws : DOMException - INVALID_CHARACTER_ERR: Raised if the string contains an illegal character
*
* @return : DOMElement - The new Element object.
*/
DOMDocument.prototype.createElement = function DOMDocument_createElement(tagName)
{
    /// <summary>DOMDocument.createElement - Creates an element of the type specified. Note that the instance returned implements the Element interface, so attributes can be specified directly on the returned object.</summary>
    /// <param name="tagName" type="String">The name of the element type to instantiate.</param>
    /// <returns type="DOMElement">The new Element object.</returns>
    /// <exception cref="DOMException">INVALID_CHARACTER_ERR: Raised if the string contains an illegal character</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // throw Exception if the tagName string contains an illegal character
    if (this.ownerDocument.implementation.errorChecking && (!this.ownerDocument.implementation._isValidName(tagName)))
    {
        throw (new DOMException(DOMException.INVALID_CHARACTER_ERR));
    }

    // create DOMElement specifying 'this' as ownerDocument
    var node = new DOMElement(this);

    // assign values to properties (and aliases)
    node.tagName = tagName;
    node.nodeName = tagName;

    // add Element to 'all' collection
    this.all[this.all.length] = node;

    return node;
};

/**
* @method DOMDocument.createDocumentFragment - CCreates an empty DocumentFragment object.
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : DOMDocumentFragment - The new DocumentFragment object
*/
DOMDocument.prototype.createDocumentFragment = function DOMDocument_createDocumentFragment()
{
    /// <summary>DOMDocument.createDocumentFragment - CCreates an empty DocumentFragment object.</summary>
    /// <returns type="DOMDocumentFragment">The new DocumentFragment object</returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // create DOMDocumentFragment specifying 'this' as ownerDocument
    var node = new DOMDocumentFragment(this);

    return node;
};

/**
* @method DOMDocument.createTextNode - Creates a Text node given the specified string.
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  data : string - The data for the node.
*
* @return : DOMText - The new Text object.
*/
DOMDocument.prototype.createTextNode = function DOMDocument_createTextNode(data)
{
    /// <summary>DOMDocument.createTextNode - Creates a Text node given the specified string.</summary>
    /// <param name="data" type="String">The data for the node.</param>
    /// <returns type="DOMText">The new Text object.</returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // create DOMText specifying 'this' as ownerDocument
    var node = new DOMText(this);

    // assign values to properties (and aliases)
    node.data = data;
    node.nodeValue = data;

    // set initial length
    node.length = data.length;

    return node;
};

/**
* @method DOMDocument.createComment - Creates a Text node given the specified string.
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  data : string - The data for the node.
*
* @return : DOMComment - The new Comment object.
*/
DOMDocument.prototype.createComment = function DOMDocument_createComment(data)
{
    /// <summary>DOMDocument.createComment - Creates a Text node given the specified string.</summary>
    /// <param name="data" type="String">The data for the node.</param>
    /// <returns type="DOMComment">The new Comment object.</returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // create DOMComment specifying 'this' as ownerDocument
    var node = new DOMComment(this);

    // assign values to properties (and aliases)
    node.data = data;
    node.nodeValue = data;

    // set initial length
    node.length = data.length;

    return node;
};

/**
* @method DOMDocument.createCDATASection - Creates a CDATASection node whose value is the specified string.
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  data : string - The data for the node.
*
* @return : DOMCDATASection - The new CDATASection object.
*/
DOMDocument.prototype.createCDATASection = function DOMDocument_createCDATASection(data)
{
    /// <summary>DOMDocument.createCDATASection - Creates a CDATASection node whose value is the specified string.</summary>
    /// <param name="data" type="String">The data for the node.</param>
    /// <returns type="DOMCDATASection">The new CDATASection object.</returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // create DOMCDATASection specifying 'this' as ownerDocument
    var node = new DOMCDATASection(this);

    // assign values to properties (and aliases)
    node.data = data;
    node.nodeValue = data;

    // set initial length
    node.length = data.length;

    return node;
};

/**
* @method DOMDocument.createProcessingInstruction - Creates a ProcessingInstruction node given the specified target and data strings.
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  target : string - The target part of the processing instruction.
* @param  data   : string - The data for the node.
*
* @throws : DOMException - INVALID_CHARACTER_ERR: Raised if the string contains an illegal character
*
* @return : DOMProcessingInstruction - The new ProcessingInstruction object.
*/
DOMDocument.prototype.createProcessingInstruction = function DOMDocument_createProcessingInstruction(target, data)
{
    /// <summary>DOMDocument.createProcessingInstruction - Creates a ProcessingInstruction node given the specified target and data strings.</summary>
    /// <param name="target" type="String">The target part of the processing instruction.</param>
    /// <param name="data" type="String">The data for the node.</param>
    /// <returns type="DOMProcessingInstruction">The new ProcessingInstruction object.</returns>
    /// <exception cref="DOMException">INVALID_CHARACTER_ERR: Raised if the string contains an illegal character</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // throw Exception if the target string contains an illegal character
    if (this.ownerDocument.implementation.errorChecking && (!this.implementation._isValidName(target)))
    {
        throw (new DOMException(DOMException.INVALID_CHARACTER_ERR));
    }

    // create DOMProcessingInstruction specifying 'this' as ownerDocument
    var node = new DOMProcessingInstruction(this);

    // assign values to properties (and aliases)
    node.target = target;
    node.nodeName = target;
    node.data = data;
    node.nodeValue = data;

    // set initial length
    node.length = data.length;

    return node;
};

/**
* @method DOMDocument.createAttribute - Creates an Attr of the given name
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  name : string - The name of the attribute.
*
* @throws : DOMException - INVALID_CHARACTER_ERR: Raised if the string contains an illegal character
*
* @return : DOMAttr - The new Attr object.
*/
DOMDocument.prototype.createAttribute = function DOMDocument_createAttribute(name)
{
    /// <summary>DOMDocument.createAttribute - Creates an Attr of the given name</summary>
    /// <param name="name" type="String">The name of the attribute.</param>
    /// <returns type="DOMAttr">The new Attr object.</returns>
    /// <exception cref="DOMException">INVALID_CHARACTER_ERR: Raised if the string contains an illegal character</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // throw Exception if the name string contains an illegal character
    if (this.ownerDocument.implementation.errorChecking && (!this.ownerDocument.implementation._isValidName(name)))
    {
        throw (new DOMException(DOMException.INVALID_CHARACTER_ERR));
    }

    // create DOMAttr specifying 'this' as ownerDocument
    var node = new DOMAttr(this);

    // assign values to properties (and aliases)
    node.name = name;
    node.nodeName = name;

    return node;
};

/**
* @method DOMDocument.createElementNS - Creates an element of the type specified,
*   within the specified namespace.
*   Note that the instance returned implements the Element interface,
*   so attributes can be specified directly on the returned object.
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  namespaceURI  : string - The namespace URI of the element.
* @param  qualifiedName : string - The qualified name of the element type to instantiate.
*
* @throws : DOMException - NAMESPACE_ERR: Raised if the Namespace is invalid
* @throws : DOMException - INVALID_CHARACTER_ERR: Raised if the string contains an illegal character
*
* @return : DOMElement - The new Element object.
*/
DOMDocument.prototype.createElementNS = function DOMDocument_createElementNS(namespaceURI, qualifiedName)
{
    /// <summary>DOMDocument.createElementNS - Creates an element of the type specified, within the specified namespace. Note that the instance returned implements the Element interface, so attributes can be specified directly on the returned object.</summary>
    /// <param name="namespaceURI" type="String">The namespace URI of the element.</param>
    /// <param name="qualifiedName" type="String">The qualified name of the element type to instantiate.</param>
    /// <returns type="DOMElement">The new Element object.</returns>
    /// <exception cref="DOMException">NAMESPACE_ERR: Raised if the Namespace is invalid</exception>
    /// <exception cref="DOMException">INVALID_CHARACTER_ERR: Raised if the string contains an illegal character</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // test for exceptions
    if (this.ownerDocument.implementation.errorChecking)
    {
        // throw Exception if the Namespace is invalid
        if (!this.ownerDocument._isValidNamespace(namespaceURI, qualifiedName))
        {
            throw (new DOMException(DOMException.NAMESPACE_ERR));
        }

        // throw Exception if the qualifiedName string contains an illegal character
        if (!this.ownerDocument.implementation._isValidName(qualifiedName))
        {
            throw (new DOMException(DOMException.INVALID_CHARACTER_ERR));
        }
    }

    // create DOMElement specifying 'this' as ownerDocument
    var node = new DOMElement(this);
    var qname = this.implementation._parseQName(qualifiedName);

    // assign values to properties (and aliases)
    node.nodeName = qualifiedName;
    node.namespaceURI = namespaceURI;
    node.prefix = qname.prefix;
    node.localName = qname.localName;
    node.tagName = qualifiedName;

    // add Element to 'all' collection
    this.all[this.all.length] = node;

    return node;
};

/**
* @method DOMDocument.createAttributeNS - Creates an Attr of the given name
*   within the specified namespace.
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  namespaceURI  : string - The namespace URI of the attribute.
* @param  qualifiedName : string - The qualified name of the attribute.
*
* @throws : DOMException - NAMESPACE_ERR: Raised if the Namespace is invalid
* @throws : DOMException - INVALID_CHARACTER_ERR: Raised if the string contains an illegal character
*
* @return : DOMAttr - The new Attr object.
*/
DOMDocument.prototype.createAttributeNS = function DOMDocument_createAttributeNS(namespaceURI, qualifiedName)
{
    /// <summary>DOMDocument.createAttributeNS - Creates an Attr of the given name within the specified namespace.</summary>
    /// <param name="namespaceURI" type="String">The namespace URI of the attribute.</param>
    /// <param name="qualifiedName" type="String">The qualified name of the attribute.</param>
    /// <returns type="DOMAttr">The new Attr object.</returns>
    /// <exception cref="DOMException">NAMESPACE_ERR: Raised if the Namespace is invalid</exception>
    /// <exception cref="DOMException">INVALID_CHARACTER_ERR: Raised if the string contains an illegal character</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // test for exceptions
    if (this.ownerDocument.implementation.errorChecking)
    {
        // throw Exception if the Namespace is invalid
        if (!this.ownerDocument._isValidNamespace(namespaceURI, qualifiedName, true))
        {
            throw (new DOMException(DOMException.NAMESPACE_ERR));
        }

        // throw Exception if the qualifiedName string contains an illegal character
        if (!this.ownerDocument.implementation._isValidName(qualifiedName))
        {
            throw (new DOMException(DOMException.INVALID_CHARACTER_ERR));
        }
    }

    // create DOMAttr specifying 'this' as ownerDocument
    var node = new DOMAttr(this);
    var qname = this.implementation._parseQName(qualifiedName);

    // assign values to properties (and aliases)
    node.nodeName = qualifiedName
    node.namespaceURI = namespaceURI
    node.prefix = qname.prefix;
    node.localName = qname.localName;
    node.name = qualifiedName
    node.nodeValue = "";

    return node;
};

/**
* @method DOMDocument.createNamespace - Creates an Namespace of the given name
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  qualifiedName : string - The qualified name of the attribute.
*
* @return : DOMNamespace - The new Namespace object.
*/
DOMDocument.prototype.createNamespace = function DOMDocument_createNamespace(qualifiedName)
{
    /// <summary>DOMDocument.createNamespace - Creates an Namespace of the given name</summary>
    /// <param name="qualifiedName" type="String">The qualified name of the attribute.</param>
    /// <returns type="DOMNamespace">The new Namespace object.</returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // create DOMNamespace specifying 'this' as ownerDocument
    var node = new DOMNamespace(this);
    var qname = this.implementation._parseQName(qualifiedName);

    // assign values to properties (and aliases)
    node.nodeName = qualifiedName
    node.prefix = qname.prefix;
    node.localName = qname.localName;
    node.name = qualifiedName
    node.nodeValue = "";

    return node;
};

/**
* @method DOMDocument.getElementById - Return the Element whose ID is given by elementId
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  elementId : string - The unique ID of the Element
*
* @return : DOMElement - The requested DOMElement
*/
DOMDocument.prototype.getElementById = function DOMDocument_getElementById(elementId)
{
    /// <summary>DOMDocument.getElementById - Return the Element whose ID is given by elementId</summary>
    /// <param name="elementId" type="String">The unique ID of the Element</param>
    /// <returns type="DOMElement">The requested DOMElement</returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    //  return this._ids[elementId];
    retNode = null;

    // loop through all Elements in the 'all' collection
    for (var i = 0; i < this.all.length; i++)
    {
        var node = this.all[i];

        // if id matches & node is alive (ie, connected (in)directly to the documentElement)
        if ((node.id == elementId) && (node._isAncestor(node.ownerDocument.documentElement)))
        {
            retNode = node;
            break;
        }
    }

    return retNode;
};



/**
* @method DOMDocument._genId - generate a unique internal id
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : string - The unique (serial) id
*/
DOMDocument.prototype._genId = function DOMDocument__genId()
{
    /// <summary>DOMDocument._genId - generate a unique internal id</summary>
    /// <returns type="String">The unique (serial) id</returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    this._lastId += 1;                             // increment lastId (to generate unique id)

    return this._lastId;
};


/**
* @method DOMDocument._isValidNamespace - test if Namespace is valid
*  ie, not valid if;
*    the qualifiedName is malformed, or
*    the qualifiedName has a prefix and the namespaceURI is null, or
*    the qualifiedName has a prefix that is "xml" and the namespaceURI is
*     different from "http://www.w3.org/XML/1998/namespace" [Namespaces].
*
* @author Jon van Noort (jon@webarcana.com.au), David Joham (djoham@yahoo.com) and Scott Severtson
*
* @param  namespaceURI  : string - the namespace URI
* @param  qualifiedName : string - the QName
* @Param  isAttribute   : boolean - true, if the requesting node is an Attr
*
* @return : boolean
*/
DOMDocument.prototype._isValidNamespace = function DOMDocument__isValidNamespace(namespaceURI, qualifiedName, isAttribute)
{
    /// <summary>DOMDocument._isValidNamespace - test if Namespace is valid ie, not valid if; the qualifiedName is malformed, or the qualifiedName has a prefix and the namespaceURI is null, or the qualifiedName has a prefix that is "xml" and the namespaceURI is different from "http://www.w3.org/XML/1998/namespace" [Namespaces].</summary>
    /// <param name="namespaceURI" type="String">the namespace URI</param>
    /// <param name="qualifiedName" type="String">the QName</param>
    /// <param name="isAttribute" type="Boolean">true, if the requesting node is an Attr</param>
    /// <returns type="Boolean"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au), David Joham (djoham@yahoo.com) and Scott Severtson</remarks>



    if (this._performingImportNodeOperation == true)
    {
        //we're doing an importNode operation (or a cloneNode) - in both cases, there
        //is no need to perform any namespace checking since the nodes have to have been valid
        //to have gotten into the DOM in the first place
        return true;
    }

    var valid = true;
    // parse QName
    var qName = this.implementation._parseQName(qualifiedName);


    //only check for namespaces if we're finished parsing
    if (this._parseComplete == true)
    {

        // if the qualifiedName is malformed
        if (qName.localName.indexOf(":") > -1)
        {
            valid = false;
        }

        if ((valid) && (!isAttribute))
        {
            // if the namespaceURI is not null
            if (!namespaceURI)
            {
                valid = false;
            }
        }

        // if the qualifiedName has a prefix
        if ((valid) && (qName.prefix == ""))
        {
            valid = false;
        }

    }

    // if the qualifiedName has a prefix that is "xml" and the namespaceURI is
    //  different from "http://www.w3.org/XML/1998/namespace" [Namespaces].
    if ((valid) && (qName.prefix == "xml") && (namespaceURI != "http://www.w3.org/XML/1998/namespace"))
    {
        valid = false;
    }

    return valid;
}

/**
* @method DOMDocument.toString - Serialize the document into an XML string
*
* @author David Joham (djoham@yahoo.com)
*
* @return : string
*/
DOMDocument.prototype.toString = function DOMDocument_toString()
{
    /// <summary>DOMDocument.toString - Serialize the document into an XML string</summary>
    /// <returns type="String"></returns>
    /// <remarks>David Joham (djoham@yahoo.com)</remarks>


    return "" + this.childNodes;
} // end function getXML


/**
* @class  DOMElement - By far the vast majority of objects (apart from text) that authors encounter
*   when traversing a document are Element nodes.
*
* @extends DOMNode
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  ownerDocument : DOMDocument - The Document object associated with this node.
*/
DOMElement = function(ownerDocument)
{
    /// <summary>DOMElement - By far the vast majority of objects (apart from text) that authors encounter when traversing a document are Element nodes.</summary>
    /// <param name="ownerDocument" type="DOMDocument">The Document object associated with this node.</param>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>
    /// <extends>DOMNode</extends>


    this._class = addClass(this._class, "DOMElement");
    this.DOMNode = DOMNode;
    this.DOMNode(ownerDocument);

    this.tagName = "";                             // The name of the element.
    this.id = "";                                  // the ID of the element

    this.nodeType = DOMNode.ELEMENT_NODE;
};
DOMElement.prototype = new DOMNode;

/**
* @method DOMElement.getTagName - Java style gettor for .TagName
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : string
*/
DOMElement.prototype.getTagName = function DOMElement_getTagName()
{
    /// <summary>DOMElement.getTagName - Java style gettor for .TagName</summary>
    /// <returns type="String"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    return this.tagName;
};

/**
* @method DOMElement.getAttribute - Retrieves an attribute value by name
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  name : string - The name of the attribute to retrieve
*
* @return : string - The Attr value as a string, or the empty string if that attribute does not have a specified value.
*/
DOMElement.prototype.getAttribute = function DOMElement_getAttribute(name)
{
    /// <summary>DOMElement.getAttribute - Retrieves an attribute value by name</summary>
    /// <param name="name" type="String">The name of the attribute to retrieve</param>
    /// <returns type="String">The Attr value as a string, or the empty string if that attribute does not have a specified value.</returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    var ret = "";

    // if attribute exists, use it
    var attr = this.attributes.getNamedItem(name);

    if (attr)
    {
        ret = attr.value;
    }

    return ret; // if Attribute exists, return its value, otherwise, return ""
};

/**
* @method DOMElement.setAttribute - Retrieves an attribute value by name
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  name  : string - The name of the attribute to create or alter
* @param  value : string - Value to set in string form
*
* @throws : DOMException - INVALID_CHARACTER_ERR: Raised if the string contains an illegal character
* @throws : DOMException - NO_MODIFICATION_ALLOWED_ERR: Raised if the Attribute is readonly.
*/
DOMElement.prototype.setAttribute = function DOMElement_setAttribute(name, value)
{
    /// <summary>DOMElement.setAttribute - Retrieves an attribute value by name</summary>
    /// <param name="name" type="String">The name of the attribute to create or alter</param>
    /// <param name="value" type="String">Value to set in string form</param>
    /// <exception cref="DOMException">INVALID_CHARACTER_ERR: Raised if the string contains an illegal character</exception>
    /// <exception cref="DOMException">NO_MODIFICATION_ALLOWED_ERR: Raised if the Attribute is readonly.</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // if attribute exists, use it
    var attr = this.attributes.getNamedItem(name);

    if (!attr)
    {
        attr = this.ownerDocument.createAttribute(name);  // otherwise create it
    }

    var value = new String(value);

    // test for exceptions
    if (this.ownerDocument.implementation.errorChecking)
    {
        // throw Exception if Attribute is readonly
        if (attr._readonly)
        {
            throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
        }

        // throw Exception if the value string contains an illegal character
        if (!this.ownerDocument.implementation._isValidString(value))
        {
            throw (new DOMException(DOMException.INVALID_CHARACTER_ERR));
        }
    }

    if (this.ownerDocument.implementation._isIdDeclaration(name))
    {
        this.id = value;  // cache ID for getElementById()
    }

    // assign values to properties (and aliases)
    attr.value = value;
    attr.nodeValue = value;

    // update .specified
    if (value.length > 0)
    {
        attr.specified = true;
    }
    else
    {
        attr.specified = false;
    }

    // add/replace Attribute in NamedNodeMap
    this.attributes.setNamedItem(attr);
};

/**
* @method DOMElement.removeAttribute - Removes an attribute by name
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  name  : string - The name of the attribute to remove
*
* @throws : DOMException - NO_MODIFICATION_ALLOWED_ERR: Raised if the Attrbute is readonly.
*/
DOMElement.prototype.removeAttribute = function DOMElement_removeAttribute(name)
{
    /// <summary>DOMElement.removeAttribute - Removes an attribute by name</summary>
    /// <param name="name" type="String">The name of the attribute to remove</param>
    /// <exception cref="DOMException">NO_MODIFICATION_ALLOWED_ERR: Raised if the Attrbute is readonly.</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // delegate to DOMNamedNodeMap.removeNamedItem
    return this.attributes.removeNamedItem(name);
};

/**
* @method DOMElement.getAttributeNode - Retrieves an Attr node by name
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  name  : string - The name of the attribute to remove
*
* @return : DOMAttr - The Attr node with the specified attribute name or null if there is no such attribute.
*/
DOMElement.prototype.getAttributeNode = function DOMElement_getAttributeNode(name)
{
    /// <summary>DOMElement.getAttributeNode - Retrieves an Attr node by name</summary>
    /// <param name="name" type="String">The name of the attribute to remove</param>
    /// <returns type="DOMAttr">The Attr node with the specified attribute name or null if there is no such attribute.</returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // delegate to DOMNamedNodeMap.getNamedItem
    return this.attributes.getNamedItem(name);
};

/**
* @method DOMElement.setAttributeNode - Adds a new attribute
*   If an attribute with that name is already present in the element, it is replaced by the new one
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  newAttr : DOMAttr - The attribute node to be attached
*
* @throws : DOMException - WRONG_DOCUMENT_ERR: Raised if arg was created from a different document than the one that created this map.
* @throws : DOMException - NO_MODIFICATION_ALLOWED_ERR: Raised if this Element is readonly.
* @throws : DOMException - INUSE_ATTRIBUTE_ERR: Raised if arg is an Attr that is already an attribute of another Element object.
*
* @return : DOMAttr - If the newAttr attribute replaces an existing attribute with the same name,
*   the previously existing Attr node is returned, otherwise null is returned.
*/
DOMElement.prototype.setAttributeNode = function DOMElement_setAttributeNode(newAttr)
{
    /// <summary>DOMElement.setAttributeNode - Adds a new attribute If an attribute with that name is already present in the element, it is replaced by the new one</summary>
    /// <param name="newAttr" type="DOMAttr">The attribute node to be attached</param>
    /// <returns type="DOMAttr">If the newAttr attribute replaces an existing attribute with the same name, the previously existing Attr node is returned, otherwise null is returned.</returns>
    /// <exception cref="DOMException">WRONG_DOCUMENT_ERR: Raised if arg was created from a different document than the one that created this map.</exception>
    /// <exception cref="DOMException">NO_MODIFICATION_ALLOWED_ERR: Raised if this Element is readonly.</exception>
    /// <exception cref="DOMException">INUSE_ATTRIBUTE_ERR: Raised if arg is an Attr that is already an attribute of another Element object.</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // if this Attribute is an ID
    if (this.ownerDocument.implementation._isIdDeclaration(newAttr.name))
    {
        this.id = newAttr.value;  // cache ID for getElementById()
    }

    // delegate to DOMNamedNodeMap.setNamedItem
    return this.attributes.setNamedItem(newAttr);
};

/**
* @method DOMElement.removeAttributeNode - Removes the specified attribute
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  oldAttr  : DOMAttr - The Attr node to remove from the attribute list
*
* @throws : DOMException - NO_MODIFICATION_ALLOWED_ERR: Raised if this Element is readonly.
* @throws : DOMException - INUSE_ATTRIBUTE_ERR: Raised if arg is an Attr that is already an attribute of another Element object.
*
* @return : DOMAttr - The Attr node that was removed.
*/
DOMElement.prototype.removeAttributeNode = function DOMElement_removeAttributeNode(oldAttr)
{
    /// <summary>DOMElement.removeAttributeNode - Removes the specified attribute</summary>
    /// <param name="oldAttr" type="DOMAttr">The Attr node to remove from the attribute list</param>
    /// <returns type="DOMAttr">The Attr node that was removed.</returns>
    /// <exception cref="DOMException">NO_MODIFICATION_ALLOWED_ERR: Raised if this Element is readonly.</exception>
    /// <exception cref="DOMException">INUSE_ATTRIBUTE_ERR: Raised if arg is an Attr that is already an attribute of another Element object.</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // throw Exception if Attribute is readonly
    if (this.ownerDocument.implementation.errorChecking && oldAttr._readonly)
    {
        throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
    }

    // get item index
    var itemIndex = this.attributes._findItemIndex(oldAttr._id);

    // throw Exception if node does not exist in this map
    if (this.ownerDocument.implementation.errorChecking && (itemIndex < 0))
    {
        throw (new DOMException(DOMException.NOT_FOUND_ERR));
    }

    return this.attributes._removeChild(itemIndex);
};

/**
* @method DOMElement.getAttributeNS - Retrieves an attribute value by namespaceURI and localName
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  namespaceURI : string - the namespace URI of the required node
* @param  localName    : string - the local name of the required node
*
* @return : string - The Attr value as a string, or the empty string if that attribute does not have a specified value.
*/
DOMElement.prototype.getAttributeNS = function DOMElement_getAttributeNS(namespaceURI, localName)
{
    /// <summary>DOMElement.getAttributeNS - Retrieves an attribute value by namespaceURI and localName</summary>
    /// <param name="namespaceURI" type="String">the namespace URI of the required node</param>
    /// <param name="localName" type="String">the local name of the required node</param>
    /// <returns type="String">The Attr value as a string, or the empty string if that attribute does not have a specified value.</returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    var ret = "";

    // delegate to DOMNAmedNodeMap.getNamedItemNS
    var attr = this.attributes.getNamedItemNS(namespaceURI, localName);


    if (attr)
    {
        ret = attr.value;
    }

    return ret;  // if Attribute exists, return its value, otherwise return ""
};

/**
* @method DOMElement.setAttributeNS - Sets an attribute value by namespaceURI and localName
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  namespaceURI : string - the namespace URI of the required node
* @param  qualifiedName : string - the qualified name of the required node
* @param  value        : string - Value to set in string form
*
* @throws : DOMException - INVALID_CHARACTER_ERR: Raised if the string contains an illegal character
* @throws : DOMException - NO_MODIFICATION_ALLOWED_ERR: Raised if the Attrbute is readonly.
* @throws : DOMException - NAMESPACE_ERR: Raised if the Namespace is invalid
*/
DOMElement.prototype.setAttributeNS = function DOMElement_setAttributeNS(namespaceURI, qualifiedName, value)
{
    /// <summary>DOMElement.setAttributeNS - Sets an attribute value by namespaceURI and localName</summary>
    /// <param name="namespaceURI" type="String">the namespace URI of the required node</param>
    /// <param name="qualifiedName" type="String">the qualified name of the required node</param>
    /// <param name="value" type="String">Value to set in string form</param>
    /// <exception cref="DOMException">INVALID_CHARACTER_ERR: Raised if the string contains an illegal character</exception>
    /// <exception cref="DOMException">NO_MODIFICATION_ALLOWED_ERR: Raised if the Attrbute is readonly.</exception>
    /// <exception cref="DOMException">NAMESPACE_ERR: Raised if the Namespace is invalid</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // call DOMNamedNodeMap.getNamedItem
    var attr = this.attributes.getNamedItem(namespaceURI, qualifiedName);

    if (!attr)
    {  // if Attribute exists, use it
        // otherwise create it
        attr = this.ownerDocument.createAttributeNS(namespaceURI, qualifiedName);
    }

    var value = new String(value);

    // test for exceptions
    if (this.ownerDocument.implementation.errorChecking)
    {
        // throw Exception if Attribute is readonly
        if (attr._readonly)
        {
            throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
        }

        // throw Exception if the Namespace is invalid
        if (!this.ownerDocument._isValidNamespace(namespaceURI, qualifiedName))
        {
            throw (new DOMException(DOMException.NAMESPACE_ERR));
        }

        // throw Exception if the value string contains an illegal character
        if (!this.ownerDocument.implementation._isValidString(value))
        {
            throw (new DOMException(DOMException.INVALID_CHARACTER_ERR));
        }
    }

    // if this Attribute is an ID
    if (this.ownerDocument.implementation._isIdDeclaration(name))
    {
        this.id = value;  // cache ID for getElementById()
    }

    // assign values to properties (and aliases)
    attr.value = value;
    attr.nodeValue = value;

    // update .specified
    if (value.length > 0)
    {
        attr.specified = true;
    }
    else
    {
        attr.specified = false;
    }

    // delegate to DOMNamedNodeMap.setNamedItem
    this.attributes.setNamedItemNS(attr);
};

/**
* @method DOMElement.removeAttributeNS - Removes an attribute by namespaceURI and localName
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  namespaceURI : string - the namespace URI of the required node
* @param  localName    : string - the local name of the required node
*
* @throws : DOMException - NO_MODIFICATION_ALLOWED_ERR: Raised if the Attrbute is readonly.
*
* @return : DOMAttr
*/
DOMElement.prototype.removeAttributeNS = function DOMElement_removeAttributeNS(namespaceURI, localName)
{
    /// <summary>DOMElement.removeAttributeNS - Removes an attribute by namespaceURI and localName</summary>
    /// <param name="namespaceURI" type="String">the namespace URI of the required node</param>
    /// <param name="localName" type="String">the local name of the required node</param>
    /// <returns type="DOMAttr"></returns>
    /// <exception cref="DOMException">NO_MODIFICATION_ALLOWED_ERR: Raised if the Attrbute is readonly.</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // delegate to DOMNamedNodeMap.removeNamedItemNS
    return this.attributes.removeNamedItemNS(namespaceURI, localName);
};

/**
* @method DOMElement.getAttributeNodeNS - Retrieves an Attr node by namespaceURI and localName
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  namespaceURI : string - the namespace URI of the required node
* @param  localName    : string - the local name of the required node
*
* @return : DOMAttr - The Attr node with the specified attribute name or null if there is no such attribute.
*/
DOMElement.prototype.getAttributeNodeNS = function DOMElement_getAttributeNodeNS(namespaceURI, localName)
{
    /// <summary>DOMElement.getAttributeNodeNS - Retrieves an Attr node by namespaceURI and localName</summary>
    /// <param name="namespaceURI" type="String">the namespace URI of the required node</param>
    /// <param name="localName" type="String">the local name of the required node</param>
    /// <returns type="DOMAttr">The Attr node with the specified attribute name or null if there is no such attribute.</returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // delegate to DOMNamedNodeMap.getNamedItemNS
    return this.attributes.getNamedItemNS(namespaceURI, localName);
};

/**
* @method DOMElement.setAttributeNodeNS - Adds a new attribute
*   If an attribute with that name is already present in the element, it is replaced by the new one
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  newAttr      : DOMAttr - the attribute node to be attached
*
* @throws : DOMException - NO_MODIFICATION_ALLOWED_ERR: Raised if the Attrbute is readonly.
* @throws : DOMException - WRONG_DOCUMENT_ERR: Raised if arg was created from a different document than the one that created this map.
* @throws : DOMException - INUSE_ATTRIBUTE_ERR: Raised if arg is an Attr that is already an attribute of another Element object.
*  The DOM user must explicitly clone Attr nodes to re-use them in other elements.
*
* @return : DOMAttr - If the newAttr attribute replaces an existing attribute with the same name,
*   the previously existing Attr node is returned, otherwise null is returned.
*/
DOMElement.prototype.setAttributeNodeNS = function DOMElement_setAttributeNodeNS(newAttr)
{
    /// <summary>DOMElement.setAttributeNodeNS - Adds a new attribute If an attribute with that name is already present in the element, it is replaced by the new one</summary>
    /// <param name="newAttr" type="DOMAttr">the attribute node to be attached</param>
    /// <returns type="DOMAttr">If the newAttr attribute replaces an existing attribute with the same name, the previously existing Attr node is returned, otherwise null is returned.</returns>
    /// <exception cref="DOMException">NO_MODIFICATION_ALLOWED_ERR: Raised if the Attrbute is readonly.</exception>
    /// <exception cref="DOMException">WRONG_DOCUMENT_ERR: Raised if arg was created from a different document than the one that created this map.</exception>
    /// <exception cref="DOMException">INUSE_ATTRIBUTE_ERR: Raised if arg is an Attr that is already an attribute of another Element object. The DOM user must explicitly clone Attr nodes to re-use them in other elements.</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // if this Attribute is an ID
    if ((newAttr.prefix == "") && this.ownerDocument.implementation._isIdDeclaration(newAttr.name))
    {
        this.id = newAttr.value;  // cache ID for getElementById()
    }

    // delegate to DOMNamedNodeMap.setNamedItemNS
    return this.attributes.setNamedItemNS(newAttr);
};

/**
* @method DOMElement.hasAttribute - Returns true if specified node exists
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  name : string - the name of the required node
*
* @return : boolean
*/
DOMElement.prototype.hasAttribute = function DOMElement_hasAttribute(name)
{
    /// <summary>DOMElement.hasAttribute - Returns true if specified node exists</summary>
    /// <param name="name" type="String">the name of the required node</param>
    /// <returns type="Boolean"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // delegate to DOMNamedNodeMap._hasAttribute
    return this.attributes._hasAttribute(name);
}

/**
* @method DOMElement.hasAttributeNS - Returns true if specified node exists
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  namespaceURI : string - the namespace URI of the required node
* @param  localName    : string - the local name of the required node
*
* @return : boolean
*/
DOMElement.prototype.hasAttributeNS = function DOMElement_hasAttributeNS(namespaceURI, localName)
{
    /// <summary>DOMElement.hasAttributeNS - Returns true if specified node exists</summary>
    /// <param name="namespaceURI" type="String">the namespace URI of the required node</param>
    /// <param name="localName" type="String">the local name of the required node</param>
    /// <returns type="Boolean"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // delegate to DOMNamedNodeMap._hasAttributeNS
    return this.attributes._hasAttributeNS(namespaceURI, localName);
}

/**
* @method DOMElement.toString - Serialize this Element and its children into an XML string
*
* @author Jon van Noort (jon@webarcana.com.au) and David Joham (djoham@yahoo.com)
*
* @return : string
*/
DOMElement.prototype.toString = function DOMElement_toString()
{
    /// <summary>DOMElement.toString - Serialize this Element and its children into an XML string</summary>
    /// <returns type="String"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au) and David Joham (djoham@yahoo.com)</remarks>


    var ret = "";

    // serialize namespace declarations
    var ns = this._namespaces.toString();
    if (ns.length > 0) ns = " " + ns;

    // serialize Attribute declarations
    var attrs = this.attributes.toString();
    if (attrs.length > 0) attrs = " " + attrs;

    // serialize this Element
    ret += "<" + this.nodeName + ns + attrs + ">";
    ret += this.childNodes.toString(); ;
    ret += "</" + this.nodeName + ">";

    return ret;
}

/**
* @class  DOMAttr - The Attr interface represents an attribute in an Element object
*
* @extends DOMNode
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  ownerDocument : DOMDocument - The Document object associated with this node.
*/
DOMAttr = function(ownerDocument)
{
    /// <summary>DOMAttr - The Attr interface represents an attribute in an Element object</summary>
    /// <param name="ownerDocument" type="DOMDocument">The Document object associated with this node.</param>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>
    /// <extends>DOMNode</extends>


    this._class = addClass(this._class, "DOMAttr");
    this.DOMNode = DOMNode;
    this.DOMNode(ownerDocument);

    this.name = "";                           // the name of this attribute

    // If this attribute was explicitly given a value in the original document, this is true; otherwise, it is false.
    // Note that the implementation is in charge of this attribute, not the user.
    // If the user changes the value of the attribute (even if it ends up having the same value as the default value)
    // then the specified flag is automatically flipped to true
    // (I wish! You will need to use setValue to 'automatically' update specified)
    this.specified = false;

    this.value = "";                           // the value of the attribute is returned as a string

    this.nodeType = DOMNode.ATTRIBUTE_NODE;

    this.ownerElement = null;                      // set when Attr is added to NamedNodeMap

    // disable childNodes
    this.childNodes = null;
    this.attributes = null;
};
DOMAttr.prototype = new DOMNode;

/**
* @method DOMAttr.getName - Java style gettor for .name
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : string
*/
DOMAttr.prototype.getName = function DOMAttr_getName()
{
    /// <summary>DOMAttr.getName - Java style gettor for .name</summary>
    /// <returns type="String"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    return this.nodeName;
};

/**
* @method DOMAttr.getSpecified - Java style gettor for .specified
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : boolean
*/
DOMAttr.prototype.getSpecified = function DOMAttr_getSpecified()
{
    /// <summary>DOMAttr.getSpecified - Java style gettor for .specified</summary>
    /// <returns type="Boolean"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    return this.specified;
};

/**
* @method DOMAttr.getValue - Java style gettor for .value
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : string
*/
DOMAttr.prototype.getValue = function DOMAttr_getValue()
{
    /// <summary>DOMAttr.getValue - Java style gettor for .value</summary>
    /// <returns type="String"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    return this.nodeValue;
};

/**
* @method DOMAttr.setValue - Java style settor for .value
*   alias for DOMAttr.setNodeValue
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  value : string - the new attribute value
*
* @throws : DOMException - NO_MODIFICATION_ALLOWED_ERR: Raised if this Attribute is readonly.
*/
DOMAttr.prototype.setValue = function DOMAttr_setValue(value)
{
    /// <summary>DOMAttr.setValue - Java style settor for .value alias for DOMAttr.setNodeValue</summary>
    /// <param name="value" type="String">the new attribute value</param>
    /// <exception cref="DOMException">NO_MODIFICATION_ALLOWED_ERR: Raised if this Attribute is readonly.</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // throw Exception if Attribute is readonly
    if (this.ownerDocument.implementation.errorChecking && this._readonly)
    {
        throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
    }

    // delegate to setNodeValue
    this.setNodeValue(value);
};

/**
* @method DOMAttr.setNodeValue - Java style settor for .nodeValue
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  value : string - the new attribute value
*/
DOMAttr.prototype.setNodeValue = function DOMAttr_setNodeValue(value)
{
    /// <summary>DOMAttr.setNodeValue - Java style settor for .nodeValue</summary>
    /// <param name="value" type="String">the new attribute value</param>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    this.nodeValue = new String(value);
    this.value = this.nodeValue;
    this.specified = (this.value.length > 0);
};

/**
* @method DOMAttr.toString - Serialize this Attr into an XML string
*
* @author Jon van Noort (jon@webarcana.com.au) and David Joham (djoham@yahoo.com)
*
* @return : string
*/
DOMAttr.prototype.toString = function DOMAttr_toString()
{
    /// <summary>DOMAttr.toString - Serialize this Attr into an XML string</summary>
    /// <returns type="String"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au) and David Joham (djoham@yahoo.com)</remarks>


    var ret = "";

    // serialize Attribute
    ret += this.nodeName + "=\"" + this.__escapeString(this.nodeValue) + "\"";

    return ret;
}

DOMAttr.prototype.getOwnerElement = function()
{

    return this.ownerElement;

}

/**
* @class  DOMNamespace - The Namespace interface represents an namespace in an Element object
*
* @extends DOMNode
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  ownerDocument : DOMDocument - The Document object associated with this node.
*/
DOMNamespace = function(ownerDocument)
{
    /// <summary>DOMNamespace - The Namespace interface represents an namespace in an Element object</summary>
    /// <param name="ownerDocument" type="DOMDocument">The Document object associated with this node.</param>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>
    /// <extends>DOMNode</extends>


    this._class = addClass(this._class, "DOMNamespace");
    this.DOMNode = DOMNode;
    this.DOMNode(ownerDocument);

    this.name = "";                           // the name of this attribute

    // If this attribute was explicitly given a value in the original document, this is true; otherwise, it is false.
    // Note that the implementation is in charge of this attribute, not the user.
    // If the user changes the value of the attribute (even if it ends up having the same value as the default value)
    // then the specified flag is automatically flipped to true
    // (I wish! You will need to use _setValue to 'automatically' update specified)
    this.specified = false;

    this.value = "";                           // the value of the attribute is returned as a string

    this.nodeType = DOMNode.NAMESPACE_NODE;
};
DOMNamespace.prototype = new DOMNode;

/**
* @method DOMNamespace.getValue - Java style gettor for .value
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : string
*/
DOMNamespace.prototype.getValue = function DOMNamespace_getValue()
{
    /// <summary>DOMNamespace.getValue - Java style gettor for .value</summary>
    /// <returns type="String"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    return this.nodeValue;
};

/**
* @method DOMNamespace.setValue - utility function to set value (rather than direct assignment to .value)
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  value : string - the new namespace value
*/
DOMNamespace.prototype.setValue = function DOMNamespace_setValue(value)
{
    /// <summary>DOMNamespace.setValue - utility function to set value (rather than direct assignment to .value)</summary>
    /// <param name="value" type="String">the new namespace value</param>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // assign values to properties (and aliases)
    this.nodeValue = new String(value);
    this.value = this.nodeValue;
};

/**
* @method DOMNamespace.toString - Serialize this Attr into an XML string
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : string
*/
DOMNamespace.prototype.toString = function DOMNamespace_toString()
{
    /// <summary>DOMNamespace.toString - Serialize this Attr into an XML string</summary>
    /// <returns type="String"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    var ret = "";

    // serialize Namespace Declaration
    if (this.nodeName != "")
    {
        ret += this.nodeName + "=\"" + this.__escapeString(this.nodeValue) + "\"";
    }
    else
    {  // handle default namespace
        ret += "xmlns=\"" + this.__escapeString(this.nodeValue) + "\"";
    }

    return ret;
}

/**
* @class  DOMCharacterData - parent abstract class for DOMText and DOMComment
*
* @extends DOMNode
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  ownerDocument : DOMDocument - The Document object associated with this node.
*/
DOMCharacterData = function(ownerDocument)
{
    /// <summary>DOMCharacterData - parent abstract class for DOMText and DOMComment</summary>
    /// <param name="ownerDocument" type="DOMDocument">The Document object associated with this node.</param>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>
    /// <extends>DOMNode</extends>


    this._class = addClass(this._class, "DOMCharacterData");
    this.DOMNode = DOMNode;
    this.DOMNode(ownerDocument);

    this.data = "";
    this.length = 0;
};
DOMCharacterData.prototype = new DOMNode;

/**
* @method DOMCharacterData.getData - Java style gettor for .data
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : string
*/
DOMCharacterData.prototype.getData = function DOMCharacterData_getData()
{
    /// <summary>DOMCharacterData.getData - Java style gettor for .data</summary>
    /// <returns type="String"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    return this.nodeValue;
};

/**
* @method DOMCharacterData.setData - Java style settor for .data
*  alias for DOMCharacterData.setNodeValue
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  data : string - the character data
*
* @throws : DOMException - NO_MODIFICATION_ALLOWED_ERR: Raised if this Attribute is readonly.
*/
DOMCharacterData.prototype.setData = function DOMCharacterData_setData(data)
{
    /// <summary>DOMCharacterData.setData - Java style settor for .data alias for DOMCharacterData.setNodeValue</summary>
    /// <param name="data" type="String">the character data</param>
    /// <exception cref="DOMException">NO_MODIFICATION_ALLOWED_ERR: Raised if this Attribute is readonly.</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // delegate to setNodeValue
    this.setNodeValue(data);
};

/**
* @method DOMCharacterData.setNodeValue - Java style settor for .nodeValue
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  data : string - the node value
*
* @throws : DOMException - NO_MODIFICATION_ALLOWED_ERR: Raised if this Attribute is readonly.
*/
DOMCharacterData.prototype.setNodeValue = function DOMCharacterData_setNodeValue(data)
{
    /// <summary>DOMCharacterData.setNodeValue - Java style settor for .nodeValue</summary>
    /// <param name="data" type="String">the node value</param>
    /// <exception cref="DOMException">NO_MODIFICATION_ALLOWED_ERR: Raised if this Attribute is readonly.</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // throw Exception if Attribute is readonly
    if (this.ownerDocument.implementation.errorChecking && this._readonly)
    {
        throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
    }

    // assign values to properties (and aliases)
    this.nodeValue = new String(data);
    this.data = this.nodeValue;

    // update length
    this.length = this.nodeValue.length;
};

/**
* @method DOMCharacterData.getLength - Java style gettor for .length
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : string
*/
DOMCharacterData.prototype.getLength = function DOMCharacterData_getLength()
{
    /// <summary>DOMCharacterData.getLength - Java style gettor for .length</summary>
    /// <returns type="String"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    return this.nodeValue.length;
};

/**
* @method DOMCharacterData.substringData - Extracts a range of data from the node
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  offset : int - Start offset of substring to extract
* @param  count  : int - The number of characters to extract
*
* @throws : DOMException - INDEX_SIZE_ERR: Raised if specified offset is negative or greater than the number of 16-bit units in data,
*
* @return : string - The specified substring.
*   If the sum of offset and count exceeds the length, then all characters to the end of the data are returned.
*/
DOMCharacterData.prototype.substringData = function DOMCharacterData_substringData(offset, count)
{
    /// <summary>DOMCharacterData.substringData - Extracts a range of data from the node</summary>
    /// <param name="offset" type="int">Start offset of substring to extract</param>
    /// <param name="count" type="int">The number of characters to extract</param>
    /// <returns type="String">The specified substring. If the sum of offset and count exceeds the length, then all characters to the end of the data are returned.</returns>
    /// <exception cref="DOMException">INDEX_SIZE_ERR: Raised if specified offset is negative or greater than the number of 16-bit units in data,</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    var ret = null;

    if (this.data)
    {
        // throw Exception if offset is negative or greater than the data length,
        // or the count is negative
        if (this.ownerDocument.implementation.errorChecking && ((offset < 0) || (offset > this.data.length) || (count < 0)))
        {
            throw (new DOMException(DOMException.INDEX_SIZE_ERR));
        }

        // if count is not specified
        if (!count)
        {
            ret = this.data.substring(offset); // default to 'end of string'
        }
        else
        {
            ret = this.data.substring(offset, offset + count);
        }
    }

    return ret;
};

/**
* @method DOMCharacterData.appendData - Append the string to the end of the character data of the node.
*   Upon success, data provides access to the concatenation of data and the DOMString specified.
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  arg : string - The string to append
*
* @throws : DOMException - NO_MODIFICATION_ALLOWED_ERR: Raised if this CharacterData is readonly.
*/
DOMCharacterData.prototype.appendData = function DOMCharacterData_appendData(arg)
{
    /// <summary>DOMCharacterData.appendData - Append the string to the end of the character data of the node. Upon success, data provides access to the concatenation of data and the DOMString specified.</summary>
    /// <param name="arg" type="String">The string to append</param>
    /// <exception cref="DOMException">NO_MODIFICATION_ALLOWED_ERR: Raised if this CharacterData is readonly.</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // throw Exception if DOMCharacterData is readonly
    if (this.ownerDocument.implementation.errorChecking && this._readonly)
    {
        throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
    }

    // append data
    this.setData("" + this.data + arg);
};

/**
* @method DOMCharacterData.insertData - Insert a string at the specified character offset.
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  offset : int    - The character offset at which to insert
* @param  arg    : string - The string to insert
*
* @throws : DOMException - INDEX_SIZE_ERR: Raised if specified offset is negative or greater than the number of 16-bit units in data,
*   or if the specified count is negative.
* @throws : DOMException - NO_MODIFICATION_ALLOWED_ERR: Raised if this CharacterData is readonly.
*/
DOMCharacterData.prototype.insertData = function DOMCharacterData_insertData(offset, arg)
{
    /// <summary>DOMCharacterData.insertData - Insert a string at the specified character offset.</summary>
    /// <param name="offset" type="int">The character offset at which to insert</param>
    /// <param name="arg" type="String">The string to insert</param>
    /// <exception cref="DOMException">INDEX_SIZE_ERR: Raised if specified offset is negative or greater than the number of 16-bit units in data, or if the specified count is negative.</exception>
    /// <exception cref="DOMException">NO_MODIFICATION_ALLOWED_ERR: Raised if this CharacterData is readonly.</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // throw Exception if DOMCharacterData is readonly
    if (this.ownerDocument.implementation.errorChecking && this._readonly)
    {
        throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
    }

    if (this.data)
    {
        // throw Exception if offset is negative or greater than the data length,
        if (this.ownerDocument.implementation.errorChecking && ((offset < 0) || (offset > this.data.length)))
        {
            throw (new DOMException(DOMException.INDEX_SIZE_ERR));
        }

        // insert data
        this.setData(this.data.substring(0, offset).concat(arg, this.data.substring(offset)));
    }
    else
    {
        // throw Exception if offset is negative or greater than the data length,
        if (this.ownerDocument.implementation.errorChecking && (offset != 0))
        {
            throw (new DOMException(DOMException.INDEX_SIZE_ERR));
        }

        // set data
        this.setData(arg);
    }
};

/**
* @method DOMCharacterData.deleteData - Remove a range of characters from the node.
*   Upon success, data and length reflect the change
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  offset : int - The offset from which to remove characters
* @param  count  : int - The number of characters to delete.
*   If the sum of offset and count exceeds length then all characters from offset to the end of the data are deleted
*
* @throws : DOMException - INDEX_SIZE_ERR: Raised if specified offset is negative or greater than the number of 16-bit units in data,
*   or if the specified count is negative.
* @throws : DOMException - NO_MODIFICATION_ALLOWED_ERR: Raised if this CharacterData is readonly.
*/
DOMCharacterData.prototype.deleteData = function DOMCharacterData_deleteData(offset, count)
{
    /// <summary>DOMCharacterData.deleteData - Remove a range of characters from the node. Upon success, data and length reflect the change</summary>
    /// <param name="offset" type="int">The offset from which to remove characters</param>
    /// <param name="count" type="int">The number of characters to delete. If the sum of offset and count exceeds length then all characters from offset to the end of the data are deleted</param>
    /// <exception cref="DOMException">INDEX_SIZE_ERR: Raised if specified offset is negative or greater than the number of 16-bit units in data, or if the specified count is negative.</exception>
    /// <exception cref="DOMException">NO_MODIFICATION_ALLOWED_ERR: Raised if this CharacterData is readonly.</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // throw Exception if DOMCharacterData is readonly
    if (this.ownerDocument.implementation.errorChecking && this._readonly)
    {
        throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
    }

    if (this.data)
    {
        // throw Exception if offset is negative or greater than the data length,
        if (this.ownerDocument.implementation.errorChecking && ((offset < 0) || (offset > this.data.length) || (count < 0)))
        {
            throw (new DOMException(DOMException.INDEX_SIZE_ERR));
        }

        // delete data
        if (!count || (offset + count) > this.data.length)
        {
            this.setData(this.data.substring(0, offset));
        }
        else
        {
            this.setData(this.data.substring(0, offset).concat(this.data.substring(offset + count)));
        }
    }
};

/**
* @method DOMCharacterData.replaceData - Replace the characters starting at the specified character offset with the specified string
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  offset : int    - The offset from which to start replacing
* @param  count  : int    - The number of characters to replace.
*   If the sum of offset and count exceeds length, then all characters to the end of the data are replaced
* @param  arg    : string - The string with which the range must be replaced
*
* @throws : DOMException - INDEX_SIZE_ERR: Raised if specified offset is negative or greater than the number of 16-bit units in data,
*   or if the specified count is negative.
* @throws : DOMException - NO_MODIFICATION_ALLOWED_ERR: Raised if this CharacterData is readonly.
*/
DOMCharacterData.prototype.replaceData = function DOMCharacterData_replaceData(offset, count, arg)
{
    /// <summary>DOMCharacterData.replaceData - Replace the characters starting at the specified character offset with the specified string</summary>
    /// <param name="offset" type="int">The offset from which to start replacing</param>
    /// <param name="count" type="int">The number of characters to replace. If the sum of offset and count exceeds length, then all characters to the end of the data are replaced</param>
    /// <param name="arg" type="String">The string with which the range must be replaced</param>
    /// <exception cref="DOMException">INDEX_SIZE_ERR: Raised if specified offset is negative or greater than the number of 16-bit units in data, or if the specified count is negative.</exception>
    /// <exception cref="DOMException">NO_MODIFICATION_ALLOWED_ERR: Raised if this CharacterData is readonly.</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // throw Exception if DOMCharacterData is readonly
    if (this.ownerDocument.implementation.errorChecking && this._readonly)
    {
        throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
    }

    if (this.data)
    {
        // throw Exception if offset is negative or greater than the data length,
        if (this.ownerDocument.implementation.errorChecking && ((offset < 0) || (offset > this.data.length) || (count < 0)))
        {
            throw (new DOMException(DOMException.INDEX_SIZE_ERR));
        }

        // replace data
        this.setData(this.data.substring(0, offset).concat(arg, this.data.substring(offset + count)));
    }
    else
    {
        // set data
        this.setData(arg);
    }
};

/**
* @class  DOMText - The Text interface represents the textual content (termed character data in XML) of an Element or Attr.
*   If there is no markup inside an element's content, the text is contained in a single object implementing the Text interface
*   that is the only child of the element. If there is markup, it is parsed into a list of elements and Text nodes that form the
*   list of children of the element.
*
* @extends DOMCharacterData
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  ownerDocument : DOMDocument - The Document object associated with this node.
*/
DOMText = function(ownerDocument)
{
    /// <summary>DOMText - The Text interface represents the textual content (termed character data in XML) of an Element or Attr. If there is no markup inside an element's content, the text is contained in a single object implementing the Text interface that is the only child of the element. If there is markup, it is parsed into a list of elements and Text nodes that form the list of children of the element.</summary>
    /// <param name="ownerDocument" type="DOMDocument">The Document object associated with this node.</param>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>
    /// <extends>DOMCharacterData</extends>


    this._class = addClass(this._class, "DOMText");
    this.DOMCharacterData = DOMCharacterData;
    this.DOMCharacterData(ownerDocument);

    this.nodeName = "#text";
    this.nodeType = DOMNode.TEXT_NODE;
};
DOMText.prototype = new DOMCharacterData;

/**
* @method DOMText.splitText - Breaks this Text node into two Text nodes at the specified offset,
*   keeping both in the tree as siblings. This node then only contains all the content up to the offset point.
*   And a new Text node, which is inserted as the next sibling of this node, contains all the content at and after the offset point.
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  offset : int - The offset at which to split, starting from 0.
*
* @throws : DOMException - INDEX_SIZE_ERR: Raised if specified offset is negative or greater than the number of 16-bit units in data,
* @throws : DOMException - NO_MODIFICATION_ALLOWED_ERR: Raised if this Text is readonly.
*
* @return : DOMText - The new Text node
*/
DOMText.prototype.splitText = function DOMText_splitText(offset)
{
    /// <summary>DOMText.splitText - Breaks this Text node into two Text nodes at the specified offset, keeping both in the tree as siblings. This node then only contains all the content up to the offset point. And a new Text node, which is inserted as the next sibling of this node, contains all the content at and after the offset point.</summary>
    /// <param name="offset" type="int">The offset at which to split, starting from 0.</param>
    /// <returns type="DOMText">The new Text node</returns>
    /// <exception cref="DOMException">INDEX_SIZE_ERR: Raised if specified offset is negative or greater than the number of 16-bit units in data,</exception>
    /// <exception cref="DOMException">NO_MODIFICATION_ALLOWED_ERR: Raised if this Text is readonly.</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    var data, inode;

    // test for exceptions
    if (this.ownerDocument.implementation.errorChecking)
    {
        // throw Exception if Node is readonly
        if (this._readonly)
        {
            throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
        }

        // throw Exception if offset is negative or greater than the data length,
        if ((offset < 0) || (offset > this.data.length))
        {
            throw (new DOMException(DOMException.INDEX_SIZE_ERR));
        }
    }

    if (this.parentNode)
    {
        // get remaining string (after offset)
        data = this.substringData(offset);

        // create new TextNode with remaining string
        inode = this.ownerDocument.createTextNode(data);

        // attach new TextNode
        if (this.nextSibling)
        {
            this.parentNode.insertBefore(inode, this.nextSibling);
        }
        else
        {
            this.parentNode.appendChild(inode);
        }

        // remove remaining string from original TextNode
        this.deleteData(offset);
    }

    return inode;
};

/**
* @method DOMText.toString - Serialize this Text into an XML string
*
* @author Jon van Noort (jon@webarcana.com.au) and David Joham (djoham@yahoo.com)
*
* @return : string
*/
DOMText.prototype.toString = function DOMText_toString()
{
    /// <summary>DOMText.toString - Serialize this Text into an XML string</summary>
    /// <returns type="String"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au) and David Joham (djoham@yahoo.com)</remarks>


    return this.__escapeString("" + this.nodeValue);
}

/**
* @class  DOMCDATASection - CDATA sections are used to escape blocks of text containing characters that would otherwise be regarded as markup.
*   The only delimiter that is recognized in a CDATA section is the "\]\]\>" string that ends the CDATA section
*
* @extends DOMCharacterData
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  ownerDocument : DOMDocument - The Document object associated with this node.
*/
DOMCDATASection = function(ownerDocument)
{
    /// <summary>DOMCDATASection - CDATA sections are used to escape blocks of text containing characters that would otherwise be regarded as markup. The only delimiter that is recognized in a CDATA section is the "\]\]\&gt;" string that ends the CDATA section</summary>
    /// <param name="ownerDocument" type="DOMDocument">The Document object associated with this node.</param>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>
    /// <extends>DOMCharacterData</extends>


    this._class = addClass(this._class, "DOMCDATASection");
    this.DOMCharacterData = DOMCharacterData;
    this.DOMCharacterData(ownerDocument);

    this.nodeName = "#cdata-section";
    this.nodeType = DOMNode.CDATA_SECTION_NODE;
};
DOMCDATASection.prototype = new DOMCharacterData;

/**
* @method DOMCDATASection.splitText - Breaks this CDATASection node into two CDATASection nodes at the specified offset,
*   keeping both in the tree as siblings. This node then only contains all the content up to the offset point.
*   And a new CDATASection node, which is inserted as the next sibling of this node, contains all the content at and after the offset point.
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  offset : int - The offset at which to split, starting from 0.
*
* @return : DOMCDATASection - The new CDATASection node
*/
DOMCDATASection.prototype.splitText = function DOMCDATASection_splitText(offset)
{
    /// <summary>DOMCDATASection.splitText - Breaks this CDATASection node into two CDATASection nodes at the specified offset, keeping both in the tree as siblings. This node then only contains all the content up to the offset point. And a new CDATASection node, which is inserted as the next sibling of this node, contains all the content at and after the offset point.</summary>
    /// <param name="offset" type="int">The offset at which to split, starting from 0.</param>
    /// <returns type="DOMCDATASection">The new CDATASection node</returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    var data, inode;

    // test for exceptions
    if (this.ownerDocument.implementation.errorChecking)
    {
        // throw Exception if Node is readonly
        if (this._readonly)
        {
            throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
        }

        // throw Exception if offset is negative or greater than the data length,
        if ((offset < 0) || (offset > this.data.length))
        {
            throw (new DOMException(DOMException.INDEX_SIZE_ERR));
        }
    }

    if (this.parentNode)
    {
        // get remaining string (after offset)
        data = this.substringData(offset);

        // create new CDATANode with remaining string
        inode = this.ownerDocument.createCDATASection(data);

        // attach new CDATANode
        if (this.nextSibling)
        {
            this.parentNode.insertBefore(inode, this.nextSibling);
        }
        else
        {
            this.parentNode.appendChild(inode);
        }

        // remove remaining string from original CDATANode
        this.deleteData(offset);
    }

    return inode;
};

/**
* @method DOMCDATASection.toString - Serialize this CDATASection into an XML string
*
* @author Jon van Noort (jon@webarcana.com.au) and David Joham (djoham@yahoo.com)
*
* @return : string
*/
DOMCDATASection.prototype.toString = function DOMCDATASection_toString()
{
    /// <summary>DOMCDATASection.toString - Serialize this CDATASection into an XML string</summary>
    /// <returns type="String"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au) and David Joham (djoham@yahoo.com)</remarks>


    var ret = "";
    //do NOT unescape the nodeValue string in CDATA sections!
    ret += "<![CDATA[" + this.nodeValue + "\]\]\>";

    return ret;
}

/**
* @class  DOMComment - This represents the content of a comment, i.e., all the characters between the starting '<!--' and ending '-->'
*
* @extends DOMCharacterData
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  ownerDocument : DOMDocument - The Document object associated with this node.
*/
DOMComment = function(ownerDocument)
{
    /// <summary>DOMComment - This represents the content of a comment, i.e., all the characters between the starting '&lt;!--' and ending '--&gt;'</summary>
    /// <param name="ownerDocument" type="DOMDocument">The Document object associated with this node.</param>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>
    /// <extends>DOMCharacterData</extends>


    this._class = addClass(this._class, "DOMComment");
    this.DOMCharacterData = DOMCharacterData;
    this.DOMCharacterData(ownerDocument);

    this.nodeName = "#comment";
    this.nodeType = DOMNode.COMMENT_NODE;
};
DOMComment.prototype = new DOMCharacterData;

/**
* @method DOMComment.toString - Serialize this Comment into an XML string
*
* @author Jon van Noort (jon@webarcana.com.au) and David Joham (djoham@yahoo.com)
*
* @return : string
*/
DOMComment.prototype.toString = function DOMComment_toString()
{
    /// <summary>DOMComment.toString - Serialize this Comment into an XML string</summary>
    /// <returns type="String"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au) and David Joham (djoham@yahoo.com)</remarks>


    var ret = "";

    ret += "<!--" + this.nodeValue + "-->";

    return ret;
}

/**
* @class  DOMProcessingInstruction - The ProcessingInstruction interface represents a "processing instruction",
*   used in XML as a way to keep processor-specific information in the text of the document
*
* @extends DOMNode
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  ownerDocument : DOMDocument - The Document object associated with this node.
*/
DOMProcessingInstruction = function(ownerDocument)
{
    /// <summary>DOMProcessingInstruction - The ProcessingInstruction interface represents a "processing instruction", used in XML as a way to keep processor-specific information in the text of the document</summary>
    /// <param name="ownerDocument" type="DOMDocument">The Document object associated with this node.</param>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>
    /// <extends>DOMNode</extends>


    this._class = addClass(this._class, "DOMProcessingInstruction");
    this.DOMNode = DOMNode;
    this.DOMNode(ownerDocument);

    // The target of this processing instruction.
    // XML defines this as being the first token following the markup that begins the processing instruction.
    this.target = "";

    // The content of this processing instruction.
    // This is from the first non white space character after the target to the character immediately preceding the ?>
    this.data = "";

    this.nodeType = DOMNode.PROCESSING_INSTRUCTION_NODE;
};
DOMProcessingInstruction.prototype = new DOMNode;

/**
* @method DOMProcessingInstruction.getTarget - Java style gettor for .target
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : string
*/
DOMProcessingInstruction.prototype.getTarget = function DOMProcessingInstruction_getTarget()
{
    /// <summary>DOMProcessingInstruction.getTarget - Java style gettor for .target</summary>
    /// <returns type="String"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    return this.nodeName;
};

/**
* @method DOMProcessingInstruction.getData - Java style gettor for .data
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @return : string
*/
DOMProcessingInstruction.prototype.getData = function DOMProcessingInstruction_getData()
{
    /// <summary>DOMProcessingInstruction.getData - Java style gettor for .data</summary>
    /// <returns type="String"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    return this.nodeValue;
};

/**
* @method DOMProcessingInstruction.setData - Java style settor for .data
*   alias for DOMProcessingInstruction.setNodeValue
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  data : string - The new data of this processing instruction.
*/
DOMProcessingInstruction.prototype.setData = function DOMProcessingInstruction_setData(data)
{
    /// <summary>DOMProcessingInstruction.setData - Java style settor for .data alias for DOMProcessingInstruction.setNodeValue</summary>
    /// <param name="data" type="String">The new data of this processing instruction.</param>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // delegate to setNodeValue
    this.setNodeValue(data);
};

/**
* @method DOMProcessingInstruction.setNodeValue - Java style settor for .nodeValue
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  data : string - The new data of this processing instruction.
*/
DOMProcessingInstruction.prototype.setNodeValue = function DOMProcessingInstruction_setNodeValue(data)
{
    /// <summary>DOMProcessingInstruction.setNodeValue - Java style settor for .nodeValue</summary>
    /// <param name="data" type="String">The new data of this processing instruction.</param>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // throw Exception if DOMNode is readonly
    if (this.ownerDocument.implementation.errorChecking && this._readonly)
    {
        throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
    }

    // assign values to properties (and aliases)
    this.nodeValue = new String(data);
    this.data = this.nodeValue;
};

/**
* @method DOMProcessingInstruction.toString - Serialize this ProcessingInstruction into an XML string
*
* @author Jon van Noort (jon@webarcana.com.au) and David Joham (djoham@yahoo.com)
*
* @return : string
*/
DOMProcessingInstruction.prototype.toString = function DOMProcessingInstruction_toString()
{
    /// <summary>DOMProcessingInstruction.toString - Serialize this ProcessingInstruction into an XML string</summary>
    /// <returns type="String"></returns>
    /// <remarks>Jon van Noort (jon@webarcana.com.au) and David Joham (djoham@yahoo.com)</remarks>


    var ret = "";

    ret += "<?" + this.nodeName + " " + this.nodeValue + " ?>";

    return ret;
}

/**
* @class  DOMDocumentFragment - DocumentFragment is a "lightweight" or "minimal" Document object.
*
* @extends DOMNode
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  ownerDocument : DOMDocument - The Document object associated with this node.
*/
DOMDocumentFragment = function(ownerDocument)
{
    /// <summary>DOMDocumentFragment - DocumentFragment is a "lightweight" or "minimal" Document object.</summary>
    /// <param name="ownerDocument" type="DOMDocument">The Document object associated with this node.</param>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>
    /// <extends>DOMNode</extends>


    this._class = addClass(this._class, "DOMDocumentFragment");
    this.DOMNode = DOMNode;
    this.DOMNode(ownerDocument);

    this.nodeName = "#document-fragment";
    this.nodeType = DOMNode.DOCUMENT_FRAGMENT_NODE;
};
DOMDocumentFragment.prototype = new DOMNode;

/**
* @method DOMDocumentFragment.toString - Serialize this DocumentFragment into an XML string
*
* @author David Joham (djoham@yahoo.com)
*
* @return : string
*/
DOMDocumentFragment.prototype.toString = function DOMDocumentFragment_toString()
{
    /// <summary>DOMDocumentFragment.toString - Serialize this DocumentFragment into an XML string</summary>
    /// <returns type="String"></returns>
    /// <remarks>David Joham (djoham@yahoo.com)</remarks>


    var xml = "";
    var intCount = this.getChildNodes().getLength();

    // create string concatenating the serialized ChildNodes
    for (intLoop = 0; intLoop < intCount; intLoop++)
    {
        xml += this.getChildNodes().item(intLoop).toString();
    }

    return xml;
}

///////////////////////
//  NOT IMPLEMENTED  //
///////////////////////
DOMDocumentType = function() { alert("DOMDocumentType.constructor(): Not Implemented"); };
DOMEntity = function() { alert("DOMEntity.constructor(): Not Implemented"); };
DOMEntityReference = function() { alert("DOMEntityReference.constructor(): Not Implemented"); };
DOMNotation = function() { alert("DOMNotation.constructor(): Not Implemented"); };


Strings = new Object()
Strings.WHITESPACE = " \t\n\r";
Strings.QUOTES = "\"'";

Strings.isEmpty = function Strings_isEmpty(strD)
{
    return (strD == null) || (strD.length == 0);
};
Strings.indexOfNonWhitespace = function Strings_indexOfNonWhitespace(strD, iB, iE)
{
    if (Strings.isEmpty(strD)) return -1;
    iB = iB || 0;
    iE = iE || strD.length;

    for (var i = iB; i < iE; i++)
        if (Strings.WHITESPACE.indexOf(strD.charAt(i)) == -1)
    {
        return i;
    }
    return -1;
};
Strings.lastIndexOfNonWhitespace = function Strings_lastIndexOfNonWhitespace(strD, iB, iE)
{
    if (Strings.isEmpty(strD)) return -1;
    iB = iB || 0;
    iE = iE || strD.length;

    for (var i = iE - 1; i >= iB; i--)
        if (Strings.WHITESPACE.indexOf(strD.charAt(i)) == -1)
        return i;
    return -1;
};
Strings.indexOfWhitespace = function Strings_indexOfWhitespace(strD, iB, iE)
{
    if (Strings.isEmpty(strD)) return -1;
    iB = iB || 0;
    iE = iE || strD.length;

    for (var i = iB; i < iE; i++)
        if (Strings.WHITESPACE.indexOf(strD.charAt(i)) != -1)
        return i;
    return -1;
};
Strings.replace = function Strings_replace(strD, iB, iE, strF, strR)
{
    if (Strings.isEmpty(strD)) return "";
    iB = iB || 0;
    iE = iE || strD.length;

    return strD.substring(iB, iE).split(strF).join(strR);
};
Strings.getLineNumber = function Strings_getLineNumber(strD, iP)
{
    if (Strings.isEmpty(strD)) return -1;
    iP = iP || strD.length;

    return strD.substring(0, iP).split("\n").length
};
Strings.getColumnNumber = function Strings_getColumnNumber(strD, iP)
{
    if (Strings.isEmpty(strD)) return -1;
    iP = iP || strD.length;

    var arrD = strD.substring(0, iP).split("\n");
    var strLine = arrD[arrD.length - 1];
    arrD.length--;
    var iLinePos = arrD.join("\n").length;

    return iP - iLinePos;
};


StringBuffer = function() { this._a = new Array(); };
StringBuffer.prototype.append = function StringBuffer_append(d) { this._a[this._a.length] = d; };
StringBuffer.prototype.toString = function StringBuffer_toString() { return this._a.join(""); };

