////////////////////////////////////////////////////////////////////////////////
// Constants
////////////////////////////////////////////////////////////////////////////////

var __CFEVENT_ERROR_HANDLER_SET_ARGUMENT = "invalid argument";
var __CFEVENT_ERROR_HANDLER_SET_KEY = "invalid handler set key";
var __CFEVENT_ERROR_HANDLER_SET_REMOVE = "handler not found";

var __CFEVENT_WARNING_LAST_ERROR = "function in event handler raised exception";
var __CFEVENT_WARNING_NOT_LOADED =
    "event can't process until document is loaded";

var __CFEVENT_HANDLER_BODY = "return __cfEventHandlerExecute(this, " +
                             "arguments.callee.__f, arguments);";

////////////////////////////////////////////////////////////////////////////////
// Classes
////////////////////////////////////////////////////////////////////////////////

// CFEventHandlerSet

function CFEventHandlerSet(debug)
{
    this.__debug = debug;
    this.__errorResult = new Object();
    this.__handlers = new Array();
}

CFEventHandlerSet.prototype.add = function()
{
    var func = arguments[0];
    if (typeof(func) != "function") {
        return cfErrorTrigger("CFEventHandlerSet::add: " +
                              __CFEVENT_ERROR_HANDLER_SET_ARGUMENT);
    }
    var handlerArgs = new Array();
    for (var i = 1; i < arguments.length; i++) {
        handlerArgs.push(arguments[i]);
    }
    var handler = {func: func, arguments: handlerArgs};
    this.__handlers.push(handler);
    return handler;
}

CFEventHandlerSet.prototype.execute = function()
{
    var debug = this.__debug;
    var handlers = this.__handlers;
    var result = this.__errorResult;
    var returnValues = new Array();
    var value;
    for (var i = 0; i < handlers.length; i++) {
        var handler = handlers[i];
        var handlerArgs = new Array();
        handlerArgs.extend(handler.arguments);
        for (var j = 0; j < arguments.length; j++) {
            handlerArgs.push(arguments[j]);
        }
        var func = handler.func;
        if (debug) {
            value = __cfErrorWatch(undefined, func, handlerArgs, false, result);
            if (value === result) {
                alert("CFEventHandlerSet::execute: '" + func.name +
                      "': " + __CFEVENT_WARNING_LAST_ERROR);
                continue;
            }
        } else {
            value = func.apply(undefined, handlerArgs);
        }
        returnValues.push(value);
    }
    return returnValues;
}

CFEventHandlerSet.prototype.getLength = function()
{
    return this.__handlers.length;
}

CFEventHandlerSet.prototype.remove = function(handler)
{
    if (! this.__handlers.remove(handler)) {
        return cfErrorTrigger("CFEventHandlerSet::remove: " +
                              __CFEVENT_ERROR_HANDLER_SET_REMOVE);
    }
}

// CFEventHandlerExpandedSet

function CFEventHandlerExpandedSet(debug)
{
    this.__debug = debug;
    this.__handlerSets = new Array();
}

CFEventHandlerExpandedSet.prototype.add = function()
{
    var key = arguments[0];
    var handlerSets = this.__handlerSets;
    var handlerSet = handlerSets[key];
    if (typeof(handlerSet) == "undefined") {
        handlerSets[key] = handlerSet = new CFEventHandlerSet(this.__debug);
    }
    var handlerArgs = new Array();
    for (var i = 1; i < arguments.length; i++) {
        handlerArgs.push(arguments[i]);
    }
    return handlerSet.add.apply(handlerSet, handlerArgs);
}

CFEventHandlerExpandedSet.prototype.execute = function()
{
    var handlerSet = this.__handlerSets[arguments[0]];
    if (typeof(handlerSet) == "undefined") {
        return new Array();
    }
    var handlerArgs = new Array();
    for (var i = 1; i < arguments.length; i++) {
        handlerArgs.push(arguments[i]);
    }
    return handlerSet.execute.apply(handlerSet, handlerArgs);
}

CFEventHandlerExpandedSet.prototype.remove = function(key, handler)
{
    var handlerSet = this.__handlerSets[key];
    if (typeof(handlerSet) == "undefined") {
        return cfErrorTrigger("CFEventHandlerExpandedSet::remove: '" + key +
                              "': " + __CFEVENT_ERROR_HANDLER_SET_KEY);
    }
    return handlerSet.remove(handler);
}

////////////////////////////////////////////////////////////////////////////////
// Private functions
////////////////////////////////////////////////////////////////////////////////

function __cfEventHandleDocumentLoadedEvent()
{
    __cfEventHandlerExecute = cfErrorWatch;
}

function __cfEventHandlerExecute()
{
    return cfWarningTrigger("__cfEventHandlerExecute: " +
                            __CFEVENT_WARNING_NOT_LOADED);
}

////////////////////////////////////////////////////////////////////////////////
// Public API
////////////////////////////////////////////////////////////////////////////////

function cfEventHandlerCreate(f, ignoreError, errorResult)
{
    return function()
    {
        // Hack: The global scope isn't immediately initialized for inner
        // functions in certain versions of Firefox.
        return window.__cfEventHandlerExecute(this, f, arguments, ignoreError,
                                              errorResult);
    };
}

////////////////////////////////////////////////////////////////////////////////
// Initialization
////////////////////////////////////////////////////////////////////////////////

cfDocumentAddOnLoadedCallback(__cfEventHandleDocumentLoadedEvent);
