/*jslint browser: true */

/*globals CFEventHandlerSet cfDocumentAddOnLoadedCallback
    cfDocumentGetLoadStatusMessage cfWarningTrigger window */

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

var __CFERROR_DEBUG_PRE_LOADED_ERRORS = true;
var __CFERROR_INSTALL_ALERT_HANDLER = true;
var __CFERROR_INSTALL_ONERROR_HANDLER = true;
var __CFERROR_SHOW_STACK = true;

// XXX: This doesn't take care of JavaScript 1.4 browsers.
var __CFERROR_SUPPORTS_EXCEPTIONS = !!window.Error;

var __CFERROR_WARNING_EXCEPTIONS = "browser appears to lack exception support";

////////////////////////////////////////////////////////////////////////////////
// Static variables
////////////////////////////////////////////////////////////////////////////////

var __cfErrorHandlerSet = new CFEventHandlerSet();

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

function __cfFilterErrorMessages(message) {
    "use strict";
    if (message === 'Script error.') {
        return true;
    }
    if (message === 'Error: Error calling method on NPObject!') {
        return true;
    }
    if (message.search('elt.parentNode') !== -1) {
        return true;
    }
    return false;
}

function __cfErrorDisplayAlertBox(message, url, lineNumber, stack, loadStatus) {
    "use strict";
    if (__cfFilterErrorMessages(message)) {
        return true;
    }
    var msg = "A JavaScript error has occurred.  Here are the details:\n\n";
    if (message) {
        msg += "\tMessage: " + message + '\n';
    }
    if (url) {
        msg += "\tURL: " + url + '\n';
    }
    if (lineNumber) {
        msg += "\tLine Number: " + lineNumber + '\n';
    }
    if (loadStatus) {
        msg += "\tLoad Status: " + loadStatus + '\n';
    }
    if (stack && __CFERROR_SHOW_STACK) {
        msg += "\nStack:\n\n" + stack + '\n';
    }
    msg += "\nWe apologize for the inconvenience.";
    window.alert(msg);
    return true;
}

function __cfErrorHandle(message, url, lineNumber, stackTrace) {
    "use strict";
    var loadStatus = cfDocumentGetLoadStatusMessage();
    if (__CFERROR_DEBUG_PRE_LOADED_ERRORS) {
        __cfErrorDisplayAlertBox(
            message,
            url,
            lineNumber,
            stackTrace,
            loadStatus
        );
    } else {
        var args = [];
        args.push(__cfErrorHandleLoaded);
        args.push(message);
        args.push(url);
        args.push(lineNumber);
        args.push(stackTrace);
        args.push(loadStatus);
        cfDocumentAddOnLoadedCallback.apply(undefined, args);
    }
}

function __cfErrorHandleDocumentLoadedEvent() {
    "use strict";
    __cfErrorHandle = __cfErrorHandleLoaded;
}

function __cfErrorHandleException(e) {
    "use strict";
    __cfErrorHandle(
        e.message || e.toString(),
        e.fileName,
        e.lineNumber,
        e.stack
    );
}

function __cfErrorHandleLoaded(
    message,
    url,
    lineNumber,
    stackTrace,
    loadStatus
) {
    "use strict";
    if (loadStatus === undefined) {
        loadStatus = cfDocumentGetLoadStatusMessage();
    }
    __cfErrorHandlerSet.execute(
        message,
        url,
        lineNumber,
        stackTrace,
        loadStatus
    );
}

function __cfErrorHandleWindowError(message, url, lineNumber) {
    "use strict";
    __cfErrorHandle(message, url, lineNumber, undefined);
    return true;
}

function __cfErrorTriggerNoException(message) {
    "use strict";
    __cfErrorHandle(message, undefined, undefined, undefined);
    return undefined;
}

function __cfErrorWatch(obj, func, args) {
    "use strict";
    if (args === undefined) {
        args = [];
    }
    return func.apply(obj, args);
}

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

function cfErrorAddOnErrorCallback() {
    "use strict";
    __cfErrorHandlerSet.add.apply(__cfErrorHandlerSet, arguments);
}

function cfErrorHasExceptionSupport() {
    "use strict";
    return __CFERROR_SUPPORTS_EXCEPTIONS;
}

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

var cfErrorTrigger, cfErrorWatch;

if (__CFERROR_SUPPORTS_EXCEPTIONS) {
    cfErrorTrigger = function (s) {
        "use strict";
        throw new Error(s);
    };
    cfErrorWatch = function (obj, func, args, ignoreError, errorResult) {
        "use strict";
        try {
            return __cfErrorWatch(obj, func, args);
        } catch (e) {
            if (!ignoreError) {
                __cfErrorHandleException(e);
            }
        }
        return errorResult;
    };
} else {
    cfErrorTrigger = __cfErrorTriggerNoException;
    cfErrorWatch = __cfErrorWatch;
    cfWarningTrigger(__CFERROR_WARNING_EXCEPTIONS);
}
if (__CFERROR_INSTALL_ALERT_HANDLER) {
    cfErrorAddOnErrorCallback(__cfErrorDisplayAlertBox);
}
if (__CFERROR_INSTALL_ONERROR_HANDLER) {
    window.onerror = __cfErrorHandleWindowError;
}
cfDocumentAddOnLoadedCallback(__cfErrorHandleDocumentLoadedEvent);
