# MWDOM core # Copyright (c)2001, MoonWolf. All right reserved. require "observer" require "singleton" module MWDOM def escape(text) text = text.gsub(/&/, "&") text.gsub!(//, ">") text.gsub!(/'/, "'") text.gsub!(/"/, """) text end module_function :escape def localName(qualifiedName) if qualifiedName=~/:/ $' else qualifiedName end end module_function :localName #class DOMString < String; end #class DOMTimeStamp < Bignum; end INDEX_SIZE_ERR = 1 DOMSTRING_SIZE_ERR = 2 HIERARCHY_REQUEST_ERR = 3 WRONG_DOCUMENT_ERR = 4 INVALID_CHARACTER_ERR = 5 NO_DATA_ALLOWED_ERR = 6 NO_MODIFICATION_ALLOWED_ERR = 7 NOT_FOUND_ERR = 8 NOT_SUPPORTED_ERR = 9 INUSE_ATTRIBUTE_ERR = 10 INVALID_STATE_ERR = 11 SYNTAX_ERR = 12 INVALID_MODIFICATION_ERR = 13 NAMESPACE_ERR = 14 INVALID_ACCESS_ERR = 15 class DOMException < Exception MSG = [ "INDEX_SIZE_ERR", "DOMSTRING_SIZE_ERR", "HIERARCHY_REQUEST_ERR", "WRONG_DOCUMENT_ERR", "INVALID_CHARACTER_ERR", "NO_DATA_ALLOWED_ERR", "NO_MODIFICATION_ALLOWED_ERR", "NOT_FOUND_ERR", "NOT_SUPPORTED_ERR", "INUSE_ATTRIBUTE_ERR", "INVALID_STATE_ERR", "SYNTAX_ERR", "INVALID_MODIFICATION_ERR", "NAMESPACE_ERR", "INVALID_ACCESS_ERR", ] def initialize(code) @code = code end attr_reader :code def to_s MSG[@code-1] end end class DOMImplementation include Singleton IMPLEMENT = { "Core" => ["2.0"], "XML" => ["2.0"], } # def hasFeature(feature, version) ver=IMPLEMENT[feature] if ver and ver.include?(version) true else false end end def createDocumentType(qualifiedName, publicId, systemId) DocumentType.new(qualifiedName, publicId, systemId) end def createDocument(namespaceURI, qualifiedName, doctype) document = Document.new(self) if doctype document.appendChild(doctype) end document end end class NodeList def initialize @nodelist = Array.new end attr_accessor :nodelist def to_s @nodelist.to_s end #Observer def update(node) @nodelist.delete(node) node.delete_observer(self) end #DOM def item(index) if index>=0 and index<@nodelist.size @nodelist[index] else nil end end def length @nodelist.length end end class NamedNodeMap def initialize(doc) @ownerDocument = doc @hash = {} end attr_reader :hash def to_s s = "" @hash.each {|k,v| s << v.to_s } s end private def nameNS(ns,name) if ns name=ns+":"+name else name end end #DOM public attr_reader :ownerDocument def getNamedItem(name) @hash[name] end def setNamedItem(arg) if not @ownerDocument.equal?(arg.ownerDocument) raise DOMException.new(INUSE_ATTRIBUTE_ERR) end name = nameNS(arg.namespaceURI,arg.nodeName.dup) if old=@hash[name] @hash[name]=arg old else @hash[name]=arg nil end end def removeNamedItem(name) @hash.delete(name) end def item(index) attr=@hash.to_a[index] if attr attr[1] else nil end end def length @hash.length end def getNamedItemNS(namespaceURI, localName) name = nameNS(namespaceURI, localName) @hash[name] end alias setNamedItemNS setNamedItem def removeNamedItemNS(namespaceURI, localName) name = nameNS(namespaceURI, localName) @hash.delete(name) end end # NodeType ELEMENT_Node = 1 ATTRIBUTE_Node = 2 TEXT_Node = 3 CDATA_SECTION_Node = 4 ENTITY_REFERENCE_Node = 5 ENTITY_Node = 6 PROCESSING_INSTRUCTION_Node = 7 COMMENT_Node = 8 DOCUMENT_Node = 9 DOCUMENT_TYPE_Node = 10 DOCUMENT_FRAGMENT_Node = 11 NOTATION_Node = 12 module Node include Observable def initialize(ownerDocument) @ownerDocument = ownerDocument @nodeName = nil @nodeValue = nil @parentNode = nil @childNodes = NodeList.new @attributes = NamedNodeMap.new(ownerDocument) @prefix = nil end #DOM def nodeName @nodeName end def nodeValue @nodeValue end def nodeValue=(val) @nodeValue=val end def nodeType raise "Attr#nodeType" end attr_accessor :parentNode attr_reader :childNodes def firstChild @childNodes.nodelist.first end def lastChild @childNodes.nodelist.last end def previousSibling if @parentNode and idx=@parentNode.childNodes.nodelist.index(self) @parentNode.childNodes.item(idx-1) end end def nextSibling if @parentNode and idx=@parentNode.childNodes.nodelist.index(self) @parentNode.childNodes.item(idx+1) end end def attributes nil end def ownerDocument @ownerDocument end def insertBefore(newChild, refChild) if refChild==nil return appendChild(newChild) end idx=@childNodes.nodelist.index(refChild) raise DOMException.new(NOT_FOUND_ERR) unless idx if newChild.nodeType==DOCUMENT_FRAGMENT_Node newChild.childNodes.nodelist.each {|node| @childNodes.nodelist[idx,0] = node; node.parentNode=self; idx += 1 } else if parent=newChild.parentNode parent.removeChild(newChild) end @childNodes.nodelist[idx,0] = newChild newChild.parentNode=self end end def replaceChild(newChild, oldChild) idx=@childNodes.nodelist.index(oldChild) removeChild(oldChild) if parent=newChild.parentNode parent.removeChild(newChild) end @childNodes.nodelist[idx,0]=newChild @childNodes.nodelist << newChild newChild.parentNode=self end def removeChild(oldChild) retval=@childNodes.nodelist.delete(oldChild) oldChild.changed oldChild.notify_observers(oldChild) oldChild.parentNode=nil return retval if retval raise DOMException.new(NOT_FOUND_ERR) end def appendChild(newChild) if newChild.nodeType==DOCUMENT_FRAGMENT_Node newChild.childNodes.nodelist.each {|node| @childNodes.nodelist << node; node.parentNode=self; } else if parent=newChild.parentNode parent.removeChild(newChild) end @childNodes.nodelist << newChild newChild.parentNode=self end end def hasChildNodes if @childNodes and @childNodes.length>0 true else false end end def cloneNode(deep) raise "Not Implemented " + self.type.to_s end def normalize nodelist = @childNodes.nodelist idx=0 while idx"+ @childNodes.to_s end #DOM Node def nodeName "#document" end def cloneNode(deep) retnode = self.type.new(@implementation) if deep @childNodes.nodelist.each {|node| retnode.appendChild(node.cloneNode(true)) } end retnode end #DOM def nodeType DOCUMENT_Node end def doctype @childNodes.nodelist.each {|node| return node if node.nodeType==DOCUMENT_TYPE_Node } return nil end def implementation @implementation end def documentElement @childNodes.nodelist.each {|node| return node if node.nodeType==ELEMENT_Node } return nil end def createElement(tagName) Element.new(self,tagName,nil) end def createDocumentFragment() DocumentFragment.new() end def createTextNode(data) Text.new(self,data) end def createComment(data) Comment.new(self,data) end def createCDATASection(data) CDATASection.new(self,data) end def createProcessingInstruction(target, data) ProcessingInstruction.new(self,target,data) end def createAttribute(name) Attr.new(self,name,nil,nil) end def createEntityReference(name) EntityReference.new(self,name) end def getElementsByTagName(tagname) retval = NodeList.new nodelist = retval.nodelist @childNodes.nodelist.each {|node| if node.nodeType==ELEMENT_Node if tagname=='*' or node.nodeName==tagname nodelist << node node.add_observer(retval) end node._getElementsByTagName(tagname).each {|node| nodelist << node node.add_observer(retval) } end } retval end def importNode(importedNode, deep) raise "Not Implemented" end def createElementNS(namespaceURI, qualifiedName) Element.new(self,qualifiedName,namespaceURI) end def createAttributeNS(namespaceURI, qualifiedName) Attr.new(self,qualifiedName,nil,namespaceURI) end def getElementsByTagNameNS(namespaceURI, localName) raise "Not Implemented" end def getElementById(elementId) raise "Not Implemented" end end class Element include Node def initialize(doc,qualifiedName,namespaceURI) super(doc) @namespaceURI = namespaceURI @nodeName = qualifiedName @ownerDocument = doc end def to_s "<{#{@namespaceURI}}:#{@nodeName}" + ( if @attributes.length==0 "" else @attributes.hash.collect {|key,attr| " "+attr.to_s }.to_s end ) + ">" + @childNodes.to_s + "" end def attributes=(attrs) @attributes=attrs end #DOM Nodes attr_reader :attributes def cloneNode(deep) retnode = self.type.new(@ownerDocument,@nodeName.dup) @attributes.hash.each {|key,attr| retnode.setAttributeNode(attr.cloneNode(true)) } if deep @childNodes.nodelist.each {|node| retnode.appendChild(node.cloneNode(true)) } end retnode end attr_reader :namespaceURI def prefix @nodeName=~/:/ $` end def prefix=(val) if @nodeName=~/:/ @nodeName=val+":"+$' else @nodeName=val+":"+@nodeName end end def localName @nodeName=~/:/ $' || @nodeName end #DOM def nodeType ELEMENT_Node end def tagName @nodeName end def getAttribute(name) if attr = @attributes.getNamedItem(name) attr.nodeValue else "" end end def setAttribute(name, value) if attr=@attributes.getNamedItem(name) attr.value = value else @attributes.setNamedItem(Attr.new(@ownerDocument,name,value)) end end def removeAttribute(name) @attributes.removeNamedItem(name) end def getAttributeNode(name) @attrbitues.getNamedItem(name) or "" end def setAttributeNode(newAttr) attr=@attributes.getNamedItem(newAttr.nodeName) @attributes.setNamedItem(newAttr) attr end def removeAttributeNode(oldAttr) name=oldAttr.name if oldAttr.id==@attributes.getNamedItem(name).id @attributes.removeNamedItem(name) else raise DOMException.new(NOT_FOUND_ERR) end end def getElementsByTagName(tagname) retval = NodeList.new nodelist = retval.nodelist @childNodes.nodelist.each {|node| if node.nodeType==ELEMENT_Node if tagname=='*' or node.nodeName==tagname nodelist << node node.add_observer(retval) end node._getElementsByTagName(tagname).each {|node| nodelist << node node.add_observer(retval) } end } retval end def _getElementsByTagName(tagname) nodelist = [] @childNodes.nodelist.each {|node| if node.nodeType==ELEMENT_Node if tagname=='*' or node.nodeName==tagname nodelist << node end nodelist.concat node._getElementsByTagName(tagname) end } nodelist end def getAttributeNS(namespaceURI, localName) raise "Not Implemented" end def setAttributeNS(namespaceURI, qualifiedName, value) localName = MWDOM::localName(qualifiedName) if attr=@attributes.getNamedItemNS(namespaceURI,localName) attr.value = value else @attributes.setNamedItemNS(Attr.new(@ownerDocument,qualifiedName,value,namespaceURI)) end end def removeAttributeNS(namespaceURI, localName) raise "Not Implemented" end def getAttributeNodeNS(namespaceURI, localName) raise "Not Implemented" end def setAttributeNodeNS(newAttr) raise "Not Implemented" end def getElementsByTagNameNS(namespaceURI, localName) raise "Not Implemented" end def hasAttribute(name) raise "Not Implemented" end def hasAttributeNS(namespaceURI, localName) raise "Not Implemented" end end class CharacterData include Node def initialize(doc,data) super(doc) @nodeValue = data end #DOM Node def cloneNode(deep) self.type.new(@ownerDocument,@nodeValue.dup) end #DOM def data @nodeValue end def data=(val) @nodeValue=val end def length @nodeValue.length end def substringData(offset, count) if offset>=0 and offset<@nodeValue.length and count>=0 return @nodeValue[offset,count] end raise DOMException.new(INDEX_SIZE_ERR) end def appendData(arg) @nodeValue << arg end def insertData(offset, arg) if offset>=0 and offset<@nodeValue.length @nodeValue[offset,0]=arg return end raise DOMException.new(INDEX_SIZE_ERR) end def deleteData(offset, count) if offset>=0 and offset<@nodeValue.length and count>=0 @nodeValue[offset,count]='' return end raise DOMException.new(INDEX_SIZE_ERR) end def replaceData(offset, count, arg) if offset>=0 and offset<@nodeValue.length and count>=0 @nodeValue[offset,count]=arg return end raise DOMException.new(INDEX_SIZE_ERR) end end class Text < CharacterData def to_s MWDOM::escape(@nodeValue) end #DOM Node def nodeName "#text" end #DOM def nodeType TEXT_Node end def splitText(offset) raise "Not Implemented" end end class Comment < CharacterData def to_s "" end #DOM Node def nodeName "#comment" end #DOM def nodeType COMMENT_Node end end class CDATASection < Text def to_s "" end #DOM Node def nodeName "#cdata-section" end #DOM def nodeType CDATA_SECTION_Node end end class DocumentType include Node def initialize(qualifiedName, publicId, systemId) super(nil) @nodeName = qualifiedName @publicId = publicId @systemId = systemId end def to_s s = "" end #DOM Node def cloneNode(deep) self.type.new(@nodeName,@publicId,@systemId) end #DOM def nodeType DOCUMENT_TYPE_Node end def name @nodeName end def entities raise "Not Implemented" end def notations raise "Not Implemented" end attr_reader :publicId attr_reader :systemId def internalSubset raise "Not Implemented" end end class Notation include Node #DOM def nodeType NOTATION_Node end def publicId raise "Not Implemented" end def systemId raise "Not Implemented" end end class Entity include Node #DOM def nodeType ENTITY_Node end def publicId raise "Not Implemented" end def systemId raise "Not Implemented" end def notationName raise "Not Implemented" end end class EntityReference include Node def initialize(doc,name) super(doc) @nodeName = name end def to_s "&#{@nodeName};" end #DOM Node def cloneNode(deep) self.type.new(@ownerDocument,@nodeName.dup) end #DOM def nodeType ENTITY_REFERENCE_Node end end class ProcessingInstruction include Node def initialize(doc,target,data) super(doc) @nodeName = target @nodeValue = data end def to_s "" end #DOM Node def cloneNode(deep) self.type.new(@ownerDocument,@nodeName,@nodeValue) end #DOM def nodeType PROCESSING_INSTRUCTION_Node end def target @nodeName end def data @nodeValue end def data=(val) @nodeValue = val end end class DocumentFragment include Node def initialize() super(nil) @childNodes = NodeList.new end def to_s @childNodes.to_s end #DOM Node def nodeName "#document-fragment" end def cloneNode(deep) retnode = self.type.new() if deep @childNodes.nodelist.each {|node| retnode.appendChild(node.cloneNode(true)) } end retnode end #DOM def nodeType DOCUMENT_FRAGMENT_Node end end end