""" domhtml - Jan 13, 2008
Fully compliant (I think) DOM 2 HTML implementation.
Currently requires pxdom (http://doxdesk.com/software/py/pxdom.html)
Licence (new-BSD-style)
Copyright (C) 2008, Paul Bonser. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions must reproduce the above copyright notice, this list
  of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.
* The name of the copyright holder may not be used to endorse or
  promote products derived from this software without specific prior
  written permission.
This software is provided by the copyright holder and contributors "as
is" and any express or implied warranties, including, but not limited
to, the implied warranties of merchantability and fitness for a
particular purpose are disclaimed. In no event shall the copyright
owner or contributors be liable for any direct, indirect, incidental,
special, exemplary, or consequential damages (including, but not
limited to, procurement of substitute goods or services; loss of use,
data, or profits; or business interruption) however caused and on any
theory of liability, whether in contract, strict liability, or tort
(including negligence or otherwise) arising in any way out of the use
of this software, even if advised of the possibility of such damage.
"""
# Extend the DOM with DOM 2 HTML, DOM 2 View, and DOM 2 CSS/Style support
#TODO get rid of as much dependence on pxdom as possible for portability between
# DOM implementations in the future
import pxdom as dom
import urlparse, string, re
def parseString(str, uri=''):
    di = getDOMImplementation()
    parser = di.createLSParser(di.MODE_SYNCHRONOUS, None)
    input = di.createLSInput()
    input.stringData = str
    input.systemId= uri
  
    document = HTMLDocument()
    parser.parseWithContext(input, document,
                            parser.ACTION_REPLACE_CHILDREN)
class HTMLDOMImplementation(dom.DOMImplementation):
    """ Add the View, HTML, and CSS/Style (not yet implemented) features """
    def __init__(self):
        self._features['views']       = ['2.0']
        self._features['html']        = ['2.0']
        self._features['stylesheets'] = ['2.0']
    def createDocument(self, namespaceURI, qualifiedName, doctype):
        if namespaceURI=='':
            namespaceURI= None
        document = HTMLDocument()
        if doctype is not None:
            document.appendChild(doctype)
        if qualifiedName is not None:
            root = document.createElementNS(namespaceURI, qualifiedName)
            document.appendChild(root)
        return document
_html_implementation = HTMLDOMImplementation()
                 
def getDOMImplementation(features= ''):
    """ DOM 3 Core hook to get the Implementation object. If features is
    supplied, only return the implementation if all features are satisfied.
    """
    fv = string.split(features, ' ')
    for index in range(0, len(fv)-1, 2):
        if not _html_implementation.hasFeature(fv[index], fv[index+1]):
            return None
    return _html_implementation
def getDOMImplementationList(features= ''):
    """ DOM 3 Core method to get implementations in a list.
    This will be either pxdom's implementation or this extended one
    """
    domimplementation = dom.getDOMImplementation(features)
    htmldomimplementation = getDOMImplementation(features)
    implementationList = DOMImplementationList()
    if domimplementation is not None:
        implementationList._append(domimplementation)
    if htmldomimplementation is not None:
        implementationList._append(htmldomimplementation)
    implementationList.readonly= True
    return implementationList
# Some constants for use below
_KEY  = 0
_TYPE = 1
_PERMISSIONS = 2
class DOMObject:
    def __init__(self, readonly= False):
        self.__dict__['_attr'] = {
            'id':        ['id', 'string', 'rw'],
            'title':     ['title', 'string', 'rw'],
            'lang':      ['lang', 'string', 'rw'],
            'dir':       ['dir', 'string', 'rw'],
            'className': ['class', 'string', 'rw']
        }
        self._readonly= readonly
        self._sub_element = None
    def _get_readonly(self):
        return self._readonly
    def _set_readonly(self, value):
        self._readonly= value
    
    def __getattr__(self, key):
        attr = self._attr.get(key)
        if attr and 'r' in attr[_PERMISSIONS]:
            if attr[_TYPE] == 'string':
                return self.getAttribute(attr[_KEY])
            elif attr[_TYPE] == 'bool':
                return self.hasAttribute(attr[_KEY])
            elif attr[_TYPE] == 'long':
                try:
                    return int(self.getAttribute(attr[_KEY]))
                except ValueError:
                    return 0
            elif attr[_TYPE] == 'local_string':
                return self.__dict__[attr[_KEY]]
            elif attr[_TYPE] == 'local_bool':
                return self.__dict__[attr[_KEY]]
            elif attr[_TYPE] == 'local_long':
                try:
                    return int(self.__dict__[attr[_KEY]])
                except ValueError:
                    return 0
            else:
                return self.getAttribute(attr[_KEY])
        
        if key[:1]=='_':
            raise AttributeError, key
        try:
            getter= getattr(self, '_get_'+key)
        except AttributeError:
            if self._sub_element:
                return getattr(self._sub_element, key)
            raise AttributeError, key
        return getter()
    def __setattr__(self, key, value):
        attr = self._attr.get(key)
        if attr:
            if 'w' in attr[_PERMISSIONS]:
                if attr[_TYPE] == 'string':
                    self.setAttribute(attr[_KEY], value)
                elif attr[_TYPE] == 'bool':
                    if value:
                        self.setAttribute(attr[_KEY], '')
                    else:
                        self.removeAttribute(attr[_KEY])
                elif attr[_TYPE] == 'long':
                    try:
                        val = int(value)
                    except ValueError:
                        val = 0
                    self.setAttribute(attr[_KEY], val)
                elif attr[_TYPE] == 'local_string':
                    self.__dict__[attr[_KEY]] = value
                elif attr[_TYPE] == 'local_bool':
                    if value:
                        self.__dict__[attr[_KEY]] = True
                    else:
                        self.__dict__[attr[_KEY]] = False
                elif attr[_TYPE] == 'local_long':
                    try:
                        val = int(value)
                    except ValueError:
                        val = 0
                    self.__dict__[attr[_KEY]] = val
                else:
                    self.setAttribute(attr[_KEY], value)
            else:
                raise NoModificationAllowedErr(self, key)
            
        if key[:1]=='_' or hasattr(self, key):
            self.__dict__[key]= value
            return
    
        if self._readonly and key not in ('readonly', 'nodeValue',
                                          'textContent'):
            raise NoModificationAllowedErr(self, key)
        try:
            setter= getattr(self, '_set_'+key)
        except AttributeError:
            if hasattr(self, '_get_'+key):
                raise NoModificationAllowedErr(self, key)
            if self._sub_element:
                setattr(self._sub_element, key, value)
            raise AttributeError, key
        setter(value)        
class FilterCollection(dom.NodeListByTagName):
    """ Works just like NodeListByTagName, but rather than just filtering by
    tagName, it takes a list of functions to run the check each node against
    """
    def __init__(self, ownerNode, namespaceURI, *checks):
        dom.NodeListByTagName.__init__(self, ownerNode, namespaceURI, '')
        self._checks = checks
    def _walk(self, element):
        """ Recursively add a node's child elements to the internal node list
        when they match the conditions passed to the constructor
        """
        for childNode in element.childNodes:
            if childNode.nodeType==dom.Node.ELEMENT_NODE:
                passed = True
                for check in self._checks:
                    check_passed = check(childNode)
                    if not check_passed:
                        passed = False
                if passed:
                    self._list.append(childNode)
            if childNode.nodeType in (dom.Node.ELEMENT_NODE,
                                      dom.Node.ENTITY_REFERENCE_NODE):
                self._walk(childNode)
class TableRowCollection(dom.NodeListByTagName):
    """ Works like NodeListByTagName, but gets the rows of a table in
    logical order
    """
    def __init__(self, ownerNode):
        dom.NodeListByTagName.__init__(self, ownerNode, dom.NONS, '')
        self._checks = checks
        
    def _walk(self, element):
        th = element.tHead
        if th:
            self._list.extend(th.rows)
        tbs = element.tBodies
        if tbs.length:
            for tb in tbs:
                self._list.extend(tb.rows)
        tf = element.tFoot
        if tf:
            self._list.extend(tf.rows)
# DOM 2 Views
# -----------
class AbstractView(DOMObject):
    """ Implements the DOM View interface """
    def __init__(self, document):
        DOMObject.__init__(self)
        self._document = document
        self._readonly = True
    def _get_document(self):
        return self._document
# DOM 2 HTML
# ----------
class HTMLCollection(DOMObject):
    def __init__(self, nodelist, html_mode=False):
        DOMObject.__init__(self)
        self._nodelist = nodelist
    def _get_length(self):
        return self._nodelist.length
    def item(self, index):
        return self._nodelist.item(index)
    def namedItem(self, name):
        for elem in self._nodelist:
            if elem.id == name:
                return elem
            if html_mode and elem.getAttribute('name') == name:
                return elem
    # Python-style methods
    #
    def __len__(self):
        return len(self._nodelist)
    
    def __getitem__(self, index):
        return self._nodelist[index]
    
    def __setitem__(self, index, value):
        raise dom.NoModificationAllowedErr(self, 'item(%s)' % str(index))
    
    def __delitem__(self, index):
        raise dom.NoModificationAllowedErr(self, 'item(%s)' % str(index))
class HTMLElement(DOMObject, dom.Element):
    def __init__(self, *args, **kwargs):
        DOMObject.__init__(self)
        dom.Element.__init__(self, *args, **kwargs)
        
    def isSupported(self, feature, version):
        return _html_implementation.hasFeature(feature, version)
    def getFeature(self, feature, version):
        if _html_implementation.hasFeature(feature, version):
            return self
        return None
class _HTMLDisabledElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._disabled = False
    def _get_disabled(self):
        return self._disabled
    def _set_disabled(self, disabled):
        self._disabled = disabled
class _HTMLTextElement(HTMLElement):
    def _get_text(self):
        return self.textContent
    def _set_text(self, text):
        self.textContent = text
class _HTMLFocusBlurElement(HTMLElement):
    def blur(self):
        if self.ownerDocument:
            self.ownerDocument._handler.element_blur(self)
    def focus(self): 
        if self.ownerDocument:
            self.ownerDocument._handler.element_focus(self)
class _HTMLClickElement(HTMLElement):
    def click(self):
        if self.ownerDocument:
            self.ownerDocument._handler.element_click(self)
class _HTMLSelectElement(HTMLElement):
    def select(self): 
        if self.ownerDocument:
            self.ownerDocument._handler.element_select(self)
            
class _HTMLBaseFormElement(HTMLElement):
    def _get_form(self):
        """ Returns the FORM element containing this control. Returns null if
        this control is not within the context of a form. """
        parent = self.parentNode
        while parent and parent.tagName != 'form':
            parent = parent.parentNode
        return parent
           
    def _reset(self):
        pass
class _HTMLFormControlElement(_HTMLBaseFormElement,_HTMLFocusBlurElement):
    def __init__(self, *args, **kwargs):
        _HTMLBaseFormElement.__init__(self, *args, **kwargs)
class _HTMLFormValueElement(_HTMLFormControlElement):
    """ Base class for form controls where value and defaultValue are attributes
    """
    def __init__(self, *args, **kwargs):
        _HTMLFormControlElement.__init__(self,  *args, **kwargs)
    def setAttributeNode(self, attr):
        if attr.name == 'value':
            self.__dict__['defaultValue'] = attr.value
        _HTMLFormControlElement.setAttributeNode(self, attr)
    
    def _reset(self):
        """ Basic function to reset form value """
        self.value = self.defaultValue
        
class HTMLHtmlElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({'version': ['version', 'string', 'rw']})
class HTMLHeadElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({'profile': ['profile', 'string', 'rw']})
class HTMLLinkElement(_HTMLDisabledElement):
    def __init__(self, *args, **kwargs):
        _HTMLDisabledElement.__init__(self, *args, **kwargs)
        self._attr.update({
            # DOM HTML Attributes
            'charset':  ['charset', 'string', 'rw'],
            'href':     ['href', 'string', 'rw'],
            'hreflang': ['hreflang', 'string', 'rw'],
            'media':    ['media', 'string', 'rw'],
            'rel':      ['rel', 'string', 'rw'],
            'rev':      ['rev', 'string', 'rw'],
            'target':   ['target', 'string', 'rw'],
            'type':     ['type', 'string', 'rw']
            })
        if self.getAttribute('rel') == 'stylesheet':
            self._attr.update({'sheet':     ['_sheet', 'local_string', 'r']})
            if self.getAttribute('type') == 'text/css':
                self._sheet = CSSStyleSheet(self)
            else:
                self._sheet = StyleSheet(self)
class HTMLTitleElement(_HTMLTextElement):
    pass
class HTMLMetaElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'content':   ['content', 'string', 'rw'],
            'httpEquiv': ['http-equiv', 'string', 'rw'],
            'name':      ['name', 'string', 'rw'],
            'scheme':    ['scheme', 'string', 'rw']
            })
    
class HTMLBaseElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'href':     ['href', 'string', 'rw'],
            'target':   ['target', 'string', 'rw'],
            })
class HTMLIsIndexElement(_HTMLBaseFormElement):
    def __init__(self, *args, **kwargs):
        _HTMLBaseFormElement.__init__(self, *args, **kwargs)
        self._attr.update({'prompt': ['prompt', 'string', 'rw']})
    
class HTMLStyleElement(_HTMLDisabledElement):
    def __init__(self, *args, **kwargs):
        _HTMLDisabledElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'media':    ['media', 'string', 'rw'],
            'type':     ['type', 'string', 'rw'],
            # DOM StyleSheet Attributes
            'sheet':     ['_sheet', 'local_string', 'r']
            })
        self._sheet = StyleSheet(self)
class HTMLBodyElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'aLink':         ['alink', 'string', 'rw'],
            'background':    ['background', 'string', 'rw'],
            'bgColor':       ['bgcolor', 'string', 'rw'],
            'link':          ['link', 'string', 'rw'],
            'text':          ['text', 'string', 'rw'],
            'vLink':         ['vlink', 'string', 'rw'],
            })
    
class HTMLFormElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'name':          ['name', 'string', 'rw'],
            'acceptCharset': ['accept-charset', 'string', 'rw'],
            'action':        ['action', 'string', 'rw'],
            'enctype':       ['enctype', 'string', 'rw'],
            'method':        ['method', 'string', 'rw'],
            'target':        ['target', 'string', 'rw']
            })
        
    def _get_elements(self):
        """ Returns a collection of all form control elements in the form. """
        return HTMLCollection(
            FilterCollection(self, dom.NONS,
                             lambda node: node.tagName in
                             ('input', 'button', 'select', 'optgroup',
                              'option', 'textarea', 'isindex', 'fieldset'))
            )
    
    def _get_length(self):
        return self.elements.length
    def submit(self):
        if self.ownerDocument:
            self.ownerDocument._event.form_submit(self)
    def reset(self):
        for element in self.elements:
            element._reset()
        
class HTMLSelectElement(_HTMLFormControlElement):
    def __init__(self, *args, **kwargs):
        _HTMLFormControlElement.__init__(self, *args, **kwargs)        
        self._attr.update({
            'name':     ['name', 'string', 'rw'],
            'disabled': ['disabled', 'bool', 'rw'],
            'multiple': ['multiple', 'bool', 'rw'], 
            'size':     ['size', 'long', 'rw'],
            'tabIndex': ['tabindex', 'long', 'rw']
            })
    def _get_type(self):
        if self.muliple:
            return 'select-multiple'
        return 'select-one'
    def _get_selectedIndex(self):
        options = self.options
        for i in range(0, self.options.length):
            if options[i].selected: return i
        return -1
    def _set_selectedIndex(self, index):
        options = self.options
        for option in options:
            option.selected = False
        options[index].selected = True
    def _get_value(self):
        si = self.selectedIndex
        if si != -1:
            return self.options[si].value
        else: return ''
    def _set_value(self, value):
        pass
    def _get_length(self):
        return self.options.length
    def _get_options(self):
        return self.getElementsByTagName('option')
    def _get_multiple(self):
        return self.hasAttribute('multiple')
    def _set_multiple(self, multiple):
        if multiple:
            self.setAttribute('multiple', '')
        else:
            self.removeAttribute('multiple')
    def add(self, element, before):
        if element.tagName not in ('option', 'optgroup'):
            return
        if before:
            self.insertBefore(element, before)
        else:
            self.appendChild(element)
    def remove(self, index):
        option = self.options.item(index)
        if option:
            self.removeChild(option)
class HTMLOptGroupElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'disabled': ['disabled', 'bool', 'rw'],        
            'label':    ['label', 'string', 'rw'],        
            })
class HTMLOptionElement(_HTMLBaseFormElement,_HTMLTextElement):
    def __init__(self, *args, **kwargs):
        _HTMLBaseFormElement.__init__(self, *args, **kwargs)
        self.defaultSelected = self.hasAttribute('selected')
        self._selected = self.defaultSelected
        self._attr.update({
            'disabled': ['disabled', 'bool', 'rw'],        
            'label':    ['label', 'string', 'rw'],        
            })
    def _reset(self):
        self.selected = self.defaultSelected
        
    def _set_text(self, text):
        raise NoModificationAllowedErr(self, 'text')
    def _get_index(self):
        parent = self.parentNode
        while parent and parent.tagName != 'select':
            parent = parent.parentNode
        if parent:
            options = parent.options
            for i in range(parent.length):
                if options[i] is self:
                    return i
        return None
    def _get_selected(self):
        return self._selected
    def _set_selected(self, selected):
        if selected:
            self._selected = True
        else:
            self._selected = False
    def _get_value(self):
        val = self.getAttributeNode('value')
        if val:
            return val.value
        return self.text
    def _set_value(self, value):
        self.setAttributeNode('value', value)
class HTMLInputElement(_HTMLFormValueElement,_HTMLFocusBlurElement,
                       _HTMLClickElement,_HTMLSelectElement):
    def __init__(self, *args, **kwargs):
        _HTMLFormValueElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'accept':   ['accept', 'string', 'rw'],        
            'accessKey': ['accesskey', 'string', 'rw'],        
            'align':    ['align', 'string', 'rw'],        
            'alt':      ['alt', 'string', 'rw'],
            'checked':  ['_checked', 'local_bool', 'rw'],            
            'disabled': ['disabled', 'bool', 'rw'],
            'maxLength': ['maxlength', 'long', 'rw'],            
            'name':      ['name', 'string', 'rw'],        
            'readOnly':  ['readonly', 'boolean', 'rw'],        
            'size':      ['size', 'long', 'rw'],        
            'src':       ['src', 'string', 'rw'],        
            'tabIndex':  ['tabindex', 'long', 'rw'],        
            'type':      ['type', 'string', 'rw'],        
            'useMap':    ['usemap', 'string', 'rw'],        
            'value':     ['_value', 'local_string', 'rw']
            })
        self.__dict__['defaultChecked'] = False
        self.__dict__['_checked'] = False
    def _reset(self):
        _HTMLFormValueElement._reset(self)
        self.checked = self.defaultChecked
    _attrs_to_catch = {
        'value': ('_value',),
        'checked': ('_checked', 'defaultChecked')
        }
    def setAttributeNode(self, attr):
        tc = self._attrs_to_catch.get(attr.name, None)
        if tc:
            for item in tc:
                self.__dict__[item] = attr.value
            
        _HTMLFormValueElement.setAttributeNode(self, attr)
class HTMLTextAreaElement(_HTMLFormControlElement,_HTMLFocusBlurElement,
                          _HTMLSelectElement):
    def __init__(self, *args, **kwargs):
        _HTMLFormControlElement.__init__(self, *args, **kwargs)
        self.defaultValue = self.textContent
        self.value = self.defaultValue
        self._attr.update({
            'accessKey': ['accesskey', 'string', 'rw'],        
            'cols':     ['cols', 'long', 'rw'],        
            'disabled': ['disabled', 'bool', 'rw'],
            'name':      ['name', 'string', 'rw'],        
            'readOnly':  ['readonly', 'boolean', 'rw'],        
            'rows':      ['rows', 'long', 'rw'],        
            'tabIndex':  ['tabindex', 'long', 'rw'],        
            'type':      ['type', 'string', 'rw'],        
            })
        
    def _reset(self):
        """ Basic function to reset form value """
        self.value = self.defaultValue
        
    def _get_type(self):
        return 'textarea'
class HTMLButtonElement(_HTMLBaseFormElement):
    def __init__(self, *args, **kwargs):
        _HTMLBaseFormElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'accessKey': ['accesskey', 'string', 'rw'],        
            'disabled': ['disabled', 'bool', 'rw'],
            'name':     ['name', 'string', 'rw'],        
            'tabIndex': ['tabindex', 'long', 'rw'],        
            'type':     ['type', 'string', 'r'],
            'value':    ['value', 'string', 'rw']
            })
    
class HTMLLabelElement(_HTMLBaseFormElement):
    def __init__(self, *args, **kwargs):
        _HTMLBaseFormElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'accessKey': ['accesskey', 'string', 'rw'],        
            'htmlFor':  ['for', 'string', 'rw']
            })
class HTMLFieldSetElement(_HTMLBaseFormElement):pass
class HTMLLegendElement(_HTMLBaseFormElement):
    def __init__(self, *args, **kwargs):
        _HTMLBaseFormElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'accessKey': ['accesskey', 'string', 'rw'],        
            'align':    ['align', 'string', 'rw']
            })    
class HTMLULstElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'compact': ['compact', 'bool', 'rw'],        
            'type':    ['type', 'string', 'rw']
            })
class HTMLOLstElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'compact': ['compact', 'bool', 'rw'],
            'start':   ['start', 'long', 'rw'],
            'type':    ['type', 'string', 'rw']
            })
        
class HTMLDListElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({'compact': ['compact', 'bool', 'rw']})
class HTMLDirectoryElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({'compact': ['compact', 'bool', 'rw']})
class HTMLMenuElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({'compact': ['compact', 'bool', 'rw']})
class HTMLLIElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'type':  ['type', 'string', 'rw'],
            'value': ['value', 'long', 'rw']
            })
class HTMLDivElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({'align': ['align', 'string', 'rw']})
class HTMLParagraphElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({'align': ['align', 'string', 'rw']})
class HTMLHeadingElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({'align': ['align', 'string', 'rw']})
class HTMLQuoteElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({'cite': ['cite', 'string', 'rw']})
        
class HTMLPreElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({'width': ['width', 'long', 'rw']})
class HTMLBRElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({'clear': ['clear', 'string', 'rw']})
        
class HTMLBaseFontElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'color':  ['color', 'string', 'rw'],
            'face':   ['face', 'string', 'rw'],
            'size':   ['size', 'long', 'rw']
            })
        
class HTMLFontElement(HTMLBaseFontElement):pass
class HTMLHRElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'align':   ['align', 'string', 'rw'],
            'noShade': ['noshade', 'boolean', 'rw'],
            'size':    ['size', 'string', 'rw'],
            'width':   ['width', 'string', 'rw']
            })
        
class HTMLModElement(HTMLElement):
   def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'cite':     ['cite', 'string', 'rw'],
            'dateTime': ['datetime', 'string', 'rw']
            })
        
class HTMLAnchorElement(_HTMLFocusBlurElement):
    def __init__(self, *args, **kwargs):
        _HTMLFocusBlurElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'accessKey': ['accesskey', 'string', 'rw'],
            'charset':   ['charset', 'string', 'rw'],
            'coords':    ['coords', 'string', 'rw'],
            'href':      ['href', 'string', 'rw'],
            'hreflang':  ['hreflang', 'string', 'rw'],
            'name':      ['name', 'string', 'rw'],
            'rel':       ['rel', 'string', 'rw'],
            'rev':       ['rev', 'string', 'rw'],
            'shape':     ['shape', 'string', 'rw'],
            'tabIndex':  ['tabindex', 'long', 'rw'],
            'target':    ['target', 'string', 'rw'],
            'type':      ['type', 'string', 'rw']
            })
        
class HTMLImageElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'name':     ['name', 'string', 'rw'],
            'align':    ['align', 'string', 'rw'],
            'alt':      ['alt', 'string', 'rw'],
            'border':   ['border', 'string', 'rw'],
            'height':   ['height', 'long', 'rw'],
            'hspace':   ['hspace', 'long', 'rw'],
            'isMap':    ['ismap', 'bool', 'rw'],
            'longDesc': ['longdesc', 'string', 'rw'],
            'src':      ['src', 'string', 'rw'],
            'useMap':   ['usemap', 'string', 'rw'],
            'vspace':   ['vspace', 'long', 'rw'],
            'width':    ['width', 'long', 'rw']
            })
        
class HTMLObjectElement(_HTMLBaseFormElement):
    def __init__(self, *args, **kwargs):
        _HTMLBaseFormElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'code':     ['code', 'string', 'rw'],
            'align':    ['align', 'string', 'rw'],
            'archive':  ['archive', 'string', 'rw'],
            'border':   ['border', 'string', 'rw'],
            'codeBase': ['codebase', 'string', 'rw'],
            'codeType': ['codetype', 'string', 'rw'],
            'data':     ['data', 'string', 'rw'],
            'declare':  ['declare', 'bool', 'rw'],
            'height':   ['height', 'string', 'rw'],
            'hspace':   ['hspace', 'long', 'rw'],
            'name':     ['name', 'string', 'rw'],
            'standby':  ['standby', 'string', 'rw'],
            'tabIndex': ['tabindex', 'long', 'rw'],
            'type':     ['type', 'string', 'rw'],
            'useMap':   ['usemap', 'string', 'rw'],
            'vspace':   ['vspace', 'long', 'rw'],
            'width':    ['width', 'long', 'rw']
            })
        self._contentDocument = None
        
    def _get_contentDocument(self):
        return self._contentDocument
        
class HTMLParamElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'name':      ['name', 'string', 'rw'],
            'type':      ['type', 'string', 'rw'],
            'value':     ['value', 'string', 'rw'],
            'valueType': ['valuetype', 'string', 'rw']
            })
        
class HTMLAppletElement         (HTMLElement):
    def __init__(self, *args, **kwargs):
        _HTMLBaseFormElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'align':    ['align', 'string', 'rw'],
            'alt':      ['alt', 'string', 'rw'],
            'archive':  ['archive', 'string', 'rw'],
            'code':     ['code', 'string', 'rw'],
            'codeBase': ['codebase', 'string', 'rw'],
            'height':   ['height', 'string', 'rw'],
            'hspace':   ['hspace', 'long', 'rw'],
            'name':     ['name', 'string', 'rw'],
            'object':   ['object', 'string', 'rw'],
            'vspace':   ['vspace', 'long', 'rw'],
            'width':    ['width', 'long', 'rw']
            })
class HTMLMapElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({'name': ['name', 'string', 'rw']})
    def _get_areas(self):
        return self.getElementsByTagName('area')
        
class HTMLAreaElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'accessKey': ['accesskey', 'string', 'rw'],
            'alt':       ['alt', 'string', 'rw'],
            'coords':    ['coords', 'string', 'rw'],
            'href':      ['href', 'string', 'rw'],
            'noHref':    ['nohref', 'bool', 'rw'],
            'shape':     ['shape', 'string', 'rw'],
            'tabIndex':  ['tabindex', 'long', 'rw'],
            'target':    ['target', 'string', 'rw']
            })
        
class HTMLScriptElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'text':    ['text', 'string', 'rw'],
            'htmlFor': ['for', 'string', 'rw'],
            'event':   ['event', 'string', 'rw'],
            'charset': ['charset', 'string', 'rw'],
            'defer':   ['defer', 'bool', 'rw'],
            'src':     ['src', 'string', 'rw'],
            'type':    ['type', 'string', 'rw']
            })
        
class HTMLTableElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'align':       ['align', 'string', 'rw'],
            'bgColor':     ['bgcolor', 'string', 'rw'],
            'border':      ['border', 'string', 'rw'],
            'cellPadding': ['cellpadding', 'string', 'rw'],
            'cellSpacing': ['cellspacing', 'bool', 'rw'],
            'frame':       ['frame', 'string', 'rw'],
            'rules':       ['rules', 'string', 'rw'],
            'summary':     ['summary', 'string', 'rw'],
            'width':       ['width', 'string', 'rw']
            })
    def _get_caption(self):
        caps = self.getElementsByTagName('caption')
        if caps.length > 0:
            return caps[0]
        return None
    def _set_caption(self, cap):
        if cap.tagName != 'caption':
            raise HierarchyRequestErr(self, cap)
        oldcap = self.caption
        if oldcap:
            self.replaceChild(cap, oldcap)
        else:
            self.appendChild(cap)
    def _get_tHead(self):
        ths = self.getElementsByTagName('thead')
        if ths.length > 0:
            return ths[0]
        return None
    def _set_tHead(self, th):
        if th.tagName != 'thead':
            raise HierarchyRequestErr(self, th)
        oldth = self.tHead
        if oldth:
            self.replaceChild(th, oldth)
        else:
            self.appendChild(th)        
    def _get_tFoot(self):
        tfs = self.getElementsByTagName('tfoot')
        if tfs.length > 0:
            return tfs[0]
        return None
    def _set_tFoot(self, tf):
        if tf.tagName != 'tfoot':
            raise HierarchyRequestErr(self, tf)
        oldtf = self.tFoot
        if oldtf:
            self.replaceChild(tf, oldtf)
        else:
            self.appendChild(tf)
    def _get_rows(self):
        return HTMLCollection(TableRowCollection(self))
    def _get_tBodies(self):
        return self.getElementsByTagName('tbody')
    def createTHead(self):
        th = self.tHead
        if th:
            return th
        th = self.ownerDocument.createElement('thead')
        self.insertBefore(th, self.firstChild)
        return th
    def deleteTHead(self):
        th = self.tHead
        if th:
            self.removeChild(th)
    def createTFoot(self):
        tf = self.tFoot
        if tf:
            return tf
        tf = self.ownerDocument.createElement('tfoot')
        self.appendChild(tf)
        return tf
    def deleteTFoot(self):
        tf = self.tFoot
        if tf:
            self.removeChild(tf)
    def createCaption(self):
        cap = self.caption
        if cap:
            return cap
        cap = self.ownerDocument.createElement('caption')
        self.appendChild(cap)
        return cap
    def deleteCaption(self):
        cap = self.caption
        if cap:
            self.removeChild(cap)
    def insertRow(self, index):
        rows = self.rows
        if index > rows.length:
            raise dom.IndexSizeErr(rows, index)
        oldrow = rows.item(index)
        newrow = self.ownerDocument.createElement('tr')
        self.insertBefore(newrow, oldrow)
        return newrow
    def deleteRow(self, index):
        oldrow = self.rows.item(index)
        if not oldrow:
            raise dom.IndexSizeErr(self.rows, index)
        self.removeChild(oldrow)
            
class HTMLTableCaptionElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({'align': ['align', 'string', 'rw']})
        
class HTMLTableColElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'align':  ['align', 'string', 'rw'],
            'ch':     ['char', 'string', 'rw'],
            'chOff':  ['charoff', 'string', 'rw'],
            'span':   ['span', 'long', 'rw'],
            'vAlign': ['valign', 'string', 'rw'],
            'width':  ['width', 'string', 'rw']
            })
        
class HTMLTableSectionElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'align':  ['align', 'string', 'rw'],
            'ch':     ['char', 'string', 'rw'],
            'chOff':  ['charoff', 'string', 'rw'],
            'vAlign': ['valign', 'string', 'rw']
            })
    def _get_rows(self):
        return self.getElementsByTagName('tr')
    def insertRow(self, index):
        rows = self.rows
        if index > rows.length:
            raise dom.IndexSizeErr(rows, index)
        oldrow = rows.item(index)
        newrow = self.ownerDocument.createElement('tr')
        self.insertBefore(newrow, oldrow)
        return newrow
    def deleteRow(self, index):
        oldrow = self.rows.item(index)
        if not oldrow:
            raise dom.IndexSizeErr(self.rows, index)
        self.removeChild(oldrow)
def _up_to(self, elements):
    """ Goes up the document tree until it finds one of the elements in
    elements and returns that element
    """
    parent = self.parentNode
    while parent and parent.tagName not in elements:
        parent = parent.parentNode
    return parent
def _find_self(self, list):
    """ Find ourselves in a list, returns our index"""
    for i in range(list.length):
        if list[i] is self:
            return i
class HTMLTableRowElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'align':   ['align', 'string', 'rw'],
            'bgColor': ['bgcolor', 'string', 'rw'],
            'ch':      ['char', 'string', 'rw'],
            'chOff':   ['charoff', 'string', 'rw'],
            'vAlign':  ['valign', 'string', 'rw'],
            })
    def _get_rowIndex(self):
        table = _up_to(self, ('table',))
        index = _find_self(self, table.rows)
        return index
    def _get_sectionRowIndex(self):
        section = _up_to(self, ('thead', 'tbody', 'tfoot'))
        index = _find_self(self, section.rows)
        return index
    def _get_cells(self):
        return HTMLCollection(FilterCollection(self, dom.NONS,
                                               lambda node:
                                               node.tagName in ('th', 'td')))
    def insertCell(self, index):
        cells = self.cells
        if index > cells.length or index < -1:
            raise dom.IndexSizeErr(cells, index)
        oldcell = cells.item(index)
        newcell = self.ownerDocument.createElement('td')
        self.insertBefore(newcell, oldcell)
        return newcell
          
    def deleteCell(self, index):
        oldcell = self.cells.item(index)
        if not oldcell:
            raise dom.IndexSizeErr(self.cells, index)
        self.removeChild(oldcell)      
        
class HTMLTableCellElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'abbr':    ['abbr', 'string', 'rw'],
            'align':   ['align', 'string', 'rw'],
            'axis':    ['axis', 'string', 'rw'],
            'bgColor': ['bgcolor', 'string', 'rw'],
            'ch':      ['char', 'string', 'rw'],
            'chOff':   ['charoff', 'string', 'rw'],
            'colSpan': ['colspan', 'long', 'rw'],
            'headers': ['headers', 'string', 'rw'],
            'height':  ['height', 'string', 'rw'],
            'noWrap':  ['nowrap', 'bool', 'rw'],
            'rowSpan': ['rowspan', 'long', 'rw'],
            'scope':   ['scope', 'string', 'rw'],
            'vAlign':  ['valign', 'string', 'rw'],
            'width':   ['width', 'string', 'rw']
            })
        
    def _get_cellIndex(self):
        row = _up_to(self, ('tr'))
        index = _find_self(self, row.cells)
        return index
        
class HTMLFrameSetElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'cols': ['cols', 'string', 'rw'],
            'rows': ['rows', 'string', 'rw']
            })
        
class HTMLFrameElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'contentDocument':  ['_contentDocument', 'local_string', 'r'],
            'frameBorder':      ['frameborder', 'string', 'rw'],
            'longDesc':         ['longdesc', 'string', 'rw'],
            'marginHeight':     ['marginheight', 'string', 'rw'],
            'marginWidth':      ['marginwidth', 'string', 'rw'],
            'name':             ['name', 'string', 'rw'],
            'noResize':         ['noresize', 'bool', 'rw'],
            'scrolling':        ['scrolling', 'string', 'rw'],
            'src':              ['src', 'string', 'rw']
            })
        self._contentDocument = None
class HTMLIFrameElement(HTMLElement):
    def __init__(self, *args, **kwargs):
        HTMLElement.__init__(self, *args, **kwargs)
        self._attr.update({
            'contentDocument':  ['_contentDocument', 'local_string', 'r'],
            'align':            ['align', 'string', 'rw'],
            'frameBorder':      ['frameborder', 'string', 'rw'],
            'height':           ['height', 'string', 'rw'],
            'longDesc':         ['longdesc', 'string', 'rw'],
            'marginHeight':     ['marginheight', 'string', 'rw'],
            'marginWidth':      ['marginwidth', 'string', 'rw'],
            'name':             ['name', 'string', 'rw'],
            'scrolling':        ['scrolling', 'string', 'rw'],
            'src':              ['src', 'string', 'rw'],
            'width':            ['width', 'string', 'rw']
            })
        self._contentDocument = None
# These elements are implemented with the base HTMLElement class
BASE_HTML_ELEMENTS = set([
    'sub', 'sup', 'span', 'bdo', 'tt', 'i', 'b', 'u', 's', 'strike', 'big',
    'small', 'em', 'strong', 'dfn', 'code', 'samp', 'kbd', 'var', 'cite',
    'acronym', 'abbr', 'dd', 'dt', 'noframes', 'noscript', 'address', 'center'
    ])
# These elements are specialized subclasses of HTMLElement
EXTENDED_HTML_ELEMENTS = {
    'html': HTMLHtmlElement, 'head': HTMLHeadElement, 'link': HTMLLinkElement,
    'title': HTMLTitleElement, 'meta': HTMLMetaElement, 'base': HTMLBaseElement,
    'isindex': HTMLIsIndexElement, 'style': HTMLStyleElement,
    'body': HTMLBodyElement, 'form': HTMLFormElement,
    'select': HTMLSelectElement, 'optgroup': HTMLOptGroupElement,
    'option': HTMLOptionElement, 'input': HTMLInputElement,
    'textarea': HTMLTextAreaElement, 'button': HTMLButtonElement,
    'label': HTMLLabelElement, 'fieldset': HTMLFieldSetElement,
    'legend': HTMLLegendElement, 'ul': HTMLULstElement,
    'ol': HTMLOLstElement, 'dl': HTMLDListElement,
    'dir': HTMLDirectoryElement, 'menu': HTMLMenuElement, 'li': HTMLLIElement,
    'div': HTMLDivElement, 'p': HTMLParagraphElement,
    # headers
    'h1': HTMLHeadingElement, 'h2': HTMLHeadingElement,
    'h3': HTMLHeadingElement, 'h4': HTMLHeadingElement,
    'h5': HTMLHeadingElement, 'h6': HTMLHeadingElement,
    'q': HTMLQuoteElement, 'blockquote':HTMLQuoteElement, 'pre': HTMLPreElement,
    'br': HTMLBRElement, 'basefont': HTMLBaseFontElement,
    'font': HTMLFontElement, 'hr': HTMLHRElement,
    'ins': HTMLModElement, 'del': HTMLModElement,
    'a': HTMLAnchorElement, 'img': HTMLImageElement,
    'object': HTMLObjectElement, 'param': HTMLParamElement,
    'applet': HTMLAppletElement, 'map': HTMLMapElement, 'area': HTMLAreaElement,
    'script': HTMLScriptElement,
    # table stuff
    'table': HTMLTableElement,'caption': HTMLTableCaptionElement,
    'col': HTMLTableColElement, 'thead': HTMLTableSectionElement,
    'tfoot': HTMLTableSectionElement, 'tbody': HTMLTableSectionElement,
    'tr': HTMLTableRowElement, 'td': HTMLTableCellElement,
    #frame stuff
    'frameset': HTMLFrameSetElement, 'frame': HTMLFrameElement,
    'iframe': HTMLIFrameElement
    }
def _lookup_html_element(tagName):
    if tagName in BASE_HTML_ELEMENTS:
        return HTMLElement
    if tagName in EXTENDED_HTML_ELEMENTS:
        return EXTENDED_HTML_ELEMENTS[tagName]
    return HTMLElement
def _copy_and_extend(source_node, dest_node, owner_document):
    """ Walk through the DOM tree of the provided source_node and copy the whole
    structure into the dest_node. Also take any HTML-specific elements found and
    extend them into their HTML-specific version before copying
    """
    for childNode in source_node.childNodes:
        newChildNode = owner_document.createElement(childNode.tagName)
        dest_node.appendChild(newChildNode)
        _copy_and_extend(childNode, newChildNode, owner_document)
#        if self._current_node.childNodes:
#            self._current_node = self._current_node.firstChild
#        else:
#            old_node = self._current_node # backup in case there's no next
#            
#            while (not self._current_node.nextSibling and
#                   self._current_node.parentNode):
#                self._current_node = self._current_node.parentNode
#            if not self._current_node.nextSibling:
#                self._current_node = old_node
#                return None
#            self._current_node = self._current_node.nextSibling
#        elem = self._current_node
#        elem._computed_style = self._document.defaultView.getComputedStyle(
#            elem, None)
#        return elem
class _HTMLEventHandler:
    def __init__(self):
        self._handlers = {}
    def __getattr__(self, key):
        return self._handlers.get(key, lambda x: None)
   
class HTMLDocument(dom.Document):
    """ Implements the DOM HTMLDocument interface
    and the DOM DocumentStyle interface
    """
    
    def __init__(self, document=None, uri='', referrer=''):
        """ Initialize the HTMLDocument from an existing Document """
        dom.Document.__init__(self)
        self._handler = _HTMLEventHandler()
        self._styleSheets = StyleSheetList(self)
        self._referrer = referrer
        if document:
            # copy the original document
            document._cloneTo(self)
            self._documentURI = self._documentURI or uri
            # Convert the DOM over to HTML and copy children over
            _copy_and_extend(document, self, self)
    def _get_defaultView(self):
        return ViewCSS(self)
    def _get_styleSheets(self):
        return self._styleSheets
    def _get_implementation(self):
        return _html_implementation
    def isSupported(self, feature, version):
        return _html_implementation.hasFeature(feature, version)
    def getFeature(self, feature, version):
        if _html_implementation.hasFeature(feature, version):
            return self
        return None
    def createElement(self, tagName):
        element = _lookup_html_element(tagName)(self, dom.NONS, tagName, None)
        element._setDefaultAttributes()
        return element
    
    def createElementNS(self, namespaceURI, qualifiedName):
        if namespaceURI=='':
            namespaceURI= None
        dom._checkName(qualifiedName)
        prefix, localName= dom._splitName(qualifiedName)
        if (
            localName is None or
            namespaceURI is None and prefix is not None or
            prefix=='xml' and namespaceURI!=dom.XMNS or
            (namespaceURI==dom.NSNS) != ('xmlns' in (prefix, qualifiedName))
        ):
            raise NamespaceErr(qualifiedName, namespaceURI)
        element = _lookup_html_element(localName)(self, namespaceURI,
                                                  localName, prefix)
        element._setDefaultAttributes()
        return element
    def _get_title(self):
        title_elem = self.getElementsByTagName('title')
        if title_elem.length == 0: return ''
        return title_elem[0].text
    def _set_title(self, text):
        title_elem = self.getElementsByTagName('title')
        if title_elem.length == 0: return
        title_elem[0].text = text
    def _get_referrer(self):
        return self._referrer
    def _get_domain(self):
        return urlparse.urlsplit(self._documentURI).hostname
    def _get_URL(self):
        return self._documentURI
    def _get_body(self):
        e = self.getElementsByTagName('body')
        if e.length is 0:
            e = self.getElementsByTagName('frameset')
        return e[0]
    def _set_body(self, new_body):
        old_body = self._get_body()
        self.replaceChild(new_body, old_body)
    def _get_images(self):
        return HTMLCollection(self.getElementsByTagName('img'))
    def _get_applets(self):
        """ A collection of all the OBJECT elements that include applets and
        APPLET (depricated) elements in a document """
        return HTMLCollection(
            FilterCollection(self, dom.NONS,
                             lambda node:
                             node.tagName == 'applet' or
                             (node.tagName == 'object' and
                              node.getElementsByTagName.length > 0)
                             )
            )
    def _get_links(self):
        """ A collection of all AREA elements and anchor (A) elements in a
        document with a value for the href attribute. """
        return HTMLCollection(
            FilterCollection(self, dom.NONS,
                             lambda node:
                             (node.tagName == 'area' or
                              node.tagName == 'a') and
                             node.href != ''
                             )
            )
    def _get_forms(self):
        return HTMLCollection(self.getElementsByTagName('form'))
    def _get_anchors(self):
        return HTMLCollection(
            FilterCollection(self, dom.NONS,
                             lambda node:
                             node.tagName == 'a' and
                             node.name != ''
                             )
            )
    def set_handler(self, key, handler):
        self._handler._handlers[key] = handler
    def _get_cookie(self):
        return self._handler.cookie_read(None) or ''
    def _set_cookie(self, cookie):
        return self._handler.cookie_write(cookie)
    def open(self):
        self._open = True
        self._write_document = ''
    def close(self):
        self._open = False
        self.pxdomContent = self._write_document
    def write(self, text):
        if self._open:
            self._write_document += text
    def writeln(self, text):
        if self._open:
            self._write_document += text + "\n"
    def getElementsByTagNameNS(self, ns, name):
        """ Wrap the Document's implementation to add the namedItem function """
        return HTMLCollection(
            dom.Document.getElementsByTagNameNS(self, ns, name))
    def getElementsByName(self, name):
        return HTMLCollection(
            FilterCollection(self, dom.NONS,
                             lambda node:
                             hasattr(node, 'name') and node.name == name)
            )
# DOM 2 Style Sheets
# ------------------
class StyleSheet(DOMObject):
    def __init__(self, ownerNode, parentSheet=None):
        DOMObject.__init__(self)
        self._ownerNode = ownerNode
        self._parentStyleSheet = parentSheet
        self._disabled = False
        
        self._attr.update({
            'disabled':         ['_disabled', 'local_bool', 'rw'],
            'ownerNode':        ['ownerNode', 'local_string', 'r'],
            'parentStyleSheet': ['parentStypeSheet', 'local_string', 'r'],
            'media':             ['_media', 'local_string', 'r']
            })
        # We only ever need a single instance of this MediaList
        if self._ownerNode:
            media = self._ownerNode.media
        else:
            media = self._parentStyleSheet.media
        self._media = MediaList(media)
        
    def _get_type(self):
        if self._ownerNode:
            return self._ownerNode.type
        return self._parentStyleSheet.type
    def _get_href(self):
        if self._ownerNode and self._ownerNode.tagName != 'style':
            return self._ownerNode.href
        return None
    def _get_title(self):
        if self._ownerNode:
            return self._ownerNode.title
        return None
class StyleSheetList(DOMObject):
    def __init__(self, document):
        DOMObject.__init__(self)
        self._document = document
        self._sequence = None
    # The following two methods make sure the list is always accurate
    def _check(self):
        if self._sequence != self._document._sequence:
            self._regen_list()
            self._sequence = self._document._sequence
    def _regen_list(self):
        elems = FilterCollection(self._document, dom.NONS,
                                 lambda node:
                                 node.tagName in ('link', 'style'))
        self._list = []
        for elem in elems:
            self._list.append(elem.sheet)
    def _get_length(self):
        self._check()
        return self._list.length
    def item(self, index):
        self._check()
        return self._list.get(index)
class MediaList(DOMObject):
    _allowed_media = ['screen', 'tty', 'tv', 'projection', 'handheld', 'print',
                      'braile', 'aural', 'all']
    def __init__(self, mediastring):
        """ Take mediastring and set the internal media list according to the
        rules at http://www.w3.org/TR/1998/REC-html40-19980424/types.html#h-6.13
        """
        DOMObject.__init__(self)
        self._mediaText = mediastring
        self._parse()
    def _parse(self):
        medialist = self._mediaText.split(',')
        regex = re.compile('^[^-a-zA-Z0-9]')
        for i in range(len(medialist)):
            value = medialist[i].strip()
            mo = regex.match(value)
            if mo:
                medialist[i] = mo.group()
            else:
                medialist[i] = None
        self._medialist = filter(lambda item:
                                 item in self._allowed_media, medialist)
        self._mediaText = ', '.join(self._medialist)
    def _get_length(self):
        return self._medialist.length
    def item(self, index):
        return self._medialist.get(index)
    def deleteMedium(self, oldmedium):
        if self._readonly:
            raise dom.NoModificationAllowedErr(self, 'list')
        if not oldmedium in self._medialist:
            raise dom.NotFoundErr(self, None, oldmedium)
        self._medialist.remove(oldmedium)
    def appendMedium(self, newmedium):
        if self._readonly:
            raise dom.NoModificationAllowedErr(self, 'list')
        if newmedium not in self._allowed_media:
            return
        if newmedium in self._medialist:
            self.deleteMedium(newmedium)
        self._medialist.append(newmedium)
        
# DOM 2 CSS
# ---------
#class CSSStyleSheet(StyleSheet):
#    """ Wrapper around cssutils.css.StyleSheet """
#    def __init__(self, ownerNode, parentSheet):
#        StyleSheet.__init__(self, ownerNode, parentSheet)
#        self._sub_element = css.CSSStyleSheet(
class ViewCSS(AbstractView):
    def getComputedStyle(elt, psuedoElt):
        """ Return a CSSStyleDeclaration with the computed style of the element
        (or psuedo element if psuedoElt is not None)
        This is just a dummy version for now which returns the very minimum
        basic styles.
        """