/* <?php require_once('main_js.php'); ?> */
/**
* Events class
*
*
* This was my first dive into javascript.  I realized I needed a better way
* to ask for, handle and most importantly *share* events.  I also was with
* the first to pioneer the ondomload mechanism and incorporated this into
* events.
*
* Sinec I wrote this, a number of free and good classes out there have been
* made available to do this.  Yahoo has one, which we actually use also as
* it is a pre-req for the Yahoo history Manager and Yahoo Connection stuff.
*
* It's a bit of a waste to have two event managers in the same page, but they
* do co-exist alright.  One day I may migrate to the Yahoo one, but I will
* miss some of the features I provide, like my custom board and ef events.
* Supposedly Yahoo allows custom events - maybe the switch will be good.
*
* ... original comments below ..................
*
* Allows multiple callback handlers to register for browser events.
*
* Classic javascript only allows one callback function per event.  This makes
* it very difficult to have different pieces of javascript logic to coexist
* on one page - if they rely on the same events.  You would have to integrate
* them, which is not always a desirable thing, since different web pages will
* have different needs.
*
* For instance, the window.onload event (or equivalent) is very useful for
* many different initializations.  A simple one is to default the keyboard
* cursor to the first field of a form.  Another would be to initialize a
* trace output window when debugging javascript code.  Another would be to
* register other event handlers of DOM elements - that can only be registered
* once the DOM elements have been created (thus having to be registered on
* the onload event).
*
* As you can see, this gets really messy when different web pages have
* slightly different requirements.
*
* A better approach is to build a generic event registration system, so
* pieces of javascript logic can coexist without special knowledge of each
* other.  events.js aims to assist in that wrt event callbacks.
*
* Even though new features of javascript allow functions to register for
* events, the implementation is so bad on IE and different on browsers, that
* it is not usable.  It also in not available on earlier browsers - so it
* is a no-brainer to avoid these.
*
* This code consists of a global object (g_events), and a class (Event).
* The g_events object is basically an array of Event objects.
*
* An Event object is created for each {DOM element}.{event} pair.  For
* instance, the window.onload event would be represented by one Event object.
* This object holds an array of "listeners" (callbacks / event handlers) that
* want to be invoked by the event.  When the event fires, the Event.notify()
* routine is the point of entry, which iterates through the array - calling
* each listener function.
*
* The only "public" functions are:
*
*   g_events.add()
*   g_events.remove()
*
* Through these two functions, g_events handles the storage of all the Event
* objects, and adds and removes event handlers, and also creates and deletes
* the event objects as necessary.
*
* All DOM events are supported, as well as a special "ondomload" event.  Also,
* events can be registered prior to the browser rendering a webpage.  So
* g_events.add() can be called from the main line, before the browser has even
* had the chance to interpret the HTML and understand what DOM element you
* want an event from.  g_events does this by queuing the request until the
* DOM has finished loading (aka the "ondomload" event), and setting the
* listeners up from that.
*
* Note that events for the "window" object like "onload", "onresize" and the
* special "ondomload" are queued immediately, because "window" exists before
* javascript code runs.  What's a little confusing, is the g_events queues
* itself on the "ondomload" event, so it can add the DOM events that were
* queued.  But it works fine.
*
* Features added :
*
*   optional object - event sets "this" to the callback's object
*   optional param  - pass a user-defined parameter to the callback
*
* Example to use:
*
*   myobj.runme = function(one)
*   {
*     g_trace.log("this is set to myobj, and one is set to 1");
*   }
*
*   g_events.add("window", "ondomload", myobj.runme, myobj, "1");
*
*
* @copyright 2006 flickaway.com
* @version   $Id: events.js 2045 2009-05-12 06:57:05Z glen $
*/


var ge = g_events = [];
ge.listeners=[];               // create a new listeners array
ge.has_dom_loaded = 0;
ge.stored_listeners=[];


//
// Add event handler
//
// element_id_str  - the id string of the DOM element, or "window"   e.g.  "window", "password"
// event_type      - the event to listen for                         e.g.  "onload"
// listener        - the callback function                           e.g.  uploadform.password_changed
// listener_obj    - (optional) the object of the callback           e.g.  uploadform
// listener_param  - (optional) parameter to pass to the callback    e.g.  pass_me
//
ge.add = function(element_id_str, event_type, listener, listener_obj, listener_param)
{
  // I don't think this is required anymore
  //if (event_type == "ondomload")
  //{
  //  if (g_browser.saf)
  //    event_type = "onload";
  //}

  var name = element_id_str + event_type;

  // If we are referencing a DOM element, and the DOM has not finished being loaded,
  // then we store this add request and add it later, when the DOM has loaded.
  if (!this.has_dom_loaded && element_id_str != "window")
  {
    var to_store = [];

    to_store.element_id_str = element_id_str;
    to_store.event_type     = event_type;
    to_store.listener       = listener;
    to_store.listener_obj   = listener_obj;
    to_store.listener_param = listener_param;

    //this.stored_listeners.append(to_store);
    this.stored_listeners[this.stored_listeners.length]=to_store;

    return;
  }

  var element;

  // Window is a special case - otherwise we look up the DOM element
  switch (element_id_str)
  {
    case "window"   : element = window; break;
    case "document" : element = document; break;
    case "body"     : element = document.body; break;
    default         :
     element = $GE(element_id_str);
     if (!(!(element)))
       break;
     // if reached here - then the element doesn't exist.
     ;;; g_trace.log("(Event) Invalid element: " + element_id_str + " with event_type: " + event_type);
     return;
  }

  // OnClick is another special case.  Internet Retardor doesn't set the cursor correctly sometimes..
  if (event_type == "onclick" && document.all && element.style)
    element.style.cursor = "hand";

  // Create a new Event if necessary - and add the listener
  if (this.listeners[name] == undefined)
  {
    this.listeners[name] = new Event(element, event_type);
    this.listeners[name].add_listener(listener, listener_obj, listener_param);
  }
  else
  {
    this.listeners[name].add_listener(listener, listener_obj, listener_param);
  }
}


//
// Remove event handler
//
// element_string  - a unique string describing the DOM element e.g.  "window"
// event_type      - the event to not listen for anymore...     e.g.  "onload"
// listener        - ... by the callback function               e.g.  runme
//
ge.remove = function(element_id_str, event_type, listener, listener_obj)
{
  var name = element_id_str + event_type;

  if (!this.has_dom_loaded && element_id_str != "window")     // need to remove it from the to_store array
  {
    var i, max, entry;

    for (i=0, max=this.stored_listeners.length; i<max; i++)
    {
      entry = this.stored_listeners[i];
      if (element_id_str == entry.element_id_str && entry.event_type == event_type && listener == entry.listener)
      {
        this.stored_listeners.splice(i,1);
        return;
      }
    }
  }

  // check if the event even exists
  if (this.listeners[name] == undefined)
  {
    ;;; g_trace.log("error - remove event failed for " + element_id_str + "(" + event_type + ")");
    return;
  }

  // remove the listener
  this.listeners[name].remove_listener(listener, listener_obj);

  // if there are no more listeners, then clear the handler and remove the array
  if (this.listeners[name].gimme_length() == 0)
  {
    //g_trace.log("removed all listeners for " + name);
    this.listeners[name].clear(event_type);
    delete this.listeners[name];
  }
}



//
// Trigger an onresize event manually.  This is useful when a DOM change is made which
// changes the size of web page elements.  By manually triggering this event, any object
// which is dependent on code to position itself can be reinvoked and be allowed to
// adjust itself.
ge.trigger_resize = function()
{
  var name = "window" + "onresize";
  this.listeners[name].notify();
}


// This is to inform g_events that the DOM has completely loaded.
ge.domloaded = function()
{
  var to_register;
  this.has_dom_loaded = 1;
  //alert ("listeners = " + this.stored_listeners.length + "  has_dom_loaded = " + this.has_dom_loaded);

  while (this.stored_listeners.length > 0)
  {
    to_register = this.stored_listeners.shift();
    //alert(to_register.element + "  " + to_register.element_id_str + "  " + to_register.event_type + "  " + to_register.listener);
    this.add(to_register.element_id_str, to_register.event_type, to_register.listener, to_register.listener_obj, to_register.listener_param);
  }

}




//---------------------------------------------------------------------------
// Event Class
//---------------------------------------------------------------------------

var g_domload_event_object = null;

// Constructor
//
// Takes two parameters :
//  element    -  a DOM element or other object to listen to
//  event_type -  a valid browser event (such as onmouseover) for the DOM element
Event = function(element, event_type)
{
  //if (eval('(typeof(g_trace) != "undefined");'))
  //{
  //  g_trace.log("adding element:" + element + " event_type:" + event_type);
  //}

  this.listeners=[];                // create a new listeners array
  this.event_type = event_type;
  this.element=element;  // store the element -- only needed for clear() later.
  switch (event_type)
  {
   /*
    case "onblur"         : element.r_onblur         = this; element[event_type]=Event.cb_onblur;         break;
    case "onclick"        : element.r_onclick        = this; element[event_type]=Event.cb_onclick;        break;
    case "onchange"       : element.r_onchange       = this; element[event_type]=Event.cb_onchange;       break;
    case "onfocus"        : element.r_onfocus        = this; element[event_type]=Event.cb_onfocus;        break;
    case "onload"         : element.r_onload         = this; element[event_type]=Event.cb_onload;         break;
    case "onmousedown"    : element.r_onmousedown    = this; element[event_type]=Event.cb_onmousedown;    break;
    case "onmousemove"    : element.r_onmousemove    = this; element[event_type]=Event.cb_onmousemove;    break;
    case "onmouseup"      : element.r_onmouseup      = this; element[event_type]=Event.cb_onmouseup;      break;
    case "onmouseover"    : element.r_onmouseover    = this; element[event_type]=Event.cb_onmouseover;    break;
    case "onmouseout"     : element.r_onmouseout     = this; element[event_type]=Event.cb_onmouseout;     break;
    case "onmove"         : element.r_onmove         = this; element[event_type]=Event.cb_onmove;         break;
    case "onkeydown"      : element.r_onkeydown      = this; element[event_type]=Event.cb_onkeydown;      break;
    case "onkeyup"        : element.r_onkeyup        = this; element[event_type]=Event.cb_onkeyup;        break;
    case "onkeypress"     : element.r_onkeypress     = this; element[event_type]=Event.cb_onkeypress;     break;
    case "onresize"       : element.r_onresize       = this; element[event_type]=Event.cb_onresize;       break;
    case "onselect"       : element.r_onselect       = this; element[event_type]=Event.cb_onselect;       break;
    case "onselectstart"  : element.r_onselectstart  = this; element[event_type]=Event.cb_onselectstart;  break;
    case "onsubmit"       : element.r_onsubmit       = this; element[event_type]=Event.cb_onsubmit;       break;
    case "onunload"       : element.r_onunload       = this; element[event_type]=Event.cb_onunload;       break;
    case "onbeforeunload" : element.r_onbeforeunload = this; element[event_type]=Event.cb_onbeforeunload; break;
    */

    case "onmousewheel" :
        element.r_onmousewheel=this;
        if (window.addEventListener)    // Special code for Mozilla
        {
          element.addEventListener('DOMMouseScroll', Event.cb_onmousewheel, false);
        }
        element[event_type]=Event.cb_onmousewheel;
        break;

    case "onefchange" :
        g_ef.set_listener(this.notify, this);
        g_ef.start_detect();
        break;

    case "onboardresize" :
        g_board.register_adjust_me_too(this, this.notify);
        break;

    case "mouseenter" :
        element.r_mouseenter = this;
        element["onmouseover"]=this._faux_mouse_cbf("mouseenter");
        break;

    case "mouseleave" :
        element.r_mouseleave = this;
        element["onmouseout"]=this._faux_mouse_cbf("mouseleave");
        break;

    case "ondomload"   : g_domload_event_object = this; break;

    default:
       element["r_" + event_type] = this;
       element[event_type]=this._callback_factory(event_type);
       break;

//    default : g_trace.log("error - invalid event_type - " + event_type); break;
  }
}

p = Event.prototype;


// Remove the event handler
Event.prototype.clear = function(event_type)
{
  if (event_type == "onmousewheel" && window.addEventListener)  // Special case for Mozilla and onmousewheel event
  {
    this.element.removeEventListener('DOMMouseScroll', Event.cb_onmousewheel_moz, false);
  }
  switch (event_type)
  {
    case "onefchange"     : g_ef.stop_detect();                  break;             // SPECIAL CASES
    case "onboardresize"  : g_board.unregister_adjust_me_too();  break;
    case "ondomload"      :                                      break;
    case "mouseenter"     : this.element["onmouseover"] = null; this.element.r_mouseenter = null; break;
    case "mouseleave"     : this.element["onmouseout"] = null;  this.element.r_mouseleave = null; break;

    default:
      this.element["r_" + event_type] = null;

/*
    case "onblur"         : this.element.r_onblur=null;         break;
    case "onclick"        : this.element.r_onclick=null;        break;
    case "onchange"       : this.element.r_onchange=null;       break;
    case "onfocus"        : this.element.r_onfocus=null;        break;
    case "onload"         : this.element.r_onload=null;         break;
    case "onmousedown"    : this.element.r_onmousedown=null;    break;
    case "onmousemove"    : this.element.r_onmousemove=null;    break;
    case "onmouseup"      : this.element.r_onmouseup=null;      break;
    case "onmouseover"    : this.element.r_onmouseover=null;    break;
    case "onmouseout"     : this.element.r_onmouseout=null;     break;
    case "onmove"         : this.element.r_onmove=null;         break;
    case "onkeydown"      : this.element.r_onkeydown=null;      break;
    case "onkeyup"        : this.element.r_onkeyup=null;        break;
    case "onkeypress"     : this.element.r_onkeypress=null;     break;
    case "onresize"       : this.element.r_onresize=null;       break;
    case "onselect"       : this.element.r_onselect=null;       break;
    case "onselectstart"  : this.element.r_onselectstart=null;  break;
    case "onsubmit"       : this.element.r_onsubmit=null;       break;
    case "onunload"       : this.element.r_onunload=null;       break;
    case "onbeforeunload" : this.element.r_onbeforeunload=null; break;
    case "onmousewheel"   : this.element.r_onmousewheel=null;   break;
    default : g_trace.log("events: error clearing invalid event_type - " + event_type); break;
*/
  }
  this.element[event_type]=null;
  delete this.listeners;
  this.element = null;                 // DAMN!  This line is needed for IE to release memory.
}

// Add a listener
Event.prototype.add_listener = function(listener, listener_obj, listener_param)
{
  // first try to remove the listener - in case it is already listening (we should not allow
  // the same listener to be called twice.
  if (this.remove_listener(listener, listener_obj) == 1)
  {
    ;;; g_trace.log("duplicate event for element: " + this.element + " event_type: " + this.event_type + " listener: " + listener + " removed before adding again");
  }

  var to_store = [];

  to_store.listener = listener;
  if (listener_obj == 'undefined' || listener_obj == null)
    to_store.listener_obj = this;
  else
    to_store.listener_obj = listener_obj;
  to_store.listener_param = listener_param;

  // add the listener to the listeners array
  //this.listeners.append(to_store);
  this.listeners[this.listeners.length]=to_store;

  //this.listeners[this.listeners.length]=to_store;
}

// Remove a listener
Event.prototype.remove_listener = function(listener, listener_obj)
{
  var listeners = this.listeners;

  //-----------------------------------------------------------------------------------
  // TEMPORARY CODE !!!!!!!!
  //
  // Remove this once you have added scope parameters to all g_events.remove() calls
  //
  //-----------------------------------------------------------------------------------
  if (listener_obj == undefined)
  {
    for (var i=0; i<listeners.length; i++)
    {
      if (listeners[i].listener==listener)
      {
        //g_trace.log("removed listener");
        listeners.splice(i,1);
        return 1;
      }
    }
  }
  else
  {
    for (var i=0; i<listeners.length; i++)
    {
      if (listeners[i].listener==listener && listeners[i].listener_obj==listener_obj)
      {
        //g_trace.log("removed listener");
        listeners.splice(i,1);
        return 1;
      }
    }
  }
  return 0;
}

Event.prototype.gimme_length = function()
{
  return this.listeners.length;
}

// Notify all listeners of the event.  This is called by the static callback function
Event.prototype.notify = function(event)
{
  // We need to copy the listeners, before iterating through them and invoking the callbacks.
  // This is because a callback could change the event queue (say it removes itself from receiving
  // more events).  This will change the this.listeners array while we are iterating though it.
  // Removing oneself, for instance, will cause the next listener to be missed.
  //
  // Since Javascript always passes objects and arrays by value, we need to explicitly make a copy
  // of this.listners.  Each array element actually contains another array - but we don't need to
  // copy these (a reference will do), since they don't change.

  var listeners_copy = [];
  var i, max, ret;

  for (i=0, max=this.listeners.length; i<max; i++)
  {
    listeners_copy[i] = this.listeners[i];
  }

  for(i=0, max=listeners_copy.length; i<max; i++)
  {
    var listener  = listeners_copy[i].listener;
    var l_obj   = listeners_copy[i].listener_obj;
    var l_param = listeners_copy[i].listener_param;
//    g_trace.log("listener=" + listener + " l_obj=" + l_obj + " l_param=" + l_param);
    ret = listener.call(l_obj, event, l_param);
  }

  delete listeners_copy;

  return ret;
}


// Static callback event handler, for which this will be the DOM element.
// It locates the reference to the router object, and calls notify()
// This particular routine is not used.  It is the template for the many below.
//Event.callback = function(event)
//{
//  var e = event || window.event;
//  var router=this.event_router;
//  return router.notify(e);
//}


/*
Event.cb_onblur      = function(event){ return this.r_onblur.notify(event || window.event); }
Event.cb_onclick     = function(event){ return this.r_onclick.notify(event || window.event); }
Event.cb_onchange    = function(event){ return this.r_onchange.notify(event || window.event); }
Event.cb_onfocus     = function(event){ return this.r_onfocus.notify(event || window.event); }
Event.cb_onload      = function(event){ return this.r_onload.notify(event || window.event); }
Event.cb_onmousedown = function(event){ return this.r_onmousedown.notify(event || window.event); }
Event.cb_onmousemove = function(event){ return this.r_onmousemove.notify(event || window.event); }
Event.cb_onmouseup   = function(event){ return this.r_onmouseup.notify(event || window.event); }
Event.cb_onmouseover = function(event){ return this.r_onmouseover.notify(event || window.event); }
Event.cb_onmouseout  = function(event){ return this.r_onmouseout.notify(event || window.event); }
Event.cb_onmove      = function(event){ return this.r_onmove.notify(event || window.event); }
Event.cb_onkeydown   = function(event){ return this.r_onkeydown.notify(event || window.event); }
Event.cb_onkeyup     = function(event){ return this.r_onkeyup.notify(event || window.event); }
Event.cb_onkeypress  = function(event){ return this.r_onkeypress.notify(event || window.event); }
Event.cb_onresize    = function(event){ return this.r_onresize.notify(event || window.event); }
Event.cb_onselect    = function(event){ return this.r_onselect.notify(event || window.event); }
Event.cb_onselectstart = function(event){ return this.r_onselectstart.notify(event || window.event); }
Event.cb_onsubmit    = function(event){ return this.r_onsubmit.notify(event || window.event); }
Event.cb_onunload    = function(event){ return this.r_onunload.notify(event || window.event); }
Event.cb_onbeforeunload = function(event){ return this.r_onbeforeunload.notify(event || window.event); }
*/


//-------------------------------------------------------------------------------------------
// At one point, I had individual routines for each event type, but this way consumes much
// less code.  This routine creates a custom function and returns it.  cool.
//-------------------------------------------------------------------------------------------
Event.prototype._callback_factory = function(event_type)
{
  return function(event)
  {
    return this["r_" + event_type].notify(event || window.event);
  }
}


//-------------------------------------------------------------------------------------------
// The FAUX mouseenter and mouseleave events are really mouseover and mouseout events, but
// they ignore events bubbled from children.  I got this code from here:
//
// http://blog.stchur.com/2007/03/15/mouseenter-and-mouseleave-events-for-firefox-and-other-non-ie-browsers/
//
//-------------------------------------------------------------------------------------------
Event.prototype._faux_mouse_cbf = function(event_type)
{
  return function(event)
  {
    //---------------------------------------------------------------------------------------------------
    // Must figure out if the event was bubbled from a child.
    //
    //   "this"                = the element that is mouse we are targeting
    //   "event.relatedTarget" = the opposite element (for onmouseover = elem being left, for onmouseout = elem going to)
    //
    // Now - IE has no relatedTarget, but does have "fromElement" and "toElement"
    //
    // We must make sure:
    //   - the related target is not the target
    //   - the related target is not a child of the target
    //---------------------------------------------------------------------------------------------------
    var e = (event || window.event);                                                                                     // get the event (different for IE)
    var rel_target = (document.all) ? ( (e.type=='mouseover')?e.fromElement:e.toElement ) : e.relatedTarget;             // get the related target (diff for IE)
    return (this === rel_target || gu.is_a_child_of(this, rel_target))? false : this["r_" + event_type].notify(e);       // make sure target isn't the related, or a parent of the related
  }
}


Event.cb_onmousewheel = function(event)
                        {
                          var delta;
                          if (!event) event = window.event;        // for IE
                          if (event.wheelDelta)                    // IE/Opera/Safari (i think)
                          {
                            delta = event.wheelDelta/120;
                            if (window.opera)        // Opera differs slightly..
                            {
                              delta = -delta;
                            }
                          }
                          else
                          {
                            delta = -event.detail/3;
                          }
                          return this.r_onmousewheel.notify(delta);
                        }



Event.domload_callback = function()
{
  if (g_domload_event_object == null)
  {
    return;
  }

  var router = g_domload_event_object;
  router.notify();
  //alert("domload succeeded");
}

swfobject.addDomLoadEvent(Event.domload_callback);

ge.add("window", "ondomload", ge.domloaded, ge);

