[pypy-svn] r34544 - in pypy/dist/pypy/translator/js/modules: . test

guido at codespeak.net guido at codespeak.net
Mon Nov 13 09:20:47 CET 2006


Author: guido
Date: Mon Nov 13 09:20:45 2006
New Revision: 34544

Modified:
   pypy/dist/pypy/translator/js/modules/dom.py
   pypy/dist/pypy/translator/js/modules/test/test_dom.py
Log:
More shuffling and cleanups, added basic support for events.


Modified: pypy/dist/pypy/translator/js/modules/dom.py
==============================================================================
--- pypy/dist/pypy/translator/js/modules/dom.py	(original)
+++ pypy/dist/pypy/translator/js/modules/dom.py	Mon Nov 13 09:20:45 2006
@@ -1,11 +1,11 @@
 
 """Document Object Model support
 
-    this provides a mock browser API, both the standard DOM l. 1 and 2 stuff as
-    the browser-specific (level 0) additions
+    this provides a mock browser API, both the standard DOM level 2 stuff as
+    the browser-specific additions
 
     note that the API is not and will not be complete: more exotic features 
-    will most probably not behave as expected
+    will most probably not behave as expected, or are not implemented at all
     
     http://www.w3.org/DOM/ - main standard
     http://www.w3schools.com/dhtml/dhtml_dom.asp - more informal stuff
@@ -45,58 +45,114 @@
         original = getattr(other, '_original', other)
         return original is self._original
 
-def _quote_html(text):
-    for char, e in [('&', 'amp'), ('<', 'lt'), ('>', 'gt'), ('"', 'quot'),
-                    ("'", 'apos')]:
-        text = text.replace(char, '&%s;' % (e,))
-    return text
+class Element(Node):
+    nodeType = 1
 
-_singletons = ['link', 'meta']
-def _serialize_html(node):
-    ret = []
-    if node.nodeType == 1:
-        nodeName = getattr(node, '_original', node).nodeName
-        ret += ['<', nodeName]
-        if len(node.attributes):
-            for aname in node.attributes.keys():
-                attr = node.attributes[aname]
-                ret.append(' %s="%s"' % (attr.nodeName, attr.nodeValue))
-        if len(node.childNodes) or nodeName not in _singletons:
-            ret.append('>')
-            for child in node.childNodes:
-                if child.nodeType == 1:
-                    ret.append(_serialize_html(child))
-                else:
-                    ret.append(_quote_html(child.nodeValue))
-            ret += ['</', nodeName, '>']
-        else:
-            ret.append(' />')
-    return ''.join(ret)
+class Attribute(Node):
+    nodeType = 2
 
-class HTMLNode(Node):
-    def getElementsByTagName(self, name):
-        name = name.lower()
-        return self.__getattr__('getElementsByTagName')(name)
+class Text(Node):
+    nodeType = 3
 
-    def _get_innerHTML(self):
-        ret = []
-        for child in self.childNodes:
-            ret.append(_serialize_html(child))
-        return ''.join(ret)
+class Document(Node):
+    nodeType = 9
+    
+    def __init__(self, docnode=None):
+        super(Document, self).__init__(docnode)
+        self._original = docnode
 
-    def _set_innerHTML(self, html):
-        dom = minidom.parseString('<doc>%s</doc>' % (html,))
-        while self.childNodes:
-            self.removeChild(self.lastChild)
-        for child in dom.documentElement.childNodes:
-            child = self.ownerDocument.importNode(child, True)
-            self.appendChild(child)
-        del dom
+    def createEvent(self, group=''):
+        """create an event
 
-    innerHTML = property(_get_innerHTML, _set_innerHTML)
+            note that the group argument is ignored
+        """
+        if group in ('KeyboardEvent', 'KeyboardEvents'):
+            return KeyEvent()
+        elif group in ('MouseEvent', 'MouseEvents'):
+            return MouseEvent()
+        return Event()
 
-class Element(Node):
-    nodeType = 1
+# the standard DOM stuff that doesn't directly deal with XML
+#   note that we're mimicking the standard (Mozilla) APIs, so things tested
+#   against this code may not work in Internet Explorer
+
+# XXX note that we store the events on the wrapped minidom node to avoid losing
+# them on re-wrapping
+class EventTarget(BasicExternal): # XXX mixin... is the super correct?!?
+    def addEventListener(self, type, listener, useCapture):
+        if not hasattr(self._original, '_events'):
+            self._original._events = []
+        # XXX note that useCapture is ignored...
+        self._original._events.append((type, listener, useCapture))
+
+    def dispatchEvent(self, event):
+        if event._cancelled:
+            return
+        event.currentTarget = self
+        if event.target is None:
+            event.target = self
+        if event.originalTarget is None:
+            event.originalTarget = self
+        if hasattr(self._original, '_events'):
+            for etype, handler, capture in self._original._events:
+                if etype == event.type:
+                    handler(event)
+        if event._cancelled or event.cancelBubble:
+            return
+        parent = getattr(self, 'parentNode', None)
+        if parent is not None:
+            parent.dispatchEvent(event)
+
+    def removeEventListener(self, type, listener, useCapture):
+        if not hasattr(self._original, '_events'):
+            raise ValueError('no registration for listener')
+        filtered = []
+        for data in self._original._events:
+            if data != (type, listener, useCapture):
+                filtered.append(data)
+        if filtered == self._original._events:
+            raise ValueError('no registration for listener')
+        self._original._events = filtered
+
+class Event(BasicExternal):
+    def initEvent(self, type, bubbles, cancelable):
+        self.type = type
+        self.cancelBubble = not bubbles
+        self.cancelable = cancelable
+        self.target = None
+        self.currentTarget = None
+        self.originalTarget = None
+        self._cancelled = False
+
+    def preventDefault(self):
+        if not self.cancelable:
+            raise TypeError('event can not be canceled')
+        self._cancelled = True
+
+    def stopPropagation(self):
+        self.cancelBubble = True
+
+class KeyEvent(Event):
+    pass
+
+class MouseEvent(Event):
+    pass
+
+class Style(BasicExternal):
+    def __getattr__(self, name):
+        if name not in self._fields:
+            raise AttributeError, name
+        return None
+
+# HTML DOM
+
+class HTMLNode(Node, EventTarget):
+    def getElementsByTagName(self, name):
+        name = name.lower()
+        return self.__getattr__('getElementsByTagName')(name)
+
+    def __str__(self):
+        return '<%s %s>' % (self.__class__.__name__, self.nodeName)
 
 class HTMLElement(HTMLNode, Element):
     id = ''
@@ -115,31 +171,42 @@
         return self._original.nodeName.upper()
     nodeName = property(_nodeName)
 
-class Attribute(Node):
-    nodeType = 2
+    def _get_innerHTML(self):
+        ret = []
+        for child in self.childNodes:
+            ret.append(_serialize_html(child))
+        return ''.join(ret)
 
-class Text(Node):
-    nodeType = 3
+    def _set_innerHTML(self, html):
+        dom = minidom.parseString('<doc>%s</doc>' % (html,))
+        while self.childNodes:
+            self.removeChild(self.lastChild)
+        for child in dom.documentElement.childNodes:
+            child = self.ownerDocument.importNode(child, True)
+            self._original.appendChild(child)
+        del dom
 
-class Document(HTMLNode):
-    nodeType = 9
-    
-    def __init__(self, docnode=None):
-        super(Document, self).__init__(docnode)
-        self._original = docnode
+    innerHTML = property(_get_innerHTML, _set_innerHTML)
 
+class HTMLDocument(Document, HTMLNode):
     def getElementById(self, id):
         nodes = self.getElementsByTagName('*')
         for node in nodes:
             if node.getAttribute('id') == id:
                 return node
 
+# non-DOM ('DOM level 0') stuff
+
+# Window is the main environment, the root node of the JS object tree
+
 class Window(BasicExternal):
     def __init__(self, html=('<html><head><title>Untitled document</title>'
-                             '</head><body></body></html>'), parent=None):
+                             '</head><body></body></html>'), parent=None,
+                             emit_events=False):
         global document
         self._html = html
-        self.document = document = Document(minidom.parseString(html))
+        self._emit_events = emit_events
+        self.document = document = HTMLDocument(minidom.parseString(html))
 
         # references to windows
         self.content = self
@@ -154,68 +221,12 @@
 
         # other properties
         self.closed = True
+        if self._emit_events:
+            _emit_event(self, 'onload')
 
     def __getattr__(self, name):
         return globals()[name]
 
-# the following code wraps minidom nodes with Node classes, and makes
-# sure all methods on the nodes return wrapped nodes
-
-class _FunctionWrapper(object):
-    """makes sure function return values are wrapped if appropriate"""
-    def __init__(self, callable):
-        self._original = callable
-
-    def __call__(self, *args, **kwargs):
-        args = list(args)
-        for i, arg in enumerate(args):
-            if isinstance(arg, Node):
-                args[i] = arg._original
-        for name, arg in kwargs.iteritems():
-            if isinstance(arg, Node):
-                kwargs[arg] = arg._original
-        value = self._original(*args, **kwargs)
-        return _wrap(value)
-
-_typetoclass = {
-    1: HTMLElement,
-    2: Attribute,
-    3: Text,
-    9: Document,
-}
-def _wrap(value):
-    if isinstance(value, minidom.Node):
-        nodeclass = _typetoclass[value.nodeType]
-        return nodeclass(value)
-    elif callable(value):
-        return _FunctionWrapper(value)
-    # nothing fancier in minidom, i hope...
-    # XXX and please don't add anything fancier either ;)
-    elif isinstance(value, list):
-        return [_wrap(x) for x in value]
-    return value
-
-# more DOM API, the stuff that doesn't directly deal with XML
-#   note that we're mimicking the standard (Mozilla) APIs, so things tested
-#   against this code may not work in Internet Explorer
-
-class Event(BasicExternal):
-    pass
-
-class KeyEvent(Event):
-    pass
-
-class MouseEvent(Event):
-    pass
-
-class Style(BasicExternal):
-    def __getattr__(self, name):
-        if name not in self._fields:
-            raise AttributeError, name
-        return None
-
-# non-DOM ('DOM level 0') stuff
-
 def setTimeout(func, delay):
     # scheduler call, but we don't want to mess with threads right now
     if one():
@@ -227,7 +238,7 @@
 def alert(msg):
     pass
 
-# some helper functions (XXX imo these can go, but the code seem to use them
+# some helper functions (XXX imo these can go, but the code seems to use them
 # a lot... isn't it possible to just use dom.window and dom.document instead?)
 
 def get_document():
@@ -257,12 +268,44 @@
     'previousSibling' : Element(),
     'tagName' : "aa",
     'textContent' : "aa",
-    'value' : "aa",
+}
+
+Node._methods = {
+    'appendChild' : MethodDesc([Element()]),
+    'cloneNode' : MethodDesc([12], Element()),
+    'getElementsByTagName' : MethodDesc(["aa"], [Element(), Element()]),
+    'hasChildNodes' : MethodDesc([], True),
+    'insertBefore' : MethodDesc([Element(), Element()], Element()),
+    'removeChild' : MethodDesc([Element()], Element()),
+    'replaceChild' : MethodDesc([Element(), Element()], Element()),
 }
 
 Element._fields = Node._fields.copy()
 
-Element._fields.update({
+Element._methods = Node._methods.copy()
+Element._methods.update({
+    'getAttribute' : MethodDesc(["aa"], "aa"),
+    'getAttributeNS' : MethodDesc(["aa", "aa"], "aa"),
+    'getAttributeNode' : MethodDesc(["aa"], Element()),
+    'getAttributeNodeNS' : MethodDesc(["aa", "aa"], Element()),
+    'hasAttribute' : MethodDesc(["aa"], True),
+    'hasAttributeNS' : MethodDesc(["aa", "aa"], True),
+    'hasAttributes' : MethodDesc([], True),
+    'removeAttribute' : MethodDesc(['aa']),
+    'removeAttributeNS' : MethodDesc(["aa", "aa"]),
+    'removeAttributeNode' : MethodDesc([Element()], "aa"),
+    'setAttribute' : MethodDesc(["aa", "aa"]),
+    'setAttributeNS' : MethodDesc(["aa", "aa", "aa"]),
+    'setAttributeNode' : MethodDesc([Element()], Element()),
+    'setAttributeNodeNS' : MethodDesc(["ns", Element()], Element()),
+})
+
+HTMLNode._fields = Node._fields.copy()
+HTMLNode._methods = Node._methods.copy()
+
+HTMLElement._fields = HTMLNode._fields.copy()
+HTMLElement._fields.update(Element._fields.copy())
+HTMLElement._fields.update({
     'className' : "aa",
     'clientHeight' : 12,
     'clientWidth' : 12,
@@ -270,8 +313,8 @@
     'clientTop' : 12,
     'dir' : "aa",
     'innerHTML' : "asd",
-    'lang' : "asd",
     'id' : "aa",
+    'lang' : "asd",
     'offsetHeight' : 12,
     'offsetLeft' : 12,
     'offsetParent' : 12,
@@ -283,62 +326,30 @@
     'scrollWidth' : 12,
     'style' : Style(),
     'tabIndex' : 12,
-    'onblur' : MethodDesc([Event()]),
-    'onclick' : MethodDesc([MouseEvent()]),
-    'ondblclick' : MethodDesc([MouseEvent()]),
-    'onfocus' : MethodDesc([Event()]),
-    'onkeydown' : MethodDesc([KeyEvent()]),
-    'onkeypress' : MethodDesc([KeyEvent()]),
-    'onkeyup' : MethodDesc([KeyEvent()]),
-    'onmousedown' : MethodDesc([MouseEvent()]),
-    'onmousemove' : MethodDesc([MouseEvent()]),
-    'onmouseup' : MethodDesc([MouseEvent()]),
-    'onmouseover' : MethodDesc([MouseEvent()]),
-    'onmouseup' : MethodDesc([MouseEvent()]),
-    'onresize' : MethodDesc([Event()]),
+    'value' : "aa", # XXX?
 })
 
-HTMLElement._fields = Element._fields.copy()
-Node._methods = {
-    'appendChild' : MethodDesc([HTMLElement()]),
-    'cloneNode' : MethodDesc([12], HTMLElement()),
-    'hasChildNodes' : MethodDesc([], True),
-    'insertBefore' : MethodDesc([HTMLElement(), HTMLElement()], HTMLElement()),
-    'removeChild' : MethodDesc([HTMLElement()], HTMLElement()),
-    'replaceChild' : MethodDesc([HTMLElement(), HTMLElement()], HTMLElement()),
-}
-
-Element._methods = Node._methods.copy()
-Element._methods.update({
-    'addEventListener' : MethodDesc(["aa", lambda : None, True]),
-    'getAttribute' : MethodDesc(["aa"], "aa"),
-    'getAttributeNS' : MethodDesc(["aa", "aa"], "aa"),
-    'getAttributeNode' : MethodDesc(["aa"], Element()),
-    'getAttributeNodeNS' : MethodDesc(["aa", "aa"], Element()),
-    'getElementsByTagName' : MethodDesc(["aa"], [Element(), Element()]),
-    'hasAttribute' : MethodDesc(["aa"], True),
-    'hasAttributeNS' : MethodDesc(["aa", "aa"], True),
-    'hasAttributes' : MethodDesc([], True),
-    'removeAttribute' : MethodDesc(['aa']),
-    'removeAttributeNS' : MethodDesc(["aa", "aa"]),
-    'removeAttributeNode' : MethodDesc([Element()], "aa"),
-    'removeEventListener' : MethodDesc(["aa", lambda : None, True]),
-    'setAttribute' : MethodDesc(["aa", "aa"]),
-    'setAttributeNS' : MethodDesc(["aa", "aa", "aa"]),
-    'setAttributeNode' : MethodDesc([Element()], Element()),
-    'setAttributeNodeNS' : MethodDesc(["ns", Element()], Element()),
+HTMLElement._methods = HTMLNode._methods.copy()
+HTMLElement._methods.update(Element._methods.copy())
+HTMLElement._methods.update({
     'blur' : MethodDesc([]),
     'click' : MethodDesc([]),
-    'dispatchEvent' : MethodDesc(["aa"], True),
     'focus' : MethodDesc([]),
     'normalize' : MethodDesc([]),
     'scrollIntoView' : MethodDesc([12]),
     'supports' : MethodDesc(["aa", 1.0]),
 })
 
-HTMLElement._methods = Element._methods.copy()
+Document._fields = Node._fields.copy()
+Document._fields.update({
+    'characterSet' : "aa",
+    'contentWindow' : Window(),
+    'doctype' : "aa",
+    'documentElement' : Element(),
+    'styleSheets' : [Style(), Style()],
+})
 
-Document._methods = Element._methods.copy()
+Document._methods = Node._methods.copy()
 Document._methods.update({
     'clear' : MethodDesc([]),
     'close' : MethodDesc([]),
@@ -346,8 +357,8 @@
     'createDocumentFragment' : MethodDesc([], Element()),
     'createElement' : MethodDesc(["aa"], Element()),
     'createElementNS' : MethodDesc(["aa", "aa"], Element()),
-    'createTextNode' : MethodDesc(["aa"], Element()),
     'createEvent' : MethodDesc(["aa"], Event()),
+    'createTextNode' : MethodDesc(["aa"], Element()),
     #'createRange' : MethodDesc(["aa"], Range()) - don't know what to do here
     'getElementById' : MethodDesc(["aa"], Element()),
     'getElementsByName' : MethodDesc(["aa"], [Element(), Element()]),
@@ -357,21 +368,16 @@
     'writeln' : MethodDesc(["aa"]),
 })
 
-Document._fields = Element._fields.copy()
-Document._fields.update({
+HTMLDocument._fields = Document._fields.copy()
+HTMLDocument._fields.update({
     'alinkColor' : "aa",
     'bgColor' : "aa",
     'body' : Element(),
-    'characterSet' : "aa",
     'cookie' : "aa",
-    'contentWindow' : Window(),
     'defaultView' : Window(),
-    'doctype' : "aa",
-    'documentElement' : Element(),
     'domain' : "aa",
     'embeds' : [Element(), Element()],
     'fgColor' : "aa",
-    'firstChild' : Element(),
     'forms' : [Element(), Element()],
     'height' : 123,
     'images' : [Element(), Element()],
@@ -380,13 +386,14 @@
     'links' : [Element(), Element()],
     'location' : "aa",
     'referrer' : "aa",
-    'styleSheets' : [Style(), Style()],
     'title' : "aa",
     'URL' : "aa",
     'vlinkColor' : "aa",
     'width' : 123,
 })
 
+HTMLDocument._methods = Document._methods.copy()
+
 Window._fields = {
     'content' : Window(),
     'closed' : True,
@@ -422,8 +429,7 @@
     'window' : Window(),
 }
 
-Window._methods = Element._methods.copy()
-Window._methods.update({
+Window._methods = {
     'alert' : MethodDesc(["aa"]),
     'atob' : MethodDesc(["aa"], "aa"),
     'back' : MethodDesc([]),
@@ -461,7 +467,7 @@
     'onselect' : MethodDesc([MouseEvent()]),
     'onsubmit' : MethodDesc([MouseEvent()]),
     'onunload' : MethodDesc([Event()]),
-})
+}
 
 Style._fields = {
     'azimuth' : 'aa',
@@ -593,16 +599,124 @@
     'zIndex' : 'aa',
 }
 
-KeyEvent_fields = {
+EventTarget._fields = {
+    'onblur' : MethodDesc([Event()]),
+    'onclick' : MethodDesc([MouseEvent()]),
+    'ondblclick' : MethodDesc([MouseEvent()]),
+    'onfocus' : MethodDesc([Event()]),
+    'onkeydown' : MethodDesc([KeyEvent()]),
+    'onkeypress' : MethodDesc([KeyEvent()]),
+    'onkeyup' : MethodDesc([KeyEvent()]),
+    'onmousedown' : MethodDesc([MouseEvent()]),
+    'onmousemove' : MethodDesc([MouseEvent()]),
+    'onmouseup' : MethodDesc([MouseEvent()]),
+    'onmouseover' : MethodDesc([MouseEvent()]),
+    'onmouseup' : MethodDesc([MouseEvent()]),
+    'onresize' : MethodDesc([Event()]),
+}
+
+EventTarget._methods = {
+    'addEventListener' : MethodDesc(["aa", lambda : None, True]),
+    'dispatchEvent' : MethodDesc(["aa"], True),
+    'removeEventListener' : MethodDesc(["aa", lambda : None, True]),
+}
+
+Event._fields = {
+    'bubbles': True,
+    'cancelBubble': True,
+    'cancelable': True,
+    'currentTarget': Element(),
+    'detail': 1,
+    'relatedTarget': Element(),
+    'target': Element(),
+    'type': 'aa',
+}
+
+Event._methods = {
+    'initEvent': MethodDesc(["aa", True, True]),
+    'preventDefault': MethodDesc([]),
+    'stopPropagation': MethodDesc([]),
+}
+
+KeyEvent._fields = Event._fields.copy()
+KeyEvent._fields.update({
     'keyCode' : 12,
     'charCode' : 12,
-}
+})
 
 get_window.suggested_primitive = True
 get_document.suggested_primitive = True
 setTimeout.suggested_primitive = True
 alert.suggested_primitive = True
 
+# the following code wraps minidom nodes with Node classes, and makes
+# sure all methods on the nodes return wrapped nodes
+
+class _FunctionWrapper(object):
+    """makes sure function return values are wrapped if appropriate"""
+    def __init__(self, callable):
+        self._original = callable
+
+    def __call__(self, *args, **kwargs):
+        args = list(args)
+        for i, arg in enumerate(args):
+            if isinstance(arg, Node):
+                args[i] = arg._original
+        for name, arg in kwargs.iteritems():
+            if isinstance(arg, Node):
+                kwargs[arg] = arg._original
+        value = self._original(*args, **kwargs)
+        return _wrap(value)
+
+_typetoclass = {
+    1: HTMLElement,
+    2: Attribute,
+    3: Text,
+    9: HTMLDocument,
+}
+def _wrap(value):
+    if isinstance(value, minidom.Node):
+        nodeclass = _typetoclass[value.nodeType]
+        return nodeclass(value)
+    elif callable(value):
+        return _FunctionWrapper(value)
+    # nothing fancier in minidom, i hope...
+    # XXX and please don't add anything fancier either ;)
+    elif isinstance(value, list):
+        return [_wrap(x) for x in value]
+    return value
+
+# some helper functions
+
+def _quote_html(text):
+    for char, e in [('&', 'amp'), ('<', 'lt'), ('>', 'gt'), ('"', 'quot'),
+                    ("'", 'apos')]:
+        text = text.replace(char, '&%s;' % (e,))
+    return text
+
+_singletons = ['link', 'meta']
+def _serialize_html(node):
+    ret = []
+    if node.nodeType == 1:
+        nodeName = getattr(node, '_original', node).nodeName
+        ret += ['<', nodeName]
+        if len(node.attributes):
+            for aname in node.attributes.keys():
+                attr = node.attributes[aname]
+                ret.append(' %s="%s"' % (attr.nodeName,
+                                         _quote_html(attr.nodeValue)))
+        if len(node.childNodes) or nodeName not in _singletons:
+            ret.append('>')
+            for child in node.childNodes:
+                if child.nodeType == 1:
+                    ret.append(_serialize_html(child))
+                else:
+                    ret.append(_quote_html(child.nodeValue))
+            ret += ['</', nodeName, '>']
+        else:
+            ret.append(' />')
+    return ''.join(ret)
+
 # initialization
 
 # set the global 'window' instance to an empty HTML document, override using

Modified: pypy/dist/pypy/translator/js/modules/test/test_dom.py
==============================================================================
--- pypy/dist/pypy/translator/js/modules/test/test_dom.py	(original)
+++ pypy/dist/pypy/translator/js/modules/test/test_dom.py	Mon Nov 13 09:20:45 2006
@@ -1,8 +1,27 @@
 import py
 from pypy.translator.js.modules import dom
 from pypy.translator.js.main import rpython2javascript
+from xml.dom.minidom import parseString
 import sys
 
+def test_quote_html():
+    assert dom._quote_html('foo&bar') == 'foo&amp;bar'
+    assert dom._quote_html('foo"&bar') == 'foo&quot;&amp;bar'
+
+def test_serialize_html():
+    def roundtrip(html):
+        return dom._serialize_html(parseString(html).documentElement)
+    html = '<div class="bar">content</div>'
+    assert roundtrip(html) == html
+    html = '<iframe src="foo?bar&amp;baz"></iframe>'
+    assert roundtrip(html) == html
+    html = '<meta name="foo"></meta>'
+    assert roundtrip(html) == '<meta name="foo" />'
+    html = '<div>\n  <div>foo</div>\n</div>'
+    assert roundtrip(html) == html
+    html = '<div>foo&amp;bar</div>'
+    assert roundtrip(html) == html
+
 def test_init():
     window = dom.Window('<html><body>foo</body></html>')
     nodeType = window.document.nodeType
@@ -13,6 +32,9 @@
     somediv = window.document.getElementsByTagName('body')[0].childNodes[0]
     assert somediv.nodeValue == 'foo'
 
+    py.test.raises(py.std.xml.parsers.expat.ExpatError,
+                   'dom.Window("<html><body></html>")')
+
 def test_wrap():
     window = dom.Window()
     document = window.document
@@ -84,6 +106,93 @@
     assert body.innerHTML == ''
     body.innerHTML = '<div>some content</div>'
     assert body.innerHTML == '<div>some content</div>'
+    assert body.childNodes[0].nodeName == 'DIV'
+
+def test_event_init():
+    window = dom.Window()
+    e = dom.Event()
+    e.initEvent('click', True, True)
+    assert e.cancelable == True
+    assert e.target == None
+    body = window.document.getElementsByTagName('body')[0]
+    body.dispatchEvent(e)
+    assert e.target is body
+
+def test_event_handling():
+    class handler:
+        called = False
+        def __call__(self, e):
+            self.called = True
+    h = handler()
+    window = dom.Window()
+    body = window.document.getElementsByTagName('body')[0]
+    body.addEventListener('click', h, False)
+    e = dom.Event()
+    e.initEvent('click', True, True)
+    body.dispatchEvent(e)
+    assert h.called == True
+
+def test_event_bubbling():
+    class handler:
+        called = False
+        def __call__(self, e):
+            self.called = True
+    h = handler()
+    window = dom.Window()
+    body = window.document.getElementsByTagName('body')[0]
+    div = window.document.createElement('div')
+    body.appendChild(div)
+    body.addEventListener('click', h, False)
+    e = dom.Event()
+    e.initEvent('click', False, True)
+    div.dispatchEvent(e)
+    assert h.called == False
+    e = dom.Event()
+    e.initEvent('click', True, True)
+    div.dispatchEvent(e)
+    assert h.called == True
+
+def test_remove_event_listener():
+    class handler:
+        called = False
+        def __call__(self, e):
+            self.called = True
+    window = dom.Window()
+    body = window.document.getElementsByTagName('body')[0]
+    div = window.document.createElement('div')
+    body.appendChild(div)
+    py.test.raises(Exception, 'body.removeEventListener("click", h, False)')
+    h = handler()
+    body.addEventListener('click', h, False)
+    e = dom.Event()
+    e.initEvent('click', True, True)
+    body.dispatchEvent(e)
+    assert h.called == True
+    h.called = False
+    body.removeEventListener('click', h, False)
+    e = dom.Event()
+    e.initEvent('click', True, True)
+    body.dispatchEvent(e)
+    assert h.called == False
+
+def test_event_vars():
+    class handler:
+        event = None # XXX annotator problem?
+        def __call__(self, e):
+            self.event = e
+            e.stopPropagation()
+    window = dom.Window()
+    body = window.document.getElementsByTagName('body')[0]
+    div = window.document.createElement('div')
+    body.appendChild(div)
+    h = handler()
+    body.addEventListener('click', h, False)
+    e = dom.Event()
+    e.initEvent('click', True, True)
+    div.dispatchEvent(e)
+    assert h.event.target == div
+    assert h.event.originalTarget == div
+    assert h.event.currentTarget == body
 
 def test_build():
     py.test.skip("Not implemented yet")
@@ -91,3 +200,4 @@
         if var.startswith('test_') and var != 'test_build':
             # just build it
             rpython2javascript(sys.modules[__name__], [var])
+



More information about the Pypy-commit mailing list