# HG changeset patch # User paulb # Date 1175884686 0 # Node ID 364d9889087da995bd1a39b4982e4eba7ed69e8a # Parent f572539743a166ec50121ef373433e853baad4d7 [project @ 2007-04-06 18:38:06 by paulb] Added getElementById and __hash__ to the Node class, the latter supporting event listeners in the events module. Expanded the events and svg modules to support event registration and dispatch, amongst other things. Updated release information. diff -r f572539743a1 -r 364d9889087d libxml2dom/__init__.py --- a/libxml2dom/__init__.py Fri Apr 06 18:37:16 2007 +0000 +++ b/libxml2dom/__init__.py Fri Apr 06 18:38:06 2007 +0000 @@ -20,7 +20,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ -__version__ = "0.4.2" +__version__ = "0.4.3" from libxml2dom.macrolib import * from libxml2dom.macrolib import \ @@ -30,6 +30,7 @@ toString as Node_toString, toStream as Node_toStream, \ toFile as Node_toFile import urllib # for parseURI in HTML mode +import xml.dom # for getElementById class Implementation(object): @@ -363,6 +364,14 @@ else: Node_removeChild(self._node, tmp) + def getElementById(self, identifier): + nodes = self.xpath(".//*[@xml:id='" + identifier.replace("'", "'") + "']", + namespaces={"xml" : xml.dom.XML_NAMESPACE}) + if nodes: + return nodes[0] + else: + return None + def getElementsByTagName(self, tagName): return self.xpath(".//" + tagName) @@ -413,6 +422,9 @@ def isSameNode(self, other): return self == other + def __hash__(self): + return hash(self.localName) + def __eq__(self, other): return isinstance(other, Node) and libxml2mod.xmlXPathCmpNodes(self._node, other._node) == 0 diff -r f572539743a1 -r 364d9889087d libxml2dom/events.py --- a/libxml2dom/events.py Fri Apr 06 18:37:16 2007 +0000 +++ b/libxml2dom/events.py Fri Apr 06 18:38:06 2007 +0000 @@ -21,6 +21,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ +import xml.dom import time XML_EVENTS_NAMESPACE = "http://www.w3.org/2001/xml-events" @@ -32,13 +33,19 @@ class DocumentEvent: - "An event interface supportable by documents." + """ + An event interface supportable by documents. + See: http://www.w3.org/TR/DOM-Level-3-Events/events.html#Events-DocumentEvent + """ def canDispatch(self, namespaceURI, type): - raise NotImplementedError, "canDispatch" + return namespaceURI is None and event_types.has_key(type) def createEvent(self, eventType): - raise NotImplementedError, "createEvent" + try: + return event_types[eventType]() + except KeyError: + raise xml.dom.DOMException(xml.dom.NOT_SUPPORTED_ERR) class Event: @@ -48,16 +55,15 @@ AT_TARGET = 2 BUBBLING_PHASE = 3 - def __init__(self, target, currentTarget): + def __init__(self): "Initialise the event." - self.target = target - self.currentTarget = currentTarget - self.defaultPrevented = 0 - # Initialised later: + self.target = None + self.currentTarget = None + self.defaultPrevented = 0 self.type = None self.namespaceURI = None @@ -85,26 +91,51 @@ def stopImmediatePropagation(self): pass +class UIEvent(Event): + + "A user interface event." + + def __init__(self): + Event.__init__(self) + self.detail = None + +class MouseEvent(UIEvent): + + "A mouse-related event." + + def __init__(self): + Event.__init__(self) + self.screenX, self.screenY, self.clientX, self.clientY, self.button = None, None, None, None, None + +# Event types registry. + +event_types = { + "Event" : Event, + "UIEvent" : UIEvent, + "MouseEvent" : MouseEvent + } + class EventTarget: "An event target class." - def __init__(self): - self.listeners = {} - def addEventListener(self, type, listener, useCapture): self.addEventListenerNS(None, type, listener, useCapture) - def addEventListenerNS(self, namespaceURI, type, listener, useCapture): - if not self.listeners.has_key((namespaceURI, type)): - self.listeners[(namespaceURI, type)] = [] - self.listeners[(namespaceURI, type)].append((listener, useCapture)) + def addEventListenerNS(self, namespaceURI, type, listener, useCapture, group=None): # NOTE: group ignored + listeners = self.ownerDocument.global_.listeners + if not listeners.has_key(self): + listeners[self] = {} + if not listeners[self].has_key((namespaceURI, type)): + listeners[self][(namespaceURI, type)] = [] + listeners[self][(namespaceURI, type)].append((listener, useCapture)) def dispatchEvent(self, evt): + listeners = self.ownerDocument.global_.listeners if not evt.type: raise EventException(EventException.UNSPECIFIED_EVENT_TYPE_ERR) # Dispatch on namespaceURI, type. - for listener in self.listeners.get((evt.namespaceURI, evt.type), []): + for listener, useCapture in listeners.get(self, {}).get((evt.namespaceURI, evt.type), []): listener.handleEvent(evt) return evt.defaultPrevented @@ -112,10 +143,10 @@ self.removeEventListenerNS(None, type, listener, useCapture) def removeEventListenerNS(self, namespaceURI, type, listener, useCapture): - if self.listeners.has_key((namespaceURI, type)): - listeners = self.listeners[(namespaceURI, type)] + listeners = self.ownerDocument.global_.listeners + if listeners.has_key(self) and listeners[self].has_key((namespaceURI, type)): try: - listeners.remove((listener, useCapture)) + listeners[self][(namespaceURI, type)].remove((listener, useCapture)) except ValueError: pass diff -r f572539743a1 -r 364d9889087d libxml2dom/svg.py --- a/libxml2dom/svg.py Fri Apr 06 18:37:16 2007 +0000 +++ b/libxml2dom/svg.py Fri Apr 06 18:38:06 2007 +0000 @@ -28,6 +28,7 @@ from libxml2dom.macrolib import \ createDocument as Node_createDocument import xml.dom +import urllib import math import re @@ -80,6 +81,9 @@ else: return libxml2dom.Implementation.get_node(self, _node, context_node) + def get_global(self, doc): + return SVGGlobal(doc) + # Convenience functions. def createSVGDocument(self): @@ -141,7 +145,23 @@ nextElementSibling = property(_nextElementSibling) previousElementSibling = property(_previousElementSibling) -class SVGGlobal: # Global, EventListenerInitializer2 +class EventListenerInitializer2: + + "An event listener initialisation interface." + + def initializeEventListeners(self, scriptElement): + pass + + def createEventListener(self, handlerElement): + pass + +class Global: + + "An empty global interface." + + pass + +class SVGGlobal(Global, EventListenerInitializer2): "An SVG global." @@ -151,6 +171,10 @@ self.document = document + # Listener management. + + self.listeners = {} + def createConnection(self): raise NotImplementedError, "createConnection" @@ -161,10 +185,16 @@ raise NotImplementedError, "gotoLocation" def binaryToString(self, octets, encoding): - raise NotImplementedError, "binaryToString" + try: + return unicode(octets, encoding) + except UnicodeDecodeError, exc: + raise GlobalException(GlobalException.ENCODING_ERR) def stringToBinary(self, data, encoding): - raise NotImplementedError, "stringToBinary" + try: + return data.encode(encoding) + except UnicodeEncodeError, exc: + raise GlobalException(GlobalException.ENCODING_ERR) def getURL(self, iri, callback): @@ -183,8 +213,27 @@ finally: f.close() - def postURL(self, iri, data, callback, type, encoding): - raise NotImplementedError, "postURL" + def postURL(self, iri, data, callback, type=None, encoding=None): + + # NOTE: Not asynchronous. + # NOTE: The urlopen function may not support IRIs. + # No exceptions are supposed to be raised, which is a bit nasty. + + opener = urllib.URLopener() + opener.addheader("Content-Type", type or "text/plain") + if encoding: + opener.addheader("Content-Encoding", encoding) + f = opener.open(iri, data) + try: + try: + content = f.read() + contentType = f.headers["Content-Type"] + callback.operationComplete(AsyncURLStatus(1, contentType, content)) + except: + callback.operationComplete(AsyncURLStatus(0, None, None)) + finally: + f.close() + opener.close() def parseXML(self, data, contextDoc): doc = parseString(data) @@ -641,7 +690,7 @@ namespaces["svg"] = SVG_NAMESPACE return libxml2dom.Node.xpath(self, expr, variables, namespaces) -class SVGDocument(libxml2dom._Document, SVGNode, DocumentEvent, EventTarget): +class SVGDocument(libxml2dom._Document, SVGNode, EventTarget, DocumentEvent): # NOTE: The latter is from DOM Level 3 Events. "An SVG-specific document node." @@ -653,12 +702,15 @@ """ libxml2dom._Document.__init__(self, node, impl) - self.global_ = SVGGlobal(self) # parent + self.global_ = self.impl.get_global(self) # parent class SVGElement(SVGNode, EventTarget, TraitAccess, ElementTraversal): # NOTE: SVGNode instead of Element. "An SVG-specific element." + def __init__(self, *args, **kw): + SVGNode.__init__(self, *args, **kw) + def _id(self): return self.getAttribute("id") @@ -763,6 +815,27 @@ currentTranslate = property(_currentTranslate) viewport = property(_viewport) +# Event handler initialisation. + +def initialiseEvents(doc): + + """ + See: http://www.w3.org/TR/SVGMobile12/svgudom.html#svg__EventListenerInitializer2 + """ + + for script in doc.xpath("//svg:script"): + doc.global_.initializeEventListeners(script) + for handler in doc.xpath("//svg:handler"): + listener = doc.global_.createEventListener(handler) + + # NOTE: May need to have the event type understood and correctly parameterised. + + handler.parentNode.addEventListener( + handler.getAttributeNS(libxml2dom.events.XML_EVENTS_NAMESPACE, "event"), + listener, + 0 + ) + # Utility functions. createDocument = libxml2dom.createDocument @@ -771,17 +844,25 @@ def createSVGDocument(): return default_impl.createSVGDocument() -def parse(stream_or_string, html=0, htmlencoding=None): - return libxml2dom.parse(stream_or_string, html, htmlencoding, default_impl) +def parse(stream_or_string, html=0, htmlencoding=None, impl=None): + doc = libxml2dom.parse(stream_or_string, html, htmlencoding, impl or default_impl) + initialiseEvents(doc) + return doc -def parseFile(filename, html=0, htmlencoding=None): - return libxml2dom.parseFile(filename, html, htmlencoding, default_impl) +def parseFile(filename, html=0, htmlencoding=None, impl=None): + doc = libxml2dom.parseFile(filename, html, htmlencoding, impl or default_impl) + initialiseEvents(doc) + return doc -def parseString(s, html=0, htmlencoding=None): - return libxml2dom.parseString(s, html, htmlencoding, default_impl) +def parseString(s, html=0, htmlencoding=None, impl=None): + doc = libxml2dom.parseString(s, html, htmlencoding, impl or default_impl) + initialiseEvents(doc) + return doc -def parseURI(uri, html=0, htmlencoding=None): - return libxml2dom.parseURI(uri, html, htmlencoding, default_impl) +def parseURI(uri, html=0, htmlencoding=None, impl=None): + doc = libxml2dom.parseURI(uri, html, htmlencoding, impl or default_impl) + initialiseEvents(doc) + return doc # Single instance of the implementation.