ErrorClass = new Object();

ErrorClass.defaults = {
client_side_action: 'throw', //or 'silent' or 'throw' or 'alert_and_throw'
client_alert_msg: '',    // '' = use default in program below
client_force_details: true,    // '' = use default in program below
server_side_action: 'diag_and_email',  // 'diag_only' or 'diag_and_email' or 'none'
server_diag_file: 'diag.clientside',
server_email: '',
customer_service: 'michael.olsen@elmbrookrotary.org',
max_stack_trace: 10,
img_src: '/clienterrors.cgi',
application_name: 'Application',
application_page: '',
error_callback: '', // if is a function, that function is called before sending the error
customer_info: ''
}


ErrorClass.held_functions = new Array();
ErrorClass.held_passed_options = new Array();
ErrorClass.held_targetname = new Array();

ErrorClass.Obj = function (application_name,application_page,customer_info,options) {

    var code_marker = '';

    if(!options) options = {};
    for (var i in ErrorClass.defaults) {
        if(typeof(options[i]) == "undefined") options[i] = ErrorClass.defaults[i];
    }
    options.application_name = application_name;
    options.application_page = application_page;
    options.customer_info = customer_info;

    function handle_error(e,passed_code_marker,cur_options) {

        if(typeof(cur_options) == "undefined") cur_options = options;
        else {
            for (var i in ErrorClass.defaults) {
                if(typeof(cur_options[i]) == "undefined") cur_options[i] = options[i];
            }
        }

        var error_param = 'application_name='+options.application_name+'&application_page='+options.application_page+'&customer_info='+options.customer_info;
        error_param += '&path_string=' + location.pathname;
        error_param += '&query_string=' + location.search.replace(/\&/g,"|");
        error_param += '&parent_query_string=' + parent.location.search.replace(/\&/g,"|");
        error_param += '&code_marker_passed=' + passed_code_marker;
        error_param += '&code_marker_global=' + code_marker;
        if(typeof(cur_options.error_callback) == "function") 
            error_param += '&callback_results=' + cur_options.error_callback();
        error_param += '&server_side_action=' + cur_options.server_side_action;
        error_param += '&server_diag_file=' + cur_options.server_diag_file;
        error_param += '&server_email=' + cur_options.server_email;
        error_param += '&client_side_action=' + cur_options.client_side_action;
        error_param += '&d=' + (new Date()).getSeconds();

        if(window.event) {
            try {
                var curTarget = window.event.target || window.event.srcElement;
                if(typeof(curTarget) == 'object') {
                    error_param += '&event_target_id='+curTarget.id;
                    error_param += '&event_target_name='+curTarget.name;
                }
            } catch (threw) { }
        } 
        try {
            if(typeof(e) == 'string') {
                error_param += '&exception[exception_string]='+encodeURIComponent(e);
            } else {
                throw 'reg';
            }

        } catch (threw) {
            for (var x in e) { if(typeof(e[x]) != 'function') error_param += '&exception['+encodeURIComponent(x)+']='+encodeURIComponent(e[x]);}
        }
        try {
        error_param += '&exception[stack_trace]='+encodeURIComponent(_stack_trace(handle_error.caller.caller,0));
        } catch(e) {}
        var m = new Image();
        m.src = cur_options.img_src+'?'+error_param.substring(0,2000);
    	
        var rd_regexp = new RegExp(/http[s]?:\/\/rd2?\..*/);
    	if(cur_options.client_side_action == 'alert' || cur_options.client_side_action == 'alert_and_throw') {
           if(cur_options.client_alert_msg == '') alert("The action requested could not be completed because of an error.  Please hit your browser's refresh button and try the request again.\n\nIf the problem persists, please contact "+cur_options.customer_service+".  We apologize for any inconvenience.\n"+((options.client_force_details || document.URL.search(rd_regexp) > -1) ? ('\n'+e.msg+'\n\n'+decodeURIComponent(error_param.replace(/\&/g,"\n")) + '\n' + e.stack) : ""));
           else alert(cur_options.client_alert_msg);
        }

        if(cur_options.client_side_action == 'throw' || cur_options.client_side_action == 'alert_and_throw') {
           throw e;
           return false;
        }

        return true;
    }
    this.handle_error = handle_error;

    function _stack_trace(cur_func,num) {
        var stack = '';
        try {
        if(num > options.max_stack_trace) return;
            stack = (cur_func && cur_func.caller ? '{/n}'+_stack_trace(cur_func.caller,++num) : '');
        } catch (e) { }
        var cur_func_string = '  '+(cur_func + '').replace(/ +/g,' ').replace(/\t+/g,' ').replace(/\n/,'{/n}\t').replace(/\n/,'{/n}\t').replace(/\n/,'{/n}\t').replace(/\n/g,'ERRRTN').replace(/ERRRTN.*(ERRRTN)*/,'{/n}\t(...)').substring(0,300);
        return (cur_func_string + '' + stack);
    }

    
    function set_code_marker(set_val) {
        code_marker = set_val;
    }
    this.set_code_marker = set_code_marker;
    
    function change_options(new_options) {
        for (var i in new_options) {
            options[i] = new_options[i];
        }
    }
    this.change_options = change_options;


    // Function that can apply error catching to functions programatically
    //
    // usage:   ErrorHandler.set_error_catching('myfunction',window,{},{serverside_action:'diag_and_email'});       --> apply to a single function
    //          ErrorHandler.set_error_catching(Myobject,false,{recursive:true},{serverside_action:'diag_and_email'});  --> apply to all functions in Myobject
    //
    // target:  either a string or an object.  
    //          If string, it represents the function name and container must be filled in with an object.  
    //          If object, all functions within that object will be have error catching applied.  Sending in 'window' does NOT work in IE.
    // container: object or null or false
    //          If target is a string representing a function name, this is the fuction's parent object.  If the function is not in an object, this object should be the window
    //          Otherwise, should be empty or false
    // passed_set_options:  Hash that sets options for this function's operation.  
    //          Only {recursive: } is currently an option-- if true and target is an object, it will search all descendent objects for functions to apply error catching to
    // passed_handler_options:  Hash that is passed to handle error if an error is caught
    
    var num_set_functions = 0;

    function set_error_catching(target,container,passed_set_options,passed_handler_options) {

        if(container) {
            target_func = container[target];
            if(typeof(target_func) == 'function') {
                ErrorClass.held_functions.push(target_func);
                ErrorClass.held_passed_options.push(passed_handler_options);
                ErrorClass.held_targetname.push(target);
                container[target] = new Function("try {ErrorClass.held_functions["+num_set_functions+"].apply(window,arguments);} "+
                                                 "catch (e) { ErrorHandler.handle_error(e,'Function: '+ErrorClass.held_targetname["+num_set_functions+"],ErrorClass.held_passed_options["+num_set_functions+"]);}");
                num_set_functions++;
            }
        } else {
            if(typeof(target) == 'object') {
                if(target == window) return false;
                for (var ele in target) {
                    if(typeof(target[ele]) == 'object' || typeof(target[ele]) == 'function') {
                        if(typeof(target[ele]) == 'object' && passed_set_options.recursive) set_error_catching(target[ele],false,{recursive:true},passed_handler_options);
                        else if(typeof(target[ele]) == 'function') set_error_catching(ele,target,{},passed_handler_options);
                    }
                }
            }
        }
    }
    this.set_error_catching = set_error_catching;

    // Allows devlopers to set window.onerror to catch all uncaught errors
    function set_catchall (options) {
        window.onerror = error_catchall.ec_bind(this,options);
    }
    this.set_catchall = set_catchall;

    function unset_catchall (options) {
        window.onerror = function () {};
    }
    this.unset_catchall = unset_catchall;

    function error_catchall(options,msg,url,linenum) {
        var error_obj = {msg: msg, url:url, linenum:linenum, catchall:'catchall'};
        return handle_error(error_obj,'',options);
    }
    this.error_catchall = error_catchall;

}



// Binding functions from prototype.js
Function.prototype.ec_bind = function() {
  var __method = this, args = ec_A(arguments), object = args.shift();
  return function() {
    return __method.apply(object, args.concat(ec_A(arguments)));
  }
}

ec_A = function(iterable) {
  if (!iterable) return [];
  if (iterable.toArray) {
    return iterable.toArray();
  } else {
    var results = [];
    for (var i = 0; i < iterable.length; i++)
      results.push(iterable[i]);
    return results;
  }
}


