summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xdoc/lib.txt13
-rw-r--r--lib/pure/xmldom.nim1009
-rw-r--r--lib/pure/xmldomparser.nim110
-rwxr-xr-xweb/news.txt2
-rwxr-xr-xweb/nimrod.ini4
5 files changed, 1136 insertions, 2 deletions
diff --git a/doc/lib.txt b/doc/lib.txt
index 7167cbf4e..06edf997c 100755
--- a/doc/lib.txt
+++ b/doc/lib.txt
@@ -68,6 +68,9 @@ String handling
   is done in O(1) instead of O(n).
 
 * `unidecode <unidecode.html>`_ 
+  This module provides Unicode to ASCII transliterations:
+  It finds the sequence of ASCII characters that is the closest approximation
+  to the Unicode string.
   
 
 Generic Operating System Services
@@ -162,6 +165,16 @@ Parsers
   scheme for lexers and parsers. This is used by the diverse parsing modules.
 
 
+XML Processing
+--------------
+
+* `xmldom <xmldom.html>`_ 
+  This module implements the XML DOM Level 2.
+
+* `xmldomparser <xmldomparser.html>`_
+  This module parses a XML Document into a XML DOM Document representation.
+
+
 Code generation
 ---------------
 
diff --git a/lib/pure/xmldom.nim b/lib/pure/xmldom.nim
new file mode 100644
index 000000000..12578a793
--- /dev/null
+++ b/lib/pure/xmldom.nim
@@ -0,0 +1,1009 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2010 Dominik Picheta
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+
+import strutils
+## This module implements the XML DOM Level 2
+
+#http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html
+#DOMString = String
+#DOMTimeStamp = int16 ??
+
+#DECLARATIONS
+
+#Exceptions
+type
+  EDOMException* = object of E_Base #Base exception object for all DOM Exceptions
+  EDOMStringSizeErr* = object of EDOMException #If the specified range of text does not fit into a DOMString
+                                               #Currently not used(Since DOMString is just string)
+  EHierarchyRequestErr* = object of EDOMException #If any node is inserted somewhere it doesn't belong
+  EIndexSizeErr* = object of EDOMException #If index or size is negative, or greater than the allowed value
+  EInuseAttributeErr* = object of EDOMException #If an attempt is made to add an attribute that is already in use elsewhere
+  EInvalidAccessErr* = object of EDOMException #If a parameter or an operation is not supported by the underlying object.
+  EInvalidCharacterErr* = object of EDOMException #This exception is raised when a string parameter contains an illegal character
+  EInvalidModificationErr* = object of EDOMException #If an attempt is made to modify the type of the underlying object.
+  EInvalidStateErr* = object of EDOMException #If an attempt is made to use an object that is not, or is no longer, usable.
+  ENamespaceErr* = object of EDOMException #If an attempt is made to create or change an object in a way which is incorrect with regard to namespaces.
+  ENotFoundErr* = object of EDOMException #If an attempt is made to reference a node in a context where it does not exist
+  ENotSupportedErr* = object of EDOMException #If the implementation does not support the requested type of object or operation.
+  ENoDataAllowedErr* = object of EDOMException #If data is specified for a node which does not support data
+  ENoModificationAllowedErr* = object of EDOMException #If an attempt is made to modify an object where modifications are not allowed
+  ESyntaxErr* = object of EDOMException #If an invalid or illegal string is specified.
+  EWrongDocumentErr* = object of EDOMException #If a node is used in a different document than the one that created it (that doesn't support it)
+
+template newException(exceptn, message: expr): expr =
+  block: # open a new scope
+    var
+      e: ref exceptn
+    new(e)
+    e.msg = message
+    e
+
+const
+  ElementNode* = 1
+  AttributeNode* = 2
+  TextNode* = 3
+  CDataSectionNode* = 4
+  ProcessingInstructionNode* = 7
+  CommentNode* = 8
+  DocumentNode* = 9
+  DocumentFragmentNode* = 11
+
+  # Nodes which are childless - Not sure about AttributeNode
+  childlessObjects = {DocumentNode, AttributeNode, TextNode, CDataSectionNode, ProcessingInstructionNode, CommentNode}
+  # Illegal characters
+  illegalChars = {'>', '<', '&', '"'}
+
+
+type
+  Feature = tuple[name: string, version: string]
+  PDOMImplementation* = ref DOMImplementation
+  DOMImplementation = object
+    Features: seq[Feature] #Read-Only
+
+  PNode* = ref Node
+  Node = object
+    attributes: seq[PAttr] #Read-only
+    childNodes*: seq[PNode] #Read-only
+    FLocalName: string #Read-only
+    FNamespaceURI: string #Read-only
+    FNodeName: string #Read-only
+    nodeValue*: string
+    FNodeType: int #Read-only
+    FOwnerDocument: PDocument #Read-Only
+    FParentNode: PNode #Read-Only
+    prefix*: string # Setting this should change some values... TODO!
+  
+  PElement* = ref Element
+  Element = object of Node
+    FTagName: string #Read-only
+  
+  PCharacterData = ref CharacterData
+  CharacterData = object of Node
+    data*: string
+    
+  PDocument* = ref Document
+  Document = object of Node
+    FImplementation: PDOMImplementation #Read-only
+    FDocumentElement: PElement #Read-only
+    
+  PAttr* = ref Attr  
+  Attr = object of Node
+    FName: string #Read-only
+    FSpecified: bool #Read-only
+    value*: string
+    FOwnerElement: PElement #Read-only
+
+  PDocumentFragment* = ref DocumentFragment
+  DocumentFragment = object of Node
+
+  PText* = ref Text
+  Text = object of CharacterData
+  
+  PComment* = ref comment
+  Comment = object of CharacterData
+  
+  PCDataSection* = ref CDataSection
+  CDataSection = object of Text
+    
+  PProcessingInstruction* = ref ProcessingInstruction
+  ProcessingInstruction = object of Node
+    data*: string
+    FTarget: string #Read-only
+
+#DOMImplementation
+proc getDOM*(): PDOMImplementation =
+  ##Returns a DOMImplementation
+  var DOMImpl: PDOMImplementation
+  new(DOMImpl)
+  DOMImpl.Features = @[(name: "core", version: "2.0"), (name: "core", version: "1.0"), (name: "XML", version: "2.0")]
+  return DOMImpl
+
+proc createDocument*(dom: PDOMImplementation, namespaceURI: string, qualifiedName: string): PDocument =
+  ##Creates an XML Document object of the specified type with its document element.
+  var doc: PDocument
+  new(doc)
+  doc.FNamespaceURI = namespaceURI
+  doc.FImplementation = dom
+  
+  var elTag: PElement
+  new(elTag)
+  elTag.FTagName = qualifiedName
+  elTag.FNodeName = qualifiedName
+  doc.FDocumentElement = elTag
+  doc.FNodeType = DocumentNode
+  
+  return doc
+  
+proc createDocument*(dom: PDOMImplementation, n: PElement): PDocument =
+  ##Creates an XML Document object of the specified type with its document element.
+  #This procedure is not in the specification, it's provided for the parser.
+  var doc: PDocument
+  new(doc)
+  doc.FDocumentElement = n
+  doc.FImplementation = dom
+  doc.FNodeType = DocumentNode
+  
+  return doc
+  
+proc hasFeature*(dom: PDOMImplementation, feature: string, version: string = ""): bool =
+  ##Returns ``true`` if this ``version`` of the DomImplementation implements ``feature``, otherwise ``false``
+  for iName, iVersion in items(dom.Features):
+    if iName == feature:
+      if version == "":
+        return True
+      else:
+        if iVersion == version:
+          return True
+  return False
+
+
+#Document
+#Attributes
+  
+proc implementation*(doc: PDocument): PDOMImplementation =
+  return doc.FImplementation
+  
+proc documentElement*(doc: PDocument): PElement = 
+  return doc.FDocumentElement
+
+#Internal procedures
+proc findNodes(nl: PNode, name: string): seq[PNode] =
+  #Made for getElementsByTagName
+  var r: seq[PNode] = @[]
+  if nl.childNodes == nil: return @[]
+  if nl.childNodes.len() == 0: return @[]
+
+  for i in items(nl.childNodes):
+    if i.FNodeType == ElementNode:
+      if i.FNodeName == name or name == "*":
+        r.add(i)
+        
+      if i.childNodes != nil:
+        if i.childNodes.len() != 0:
+          r.add(findNodes(i, name))
+    
+  return r
+  
+proc findNodesNS(nl: PNode, namespaceURI: string, localName: string): seq[PNode] =
+  #Made for getElementsByTagNameNS
+  var r: seq[PNode] = @[]
+  if nl.childNodes == nil: return @[]
+  if nl.childNodes.len() == 0: return @[]
+
+  for i in items(nl.childNodes):
+    if i.FNodeType == ElementNode:
+      if (i.FNamespaceURI == namespaceURI or namespaceURI == "*") and (i.FLocalName == localName or localName == "*"):
+        r.add(i)
+        
+      if i.childNodes != nil:
+        if i.childNodes.len() != 0:
+          r.add(findNodesNS(i, namespaceURI, localName))
+    
+  return r
+    
+
+#Procedures
+proc createAttribute*(doc: PDocument, name: string): PAttr =
+  ##Creates an Attr of the given name. Note that the Attr instance can then be set on an Element using the setAttributeNode method.
+  ##To create an attribute with a qualified name and namespace URI, use the createAttributeNS method. 
+  
+  #Check if name contains illegal characters
+  if illegalChars in name:
+    raise newException(EInvalidCharacterErr, "Invalid character")
+  
+  var AttrNode: PAttr
+  new(AttrNode)
+  AttrNode.FName = name
+  AttrNode.FNodeName = name
+  AttrNode.FLocalName = nil
+  AttrNode.prefix = nil
+  AttrNode.FNamespaceURI = nil
+  AttrNode.value = ""
+  AttrNode.FSpecified = False
+  return AttrNode
+
+proc createAttributeNS*(doc: PDocument, namespaceURI: string, qualifiedName: string): PAttr =
+  ##Creates an attribute of the given qualified name and namespace URI
+  
+  #Check if name contains illegal characters
+  if illegalChars in namespaceURI or illegalChars in qualifiedName:
+    raise newException(EInvalidCharacterErr, "Invalid character")
+  #Exceptions
+  if qualifiedName.contains(':'):
+    if namespaceURI == nil or namespaceURI == "":
+      raise newException(ENamespaceErr, "When qualifiedName contains a prefix namespaceURI cannot be nil")
+    elif qualifiedName.split(':')[0].toLower() == "xml" and namespaceURI != "http://www.w3.org/XML/1998/namespace":
+      raise newException(ENamespaceErr, 
+        "When the namespace prefix is \"xml\" namespaceURI has to be \"http://www.w3.org/XML/1998/namespace\"")
+    elif qualifiedName.split(':')[1].toLower() == "xmlns" and namespaceURI != "http://www.w3.org/2000/xmlns/":
+      raise newException(ENamespaceErr, 
+        "When the namespace prefix is \"xmlns\" namespaceURI has to be \"http://www.w3.org/2000/xmlns/\"")
+  
+  var AttrNode: PAttr
+  new(AttrNode)
+  AttrNode.FName = qualifiedName
+  AttrNode.FNodeName = qualifiedName
+  AttrNode.FSpecified = False
+  AttrNode.FNamespaceURI = namespaceURI
+  if qualifiedName.contains(':'):
+    AttrNode.prefix = qualifiedName.split(':')[0]
+    AttrNode.FLocalName = qualifiedName.split(':')[1]
+  else:
+    AttrNode.prefix = nil
+    AttrNode.FLocalName = qualifiedName
+  AttrNode.value = ""
+  
+  AttrNode.FNodeType = AttributeNode
+  return AttrNode
+
+proc createCDATASection*(doc: PDocument, data: string): PCDATASection =
+  ##Creates a CDATASection node whose value is the specified string.
+  var CData: PCDATASection
+  new(CData)
+  CData.data = data
+  CData.nodeValue = data
+  CData.FNodeName = "#text" #Not sure about this, but this is technically a TextNode
+  CData.FNodeType = CDataSectionNode
+  return CData
+
+proc createComment*(doc: PDocument, data: string): PComment =
+  ##Creates a Comment node given the specified string. 
+  var Comm: PComment
+  new(Comm)
+  Comm.data = data
+  Comm.nodeValue = data
+  
+  Comm.FNodeType = CommentNode
+  return Comm
+
+proc createDocumentFragment*(doc: PDocument): PDocumentFragment =
+  ##Creates an empty DocumentFragment object.
+  var DF: PDocumentFragment
+  new(DF)
+  return DF
+
+proc createElement*(doc: PDocument, tagName: string): PElement =
+  ##Creates an element of the type specified.
+  
+  #Check if name contains illegal characters
+  if illegalChars in tagName:
+    raise newException(EInvalidCharacterErr, "Invalid character")
+    
+  var elNode: PElement
+  new(elNode)
+  elNode.FTagName = tagName
+  elNode.FNodeName = tagName
+  elNode.FLocalName = nil
+  elNode.prefix = nil
+  elNode.FNamespaceURI = nil
+  elNode.childNodes = @[]
+  elNode.attributes = @[]
+  
+  elNode.FNodeType = ElementNode
+  
+  return elNode
+
+proc createElementNS*(doc: PDocument, namespaceURI: string, qualifiedName: string): PElement =
+  ##Creates an element of the given qualified name and namespace URI.
+  if qualifiedName.contains(':'):
+    if namespaceURI == nil or namespaceURI == "":
+      raise newException(ENamespaceErr, "When qualifiedName contains a prefix namespaceURI cannot be nil")
+    elif qualifiedName.split(':')[0].toLower() == "xml" and namespaceURI != "http://www.w3.org/XML/1998/namespace":
+      raise newException(ENamespaceErr, 
+        "When the namespace prefix is \"xml\" namespaceURI has to be \"http://www.w3.org/XML/1998/namespace\"")
+        
+  #Check if name contains illegal characters
+  if illegalChars in namespaceURI or illegalChars in qualifiedName:
+    raise newException(EInvalidCharacterErr, "Invalid character")
+    
+  var elNode: PElement
+  new(elNode)
+  elNode.FTagName = qualifiedName
+  elNode.FNodeName = qualifiedName
+  if qualifiedName.contains(':'):
+    elNode.prefix = qualifiedName.split(':')[0]
+    elNode.FLocalName = qualifiedName.split(':')[1]
+  else:
+    elNode.prefix = nil
+    elNode.FLocalName = qualifiedName
+  elNode.FNamespaceURI = namespaceURI
+  elNode.childNodes = @[]
+  elNode.attributes = @[]
+  
+  elNode.FNodeType = ElementNode
+  
+  return elNode
+
+proc createProcessingInstruction*(doc: PDocument, target: string, data: string): PProcessingInstruction = 
+  ##Creates a ProcessingInstruction node given the specified name and data strings. 
+  
+  #Check if name contains illegal characters
+  if illegalChars in target:
+    raise newException(EInvalidCharacterErr, "Invalid character")
+    
+  var PI: PProcessingInstruction
+  new(PI)
+  PI.FTarget = target
+  PI.data = data
+  PI.FNodeType = ProcessingInstructionNode
+  return PI
+
+proc createTextNode*(doc: PDocument, data: string): PText = #Propably TextNode
+  ##Creates a Text node given the specified string. 
+  var txtNode: PText
+  new(txtNode)
+  txtNode.data = data
+  txtNode.nodeValue = data
+  txtNode.FNodeName = "#text"
+  
+  txtNode.FNodeType = TextNode
+  return txtNode
+
+discard """proc getElementById*(doc: PDocument, elementId: string): PElement =
+  ##Returns the ``Element`` whose ID is given by ``elementId``. If no such element exists, returns ``nil``
+  #TODO"""
+
+proc getElementsByTagName*(doc: PDocument, tagName: string): seq[PNode] =
+  ##Returns a NodeList of all the Elements with a given tag name in
+  ##the order in which they are encountered in a preorder traversal of the Document tree. 
+  var result: seq[PNode] = @[]
+  if doc.FDocumentElement.FNodeName == tagName or tagName == "*":
+    result.add(doc.FDocumentElement)
+  
+  result.add(doc.FDocumentElement.findNodes(tagName))
+  return result
+  
+proc getElementsByTagNameNS*(doc: PDocument, namespaceURI: string, localName: string): seq[PNode] =
+  ##Returns a NodeList of all the Elements with a given localName and namespaceURI
+  ##in the order in which they are encountered in a preorder traversal of the Document tree. 
+  var result: seq[PNode] = @[]
+  if doc.FDocumentElement.FLocalName == localName or localName == "*":
+    if doc.FDocumentElement.FNamespaceURI == namespaceURI or namespaceURI == "*":
+      result.add(doc.FDocumentElement)
+      
+  result.add(doc.FDocumentElement.findNodesNS(namespaceURI, localName))
+  return result
+
+proc importNode*(doc: PDocument, importedNode: PNode, deep: bool): PNode =
+  ## Imports a node from another document to this document
+  case importedNode.FNodeType
+  of AttributeNode:
+    var nAttr: PAttr = PAttr(importedNode)
+    nAttr.FOwnerDocument = doc
+    nAttr.FParentNode = nil
+    nAttr.FOwnerElement = nil
+    nAttr.FSpecified = True
+    return nAttr
+  of DocumentFragmentNode:
+    var n: PNode
+    new(n)
+    n = importedNode
+    n.FOwnerDocument = doc
+    n.FParentNode = nil
+
+    n.FOwnerDocument = doc
+    n.FParentNode = nil
+    var tmp: seq[PNode] = n.childNodes
+    n.childNodes = @[]
+    if deep == True:
+      for i in low(tmp.len())..high(tmp.len()):
+        n.childNodes.add(importNode(doc, tmp[i], deep))
+        
+    return n
+  of ElementNode:
+    var n: PNode
+    new(n)
+    n = importedNode
+    n.FOwnerDocument = doc
+    n.FParentNode = nil
+    
+    var tmpA: seq[PAttr] = n.attributes
+    n.attributes = @[]
+    # Import the Element node's attributes
+    for i in low(tmpA.len())..high(tmpA.len()):
+      n.attributes.add(PAttr(importNode(doc, tmpA[i], deep)))
+    # Import the childNodes
+    var tmp: seq[PNode] = n.childNodes
+    n.childNodes = @[]
+    if deep == True:
+      for i in low(tmp.len())..high(tmp.len()):
+        n.childNodes.add(importNode(doc, tmp[i], deep))
+        
+    return n
+  of ProcessingInstructionNode, TextNode, CDataSectionNode, CommentNode:
+    var n: PNode
+    new(n)
+    n = importedNode
+    n.FOwnerDocument = doc
+    n.FParentNode = nil
+    return n
+  else:
+    raise newException(ENotSupportedErr, "The type of node being imported is not supported")
+  
+
+# Node
+# Attributes
+proc Attributes*(n: PNode): seq[PAttr] =
+  if n.attributes == nil: n.attributes = @[] # Initialize the sequence if it's nil
+  return n.attributes
+  
+proc firstChild*(n: PNode): PNode =
+  if n.childNodes.len() > 0:
+    return n.childNodes[0]
+  else:
+    return nil
+  
+proc lastChild*(n: PNode): PNode =
+  if n.childNodes.len() > 0:
+    return n.childNodes[n.childNodes.len() - 1]
+  else:
+    return nil
+  
+proc localName*(n: PNode): string =
+  return n.FLocalName
+
+proc namespaceURI*(n: PNode): string =
+  return n.FNamespaceURI
+
+proc nextSibling*(n: PNode): PNode =
+  var nLow: int = low(n.FParentNode.childNodes)
+  var nHigh: int = high(n.FParentNode.childNodes)
+  for i in nLow..nHigh:
+    if n.FParentNode.childNodes[i] == n: # HAVE TO TEST this line, not sure if ``==`` will work
+      return n.FParentNode.childNodes[i + 1]
+  return nil
+
+proc nodeName*(n: PNode): string =
+  return n.FNodeName
+
+proc nodeType*(n: PNode): int =
+  return n.FNodeType
+
+proc ownerDocument*(n: PNode): PDocument =
+  return n.FOwnerDocument
+
+proc parentNode*(n: PNode): PNode =
+  return n.FParentNode
+  
+proc previousSibling*(n: PNode): PNode =
+  var nLow: int = low(n.FParentNode.childNodes)
+  var nHigh: int = high(n.FParentNode.childNodes)
+  for i in nLow..nHigh:
+    if n.FParentNode.childNodes[i] == n: # HAVE TO TEST this line, not sure if ``==`` will work
+      return n.FParentNode.childNodes[i - 1]
+  return nil
+  
+proc `prefix=`*(n: var PNode, value: string) =
+  # Setter
+  # Check if name contains illegal characters
+  if illegalChars in value:
+    raise newException(EInvalidCharacterErr, "Invalid character")
+
+  if n.FNamespaceURI == nil:
+    raise newException(ENamespaceErr, "namespaceURI cannot be nil")
+  elif value.toLower() == "xml" and n.FNamespaceURI != "http://www.w3.org/XML/1998/namespace":
+    raise newException(ENamespaceErr, 
+      "When the namespace prefix is \"xml\" namespaceURI has to be \"http://www.w3.org/XML/1998/namespace\"")
+  elif value.toLower() == "xmlns" and n.FNamespaceURI != "http://www.w3.org/2000/xmlns/":
+    raise newException(ENamespaceErr, 
+      "When the namespace prefix is \"xmlns\" namespaceURI has to be \"http://www.w3.org/2000/xmlns/\"")
+  elif value.toLower() == "xmlns" and n.FNodeType == AttributeNode:
+    raise newException(ENamespaceErr, "An AttributeNode cannot have a prefix of \"xmlns\"")
+
+  n.FNodeName = value & ":" & n.FLocalName
+  if n.nodeType == ElementNode:
+    var el: PElement = PElement(n)
+    el.FTagName = value & ":" & n.FLocalName
+    n = PNode(el)
+  elif n.nodeType == AttributeNode:
+    var attr: PAttr = PAttr(n)
+    attr.FName = value & ":" & n.FLocalName
+    n = PNode(attr)
+
+# Procedures
+proc appendChild*(n: PNode, newChild: PNode) =
+  ## 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.
+  
+  # TODO - Check if n contains newChild
+  # TODO - Exceptions
+  
+  # Check if newChild is from this nodes document
+  if n.FOwnerDocument != newChild.FOwnerDocument:
+    raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.")
+  
+  if n == newChild:
+    raise newException(EHierarchyRequestErr, "You can't add a node into itself")
+  
+  if n.childNodes == nil: n.childNodes = @[]
+    
+  newChild.FParentNode = n
+  for i in low(n.childNodes)..high(n.childNodes):
+    if n.childNodes[i] == newChild:
+      n.childNodes[i] = newChild
+    
+  n.childNodes.add(newChild)
+
+proc cloneNode*(n: PNode, deep: bool): PNode = 
+  ## Returns a duplicate of this node, if ``deep`` is `true`, Element node's children are copied
+  case n.FNodeType
+  of AttributeNode:
+    var newNode: PAttr
+    new(newNode)
+    newNode = PAttr(n)
+    newNode.FSpecified = True
+    newNode.FOwnerElement = nil
+    return newNode
+  of ElementNode:
+    var newNode: PElement
+    new(newNode)
+    newNode = PElement(n)
+    # Import the childNodes
+    var tmp: seq[PNode] = n.childNodes
+    n.childNodes = @[]
+    if deep == True:
+      for i in low(tmp.len())..high(tmp.len()):
+        n.childNodes.add(cloneNode(tmp[i], deep))
+    return newNode
+  else:
+    var newNode: PNode
+    new(newNode)
+    newNode = n
+    return newNode
+
+proc hasAttributes*(n: PNode): bool =
+  ## Returns whether this node (if it is an element) has any attributes. 
+  return n.attributes.len() > 0
+
+proc hasChildNodes*(n: PNode): bool = 
+  ## Returns whether this node has any children.
+  return n.childNodes.len() > 0
+
+proc insertBefore*(n: PNode, newChild: PNode, refChild: PNode): PNode =
+  ## Inserts the node ``newChild`` before the existing child node ``refChild``.
+  ## If ``refChild`` is nil, insert ``newChild`` at the end of the list of children.
+  
+  # Check if newChild is from this nodes document
+  if n.FOwnerDocument != newChild.FOwnerDocument:
+    raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.")
+    
+  for i in low(n.childNodes)..high(n.childNodes):
+    if n.childNodes[i] == refChild:
+      n.childNodes.insert(newChild, i - 1)
+    return
+
+proc isSupported*(n: PNode, feature: string, version: string): bool =
+  ## Tests whether the DOM implementation implements a specific 
+  ## feature and that feature is supported by this node. 
+  return n.FOwnerDocument.FImplementation.hasFeature(feature, version)
+
+proc normalize*(n: PNode) =
+  ## Puts all Text nodes in the full depth of the sub-tree underneath this Node
+  
+  # TODO
+
+proc removeChild*(n: PNode, oldChild: PNode): PNode =
+  ## Removes the child node indicated by ``oldChild`` from the list of children, and returns it.
+  for i in low(n.childNodes)..high(n.childNodes):
+    if n.childNodes[i] == oldChild:
+      result = n.childNodes[i]
+      n.childNodes.delete(i)
+      return result
+      
+  raise newException(ENotFoundErr, "Node not found")
+    
+proc replaceChild*(n: PNode, newChild: PNode, oldChild: PNode): PNode =
+  ## Replaces the child node ``oldChild`` with ``newChild`` in the list of children, and returns the ``oldChild`` node.
+  
+  # Check if newChild is from this nodes document
+  if n.FOwnerDocument != newChild.FOwnerDocument:
+    raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.")
+  
+  for i in low(n.childNodes)..high(n.childNodes):
+    if n.childNodes[i] == oldChild:
+      result = n.childNodes[i]
+      n.childNodes[i] = newChild
+      return result
+  
+  raise newException(ENotFoundErr, "Node not found")
+  
+# NamedNodeMap
+
+proc getNamedItem*(NList: seq[PNode], name: string): PNode =
+  ## Retrieves a node specified by ``name``. If this node cannot be found returns ``nil``
+  for i in items(NList):
+    if i.nodeName() == name:
+      return i
+  return nil
+  
+proc getNamedItem*(NList: seq[PAttr], name: string): PAttr =
+  ## Retrieves a node specified by ``name``. If this node cannot be found returns ``nil``
+  for i in items(NList):
+    if i.nodeName() == name:
+      return i
+  return nil
+      
+proc getNamedItemNS*(NList: seq[PNode], namespaceURI: string, localName: string): PNode =
+  ## Retrieves a node specified by ``localName`` and ``namespaceURI``. If this node cannot be found returns ``nil``
+  for i in items(NList):
+    if i.namespaceURI() == namespaceURI and i.localName() == localName:
+      return i
+  return nil
+  
+proc getNamedItemNS*(NList: seq[PAttr], namespaceURI: string, localName: string): PAttr = 
+  ## Retrieves a node specified by ``localName`` and ``namespaceURI``. If this node cannot be found returns ``nil``
+  for i in items(NList):
+    if i.NamespaceURI() == namespaceURI and i.LocalName() == localName:
+      return i
+  return nil
+
+proc item*(NList: seq[PNode], index: int): PNode =
+  ## Returns the ``index`` th item in the map. 
+  ## If ``index`` is greater than or equal to the number of nodes in this map, this returns ``nil``.
+  if index >= NList.len(): return nil
+  else: return NList[index]
+
+proc removeNamedItem*(NList: var seq[PNode], name: string): PNode =
+  ## Removes a node specified by ``name``
+  ## Raises the ``ENotFoundErr`` exception, if the node was not found
+  for i in low(NList)..high(NList):
+    if NList[i].FNodeName == name:
+      result = NList[i]
+      NList.delete(i)
+      return result
+  
+  raise newException(ENotFoundErr, "Node not found")
+  
+proc removeNamedItemNS*(NList: var seq[PNode], namespaceURI: string, localName: string): PNode =
+  ## Removes a node specified by local name and namespace URI
+  for i in low(NList)..high(NList):
+    if NList[i].FLocalName == localName and NList[i].FNamespaceURI == namespaceURI:
+      result = NList[i]
+      NList.delete(i)
+      return result
+  
+  raise newException(ENotFoundErr, "Node not found")
+
+proc setNamedItem*(NList: var seq[PNode], arg: PNode): PNode =
+  ## Adds ``arg`` as a ``Node`` to the ``NList``
+  ## If a node with the same name is already present in this map, it is replaced by the new one.
+  if NList != nil:
+    if NList.len() > 0:
+      #Check if newChild is from this nodes document
+      if NList[0].FOwnerDocument != arg.FOwnerDocument:
+        raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.")
+  #Exceptions End
+  
+  var item: PNode = NList.getNamedItem(arg.NodeName())
+  if item == nil:
+    NList.add(arg)
+    return nil
+  else:
+    # Node with the same name exists
+    var index: int = 0
+    for i in low(NList)..high(NList):
+      if NList[i] == item:
+        index = i
+        break
+    NList[index] = arg
+    return item # Return the replaced node
+    
+proc setNamedItem*(NList: var seq[PAttr], arg: PAttr): PAttr =
+  ## Adds ``arg`` as a ``Node`` to the ``NList``
+  ## If a node with the same name is already present in this map, it is replaced by the new one.
+  if NList != nil:
+    if NList.len() > 0:
+      # Check if newChild is from this nodes document
+      if NList[0].FOwnerDocument != arg.FOwnerDocument:
+        raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.")
+        
+  if arg.FOwnerElement != nil:
+    raise newException(EInuseAttributeErr, "This attribute is in use by another element, use cloneNode")
+        
+  # Exceptions end
+  var item: PAttr = NList.getNamedItem(arg.nodeName())
+  if item == nil:
+    NList.add(arg)
+    return nil
+  else:
+    # Node with the same name exists
+    var index: int = 0
+    for i in low(NList)..high(NList):
+      if NList[i] == item:
+        index = i
+        break
+    NList[index] = arg
+    return item # Return the replaced node
+    
+proc setNamedItemNS*(NList: var seq[PNode], arg: PNode): PNode =
+  ## Adds a node using its ``namespaceURI`` and ``localName``
+  if NList != nil:
+    if NList.len() > 0:
+      # Check if newChild is from this nodes document
+      if NList[0].FOwnerDocument != arg.FOwnerDocument:
+        raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.")
+  #Exceptions end
+        
+  var item: PNode = NList.getNamedItemNS(arg.namespaceURI(), arg.localName())
+  if item == nil:
+    NList.add(arg)
+    return nil
+  else:
+    # Node with the same name exists
+    var index: int = 0
+    for i in low(NList)..high(NList):
+      if NList[i] == item:
+        index = i
+        break
+    NList[index] = arg
+    return item # Return the replaced node
+    
+proc setNamedItemNS*(NList: var seq[PAttr], arg: PAttr): PAttr =
+  ## Adds a node using its ``namespaceURI`` and ``localName``
+  if NList != nil:
+    if NList.len() > 0:
+      # Check if newChild is from this nodes document
+      if NList[0].FOwnerDocument != arg.FOwnerDocument:
+        raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.")
+        
+  if arg.FOwnerElement != nil:
+    raise newException(EInuseAttributeErr, "This attribute is in use by another element, use cloneNode")
+        
+  # Exceptions end
+  var item: PAttr = NList.getNamedItemNS(arg.namespaceURI(), arg.localName())
+  if item == nil:
+    NList.add(arg)
+    return nil
+  else:
+    # Node with the same name exists
+    var index: int = 0
+    for i in low(NList)..high(NList):
+      if NList[i] == item:
+        index = i
+        break
+    NList[index] = arg
+    return item # Return the replaced node
+    
+# TODO - Maybe implement a ChildlessNode!^
+    
+# CharacterData - Decided to implement this, 
+# Didn't add the procedures, because you can just edit .data
+
+# Attr
+# Attributes
+proc name*(a: PAttr): string =
+  return a.FName
+  
+proc specified*(a: PAttr): bool =
+  return a.FSpecified
+  
+proc ownerElement*(a: PAttr): PElement = 
+  return a.FOwnerElement
+
+# Element
+# Attributes
+
+proc tagName*(el: PElement): string =
+  return el.FTagName
+
+# Procedures
+proc getAttribute*(el: PElement, name: string): string =
+  ## Retrieves an attribute value by ``name``
+  var attribute = el.attributes.getNamedItem(name)
+  if attribute != nil:
+    return attribute.value
+  else:
+    return nil
+
+proc getAttributeNS*(el: PElement, namespaceURI: string, localName: string): string =
+  ## Retrieves an attribute value by ``localName`` and ``namespaceURI``
+  var attribute = el.attributes.getNamedItemNS(namespaceURI, localName)
+  if attribute != nil:
+    return attribute.value
+  else:
+    return nil
+    
+proc getAttributeNode*(el: PElement, name: string): PAttr =
+  ## Retrieves an attribute node by ``name``
+  ## To retrieve an attribute node by qualified name and namespace URI, use the `getAttributeNodeNS` method
+  return el.attributes.getNamedItem(name)
+
+proc getAttributeNodeNS*(el: PElement, namespaceURI: string, localName: string): PAttr =
+  ## Retrieves an `Attr` node by ``localName`` and ``namespaceURI``
+  return el.attributes.getNamedItemNS(namespaceURI, localName)
+
+proc getElementsByTagName*(el: PElement, name: string): seq[PNode] =
+  ## Returns a `NodeList` of all descendant `Elements` of ``el`` with a given tag ``name``,
+  ## in the order in which they are encountered in a preorder traversal of this `Element` tree
+  ## If ``name`` is `*`, returns all descendant of ``el``
+  result = el.findNodes(name)
+
+proc getElementsByTagNameNS*(el: PElement, namespaceURI: string, localName: string): seq[PNode] =
+  ## Returns a `NodeList` of all the descendant Elements with a given
+  ## ``localName`` and ``namespaceURI`` in the order in which they are
+  ## encountered in a preorder traversal of this Element tree
+  result = el.findNodesNS(namespaceURI, localName)
+
+proc hasAttribute*(el: PElement, name: string): bool =
+  ## Returns ``true`` when an attribute with a given ``name`` is specified 
+  ## on this element , ``false`` otherwise. 
+  return el.attributes.getNamedItem(name) != nil
+
+proc hasAttributeNS*(el: PElement, namespaceURI: string, localName: string): bool =
+  ## Returns ``true`` when an attribute with a given ``localName`` and
+  ## ``namespaceURI`` is specified on this element , ``false`` otherwise 
+  return el.attributes.getNamedItemNS(namespaceURI, localName) != nil
+
+proc removeAttribute*(el: PElement, name: string) =
+  ## Removes an attribute by ``name``
+  for i in low(el.attributes)..high(el.attributes):
+    if el.attributes[i].FName == name:
+      el.attributes.delete(i)
+      
+proc removeAttributeNS*(el: PElement, namespaceURI: string, localName: string) =
+  ## Removes an attribute by ``localName`` and ``namespaceURI``
+  for i in low(el.attributes)..high(el.attributes):
+    if el.attributes[i].FNamespaceURI == namespaceURI and 
+        el.attributes[i].FLocalName == localName:
+      el.attributes.delete(i)
+  
+proc removeAttributeNode*(el: PElement, oldAttr: PAttr): PAttr =
+  ## Removes the specified attribute node
+  ## If the attribute node cannot be found raises ``ENotFoundErr``
+  for i in low(el.attributes)..high(el.attributes):
+    if el.attributes[i] == oldAttr:
+      result = el.attributes[i]
+      el.attributes.delete(i)
+      return result
+  
+  raise newException(ENotFoundErr, "oldAttr is not a member of el's Attributes")
+
+proc setAttributeNode*(el: PElement, newAttr: PAttr): PAttr =
+  ## Adds a new attribute node, if an attribute with the same `nodeName` is
+  ## present, it is replaced by the new one and the replaced attribute is
+  ## returned, otherwise ``nil`` is returned.
+  
+  # Check if newAttr is from this nodes document
+  if el.FOwnerDocument != newAttr.FOwnerDocument:
+    raise newException(EWrongDocumentErr, 
+      "This node belongs to a different document, use importNode.")
+        
+  if newAttr.FOwnerElement != nil:
+    raise newException(EInuseAttributeErr, 
+      "This attribute is in use by another element, use cloneNode")
+  # Exceptions end
+  
+  if el.attributes == nil: el.attributes = @[]
+  return el.attributes.setNamedItem(newAttr)
+  
+proc setAttributeNodeNS*(el: PElement, newAttr: PAttr): PAttr =
+  ## Adds a new attribute node, if an attribute with the localName and 
+  ## namespaceURI of ``newAttr`` is present, it is replaced by the new one
+  ## and the replaced attribute is returned, otherwise ``nil`` is returned.
+  
+  # Check if newAttr is from this nodes document
+  if el.FOwnerDocument != newAttr.FOwnerDocument:
+    raise newException(EWrongDocumentErr, 
+      "This node belongs to a different document, use importNode.")
+        
+  if newAttr.FOwnerElement != nil:
+    raise newException(EInuseAttributeErr, 
+      "This attribute is in use by another element, use cloneNode")
+  # Exceptions end
+  
+  if el.attributes == nil: el.attributes = @[]
+  return el.attributes.setNamedItemNS(newAttr)
+
+proc setAttribute*(el: PElement, name: string, value: string) =
+  ## Adds a new attribute, as specified by ``name`` and ``value``
+  ## If an attribute with that name is already present in the element, its 
+  ## value is changed to be that of the value parameter
+  ## Raises the EInvalidCharacterErr if the specified ``name`` contains 
+  ## illegal characters
+  var AttrNode = el.FOwnerDocument.createAttribute(name)
+  # Check if name contains illegal characters
+  if illegalChars in name:
+    raise newException(EInvalidCharacterErr, "Invalid character")
+    
+  discard el.setAttributeNode(AttrNode)
+  # Set the info later, the setAttributeNode checks
+  # if FOwnerElement is nil, and if it isn't it raises an exception
+  AttrNode.FOwnerElement = el
+  AttrNode.FSpecified = True
+  AttrNode.value = value
+  
+proc setAttributeNS*(el: PElement, namespaceURI, localName, value: string) =
+  ## Adds a new attribute, as specified by ``namespaceURI``, ``localName`` 
+  ## and ``value``.
+  
+  # Check if name contains illegal characters
+  if illegalChars in namespaceURI or illegalChars in localName:
+    raise newException(EInvalidCharacterErr, "Invalid character")
+    
+  var AttrNode = el.FOwnerDocument.createAttributeNS(namespaceURI, localName)
+    
+  discard el.setAttributeNodeNS(AttrNode)
+  # Set the info later, the setAttributeNode checks
+  # if FOwnerElement is nil, and if it isn't it raises an exception
+  AttrNode.FOwnerElement = el
+  AttrNode.FSpecified = True
+  AttrNode.value = value
+
+# Text  
+proc splitData*(TextNode: PText, offset: int): PText =
+  ## Breaks this node into two nodes at the specified offset, 
+  ## keeping both in the tree as siblings.
+
+  # TODO - need insert(seq[T])
+
+# ProcessingInstruction
+proc target*(PI: PProcessingInstruction): string = 
+  return PI.FTarget
+
+    
+# --Other stuff--
+# Writer
+proc nodeToXml(n: PNode, indent: int = 0): string =
+  result = repeatChar(indent, ' ') & "<" & n.nodeName
+  for i in items(n.Attributes):
+    result.add(" " & i.name & "=\"" & i.value & "\"")
+  
+  if n.childNodes.len() == 0:
+    result.add("/>") # No idea why this doesn't need a \n :O
+  else:
+    # End the beginning of this tag
+    result.add(">\n")
+    for i in items(n.childNodes):
+      case i.nodeType
+      of ElementNode:
+        result.add(nodeToXml(i, indent + 2))
+      of TextNode:
+        result.add(repeatChar(indent * 2, ' '))
+        result.add(i.nodeValue)
+      of CDataSectionNode:
+        result.add(repeatChar(indent * 2, ' '))
+        result.add("<![CDATA[" & i.nodeValue & "]]>")
+      of ProcessingInstructionNode:
+        result.add(repeatChar(indent * 2, ' '))
+        result.add("<?" & PProcessingInstruction(i).target & " " &
+                          PProcessingInstruction(i).data & " ?>")
+      of CommentNode:
+        result.add(repeatChar(indent * 2, ' '))
+        result.add("<!-- " & i.nodeValue & " -->")
+      else:
+        continue
+      result.add("\n")
+    # Add the ending tag - </tag>
+    result.add(repeatChar(indent, ' ') & "</" & n.nodeName & ">")
+
+proc `$`*(doc: PDocument): string =
+  ## Converts a PDocument object into a string representation of it's XML
+  result = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
+  result.add(nodeToXml(doc.documentElement))
diff --git a/lib/pure/xmldomparser.nim b/lib/pure/xmldomparser.nim
new file mode 100644
index 000000000..90d4d85b1
--- /dev/null
+++ b/lib/pure/xmldomparser.nim
@@ -0,0 +1,110 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2010 Dominik Picheta
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+import xmldom, os, streams, parsexml, strutils
+
+## This module parses a XML Document into a XML DOM Document representation.
+
+#XMLDom's Parser - Turns XML into a Document
+
+type
+  #Parsing errors
+  EMismatchedTag* = object of E_Base ## Raised when a tag is not properly closed
+
+template newException(exceptn, message: expr): expr =
+  block: # open a new scope
+    var
+      e: ref exceptn
+    new(e)
+    e.msg = message
+    e
+    
+proc parseText(x: var TXmlParser, doc: var PDocument): PText =
+  result = doc.createTextNode(x.charData())
+
+proc parseElement(x: var TXmlParser, doc: var PDocument): PElement =
+  var n = doc.createElement("")
+
+  while True:
+    case x.kind()
+    of xmlEof:
+      break
+    of xmlElementStart:
+      if n.tagName() != "":
+        n.appendChild(parseElement(x, doc))
+      else:
+        n = doc.createElement(x.elementName)
+    of xmlElementOpen:
+      if n.tagName() != "":
+        n.appendChild(parseElement(x, doc))
+      else:
+        if x.elementName.contains(':'):
+          #TODO: NamespaceURI
+          n = doc.createElementNS("nil", x.elementName)
+        else:  
+          n = doc.createElement(x.elementName)
+        
+    of xmlElementEnd:
+      if x.elementName == n.nodeName:
+        return n
+      else: #The wrong element is ended
+        raise newException(EMismatchedTag, "Mismatched tag at line " & 
+          $x.getLine() & " column " & $x.getColumn)
+      
+    of xmlCharData:
+      n.appendChild(parseText(x, doc))
+    of xmlAttribute:
+      if x.attrKey.contains(':'):
+        #TODO: NamespaceURI
+        n.setAttributeNS("nil", x.attrKey, x.attrValue)
+      else:
+        n.setAttribute(x.attrKey, x.attrValue)
+    of xmlCData:
+      n.appendChild(doc.createCDATASection(x.charData()))
+    of xmlComment:
+      n.appendChild(doc.createComment(x.charData()))
+    of xmlPI:
+      n.appendChild(doc.createProcessingInstruction(x.PIName(), x.PIRest()))
+    else:
+      # echo(x.kind()) # XXX do nothing here!?
+    x.next()
+
+  raise newException(EMismatchedTag, 
+    "Mismatched tag at line " & $x.getLine() & " column " & $x.getColumn)
+    
+proc loadXML*(path: string): PDocument =
+  ## Loads and parses XML from file specified by ``path``, and returns 
+  ## a ``PDocument``
+  
+  var s = newFileStream(path, fmRead)
+  if s == nil: raise newException(EIO, "Unable to read file " & path)
+
+  var x: TXmlParser
+  open(x, s, path, {reportComments})
+  
+  var XmlDoc: PDocument
+  var DOM: PDOMImplementation = getDOM()
+  
+  while True:
+    x.next()
+    case x.kind()
+    of xmlEof:
+      break
+    of xmlElementStart, xmlElementOpen:
+      var el: PElement = parseElement(x, XmlDoc)
+      XmlDoc = dom.createDocument(el)
+    else:
+      # echo(x.kind())
+
+  return XmlDoc
+
+
+when isMainModule:
+  var xml = loadXML(r"C:\Users\Dominik\Desktop\Code\Nimrod\xmldom\test.xml")
+  echo($xml)
diff --git a/web/news.txt b/web/news.txt
index cea6bc124..096ee8ba9 100755
--- a/web/news.txt
+++ b/web/news.txt
@@ -34,6 +34,8 @@ Additions
 - Added ``httpclient`` module.
 - Added ``parseutils`` module.
 - Added ``unidecode`` module.
+- Added ``xmldom`` module.
+- Added ``xmldomparser`` module.
 - Many wrappers now do not contain redundant name prefixes (like ``GTK_``,
   ``lua``). The new wrappers are available in ``lib/newwrap``. Change
   your configuration file to use these.
diff --git a/web/nimrod.ini b/web/nimrod.ini
index af2d0c4b7..1cb494f9c 100755
--- a/web/nimrod.ini
+++ b/web/nimrod.ini
@@ -30,9 +30,9 @@ srcdoc: "pure/parsecfg;pure/parsexml;pure/parsecsv;pure/parsesql"
 srcdoc: "pure/streams;pure/terminal;pure/cgi;impure/web;pure/unicode"
 srcdoc: "impure/zipfiles;pure/xmlgen;pure/macros;pure/parseutils;pure/browsers"
 srcdoc: "impure/db_postgres;impure/db_mysql;pure/httpserver;pure/httpclient"
-srcdoc: "pure/ropes"
+srcdoc: "pure/ropes;pure/unidecode/unidecode;pure/xmldom;pure/xmldomparser"
 
-webdoc: "wrappers/libcurl;pure/md5;wrappers/mysql"
+webdoc: "wrappers/libcurl;pure/md5;wrappers/mysql;wrappers/iup"
 webdoc: "wrappers/sqlite3;wrappers/python;wrappers/tcl"
 
 webdoc: "posix/posix;wrappers/odbcsql;impure/dialogs"