/* =================================================================================================
 * Listener - by Aaron Boodman
 * 5/23/2002; Queens, NY.
 * http://www.youngpup.net/projects/dhtml/listener/
 * 
 * =================================================================================================
 * 10/03/2003 - Phil DeJarnett
 *  Modified script to allow prioritization of listener elements.
 *  This allows the developer to specify that certain events occur before or after other events.
 * =================================================================================================
//
// Event listener implimentation for javascript. Works with native events as well as custom events. 
//
// Essentially, Listener hijacks a member of an object and makes it a pointer to an internal 
// Listener method. When the hijacked member is called, Listener fires any number of other methods 
// by proxy in whatever scope you (the script author) specify. All arguments passed to the source 
// event are received by all listeners automatically.
// 
// This amounts to a very powerful event listener setup similar to "signals and slots", but without 
// the confusing nomenclature and lack of distinction between methods and events.
//
//
// syntax:
//
// Listener.add(objSource, strEventName, fpListener, objScope, blnRunOnce);
//   objSource    : the object whose events you are listening for
//   strEventName : the name of the event to listen for ("onclick", "onUpdateComplete", etc)
//   fpListener   : a pointer to the function which should fire in response to 
//                  objSource[sEventName]();
//   objScope     : the object whose scope fpListener should be run in when fired.
//   blnRunOnce   : flag that indicates whether the listener should automatically be removed after 
//                  it is first fired.
//   priority     : Integer representing importance of a listener.  Higher-priority listeners
//                  are fired before lower priority listeners.  Default is zero, and there
//                  is no limit on the number used.
//
// Listener.remove(objSource, strEventNae, fpListener, objScope);
//   *see above for args*
//
//
 * http://www.youngpup.net/
 * ============================================================================================== */

// Tim Scarfe :
// Fix to Mozilla bug, remove attrib on listener is being confused with Listener.remove (thanks to "standard" on dhtmlcentral)
// Changed delete arr[x] to arr[x]=null for IE4 support.

function Listener(fp, scope, removing, priority) {
    this.fp = fp;
    this.scope = scope;
    this.remove = removing;
    this.priority = priority;
}

Listener.add = function(oSource, sEvent, fpDest, oScope, bRunOnce, priority) {
    if(priority==null || isNaN(priority)) {
        priority = 0;
    }
    if (!oSource[sEvent] || oSource[sEvent] == null || !oSource[sEvent]._listeners) {
        oSource[sEvent] = function() { Listener.fire(oSource, sEvent, arguments) };
        oSource[sEvent]._listeners = new Array();
    }

    var idx = this.findForEvent(oSource[sEvent], fpDest, oScope);
    if(idx == -1) {
        idx = oSource[sEvent]._listeners.length;
    }
    
    oSource[sEvent]._listeners[idx] = new Listener(fpDest, oScope, bRunOnce, priority);
    oSource[sEvent]._listeners.sort(function(a, b) {
        // sort the highest priority last in the array.
        // this causes it to fire first later.
        return a.priority - b.priority;
    });
}

Listener.remove = function(oSource, sEvent, fpDest, oScope) {
        var idx = this.findForEvent(oSource[sEvent], fpDest, oScope);
        if (idx != -1) {
                var iLast = oSource[sEvent]._listeners.length - 1;
                oSource[sEvent]._listeners[idx] = oSource[sEvent]._listeners[iLast];
                oSource[sEvent]._listeners.length--;
        }
}

Listener.findForEvent = function(fpEvent, fpDest, oScope) {
    if (fpEvent._listeners) {
        for (var i = 0; i < fpEvent._listeners.length; i++) {
            if (fpEvent._listeners[i].scope == oScope && fpEvent._listeners[i].fp == fpDest) {
                    return i;
            }
        }
    }
    return -1;
}

Listener.fire = function(oSourceObj, sEvent, args) {

	if(oSourceObj&&oSourceObj[sEvent]&&oSourceObj[sEvent]._listeners) { // TRS 

		var lstnr, fp;
		var last = oSourceObj[sEvent]._listeners.length - 1;

		// must loop in reverse, because we might be removing elements as we go.

		for (var i = last; i >= 0; i--) {
			lstnr = oSourceObj[sEvent]._listeners[i];
			fp = lstnr.fp;
			fp.apply(lstnr.scope, args);

			if (lstnr.remove) {
                Listener.remove(oSourceObj, sEvent, lstnr.fp, lstnr.scope);
            }
		}
	}
	
	return(-1)
}

// impliment function apply for browsers which don't support it natively
if (!Function.prototype.apply) {
    Function.prototype.apply = function(oScope, args) {
        var sarg = [];
        var rtrn, call;

        if (!oScope) {
            oScope = window;
        }
        if (!args) {
            args = [];
        }

        for (var i = 0; i < args.length; i++) {
            sarg[i] = "args["+i+"]";
        }

        call = "oScope.__applyTemp__(" + sarg.join(",") + ");";

        oScope.__applyTemp__ = this;
        rtrn = eval(call);
        delete oScope.__applyTemp__;
        return rtrn;
    }
}