Commits

Shu Zong Chen committed 02d8dea

Initial version

  • Participants
  • Parent commits 56896f1

Comments (0)

Files changed (1)

File js_error_reporter.js

 
 (function(){
 	var root = this;
-	var previousJSErrorReporter = root.JSErrorReporter;
+	var previousJSErrorReport = root.JSErrorReport;
 
-	var JSErrorReporter;
+	var JSErrorReport;
 	if (typeof exports !== 'undefined'){
-		JSErrorReporter = exports;
+		JSErrorReport = exports;
 	}else{
-		JSErrorReporter = root.JSErrorReporter = {};
+		JSErrorReport = root.JSErrorReport = {};
 	}
+	JSErrorReport.VERSION = "0.0.1";
 
-	JSErrorReporter.VERSION = "0.0.1";
-
-	JSErrorReporter.noConflict = function(){
-		root.JSErrorReporter = previousJSErrorReporter;
+	JSErrorReport.noConflict = function(){
+		root.JSErrorReport = previousJSErrorReport;
 		return this;
 	};
 
-	JSErrorReporter._reporters = [];
+	var JER = JSErrorReport;
 
-	JSErrorReporter.register = function(){
-		console.log("blah");
+	JER._reporters = new Array();
+
+	JER.register = function(reporter){
+		/* Attach reporter to error reporter */
+		if (!reporter.spec['endpoint']){
+			console.log("Error: Reporter has no endpoint");
+		}else{
+			JER._reporters.push(reporter);
+		}
 	};
 
-	JSErrorReporter.listen = function(){
-		window.onerror = JSErrorReporter.onerror;
+	JER.listen = function(){
+		/* Replace built-in error handler with our own */
+		window.onerror = JER.onerror;
 	};
 
-	JSErrorReporter.onerror = function(desc, page, line, chr){
-		console.log("ERROR" + desc + page + line + chr);
+	JER.onerror = function(desc, page, line, chr){
+		/* Build the initial error data, and find a
+		Reporter to handle it */
+		var args = Array.prototype.slice.call(arguments);
+		var context = {};
+		var e = new Error();
+		context['description'] = desc;
+		context['page'] = page;
+		var _page = page.split('/');
+		context['file'] = _page[_page.length - 1];
+		context['e'] = e;
+		context['stack'] = e.stack;
+		/* Going through the registered reporters */
+		for (var i = 0; i < JER._reporters.length; i++){
+			if (JER._reporters[i].match(context)){
+				break;
+			}
+		}
+		return true;
 	};
 
-	JSErrorReporter.Reporter = function(spec){
-		/* Object that sends reports */
-		if (!(this instanceof JSErrorReporter.Reporter)) return new JSErrorReporter.Reporter(spec);
+	JER.Reporter = function(spec){
+		/* Object that handles reports */
+		/* Pass in a spec array that contains any of the following:
+
+		endpoint: string representing the url to send data to.
+
+		match: string or RegExp. Pattern to match against filename.
+		       A success means this Reporter will handle the error.
+		       String parameter supports * globbing (is automatically
+		       converted to a RegExp, with `*` converted to .*)
+
+		exclude: Like match, but to test for files you don't want
+		         to handle. Takes precedence over match.
+
+		emitter: Not used yet. Will eventually be the type of reporter.
+		         For now it's always a JSON emitter.
+
+		extend: A function that returns a array of data to be passed
+		        to the emitter.
+
+		propagate: Whether this Reporter handling an error should
+		           cause the error to stop considering other handlers.
+		*/
+		if (!(this instanceof JER.Reporter)) return new JER.Reporter(spec);
 		this.init(spec);
 	};
 
-	JSErrorReporter.Reporter.prototype.init = function(spec){
+	JER.Reporter.prototype.init = function(spec){
+		var default_spec = JER.utils.clone(JER.Reporter._default_spec);
+		this.spec = JER.utils.extend(default_spec, spec);
+		if (typeof (this.spec['match']) == 'string'){
+			this.spec['match'] = JER.utils.expand_glob(this.spec['match']);
+		}
+		if (typeof (this.spec['exclude']) == 'string'){
+			this.spec['exclude'] = JER.utils.expand_glob(this.spec['exclude']);
+		}
+	};
+
+	JER.Reporter.prototype.name = "Reporter";
+
+	JER.Reporter.prototype.match = function(context){
+		/* Decides whether this Reporter handles the
+		given error. Returns True if the error should
+		stop propagating. */
+		if (this.spec['exclude']){
+			if (this.spec['exclude'].test(context['file'])){
+				return false;
+			}
+		}
+		if (this.spec['match']){
+			if (this.spec['match'].test(context['file'])){
+				this.handle(context);
+				return !this.spec['propagate'];
+			}
+		}
+		return false;
+	};
+
+	JER.Reporter.prototype.handle = function(context){
+		var ctx = JER.utils.clone(context);
+		if (typeof (this.spec['extend']) == 'function'){
+			var ext = this.spec['extend'](context);
+			ctx = JER.utils.extend(ctx, ext);
+		}
+		var url = this.spec['endpoint'];
+		var data = this.spec['envelope'](ctx);
+		data = encodeURIComponent(data);
+		this.spec['emitter'](url, data);
+	};
+
+	JER.JSONEnvelope = function(context){
+		return JSON.stringify(context);
+	};
+
+	JER.AsyncPOSTEmitter = function(url, data){
+		var req = new XMLHttpRequest();
+		req.open('POST', url, true);
+		req.send(data);
+	};
+
+	JER.test = function(){
+		nosuchfunction();
+	};
+
+	JER.utils = {};
+
+	JER.utils.clone = function(a){
+		var ret = {};
+		return JER.utils.extend(ret, a);
+	};
+
+	JER.utils.extend = function(a, b){
+		for (var prop in b){
+			a[prop] = b[prop];
+		};
+		return a;
+	};
+
+	JER.utils.expand_glob = function(a){
+		return new RegExp(a.replace("*", "(.*)"), "g");
+	};
+
+	JER.utils.build_query = function(args){
+		var ret = [];
+		for (var i in args){
+			ret.push(encodeURIComponent(i) + "=" + encodeURIComponent(args[i]));
+		};
+		return ret.join("&");
+	};
+
+	JER.Reporter._default_spec = {
+		endpoint: null,
+		match: '*',
+		exclude: null,
+		envelope: JER.JSONEnvelope,
+		emitter: JER.AsyncPOSTEmitter,
+		extend: null,
+		propagate: false
 	};
 
 })();