function userOnBeforePage()
{
  eventsOnBeforePage();
}

function userOnAfterPage()
{
  eventsOnAfterPage();
}

var gEventsPending;
var gEventsPendingLast;
var gEventsPendingObserverId;
var gKungFooDeathGrip;

function eventsOnMouseEvent(event)
{
  if (!gInChrome)
  {
    var e;
    try
    {
      netscape.security.PrivilegeManager.enablePrivilege('UniversalBrowserRead');
    }
    catch(e)
    {
      dlog('eventsOnMouseEvent Exception: ' + e);
      return;
    }
  }

  if (event.metaKey && (event.eventPhase == event.AT_TARGET))
  {
    --gEventsPending;
    updateScriptUrlStatus('Pending Events: ' + gEventsPending);
  }
  if (gEventsPending <= 0)
  {
    if (gEventsPendingObserverId)
    {
      clearTimeout(gEventsPendingObserverId);
    }
    gPageCompleted = true;
  }
  dlog('eventsOnMouseEvent: KungFoo ' + gKungFooDeathGrip + 
       ' gPageCompleted ' + gPageCompleted + 
       ' gEventsPending ' + gEventsPending);
}

function eventsOnBeforePage()
{
  gKungFooDeathGrip = true;
  gEventsPending = gEventsPending = 0;
  if (gEventsPendingObserverId)
  {
    clearTimeout(gEventsPendingObserverId);
  }
  gEventsPendingObserverId = setTimeout(eventsObservePending, 60000);
}

function eventsOnAfterPage()
{
  dlog('eventsOnAfterPage enter : KungFoo ' + gKungFooDeathGrip + 
       ' gPageCompleted ' + gPageCompleted + 
       ' gEventsPending ' + gEventsPending);

  var excp;

  try
  {
    var timeoffset = 0;
    var timeinc = 100;
    var doc = gSpider.mDocument;

    if (!doc)
    {
      dlog('eventsOnAfterPage: missing document');
      return;
    }

    var i;
    var elm;
    var elmlist;

    if ('body' in doc)
    {
      elmlist = getLeaves(doc.body);
      dlog('eventsOnAfterPage: found ' + elmlist.length + ' leaf nodes');
    }
    else
    {
      elmlist = [];
      dlog('eventsOnAfterPage: no body element');
    }

    for (i = 0; i < elmlist.length; i++)
    {
      elm = elmlist[i];

      elm.addEventListener('mouseover', eventsOnMouseEvent, false);
      elm.addEventListener('mouseout', eventsOnMouseEvent, false);

      timeoffset += timeinc;
      ++gEventsPending;
      setTimeout(eventsSendMouseEvent, timeoffset, 'mouseover', elm);

      timeoffset += timeinc;
      ++gEventsPending;
      setTimeout(eventsSendMouseEvent, timeoffset, 'mouseout', elm);
    }
  }
  catch(excp)
  {
    dlog('eventsOnAfterPage Exception: ' + excp);
  }
  // remove Kung Foo Death grip
  gKungFooDeathGrip = false;
  if (gEventsPending <= 0)
  {
    dlog('eventsOnAfterPage: gPageCompleted');
    if (gEventsPendingObserverId)
    {
      clearTimeout(gEventsPendingObserverId);
    }
    gPageCompleted = true;
  }
  dlog('eventsOnAfterPage exit  : KungFoo ' + gKungFooDeathGrip + 
       ' gPageCompleted ' + gPageCompleted + 
       ' gEventsPending ' + gEventsPending);
}

function eventsSendMouseEvent(eventtype, elm)
{
  if (!gInChrome)
  {
    var e;
    try
    {
      netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect UniversalBrowserRead');
    }
    catch(e)
    {
      dlog('eventsSendMouseEvent Exception: ' + e);
      return;
    }
  }

  dlog('eventsSendMouseEvent enter : KungFoo ' + gKungFooDeathGrip + 
       ' gPageCompleted ' + gPageCompleted + 
       ' gEventsPending ' + gEventsPending);

  var view = document.getElementById('contentSpider').contentWindow;
  var canBubble = true;
  var cancelable = true;
  var detail = 0;

  var doc = gSpider.mDocument;

  if (!doc)
  {
    dlog('eventsSendMouseEvent: missing document');
    return;
  }

  var clientPosition = getClientPosition(elm);

  var screenPosition = {
    x: clientPosition.x + window.screenX,
    y: clientPosition.y + window.screenY
  };

  try
  {
    var evtMouse = doc.createEvent('MouseEvents');
    evtMouse.initMouseEvent(
                 eventtype,
                 canBubble, 
                 cancelable, 
                 view, 
                 detail,
                 screenPosition.x,
                 screenPosition.y,
                 clientPosition.x,
                 clientPosition.y,
                 false,
                 false,
                 false,
                 true, /* fake metaKey to distinquish hook events from user events */
                 0, 
                 elm.parentNode
                 );
     elm.dispatchEvent(evtMouse);
   }
   catch(e)
   {
     // this can happen in particular when calling dispatchEvent on an
     // applet. There appears to be no conversion from a DOM2 event
     // and an AWTEvent.
     /*
     Chrome JavaScript Error: There is no Java method *.dispatchEvent 
     that matches JavaScript argument types (object). 
     Candidate methods with the same name are:    
     void dispatchEvent(java.awt.AWTEvent) . 
     Source File: chrome://spider/content/spider.js, Line: 451.
     */
     --gEventsPending;
     dlog('eventsSendMouseEvent: unable to call ' + elm + '.dispatchEvent ' + e);
   }
  dlog('eventsSendMouseEvent exit  : KungFoo ' + gKungFooDeathGrip + 
       ' gPageCompleted ' + gPageCompleted + 
       ' gEventsPending ' + gEventsPending);

}

function getClientPosition(elm)
{
  var position = { x: elm.offsetLeft, y:  elm.offsetTop};

  while (elm.offsetParent)
  {
    elm = elm.offsetParent;
    position.x += elm.offsetLeft;
    position.y += elm.offsetTop;
  }

  return position;
}

function eventsObservePending()
{
  dlog('eventsObservePending enter : KungFoo ' + gKungFooDeathGrip + 
       ' gPageCompleted ' + gPageCompleted + 
       ' gEventsPending ' + gEventsPending);

  if (!gPageCompleted)
  {
    if (!gKungFooDeathGrip && (gEventsPendingLast == gEventsPending))
    {
      gEventsPendingObserverId = 0;
      gEventsPendingLast = gEventsPending = 0;
      gPageCompleted = true;
    }
    else
    {
      gEventsPendingLast = gEventsPending;
      gEventsPendingObserverId = setTimeout(eventsObservePending, 60000);
    }
  }
  dlog('eventsObservePending exit  : KungFoo ' + gKungFooDeathGrip + 
       ' gPageCompleted ' + gPageCompleted + 
       ' gEventsPending ' + gEventsPending);

}

function getLeaves(node)
{
  var leaves = [];

  _getLeaves(node, leaves);

  return leaves;

}
function _getLeaves(node, leaves)
{
  if (node.nodeType != Node.ELEMENT_NODE)
  {
    return;
  }

  if (node.getElementsByTagName('*').length == 0)
  {
    leaves.push(node);
  }
  else
  {
    for (var curr = node.firstChild; curr; curr = curr.nextSibling)
    {
      if (curr.nodeType == Node.ELEMENT_NODE)
      {
        _getLeaves(curr, leaves);
      }
    }
  }
}


