//  Keep reference to global scope 

__global__ = this

// IE 5 (except IE Win 5.5) doesn't have Array.push(), so we'll fix
// that here.
Array.prototype.push = function(el) {
   this[this.length] = el
   return this.length
}
Array.prototype.pop = function() {
   if (this.length <= 0) {
      return null
   }
   el = this[this.length-1]
   this.length = this.length-1
   return el
}

Array.prototype.clear = function() {
   this.length = 0
}

// Ask an object to list its properties
Object.prototype.keys = function () {
   var key, result
   result = new Array();
   for (key in this) { if (typeof(key) == 'string' && typeof(this[key] != 'function')) {result.push(key)} }
   return result
}

// Ask an object to list its properties, including "keys" and functions.
Object.prototype.allkeys = function () {
   var key, result
   result = new Array();
   for (key in this) { result.push(key) }
   return result
}

// Call all property change listeners. Return true if none "veto"
// (Veto exception not yet implemented)
Object.prototype.notifyPropertyChange = function (propname, oldval, newval) {
   var pcl, listeners, i
   if (!((pcl = this.pclisteners) && (listeners = pcl[propname]))) return true
   for (i = 0; i < listeners.length; i++) {
      listeners[i].onPropertyChange(this, propname, oldval, newval)
   }
   return true
}

// Define a property and its accessors
Object.prototype.defineProperty = function(name, value) {
   var self = this

   if (typeof(this[name]) != "undefined") {
      alert("Tried to define existing property " + name)
      return
   }

   // Create property accessors
   var fn = name.charAt(0).toUpperCase() + name.substring(1, name.length)
   var settername = "set" + fn
   var gettername = "get" + fn

   // Define unless overridden
   if (typeof(this[gettername]) == "undefined") {
      this[gettername] = function () { return this[name] }
   }

   // Define unless overridden
   if (typeof(this[settername]) == "undefined") {
      this[settername] = function (val) {
         var ok = self.notifyPropertyChange(name, self[name], val)
         if (ok) {
            self[name] = val
         }
      }
   }

   // You don't get property change events on initialization, because
   // initializing isn't a "change".
   this[name] = value   
}

Object.prototype.addPropertyChangeListener = function(propname, obj) {
   var pcl, listeners
   if (!(pcl = this.pclisteners)) {
      pcl = this.pclisteners = new Object()
   }
   if (!(listeners = pcl[propname])) {
      listeners = pcl[propname] = new Array()
   }
   listeners.push(obj)
}

Object.prototype.removePropertyChangeListener = function(propname, obj) {
   var pcl, listeners, i
   if (!((pcl = this.pclisteners) && (listeners = pcl[propname]))) {
      return
   }
   for (i = 0; i < listeners.length; i++) {
      if (listeners[i] == obj) {
         listeners.splice(i, 1)
         break
      }
   }
}

// Property change events must return true. If one returns false,
// the property change is disallowed. Override this method to implement
// your own.
Object.prototype.onPropertyChange = function(obj, propname, oldval, newval) {
   return true
}

Object.prototype.addActionListener = function(obj) {
   var al, listeners
   if (!(al = this.alisteners)) {
      al = this.alisteners = new Array()
   }
   al.push(obj)
}

Object.prototype.removeActionListener = function(obj) {
   var al, listeners, i
   if (!(al = this.alisteners)) {
      return
   }
   for (i = 0; i < al.length; i++) {
      if (al[i] == obj) {
         al.splice(i, 1)
         break
      }
   }
}

// Override to supply your own functionality for this.
// "Actiondata" is passed by the firing object (obj).
Object.prototype.onAction = function(obj, actiondata) {
   return true
}

// Call all action listeners.
Object.prototype.fireAction = function (actiondata) {
   var al, listeners, i, n

   n = this.getNode()

   if (n && (al = n.alisteners)) {
      for (i = 0; i < al.length; i++) {
         al[i].onAction.call(al[i], this, actiondata)
      }
      return true
   }
   return false
}

// Array method returns index of first occurrence of string in array.
// Returns -1 if not found
Array.prototype.indexOf = function(s) {
   var i
   for (i = 0; i < this.length; i++) {
      if (this[i] == s) {
         return i
      }
   }
   return -1
}

//
// Cross-browser event listeners
//

function addEvent(node, evType, component, useCapture){
  var handlerName = "on" + evType

  // A dict of listeners whose keys are event type
  if (!node.listeners) {
     node.listeners = new Object()
  }

  // Create list of listeners for this event type
  // if it doesn''t already exist
  if (!node.listeners[evType]) {
     node.listeners[evType] = new Array()
  }

  // Register the component as a listener for the given event
  node.listeners[evType].push(component)

  // If there was an existing handler for this node,
  // stash it to call later (component._dispatchEvent checks for and calls it)
  if (node[handlerName] && node[handlerName] != component._dispatchEvent) {
    if (!node.oldHandlers) {
      node.oldHandlers = new Object()
    } 
    node.oldHandlers[handlerName] = node[handlerName]
  }

  // Set framework event handler
  node[handlerName] = component._dispatchEvent

  return true
}

function removeEvent(obj, evType, fn, useCapture){

  // Remove from listeners array if necessary
  if (node.listeners) {
     var t = node.listeners[evType]
     if (t) {
        for (var i = t.length; i > 0; i--) {
           if (t[i] == fn) {
               t.splice(i, 1)
           }
        }
     }
  }

  // FIXME: If there are no more listening objects, remove
  // event handler. Also, if there was an old handler, reinstall it.

  if (obj.removeEventListener){
    obj.removeEventListener(evType, fn, useCapture);
    return true;
  } else if (obj.detachEvent){
    var r = obj.detachEvent("on"+evType, fn);
    return r;
  } else {
    alert("Handler could not be removed");
  }
}

// Create a jsComponent object and tie it to the node it decorates
// Return the new component instance.
// If the component says it''s not applicable, return null.
function bind(node, binding) {

//   alert("Bind(" + node.tagName + ", " + binding.toSource())

   // Ask the class if it can decorate the node
   // Ask the class if it can decorate the node
   try {
      var appl = binding.f.applicable

      // If Function.call() not supported (IE5/Mac), then framework not supported.
      if (!appl.call) {
          return null
      }

      // Return null if it says it''s not applicable
      if (appl && appl.call && !appl.call(__global__, node)) {
         return null
      }
   } catch (e) {
       // Report error to console (Mozilla)
       if (consoleService) {
         consoleService.logStringMessage("bind(" + node.tagName + "): " + (e.message ? e.message : e.description))
       }
   }

   // Create decorator
   var jsc = new binding.f(node, binding.data)

   // Tie decorator to the events its registration defines
   jsc.addEvents(node, binding.eventNames)

   return jsc
}

// This should be reimplemented by creating an element with DOM
function importFile(jsfile) {
   document.write("<script language=\"javascript\" src=\"" +
                  jsfile + ".js\"><\/script>") 
}

document.createNewId = function()
{
   var newid = null

   while (!newid || document.getElementById(newid)) {
       newid = "XID" + Math.round(Math.random() * 65536).toString(16)
   }
   return newid
}

// Visit Element nodes in document
document.visit = function(visitor, data)
{
   // Walk document tree
   function walk(node, visitor, data)
   {
      if (node) {
         if (node.nodeType == 1) {
              visitor(node, data);
         }
         for (var kid = node.firstChild; kid; kid = kid.nextSibling) {
            walk(kid, visitor, data);
         }
      }
   }
   
   var d = document.documentElement

   // Test for DOM presence before doing anything else
   if (d.nodeType && d.firstChild) {
      walk(d, visitor, data);
   }
   // else { alert ("Can't walk") }
}


//  Get attribute "class" of node 
//  Mozilla uses "class", IE uses (correctly) "className" 
function getClassAttribute(node) {
   var rval = null
   if (node.getAttribute) {
      rval = node.getAttribute("class")
      if (rval == null) {
        rval = node.getAttribute("className")
      }
   }
   return rval
}

// This works the same in all browsers, but just for symmetry''s sake...
function setClassAttribute(node, value) {
   node.className = value
}

// Return array of class names for this node
function getClassList(node) {
   var classes = getClassAttribute(node)
   if (classes) {
      return classes.split(" ")
   }
   return null
}

function hasClass(node, className) {
   var classes = getClassAttribute(node)
   var pat = new RegExp("\\b" + className + "\\b")
   return pat.exec(classes)
}

//
// addClass, removeClass, and replaceClass add, remove, and replace
// classes from the space-separated list of class names in a
// node''s "style" attribute.
//

//  Add a classname to a node (unless it''s already there) 
function addClass(node, classname) {

   var classes = getClassList(node)
   if (classes == null) {
      classes = new Array();
   }

   for (var i = 0; i < classes.length; i++) {
      if (classes[i] == classname) {
         return // Already there
      }
   }
   
   classes.push(classname)

   setClassAttribute(node, classes.join(" "))
}

function removeClass(node, classname)
{
   var classes = getClassList(node);
   if (classes == null) {
      classes = new Array();
   }

   for (var i = classes.length-1; i >= 0; i--) {
      if (classes[i] == classname) {
         classes.splice(i, 1)
      }
   }

   setClassAttribute(node, classes.join(" "))
}

function replaceClass(node, oldclass, newclass) {
   var classes = getClassList(node);
   if (classes == null) {
      classes = new Array();
   }

   for (var i = classes.length-1; i >= 0; i--) {
      if (classes[i] == oldclass) {
         classes.splice(i, 1, newclass)
      }
   }

   setClassAttribute(node, classes.join(" "))
}

// Save the path of a style sheet to be added to the
// document after load
document.addStyleSheet = function(name) {
   if (!document._jslibStyleSheets) {
      document._jslibStyleSheets = new Array();
   }
   document._jslibStyleSheets.push(name);
}

// Call after document loads with a stylesheet URL to
// add the stylesheet to the document
document.insertStyleSheet = function(ssname) {
   var d = document.getElementsByTagName("HEAD")
   if (!d) {
      alert("insertStyleSheet: no <HEAD> tag in document")
      return
   }
   if (d && d.length > 0) {
      d = d.item(0)

      // Return if the stylesheet is already in the page
      if (!d.getElementsByTagName) {
        return
      }
      var links = d.getElementsByTagName("LINK")
      for (var i = 0; i < links.length; i++) {
         var link = links[i]   
         if (link.href && link.href == ssname) {
              return
         }
      }
   }
   
   // Create new stylesheet node with name as given href
   var newss = document.createElement("LINK")
   newss.href = ssname
   newss.rel = "stylesheet"
   newss.type = "text/css"
   d.appendChild(newss)
}

function removeStyleSheet(ssname) {
   var d = document.getElementsByTagName("HEAD")
   var slen = ssname.length
   if (!d) {
      alert("removeStyleSheet: no <HEAD> tag in document")
      return
   }
   if (d) d = d[0]

   printStyleLinks()

   // Delete all links to the stylesheet
   var links = d.getElementsByTagName("LINK")
   for (var i = links.length-1; i >= 0; i--) {
      var link = links[i]   
      var hreff = link.href
      if (hreff && hreff.substring(hreff.length-slen, hreff.length) == ssname) {
           d.removeChild(link)
      }
   }
   printStyleLinks()

}

//
// Bind a node without using the registry
// Binds event handler "f" to node "n" for events in "eventNames"
//
function bindNode(node, f, data, events)
{
   return bind(node, new Binding(f, data, events))
}

//
// Apply any registered bindings for a node.
// 
// regtype: is in ["classes", "ids", "attrs", "names", "tags"],
//    and indicates how component is registered for node.
//
// node: the node to bind handlers to
//
// key: the array of handlers is registry[regtype][key].
//
//    Value of key depends on regtype; for example, if "regtype" is "id", then
//    the key is the id. If it''s "classes", then the key
//    is the classname
//

function applyBindings(regtype, node, key) {
   var registry = getRegistry()
   var bindings
   var i

   if (registry[regtype]) {
      bindings = registry[regtype][key]
   }

   for (i = 0; bindings && i < bindings.length; i++) {
      binding = bindings[i]
      obj = bind(node, binding)
   }
}


// Implement event handling by class
function bindClasses(node) {

   //   Does this node have a class we''re looking for? 
   var classes = getClassList(node)
   for (var ci = 0; classes && ci < classes.length; ci++) {
      var c = classes[ci].toLowerCase()
      __zz++
      __qq[c] = 1
      applyBindings("classes", node, c)
   }
}

// Implement event handling by class
function bindAttrs(node) {

   //   Does this node have a class we''re looking for? 
   var nl, node, ai

   nl = node.attributes
   for (var ai = 0; nl && (ai < nl.length); ai++) {
      var a = nl.item(ai).name.toLowerCase()
      applyBindings("attrs", node, a)
   }
}

// Bind a node to a handler
function bindByTag(node, tag) {
   applyBindings("tags", node, tag)
}


// Bind a node to a handler
function bindByName(node, name) {
   applyBindings("names", node, name)
}

// Bind a node to a handler
function bindById(node, id) {
   applyBindings("ids", node, id)
}

function bindNames(node) {
   alert("bindNames unimplemented")
}

//
// This object binds a DOM node to a function that
// handles events for the node
//
function Binding(f, data, eventNames) {
   this.f = f
   this.data = data
   if (eventNames.constructor == String) {
      this.eventNames = eventNames.split(" ")
   } else {
      this.eventNames = eventNames
   }
   this.toString = function() {
      return "Binding(" + this.f + ":" + this.eventNames.join(",") +":" + data + ")"
   }
}

//
// Clients register callback functions with a specific
// id, name, class, attribute, or tag name.
//

function register(regtype, keyexpr, eventNames, handlerCtor, data) {
   var registry = getRegistry()
   var key, keys, reghash, reglist

   // If we''ve been passed a string instead of a function, then
   // get the function.
   if (typeof(handlerCtor) == "string") {
      handlerCtor = __global__[handlerCtor]
   }

   // If component says it''s not usable in this browser, don''t register
   try {
      if (!handlerCtor.usable()) {
         return false
      }
   } catch (e) { }

   // Create ids index if it doesn''t yet exist
   if (!(registry[regtype])) {
       registry[regtype] = new Object()
   }
   reghash = registry[regtype]

   // Split keys on space
   keylist = keyexpr.split(/ +/)

   // For each key, register the key to its handler type
   for (i = 0; i < keylist.length; i++) {
      key = keylist[i]

      // Create binding array for this key if it doesn''t exist
      if (!(reglist = reghash[key])) {
          reglist = reghash[key] = new Array()
      }
   
      var newBinding = new Binding(handlerCtor, data, eventNames)
      reglist.push(newBinding)
  }
}

//
// Clients register callback functions with a specific
// id, name, class, attribute, or tag name.
//

function registerId(ids, eventNames, nameOfObject, data) {
   register("ids", ids, eventNames, nameOfObject, data)
}

function registerName(name, eventNames, nameOfObject, data) {
   register("names", name, eventNames, nameOfObject, data)
}

function registerClass(cls, eventNames, nameOfObject, data) {
   register("classes", cls, eventNames, nameOfObject, data)
}

function registerTag(tag, eventNames, nameOfObject, data) {
   register("tags", tag, eventNames, nameOfObject, data)
}

function registerAttr(attr, eventNames, nameOfObject, data) {
   register("attrs", attr.toLowerCase(), eventNames, nameOfObject, data)
}

var _registry

function getRegistry() {
   if (!_registry) {
      _registry = new Object();
//      _registry.classes = new Object();
//      _registry.ids = new Object();
//      _registry.names = new Object();
//      _registry.tags = new Object();
//      _registry.attrs = new Object();
   }
   return _registry;      
}

//  Return an array of all documents with given class 
document.getElementsByClass = function (item)
{
  function _GetElementsByClass(outArray, thisNode, item)
  {
    while (thisNode) {
      if (thisNode.nodeType == 1) {
        if (hasClass(thisNode, item)) {
          outArray.push(thisNode)
        }
        _GetElementsByClass(outArray, thisNode.firstChild, item)
      }
      thisNode = thisNode.nextSibling;
    }
  }

  var outArray = new Array();
  _GetElementsByClass(outArray, document.documentElement, item);
  return outArray;
}

//  Of course, M$ doesn''t implement all of the W3C DOM 
function hasAttribute(node, attr) {
    if (!node) return false
    if (node.hasAttribute) {
       return node.hasAttribute(attr)
    } else if (node.getAttribute) {
      return node.getAttribute(attr) != null
    } else {
       return node.getAttributeNode(attr).specified
    }
}

function setStyle(node, style, value) {
//   alert(node.tagName + " " + style + "='" + value +"'")
   node.style[style] = value
}

function getStyle(node, style) {
   if(!document.getElementById) return;
   
   var value = node.style[toCamelCase(style)];
   
    if(!value)
        if(document.defaultView)
            value = document.defaultView.
                 getComputedStyle(node, "").getPropertyValue(style);
       
        else if(node.currentStyle)
            value = node.currentStyle[toCamelCase(style)];
     
     return value;
}

function toCamelCase( sInput ) {
    var oStringList = sInput.split('-');
    if(oStringList.length == 1)    
        return oStringList[0];
    var ret = sInput.indexOf("-") == 0 ? 
      oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1) : oStringList[0];
    for(var i = 1, len = oStringList.length; i < len; i++){
        var s = oStringList[i];
        ret += s.charAt(0).toUpperCase() + s.substring(1)
    }
    return ret;
}

document.getSubElementsByClassAux = function(outArray, node, item)
{
   while (node) {
      if (node.nodeType == 1) {
         if (hasAttribute(node, "class")) {
            var c = " " + node.className + " ";
            if (c.indexOf(" " + item + " ") != -1) {
               outArray.push(node);
            }
         }
         document.getSubElementsByClassAux(outArray, node.firstChild, item)
      }
      node = node.nextSibling;
   }
}

//  Return an array of all documents with given class 
document.getSubElementsByClass = function(node, item)
{
  var outArray = new Array();
  document.getSubElementsByClassAux(outArray, node, item);
  return outArray;
}

document.initializeExtensions = function() {
   var i, tag, node, nodes, name, id, expr
         
   // Insert any necessary stylesheets
   // Did any components register stylesheets?
   if (document._jslibStyleSheets) {
      for (i = 0; i < document._jslibStyleSheets.length; i++) {
         var ss = document._jslibStyleSheets[i];
         document.insertStyleSheet(ss);
      }
   }

   // Do we have handlers registered by tag?
   var d = new Date()
   if (_registry.tags && document.getElementsByTagName) {
       for (tag in _registry.tags) {
          nodes = document.getElementsByTagName(tag)
          for (i = 0; i < nodes.length; i++) {
             bindByTag(nodes.item(i), tag)
          }
       }
   }
   var p = new Date()
//   _dbgWrite("Tags: " + (p - d) + " ms<br />")


   // Do we have handlers registered by name?
   d = new Date()
   if (_registry.names && document.getElementsByName) {
       for (name in _registry.names) {
          nodes = document.getElementsByName(name)
          for (i = 0; i < nodes.length; i++) {
             bindByName(nodes.item(i), name)
          }
       }
   }
   p = new Date()
//   _dbgWrite("Names: " + (p - d) + " ms<br />")

   // Do we have handlers registered by id?
   d = new Date()

   var j = 0
   if (_registry.ids && document.getElementById) {
       for (id in _registry.ids) {
         j++
         if (node = document.getElementById(id)) {
           bindById(node, id)
         }
       }
   }
   p = new Date()

   var classesDone = typeof(_registry.classes) == 'undefined'
   if (!classesDone) {
      var classes = _registry.classes.keys()
   }

   var attrsDone = typeof(_registry.attrs) == 'undefined'
   if (!attrsDone) {
      var attrs = _registry.attrs.keys()
   }

// alert("c = " + classes.length + ", a = " + attrs.length)
// 
// if (classes.length > 0) {
//   var s = ""
//   for (var qqq in classes) {
//     s = s + ": " + typeof(classes[qqq]) + " " + qqq + "=" + classes[qqq] +"\n"
//   }
//   alert(s)
// }
// 
   d = new Date()

   // Can we use xpath to get all nodes that define classes?
   __zz = 0
   __qq = new Object()

   // Do attributes and classes using xpath
   if (document.evaluate) {
     
      // Create xpath expression that matches all
      // nodes that define a "class" attribute
      if (!classesDone) {
         expr = "//*[@class]"
   
   
         nodes = document.evaluate(expr, document, null, XPathResult.ANY_TYPE, null)
   
         // Try to bind each matched node
         var nodecount = 0
   
         node = nodes && nodes.iterateNext()
   
         // Collect nodes
         while (node) {
   //         _dbgWrite("Node has classes: " + getAttribute(node, "class") + ": " + node.innerHTML + "<br />")
            bindClasses(node)
            nodecount++
            node = nodes.iterateNext()
         }
   
         classesDone = true
     }

    p = new Date()
//     _dbgWrite("classes with xpath: " + (p - d) + " ms, nodecount = " + nodecount + ", calls = " + __zz + ", classes found = " + 
//         __qq.keys().join(", ") + "<br />")

     d = new Date()

     // Todo: do attributes using XPath. 
     // Create xpath expression that matches all
     // nodes that define a "class" attribute
     if (!attrsDone) {
        expr = "//*[@" + attrs.join("|@") + "]"
//         _dbgWrite(expr + "<br />")

        nodecount = 0
        nodes = document.evaluate(expr, document, null, XPathResult.ANY_TYPE, null)
           node = nodes && nodes.iterateNext()
   
        // Collect nodes
        while (node) {
   //         _dbgWrite("Node has attrs: " + getAttribute(node, "class") + ": " + node.innerHTML + "<br />")
           bindAttrs(node)
           nodecount++
           node = nodes.iterateNext()
        }
   
        attrsDone = true
     }

     p = new Date()
//     _dbgWrite("attrs with xpath: " + (p - d) + " ms, nodecount = " + nodecount + "<br/>")
   
  }

   // Only visit every tag if we have to. Maybe
   // this can be made faster with XPath, if it''s available?
   // Too bad IE makes us do this because we can''t get to XPath...
   if (_registry.classes || _registry.attrs) {

     if (!(classesDone && attrsDone)) {
        d = new Date()
     
        var tags = document.getElementsByTagName("*")
//        alert("Tag length = " + tags.length + "<br />")
   
        for (i = 0; i < tags.length; i++) { 
           if (!classesDone) { bindClasses(tags[i]) }
           bindAttrs(tags[i])
   
           if (!document.getElementsByName) {
              bindNames(tags[i])
           }
        }

        p = new Date()
//        alert("classes and attrs: " + (p - d) + ", j= " + j + ", nodecount = " + nodecount + "<br />")
     }
   }
   return true
}

// Throw this object to indicate errors
function UserException(message) { 
   this.message = message
}

// Add an onload closure to be executed when the document finishes loading.
window.addLoadEvent = function(func) {
  var oldonload = window.onload;
  if (typeof window.onload != 'function') {
    window.onload = func;
  } else {
    window.onload = function() {
      oldonload();
      func();
    }
  }
}

// If onload is disabled somehow, this won't work.
window.addLoadEvent(document.initializeExtensions);

// Constructor
function JsComponent() {
   _node = null
   _udata = null
}

//
// ======== Class methods
//

// Class method: usable--This method inidicates whether the class is usable in
// the current browser. Override if you''re doing something weird or
// depending on some inconsistently-available API.
JsComponent.usable = function() {
    return true
}

JsComponent.applicable = function(node) {
   return true
}

//
// ========== Instance methods. The get/set things may never work...
//

JsComponent.prototype.init = function(node, udata) {
   this.setNode(node)
   this.setUdata(udata)
}

JsComponent.prototype.setNode = function(node) {
   this._node = node
}

JsComponent.prototype.getNode = function() {
   return this._node
}

JsComponent.prototype.setUdata = function(udata) {
   this._udata = udata
}

JsComponent.prototype.getUdata = function() {
   return this._udata
}

//
// addEvents -- eventNames is a space-separated list of names
// of events ("click mouseover mouseout keypress"). Associate
// _dispatchEvent for this instance with each event
// type.
//
JsComponent.prototype.addEvents = function(node, eventNames) {

   // For each event type, make this object''s _eventHandler
   // method the handler for that event. Then record that
   // this object handles this event

   var nevents = eventNames.length
   var eventName

   for (var i = 0; i < nevents; i++) {
      eventName = eventNames[i]
      addEvent(node, eventName, this, true)
   }
}


// This is the onclick handler for a JsComponent instance.
// BLACK MAGIC: Moz: On callback from a click event, "this" is the
// node, not the JsComponent object. Once we call the subclass
// method onclick(), "this" becomes the JsComponent object, and
// "node" is the DOM node. Weird.
// IE: "this" is the window; in other words, event routine runs
// in global scope. srcElement is the node on which the event occurred.
//
// This is the event handler for all events that this object
// handles. It asks the event what happened and dispatches
// to the appropriate handler

JsComponent.prototype._dispatchEvent = function(event) {

   // IE event is global
   if (!event) event = window.event

   // Get the node
   var node = this

//   _dbgWrite(event.type + ": (lX, lY) = (" + event.layerX + "," + event.layerY + "), " + "(pX, pY) = (" + event.pageX + "," + event.pageY + ")<br />")

   // returnvalue returns true if all handlers return true,
   // else it returns false. This behavior should be controllable,
   // but it currently isn''t
   var returnValue = true

   if (typeof(node.nodeType) == "undefined") {
       node = window.event.srcElement
   }

   // If this node isn''t listening for anything, return
   // This actually should never happen
   if (node.listeners == null) {
      return false
   }

   var handlerObj
   var eventType = event.type
   var listeners = node.listeners[eventType]

   if (listeners == null) return false

//   _dbgWrite("Found " + listeners.length + " listeners for " + eventType)

   // Send the event to each object that is listening for this event
   for (var i = 0; i < listeners.length; i++) {
      handlerObj = listeners[i]
      var cmd = handlerObj[eventType]
      returnValue = cmd.call(handlerObj, node, event) && returnValue
   }

   // If there was an old handler for this event, invoke it now
   if (node.oldHandlers) {
     var handler = node.oldHandlers["on" + eventType]
     if (handler) {
       returnValue = handler(event) && returnValue
     }
   }

   return returnValue
}



var __global__ = this

//
// Return the name of a function. Not all browsers support
// function.name property. (Safari)
//
Function.prototype.getName = function() {
   return this.toString().match(/function\s+(\w+)/)[1]
}

// New function for String builtin
String.prototype.startsWith = function(s) {
   return this.substr(0, s.length) == s   
}

// Execute superclass constructor to create superclass prototype
// Set this class' constructor to self (because it's overwritten
// by the assignment in step 1). See Flanagan (O'Reilly Books), pp129-130.

function subClass(thisSubClass, thisSuperClass) {
   if (thisSuperClass != null) {
      thisSubClass.prototype = new thisSuperClass()
      thisSubClass.prototype.constructor = thisSubClass
   }
   thisSubClass._name = thisSubClass.getName()
   thisSubClass._super = thisSuperClass
   importMethods(thisSubClass._name)
}

//
// For the named class, find all of the functions
// in global scope whose name is the class name, plus an
// underscore, plus a string. Use the string as a property
// name to which to assign a reference in the class
// prototype. So if a global method
//   Foo_bar()
// exists, this method will execute
//   Foo.prototype.bar = Foo_bar
// The idea is to add methods to a class using a naming
// convention instead of manually assigning function
// references to named properties
//
// Currently this only works in Mozilla, because IE
// doesn't allow me to iterate the global namespace. Sigh.
//
function importMethods(nameOfClass) {
  return // Until there is a workaround for IE

  // Get all functions that start with name of class in
  // global scope.
  var propname
  var prefix = nameOfClass + "_"
  var assignment
  for (var m in __global__) {
     var f = __global__[m]
     document.write(typeof(f) + " " + m+"<br/>")
     if (typeof(f) == 'function') {
        document.write(typeof(f) + " " + m+"<br/>")
     }
     if (typeof(f) == 'function' && m.startsWith(prefix)) {
        alert("importing " + m + " ~ ")
        propname = m.substring(prefix.length)        
//        alert(propname + "<br/>")
        assignment = nameOfClass + ".prototype." + propname + "=" + f.getName()
        alert(assignment + "<br/>")
        eval(assignment)
     }
  }
}
