/*
 *  This file is part of the KDE libraries
 *  Copyright (C) 2000 Harri Porten (porten@kde.org)
 *  Copyright (C) 2003 Apple Computer, Inc.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <tdehtmlview.h>
#include "xml/dom2_eventsimpl.h"
#include "rendering/render_canvas.h"
#include "rendering/render_layer.h"
#include "xml/dom_nodeimpl.h"
#include "xml/dom_docimpl.h"
#include "misc/htmltags.h" // ID_*
#include "misc/htmlattrs.h" // ATTR_*
#include "html/html_baseimpl.h"
#include <kdebug.h>
#include <tdehtml_part.h>

#include "kjs_dom.h"
#include "kjs_html.h"
#include "kjs_css.h"
#include "kjs_range.h"
#include "kjs_traversal.h"
#include "kjs_events.h"
#include "kjs_views.h"
#include "kjs_window.h"
#include "dom/dom_exception.h"
#include "kjs_dom.lut.h"
#include "tdehtmlpart_p.h"

namespace KJS {

// -------------------------------------------------------------------------
/* Source for DOMNodeConstantsTable.
@begin DOMNodeConstantsTable 11
  ELEMENT_NODE      DOM::Node::ELEMENT_NODE     DontDelete|ReadOnly
  ATTRIBUTE_NODE    DOM::Node::ATTRIBUTE_NODE       DontDelete|ReadOnly
  TEXT_NODE     DOM::Node::TEXT_NODE        DontDelete|ReadOnly
  CDATA_SECTION_NODE    DOM::Node::CDATA_SECTION_NODE   DontDelete|ReadOnly
  ENTITY_REFERENCE_NODE DOM::Node::ENTITY_REFERENCE_NODE    DontDelete|ReadOnly
  ENTITY_NODE       DOM::Node::ENTITY_NODE      DontDelete|ReadOnly
  PROCESSING_INSTRUCTION_NODE DOM::Node::PROCESSING_INSTRUCTION_NODE DontDelete|ReadOnly
  COMMENT_NODE      DOM::Node::COMMENT_NODE     DontDelete|ReadOnly
  DOCUMENT_NODE     DOM::Node::DOCUMENT_NODE        DontDelete|ReadOnly
  DOCUMENT_TYPE_NODE    DOM::Node::DOCUMENT_TYPE_NODE   DontDelete|ReadOnly
  DOCUMENT_FRAGMENT_NODE DOM::Node::DOCUMENT_FRAGMENT_NODE  DontDelete|ReadOnly
  NOTATION_NODE     DOM::Node::NOTATION_NODE        DontDelete|ReadOnly
@end
*/
IMPLEMENT_CONSTANT_TABLE(DOMNodeConstants,"DOMNodeConstants")
// -------------------------------------------------------------------------
/* Source for DOMNodeProtoTable.
@begin DOMNodeProtoTable 13
  insertBefore	DOMNode::InsertBefore	DontDelete|Function 2
  replaceChild	DOMNode::ReplaceChild	DontDelete|Function 2
  removeChild	DOMNode::RemoveChild	DontDelete|Function 1
  appendChild	DOMNode::AppendChild	DontDelete|Function 1
  hasAttributes	DOMNode::HasAttributes	DontDelete|Function 0
  hasChildNodes	DOMNode::HasChildNodes	DontDelete|Function 0
  cloneNode	DOMNode::CloneNode	DontDelete|Function 1
# DOM2
  normalize	DOMNode::Normalize	DontDelete|Function 0
  isSupported   DOMNode::IsSupported	DontDelete|Function 2
# from the EventTarget interface
  addEventListener	DOMNode::AddEventListener	DontDelete|Function 3
  removeEventListener	DOMNode::RemoveEventListener	DontDelete|Function 3
  dispatchEvent		DOMNode::DispatchEvent	DontDelete|Function 1
# IE extensions
  contains	DOMNode::Contains		DontDelete|Function 1
  insertAdjacentHTML	DOMNode::InsertAdjacentHTML	DontDelete|Function 2
# "DOM level 0" (from Gecko DOM reference; also in WinIE)
  item          DOMNode::Item           DontDelete|Function 1
@end
*/
IMPLEMENT_PROTOFUNC_DOM(DOMNodeProtoFunc)
KJS_IMPLEMENT_PROTOTYPE("DOMNode", DOMNodeProto, DOMNodeProtoFunc)

const ClassInfo DOMNode::info = { "Node", 0, &DOMNodeTable, 0 };

DOMNode::DOMNode(ExecState *exec, const DOM::Node& n)
  : DOMObject(DOMNodeProto::self(exec)), node(n)
{
}

DOMNode::DOMNode(const Object& proto, const DOM::Node& n)
  : DOMObject(proto), node(n)
{
}

DOMNode::~DOMNode()
{
  ScriptInterpreter::forgetDOMObject(node.handle());
}

bool DOMNode::toBoolean(ExecState *) const
{
    return !node.isNull();
}

/* Source for DOMNodeTable.
@begin DOMNodeTable 53
  nodeName	DOMNode::NodeName	DontDelete|ReadOnly
  nodeValue	DOMNode::NodeValue	DontDelete
  nodeType	DOMNode::NodeType	DontDelete|ReadOnly
  parentNode	DOMNode::ParentNode	DontDelete|ReadOnly
  parentElement	DOMNode::ParentElement	DontDelete|ReadOnly
  childNodes	DOMNode::ChildNodes	DontDelete|ReadOnly
  firstChild	DOMNode::FirstChild	DontDelete|ReadOnly
  lastChild	DOMNode::LastChild	DontDelete|ReadOnly
  previousSibling  DOMNode::PreviousSibling DontDelete|ReadOnly
  nextSibling	DOMNode::NextSibling	DontDelete|ReadOnly
  attributes	DOMNode::Attributes	DontDelete|ReadOnly
  namespaceURI	DOMNode::NamespaceURI	DontDelete|ReadOnly
# DOM2
  prefix	DOMNode::Prefix		DontDelete
  localName	DOMNode::LocalName	DontDelete|ReadOnly
  ownerDocument	DOMNode::OwnerDocument	DontDelete|ReadOnly
# DOM3
  textContent	DOMNode::TextContent		DontDelete
# Event handlers
# IE also has: onactivate, onbefore*, oncontextmenu, oncontrolselect, oncut,
# ondeactivate, ondrag*, ondrop, onfocusin, onfocusout, onhelp, onmousewheel,
# onmove*, onpaste, onpropertychange, onreadystatechange, onresizeend/start,
# onselectionchange, onstop
  onabort	DOMNode::OnAbort		DontDelete
  onblur	DOMNode::OnBlur			DontDelete
  onchange	DOMNode::OnChange		DontDelete
  onclick	DOMNode::OnClick		DontDelete
  ondblclick	DOMNode::OnDblClick		DontDelete
  ondragdrop	DOMNode::OnDragDrop		DontDelete
  onerror	DOMNode::OnError		DontDelete
  onfocus	DOMNode::OnFocus       		DontDelete
  onkeydown	DOMNode::OnKeyDown		DontDelete
  onkeypress	DOMNode::OnKeyPress		DontDelete
  onkeyup	DOMNode::OnKeyUp		DontDelete
  onload	DOMNode::OnLoad			DontDelete
  onmousedown	DOMNode::OnMouseDown		DontDelete
  onmousemove	DOMNode::OnMouseMove		DontDelete
  onmouseout	DOMNode::OnMouseOut		DontDelete
  onmouseover	DOMNode::OnMouseOver		DontDelete
  onmouseup	DOMNode::OnMouseUp		DontDelete
  onmove	DOMNode::OnMove			DontDelete
  onreset	DOMNode::OnReset		DontDelete
  onresize	DOMNode::OnResize		DontDelete
  onselect	DOMNode::OnSelect		DontDelete
  onsubmit	DOMNode::OnSubmit		DontDelete
  onunload	DOMNode::OnUnload		DontDelete
# IE extensions
  offsetLeft	DOMNode::OffsetLeft		DontDelete|ReadOnly
  offsetTop	DOMNode::OffsetTop		DontDelete|ReadOnly
  offsetWidth	DOMNode::OffsetWidth		DontDelete|ReadOnly
  offsetHeight	DOMNode::OffsetHeight		DontDelete|ReadOnly
  offsetParent	DOMNode::OffsetParent		DontDelete|ReadOnly
  clientWidth	DOMNode::ClientWidth		DontDelete|ReadOnly
  clientHeight	DOMNode::ClientHeight		DontDelete|ReadOnly
  scrollLeft	DOMNode::ScrollLeft		DontDelete
  scrollTop	DOMNode::ScrollTop		DontDelete
  scrollWidth   DOMNode::ScrollWidth            DontDelete|ReadOnly
  scrollHeight  DOMNode::ScrollHeight           DontDelete|ReadOnly
  sourceIndex	DOMNode::SourceIndex		DontDelete|ReadOnly
@end
*/
Value DOMNode::tryGet(ExecState *exec, const Identifier &propertyName) const
{
#ifdef KJS_VERBOSE
  kdDebug(6070) << "DOMNode::tryGet " << propertyName.qstring() << endl;
#endif
  return DOMObjectLookupGetValue<DOMNode, DOMObject>(exec, propertyName, &DOMNodeTable, this);
}

static tdehtml::RenderObject* handleBodyRootQuirk(const DOM::Node& node, tdehtml::RenderObject* rend, int token)
{
  //This emulates the quirks of various height/width properties on the viewport and root. Note that it 
  //is (mostly) IE-compatible in quirks, and mozilla-compatible in strict.
  if (!rend) return 0;

  bool quirksMode = rend->style() && rend->style()->htmlHacks();
  
  //There are a couple quirks here. One is that in quirks mode body is always forwarded to root...
  //This is relevant for even the scrollTop/scrollLeft type properties.
  if (quirksMode && node.handle()->id() == ID_BODY) {
    while (rend->parent() && !rend->isRoot())
      rend = rend->parent();
  }

  //Also, some properties of the root are really done in terms of the viewport.
  //These are  {offset/client}{Height/Width}. The offset versions do it only in 
  //quirks mode, the client always.
  if (!rend->isRoot()) return rend; //Don't care about non-root things here!
  bool needViewport = false;

  switch (token) {
    case DOMNode::OffsetHeight:
    case DOMNode::OffsetWidth:
      needViewport = quirksMode;
      break;
    case DOMNode::ClientHeight:
    case DOMNode::ClientWidth:
      needViewport = true;
      break;
  }
  
  if (needViewport) {
    //Scan up to find the new target
    while (rend->parent())
      rend = rend->parent();
  }
  return rend;
}

Value DOMNode::getValueProperty(ExecState *exec, int token) const
{
  switch (token) {
  case NodeName:
    return String(node.nodeName());
  case NodeValue:
    return getString(node.nodeValue()); // initially null, per domts/level1/core/hc_documentcreateelement.html
  case NodeType:
    return Number((unsigned int)node.nodeType());
  case ParentNode:
    return getDOMNode(exec,node.parentNode());
  case ParentElement: // IE only apparently
    return getDOMNode(exec,node.parentNode());
  case ChildNodes:
    return getDOMNodeList(exec,node.childNodes());
  case FirstChild:
    return getDOMNode(exec,node.firstChild());
  case LastChild:
    return getDOMNode(exec,node.lastChild());
  case PreviousSibling:
    return getDOMNode(exec,node.previousSibling());
  case NextSibling:
    return getDOMNode(exec,node.nextSibling());
  case Attributes:
    return getDOMNamedNodeMap(exec,node.attributes());
  case NamespaceURI:
    return getString(node.namespaceURI()); // Moz returns null if not set (dom/namespaces.html)
  case Prefix:
    return getString(node.prefix());  // Moz returns null if not set (dom/namespaces.html)
  case TextContent:
     return getString(node.textContent()); //DOM3 says return null, but I really should test mozilla..
  case LocalName:
    return getString(node.localName());  // Moz returns null if not set (dom/namespaces.html)
  case OwnerDocument:
    return getDOMNode(exec,node.ownerDocument());
  case OnAbort:
    return getListener(DOM::EventImpl::ABORT_EVENT);
  case OnBlur:
    return getListener(DOM::EventImpl::BLUR_EVENT);
  case OnChange:
    return getListener(DOM::EventImpl::CHANGE_EVENT);
  case OnClick:
    return getListener(DOM::EventImpl::TDEHTML_ECMA_CLICK_EVENT);
  case OnDblClick:
    return getListener(DOM::EventImpl::TDEHTML_ECMA_DBLCLICK_EVENT);
  case OnDragDrop:
    return getListener(DOM::EventImpl::TDEHTML_DRAGDROP_EVENT);
  case OnError:
    return getListener(DOM::EventImpl::ERROR_EVENT);
  case OnFocus:
    return getListener(DOM::EventImpl::FOCUS_EVENT);
  case OnKeyDown:
    return getListener(DOM::EventImpl::KEYDOWN_EVENT);
  case OnKeyPress:
    return getListener(DOM::EventImpl::KEYPRESS_EVENT);
  case OnKeyUp:
    return getListener(DOM::EventImpl::KEYUP_EVENT);
  case OnLoad:
    return getListener(DOM::EventImpl::LOAD_EVENT);
  case OnMouseDown:
    return getListener(DOM::EventImpl::MOUSEDOWN_EVENT);
  case OnMouseMove:
    return getListener(DOM::EventImpl::MOUSEMOVE_EVENT);
  case OnMouseOut:
    return getListener(DOM::EventImpl::MOUSEOUT_EVENT);
  case OnMouseOver:
    return getListener(DOM::EventImpl::MOUSEOVER_EVENT);
  case OnMouseUp:
    return getListener(DOM::EventImpl::MOUSEUP_EVENT);
  case OnMove:
    return getListener(DOM::EventImpl::TDEHTML_MOVE_EVENT);
  case OnReset:
    return getListener(DOM::EventImpl::RESET_EVENT);
  case OnResize:
    return getListener(DOM::EventImpl::RESIZE_EVENT);
  case OnSelect:
    return getListener(DOM::EventImpl::SELECT_EVENT);
  case OnSubmit:
    return getListener(DOM::EventImpl::SUBMIT_EVENT);
  case OnUnload:
    return getListener(DOM::EventImpl::UNLOAD_EVENT);
  case SourceIndex: {
    // Retrieves the ordinal position of the object, in source order, as the object
    // appears in the document's all collection
    // i.e. document.all[n.sourceIndex] == n
    DOM::Document doc = node.ownerDocument();
    if (doc.isHTMLDocument()) {
      DOM::HTMLCollection all = static_cast<DOM::HTMLDocument>(doc).all();
      unsigned long i = 0;
      DOM::Node n = all.firstItem();
      for ( ; !n.isNull() && n != node; n = all.nextItem() )
        ++i;
      Q_ASSERT( !n.isNull() ); // node not in document.all !?
      return Number(i);
    }
  }
  default:
    // no DOM standard, found in IE only

    // Make sure our layout is up to date before we allow a query on these attributes.
    DOM::DocumentImpl* docimpl = node.handle()->getDocument();
    if (docimpl) {
      docimpl->updateLayout();
    }

    tdehtml::RenderObject *rend = node.handle()->renderer();

    //In quirks mode, may need to forward if to body.
    rend = handleBodyRootQuirk(node, rend, token);

    switch (token) {
    case OffsetLeft:
      return rend ? static_cast<Value>( Number( rend->offsetLeft() ) ) : Number(0);
    case OffsetTop:
      return rend ? static_cast<Value>(  Number( rend->offsetTop() ) ) : Number(0);
    case OffsetWidth:
      return rend ? static_cast<Value>(  Number( rend->offsetWidth() ) ) : Number(0);
    case OffsetHeight:
      return rend ? static_cast<Value>(  Number( rend->offsetHeight() ) ) : Number(0);
    case OffsetParent:
    {
      tdehtml::RenderObject* par = rend ? rend->offsetParent() : 0;
      return getDOMNode( exec, par ? par->element() : 0 );
    }
    case ClientWidth:
      return rend ? static_cast<Value>( Number( rend->clientWidth() ) ) : Number(0);
    case ClientHeight:
      return rend ? static_cast<Value>( Number( rend->clientHeight() ) ) : Number(0);
    case ScrollWidth:
      return rend ? static_cast<Value>( Number(rend->scrollWidth()) ) : Number(0);
    case ScrollHeight:
      return rend ? static_cast<Value>( Number(rend->scrollHeight()) ) : Number(0);
    case ScrollLeft:
      if (rend && rend->layer()) {
          if (rend->isRoot() && !rend->style()->hidesOverflow())
              return Number( node.ownerDocument().view() ? node.ownerDocument().view()->contentsX() : 0);
          return Number( rend->layer()->scrollXOffset() );
      }
      return Number( 0 );
    case ScrollTop:
      if (rend && rend->layer()) {
          if (rend->isRoot() && !rend->style()->hidesOverflow())
              return Number( node.ownerDocument().view() ? node.ownerDocument().view()->contentsY() : 0);
          return Number( rend->layer()->scrollYOffset() );
      }
      return Number( 0 );
    default:
      kdDebug(6070) << "WARNING: Unhandled token in DOMNode::getValueProperty : " << token << endl;
      break;
    }
  }
  return Undefined();
}


void DOMNode::tryPut(ExecState *exec, const Identifier& propertyName, const Value& value, int attr)
{
#ifdef KJS_VERBOSE
  kdDebug(6070) << "DOMNode::tryPut " << propertyName.qstring() << endl;
#endif
  DOMObjectLookupPut<DOMNode,DOMObject>(exec, propertyName, value, attr,
                                        &DOMNodeTable, this );
}

void DOMNode::putValueProperty(ExecState *exec, int token, const Value& value, int /*attr*/)
{
  switch (token) {
  case NodeValue:
    node.setNodeValue(value.toString(exec).string());
    break;
  case Prefix:
    node.setPrefix(value.toString(exec).string());
    break;
  case TextContent:
    node.setTextContent(value.toString(exec).string());
    break;
  case OnAbort:
    setListener(exec,DOM::EventImpl::ABORT_EVENT,value);
    break;
  case OnBlur:
    setListener(exec,DOM::EventImpl::BLUR_EVENT,value);
    break;
  case OnChange:
    setListener(exec,DOM::EventImpl::CHANGE_EVENT,value);
    break;
  case OnClick:
    setListener(exec,DOM::EventImpl::TDEHTML_ECMA_CLICK_EVENT,value);
    break;
  case OnDblClick:
    setListener(exec,DOM::EventImpl::TDEHTML_ECMA_DBLCLICK_EVENT,value);
    break;
  case OnDragDrop:
    setListener(exec,DOM::EventImpl::TDEHTML_DRAGDROP_EVENT,value);
    break;
  case OnError:
    setListener(exec,DOM::EventImpl::ERROR_EVENT,value);
    break;
  case OnFocus:
    setListener(exec,DOM::EventImpl::FOCUS_EVENT,value);
    break;
  case OnKeyDown:
    setListener(exec,DOM::EventImpl::KEYDOWN_EVENT,value);
    break;
  case OnKeyPress:
    setListener(exec,DOM::EventImpl::KEYPRESS_EVENT,value);
    break;
  case OnKeyUp:
    setListener(exec,DOM::EventImpl::KEYUP_EVENT,value);
    break;
  case OnLoad:
    setListener(exec,DOM::EventImpl::LOAD_EVENT,value);
    break;
  case OnMouseDown:
    setListener(exec,DOM::EventImpl::MOUSEDOWN_EVENT,value);
    break;
  case OnMouseMove:
    setListener(exec,DOM::EventImpl::MOUSEMOVE_EVENT,value);
    break;
  case OnMouseOut:
    setListener(exec,DOM::EventImpl::MOUSEOUT_EVENT,value);
    break;
  case OnMouseOver:
    setListener(exec,DOM::EventImpl::MOUSEOVER_EVENT,value);
    break;
  case OnMouseUp:
    setListener(exec,DOM::EventImpl::MOUSEUP_EVENT,value);
    break;
  case OnMove:
    setListener(exec,DOM::EventImpl::TDEHTML_MOVE_EVENT,value);
    break;
  case OnReset:
    setListener(exec,DOM::EventImpl::RESET_EVENT,value);
    break;
  case OnResize:
    setListener(exec,DOM::EventImpl::RESIZE_EVENT,value);
    break;
  case OnSelect:
    setListener(exec,DOM::EventImpl::SELECT_EVENT,value);
    break;
  case OnSubmit:
    setListener(exec,DOM::EventImpl::SUBMIT_EVENT,value);
    break;
  case OnUnload:
    setListener(exec,DOM::EventImpl::UNLOAD_EVENT,value);
    break;
  default:
    // Make sure our layout is up to date 
    DOM::DocumentImpl* docimpl = node.handle()->getDocument();
    if (docimpl)
      docimpl->updateLayout();

    tdehtml::RenderObject *rend = node.handle() ? node.handle()->renderer() : 0L;

    //In quirks mode, may need to forward.
    rend = handleBodyRootQuirk(node, rend, token);

    switch (token) {
      case ScrollLeft:
        if (rend && rend->layer()) {
          if (rend->style()->hidesOverflow())
            rend->layer()->scrollToXOffset(value.toInt32(exec));
          else if (rend->isRoot()) {
            TQScrollView* sview = node.ownerDocument().view();
            if (sview)
              sview->setContentsPos(value.toInt32(exec), sview->contentsY());
          }
        }
        break;
      case ScrollTop:
        if (rend && rend->layer()) {
          if (rend->style()->hidesOverflow())
            rend->layer()->scrollToYOffset(value.toInt32(exec));
          else if (rend->isRoot()) {
            TQScrollView* sview = node.ownerDocument().view();
            if (sview)
              sview->setContentsPos(sview->contentsX(), value.toInt32(exec));
          }
        }
        break;
      default:
      kdDebug(6070) << "WARNING: DOMNode::putValueProperty unhandled token " << token << endl;
    }
  }
}

Value DOMNode::toPrimitive(ExecState *exec, Type /*preferred*/) const
{
  if (node.isNull())
    return Null();

  return String(toString(exec));
}

UString DOMNode::toString(ExecState *) const
{
  if (node.isNull())
    return "null";
  UString s;

  DOM::Element e = node;
  if ( !e.isNull() ) {
    s = static_cast<UString>(e.nodeName().string());
  } else
    s = className(); // fallback

  return "[object " + s + "]";
}

void DOMNode::setListener(ExecState *exec, int eventId, const Value& func) const
{
  node.handle()->setHTMLEventListener(eventId,Window::retrieveActive(exec)->getJSEventListener(func,true));
}

Value DOMNode::getListener(int eventId) const
{
  DOM::EventListener *listener = node.handle()->getHTMLEventListener(eventId);
  JSEventListener *jsListener = static_cast<JSEventListener*>(listener);
  if ( jsListener && jsListener->listenerObjImp() )
    return jsListener->listenerObj();
  else
    return Null();
}

void DOMNode::pushEventHandlerScope(ExecState *, ScopeChain &) const
{
}

Value DOMNodeProtoFunc::tryCall(ExecState *exec, Object &thisObj, const List &args)
{
  KJS_CHECK_THIS( DOMNode, thisObj );
  DOM::Node node = static_cast<DOMNode *>( thisObj.imp() )->toNode();
  switch (id) {
    case DOMNode::HasAttributes:
      return Boolean(node.hasAttributes());
    case DOMNode::HasChildNodes:
      return Boolean(node.hasChildNodes());
    case DOMNode::CloneNode:
      return getDOMNode(exec,node.cloneNode(args[0].toBoolean(exec)));
    case DOMNode::Normalize:
      node.normalize();
      return Undefined();
    case DOMNode::IsSupported:
      return Boolean(node.isSupported(args[0].toString(exec).string(),args[1].toString(exec).string()));
    case DOMNode::AddEventListener: {
        JSEventListener *listener = Window::retrieveActive(exec)->getJSEventListener(args[1]);
        node.addEventListener(args[0].toString(exec).string(),listener,args[2].toBoolean(exec));
        return Undefined();
    }
    case DOMNode::RemoveEventListener: {
        JSEventListener *listener = Window::retrieveActive(exec)->getJSEventListener(args[1]);
        node.removeEventListener(args[0].toString(exec).string(),listener,args[2].toBoolean(exec));
        return Undefined();
    }
    case DOMNode::DispatchEvent:
      return Boolean(node.dispatchEvent(toEvent(args[0])));
    case DOMNode::AppendChild:
      return getDOMNode(exec,node.appendChild(toNode(args[0])));
    case DOMNode::RemoveChild:
      return getDOMNode(exec,node.removeChild(toNode(args[0])));
    case DOMNode::InsertBefore:
      return getDOMNode(exec,node.insertBefore(toNode(args[0]), toNode(args[1])));
    case DOMNode::ReplaceChild:
      return getDOMNode(exec,node.replaceChild(toNode(args[0]), toNode(args[1])));
    case DOMNode::Contains:
    {
	DOM::Node other = toNode(args[0]);
	if (!other.isNull() && node.nodeType()==DOM::Node::ELEMENT_NODE)
	{
	    DOM::NodeBaseImpl *impl = static_cast<DOM::NodeBaseImpl *>(node.handle());
	    bool retval = other.handle()->isAncestor(impl);
	    return Boolean(retval);
	}
        return Undefined();
    }
    case DOMNode::InsertAdjacentHTML:
    {
      // see http://www.faqts.com/knowledge_base/view.phtml/aid/5756
      // and http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/insertAdjacentHTML.asp
      Range range = node.ownerDocument().createRange();

      range.setStartBefore(node);

      DocumentFragment docFrag = range.createContextualFragment(args[1].toString(exec).string());

      DOMString where = args[0].toString(exec).string();

      if (where == "beforeBegin" || where == "BeforeBegin") {
        node.parentNode().insertBefore(docFrag, node);
      }
      else if (where == "afterBegin" || where == "AfterBegin") {
        node.insertBefore(docFrag, node.firstChild());
      }
      else if (where == "beforeEnd" || where == "BeforeEnd") {
        return getDOMNode(exec, node.appendChild(docFrag));
      }
      else if (where == "afterEnd" || where == "AfterEnd") {
        if (!node.nextSibling().isNull()) {
	  node.parentNode().insertBefore(docFrag, node.nextSibling());
	}
	else {
	  node.parentNode().appendChild(docFrag);
	}
      }

      return Undefined();
    }
    case DOMNode::Item:
      return getDOMNode(exec, node.childNodes().item(static_cast<unsigned long>(args[0].toNumber(exec))));
  }

  return Undefined();
}

// -------------------------------------------------------------------------

/*
@begin DOMNodeListProtoTable 2
  item		DOMNodeList::Item		DontDelete|Function 1
# IE extension (IE treats DOMNodeList like an HTMLCollection)
  namedItem	DOMNodeList::NamedItem		DontDelete|Function 1
@end
*/
KJS_DEFINE_PROTOTYPE(DOMNodeListProto)
IMPLEMENT_PROTOFUNC_DOM(DOMNodeListProtoFunc)
KJS_IMPLEMENT_PROTOTYPE("DOMNodeList", DOMNodeListProto, DOMNodeListProtoFunc)


const ClassInfo DOMNodeList::info = { "NodeList", 0, 0, 0 };

DOMNodeList::DOMNodeList(ExecState *exec, const DOM::NodeList& l)
 : DOMObject(DOMNodeListProto::self(exec)), list(l) { }

DOMNodeList::~DOMNodeList()
{
  ScriptInterpreter::forgetDOMObject(list.handle());
}

// We have to implement hasProperty since we don't use a hashtable for 'length'
// ## this breaks "for (..in..)" though.
bool DOMNodeList::hasProperty(ExecState *exec, const Identifier &p) const
{
  if (p == lengthPropertyName)
    return true;

  if (ObjectImp::hasProperty(exec, p))
    return true;

  bool ok;
  unsigned long pos = p.toULong(&ok);
  if (ok && pos < list.length())
    return true;

  // ## missing: accept p if item id...
  return false;
}

Value DOMNodeList::tryGet(ExecState *exec, const Identifier &p) const
{
#ifdef KJS_VERBOSE
  kdDebug(6070) << "DOMNodeList::tryGet " << p.ascii() << endl;
#endif
  if (p == lengthPropertyName)
    return Number(list.length());

  // Look in the prototype (for functions) before assuming it's an item's name
  Object proto = Object::dynamicCast(prototype());
  if (proto.isValid() && proto.hasProperty(exec,p))
    return proto.get(exec,p);

  Value result;

  // array index ?
  bool ok;
  long unsigned int idx = p.toULong(&ok);
  if (ok)
    result = getDOMNode(exec,list.item(idx));
  else {
    // Find by ID
    DOM::HTMLElement e;
    unsigned long l = list.length();
    bool found = false;

    for ( unsigned long i = 0; i < l; i++ )
      if ( ( e = list.item( i ) ).id() == p.string() ) {
        result = getDOMNode(exec, list.item( i ) );
        found = true;
        break;
      }

    if ( !found )
      result = ObjectImp::get(exec, p);
  }

  return result;
}

ReferenceList DOMNodeList::propList(ExecState *exec, bool recursive)
{
  ReferenceList properties = ObjectImp::propList(exec,recursive);

  for (unsigned i = 0; i < list.length(); ++i) {
    if (!ObjectImp::hasProperty(exec,Identifier::from(i))) {
      properties.append(Reference(this, i));
    }
  }

  if (!ObjectImp::hasProperty(exec, lengthPropertyName))
    properties.append(Reference(this, lengthPropertyName));

  return properties;
}

// Need to support both get and call, so that list[0] and list(0) work.
Value DOMNodeList::call(ExecState *exec, Object &thisObj, const List &args)
{
  // This code duplication is necessary, DOMNodeList isn't a DOMFunction
  Value val;
  try {
    val = tryCall(exec, thisObj, args);
  }
  // pity there's no way to distinguish between these in JS code
  catch (...) {
    Object err = Error::create(exec, GeneralError, "Exception from DOMNodeList");
    exec->setException(err);
  }
  return val;
}

Value DOMNodeList::tryCall(ExecState *exec, Object &, const List &args)
{
  // Do not use thisObj here. See HTMLCollection.
  UString s = args[0].toString(exec);

  // index-based lookup?
  bool ok;
  unsigned int u = s.toULong(&ok);
  if (ok)
    return getDOMNode(exec,list.item(u));

  // try lookup by name
  // ### NodeList::namedItem() would be cool to have
  // ### do we need to support the same two arg overload as in HTMLCollection?
  Value result = tryGet(exec, Identifier(s));

  if (result.isValid())
    return result;

  return Undefined();
}

// Not a prototype class currently, but should probably be converted to one
Value DOMNodeListProtoFunc::tryCall(ExecState *exec, Object &thisObj, const List &args)
{
  KJS_CHECK_THIS( KJS::DOMNodeList, thisObj );
  DOM::NodeList list = static_cast<DOMNodeList *>(thisObj.imp())->nodeList();
  switch (id) {
  case KJS::DOMNodeList::Item:
    return getDOMNode(exec, list.item(args[0].toInt32(exec)));
  case KJS::DOMNodeList::NamedItem:
  {
    // Not a real namedItem implementation like the one HTMLCollection has.
    // This is only an IE extension...
    DOM::HTMLElement e;
    unsigned long len = list.length();
    DOM::DOMString s = args[0].toString(exec).string();

    for ( unsigned long i = 0; i < len; i++ )
    {
      e = list.item( i );
      if ( !e.isNull() && (
             e.id() == s || static_cast<ElementImpl *>(e.handle())->getAttribute(ATTR_NAME) == s )
      )
      {
        return getDOMNode(exec, e );
      }
    }
    return Null(); // see HTMLCollection::NamedItem implementation
  }
  default:
    return Undefined();
  }
}

// -------------------------------------------------------------------------

//### FIXME: link to the node prototype.
const ClassInfo DOMAttr::info = { "Attr", &DOMNode::info, &DOMAttrTable, 0 };

/* Source for DOMAttrTable.
@begin DOMAttrTable 5
  name		DOMAttr::Name		DontDelete|ReadOnly
  specified	DOMAttr::Specified	DontDelete|ReadOnly
  value		DOMAttr::ValueProperty	DontDelete
  ownerElement	DOMAttr::OwnerElement	DontDelete|ReadOnly
@end
*/
Value DOMAttr::tryGet(ExecState *exec, const Identifier &propertyName) const
{
#ifdef KJS_VERBOSE
  kdDebug(6070) << "DOMAttr::tryGet " << propertyName.qstring() << endl;
#endif
  return DOMObjectLookupGetValue<DOMAttr,DOMNode>(exec, propertyName,
                                                  &DOMAttrTable, this );
}

Value DOMAttr::getValueProperty(ExecState *exec, int token) const
{
  switch (token) {
  case Name:
    return String(static_cast<DOM::Attr>(node).name());
  case Specified:
    return Boolean(static_cast<DOM::Attr>(node).specified());
  case ValueProperty:
    return String(static_cast<DOM::Attr>(node).value());
  case OwnerElement: // DOM2
    return getDOMNode(exec,static_cast<DOM::Attr>(node).ownerElement());
  }
  return Value(); // not reached
}

void DOMAttr::tryPut(ExecState *exec, const Identifier &propertyName, const Value& value, int attr)
{
#ifdef KJS_VERBOSE
  kdDebug(6070) << "DOMAttr::tryPut " << propertyName.qstring() << endl;
#endif
  DOMObjectLookupPut<DOMAttr,DOMNode>(exec, propertyName, value, attr,
                                      &DOMAttrTable, this );
}

void DOMAttr::putValueProperty(ExecState *exec, int token, const Value& value, int /*attr*/)
{
  switch (token) {
  case ValueProperty:
    static_cast<DOM::Attr>(node).setValue(value.toString(exec).string());
    return;
  default:
    kdDebug(6070) << "WARNING: DOMAttr::putValueProperty unhandled token " << token << endl;
  }
}

// -------------------------------------------------------------------------

/* Source for DOMDocumentProtoTable.
@begin DOMDocumentProtoTable 23
  createElement   DOMDocument::CreateElement                   DontDelete|Function 1
  createDocumentFragment DOMDocument::CreateDocumentFragment   DontDelete|Function 1
  createTextNode  DOMDocument::CreateTextNode                  DontDelete|Function 1
  createComment   DOMDocument::CreateComment                   DontDelete|Function 1
  createCDATASection DOMDocument::CreateCDATASection           DontDelete|Function 1
  createProcessingInstruction DOMDocument::CreateProcessingInstruction DontDelete|Function 1
  createAttribute DOMDocument::CreateAttribute                 DontDelete|Function 1
  createEntityReference DOMDocument::CreateEntityReference     DontDelete|Function 1
  getElementsByTagName  DOMDocument::GetElementsByTagName      DontDelete|Function 1
  importNode           DOMDocument::ImportNode                 DontDelete|Function 2
  createElementNS      DOMDocument::CreateElementNS            DontDelete|Function 2
  createAttributeNS    DOMDocument::CreateAttributeNS          DontDelete|Function 2
  getElementsByTagNameNS  DOMDocument::GetElementsByTagNameNS  DontDelete|Function 2
  getElementById     DOMDocument::GetElementById               DontDelete|Function 1
  createRange        DOMDocument::CreateRange                  DontDelete|Function 0
  createNodeIterator DOMDocument::CreateNodeIterator           DontDelete|Function 3
  createTreeWalker   DOMDocument::CreateTreeWalker             DontDelete|Function 4
  createEvent        DOMDocument::CreateEvent                  DontDelete|Function 1
  getOverrideStyle   DOMDocument::GetOverrideStyle             DontDelete|Function 2
  abort              DOMDocument::Abort                        DontDelete|Function 0
  load               DOMDocument::Load                         DontDelete|Function 1
  loadXML            DOMDocument::LoadXML                      DontDelete|Function 2
@end
*/
IMPLEMENT_PROTOFUNC_DOM(DOMDocumentProtoFunc)
KJS_IMPLEMENT_PROTOTYPE("DOMDocument", DOMDocumentProto, DOMDocumentProtoFunc)
IMPLEMENT_PSEUDO_CONSTRUCTOR(DocumentPseudoCtor, "Document", DOMDocumentProto)

const ClassInfo DOMDocument::info = { "Document", &DOMNode::info, &DOMDocumentTable, 0 };

/* Source for DOMDocumentTable.
@begin DOMDocumentTable 4
  doctype         DOMDocument::DocType                         DontDelete|ReadOnly
  implementation  DOMDocument::Implementation                  DontDelete|ReadOnly
  characterSet    DOMDocument::CharacterSet                    DontDelete|ReadOnly
  documentElement DOMDocument::DocumentElement                 DontDelete|ReadOnly
  styleSheets     DOMDocument::StyleSheets                     DontDelete|ReadOnly
  preferredStylesheetSet  DOMDocument::PreferredStylesheetSet  DontDelete|ReadOnly
  selectedStylesheetSet  DOMDocument::SelectedStylesheetSet    DontDelete
  readyState      DOMDocument::ReadyState                      DontDelete|ReadOnly
  defaultView     DOMDocument::DefaultView                     DontDelete|ReadOnly
  async           DOMDocument::Async                           DontDelete
@end
*/

DOMDocument::DOMDocument(ExecState *exec, const DOM::Document& d)
  : DOMNode(DOMDocumentProto::self(exec), d) { }

DOMDocument::DOMDocument(const Object& proto, const DOM::Document& d)
  : DOMNode(proto, d) { }

DOMDocument::~DOMDocument()
{
  ScriptInterpreter::forgetDOMObject(node.handle());
}

Value DOMDocument::tryGet(ExecState *exec, const Identifier &propertyName) const
{
#ifdef KJS_VERBOSE
  kdDebug(6070) << "DOMDocument::tryGet " << propertyName.qstring() << endl;
#endif
  return DOMObjectLookupGetValue<DOMDocument, DOMNode>(
    exec, propertyName, &DOMDocumentTable, this);
}

Value DOMDocument::getValueProperty(ExecState *exec, int token) const
{
  DOM::Document doc = static_cast<DOM::Document>(node);

  switch(token) {
  case DocType:
    return getDOMNode(exec,doc.doctype());
  case Implementation:
    return getDOMDOMImplementation(exec,doc.implementation());
  case DocumentElement:
    return getDOMNode(exec,doc.documentElement());
  case CharacterSet: {
    DOM::DocumentImpl* docImpl = static_cast<DOM::DocumentImpl*>(doc.handle());
    if (docImpl->part())
      return String(docImpl->part()->encoding());
    else
      return Undefined();
  }
  case StyleSheets:
    //kdDebug() << "DOMDocument::StyleSheets, returning " << doc.styleSheets().length() << " stylesheets" << endl;
    return getDOMStyleSheetList(exec, doc.styleSheets(), doc);
  case DOMDocument::DefaultView: // DOM2
    {
    TDEHTMLView *view = node.handle()->getDocument()->view();
    if (view)
        return Window::retrieve(view->part());
    return getDOMAbstractView(exec, doc.defaultView());
    }
  case PreferredStylesheetSet:
    return String(doc.preferredStylesheetSet());
  case SelectedStylesheetSet:
    return String(doc.selectedStylesheetSet());
  case ReadyState:
    {
    DOM::DocumentImpl* docimpl = node.handle()->getDocument();
    if ( docimpl && docimpl->view() )
    {
      TDEHTMLPart* part = docimpl->view()->part();
      if ( part ) {
        if (part->d->m_bComplete) return String("complete");
        if (docimpl->parsing()) return String("loading");
        return String("loaded");
        // What does the interactive value mean ?
        // Missing support for "uninitialized"
      }
    }
    return Undefined();
    }
  case Async:
    return Boolean(doc.async());
  default:
    kdDebug(6070) << "WARNING: DOMDocument::getValueProperty unhandled token " << token << endl;
    return Value();
  }
}

void DOMDocument::tryPut(ExecState *exec, const Identifier& propertyName, const Value& value, int attr)
{
#ifdef KJS_VERBOSE
  kdDebug(6070) << "DOMDocument::tryPut " << propertyName.qstring() << endl;
#endif
  DOMObjectLookupPut<DOMDocument,DOMNode>(exec, propertyName, value, attr, &DOMDocumentTable, this );
}

void DOMDocument::putValueProperty(ExecState *exec, int token, const Value& value, int /*attr*/)
{
  DOM::Document doc = static_cast<DOM::Document>(node);
  switch (token) {
    case SelectedStylesheetSet: {
      doc.setSelectedStylesheetSet(value.toString(exec).string());
      break;
    }
    case Async: {
      doc.setAsync(value.toBoolean(exec));
      break;
    }
  }
}

Value DOMDocumentProtoFunc::tryCall(ExecState *exec, Object &thisObj, const List &args)
{
  KJS_CHECK_THIS( KJS::DOMDocument, thisObj );
  DOM::Node node = static_cast<DOMNode *>( thisObj.imp() )->toNode();
  DOM::Document doc = static_cast<DOM::Document>(node);
  String str = args[0].toString(exec);
  DOM::DOMString s = str.value().string();

  switch(id) {
  case DOMDocument::CreateElement:
    return getDOMNode(exec,doc.createElement(s));
  case DOMDocument::CreateDocumentFragment:
    return getDOMNode(exec,doc.createDocumentFragment());
  case DOMDocument::CreateTextNode:
    return getDOMNode(exec,doc.createTextNode(s));
  case DOMDocument::CreateComment:
    return getDOMNode(exec,doc.createComment(s));
  case DOMDocument::CreateCDATASection:
    return getDOMNode(exec,doc.createCDATASection(s));  /* TODO: okay ? */
  case DOMDocument::CreateProcessingInstruction:
    return getDOMNode(exec,doc.createProcessingInstruction(args[0].toString(exec).string(),
                                                                 args[1].toString(exec).string()));
  case DOMDocument::CreateAttribute:
    return getDOMNode(exec,doc.createAttribute(s));
  case DOMDocument::CreateEntityReference:
    return getDOMNode(exec,doc.createEntityReference(args[0].toString(exec).string()));
  case DOMDocument::GetElementsByTagName:
    return getDOMNodeList(exec,doc.getElementsByTagName(s));
  case DOMDocument::ImportNode: // DOM2
    return getDOMNode(exec,doc.importNode(toNode(args[0]), args[1].toBoolean(exec)));
  case DOMDocument::CreateElementNS: // DOM2
    return getDOMNode(exec,doc.createElementNS(args[0].toString(exec).string(), args[1].toString(exec).string()));
  case DOMDocument::CreateAttributeNS: // DOM2
    return getDOMNode(exec,doc.createAttributeNS(args[0].toString(exec).string(),args[1].toString(exec).string()));
  case DOMDocument::GetElementsByTagNameNS: // DOM2
    return getDOMNodeList(exec,doc.getElementsByTagNameNS(args[0].toString(exec).string(),
                                                          args[1].toString(exec).string()));
  case DOMDocument::GetElementById:
#ifdef KJS_VERBOSE
  kdDebug(6070) << "DOMDocument::GetElementById looking for " << args[0].toString(exec).string() << endl;
#endif
    return getDOMNode(exec,doc.getElementById(args[0].toString(exec).string()));
  case DOMDocument::CreateRange:
    return getDOMRange(exec,doc.createRange());
  case DOMDocument::CreateNodeIterator:
    if (args[2].isA(NullType)) {
        DOM::NodeFilter filter;
        return getDOMNodeIterator(exec,
                                  doc.createNodeIterator(toNode(args[0]),
                                                         (long unsigned int)(args[1].toNumber(exec)),
                                                         filter,args[3].toBoolean(exec)));
    }
    else {
      Object obj = Object::dynamicCast(args[2]);
      if (obj.isValid())
      {
        DOM::CustomNodeFilter *customFilter = new JSNodeFilter(obj);
        DOM::NodeFilter filter = DOM::NodeFilter::createCustom(customFilter);
        return getDOMNodeIterator(exec,
          doc.createNodeIterator(
            toNode(args[0]),(long unsigned int)(args[1].toNumber(exec)),
            filter,args[3].toBoolean(exec)));
      }// else?
    }
  case DOMDocument::CreateTreeWalker:
    return getDOMTreeWalker(exec,doc.createTreeWalker(toNode(args[0]),(long unsigned int)(args[1].toNumber(exec)),
             toNodeFilter(args[2]),args[3].toBoolean(exec)));
  case DOMDocument::CreateEvent:
    return getDOMEvent(exec,doc.createEvent(s));
  case DOMDocument::GetOverrideStyle: {
    DOM::Node arg0 = toNode(args[0]);
    if (arg0.nodeType() != DOM::Node::ELEMENT_NODE)
      return Undefined(); // throw exception?
    else
      return getDOMCSSStyleDeclaration(exec,doc.getOverrideStyle(static_cast<DOM::Element>(arg0),args[1].toString(exec).string()));
  }
  case DOMDocument::Abort:
    doc.abort();
    break;
  case DOMDocument::Load: {
    Window* active = Window::retrieveActive(exec);
    // Complete the URL using the "active part" (running interpreter). We do this for the security
    // check and to make sure we load exactly the same url as we have verified to be safe
    TDEHTMLPart *tdehtmlpart = ::tqt_cast<TDEHTMLPart *>(active->part());
    if (tdehtmlpart) {
      // Security: only allow documents to be loaded from the same host
      TQString dstUrl = tdehtmlpart->htmlDocument().completeURL(s).string();
      KParts::ReadOnlyPart *part = static_cast<KJS::ScriptInterpreter*>(exec->interpreter())->part();
      if (part->url().host() == KURL(dstUrl).host()) {
	kdDebug(6070) << "JavaScript: access granted for document.load() of " << dstUrl << endl;
	doc.load(dstUrl);
      }
      else {
	kdDebug(6070) << "JavaScript: access denied for document.load() of " << dstUrl << endl;
      }
    }
    break;
  }
  case DOMDocument::LoadXML:
    doc.loadXML(s);
    break;
  default:
    break;
  }

  return Undefined();
}

// -------------------------------------------------------------------------

/* Source for DOMElementProtoTable.
@begin DOMElementProtoTable 17
  getAttribute		DOMElement::GetAttribute	DontDelete|Function 1
  setAttribute		DOMElement::SetAttribute	DontDelete|Function 2
  removeAttribute	DOMElement::RemoveAttribute	DontDelete|Function 1
  getAttributeNode	DOMElement::GetAttributeNode	DontDelete|Function 1
  setAttributeNode	DOMElement::SetAttributeNode	DontDelete|Function 2
  removeAttributeNode	DOMElement::RemoveAttributeNode	DontDelete|Function 1
  getElementsByTagName	DOMElement::GetElementsByTagName	DontDelete|Function 1
  hasAttribute		DOMElement::HasAttribute	DontDelete|Function 1
  getAttributeNS	DOMElement::GetAttributeNS	DontDelete|Function 2
  setAttributeNS	DOMElement::SetAttributeNS	DontDelete|Function 3
  removeAttributeNS	DOMElement::RemoveAttributeNS	DontDelete|Function 2
  getAttributeNodeNS	DOMElement::GetAttributeNodeNS	DontDelete|Function 2
  setAttributeNodeNS	DOMElement::SetAttributeNodeNS	DontDelete|Function 1
  getElementsByTagNameNS DOMElement::GetElementsByTagNameNS	DontDelete|Function 2
  hasAttributeNS	DOMElement::HasAttributeNS	DontDelete|Function 2
@end
*/
IMPLEMENT_PROTOFUNC_DOM(DOMElementProtoFunc)
KJS_IMPLEMENT_PROTOTYPE("DOMElement", DOMElementProto, DOMElementProtoFunc)

IMPLEMENT_PSEUDO_CONSTRUCTOR(ElementPseudoCtor, "Element", DOMElementProto)

const ClassInfo DOMElement::info = { "Element", &DOMNode::info, &DOMElementTable, 0 };
/* Source for DOMElementTable.
@begin DOMElementTable 3
  tagName	DOMElement::TagName                         DontDelete|ReadOnly
  style		DOMElement::Style                           DontDelete|ReadOnly
@end
*/
DOMElement::DOMElement(ExecState *exec, const DOM::Element& e)
  : DOMNode(DOMElementProto::self(exec), e) { }

DOMElement::DOMElement(const Object& proto, const DOM::Element& e)
  : DOMNode(proto, e) { }

Value DOMElement::tryGet(ExecState *exec, const Identifier &propertyName) const
{
#ifdef KJS_VERBOSE
  kdDebug(6070) << "DOMElement::tryGet " << propertyName.qstring() << endl;
#endif
  DOM::Element element = static_cast<DOM::Element>(node);

  const HashEntry* entry = Lookup::findEntry(&DOMElementTable, propertyName);
  if (entry)
  {
    switch( entry->value ) {
    case TagName:
      return String(element.tagName());
    case Style:
      return getDOMCSSStyleDeclaration(exec,element.style());
    default:
      kdDebug(6070) << "WARNING: Unhandled token in DOMElement::tryGet : " << entry->value << endl;
      break;
    }
  }
  // We have to check in DOMNode before giving access to attributes, otherwise
  // onload="..." would make onload return the string (attribute value) instead of
  // the listener object (function).
  if (DOMNode::hasProperty(exec, propertyName))
    return DOMNode::tryGet(exec, propertyName);

  DOM::DOMString attr = element.getAttribute( propertyName.string() );
  // Give access to attributes
  if ( !attr.isNull() )
    return String( attr );

  return Undefined();
}

Value DOMElementProtoFunc::tryCall(ExecState *exec, Object &thisObj, const List &args)
{
  KJS_CHECK_THIS( KJS::DOMNode, thisObj ); // node should be enough here, given the cast
  DOM::Node node = static_cast<DOMNode *>( thisObj.imp() )->toNode();
  DOM::Element element = static_cast<DOM::Element>(node);

  switch(id) {
    case DOMElement::GetAttribute:
      /** In theory, we should not return null here, as per DOM. In practice, that 
       breaks websites
      */
      return getString(element.getAttribute(args[0].toString(exec).string()));
    case DOMElement::SetAttribute:
      element.setAttribute(args[0].toString(exec).string(),args[1].toString(exec).string());
      return Undefined();
    case DOMElement::RemoveAttribute:
      element.removeAttribute(args[0].toString(exec).string());
      return Undefined();
    case DOMElement::GetAttributeNode:
      return getDOMNode(exec,element.getAttributeNode(args[0].toString(exec).string()));
    case DOMElement::SetAttributeNode:
      return getDOMNode(exec,element.setAttributeNode(KJS::toNode(args[0])));
    case DOMElement::RemoveAttributeNode:
      return getDOMNode(exec,element.removeAttributeNode(KJS::toNode(args[0])));
    case DOMElement::GetElementsByTagName:
      return getDOMNodeList(exec,element.getElementsByTagName(args[0].toString(exec).string()));
    case DOMElement::HasAttribute: // DOM2
      return Boolean(element.hasAttribute(args[0].toString(exec).string()));
    case DOMElement::GetAttributeNS: // DOM2
      return String(element.getAttributeNS(args[0].toString(exec).string(),args[1].toString(exec).string()));
    case DOMElement::SetAttributeNS: // DOM2
      element.setAttributeNS(args[0].toString(exec).string(),args[1].toString(exec).string(),args[2].toString(exec).string());
      return Undefined();
    case DOMElement::RemoveAttributeNS: // DOM2
      element.removeAttributeNS(args[0].toString(exec).string(),args[1].toString(exec).string());
      return Undefined();
    case DOMElement::GetAttributeNodeNS: // DOM2
      return getDOMNode(exec,element.getAttributeNodeNS(args[0].toString(exec).string(),args[1].toString(exec).string()));
    case DOMElement::SetAttributeNodeNS: // DOM2
      return getDOMNode(exec,element.setAttributeNodeNS(KJS::toNode(args[0])));
    case DOMElement::GetElementsByTagNameNS: // DOM2
      return getDOMNodeList(exec,element.getElementsByTagNameNS(args[0].toString(exec).string(),args[1].toString(exec).string()));
    case DOMElement::HasAttributeNS: // DOM2
      return Boolean(element.hasAttributeNS(args[0].toString(exec).string(),args[1].toString(exec).string()));
  default:
    return Undefined();
  }
}

// -------------------------------------------------------------------------

/* Source for DOMDOMImplementationProtoTable.
@begin DOMDOMImplementationProtoTable 5
  hasFeature		DOMDOMImplementation::HasFeature		DontDelete|Function 2
  createCSSStyleSheet	DOMDOMImplementation::CreateCSSStyleSheet	DontDelete|Function 2
# DOM2
  createDocumentType	DOMDOMImplementation::CreateDocumentType	DontDelete|Function 3
  createDocument	DOMDOMImplementation::CreateDocument		DontDelete|Function 3
  createHTMLDocument    DOMDOMImplementation::CreateHTMLDocument        DontDelete|Function 1
@end
*/
KJS_DEFINE_PROTOTYPE(DOMDOMImplementationProto)
IMPLEMENT_PROTOFUNC_DOM(DOMDOMImplementationProtoFunc)
KJS_IMPLEMENT_PROTOTYPE("DOMImplementation", DOMDOMImplementationProto, DOMDOMImplementationProtoFunc)

const ClassInfo DOMDOMImplementation::info = { "DOMImplementation", 0, 0, 0 };

DOMDOMImplementation::DOMDOMImplementation(ExecState *exec, const DOM::DOMImplementation& i)
  : DOMObject(DOMDOMImplementationProto::self(exec)), implementation(i) { }

DOMDOMImplementation::~DOMDOMImplementation()
{
  ScriptInterpreter::forgetDOMObject(implementation.handle());
}

Value DOMDOMImplementationProtoFunc::tryCall(ExecState *exec, Object &thisObj, const List &args)
{
  KJS_CHECK_THIS( KJS::DOMDOMImplementation, thisObj );
  DOM::DOMImplementation implementation = static_cast<DOMDOMImplementation *>( thisObj.imp() )->toImplementation();

  switch(id) {
  case DOMDOMImplementation::HasFeature:
    return Boolean(implementation.hasFeature(args[0].toString(exec).string(),args[1].toString(exec).string()));
  case DOMDOMImplementation::CreateDocumentType: // DOM2
    return getDOMNode(exec,implementation.createDocumentType(args[0].toString(exec).string(),args[1].toString(exec).string(),args[2].toString(exec).string()));
  case DOMDOMImplementation::CreateDocument: { // DOM2
    // Initially set the URL to document of the creator... this is so that it resides in the same
    // host/domain for security checks. The URL will be updated if Document.load() is called.
    TDEHTMLPart *part = ::tqt_cast<TDEHTMLPart*>(static_cast<KJS::ScriptInterpreter*>(exec->interpreter())->part());
    if (part) {
      Document doc = implementation.createDocument(args[0].toString(exec).string(),args[1].toString(exec).string(),toNode(args[2]));
      KURL url = static_cast<DocumentImpl*>(part->document().handle())->URL();
      static_cast<DocumentImpl*>(doc.handle())->setURL(url.url());
      return getDOMNode(exec,doc);
    }
    break;
  }
  case DOMDOMImplementation::CreateCSSStyleSheet: // DOM2
    return getDOMStyleSheet(exec,implementation.createCSSStyleSheet(args[0].toString(exec).string(),args[1].toString(exec).string()));
  case DOMDOMImplementation::CreateHTMLDocument: // DOM2-HTML
    return getDOMNode(exec, implementation.createHTMLDocument(args[0].toString(exec).string()));
  default:
    break;
  }
  return Undefined();
}

// -------------------------------------------------------------------------

const ClassInfo DOMDocumentType::info = { "DocumentType", &DOMNode::info, &DOMDocumentTypeTable, 0 };

/* Source for DOMDocumentTypeTable.
@begin DOMDocumentTypeTable 6
  name			DOMDocumentType::Name		DontDelete|ReadOnly
  entities		DOMDocumentType::Entities	DontDelete|ReadOnly
  notations		DOMDocumentType::Notations	DontDelete|ReadOnly
# DOM2
  publicId		DOMDocumentType::PublicId	DontDelete|ReadOnly
  systemId		DOMDocumentType::SystemId	DontDelete|ReadOnly
  internalSubset	DOMDocumentType::InternalSubset	DontDelete|ReadOnly
@end
*/
DOMDocumentType::DOMDocumentType(ExecState *exec, const DOM::DocumentType& dt)
  : DOMNode( /*### no proto yet*/exec, dt ) { }

Value DOMDocumentType::tryGet(ExecState *exec, const Identifier &propertyName) const
{
#ifdef KJS_VERBOSE
  kdDebug(6070) << "DOMDocumentType::tryGet " << propertyName.qstring() << endl;
#endif
  return DOMObjectLookupGetValue<DOMDocumentType, DOMNode>(exec, propertyName, &DOMDocumentTypeTable, this);
}

Value DOMDocumentType::getValueProperty(ExecState *exec, int token) const
{
  DOM::DocumentType type = static_cast<DOM::DocumentType>(node);
  switch (token) {
  case Name:
    return String(type.name());
  case Entities:
    return getDOMNamedNodeMap(exec,type.entities());
  case Notations:
    return getDOMNamedNodeMap(exec,type.notations());
  case PublicId: // DOM2
    return String(type.publicId());
  case SystemId: // DOM2
    return String(type.systemId());
  case InternalSubset: // DOM2
    return getString(type.internalSubset()); // can be null, see domts/level2/core/internalSubset01.html
  default:
    kdDebug(6070) << "WARNING: DOMDocumentType::getValueProperty unhandled token " << token << endl;
    return Value();
  }
}

// -------------------------------------------------------------------------

/* Source for DOMNamedNodeMapProtoTable.
@begin DOMNamedNodeMapProtoTable 7
  getNamedItem		DOMNamedNodeMap::GetNamedItem		DontDelete|Function 1
  setNamedItem		DOMNamedNodeMap::SetNamedItem		DontDelete|Function 1
  removeNamedItem	DOMNamedNodeMap::RemoveNamedItem	DontDelete|Function 1
  item			DOMNamedNodeMap::Item			DontDelete|Function 1
# DOM2
  getNamedItemNS	DOMNamedNodeMap::GetNamedItemNS		DontDelete|Function 2
  setNamedItemNS	DOMNamedNodeMap::SetNamedItemNS		DontDelete|Function 1
  removeNamedItemNS	DOMNamedNodeMap::RemoveNamedItemNS	DontDelete|Function 2
@end
@begin DOMNamedNodeMapTable 7
  length		DOMNamedNodeMap::Length			DontDelete|Function 1
@end
*/
KJS_DEFINE_PROTOTYPE(DOMNamedNodeMapProto)
IMPLEMENT_PROTOFUNC_DOM(DOMNamedNodeMapProtoFunc)
KJS_IMPLEMENT_PROTOTYPE("NamedNodeMap", DOMNamedNodeMapProto, DOMNamedNodeMapProtoFunc)

const ClassInfo DOMNamedNodeMap::info = { "NamedNodeMap", 0, &DOMNamedNodeMapTable, 0 };

DOMNamedNodeMap::DOMNamedNodeMap(ExecState *exec, const DOM::NamedNodeMap& m)
  : DOMObject(DOMNamedNodeMapProto::self(exec)), map(m) { }

DOMNamedNodeMap::~DOMNamedNodeMap()
{
  ScriptInterpreter::forgetDOMObject(map.handle());
}

bool DOMNamedNodeMap::hasProperty(ExecState *exec, const Identifier &p) const
{
  // ## missing? array index
  return DOMObject::hasProperty(exec, p);
}

Value DOMNamedNodeMap::tryGet(ExecState* exec, const Identifier &p) const
{
  if (p == lengthPropertyName)
    return Number(map.length());

  // array index ?
  bool ok;
  long unsigned int idx = p.toULong(&ok);
  if (ok)
    return getDOMNode(exec,map.item(idx));

  // Anything else (including functions, defined in the prototype)
  return DOMObject::tryGet(exec, p);
}

Value DOMNamedNodeMapProtoFunc::tryCall(ExecState *exec, Object &thisObj, const List &args)
{
  KJS_CHECK_THIS( KJS::DOMNamedNodeMap, thisObj );
  DOM::NamedNodeMap map = static_cast<DOMNamedNodeMap *>(thisObj.imp())->toMap();

  switch(id) {
    case DOMNamedNodeMap::GetNamedItem:
      return getDOMNode(exec, map.getNamedItem(args[0].toString(exec).string()));
    case DOMNamedNodeMap::SetNamedItem:
      return getDOMNode(exec, map.setNamedItem(KJS::toNode(args[0])));
    case DOMNamedNodeMap::RemoveNamedItem:
      return getDOMNode(exec, map.removeNamedItem(args[0].toString(exec).string()));
    case DOMNamedNodeMap::Item:
      return getDOMNode(exec, map.item(args[0].toInt32(exec)));
    case DOMNamedNodeMap::GetNamedItemNS: // DOM2
      return getDOMNode(exec, map.getNamedItemNS(args[0].toString(exec).string(),args[1].toString(exec).string()));
    case DOMNamedNodeMap::SetNamedItemNS: // DOM2
      return getDOMNode(exec, map.setNamedItemNS(toNode(args[0])));
    case DOMNamedNodeMap::RemoveNamedItemNS: // DOM2
      return getDOMNode(exec, map.removeNamedItemNS(args[0].toString(exec).string(),args[1].toString(exec).string()));
    default:
      break;
  }

  return Undefined();
}

// -------------------------------------------------------------------------
//### FIXME: proto
const ClassInfo DOMProcessingInstruction::info = { "ProcessingInstruction", &DOMNode::info, &DOMProcessingInstructionTable, 0 };

/* Source for DOMProcessingInstructionTable.
@begin DOMProcessingInstructionTable 3
  target	DOMProcessingInstruction::Target	DontDelete|ReadOnly
  data		DOMProcessingInstruction::Data		DontDelete
  sheet		DOMProcessingInstruction::Sheet		DontDelete|ReadOnly
@end
*/
Value DOMProcessingInstruction::tryGet(ExecState *exec, const Identifier &propertyName) const
{
  return DOMObjectLookupGetValue<DOMProcessingInstruction, DOMNode>(exec, propertyName, &DOMProcessingInstructionTable, this);
}

Value DOMProcessingInstruction::getValueProperty(ExecState *exec, int token) const
{
  switch (token) {
  case Target:
    return String(static_cast<DOM::ProcessingInstruction>(node).target());
  case Data:
    return String(static_cast<DOM::ProcessingInstruction>(node).data());
  case Sheet:
    return getDOMStyleSheet(exec,static_cast<DOM::ProcessingInstruction>(node).sheet());
  default:
    kdDebug(6070) << "WARNING: DOMProcessingInstruction::getValueProperty unhandled token " << token << endl;
    return Value();
  }
}

void DOMProcessingInstruction::tryPut(ExecState *exec, const Identifier &propertyName, const Value& value, int attr)
{
  // Not worth using the hashtable for this one ;)
  if (propertyName == "data")
    static_cast<DOM::ProcessingInstruction>(node).setData(value.toString(exec).string());
  else
    DOMNode::tryPut(exec, propertyName,value,attr);
}

// -------------------------------------------------------------------------

const ClassInfo DOMNotation::info = { "Notation", &DOMNode::info, &DOMNotationTable, 0 };

/* Source for DOMNotationTable.
@begin DOMNotationTable 2
  publicId		DOMNotation::PublicId	DontDelete|ReadOnly
  systemId		DOMNotation::SystemId	DontDelete|ReadOnly
@end
*/
Value DOMNotation::tryGet(ExecState *exec, const Identifier &propertyName) const
{
  return DOMObjectLookupGetValue<DOMNotation, DOMNode>(exec, propertyName, &DOMNotationTable, this);
}

Value DOMNotation::getValueProperty(ExecState *, int token) const
{
  switch (token) {
  case PublicId:
    return String(static_cast<DOM::Notation>(node).publicId());
  case SystemId:
    return String(static_cast<DOM::Notation>(node).systemId());
  default:
    kdDebug(6070) << "WARNING: DOMNotation::getValueProperty unhandled token " << token << endl;
    return Value();
  }
}

// -------------------------------------------------------------------------

const ClassInfo DOMEntity::info = { "Entity", &DOMNode::info, 0, 0 };

/* Source for DOMEntityTable.
@begin DOMEntityTable 2
  publicId		DOMEntity::PublicId		DontDelete|ReadOnly
  systemId		DOMEntity::SystemId		DontDelete|ReadOnly
  notationName		DOMEntity::NotationName	DontDelete|ReadOnly
@end
*/
Value DOMEntity::tryGet(ExecState *exec, const Identifier &propertyName) const
{
  return DOMObjectLookupGetValue<DOMEntity, DOMNode>(exec, propertyName, &DOMEntityTable, this);
}

Value DOMEntity::getValueProperty(ExecState *, int token) const
{
  switch (token) {
  case PublicId:
    return String(static_cast<DOM::Entity>(node).publicId());
  case SystemId:
    return String(static_cast<DOM::Entity>(node).systemId());
  case NotationName:
    return String(static_cast<DOM::Entity>(node).notationName());
  default:
    kdDebug(6070) << "WARNING: DOMEntity::getValueProperty unhandled token " << token << endl;
    return Value();
  }
}

// -------------------------------------------------------------------------

bool checkNodeSecurity(ExecState *exec, const DOM::Node& n)
{
  // Check to see if the currently executing interpreter is allowed to access the specified node
  if (n.isNull())
    return true;
  TDEHTMLView *view = n.handle()->getDocument()->view();
  Window* win = view && view->part() ? Window::retrieveWindow(view->part()) : 0L;
  if ( !win || !win->isSafeScript(exec) )
    return false;
  return true;
}

Value getDOMNode(ExecState *exec, const DOM::Node& n)
{
  DOMObject *ret = 0;
  if (n.isNull())
    return Null();
  ScriptInterpreter* interp = static_cast<ScriptInterpreter *>(exec->interpreter());
  if ((ret = interp->getDOMObject(n.handle())))
    return Value(ret);

  switch (n.nodeType()) {
    case DOM::Node::ELEMENT_NODE:
      if (static_cast<DOM::Element>(n).isHTMLElement())
        ret = new HTMLElement(exec, static_cast<DOM::HTMLElement>(n));
      else
        ret = new DOMElement(exec, static_cast<DOM::Element>(n));
      break;
    case DOM::Node::ATTRIBUTE_NODE:
      ret = new DOMAttr(exec, static_cast<DOM::Attr>(n));
      break;
    case DOM::Node::TEXT_NODE:
    case DOM::Node::CDATA_SECTION_NODE:
      ret = new DOMText(exec, static_cast<DOM::Text>(n));
      break;
    case DOM::Node::ENTITY_REFERENCE_NODE:
      ret = new DOMNode(exec, n);
      break;
    case DOM::Node::ENTITY_NODE:
      ret = new DOMEntity(exec, static_cast<DOM::Entity>(n));
      break;
    case DOM::Node::PROCESSING_INSTRUCTION_NODE:
      ret = new DOMProcessingInstruction(exec, static_cast<DOM::ProcessingInstruction>(n));
      break;
    case DOM::Node::COMMENT_NODE:
      ret = new DOMCharacterData(exec, static_cast<DOM::CharacterData>(n));
      break;
    case DOM::Node::DOCUMENT_NODE:
      if (static_cast<DOM::Document>(n).isHTMLDocument())
        ret = new HTMLDocument(exec, static_cast<DOM::HTMLDocument>(n));
      else
        ret = new DOMDocument(exec, static_cast<DOM::Document>(n));
      break;
    case DOM::Node::DOCUMENT_TYPE_NODE:
      ret = new DOMDocumentType(exec, static_cast<DOM::DocumentType>(n));
      break;
    case DOM::Node::DOCUMENT_FRAGMENT_NODE:
      ret = new DOMNode(exec, n);
      break;
    case DOM::Node::NOTATION_NODE:
      ret = new DOMNotation(exec, static_cast<DOM::Notation>(n));
      break;
    default:
      ret = new DOMNode(exec, n);
  }
  interp->putDOMObject(n.handle(),ret);

  return Value(ret);
}

Value getDOMNamedNodeMap(ExecState *exec, const DOM::NamedNodeMap& m)
{
  return Value(cacheDOMObject<DOM::NamedNodeMap, KJS::DOMNamedNodeMap>(exec, m));
}

Value getDOMNodeList(ExecState *exec, const DOM::NodeList& l)
{
  return Value(cacheDOMObject<DOM::NodeList, KJS::DOMNodeList>(exec, l));
}

Value getDOMDOMImplementation(ExecState *exec, const DOM::DOMImplementation& i)
{
  return Value(cacheDOMObject<DOM::DOMImplementation, KJS::DOMDOMImplementation>(exec, i));
}

// -------------------------------------------------------------------------
IMPLEMENT_PSEUDO_CONSTRUCTOR_WITH_PARENT(NodeConstructor, "NodeConstructor", DOMNodeProto, DOMNodeConstants)
// -------------------------------------------------------------------------

const ClassInfo DOMExceptionConstructor::info = { "DOMExceptionConstructor", 0, 0, 0 };

/* Source for DOMExceptionConstructorTable.
@begin DOMExceptionConstructorTable 15
  INDEX_SIZE_ERR		DOM::DOMException::INDEX_SIZE_ERR		DontDelete|ReadOnly
  DOMSTRING_SIZE_ERR		DOM::DOMException::DOMSTRING_SIZE_ERR	DontDelete|ReadOnly
  HIERARCHY_REQUEST_ERR		DOM::DOMException::HIERARCHY_REQUEST_ERR	DontDelete|ReadOnly
  WRONG_DOCUMENT_ERR		DOM::DOMException::WRONG_DOCUMENT_ERR	DontDelete|ReadOnly
  INVALID_CHARACTER_ERR		DOM::DOMException::INVALID_CHARACTER_ERR	DontDelete|ReadOnly
  NO_DATA_ALLOWED_ERR		DOM::DOMException::NO_DATA_ALLOWED_ERR	DontDelete|ReadOnly
  NO_MODIFICATION_ALLOWED_ERR	DOM::DOMException::NO_MODIFICATION_ALLOWED_ERR	DontDelete|ReadOnly
  NOT_FOUND_ERR			DOM::DOMException::NOT_FOUND_ERR		DontDelete|ReadOnly
  NOT_SUPPORTED_ERR		DOM::DOMException::NOT_SUPPORTED_ERR	DontDelete|ReadOnly
  INUSE_ATTRIBUTE_ERR		DOM::DOMException::INUSE_ATTRIBUTE_ERR	DontDelete|ReadOnly
  INVALID_STATE_ERR		DOM::DOMException::INVALID_STATE_ERR	DontDelete|ReadOnly
  SYNTAX_ERR			DOM::DOMException::SYNTAX_ERR		DontDelete|ReadOnly
  INVALID_MODIFICATION_ERR	DOM::DOMException::INVALID_MODIFICATION_ERR	DontDelete|ReadOnly
  NAMESPACE_ERR			DOM::DOMException::NAMESPACE_ERR		DontDelete|ReadOnly
  INVALID_ACCESS_ERR		DOM::DOMException::INVALID_ACCESS_ERR	DontDelete|ReadOnly
@end
*/

DOMExceptionConstructor::DOMExceptionConstructor(ExecState* exec)
  : DOMObject(exec->interpreter()->builtinObjectPrototype())
{
}

Value DOMExceptionConstructor::tryGet(ExecState *exec, const Identifier &propertyName) const
{
  return DOMObjectLookupGetValue<DOMExceptionConstructor, DOMObject>(exec, propertyName, &DOMExceptionConstructorTable, this);
}

Value DOMExceptionConstructor::getValueProperty(ExecState *, int token) const
{
  // We use the token as the value to return directly
  return Number((unsigned int)token);
#if 0
  switch (token) {
  case INDEX_SIZE_ERR:
    return Number((unsigned int)DOM::DOMException::INDEX_SIZE_ERR);
  case DOMSTRING_SIZE_ERR:
    return Number((unsigned int)DOM::DOMException::DOMSTRING_SIZE_ERR);
  case HIERARCHY_REQUEST_ERR:
    return Number((unsigned int)DOM::DOMException::HIERARCHY_REQUEST_ERR);
  case WRONG_DOCUMENT_ERR:
    return Number((unsigned int)DOM::DOMException::WRONG_DOCUMENT_ERR);
  case INVALID_CHARACTER_ERR:
    return Number((unsigned int)DOM::DOMException::INVALID_CHARACTER_ERR);
  case NO_DATA_ALLOWED_ERR:
    return Number((unsigned int)DOM::DOMException::NO_DATA_ALLOWED_ERR);
  case NO_MODIFICATION_ALLOWED_ERR:
    return Number((unsigned int)DOM::DOMException::NO_MODIFICATION_ALLOWED_ERR);
  case NOT_FOUND_ERR:
    return Number((unsigned int)DOM::DOMException::NOT_FOUND_ERR);
  case NOT_SUPPORTED_ERR:
    return Number((unsigned int)DOM::DOMException::NOT_SUPPORTED_ERR);
  case INUSE_ATTRIBUTE_ERR:
    return Number((unsigned int)DOM::DOMException::INUSE_ATTRIBUTE_ERR);
  case INVALID_STATE_ERR:
    return Number((unsigned int)DOM::DOMException::INVALID_STATE_ERR);
  case SYNTAX_ERR:
    return Number((unsigned int)DOM::DOMException::SYNTAX_ERR);
  case INVALID_MODIFICATION_ERR:
    return Number((unsigned int)DOM::DOMException::INVALID_MODIFICATION_ERR);
  case NAMESPACE_ERR:
    return Number((unsigned int)DOM::DOMException::NAMESPACE_ERR);
  case INVALID_ACCESS_ERR:
    return Number((unsigned int)DOM::DOMException::INVALID_ACCESS_ERR);
  default:
    kdDebug(6070) << "WARNING: DOMExceptionConstructor::getValueProperty unhandled token " << token << endl;
    return Value();
  }
#endif
}

Object getDOMExceptionConstructor(ExecState *exec)
{
  return cacheGlobalObject<DOMExceptionConstructor>(exec, "[[DOMException.constructor]]");
}

// -------------------------------------------------------------------------

/* Source for DOMNamedNodesCollection.
@begin DOMNamedNodesCollectionTable 1
  length		KJS::DOMNamedNodesCollection::Length		DontDelete|ReadOnly
@end
*/
const ClassInfo KJS::DOMNamedNodesCollection::info = { "DOMNamedNodesCollection", 0, &DOMNamedNodesCollectionTable, 0 };

// Such a collection is usually very short-lived, it only exists
// for constructs like document.forms.<name>[1],
// so it shouldn't be a problem that it's storing all the nodes (with the same name). (David)
DOMNamedNodesCollection::DOMNamedNodesCollection(ExecState *exec, const TQValueList<DOM::Node>& nodes )
  : DOMObject(exec->interpreter()->builtinObjectPrototype()),
  m_nodes(nodes)
{
  // Maybe we should ref (and deref in the dtor) the nodes, though ?
}

Value DOMNamedNodesCollection::tryGet(ExecState *exec, const Identifier &propertyName) const
{
  kdDebug(6070) << k_funcinfo << propertyName.ascii() << endl;
  if (propertyName == lengthPropertyName)
    return Number(m_nodes.count());
  // index?
  bool ok;
  unsigned int u = propertyName.toULong(&ok);
  if (ok && u < m_nodes.count()) {
    DOM::Node node = m_nodes[u];
    return getDOMNode(exec,node);
  }
  return DOMObject::tryGet(exec,propertyName);
}

// -------------------------------------------------------------------------

const ClassInfo DOMCharacterData::info = { "CharacterImp",
					  &DOMNode::info, &DOMCharacterDataTable, 0 };
/*
@begin DOMCharacterDataTable 2
  data		DOMCharacterData::Data		DontDelete
  length	DOMCharacterData::Length	DontDelete|ReadOnly
@end
@begin DOMCharacterDataProtoTable 7
  substringData	DOMCharacterData::SubstringData	DontDelete|Function 2
  appendData	DOMCharacterData::AppendData	DontDelete|Function 1
  insertData	DOMCharacterData::InsertData	DontDelete|Function 2
  deleteData	DOMCharacterData::DeleteData	DontDelete|Function 2
  replaceData	DOMCharacterData::ReplaceData	DontDelete|Function 2
@end
*/
KJS_DEFINE_PROTOTYPE_WITH_PROTOTYPE(DOMCharacterDataProto, DOMNodeProto)
IMPLEMENT_PROTOFUNC_DOM(DOMCharacterDataProtoFunc)
KJS_IMPLEMENT_PROTOTYPE("DOMCharacterData", DOMCharacterDataProto, DOMCharacterDataProtoFunc)

DOMCharacterData::DOMCharacterData(ExecState *exec, const DOM::CharacterData& d)
 : DOMNode(DOMCharacterDataProto::self(exec), d) {}

DOMCharacterData::DOMCharacterData(const Object& proto, const DOM::CharacterData& d)
 : DOMNode(proto, d) {}

Value DOMCharacterData::tryGet(ExecState *exec, const Identifier &p) const
{
#ifdef KJS_VERBOSE
  kdDebug(6070)<<"DOMCharacterData::tryGet "<<p.string().string()<<endl;
#endif
  return DOMObjectLookupGetValue<DOMCharacterData,DOMNode>(exec,p,&DOMCharacterDataTable,this);
}

Value DOMCharacterData::getValueProperty(ExecState *, int token) const
{
  DOM::CharacterData data = static_cast<DOM::CharacterData>(node);
  switch (token) {
  case Data:
    return String(data.data());
  case Length:
    return Number(data.length());
 default:
   kdDebug(6070) << "WARNING: Unhandled token in DOMCharacterData::getValueProperty : " << token << endl;
   return Value();
  }
}

void DOMCharacterData::tryPut(ExecState *exec, const Identifier &propertyName, const Value& value, int attr)
{
  if (propertyName == "data")
    static_cast<DOM::CharacterData>(node).setData(value.toString(exec).string());
  else
    DOMNode::tryPut(exec, propertyName,value,attr);
}

Value DOMCharacterDataProtoFunc::tryCall(ExecState *exec, Object &thisObj, const List &args)
{
  KJS_CHECK_THIS( KJS::DOMCharacterData, thisObj );
  DOM::CharacterData data = static_cast<DOMCharacterData *>(thisObj.imp())->toData();
  switch(id) {
    case DOMCharacterData::SubstringData:
      return String(data.substringData(args[0].toInteger(exec),args[1].toInteger(exec)));
    case DOMCharacterData::AppendData:
      data.appendData(args[0].toString(exec).string());
      return Undefined();
      break;
    case DOMCharacterData::InsertData:
      data.insertData(args[0].toInteger(exec),args[1].toString(exec).string());
      return  Undefined();
      break;
    case DOMCharacterData::DeleteData:
      data.deleteData(args[0].toInteger(exec),args[1].toInteger(exec));
      return  Undefined();
      break;
    case DOMCharacterData::ReplaceData:
      data.replaceData(args[0].toInteger(exec),args[1].toInteger(exec),args[2].toString(exec).string());
      return Undefined();
    default:
      break;
  }
  return Undefined();
}

// -------------------------------------------------------------------------

const ClassInfo DOMText::info = { "Text",
				 &DOMCharacterData::info, 0, 0 };
/*
@begin DOMTextProtoTable 1
  splitText	DOMText::SplitText	DontDelete|Function 1
@end
*/
KJS_DEFINE_PROTOTYPE_WITH_PROTOTYPE(DOMTextProto, DOMCharacterDataProto)
IMPLEMENT_PROTOFUNC_DOM(DOMTextProtoFunc)
KJS_IMPLEMENT_PROTOTYPE("DOMText", DOMTextProto, DOMTextProtoFunc)

DOMText::DOMText(ExecState *exec, const DOM::Text& t)
  : DOMCharacterData(DOMTextProto::self(exec), t) { }

Value DOMText::tryGet(ExecState *exec, const Identifier &p) const
{
  if (p.isEmpty())
    return Undefined(); // ### TODO
  else
    return DOMCharacterData::tryGet(exec, p);
}

Value DOMTextProtoFunc::tryCall(ExecState *exec, Object &thisObj, const List &args)
{
  KJS_CHECK_THIS( KJS::DOMText, thisObj );
  DOM::Text text = static_cast<DOMText *>(thisObj.imp())->toText();
  switch(id) {
    case DOMText::SplitText:
      return getDOMNode(exec,text.splitText(args[0].toInteger(exec)));
    default:
      break;
  }
  return Undefined();
}

}
