//--------------------------------------------------------------------------------------------------
// Listener.js
//--------------------------------------------------------------------------------------------------
// Aaron Boodman
// May 2002
//
// Easily one of the most useful bits of javascript I've ever written :)
//
// A simple, but very powerful event listening setup that works with DOM events as well as JS ones.
// Scopes fired events in the object of your choice (oScope param to Listener.add()) or no scope at
// all (pass null to oScope). 
//
// Relies on Function.prototype.apply, which is implimented for IE 5.0 in /global/js/museCore.js
//
// It is important to *not* use the traditional event handlers if using this library, as they will
// invalidate each other. But once you get used to this, you'll never want to use the regular ones
// anyways.
//--------------------------------------------------------------------------------------------------

function Listener(fp, scope, runOnce) {
        this.fp = fp;
        this.scope = scope;
        this.remove = runOnce;
}

//--------------------------------------------------------------------------------------------------
// Listener.add(oMyButton, "onclick", oMyObject.myMethod, oMyOtherObject, true);
// when oMyButton is clicked, the function referenced by myMethod on oMyObject will be executed in 
// the scope of oMyOtherObject. The listener will be immediately removed upon execution (because
// bRunOnce is set to true).
//--------------------------------------------------------------------------------------------------
Listener.add = function(oSource, sEvent, fpDest, oScope, bRunOnce) {
        if (!oSource[sEvent] || oSource[sEvent] == null || !oSource[sEvent]._listeners) {
                oSource[sEvent] = function() { return 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);
}

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 && 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) {
        var lstnr, fp, returnValue, tempReturn;
        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;
                
                tempReturn = fp.apply(lstnr.scope, args);
                if (typeof tempReturn != "undefined") returnValue = tempReturn;

                if (lstnr.remove) Listener.remove(oSourceObj, sEvent, lstnr.fp, lstnr.scope);
        }

        if (typeof returnValue != "undefined") return returnValue;
}
