1.1 --- a/libxml2dom/events.py Fri Apr 06 18:38:22 2007 +0000
1.2 +++ b/libxml2dom/events.py Fri Apr 06 21:45:01 2007 +0000
1.3 @@ -3,6 +3,7 @@
1.4 """
1.5 DOM Level 3 Events support, with SVG Tiny 1.2 implementation additions.
1.6 See: http://www.w3.org/TR/DOM-Level-3-Events/events.html
1.7 +See: http://www.w3.org/TR/xml-events/
1.8
1.9 Copyright (C) 2007 Paul Boddie <paul@boddie.org.uk>
1.10
1.11 @@ -49,7 +50,11 @@
1.12
1.13 class Event:
1.14
1.15 - "An event class."
1.16 + """
1.17 + An event class.
1.18 + See: http://www.w3.org/TR/SVGMobile12/svgudom.html#events__Event
1.19 + See: http://www.w3.org/TR/DOM-Level-3-Events/events.html#Events-Event
1.20 + """
1.21
1.22 CAPTURING_PHASE = 1
1.23 AT_TARGET = 2
1.24 @@ -69,10 +74,15 @@
1.25
1.26 # DOM Level 3 Events:
1.27
1.28 - self.bubbles = None
1.29 - self.eventPhase = self.CAPTURING_PHASE
1.30 + self.bubbles = 1
1.31 + self.eventPhase = self.AT_TARGET # permits direct invocation of dispatchEvent
1.32 self.timeStamp = time.time()
1.33
1.34 + # Propagation flags:
1.35 +
1.36 + self.stop = 0
1.37 + self.stop_now = 0
1.38 +
1.39 def initEvent(self, eventTypeArg, canBubbleArg, cancelableArg):
1.40 self.initEventNS(None, eventTypeArg, canBubbleArg, cancelableArg)
1.41
1.42 @@ -86,10 +96,11 @@
1.43 self.defaultPrevented = 1
1.44
1.45 def stopPropagation(self):
1.46 - pass
1.47 + self.stop = 1
1.48
1.49 def stopImmediatePropagation(self):
1.50 - pass
1.51 + self.stop = 1
1.52 + self.stop_now = 1
1.53
1.54 class UIEvent(Event):
1.55
1.56 @@ -117,12 +128,39 @@
1.57
1.58 class EventTarget:
1.59
1.60 - "An event target class."
1.61 + """
1.62 + An event target class.
1.63 + See: http://www.w3.org/TR/SVGMobile12/svgudom.html#events__EventTarget
1.64 + See: http://www.w3.org/TR/DOM-Level-3-Events/events.html#Events-EventTarget
1.65 +
1.66 + The listeners for a node are accessed through the global object. This common
1.67 + collection is consequently accessed by all nodes in a document, meaning that
1.68 + distinct objects representing the same node can still obtain the set of
1.69 + listeners registered for that node. In contrast, any attempt to directly
1.70 + store listeners on particular objects would result in the specific object
1.71 + which registered the listeners holding the record of such objects, whereas
1.72 + other objects obtained independently for the same node would hold no such
1.73 + record.
1.74 + """
1.75
1.76 def addEventListener(self, type, listener, useCapture):
1.77 +
1.78 + """
1.79 + For the given event 'type', register the given 'listener' for events in
1.80 + the capture phase if 'useCapture' is a true value, or for events in the
1.81 + target and bubble phases otherwise.
1.82 + """
1.83 +
1.84 self.addEventListenerNS(None, type, listener, useCapture)
1.85
1.86 def addEventListenerNS(self, namespaceURI, type, listener, useCapture, group=None): # NOTE: group ignored
1.87 +
1.88 + """
1.89 + For the given 'namespaceURI' and event 'type', register the given
1.90 + 'listener' for events in the capture phase if 'useCapture' is a true
1.91 + value, or for events in the target and bubble phases otherwise.
1.92 + """
1.93 +
1.94 listeners = self.ownerDocument.global_.listeners
1.95 if not listeners.has_key(self):
1.96 listeners[self] = {}
1.97 @@ -131,18 +169,54 @@
1.98 listeners[self][(namespaceURI, type)].append((listener, useCapture))
1.99
1.100 def dispatchEvent(self, evt):
1.101 +
1.102 + "For this node, dispatch event 'evt' to the registered listeners."
1.103 +
1.104 listeners = self.ownerDocument.global_.listeners
1.105 if not evt.type:
1.106 raise EventException(EventException.UNSPECIFIED_EVENT_TYPE_ERR)
1.107 +
1.108 + # Determine the phase and define the current target (this node) for the
1.109 + # use of listeners.
1.110 +
1.111 + capturing = evt.eventPhase == evt.CAPTURING_PHASE
1.112 + evt.currentTarget = self
1.113 +
1.114 # Dispatch on namespaceURI, type.
1.115 +
1.116 for listener, useCapture in listeners.get(self, {}).get((evt.namespaceURI, evt.type), []):
1.117 - listener.handleEvent(evt)
1.118 +
1.119 + # Detect requests to stop propagation immediately.
1.120 +
1.121 + if evt.stop_now:
1.122 + break
1.123 +
1.124 + # Dispatch the event to the appropriate listeners according to the
1.125 + # phase.
1.126 +
1.127 + if capturing and useCapture or not capturing and not useCapture:
1.128 + listener.handleEvent(evt)
1.129 +
1.130 return evt.defaultPrevented
1.131
1.132 def removeEventListener(self, type, listener, useCapture):
1.133 +
1.134 + """
1.135 + For the given event 'type', deregister the given 'listener' for events
1.136 + in the capture phase if 'useCapture' is a true value, or for events in
1.137 + the target and bubble phases otherwise.
1.138 + """
1.139 +
1.140 self.removeEventListenerNS(None, type, listener, useCapture)
1.141
1.142 def removeEventListenerNS(self, namespaceURI, type, listener, useCapture):
1.143 +
1.144 + """
1.145 + For the given 'namespaceURI' and event 'type', deregister the given
1.146 + 'listener' for events in the capture phase if 'useCapture' is a true
1.147 + value, or for events in the target and bubble phases otherwise.
1.148 + """
1.149 +
1.150 listeners = self.ownerDocument.global_.listeners
1.151 if listeners.has_key(self) and listeners[self].has_key((namespaceURI, type)):
1.152 try:
1.153 @@ -150,4 +224,46 @@
1.154 except ValueError:
1.155 pass
1.156
1.157 +# NOTE: The specification doesn't say much about the event system, but we will
1.158 +# NOTE: provide a class to manage the different phases. This is mixed into the
1.159 +# NOTE: SVGDocument class (and potentially other classes in future).
1.160 +
1.161 +class EventSystem:
1.162 +
1.163 + "An event system which manages the different DOM event flow phases."
1.164 +
1.165 + def sendEventToTarget(self, evt, target):
1.166 +
1.167 + "Send event 'evt' to the specified 'target' element."
1.168 +
1.169 + # Determine the path of the event.
1.170 +
1.171 + bubble_route = target.xpath("ancestor::*")
1.172 + capture_route = bubble_route[:]
1.173 + capture_route.reverse()
1.174 +
1.175 + # Initialise the target and execute the capture phase.
1.176 +
1.177 + evt.target = target
1.178 + evt.eventPhase = evt.CAPTURING_PHASE
1.179 + for element in capture_route:
1.180 + if evt.stop:
1.181 + break
1.182 + element.dispatchEvent(evt)
1.183 +
1.184 + # Execute the target phase.
1.185 +
1.186 + evt.eventPhase = evt.AT_TARGET
1.187 + if not evt.stop:
1.188 + target.dispatchEvent(evt)
1.189 +
1.190 + # Execute the bubble phase, if appropriate.
1.191 +
1.192 + evt.eventPhase = evt.BUBBLING_PHASE
1.193 + if evt.bubbles:
1.194 + for element in bubble_route:
1.195 + if evt.stop:
1.196 + break
1.197 + element.dispatchEvent(evt)
1.198 +
1.199 # vim: tabstop=4 expandtab shiftwidth=4