// obtained from: "JavaScript The Definitive Guide" (5th edition) from David Flanagan published by O'Reilly; Example 17-2

var hbEventHandler = {};

if (document.addEventListener)
{
  hbEventHandler.add = function (element, eventType, handler)
  {
    element.addEventListener (eventType, handler, false);
  };
  
  hbEventHandler.remove = function (element, eventType, handler)
  {
    element.removeEventListener (eventType, handler, false);
  }
}
else if (document.attachEvent)
{
  hbEventHandler.add = function (element, eventType, handler)
  {
    if (-1 != hbEventHandler._find (element, eventType, handler))
    {
      return;
    }

    var wrappedHandler = function (e)
    {
      if (!e)
      {
        e = window.event;
      }

      var event = {
        _event: e,
        type: e.type,
        target: e.srcElement,
        currentTarget: element,
        relatedTarget: e.fromElement ? e.fromElement : e.toElement,
        eventPhase: (e.srcElement == element) ? 2 : 3,
        clientX: e.clientX,
        clientY: e.clientY,
        screenX: e.screenX,
        screenY: e.screenY,
        altKey: e.altKey,
        ctrlKey: e.ctrlKey,
        shiftKey: e.shiftKey,
        charCode: e.keyCode,
        stopPropagation: function ()
        {
          this._event.cancelBubble = true;
        },
        preventDefault: function ()
        {
          this._event.returnValue = false;
        }
      }

      if (Function.prototype.call)
      {
        handler.call (element, event);
      }
      else
      {
        element._currentHandler = handler;
        element._currentHandler (event);
        element._currentHandler = null;
      }
    };

    element.attachEvent ("on" + eventType, wrappedHandler);

    var h = {
      element: element,
      eventType: eventType,
      handler: handler,
      wrappedHandler: wrappedHandler
    };

    var d = element.document || element;
    var w = d.parentWindow;
    var id = hbEventHandler._uid ();

    if (!w._allHandlers)
    {
      w._allHandlers = {};
    }

    w._allHandlers [id] = h;

    if (!element._handlers)
    {
      element._handlers = [];
    }

    element._handlers.push (id);

    if (!w._onunloadHandlerRegistered)
    {
      w._onunloadHandlerRegistered = true;
      w.attachEvent ("onunload", hbEventHandler._removeAllHandlers);
    }
  };

  hbEventHandler.remove = function (element, eventType, handler)
  {
    var i = hbEventHandler._find (element, eventType, handler);

    if (-1 == i)
    {
      return;
    }

    var d = element.document || element;
    var w = d.parentWindow;
    var handlerId = element._handlers [i];
    var h = w._allHandlers [handlerId];
    element.detachEvent ("on" + eventType, h.wrappedHandler);
    element._handlers.splice (i, 1);
    delete w._allHandlers [handlerId];
  };

  hbEventHandler._find = function (element, eventType, handler)
  {
    var handlers = element._handlers;

    if (!handlers)
    {
      return -1;
    }

    var d = element.document || element;
    var w = d.parentWindow;

    for (var i = handlers.length - 1; i >= 0; --i)
    {
      var handlerId = handlers [i];
      var h = w._allHandlers [handlerId];

      if (h.eventType == eventType && h.handler == handler)
      {
        return i;
      }
    }

    return -1;
  }

  hbEventHandler._removeAllHandlers = function ()
  {
    var w = this;

    for (id in w._allHandlers)
    {
      var h = w._allHandlers [id];
      h.element.detachEvent ("on" + h.eventType, h.wrappedHandler);
      delete w._allHandlers [id];
    }
  }

  hbEventHandler._counter = 0;

  hbEventHandler._uid = function ()
  {
    return "h" + hbEventHandler._counter++;
  };
}
