diff --git a/.jshintrc b/.jshintrc
index 6a123502afdb..79444bf25963 100644
--- a/.jshintrc
+++ b/.jshintrc
@@ -3,8 +3,8 @@
"globalstrict": true,
"browser": true,
"predef": [
- "TraceKit",
"console",
- "_slice"
+ "module",
+ "require"
]
}
diff --git a/Gruntfile.js b/Gruntfile.js
index 54c18d450e4b..c90a75eabb55 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -1,15 +1,11 @@
+var proxyquire = require('proxyquireify');
+
module.exports = function(grunt) {
"use strict";
var _ = require('lodash');
var path = require('path');
-
- var coreFiles = [
- 'template/_header.js',
- 'vendor/**/*.js',
- 'src/**/*.js',
- 'template/_footer.js'
- ];
+ var through = require('through2');
var excludedPlugins = [
'react-native'
@@ -26,6 +22,21 @@ module.exports = function(grunt) {
return path;
});
+ // custom browserify transformer to re-write plugins to
+ // self-register with Raven via addPlugin
+ function AddPluginBrowserifyTransformer() {
+ return function (file) {
+ return through(function (buf, enc, next) {
+ buf = buf.toString('utf8');
+ if (/plugins/.test(file)) {
+ buf += "\nrequire('../src/singleton').addPlugin(module.exports);";
+ }
+ this.push(buf);
+ next();
+ });
+ };
+ }
+
// Taken from http://dzone.com/snippets/calculate-all-combinations
var combine = function (a) {
var fn = function (n, src, got, all) {
@@ -65,7 +76,7 @@ module.exports = function(grunt) {
key.sort();
var dest = path.join('build/', key.join(','), '/raven.js');
- dict[dest] = coreFiles.concat(comb);
+ dict[dest] = ['src/singleton.js'].concat(comb);
return dict;
}, {});
@@ -75,18 +86,35 @@ module.exports = function(grunt) {
aws: grunt.file.exists('aws.json') ? grunt.file.readJSON('aws.json'): {},
clean: ['build'],
- concat: {
+
+ browserify: {
options: {
- separator: '\n',
- banner: grunt.file.read('template/_copyright.js'),
- process: true
+ browserifyOptions: {
+ banner: grunt.file.read('template/_copyright.js'),
+ standalone: 'Raven' // umd
+ }
},
core: {
- src: coreFiles.concat(plugins),
+ src: 'src/singleton.js',
dest: 'build/raven.js'
},
- all: {
- files: pluginConcatFiles
+ plugins: {
+ files: pluginConcatFiles,
+ options: {
+ transform: [
+ [ new AddPluginBrowserifyTransformer() ]
+ ]
+ }
+ },
+ test: {
+ src: 'test/**/*.test.js',
+ dest: 'build/raven.test.js',
+ options: {
+ browserifyOptions: {
+ debug: true // source maps
+ },
+ plugin: [proxyquire.plugin]
+ }
}
},
@@ -100,7 +128,13 @@ module.exports = function(grunt) {
sourceMappingURL: function (dest) {
return path.basename(dest, '.js') + '.map';
},
- preserveComments: 'some'
+ preserveComments: 'some',
+ compress: {
+ dead_code: true,
+ global_defs: {
+ "TEST": false
+ }
+ }
},
dist: {
src: ['build/**/*.js'],
@@ -251,13 +285,13 @@ module.exports = function(grunt) {
// Grunt contrib tasks
grunt.loadNpmTasks('grunt-contrib-uglify');
- grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-contrib-copy');
// 3rd party Grunt tasks
+ grunt.loadNpmTasks('grunt-browserify');
grunt.loadNpmTasks('grunt-mocha');
grunt.loadNpmTasks('grunt-release');
grunt.loadNpmTasks('grunt-s3');
@@ -266,15 +300,16 @@ module.exports = function(grunt) {
// Build tasks
grunt.registerTask('_prep', ['clean', 'gitinfo', 'version']);
- grunt.registerTask('concat.core', ['_prep', 'concat:core']);
- grunt.registerTask('concat.all', ['_prep', 'concat:all']);
- grunt.registerTask('build.core', ['concat.core', 'uglify', 'fixSourceMaps', 'sri:dist']);
- grunt.registerTask('build.all', ['concat.all', 'uglify', 'fixSourceMaps', 'sri:dist', 'sri:build']);
+ grunt.registerTask('browserify.core', ['_prep', 'browserify:core']);
+ grunt.registerTask('browserify.plugins', ['_prep', 'browserify:plugins']);
+ grunt.registerTask('build.test', ['_prep', 'browserify:test']);
+ grunt.registerTask('build.core', ['browserify.core', 'uglify', 'fixSourceMaps', 'sri:dist']);
+ grunt.registerTask('build.all', ['browserify.plugins', 'uglify', 'fixSourceMaps', 'sri:dist', 'sri:build']);
grunt.registerTask('build', ['build.all']);
grunt.registerTask('dist', ['build.core', 'copy:dist']);
// Test task
- grunt.registerTask('test', ['jshint', 'mocha']);
+ grunt.registerTask('test', ['jshint', 'browserify.core', 'browserify:test', 'mocha']);
// Webserver tasks
grunt.registerTask('run:test', ['connect:test']);
diff --git a/package.json b/package.json
index 7fa452898888..fb9e4976fb6e 100644
--- a/package.json
+++ b/package.json
@@ -11,10 +11,11 @@
"type": "git",
"url": "git://github.com/getsentry/raven-js.git"
},
- "main": "dist/raven.js",
+ "main": "src/singleton.js",
"devDependencies": {
"chai": "~1.8.1",
"grunt": "^0.4.5",
+ "grunt-browserify": "^4.0.1",
"grunt-cli": "~0.1.9",
"grunt-contrib-clean": "~0.4.0",
"grunt-contrib-concat": "~0.3.0",
@@ -29,7 +30,9 @@
"grunt-sri": "mattrobenolt/grunt-sri#pretty",
"jquery": "^2.1.4",
"lodash": "~2.4.0",
- "sinon": "~1.7.3"
+ "proxyquireify": "^3.0.0",
+ "sinon": "~1.7.3",
+ "through2": "^2.0.0"
},
"keywords": [
"exceptions",
diff --git a/plugins/angular.js b/plugins/angular.js
index dd929f5cddeb..4b4cff987554 100644
--- a/plugins/angular.js
+++ b/plugins/angular.js
@@ -3,60 +3,58 @@
*
* Provides an $exceptionHandler for Angular.js
*/
-;(function(window) {
'use strict';
-var angular = window.angular,
- Raven = window.Raven;
+// See https://github.com/angular/angular.js/blob/v1.4.7/src/minErr.js
+var angularPattern = /^\[((?:[$a-zA-Z0-9]+:)?(?:[$a-zA-Z0-9]+))\] (.+?)\n(\S+)$/;
-// quit if angular isn't on the page
-if (!(angular && Raven)) return;
+function angularPlugin(Raven, angular) {
+ /*jshint validthis:true*/
+ angular = angular || window.angular;
-function RavenProvider() {
- this.$get = ['$window', function($window, $log) {
- return $window.Raven;
- }];
-}
+ if (!angular) return;
-function ExceptionHandlerProvider($provide) {
- $provide.decorator('$exceptionHandler',
- ['Raven', '$delegate', exceptionHandler]);
-}
+ function RavenProvider() {
+ this.$get = ['$window', function($window) {
+ return Raven;
+ }];
+ }
-function exceptionHandler(Raven, $delegate) {
- return function (ex, cause) {
- Raven.captureException(ex, {
- extra: { cause: cause }
- });
- $delegate(ex, cause);
- };
-}
+ function ExceptionHandlerProvider($provide) {
+ $provide.decorator('$exceptionHandler',
+ ['Raven', '$delegate', exceptionHandler]);
+ }
-// See https://github.com/angular/angular.js/blob/v1.4.7/src/minErr.js
-var angularPattern = /^\[((?:[$a-zA-Z0-9]+:)?(?:[$a-zA-Z0-9]+))\] (.+?)\n(\S+)$/;
+ function exceptionHandler(Raven, $delegate) {
+ return function (ex, cause) {
+ Raven.captureException(ex, {
+ extra: { cause: cause }
+ });
+ $delegate(ex, cause);
+ };
+ }
-Raven.addPlugin(function () {
angular.module('ngRaven', [])
.provider('Raven', RavenProvider)
.config(['$provide', ExceptionHandlerProvider]);
-});
-
-Raven.setDataCallback(function(data) {
- // We only care about mutating an exception
- var exception = data.exception;
- if (exception) {
- exception = exception.values[0];
- var matches = angularPattern.exec(exception.value);
-
- if (matches) {
- // This type now becomes something like: $rootScope:inprog
- exception.type = matches[1];
- exception.value = matches[2];
- data.message = exception.type + ': ' + exception.value;
- // auto set a new tag specifically for the angular error url
- data.extra.angularDocs = matches[3].substr(0, 250);
+
+ Raven.setDataCallback(function(data) {
+ // We only care about mutating an exception
+ var exception = data.exception;
+ if (exception) {
+ exception = exception.values[0];
+ var matches = angularPattern.exec(exception.value);
+
+ if (matches) {
+ // This type now becomes something like: $rootScope:inprog
+ exception.type = matches[1];
+ exception.value = matches[2];
+ data.message = exception.type + ': ' + exception.value;
+ // auto set a new tag specifically for the angular error url
+ data.extra.angularDocs = matches[3].substr(0, 250);
+ }
}
- }
-});
+ });
+}
-}(typeof window !== 'undefined' ? window : this));
+module.exports = angularPlugin;
diff --git a/plugins/backbone.js b/plugins/backbone.js
index 6e6b64c2b034..6c777688b1f5 100644
--- a/plugins/backbone.js
+++ b/plugins/backbone.js
@@ -3,57 +3,54 @@
*
* Patches Backbone.Events callbacks.
*/
-;(function(window) {
'use strict';
-if (window.Raven) Raven.addPlugin(function backbonePlugin() {
+function backbonePlugin(Raven, Backbone) {
+ Backbone = Backbone || window.Backbone;
-var Backbone = window.Backbone;
+ // quit if Backbone isn't on the page
+ if (!Backbone) return;
-// quit if Backbone isn't on the page
-if (!Backbone) return;
-
-function makeBackboneEventsOn(oldOn) {
- return function BackboneEventsOn(name, callback, context) {
- var wrapCallback = function (cb) {
- if (Object.prototype.toString.call(cb) === '[object Function]') {
- var _callback = cb._callback || cb;
- cb = Raven.wrap(cb);
- cb._callback = _callback;
- }
- return cb;
- };
- if (Object.prototype.toString.call(name) === '[object Object]') {
- // Handle event maps.
- for (var key in name) {
- if (name.hasOwnProperty(key)) {
- name[key] = wrapCallback(name[key]);
+ function makeBackboneEventsOn(oldOn) {
+ return function BackboneEventsOn(name, callback, context) {
+ var wrapCallback = function (cb) {
+ if (Object.prototype.toString.call(cb) === '[object Function]') {
+ var _callback = cb._callback || cb;
+ cb = Raven.wrap(cb);
+ cb._callback = _callback;
}
+ return cb;
+ };
+ if (Object.prototype.toString.call(name) === '[object Object]') {
+ // Handle event maps.
+ for (var key in name) {
+ if (name.hasOwnProperty(key)) {
+ name[key] = wrapCallback(name[key]);
+ }
+ }
+ } else {
+ callback = wrapCallback(callback);
}
- } else {
- callback = wrapCallback(callback);
- }
- return oldOn.call(this, name, callback, context);
- };
-}
-
-// We're too late to catch all of these by simply patching Backbone.Events.on
-var affectedObjects = [
- Backbone.Events,
- Backbone,
- Backbone.Model.prototype,
- Backbone.Collection.prototype,
- Backbone.View.prototype,
- Backbone.Router.prototype,
- Backbone.History.prototype
-], i = 0, l = affectedObjects.length;
-
-for (; i < l; i++) {
- var affected = affectedObjects[i];
- affected.on = makeBackboneEventsOn(affected.on);
- affected.bind = affected.on;
+ return oldOn.call(this, name, callback, context);
+ };
+ }
+
+ // We're too late to catch all of these by simply patching Backbone.Events.on
+ var affectedObjects = [
+ Backbone.Events,
+ Backbone,
+ Backbone.Model.prototype,
+ Backbone.Collection.prototype,
+ Backbone.View.prototype,
+ Backbone.Router.prototype,
+ Backbone.History.prototype
+ ], i = 0, l = affectedObjects.length;
+
+ for (; i < l; i++) {
+ var affected = affectedObjects[i];
+ affected.on = makeBackboneEventsOn(affected.on);
+ affected.bind = affected.on;
+ }
}
-});
-
-}(typeof window !== 'undefined' ? window : this));
+module.exports = backbonePlugin;
diff --git a/plugins/console.js b/plugins/console.js
index c3e098a72e4e..3f2b14a4966f 100644
--- a/plugins/console.js
+++ b/plugins/console.js
@@ -4,49 +4,41 @@
* Monkey patches console.* calls into Sentry messages with
* their appropriate log levels. (Experimental)
*/
-;(function(window) {
'use strict';
-if (window.Raven) Raven.addPlugin(function ConsolePlugin() {
-
-var console = window.console || {};
-
-var originalConsole = console,
- logLevels = ['debug', 'info', 'warn', 'error'],
- level = logLevels.pop();
-
-var logForGivenLevel = function(level) {
- var originalConsoleLevel = console[level];
-
- // warning level is the only level that doesn't map up
- // correctly with what Sentry expects.
- if (level === 'warn') level = 'warning';
- return function () {
- var args = [].slice.call(arguments);
- Raven.captureMessage('' + args[0], {level: level, logger: 'console', extra: { 'arguments': args }});
-
- // this fails for some browsers. :(
- if (originalConsoleLevel) {
- // IE9 doesn't allow calling apply on console functions directly
- // See: https://stackoverflow.com/questions/5472938/does-ie9-support-console-log-and-is-it-a-real-function#answer-5473193
- Function.prototype.bind
- .call(originalConsoleLevel, originalConsole)
- .apply(originalConsole, args);
- }
+function consolePlugin(Raven, console) {
+ console = console || window.console || {};
+
+ var originalConsole = console,
+ logLevels = ['debug', 'info', 'warn', 'error'],
+ level = logLevels.pop();
+
+ var logForGivenLevel = function(level) {
+ var originalConsoleLevel = console[level];
+
+ // warning level is the only level that doesn't map up
+ // correctly with what Sentry expects.
+ if (level === 'warn') level = 'warning';
+ return function () {
+ var args = [].slice.call(arguments);
+ Raven.captureMessage('' + args[0], {level: level, logger: 'console', extra: { 'arguments': args }});
+
+ // this fails for some browsers. :(
+ if (originalConsoleLevel) {
+ // IE9 doesn't allow calling apply on console functions directly
+ // See: https://stackoverflow.com/questions/5472938/does-ie9-support-console-log-and-is-it-a-real-function#answer-5473193
+ Function.prototype.bind
+ .call(originalConsoleLevel, originalConsole)
+ .apply(originalConsole, args);
+ }
+ };
};
-};
-while(level) {
- console[level] = logForGivenLevel(level);
- level = logLevels.pop();
+ while(level) {
+ console[level] = logForGivenLevel(level);
+ level = logLevels.pop();
+ }
}
-// export
-window.console = console;
-
-// End of plugin factory
-});
-
-// console would require `window`, so we don't allow it to be optional
-}(window));
+module.exports = consolePlugin;
diff --git a/plugins/ember.js b/plugins/ember.js
index 3d669fad9d6a..fd58c590f262 100644
--- a/plugins/ember.js
+++ b/plugins/ember.js
@@ -3,32 +3,29 @@
*
* Patches event handler callbacks and ajax callbacks.
*/
-;(function(window) {
'use strict';
-if (window.Raven) Raven.addPlugin(function EmberPlugin() {
+function emberPlugin(Raven, Ember) {
+ /*jshint validthis:true*/
+ Ember = Ember || window.Ember;
-var Ember = window.Ember;
+ // quit if Ember isn't on the page
+ if (!Ember) return;
-// quit if Ember isn't on the page
-if (!Ember) return;
+ var _oldOnError = Ember.onerror;
+ Ember.onerror = function EmberOnError(error) {
+ Raven.captureException(error);
+ if (typeof _oldOnError === 'function') {
+ _oldOnError.call(this, error);
+ }
+ };
+ Ember.RSVP.on('error', function (reason) {
+ if (reason instanceof Error) {
+ Raven.captureException(reason, {extra: {context: 'Unhandled Promise error detected'}});
+ } else {
+ Raven.captureMessage('Unhandled Promise error detected', {extra: {reason: reason}});
+ }
+ });
+}
-var _oldOnError = Ember.onerror;
-Ember.onerror = function EmberOnError(error) {
- Raven.captureException(error);
- if (typeof _oldOnError === 'function') {
- _oldOnError.call(this, error);
- }
-};
-Ember.RSVP.on('error', function (reason) {
- if (reason instanceof Error) {
- Raven.captureException(reason, {extra: {context: 'Unhandled Promise error detected'}});
- } else {
- Raven.captureMessage('Unhandled Promise error detected', {extra: {reason: reason}});
- }
-});
-
-// End of plugin factory
-});
-
-}(typeof window !== 'undefined' ? window : this));
+module.exports = emberPlugin;
diff --git a/plugins/jquery.js b/plugins/jquery.js
index 63203f5329c6..34fa47566d7c 100644
--- a/plugins/jquery.js
+++ b/plugins/jquery.js
@@ -3,106 +3,103 @@
*
* Patches event handler callbacks and ajax callbacks.
*/
-;(function(window) {
'use strict';
-if (window.Raven) Raven.addPlugin(function jQueryPlugin() {
-
-var $ = window.jQuery;
-
-// quit if jQuery isn't on the page
-if (!$) return;
-
-var _oldEventAdd = $.event.add;
-$.event.add = function ravenEventAdd(elem, types, handler, data, selector) {
- var _handler;
-
- if (handler && handler.handler) {
- _handler = handler.handler;
- handler.handler = Raven.wrap(handler.handler);
- } else {
- _handler = handler;
- handler = Raven.wrap(handler);
- }
-
- // If the handler we are attaching doesn’t have the same guid as
- // the original, it will never be removed when someone tries to
- // unbind the original function later. Technically as a result of
- // this our guids are no longer globally unique, but whatever, that
- // never hurt anybody RIGHT?!
- if (_handler.guid) {
- handler.guid = _handler.guid;
- } else {
- handler.guid = _handler.guid = $.guid++;
- }
-
- return _oldEventAdd.call(this, elem, types, handler, data, selector);
-};
-
-var _oldReady = $.fn.ready;
-$.fn.ready = function ravenjQueryReadyWrapper(fn) {
- return _oldReady.call(this, Raven.wrap(fn));
-};
-
-var _oldAjax = $.ajax;
-$.ajax = function ravenAjaxWrapper(url, options) {
- var keys = ['complete', 'error', 'success'], key;
-
- // Taken from https://github.com/jquery/jquery/blob/eee2eaf1d7a189d99106423a4206c224ebd5b848/src/ajax.js#L311-L318
- // If url is an object, simulate pre-1.5 signature
- if (typeof url === 'object') {
- options = url;
- url = undefined;
- }
-
- // Force options to be an object
- options = options || {};
-
- /*jshint -W084*/
- while (key = keys.pop()) {
- if ($.isFunction(options[key])) {
- options[key] = Raven.wrap(options[key]);
+function jQueryPlugin(Raven, jQuery) {
+ /*jshint validthis:true*/
+ var $ = jQuery || window.jQuery;
+
+ // quit if jQuery isn't on the page
+ if (!$) return;
+
+ var _oldEventAdd = $.event.add;
+ $.event.add = function ravenEventAdd(elem, types, handler, data, selector) {
+ var _handler;
+
+ if (handler && handler.handler) {
+ _handler = handler.handler;
+ handler.handler = Raven.wrap(handler.handler);
+ } else {
+ _handler = handler;
+ handler = Raven.wrap(handler);
}
- }
- /*jshint +W084*/
-
- try {
- var jqXHR = _oldAjax.call(this, url, options);
- // jqXHR.complete is not a regular deferred callback
- if ($.isFunction(jqXHR.complete))
- jqXHR.complete = Raven.wrap(jqXHR.complete);
- return jqXHR;
- } catch (e) {
- Raven.captureException(e);
- throw e;
- }
-};
-
-var _oldDeferred = $.Deferred;
-$.Deferred = function ravenDeferredWrapper(func) {
- return !_oldDeferred ? null : _oldDeferred(function beforeStartWrapper(deferred) {
- var methods = ['resolve', 'reject', 'notify', 'resolveWith', 'rejectWith', 'notifyWith'], method;
-
- // since jQuery 1.9, deferred[resolve | reject | notify] are calling internally
- // deferred[resolveWith | rejectWith | notifyWith] but we need to wrap them as well
- // to support all previous versions.
+
+ // If the handler we are attaching doesn’t have the same guid as
+ // the original, it will never be removed when someone tries to
+ // unbind the original function later. Technically as a result of
+ // this our guids are no longer globally unique, but whatever, that
+ // never hurt anybody RIGHT?!
+ if (_handler.guid) {
+ handler.guid = _handler.guid;
+ } else {
+ handler.guid = _handler.guid = $.guid++;
+ }
+
+ return _oldEventAdd.call(this, elem, types, handler, data, selector);
+ };
+
+ var _oldReady = $.fn.ready;
+ $.fn.ready = function ravenjQueryReadyWrapper(fn) {
+ return _oldReady.call(this, Raven.wrap(fn));
+ };
+
+ var _oldAjax = $.ajax;
+ $.ajax = function ravenAjaxWrapper(url, options) {
+ var keys = ['complete', 'error', 'success'], key;
+
+ // Taken from https://github.com/jquery/jquery/blob/eee2eaf1d7a189d99106423a4206c224ebd5b848/src/ajax.js#L311-L318
+ // If url is an object, simulate pre-1.5 signature
+ if (typeof url === 'object') {
+ options = url;
+ url = undefined;
+ }
+
+ // Force options to be an object
+ options = options || {};
/*jshint -W084*/
- while (method = methods.pop()) {
- if ($.isFunction(deferred[method])) {
- deferred[method] = Raven.wrap(deferred[method]);
+ while (key = keys.pop()) {
+ if ($.isFunction(options[key])) {
+ options[key] = Raven.wrap(options[key]);
}
}
/*jshint +W084*/
- // Call given func if any
- if (func) {
- func.call(deferred, deferred);
+ try {
+ var jqXHR = _oldAjax.call(this, url, options);
+ // jqXHR.complete is not a regular deferred callback
+ if ($.isFunction(jqXHR.complete))
+ jqXHR.complete = Raven.wrap(jqXHR.complete);
+ return jqXHR;
+ } catch (e) {
+ Raven.captureException(e);
+ throw e;
}
- });
-};
+ };
+
+ var _oldDeferred = $.Deferred;
+ $.Deferred = function ravenDeferredWrapper(func) {
+ return !_oldDeferred ? null : _oldDeferred(function beforeStartWrapper(deferred) {
+ var methods = ['resolve', 'reject', 'notify', 'resolveWith', 'rejectWith', 'notifyWith'], method;
+
+ // since jQuery 1.9, deferred[resolve | reject | notify] are calling internally
+ // deferred[resolveWith | rejectWith | notifyWith] but we need to wrap them as well
+ // to support all previous versions.
+
+ /*jshint -W084*/
+ while (method = methods.pop()) {
+ if ($.isFunction(deferred[method])) {
+ deferred[method] = Raven.wrap(deferred[method]);
+ }
+ }
+ /*jshint +W084*/
-// End of plugin factory
-});
+ // Call given func if any
+ if (func) {
+ func.call(deferred, deferred);
+ }
+ });
+ };
+}
-}(typeof window !== 'undefined' ? window : this));
+module.exports = jQueryPlugin;
diff --git a/plugins/native.js b/plugins/native.js
index bf8b7e6d9b83..d78cb505d12f 100644
--- a/plugins/native.js
+++ b/plugins/native.js
@@ -4,35 +4,31 @@
* Extends support for global error handling for asynchronous browser
* functions. Adopted from Closure Library's errorhandler.js.
*/
-;(function(window) {
'use strict';
-if (window.Raven) Raven.addPlugin(function nativePlugin() {
-
-var _helper = function _helper(fnName) {
- var originalFn = window[fnName];
- window[fnName] = function ravenAsyncExtension() {
- // Make a copy of the arguments
- var args = [].slice.call(arguments);
- var originalCallback = args[0];
- if (typeof (originalCallback) === 'function') {
- args[0] = Raven.wrap(originalCallback);
- }
- // IE < 9 doesn't support .call/.apply on setInterval/setTimeout, but it
- // also supports only two arguments and doesn't care what this is, so we
- // can just call the original function directly.
- if (originalFn.apply) {
- return originalFn.apply(this, args);
- } else {
- return originalFn(args[0], args[1]);
- }
+function nativePlugin(Raven) {
+ var _helper = function _helper(fnName) {
+ var originalFn = window[fnName];
+ window[fnName] = function ravenAsyncExtension() {
+ // Make a copy of the arguments
+ var args = [].slice.call(arguments);
+ var originalCallback = args[0];
+ if (typeof (originalCallback) === 'function') {
+ args[0] = Raven.wrap(originalCallback);
+ }
+ // IE < 9 doesn't support .call/.apply on setInterval/setTimeout, but it
+ // also supports only two arguments and doesn't care what this is, so we
+ // can just call the original function directly.
+ if (originalFn.apply) {
+ return originalFn.apply(this, args);
+ } else {
+ return originalFn(args[0], args[1]);
+ }
+ };
};
-};
-
-_helper('setTimeout');
-_helper('setInterval');
-// End of plugin factory
-});
+ _helper('setTimeout');
+ _helper('setInterval');
+}
-}(typeof window !== 'undefined' ? window : this));
+module.exports = nativePlugin;
diff --git a/plugins/react-native.js b/plugins/react-native.js
index 80ff300c9eea..1ce887f97eb3 100644
--- a/plugins/react-native.js
+++ b/plugins/react-native.js
@@ -7,19 +7,16 @@
* var Raven = require('raven-js');
* require('raven-js/plugins/react-native')(Raven);
*/
+'use strict';
var DEVICE_PATH_RE = /^\/var\/mobile\/Containers\/Bundle\/Application\/[^\/]+\/[^\.]+\.app/;
function normalizeUrl(url) {
- "use strict";
-
return url
.replace(/^file\:\/\//, '')
.replace(DEVICE_PATH_RE, '');
}
-module.exports = function (Raven) {
- "use strict";
-
+function reactNativePlugin(Raven) {
function urlencode(obj) {
var pairs = [];
for (var key in obj) {
@@ -73,4 +70,6 @@ module.exports = function (Raven) {
});
ErrorUtils.setGlobalHandler(Raven.captureException);
-};
+}
+
+module.exports = reactNativePlugin;
diff --git a/plugins/require.js b/plugins/require.js
index 768a935b2ff5..71eddb284ba2 100644
--- a/plugins/require.js
+++ b/plugins/require.js
@@ -1,19 +1,16 @@
+/*global define*/
/**
* require.js plugin
*
* Automatically wrap define/require callbacks. (Experimental)
*/
-;(function(window) {
'use strict';
-if (window.Raven) Raven.addPlugin(function RequirePlugin() {
-
-if (typeof define === 'function' && define.amd) {
- window.define = Raven.wrap({deep: false}, define);
- window.require = Raven.wrap({deep: false}, require);
+function requirePlugin(Raven) {
+ if (typeof define === 'function' && define.amd) {
+ window.define = Raven.wrap({deep: false}, define);
+ window.require = Raven.wrap({deep: false}, require);
+ }
}
-// End of plugin factory
-});
-
-}(window));
+module.exports = requirePlugin;
diff --git a/src/configError.js b/src/configError.js
new file mode 100644
index 000000000000..bb48808f0b77
--- /dev/null
+++ b/src/configError.js
@@ -0,0 +1,10 @@
+'use strict';
+
+function RavenConfigError(message) {
+ this.name = 'RavenConfigError';
+ this.message = message;
+}
+RavenConfigError.prototype = new Error();
+RavenConfigError.prototype.constructor = RavenConfigError;
+
+module.exports = RavenConfigError;
diff --git a/src/raven.js b/src/raven.js
index 0f1d5d21f2be..d3844c5ee441 100644
--- a/src/raven.js
+++ b/src/raven.js
@@ -1,20 +1,43 @@
/*global XDomainRequest:false*/
'use strict';
+var TraceKit = require('../vendor/TraceKit/tracekit');
+var RavenConfigError = require('./configError');
+var utils = require('./utils');
+
+var isFunction = utils.isFunction;
+var isUndefined = utils.isUndefined;
+var isError = utils.isError;
+var isEmptyObject = utils.isEmptyObject;
+var hasKey = utils.hasKey;
+var joinRegExp = utils.joinRegExp;
+var each = utils.each;
+var objectMerge = utils.objectMerge;
+var truncate = utils.truncate;
+var urlencode = utils.urlencode;
+var uuid4 = utils.uuid4;
+
+var dsnKeys = 'source protocol user pass host port path'.split(' '),
+ dsnPattern = /^(?:(\w+):)?\/\/(?:(\w+)(:\w+)?@)?([\w\.-]+)(?::(\d+))?(\/.*)/;
+
+function now() {
+ return +new Date();
+}
+
// First, check for JSON support
// If there is no JSON, we no-op the core features of Raven
// since JSON is required to encode the payload
-var _Raven = window.Raven,
- hasJSON = !!(typeof JSON === 'object' && JSON.stringify),
+function Raven() {
+ this._hasJSON = !!(typeof JSON === 'object' && JSON.stringify);
// Raven can run in contexts where there's no document (react-native)
- hasDocument = typeof document !== 'undefined',
- lastCapturedException,
- lastEventId,
- globalServer,
- globalKey,
- globalProject,
- globalContext = {},
- globalOptions = {
+ this._hasDocument = typeof document !== 'undefined';
+ this._lastCapturedException = null;
+ this._lastEventId = null;
+ this._globalServer = null;
+ this._globalKey = null;
+ this._globalProject = null;
+ this._globalContext = {};
+ this._globalOptions = {
logger: 'javascript',
ignoreErrors: [],
ignoreUrls: [],
@@ -23,39 +46,30 @@ var _Raven = window.Raven,
crossOrigin: 'anonymous',
collectWindowErrors: true,
maxMessageLength: 100
- },
- isRavenInstalled = false,
- objectPrototype = Object.prototype,
+ };
+ this._isRavenInstalled = false;
// capture references to window.console *and* all its methods first
// before the console plugin has a chance to monkey patch
- originalConsole = window.console || {},
- originalConsoleMethods = {},
- plugins = [],
- startTime = now();
+ this._originalConsole = window.console || {};
+ this._originalConsoleMethods = {};
+ this._plugins = [];
+ this._startTime = now();
-for (var method in originalConsole) {
- originalConsoleMethods[method] = originalConsole[method];
+ for (var method in this._originalConsole) {
+ this._originalConsoleMethods[method] = this._originalConsole[method];
+ }
}
+
/*
* The core Raven singleton
*
* @this {Raven}
*/
-var Raven = {
- VERSION: '<%= pkg.version %>',
- debug: false,
+Raven.prototype = {
+ VERSION: '<%= pkg.version %>',
- /*
- * Allow multiple versions of Raven to be installed.
- * Strip Raven from the global context and returns the instance.
- *
- * @return {Raven}
- */
- noConflict: function() {
- window.Raven = _Raven;
- return Raven;
- },
+ debug: true,
/*
* Configure Raven with a DSN and extra options
@@ -65,13 +79,15 @@ var Raven = {
* @return {Raven}
*/
config: function(dsn, options) {
- if (globalServer) {
- logDebug('error', 'Error: Raven has already been configured');
- return Raven;
+ var self = this;
+
+ if (this._globalServer) {
+ this._logDebug('error', 'Error: Raven has already been configured');
+ return this;
}
- if (!dsn) return Raven;
+ if (!dsn) return this;
- var uri = parseDSN(dsn),
+ var uri = this._parseDSN(dsn),
lastSlash = uri.path.lastIndexOf('/'),
path = uri.path.substr(1, lastSlash);
@@ -80,50 +96,48 @@ var Raven = {
each(options, function(key, value){
// tags and extra are special and need to be put into context
if (key == 'tags' || key == 'extra') {
- globalContext[key] = value;
+ self._globalContext[key] = value;
} else {
- globalOptions[key] = value;
+ self._globalOptions[key] = value;
}
});
}
// "Script error." is hard coded into browsers for errors that it can't read.
// this is the result of a script being pulled in from an external domain and CORS.
- globalOptions.ignoreErrors.push(/^Script error\.?$/);
- globalOptions.ignoreErrors.push(/^Javascript error: Script error\.? on line 0$/);
+ this._globalOptions.ignoreErrors.push(/^Script error\.?$/);
+ this._globalOptions.ignoreErrors.push(/^Javascript error: Script error\.? on line 0$/);
// join regexp rules into one big rule
- globalOptions.ignoreErrors = joinRegExp(globalOptions.ignoreErrors);
- globalOptions.ignoreUrls = globalOptions.ignoreUrls.length ? joinRegExp(globalOptions.ignoreUrls) : false;
- globalOptions.whitelistUrls = globalOptions.whitelistUrls.length ? joinRegExp(globalOptions.whitelistUrls) : false;
- globalOptions.includePaths = joinRegExp(globalOptions.includePaths);
+ this._globalOptions.ignoreErrors = joinRegExp(this._globalOptions.ignoreErrors);
+ this._globalOptions.ignoreUrls = this._globalOptions.ignoreUrls.length ? joinRegExp(this._globalOptions.ignoreUrls) : false;
+ this._globalOptions.whitelistUrls = this._globalOptions.whitelistUrls.length ? joinRegExp(this._globalOptions.whitelistUrls) : false;
+ this._globalOptions.includePaths = joinRegExp(this._globalOptions.includePaths);
- globalKey = uri.user;
- globalProject = uri.path.substr(lastSlash + 1);
+ this._globalKey = uri.user;
+ this._globalProject = uri.path.substr(lastSlash + 1);
// assemble the endpoint from the uri pieces
- globalServer = '//' + uri.host +
+ this._globalServer = '//' + uri.host +
(uri.port ? ':' + uri.port : '') +
- '/' + path + 'api/' + globalProject + '/store/';
+ '/' + path + 'api/' + this._globalProject + '/store/';
- // can safely use protocol relative (//) if target host is
- // app.getsentry.com; otherwise use protocol from DSN
if (uri.protocol && uri.host !== 'app.getsentry.com') {
- globalServer = uri.protocol + ':' + globalServer;
+ this._globalServer = uri.protocol + ':' + this._globalServer;
}
- if (globalOptions.fetchContext) {
+ if (this._globalOptions.fetchContext) {
TraceKit.remoteFetching = true;
}
- if (globalOptions.linesOfContext) {
- TraceKit.linesOfContext = globalOptions.linesOfContext;
+ if (this._globalOptions.linesOfContext) {
+ TraceKit.linesOfContext = this._globalOptions.linesOfContext;
}
- TraceKit.collectWindowErrors = !!globalOptions.collectWindowErrors;
+ TraceKit.collectWindowErrors = !!this._globalOptions.collectWindowErrors;
// return for chaining
- return Raven;
+ return this;
},
/*
@@ -135,18 +149,20 @@ var Raven = {
* @return {Raven}
*/
install: function() {
- if (isSetup() && !isRavenInstalled) {
- TraceKit.report.subscribe(handleStackInfo);
+ var self = this;
+ if (this.isSetup() && !this._isRavenInstalled) {
+ TraceKit.report.subscribe(function () {
+ // maintain 'self'
+ self._handleStackInfo.apply(self, arguments);
+ });
// Install all of the plugins
- each(plugins, function(_, plugin) {
- plugin();
- });
+ this._drainPlugins();
- isRavenInstalled = true;
+ this._isRavenInstalled = true;
}
- return Raven;
+ return this;
},
/*
@@ -164,7 +180,7 @@ var Raven = {
options = undefined;
}
- return Raven.wrap(options, func).apply(this, args);
+ return this.wrap(options, func).apply(this, args);
},
/*
@@ -175,6 +191,8 @@ var Raven = {
* @return {function} The newly wrapped functions with a context
*/
wrap: function(options, func) {
+ var self = this;
+
// 1 argument has been passed, and it's not a function
// so just return it
if (isUndefined(func) && !isFunction(options)) {
@@ -204,13 +222,13 @@ var Raven = {
// Recursively wrap all of a function's arguments that are
// functions themselves.
- while(i--) args[i] = deep ? Raven.wrap(options, arguments[i]) : arguments[i];
+ while(i--) args[i] = deep ? self.wrap(options, arguments[i]) : arguments[i];
try {
/*jshint -W040*/
return func.apply(this, args);
} catch(e) {
- Raven.captureException(e, options);
+ self.captureException(e, options);
throw e;
}
}
@@ -238,9 +256,9 @@ var Raven = {
*/
uninstall: function() {
TraceKit.report.uninstall();
- isRavenInstalled = false;
+ this._isRavenInstalled = false;
- return Raven;
+ return this;
},
/*
@@ -252,10 +270,10 @@ var Raven = {
*/
captureException: function(ex, options) {
// If not an Error is passed through, recall as a message instead
- if (!isError(ex)) return Raven.captureMessage(ex, options);
+ if (!isError(ex)) return this.captureMessage(ex, options);
// Store the raw exception object for potential debugging and introspection
- lastCapturedException = ex;
+ this._lastCapturedException = ex;
// TraceKit.report will re-raise any exception passed to it,
// which means you have to wrap it in try/catch. Instead, we
@@ -264,14 +282,14 @@ var Raven = {
// report on.
try {
var stack = TraceKit.computeStackTrace(ex);
- handleStackInfo(stack, options);
+ this._handleStackInfo(stack, options);
} catch(ex1) {
if(ex !== ex1) {
throw ex1;
}
}
- return Raven;
+ return this;
},
/*
@@ -285,24 +303,29 @@ var Raven = {
// config() automagically converts ignoreErrors from a list to a RegExp so we need to test for an
// early call; we'll error on the side of logging anything called before configuration since it's
// probably something you should see:
- if (!!globalOptions.ignoreErrors.test && globalOptions.ignoreErrors.test(msg)) {
+ if (!!this._globalOptions.ignoreErrors.test && this._globalOptions.ignoreErrors.test(msg)) {
return;
}
// Fire away!
- send(
+ this._send(
objectMerge({
message: msg + '' // Make sure it's actually a string
}, options)
);
- return Raven;
+ return this;
},
- addPlugin: function(plugin) {
- plugins.push(plugin);
- if (isRavenInstalled) plugin();
- return Raven;
+ addPlugin: function(plugin /*arg1, arg2, ... argN*/) {
+ var pluginArgs = Array.prototype.slice.call(arguments, 1);
+
+ this._plugins.push([plugin, pluginArgs]);
+ if (this._isRavenInstalled) {
+ this._drainPlugins();
+ }
+
+ return this;
},
/*
@@ -313,9 +336,9 @@ var Raven = {
*/
setUserContext: function(user) {
// Intentionally do not merge here since that's an unexpected behavior.
- globalContext.user = user;
+ this._globalContext.user = user;
- return Raven;
+ return this;
},
/*
@@ -325,9 +348,9 @@ var Raven = {
* @return {Raven}
*/
setExtraContext: function(extra) {
- mergeContext('extra', extra);
+ this._mergeContext('extra', extra);
- return Raven;
+ return this;
},
/*
@@ -337,9 +360,9 @@ var Raven = {
* @return {Raven}
*/
setTagsContext: function(tags) {
- mergeContext('tags', tags);
+ this._mergeContext('tags', tags);
- return Raven;
+ return this;
},
/*
@@ -348,9 +371,9 @@ var Raven = {
* @return {Raven}
*/
clearContext: function() {
- globalContext = {};
+ this._globalContext = {};
- return Raven;
+ return this;
},
/*
@@ -360,7 +383,7 @@ var Raven = {
*/
getContext: function() {
// lol javascript
- return JSON.parse(JSON.stringify(globalContext));
+ return JSON.parse(JSON.stringify(this._globalContext));
},
/*
@@ -370,9 +393,9 @@ var Raven = {
* @return {Raven}
*/
setRelease: function(release) {
- globalOptions.release = release;
+ this._globalOptions.release = release;
- return Raven;
+ return this;
},
/*
@@ -383,9 +406,9 @@ var Raven = {
* @return {Raven}
*/
setDataCallback: function(callback) {
- globalOptions.dataCallback = callback;
+ this._globalOptions.dataCallback = callback;
- return Raven;
+ return this;
},
/*
@@ -396,9 +419,9 @@ var Raven = {
* @return {Raven}
*/
setShouldSendCallback: function(callback) {
- globalOptions.shouldSendCallback = callback;
+ this._globalOptions.shouldSendCallback = callback;
- return Raven;
+ return this;
},
/**
@@ -411,9 +434,9 @@ var Raven = {
* @return {Raven}
*/
setTransport: function(transport) {
- globalOptions.transport = transport;
+ this._globalOptions.transport = transport;
- return Raven;
+ return this;
},
/*
@@ -422,7 +445,7 @@ var Raven = {
* @return {error}
*/
lastException: function() {
- return lastCapturedException;
+ return this._lastCapturedException;
},
/*
@@ -431,7 +454,7 @@ var Raven = {
* @return {string}
*/
lastEventId: function() {
- return lastEventId;
+ return this._lastEventId;
},
/*
@@ -440,571 +463,441 @@ var Raven = {
* @return {boolean}
*/
isSetup: function() {
- return isSetup();
- }
-};
-
-// Deprecations
-Raven.setUser = Raven.setUserContext;
-Raven.setReleaseContext = Raven.setRelease;
-
-function triggerEvent(eventType, options) {
- // NOTE: `event` is a native browser thing, so let's avoid conflicting wiht it
- var evt, key;
-
- if (!hasDocument)
- return;
-
- options = options || {};
-
- eventType = 'raven' + eventType.substr(0,1).toUpperCase() + eventType.substr(1);
+ if (!this._hasJSON) return false; // needs JSON support
+ if (!this._globalServer) {
+ if (!this.ravenNotConfiguredError)
+ this._logDebug('error', 'Error: Raven has not been configured.');
+ this.ravenNotConfiguredError = true;
+ return false;
+ }
+ return true;
+ },
- if (document.createEvent) {
- evt = document.createEvent('HTMLEvents');
- evt.initEvent(eventType, true, true);
- } else {
- evt = document.createEventObject();
- evt.eventType = eventType;
- }
+ afterLoad: function () {
+ // TODO: remove window dependence?
- for (key in options) if (hasKey(options, key)) {
- evt[key] = options[key];
- }
+ // Attempt to initialize Raven on load
+ var RavenConfig = window.RavenConfig;
+ if (RavenConfig) {
+ this.config(RavenConfig.dsn, RavenConfig.config).install();
+ }
+ },
- if (document.createEvent) {
- // IE9 if standards
- document.dispatchEvent(evt);
- } else {
- // IE8 regardless of Quirks or Standards
- // IE9 if quirks
- try {
- document.fireEvent('on' + evt.eventType.toLowerCase(), evt);
- } catch(e) {}
- }
-}
+ /**** Private functions ****/
+ _triggerEvent: function(eventType, options) {
+ // NOTE: `event` is a native browser thing, so let's avoid conflicting wiht it
+ var evt, key;
-var dsnKeys = 'source protocol user pass host port path'.split(' '),
- dsnPattern = /^(?:(\w+):)?\/\/(?:(\w+)(:\w+)?@)?([\w\.-]+)(?::(\d+))?(\/.*)/;
+ if (!this._hasDocument)
+ return;
-function RavenConfigError(message) {
- this.name = 'RavenConfigError';
- this.message = message;
-}
-RavenConfigError.prototype = new Error();
-RavenConfigError.prototype.constructor = RavenConfigError;
-
-/**** Private functions ****/
-function parseDSN(str) {
- var m = dsnPattern.exec(str),
- dsn = {},
- i = 7;
-
- try {
- while (i--) dsn[dsnKeys[i]] = m[i] || '';
- } catch(e) {
- throw new RavenConfigError('Invalid DSN: ' + str);
- }
+ options = options || {};
- if (dsn.pass)
- throw new RavenConfigError('Do not specify your private key in the DSN!');
+ eventType = 'raven' + eventType.substr(0,1).toUpperCase() + eventType.substr(1);
- return dsn;
-}
+ if (document.createEvent) {
+ evt = document.createEvent('HTMLEvents');
+ evt.initEvent(eventType, true, true);
+ } else {
+ evt = document.createEventObject();
+ evt.eventType = eventType;
+ }
-function isUndefined(what) {
- return what === void 0;
-}
+ for (key in options) if (hasKey(options, key)) {
+ evt[key] = options[key];
+ }
-function isFunction(what) {
- return typeof what === 'function';
-}
+ if (document.createEvent) {
+ // IE9 if standards
+ document.dispatchEvent(evt);
+ } else {
+ // IE8 regardless of Quirks or Standards
+ // IE9 if quirks
+ try {
+ document.fireEvent('on' + evt.eventType.toLowerCase(), evt);
+ } catch(e) {}
+ }
+ },
-function isString(what) {
- return objectPrototype.toString.call(what) === '[object String]';
-}
+ /**
+ * Install any queued plugins
+ */
+ _drainPlugins: function() {
+ var self = this;
+
+ // FIX ME TODO
+ each(this._plugins, function(_, plugin) {
+ var installer = plugin[0];
+ var args = plugin[1];
+ installer.apply(self, [self].concat(args));
+ });
+ },
-function isObject(what) {
- return typeof what === 'object' && what !== null;
-}
+ _parseDSN: function(str) {
+ var m = dsnPattern.exec(str),
+ dsn = {},
+ i = 7;
-function isEmptyObject(what) {
- for (var k in what) return false;
- return true;
-}
+ try {
+ while (i--) dsn[dsnKeys[i]] = m[i] || '';
+ } catch(e) {
+ throw new RavenConfigError('Invalid DSN: ' + str);
+ }
-// Sorta yanked from https://github.com/joyent/node/blob/aa3b4b4/lib/util.js#L560
-// with some tiny modifications
-function isError(what) {
- return isObject(what) &&
- objectPrototype.toString.call(what) === '[object Error]' ||
- what instanceof Error;
-}
+ if (dsn.pass)
+ throw new RavenConfigError('Do not specify your private key in the DSN!');
-/**
- * hasKey, a better form of hasOwnProperty
- * Example: hasKey(MainHostObject, property) === true/false
- *
- * @param {Object} host object to check property
- * @param {string} key to check
- */
-function hasKey(object, key) {
- return objectPrototype.hasOwnProperty.call(object, key);
-}
+ return dsn;
+ },
-function each(obj, callback) {
- var i, j;
+ _handleStackInfo: function(stackInfo, options) {
+ var self = this;
+ var frames = [];
- if (isUndefined(obj.length)) {
- for (i in obj) {
- if (hasKey(obj, i)) {
- callback.call(null, i, obj[i]);
- }
- }
- } else {
- j = obj.length;
- if (j) {
- for (i = 0; i < j; i++) {
- callback.call(null, i, obj[i]);
- }
+ if (stackInfo.stack && stackInfo.stack.length) {
+ each(stackInfo.stack, function(i, stack) {
+ var frame = self._normalizeFrame(stack);
+ if (frame) {
+ frames.push(frame);
+ }
+ });
}
- }
-}
-
-function handleStackInfo(stackInfo, options) {
- var frames = [];
- if (stackInfo.stack && stackInfo.stack.length) {
- each(stackInfo.stack, function(i, stack) {
- var frame = normalizeFrame(stack);
- if (frame) {
- frames.push(frame);
- }
+ this._triggerEvent('handle', {
+ stackInfo: stackInfo,
+ options: options
});
- }
-
- triggerEvent('handle', {
- stackInfo: stackInfo,
- options: options
- });
-
- processException(
- stackInfo.name,
- stackInfo.message,
- stackInfo.url,
- stackInfo.lineno,
- frames,
- options
- );
-}
-
-function normalizeFrame(frame) {
- if (!frame.url) return;
-
- // normalize the frames data
- var normalized = {
- filename: frame.url,
- lineno: frame.line,
- colno: frame.column,
- 'function': frame.func || '?'
- }, context = extractContextFromFrame(frame), i;
-
- if (context) {
- var keys = ['pre_context', 'context_line', 'post_context'];
- i = 3;
- while (i--) normalized[keys[i]] = context[i];
- }
- normalized.in_app = !( // determine if an exception came from outside of our app
- // first we check the global includePaths list.
- (!!globalOptions.includePaths.test && !globalOptions.includePaths.test(normalized.filename)) ||
- // Now we check for fun, if the function name is Raven or TraceKit
- /(Raven|TraceKit)\./.test(normalized['function']) ||
- // finally, we do a last ditch effort and check for raven.min.js
- /raven\.(min\.)?js$/.test(normalized.filename)
- );
+ this._processException(
+ stackInfo.name,
+ stackInfo.message,
+ stackInfo.url,
+ stackInfo.lineno,
+ frames,
+ options
+ );
+ },
- return normalized;
-}
+ _normalizeFrame: function(frame) {
+ if (!frame.url) return;
+
+ // normalize the frames data
+ var normalized = {
+ filename: frame.url,
+ lineno: frame.line,
+ colno: frame.column,
+ 'function': frame.func || '?'
+ }, context = this._extractContextFromFrame(frame), i;
+
+ if (context) {
+ var keys = ['pre_context', 'context_line', 'post_context'];
+ i = 3;
+ while (i--) normalized[keys[i]] = context[i];
+ }
-function extractContextFromFrame(frame) {
- // immediately check if we should even attempt to parse a context
- if (!frame.context || !globalOptions.fetchContext) return;
+ normalized.in_app = !( // determine if an exception came from outside of our app
+ // first we check the global includePaths list.
+ (!!this._globalOptions.includePaths.test && !this._globalOptions.includePaths.test(normalized.filename)) ||
+ // Now we check for fun, if the function name is Raven or TraceKit
+ /(Raven|TraceKit)\./.test(normalized['function']) ||
+ // finally, we do a last ditch effort and check for raven.min.js
+ /raven\.(min\.)?js$/.test(normalized.filename)
+ );
- var context = frame.context,
- pivot = ~~(context.length / 2),
- i = context.length, isMinified = false;
+ return normalized;
+ },
- while (i--) {
- // We're making a guess to see if the source is minified or not.
- // To do that, we make the assumption if *any* of the lines passed
- // in are greater than 300 characters long, we bail.
- // Sentry will see that there isn't a context
- if (context[i].length > 300) {
- isMinified = true;
- break;
+ _extractContextFromFrame: function(frame) {
+ // immediately check if we should even attempt to parse a context
+ if (!frame.context || !this._globalOptions.fetchContext) return;
+
+ var context = frame.context,
+ pivot = ~~(context.length / 2),
+ i = context.length, isMinified = false;
+
+ while (i--) {
+ // We're making a guess to see if the source is minified or not.
+ // To do that, we make the assumption if *any* of the lines passed
+ // in are greater than 300 characters long, we bail.
+ // Sentry will see that there isn't a context
+ if (context[i].length > 300) {
+ isMinified = true;
+ break;
+ }
}
- }
- if (isMinified) {
- // The source is minified and we don't know which column. Fuck it.
- if (isUndefined(frame.column)) return;
+ if (isMinified) {
+ // The source is minified and we don't know which column. Fuck it.
+ if (isUndefined(frame.column)) return;
+
+ // If the source is minified and has a frame column
+ // we take a chunk of the offending line to hopefully shed some light
+ return [
+ [], // no pre_context
+ context[pivot].substr(frame.column, 50), // grab 50 characters, starting at the offending column
+ [] // no post_context
+ ];
+ }
- // If the source is minified and has a frame column
- // we take a chunk of the offending line to hopefully shed some light
return [
- [], // no pre_context
- context[pivot].substr(frame.column, 50), // grab 50 characters, starting at the offending column
- [] // no post_context
+ context.slice(0, pivot), // pre_context
+ context[pivot], // context_line
+ context.slice(pivot + 1) // post_context
];
- }
-
- return [
- context.slice(0, pivot), // pre_context
- context[pivot], // context_line
- context.slice(pivot + 1) // post_context
- ];
-}
-
-function processException(type, message, fileurl, lineno, frames, options) {
- var stacktrace, i, fullMessage;
-
- if (!!globalOptions.ignoreErrors.test && globalOptions.ignoreErrors.test(message)) return;
-
- message += '';
- fullMessage = type + ': ' + message;
-
- if (frames && frames.length) {
- fileurl = frames[0].filename || fileurl;
- // Sentry expects frames oldest to newest
- // and JS sends them as newest to oldest
- frames.reverse();
- stacktrace = {frames: frames};
- } else if (fileurl) {
- stacktrace = {
- frames: [{
- filename: fileurl,
- lineno: lineno,
- in_app: true
- }]
- };
- }
+ },
- if (!!globalOptions.ignoreUrls.test && globalOptions.ignoreUrls.test(fileurl)) return;
- if (!!globalOptions.whitelistUrls.test && !globalOptions.whitelistUrls.test(fileurl)) return;
-
- // Fire away!
- send(
- objectMerge({
- // sentry.interfaces.Exception
- exception: {
- values: [{
- type: type,
- value: message,
- stacktrace: stacktrace
+ _processException: function(type, message, fileurl, lineno, frames, options) {
+ var stacktrace, i, fullMessage;
+
+ if (!!this._globalOptions.ignoreErrors.test && this._globalOptions.ignoreErrors.test(message)) return;
+
+ message += '';
+ message = truncate(message, this._globalOptions.maxMessageLength);
+
+ fullMessage = type + ': ' + message;
+ fullMessage = truncate(fullMessage, this._globalOptions.maxMessageLength);
+
+ if (frames && frames.length) {
+ fileurl = frames[0].filename || fileurl;
+ // Sentry expects frames oldest to newest
+ // and JS sends them as newest to oldest
+ frames.reverse();
+ stacktrace = {frames: frames};
+ } else if (fileurl) {
+ stacktrace = {
+ frames: [{
+ filename: fileurl,
+ lineno: lineno,
+ in_app: true
}]
- },
- culprit: fileurl,
- message: fullMessage
- }, options)
- );
-}
-
-function objectMerge(obj1, obj2) {
- if (!obj2) {
- return obj1;
- }
- each(obj2, function(key, value){
- obj1[key] = value;
- });
- return obj1;
-}
-
-function truncate(str, max) {
- return str.length <= max ? str : str.substr(0, max) + '\u2026';
-}
+ };
+ }
-function trimPacket(data) {
- // For now, we only want to truncate the two different messages
- // but this could/should be expanded to just trim everything
- var max = globalOptions.maxMessageLength;
- data.message = truncate(data.message, max);
- if (data.exception) {
- var exception = data.exception.values[0];
- exception.value = truncate(exception.value, max);
- }
+ if (!!this._globalOptions.ignoreUrls.test && this._globalOptions.ignoreUrls.test(fileurl)) return;
+ if (!!this._globalOptions.whitelistUrls.test && !this._globalOptions.whitelistUrls.test(fileurl)) return;
- return data;
-}
+ // Fire away!
+ this._send(
+ objectMerge({
+ // sentry.interfaces.Exception
+ exception: {
+ values: [{
+ type: type,
+ value: message,
+ stacktrace: stacktrace
+ }]
+ },
+ culprit: fileurl,
+ message: fullMessage
+ }, options)
+ );
+ },
-function now() {
- return +new Date();
-}
+ _trimPacket: function(data) {
+ // For now, we only want to truncate the two different messages
+ // but this could/should be expanded to just trim everything
+ var max = this._globalOptions.maxMessageLength;
+ data.message = truncate(data.message, max);
+ if (data.exception) {
+ var exception = data.exception.values[0];
+ exception.value = truncate(exception.value, max);
+ }
-function getHttpData() {
- if (!hasDocument || !document.location || !document.location.href) {
- return;
- }
+ return data;
+ },
- var httpData = {
- headers: {
- 'User-Agent': navigator.userAgent
+ _getHttpData: function() {
+ if (!this._hasDocument || !document.location || !document.location.href) {
+ return;
}
- };
-
- httpData.url = document.location.href;
- if (document.referrer) {
- httpData.headers.Referer = document.referrer;
- }
+ var httpData = {
+ headers: {
+ 'User-Agent': navigator.userAgent
+ }
+ };
- return httpData;
-}
+ httpData.url = document.location.href;
-function send(data) {
- var baseData = {
- project: globalProject,
- logger: globalOptions.logger,
- platform: 'javascript'
- }, httpData = getHttpData();
+ if (document.referrer) {
+ httpData.headers.Referer = document.referrer;
+ }
- if (httpData) {
- baseData.request = httpData;
- }
+ return httpData;
+ },
- data = objectMerge(baseData, data);
- // Merge in the tags and extra separately since objectMerge doesn't handle a deep merge
- data.tags = objectMerge(objectMerge({}, globalContext.tags), data.tags);
- data.extra = objectMerge(objectMerge({}, globalContext.extra), data.extra);
+ _send: function(data) {
+ var self = this;
- // Send along our own collected metadata with extra
- data.extra['session:duration'] = now() - startTime;
+ var globalOptions = this._globalOptions;
- // If there are no tags/extra, strip the key from the payload alltogther.
- if (isEmptyObject(data.tags)) delete data.tags;
+ var baseData = {
+ project: this._globalProject,
+ logger: globalOptions.logger,
+ platform: 'javascript'
+ }, httpData = this._getHttpData();
- if (globalContext.user) {
- // sentry.interfaces.User
- data.user = globalContext.user;
- }
+ if (httpData) {
+ baseData.request = httpData;
+ }
- // Include the release if it's defined in globalOptions
- if (globalOptions.release) data.release = globalOptions.release;
- // Include server_name if it's defined in globalOptions
- if (globalOptions.serverName) data.server_name = globalOptions.serverName;
+ data = objectMerge(baseData, data);
- if (isFunction(globalOptions.dataCallback)) {
- data = globalOptions.dataCallback(data) || data;
- }
+ // Merge in the tags and extra separately since objectMerge doesn't handle a deep merge
+ data.tags = objectMerge(objectMerge({}, this._globalContext.tags), data.tags);
+ data.extra = objectMerge(objectMerge({}, this._globalContext.extra), data.extra);
- // Why??????????
- if (!data || isEmptyObject(data)) {
- return;
- }
+ // Send along our own collected metadata with extra
+ data.extra['session:duration'] = now() - this._startTime;
- // Check if the request should be filtered or not
- if (isFunction(globalOptions.shouldSendCallback) && !globalOptions.shouldSendCallback(data)) {
- return;
- }
+ // If there are no tags/extra, strip the key from the payload alltogther.
+ if (isEmptyObject(data.tags)) delete data.tags;
- // Send along an event_id if not explicitly passed.
- // This event_id can be used to reference the error within Sentry itself.
- // Set lastEventId after we know the error should actually be sent
- lastEventId = data.event_id || (data.event_id = uuid4());
-
- // Try and clean up the packet before sending by truncating long values
- data = trimPacket(data);
-
- logDebug('debug', 'Raven about to send:', data);
-
- if (!isSetup()) return;
-
- (globalOptions.transport || makeRequest)({
- url: globalServer,
- auth: {
- sentry_version: '7',
- sentry_client: 'raven-js/' + Raven.VERSION,
- sentry_key: globalKey
- },
- data: data,
- options: globalOptions,
- onSuccess: function success() {
- triggerEvent('success', {
- data: data,
- src: globalServer
- });
- },
- onError: function failure() {
- triggerEvent('failure', {
- data: data,
- src: globalServer
- });
+ if (this._globalContext.user) {
+ // sentry.interfaces.User
+ data.user = this._globalContext.user;
}
- });
-}
-function makeImageRequest(opts) {
- // Tack on sentry_data to auth options, which get urlencoded
- opts.auth.sentry_data = JSON.stringify(opts.data);
+ // Include the release if it's defined in globalOptions
+ if (globalOptions.release) data.release = globalOptions.release;
+ // Include server_name if it's defined in globalOptions
+ if (globalOptions.serverName) data.server_name = globalOptions.serverName;
- var img = newImage(),
- src = opts.url + '?' + urlencode(opts.auth),
- crossOrigin = opts.options.crossOrigin;
+ // Include the release if it's defined in globalOptions
+ if (globalOptions.release) data.release = globalOptions.release;
- if (crossOrigin || crossOrigin === '') {
- img.crossOrigin = crossOrigin;
- }
- img.onload = opts.onSuccess;
- img.onerror = img.onabort = opts.onError;
- img.src = src;
-}
+ if (isFunction(globalOptions.dataCallback)) {
+ data = globalOptions.dataCallback(data) || data;
+ }
-function makeXhrRequest(opts) {
- var request;
+ // Why??????????
+ if (!data || isEmptyObject(data)) {
+ return;
+ }
- function handler() {
- if (request.status === 200) {
- if (opts.onSuccess) {
- opts.onSuccess();
- }
- } else if (opts.onError) {
- opts.onError();
+ // Check if the request should be filtered or not
+ if (isFunction(globalOptions.shouldSendCallback) && !globalOptions.shouldSendCallback(data)) {
+ return;
}
- }
- request = new XMLHttpRequest();
- if ('withCredentials' in request) {
- request.onreadystatechange = function () {
- if (request.readyState !== 4) {
- return;
- }
- handler();
- };
- } else {
- request = new XDomainRequest();
- // onreadystatechange not supported by XDomainRequest
- request.onload = handler;
- }
+ // Send along an event_id if not explicitly passed.
+ // This event_id can be used to reference the error within Sentry itself.
+ // Set lastEventId after we know the error should actually be sent
+ this._lastEventId = data.event_id || (data.event_id = uuid4());
- // NOTE: auth is intentionally sent as part of query string (NOT as custom
- // HTTP header) so as to avoid preflight CORS requests
- request.open('POST', opts.url + '?' + urlencode(opts.auth));
- request.send(JSON.stringify(opts.data));
-}
+ // Try and clean up the packet before sending by truncating long values
+ data = this._trimPacket(data);
-function makeRequest(opts) {
- var hasCORS =
- 'withCredentials' in new XMLHttpRequest() ||
- typeof XDomainRequest !== 'undefined';
+ this._logDebug('debug', 'Raven about to send:', data);
- return (hasCORS ? makeXhrRequest : makeImageRequest)(opts);
-}
+ if (!this.isSetup()) return;
-// Note: this is shitty, but I can't figure out how to get
-// sinon to stub document.createElement without breaking everything
-// so this wrapper is just so I can stub it for tests.
-function newImage() {
- return document.createElement('img');
-}
+ (globalOptions.transport || this._makeRequest).call(this, {
+ url: this._globalServer,
+ auth: {
+ sentry_version: '7',
+ sentry_client: 'raven-js/' + this.VERSION,
+ sentry_key: this._globalKey
+ },
+ data: data,
+ options: globalOptions,
+ onSuccess: function success() {
+ self._triggerEvent('success', {
+ data: data,
+ src: self._globalServer
+ });
+ },
+ onError: function failure() {
+ self._triggerEvent('failure', {
+ data: data,
+ src: self._globalServer
+ });
+ }
+ });
+ },
-var ravenNotConfiguredError;
+ _makeImageRequest: function(opts) {
+ // Tack on sentry_data to auth options, which get urlencoded
+ opts.auth.sentry_data = JSON.stringify(opts.data);
-function isSetup() {
- if (!hasJSON) return false; // needs JSON support
- if (!globalServer) {
- if (!ravenNotConfiguredError)
- logDebug('error', 'Error: Raven has not been configured.');
- ravenNotConfiguredError = true;
- return false;
- }
- return true;
-}
+ var img = this._newImage(),
+ src = opts.url + '?' + urlencode(opts.auth),
+ crossOrigin = opts.options.crossOrigin;
-function joinRegExp(patterns) {
- // Combine an array of regular expressions and strings into one large regexp
- // Be mad.
- var sources = [],
- i = 0, len = patterns.length,
- pattern;
-
- for (; i < len; i++) {
- pattern = patterns[i];
- if (isString(pattern)) {
- // If it's a string, we need to escape it
- // Taken from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
- sources.push(pattern.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"));
- } else if (pattern && pattern.source) {
- // If it's a regexp already, we want to extract the source
- sources.push(pattern.source);
- }
- // Intentionally skip other cases
- }
- return new RegExp(sources.join('|'), 'i');
-}
+ if (crossOrigin || crossOrigin === '') {
+ img.crossOrigin = crossOrigin;
+ }
+ img.onload = opts.onSuccess;
+ img.onerror = img.onabort = opts.onError;
+ img.src = src;
+ },
-function uuid4() {
- var crypto = window.crypto || window.msCrypto;
+ _makeXhrRequest: function(opts) {
+ var request;
- if (!isUndefined(crypto) && crypto.getRandomValues) {
- // Use window.crypto API if available
- var arr = new Uint16Array(8);
- crypto.getRandomValues(arr);
+ function handler() {
+ if (request.status === 200) {
+ if (opts.onSuccess) {
+ opts.onSuccess();
+ }
+ } else if (opts.onError) {
+ opts.onError();
+ }
+ }
- // set 4 in byte 7
- arr[3] = arr[3] & 0xFFF | 0x4000;
- // set 2 most significant bits of byte 9 to '10'
- arr[4] = arr[4] & 0x3FFF | 0x8000;
+ request = new XMLHttpRequest();
+ if ('withCredentials' in request) {
+ request.onreadystatechange = function () {
+ if (request.readyState !== 4) {
+ return;
+ }
+ handler();
+ };
+ } else {
+ request = new XDomainRequest();
+ // onreadystatechange not supported by XDomainRequest
+ request.onload = handler;
+ }
- var pad = function(num) {
- var v = num.toString(16);
- while (v.length < 4) {
- v = '0' + v;
- }
- return v;
- };
+ // NOTE: auth is intentionally sent as part of query string (NOT as custom
+ // HTTP header) so as to avoid preflight CORS requests
+ request.open('POST', opts.url + '?' + urlencode(opts.auth));
+ request.send(JSON.stringify(opts.data));
+ },
- return (pad(arr[0]) + pad(arr[1]) + pad(arr[2]) + pad(arr[3]) + pad(arr[4]) +
- pad(arr[5]) + pad(arr[6]) + pad(arr[7]));
- } else {
- // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523
- return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
- var r = Math.random()*16|0,
- v = c == 'x' ? r : (r&0x3|0x8);
- return v.toString(16);
- });
- }
-}
+ _makeRequest: function(opts) {
+ var hasCORS =
+ 'withCredentials' in new XMLHttpRequest() ||
+ typeof XDomainRequest !== 'undefined';
-function logDebug(level) {
- if (originalConsoleMethods[level] && Raven.debug) {
- // _slice is coming from vendor/TraceKit/tracekit.js
- // so it's accessible globally
- originalConsoleMethods[level].apply(originalConsole, _slice.call(arguments, 1));
- }
-}
+ return (hasCORS ? this._makeXhrRequest : this._makeImageRequest)(opts);
+ },
-function afterLoad() {
- // Attempt to initialize Raven on load
- var RavenConfig = window.RavenConfig;
- if (RavenConfig) {
- Raven.config(RavenConfig.dsn, RavenConfig.config).install();
- }
-}
+ // Note: this is shitty, but I can't figure out how to get
+ // sinon to stub document.createElement without breaking everything
+ // so this wrapper is just so I can stub it for tests.
+ _newImage: function() {
+ return document.createElement('img');
+ },
-function urlencode(o) {
- var pairs = [];
- each(o, function(key, value) {
- pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
- });
- return pairs.join('&');
-}
+ _logDebug: function(level) {
+ if (this._originalConsoleMethods[level] && this.debug) {
+ this._originalConsoleMethods[level].apply(this._originalConsole, [].slice.call(arguments, 1));
+ }
+ },
-function mergeContext(key, context) {
- if (isUndefined(context)) {
- delete globalContext[key];
- } else {
- globalContext[key] = objectMerge(globalContext[key] || {}, context);
+ _mergeContext: function(key, context) {
+ if (isUndefined(context)) {
+ delete this._globalContext[key];
+ } else {
+ this._globalContext[key] = objectMerge(this._globalContext[key] || {}, context);
+ }
}
-}
+};
+
+// Deprecations
+Raven.prototype.setUser = Raven.prototype.setUserContext;
+Raven.prototype.setReleaseContext = Raven.prototype.setRelease;
-afterLoad();
+module.exports = Raven;
diff --git a/src/singleton.js b/src/singleton.js
new file mode 100644
index 000000000000..48cee938b016
--- /dev/null
+++ b/src/singleton.js
@@ -0,0 +1,28 @@
+/**
+ * Enforces a single instance of the Raven client, and the
+ * main entry point for Raven. If you are a consumer of the
+ * Raven library, you SHOULD load this file (vs raven.js).
+ **/
+
+'use strict';
+
+var RavenConstructor = require('./raven');
+
+var _Raven = window.Raven;
+
+var Raven = new RavenConstructor();
+
+/*
+ * Allow multiple versions of Raven to be installed.
+ * Strip Raven from the global context and returns the instance.
+ *
+ * @return {Raven}
+ */
+Raven.noConflict = function () {
+ window.Raven = _Raven;
+ return Raven;
+};
+
+Raven.afterLoad();
+
+module.exports = Raven;
diff --git a/src/utils.js b/src/utils.js
new file mode 100644
index 000000000000..21da2fcad3cc
--- /dev/null
+++ b/src/utils.js
@@ -0,0 +1,155 @@
+'use strict';
+
+var objectPrototype = Object.prototype;
+
+function isUndefined(what) {
+ return what === void 0;
+}
+
+function isFunction(what) {
+ return typeof what === 'function';
+}
+
+function isString(what) {
+ return objectPrototype.toString.call(what) === '[object String]';
+}
+
+function isObject(what) {
+ return typeof what === 'object' && what !== null;
+}
+
+function isEmptyObject(what) {
+ for (var k in what) return false;
+ return true;
+}
+
+// Sorta yanked from https://github.com/joyent/node/blob/aa3b4b4/lib/util.js#L560
+// with some tiny modifications
+function isError(what) {
+ return isObject(what) &&
+ objectPrototype.toString.call(what) === '[object Error]' ||
+ what instanceof Error;
+}
+
+function each(obj, callback) {
+ var i, j;
+
+ if (isUndefined(obj.length)) {
+ for (i in obj) {
+ if (hasKey(obj, i)) {
+ callback.call(null, i, obj[i]);
+ }
+ }
+ } else {
+ j = obj.length;
+ if (j) {
+ for (i = 0; i < j; i++) {
+ callback.call(null, i, obj[i]);
+ }
+ }
+ }
+}
+
+function objectMerge(obj1, obj2) {
+ if (!obj2) {
+ return obj1;
+ }
+ each(obj2, function(key, value){
+ obj1[key] = value;
+ });
+ return obj1;
+}
+
+function truncate(str, max) {
+ return str.length <= max ? str : str.substr(0, max) + '\u2026';
+}
+
+/**
+ * hasKey, a better form of hasOwnProperty
+ * Example: hasKey(MainHostObject, property) === true/false
+ *
+ * @param {Object} host object to check property
+ * @param {string} key to check
+ */
+function hasKey(object, key) {
+ return objectPrototype.hasOwnProperty.call(object, key);
+}
+
+function joinRegExp(patterns) {
+ // Combine an array of regular expressions and strings into one large regexp
+ // Be mad.
+ var sources = [],
+ i = 0, len = patterns.length,
+ pattern;
+
+ for (; i < len; i++) {
+ pattern = patterns[i];
+ if (isString(pattern)) {
+ // If it's a string, we need to escape it
+ // Taken from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
+ sources.push(pattern.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"));
+ } else if (pattern && pattern.source) {
+ // If it's a regexp already, we want to extract the source
+ sources.push(pattern.source);
+ }
+ // Intentionally skip other cases
+ }
+ return new RegExp(sources.join('|'), 'i');
+}
+
+function urlencode(o) {
+ var pairs = [];
+ each(o, function(key, value) {
+ pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
+ });
+ return pairs.join('&');
+}
+
+function uuid4() {
+ var crypto = window.crypto || window.msCrypto;
+
+ if (!isUndefined(crypto) && crypto.getRandomValues) {
+ // Use window.crypto API if available
+ var arr = new Uint16Array(8);
+ crypto.getRandomValues(arr);
+
+ // set 4 in byte 7
+ arr[3] = arr[3] & 0xFFF | 0x4000;
+ // set 2 most significant bits of byte 9 to '10'
+ arr[4] = arr[4] & 0x3FFF | 0x8000;
+
+ var pad = function(num) {
+ var v = num.toString(16);
+ while (v.length < 4) {
+ v = '0' + v;
+ }
+ return v;
+ };
+
+ return (pad(arr[0]) + pad(arr[1]) + pad(arr[2]) + pad(arr[3]) + pad(arr[4]) +
+ pad(arr[5]) + pad(arr[6]) + pad(arr[7]));
+ } else {
+ // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523
+ return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
+ var r = Math.random()*16|0,
+ v = c == 'x' ? r : (r&0x3|0x8);
+ return v.toString(16);
+ });
+ }
+}
+
+module.exports = {
+ isUndefined: isUndefined,
+ isFunction: isFunction,
+ isString: isString,
+ isObject: isObject,
+ isEmptyObject: isEmptyObject,
+ isError: isError,
+ each: each,
+ objectMerge: objectMerge,
+ truncate: truncate,
+ hasKey: hasKey,
+ joinRegExp: joinRegExp,
+ urlencode: urlencode,
+ uuid4: uuid4
+};
diff --git a/template/_footer.js b/template/_footer.js
deleted file mode 100644
index b6583ef31388..000000000000
--- a/template/_footer.js
+++ /dev/null
@@ -1,19 +0,0 @@
-// This is being exposed no matter what because there are too many weird
-// usecases for how people use Raven. If this is really a problem, I'm sorry.
-window.Raven = Raven;
-
-// Expose Raven to the world
-if (typeof define === 'function' && define.amd) {
- // AMD
- define('raven', [], function() {
- return Raven;
- });
-} else if (typeof module === 'object') {
- // browserify
- module.exports = Raven;
-} else if (typeof exports === 'object') {
- // CommonJS
- exports = Raven;
-}
-
-})(typeof window !== 'undefined' ? window : this);
diff --git a/template/_header.js b/template/_header.js
deleted file mode 100644
index 27595a61a2db..000000000000
--- a/template/_header.js
+++ /dev/null
@@ -1,2 +0,0 @@
-;(function(window, undefined){
-'use strict';
diff --git a/test/index.html b/test/index.html
index d988e2313b9c..74d2a67dc6f6 100644
--- a/test/index.html
+++ b/test/index.html
@@ -19,6 +19,7 @@
-
-
-
-
-
-
-
+