raven.js 84 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568
  1. /*! Raven.js 3.5.1 (bef9fa7) | github.com/getsentry/raven-js */
  2. /*
  3. * Includes TraceKit
  4. * https://github.com/getsentry/TraceKit
  5. *
  6. * Copyright 2016 Matt Robenolt and other contributors
  7. * Released under the BSD license
  8. * https://github.com/getsentry/raven-js/blob/master/LICENSE
  9. *
  10. */
  11. (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Raven = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
  12. exports = module.exports = stringify
  13. exports.getSerialize = serializer
  14. function stringify(obj, replacer, spaces, cycleReplacer) {
  15. return JSON.stringify(obj, serializer(replacer, cycleReplacer), spaces)
  16. }
  17. function serializer(replacer, cycleReplacer) {
  18. var stack = [], keys = []
  19. if (cycleReplacer == null) cycleReplacer = function(key, value) {
  20. if (stack[0] === value) return "[Circular ~]"
  21. return "[Circular ~." + keys.slice(0, stack.indexOf(value)).join(".") + "]"
  22. }
  23. return function(key, value) {
  24. if (stack.length > 0) {
  25. var thisPos = stack.indexOf(this)
  26. ~thisPos ? stack.splice(thisPos + 1) : stack.push(this)
  27. ~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key)
  28. if (~stack.indexOf(value)) value = cycleReplacer.call(this, key, value)
  29. }
  30. else stack.push(value)
  31. return replacer == null ? value : replacer.call(this, key, value)
  32. }
  33. }
  34. },{}],2:[function(_dereq_,module,exports){
  35. 'use strict';
  36. function RavenConfigError(message) {
  37. this.name = 'RavenConfigError';
  38. this.message = message;
  39. }
  40. RavenConfigError.prototype = new Error();
  41. RavenConfigError.prototype.constructor = RavenConfigError;
  42. module.exports = RavenConfigError;
  43. },{}],3:[function(_dereq_,module,exports){
  44. 'use strict';
  45. var wrapMethod = function(console, level, callback) {
  46. var originalConsoleLevel = console[level];
  47. var originalConsole = console;
  48. if (!(level in console)) {
  49. return;
  50. }
  51. var sentryLevel = level === 'warn'
  52. ? 'warning'
  53. : level;
  54. console[level] = function () {
  55. var args = [].slice.call(arguments);
  56. var msg = '' + args.join(' ');
  57. var data = {level: sentryLevel, logger: 'console', extra: {'arguments': args}};
  58. callback && callback(msg, data);
  59. // this fails for some browsers. :(
  60. if (originalConsoleLevel) {
  61. // IE9 doesn't allow calling apply on console functions directly
  62. // See: https://stackoverflow.com/questions/5472938/does-ie9-support-console-log-and-is-it-a-real-function#answer-5473193
  63. Function.prototype.apply.call(
  64. originalConsoleLevel,
  65. originalConsole,
  66. args
  67. );
  68. }
  69. };
  70. };
  71. module.exports = {
  72. wrapMethod: wrapMethod
  73. };
  74. },{}],4:[function(_dereq_,module,exports){
  75. /*global XDomainRequest:false*/
  76. 'use strict';
  77. var TraceKit = _dereq_(7);
  78. var RavenConfigError = _dereq_(2);
  79. var utils = _dereq_(6);
  80. var stringify = _dereq_(1);
  81. var isFunction = utils.isFunction;
  82. var isUndefined = utils.isUndefined;
  83. var isError = utils.isError;
  84. var isEmptyObject = utils.isEmptyObject;
  85. var hasKey = utils.hasKey;
  86. var joinRegExp = utils.joinRegExp;
  87. var each = utils.each;
  88. var objectMerge = utils.objectMerge;
  89. var truncate = utils.truncate;
  90. var urlencode = utils.urlencode;
  91. var uuid4 = utils.uuid4;
  92. var htmlTreeAsString = utils.htmlTreeAsString;
  93. var parseUrl = utils.parseUrl;
  94. var isString = utils.isString;
  95. var fill = utils.fill;
  96. var wrapConsoleMethod = _dereq_(3).wrapMethod;
  97. var dsnKeys = 'source protocol user pass host port path'.split(' '),
  98. dsnPattern = /^(?:(\w+):)?\/\/(?:(\w+)(:\w+)?@)?([\w\.-]+)(?::(\d+))?(\/.*)/;
  99. function now() {
  100. return +new Date();
  101. }
  102. // First, check for JSON support
  103. // If there is no JSON, we no-op the core features of Raven
  104. // since JSON is required to encode the payload
  105. function Raven() {
  106. this._hasJSON = !!(typeof JSON === 'object' && JSON.stringify);
  107. // Raven can run in contexts where there's no document (react-native)
  108. this._hasDocument = typeof document !== 'undefined';
  109. this._lastCapturedException = null;
  110. this._lastEventId = null;
  111. this._globalServer = null;
  112. this._globalKey = null;
  113. this._globalProject = null;
  114. this._globalContext = {};
  115. this._globalOptions = {
  116. logger: 'javascript',
  117. ignoreErrors: [],
  118. ignoreUrls: [],
  119. whitelistUrls: [],
  120. includePaths: [],
  121. crossOrigin: 'anonymous',
  122. collectWindowErrors: true,
  123. maxMessageLength: 0,
  124. stackTraceLimit: 50,
  125. autoBreadcrumbs: true
  126. };
  127. this._ignoreOnError = 0;
  128. this._isRavenInstalled = false;
  129. this._originalErrorStackTraceLimit = Error.stackTraceLimit;
  130. // capture references to window.console *and* all its methods first
  131. // before the console plugin has a chance to monkey patch
  132. this._originalConsole = window.console || {};
  133. this._originalConsoleMethods = {};
  134. this._plugins = [];
  135. this._startTime = now();
  136. this._wrappedBuiltIns = [];
  137. this._breadcrumbs = [];
  138. this._lastCapturedEvent = null;
  139. this._keypressTimeout;
  140. this._location = window.location;
  141. this._lastHref = this._location && this._location.href;
  142. for (var method in this._originalConsole) { // eslint-disable-line guard-for-in
  143. this._originalConsoleMethods[method] = this._originalConsole[method];
  144. }
  145. }
  146. /*
  147. * The core Raven singleton
  148. *
  149. * @this {Raven}
  150. */
  151. Raven.prototype = {
  152. // Hardcode version string so that raven source can be loaded directly via
  153. // webpack (using a build step causes webpack #1617). Grunt verifies that
  154. // this value matches package.json during build.
  155. // See: https://github.com/getsentry/raven-js/issues/465
  156. VERSION: '3.5.1',
  157. debug: false,
  158. TraceKit: TraceKit, // alias to TraceKit
  159. /*
  160. * Configure Raven with a DSN and extra options
  161. *
  162. * @param {string} dsn The public Sentry DSN
  163. * @param {object} options Optional set of of global options [optional]
  164. * @return {Raven}
  165. */
  166. config: function(dsn, options) {
  167. var self = this;
  168. if (this._globalServer) {
  169. this._logDebug('error', 'Error: Raven has already been configured');
  170. return this;
  171. }
  172. if (!dsn) return this;
  173. // merge in options
  174. if (options) {
  175. each(options, function(key, value){
  176. // tags and extra are special and need to be put into context
  177. if (key === 'tags' || key === 'extra') {
  178. self._globalContext[key] = value;
  179. } else {
  180. self._globalOptions[key] = value;
  181. }
  182. });
  183. }
  184. var uri = this._parseDSN(dsn),
  185. lastSlash = uri.path.lastIndexOf('/'),
  186. path = uri.path.substr(1, lastSlash);
  187. this._dsn = dsn;
  188. // "Script error." is hard coded into browsers for errors that it can't read.
  189. // this is the result of a script being pulled in from an external domain and CORS.
  190. this._globalOptions.ignoreErrors.push(/^Script error\.?$/);
  191. this._globalOptions.ignoreErrors.push(/^Javascript error: Script error\.? on line 0$/);
  192. // join regexp rules into one big rule
  193. this._globalOptions.ignoreErrors = joinRegExp(this._globalOptions.ignoreErrors);
  194. this._globalOptions.ignoreUrls = this._globalOptions.ignoreUrls.length ? joinRegExp(this._globalOptions.ignoreUrls) : false;
  195. this._globalOptions.whitelistUrls = this._globalOptions.whitelistUrls.length ? joinRegExp(this._globalOptions.whitelistUrls) : false;
  196. this._globalOptions.includePaths = joinRegExp(this._globalOptions.includePaths);
  197. this._globalOptions.maxBreadcrumbs = Math.max(0, Math.min(this._globalOptions.maxBreadcrumbs || 100, 100)); // default and hard limit is 100
  198. var autoBreadcrumbDefaults = {
  199. xhr: true,
  200. console: true,
  201. dom: true,
  202. location: true
  203. };
  204. var autoBreadcrumbs = this._globalOptions.autoBreadcrumbs;
  205. if ({}.toString.call(autoBreadcrumbs) === '[object Object]') {
  206. autoBreadcrumbs = objectMerge(autoBreadcrumbDefaults, autoBreadcrumbs);
  207. } else if (autoBreadcrumbs !== false) {
  208. autoBreadcrumbs = autoBreadcrumbDefaults;
  209. }
  210. this._globalOptions.autoBreadcrumbs = autoBreadcrumbs;
  211. this._globalKey = uri.user;
  212. this._globalSecret = uri.pass && uri.pass.substr(1);
  213. this._globalProject = uri.path.substr(lastSlash + 1);
  214. this._globalServer = this._getGlobalServer(uri);
  215. this._globalEndpoint = this._globalServer +
  216. '/' + path + 'api/' + this._globalProject + '/store/';
  217. TraceKit.collectWindowErrors = !!this._globalOptions.collectWindowErrors;
  218. // return for chaining
  219. return this;
  220. },
  221. /*
  222. * Installs a global window.onerror error handler
  223. * to capture and report uncaught exceptions.
  224. * At this point, install() is required to be called due
  225. * to the way TraceKit is set up.
  226. *
  227. * @return {Raven}
  228. */
  229. install: function() {
  230. var self = this;
  231. if (this.isSetup() && !this._isRavenInstalled) {
  232. TraceKit.report.subscribe(function () {
  233. self._handleOnErrorStackInfo.apply(self, arguments);
  234. });
  235. this._instrumentTryCatch();
  236. if (self._globalOptions.autoBreadcrumbs)
  237. this._instrumentBreadcrumbs();
  238. // Install all of the plugins
  239. this._drainPlugins();
  240. this._isRavenInstalled = true;
  241. }
  242. Error.stackTraceLimit = this._globalOptions.stackTraceLimit;
  243. return this;
  244. },
  245. /*
  246. * Wrap code within a context so Raven can capture errors
  247. * reliably across domains that is executed immediately.
  248. *
  249. * @param {object} options A specific set of options for this context [optional]
  250. * @param {function} func The callback to be immediately executed within the context
  251. * @param {array} args An array of arguments to be called with the callback [optional]
  252. */
  253. context: function(options, func, args) {
  254. if (isFunction(options)) {
  255. args = func || [];
  256. func = options;
  257. options = undefined;
  258. }
  259. return this.wrap(options, func).apply(this, args);
  260. },
  261. /*
  262. * Wrap code within a context and returns back a new function to be executed
  263. *
  264. * @param {object} options A specific set of options for this context [optional]
  265. * @param {function} func The function to be wrapped in a new context
  266. * @param {function} func A function to call before the try/catch wrapper [optional, private]
  267. * @return {function} The newly wrapped functions with a context
  268. */
  269. wrap: function(options, func, _before) {
  270. var self = this;
  271. // 1 argument has been passed, and it's not a function
  272. // so just return it
  273. if (isUndefined(func) && !isFunction(options)) {
  274. return options;
  275. }
  276. // options is optional
  277. if (isFunction(options)) {
  278. func = options;
  279. options = undefined;
  280. }
  281. // At this point, we've passed along 2 arguments, and the second one
  282. // is not a function either, so we'll just return the second argument.
  283. if (!isFunction(func)) {
  284. return func;
  285. }
  286. // We don't wanna wrap it twice!
  287. try {
  288. if (func.__raven__) {
  289. return func;
  290. }
  291. } catch (e) {
  292. // Just accessing the __raven__ prop in some Selenium environments
  293. // can cause a "Permission denied" exception (see raven-js#495).
  294. // Bail on wrapping and return the function as-is (defers to window.onerror).
  295. return func;
  296. }
  297. // If this has already been wrapped in the past, return that
  298. if (func.__raven_wrapper__ ){
  299. return func.__raven_wrapper__ ;
  300. }
  301. function wrapped() {
  302. var args = [], i = arguments.length,
  303. deep = !options || options && options.deep !== false;
  304. if (_before && isFunction(_before)) {
  305. _before.apply(this, arguments);
  306. }
  307. // Recursively wrap all of a function's arguments that are
  308. // functions themselves.
  309. while(i--) args[i] = deep ? self.wrap(options, arguments[i]) : arguments[i];
  310. try {
  311. return func.apply(this, args);
  312. } catch(e) {
  313. self._ignoreNextOnError();
  314. self.captureException(e, options);
  315. throw e;
  316. }
  317. }
  318. // copy over properties of the old function
  319. for (var property in func) {
  320. if (hasKey(func, property)) {
  321. wrapped[property] = func[property];
  322. }
  323. }
  324. wrapped.prototype = func.prototype;
  325. func.__raven_wrapper__ = wrapped;
  326. // Signal that this function has been wrapped already
  327. // for both debugging and to prevent it to being wrapped twice
  328. wrapped.__raven__ = true;
  329. wrapped.__inner__ = func;
  330. return wrapped;
  331. },
  332. /*
  333. * Uninstalls the global error handler.
  334. *
  335. * @return {Raven}
  336. */
  337. uninstall: function() {
  338. TraceKit.report.uninstall();
  339. this._restoreBuiltIns();
  340. Error.stackTraceLimit = this._originalErrorStackTraceLimit;
  341. this._isRavenInstalled = false;
  342. return this;
  343. },
  344. /*
  345. * Manually capture an exception and send it over to Sentry
  346. *
  347. * @param {error} ex An exception to be logged
  348. * @param {object} options A specific set of options for this error [optional]
  349. * @return {Raven}
  350. */
  351. captureException: function(ex, options) {
  352. // If not an Error is passed through, recall as a message instead
  353. if (!isError(ex)) return this.captureMessage(ex, options);
  354. // Store the raw exception object for potential debugging and introspection
  355. this._lastCapturedException = ex;
  356. // TraceKit.report will re-raise any exception passed to it,
  357. // which means you have to wrap it in try/catch. Instead, we
  358. // can wrap it here and only re-raise if TraceKit.report
  359. // raises an exception different from the one we asked to
  360. // report on.
  361. try {
  362. var stack = TraceKit.computeStackTrace(ex);
  363. this._handleStackInfo(stack, options);
  364. } catch(ex1) {
  365. if(ex !== ex1) {
  366. throw ex1;
  367. }
  368. }
  369. return this;
  370. },
  371. /*
  372. * Manually send a message to Sentry
  373. *
  374. * @param {string} msg A plain message to be captured in Sentry
  375. * @param {object} options A specific set of options for this message [optional]
  376. * @return {Raven}
  377. */
  378. captureMessage: function(msg, options) {
  379. // config() automagically converts ignoreErrors from a list to a RegExp so we need to test for an
  380. // early call; we'll error on the side of logging anything called before configuration since it's
  381. // probably something you should see:
  382. if (!!this._globalOptions.ignoreErrors.test && this._globalOptions.ignoreErrors.test(msg)) {
  383. return;
  384. }
  385. // Fire away!
  386. this._send(
  387. objectMerge({
  388. message: msg + '' // Make sure it's actually a string
  389. }, options)
  390. );
  391. return this;
  392. },
  393. captureBreadcrumb: function (obj) {
  394. var crumb = objectMerge({
  395. timestamp: now() / 1000
  396. }, obj);
  397. this._breadcrumbs.push(crumb);
  398. if (this._breadcrumbs.length > this._globalOptions.maxBreadcrumbs) {
  399. this._breadcrumbs.shift();
  400. }
  401. },
  402. addPlugin: function(plugin /*arg1, arg2, ... argN*/) {
  403. var pluginArgs = Array.prototype.slice.call(arguments, 1);
  404. this._plugins.push([plugin, pluginArgs]);
  405. if (this._isRavenInstalled) {
  406. this._drainPlugins();
  407. }
  408. return this;
  409. },
  410. /*
  411. * Set/clear a user to be sent along with the payload.
  412. *
  413. * @param {object} user An object representing user data [optional]
  414. * @return {Raven}
  415. */
  416. setUserContext: function(user) {
  417. // Intentionally do not merge here since that's an unexpected behavior.
  418. this._globalContext.user = user;
  419. return this;
  420. },
  421. /*
  422. * Merge extra attributes to be sent along with the payload.
  423. *
  424. * @param {object} extra An object representing extra data [optional]
  425. * @return {Raven}
  426. */
  427. setExtraContext: function(extra) {
  428. this._mergeContext('extra', extra);
  429. return this;
  430. },
  431. /*
  432. * Merge tags to be sent along with the payload.
  433. *
  434. * @param {object} tags An object representing tags [optional]
  435. * @return {Raven}
  436. */
  437. setTagsContext: function(tags) {
  438. this._mergeContext('tags', tags);
  439. return this;
  440. },
  441. /*
  442. * Clear all of the context.
  443. *
  444. * @return {Raven}
  445. */
  446. clearContext: function() {
  447. this._globalContext = {};
  448. return this;
  449. },
  450. /*
  451. * Get a copy of the current context. This cannot be mutated.
  452. *
  453. * @return {object} copy of context
  454. */
  455. getContext: function() {
  456. // lol javascript
  457. return JSON.parse(stringify(this._globalContext));
  458. },
  459. /*
  460. * Set environment of application
  461. *
  462. * @param {string} environment Typically something like 'production'.
  463. * @return {Raven}
  464. */
  465. setEnvironment: function(environment) {
  466. this._globalOptions.environment = environment;
  467. return this;
  468. },
  469. /*
  470. * Set release version of application
  471. *
  472. * @param {string} release Typically something like a git SHA to identify version
  473. * @return {Raven}
  474. */
  475. setRelease: function(release) {
  476. this._globalOptions.release = release;
  477. return this;
  478. },
  479. /*
  480. * Set the dataCallback option
  481. *
  482. * @param {function} callback The callback to run which allows the
  483. * data blob to be mutated before sending
  484. * @return {Raven}
  485. */
  486. setDataCallback: function(callback) {
  487. var original = this._globalOptions.dataCallback;
  488. this._globalOptions.dataCallback = isFunction(callback)
  489. ? function (data) { return callback(data, original); }
  490. : callback;
  491. return this;
  492. },
  493. /*
  494. * Set the shouldSendCallback option
  495. *
  496. * @param {function} callback The callback to run which allows
  497. * introspecting the blob before sending
  498. * @return {Raven}
  499. */
  500. setShouldSendCallback: function(callback) {
  501. var original = this._globalOptions.shouldSendCallback;
  502. this._globalOptions.shouldSendCallback = isFunction(callback)
  503. ? function (data) { return callback(data, original); }
  504. : callback;
  505. return this;
  506. },
  507. /**
  508. * Override the default HTTP transport mechanism that transmits data
  509. * to the Sentry server.
  510. *
  511. * @param {function} transport Function invoked instead of the default
  512. * `makeRequest` handler.
  513. *
  514. * @return {Raven}
  515. */
  516. setTransport: function(transport) {
  517. this._globalOptions.transport = transport;
  518. return this;
  519. },
  520. /*
  521. * Get the latest raw exception that was captured by Raven.
  522. *
  523. * @return {error}
  524. */
  525. lastException: function() {
  526. return this._lastCapturedException;
  527. },
  528. /*
  529. * Get the last event id
  530. *
  531. * @return {string}
  532. */
  533. lastEventId: function() {
  534. return this._lastEventId;
  535. },
  536. /*
  537. * Determine if Raven is setup and ready to go.
  538. *
  539. * @return {boolean}
  540. */
  541. isSetup: function() {
  542. if (!this._hasJSON) return false; // needs JSON support
  543. if (!this._globalServer) {
  544. if (!this.ravenNotConfiguredError) {
  545. this.ravenNotConfiguredError = true;
  546. this._logDebug('error', 'Error: Raven has not been configured.');
  547. }
  548. return false;
  549. }
  550. return true;
  551. },
  552. afterLoad: function () {
  553. // TODO: remove window dependence?
  554. // Attempt to initialize Raven on load
  555. var RavenConfig = window.RavenConfig;
  556. if (RavenConfig) {
  557. this.config(RavenConfig.dsn, RavenConfig.config).install();
  558. }
  559. },
  560. showReportDialog: function (options) {
  561. if (!window.document) // doesn't work without a document (React native)
  562. return;
  563. options = options || {};
  564. var lastEventId = options.eventId || this.lastEventId();
  565. if (!lastEventId) {
  566. throw new RavenConfigError('Missing eventId');
  567. }
  568. var dsn = options.dsn || this._dsn;
  569. if (!dsn) {
  570. throw new RavenConfigError('Missing DSN');
  571. }
  572. var encode = encodeURIComponent;
  573. var qs = '';
  574. qs += '?eventId=' + encode(lastEventId);
  575. qs += '&dsn=' + encode(dsn);
  576. var user = options.user || this._globalContext.user;
  577. if (user) {
  578. if (user.name) qs += '&name=' + encode(user.name);
  579. if (user.email) qs += '&email=' + encode(user.email);
  580. }
  581. var globalServer = this._getGlobalServer(this._parseDSN(dsn));
  582. var script = document.createElement('script');
  583. script.async = true;
  584. script.src = globalServer + '/api/embed/error-page/' + qs;
  585. (document.head || document.body).appendChild(script);
  586. },
  587. /**** Private functions ****/
  588. _ignoreNextOnError: function () {
  589. var self = this;
  590. this._ignoreOnError += 1;
  591. setTimeout(function () {
  592. // onerror should trigger before setTimeout
  593. self._ignoreOnError -= 1;
  594. });
  595. },
  596. _triggerEvent: function(eventType, options) {
  597. // NOTE: `event` is a native browser thing, so let's avoid conflicting wiht it
  598. var evt, key;
  599. if (!this._hasDocument)
  600. return;
  601. options = options || {};
  602. eventType = 'raven' + eventType.substr(0,1).toUpperCase() + eventType.substr(1);
  603. if (document.createEvent) {
  604. evt = document.createEvent('HTMLEvents');
  605. evt.initEvent(eventType, true, true);
  606. } else {
  607. evt = document.createEventObject();
  608. evt.eventType = eventType;
  609. }
  610. for (key in options) if (hasKey(options, key)) {
  611. evt[key] = options[key];
  612. }
  613. if (document.createEvent) {
  614. // IE9 if standards
  615. document.dispatchEvent(evt);
  616. } else {
  617. // IE8 regardless of Quirks or Standards
  618. // IE9 if quirks
  619. try {
  620. document.fireEvent('on' + evt.eventType.toLowerCase(), evt);
  621. } catch(e) {
  622. // Do nothing
  623. }
  624. }
  625. },
  626. /**
  627. * Wraps addEventListener to capture UI breadcrumbs
  628. * @param evtName the event name (e.g. "click")
  629. * @returns {Function}
  630. * @private
  631. */
  632. _breadcrumbEventHandler: function(evtName) {
  633. var self = this;
  634. return function (evt) {
  635. // reset keypress timeout; e.g. triggering a 'click' after
  636. // a 'keypress' will reset the keypress debounce so that a new
  637. // set of keypresses can be recorded
  638. self._keypressTimeout = null;
  639. // It's possible this handler might trigger multiple times for the same
  640. // event (e.g. event propagation through node ancestors). Ignore if we've
  641. // already captured the event.
  642. if (self._lastCapturedEvent === evt)
  643. return;
  644. self._lastCapturedEvent = evt;
  645. var elem = evt.target;
  646. var target;
  647. // try/catch htmlTreeAsString because it's particularly complicated, and
  648. // just accessing the DOM incorrectly can throw an exception in some circumstances.
  649. try {
  650. target = htmlTreeAsString(elem);
  651. } catch (e) {
  652. target = '<unknown>';
  653. }
  654. self.captureBreadcrumb({
  655. category: 'ui.' + evtName, // e.g. ui.click, ui.input
  656. message: target
  657. });
  658. };
  659. },
  660. /**
  661. * Wraps addEventListener to capture keypress UI events
  662. * @returns {Function}
  663. * @private
  664. */
  665. _keypressEventHandler: function() {
  666. var self = this,
  667. debounceDuration = 1000; // milliseconds
  668. // TODO: if somehow user switches keypress target before
  669. // debounce timeout is triggered, we will only capture
  670. // a single breadcrumb from the FIRST target (acceptable?)
  671. return function (evt) {
  672. var target = evt.target,
  673. tagName = target && target.tagName;
  674. // only consider keypress events on actual input elements
  675. // this will disregard keypresses targeting body (e.g. tabbing
  676. // through elements, hotkeys, etc)
  677. if (!tagName || tagName !== 'INPUT' && tagName !== 'TEXTAREA')
  678. return;
  679. // record first keypress in a series, but ignore subsequent
  680. // keypresses until debounce clears
  681. var timeout = self._keypressTimeout;
  682. if (!timeout) {
  683. self._breadcrumbEventHandler('input')(evt);
  684. }
  685. clearTimeout(timeout);
  686. self._keypressTimeout = setTimeout(function () {
  687. self._keypressTimeout = null;
  688. }, debounceDuration);
  689. };
  690. },
  691. /**
  692. * Captures a breadcrumb of type "navigation", normalizing input URLs
  693. * @param to the originating URL
  694. * @param from the target URL
  695. * @private
  696. */
  697. _captureUrlChange: function(from, to) {
  698. var parsedLoc = parseUrl(this._location.href);
  699. var parsedTo = parseUrl(to);
  700. var parsedFrom = parseUrl(from);
  701. // because onpopstate only tells you the "new" (to) value of location.href, and
  702. // not the previous (from) value, we need to track the value of the current URL
  703. // state ourselves
  704. this._lastHref = to;
  705. // Use only the path component of the URL if the URL matches the current
  706. // document (almost all the time when using pushState)
  707. if (parsedLoc.protocol === parsedTo.protocol && parsedLoc.host === parsedTo.host)
  708. to = parsedTo.relative;
  709. if (parsedLoc.protocol === parsedFrom.protocol && parsedLoc.host === parsedFrom.host)
  710. from = parsedFrom.relative;
  711. this.captureBreadcrumb({
  712. category: 'navigation',
  713. data: {
  714. to: to,
  715. from: from
  716. }
  717. });
  718. },
  719. /**
  720. * Install any queued plugins
  721. */
  722. _instrumentTryCatch: function() {
  723. var self = this;
  724. var wrappedBuiltIns = self._wrappedBuiltIns;
  725. function wrapTimeFn(orig) {
  726. return function (fn, t) { // preserve arity
  727. // Make a copy of the arguments to prevent deoptimization
  728. // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#32-leaking-arguments
  729. var args = new Array(arguments.length);
  730. for(var i = 0; i < args.length; ++i) {
  731. args[i] = arguments[i];
  732. }
  733. var originalCallback = args[0];
  734. if (isFunction(originalCallback)) {
  735. args[0] = self.wrap(originalCallback);
  736. }
  737. // IE < 9 doesn't support .call/.apply on setInterval/setTimeout, but it
  738. // also supports only two arguments and doesn't care what this is, so we
  739. // can just call the original function directly.
  740. if (orig.apply) {
  741. return orig.apply(this, args);
  742. } else {
  743. return orig(args[0], args[1]);
  744. }
  745. };
  746. }
  747. var autoBreadcrumbs = this._globalOptions.autoBreadcrumbs;
  748. function wrapEventTarget(global) {
  749. var proto = window[global] && window[global].prototype;
  750. if (proto && proto.hasOwnProperty && proto.hasOwnProperty('addEventListener')) {
  751. fill(proto, 'addEventListener', function(orig) {
  752. return function (evtName, fn, capture, secure) { // preserve arity
  753. try {
  754. if (fn && fn.handleEvent) {
  755. fn.handleEvent = self.wrap(fn.handleEvent);
  756. }
  757. } catch (err) {
  758. // can sometimes get 'Permission denied to access property "handle Event'
  759. }
  760. // More breadcrumb DOM capture ... done here and not in `_instrumentBreadcrumbs`
  761. // so that we don't have more than one wrapper function
  762. var before;
  763. if (autoBreadcrumbs && autoBreadcrumbs.dom && (global === 'EventTarget' || global === 'Node')) {
  764. if (evtName === 'click'){
  765. before = self._breadcrumbEventHandler(evtName);
  766. } else if (evtName === 'keypress') {
  767. before = self._keypressEventHandler();
  768. }
  769. }
  770. return orig.call(this, evtName, self.wrap(fn, undefined, before), capture, secure);
  771. };
  772. }, wrappedBuiltIns);
  773. fill(proto, 'removeEventListener', function (orig) {
  774. return function (evt, fn, capture, secure) {
  775. fn = fn && (fn.__raven_wrapper__ ? fn.__raven_wrapper__ : fn);
  776. return orig.call(this, evt, fn, capture, secure);
  777. };
  778. }, wrappedBuiltIns);
  779. }
  780. }
  781. fill(window, 'setTimeout', wrapTimeFn, wrappedBuiltIns);
  782. fill(window, 'setInterval', wrapTimeFn, wrappedBuiltIns);
  783. if (window.requestAnimationFrame) {
  784. fill(window, 'requestAnimationFrame', function (orig) {
  785. return function (cb) {
  786. return orig(self.wrap(cb));
  787. };
  788. }, wrappedBuiltIns);
  789. }
  790. // event targets borrowed from bugsnag-js:
  791. // https://github.com/bugsnag/bugsnag-js/blob/master/src/bugsnag.js#L666
  792. var eventTargets = ['EventTarget', 'Window', 'Node', 'ApplicationCache', 'AudioTrackList', 'ChannelMergerNode', 'CryptoOperation', 'EventSource', 'FileReader', 'HTMLUnknownElement', 'IDBDatabase', 'IDBRequest', 'IDBTransaction', 'KeyOperation', 'MediaController', 'MessagePort', 'ModalWindow', 'Notification', 'SVGElementInstance', 'Screen', 'TextTrack', 'TextTrackCue', 'TextTrackList', 'WebSocket', 'WebSocketWorker', 'Worker', 'XMLHttpRequest', 'XMLHttpRequestEventTarget', 'XMLHttpRequestUpload'];
  793. for (var i = 0; i < eventTargets.length; i++) {
  794. wrapEventTarget(eventTargets[i]);
  795. }
  796. var $ = window.jQuery || window.$;
  797. if ($ && $.fn && $.fn.ready) {
  798. fill($.fn, 'ready', function (orig) {
  799. return function (fn) {
  800. return orig.call(this, self.wrap(fn));
  801. };
  802. }, wrappedBuiltIns);
  803. }
  804. },
  805. /**
  806. * Instrument browser built-ins w/ breadcrumb capturing
  807. * - XMLHttpRequests
  808. * - DOM interactions (click/typing)
  809. * - window.location changes
  810. * - console
  811. *
  812. * Can be disabled or individually configured via the `autoBreadcrumbs` config option
  813. */
  814. _instrumentBreadcrumbs: function () {
  815. var self = this;
  816. var autoBreadcrumbs = this._globalOptions.autoBreadcrumbs;
  817. var wrappedBuiltIns = self._wrappedBuiltIns;
  818. function wrapProp(prop, xhr) {
  819. if (prop in xhr && isFunction(xhr[prop])) {
  820. fill(xhr, prop, function (orig) {
  821. return self.wrap(orig);
  822. }); // intentionally don't track filled methods on XHR instances
  823. }
  824. }
  825. if (autoBreadcrumbs.xhr && 'XMLHttpRequest' in window) {
  826. var xhrproto = XMLHttpRequest.prototype;
  827. fill(xhrproto, 'open', function(origOpen) {
  828. return function (method, url) { // preserve arity
  829. // if Sentry key appears in URL, don't capture
  830. if (isString(url) && url.indexOf(self._globalKey) === -1) {
  831. this.__raven_xhr = {
  832. method: method,
  833. url: url,
  834. status_code: null
  835. };
  836. }
  837. return origOpen.apply(this, arguments);
  838. };
  839. }, wrappedBuiltIns);
  840. fill(xhrproto, 'send', function(origSend) {
  841. return function (data) { // preserve arity
  842. var xhr = this;
  843. function onreadystatechangeHandler() {
  844. if (xhr.__raven_xhr && (xhr.readyState === 1 || xhr.readyState === 4)) {
  845. try {
  846. // touching statusCode in some platforms throws
  847. // an exception
  848. xhr.__raven_xhr.status_code = xhr.status;
  849. } catch (e) { /* do nothing */ }
  850. self.captureBreadcrumb({
  851. type: 'http',
  852. category: 'xhr',
  853. data: xhr.__raven_xhr
  854. });
  855. }
  856. }
  857. var props = ['onload', 'onerror', 'onprogress'];
  858. for (var j = 0; j < props.length; j++) {
  859. wrapProp(props[j], xhr);
  860. }
  861. if ('onreadystatechange' in xhr && isFunction(xhr.onreadystatechange)) {
  862. fill(xhr, 'onreadystatechange', function (orig) {
  863. return self.wrap(orig, undefined, onreadystatechangeHandler);
  864. } /* intentionally don't track this instrumentation */);
  865. } else {
  866. // if onreadystatechange wasn't actually set by the page on this xhr, we
  867. // are free to set our own and capture the breadcrumb
  868. xhr.onreadystatechange = onreadystatechangeHandler;
  869. }
  870. return origSend.apply(this, arguments);
  871. };
  872. }, wrappedBuiltIns);
  873. }
  874. // Capture breadcrumbs from any click that is unhandled / bubbled up all the way
  875. // to the document. Do this before we instrument addEventListener.
  876. if (autoBreadcrumbs.dom && this._hasDocument) {
  877. if (document.addEventListener) {
  878. document.addEventListener('click', self._breadcrumbEventHandler('click'), false);
  879. document.addEventListener('keypress', self._keypressEventHandler(), false);
  880. }
  881. else {
  882. // IE8 Compatibility
  883. document.attachEvent('onclick', self._breadcrumbEventHandler('click'));
  884. document.attachEvent('onkeypress', self._keypressEventHandler());
  885. }
  886. }
  887. // record navigation (URL) changes
  888. // NOTE: in Chrome App environment, touching history.pushState, *even inside
  889. // a try/catch block*, will cause Chrome to output an error to console.error
  890. // borrowed from: https://github.com/angular/angular.js/pull/13945/files
  891. var chrome = window.chrome;
  892. var isChromePackagedApp = chrome && chrome.app && chrome.app.runtime;
  893. var hasPushState = !isChromePackagedApp && window.history && history.pushState;
  894. if (autoBreadcrumbs.location && hasPushState) {
  895. // TODO: remove onpopstate handler on uninstall()
  896. var oldOnPopState = window.onpopstate;
  897. window.onpopstate = function () {
  898. var currentHref = self._location.href;
  899. self._captureUrlChange(self._lastHref, currentHref);
  900. if (oldOnPopState) {
  901. return oldOnPopState.apply(this, arguments);
  902. }
  903. };
  904. fill(history, 'pushState', function (origPushState) {
  905. // note history.pushState.length is 0; intentionally not declaring
  906. // params to preserve 0 arity
  907. return function (/* state, title, url */) {
  908. var url = arguments.length > 2 ? arguments[2] : undefined;
  909. // url argument is optional
  910. if (url) {
  911. // coerce to string (this is what pushState does)
  912. self._captureUrlChange(self._lastHref, url + '');
  913. }
  914. return origPushState.apply(this, arguments);
  915. };
  916. }, wrappedBuiltIns);
  917. }
  918. if (autoBreadcrumbs.console && 'console' in window && console.log) {
  919. // console
  920. var consoleMethodCallback = function (msg, data) {
  921. self.captureBreadcrumb({
  922. message: msg,
  923. level: data.level,
  924. category: 'console'
  925. });
  926. };
  927. each(['debug', 'info', 'warn', 'error', 'log'], function (_, level) {
  928. wrapConsoleMethod(console, level, consoleMethodCallback);
  929. });
  930. }
  931. },
  932. _restoreBuiltIns: function () {
  933. // restore any wrapped builtins
  934. var builtin;
  935. while (this._wrappedBuiltIns.length) {
  936. builtin = this._wrappedBuiltIns.shift();
  937. var obj = builtin[0],
  938. name = builtin[1],
  939. orig = builtin[2];
  940. obj[name] = orig;
  941. }
  942. },
  943. _drainPlugins: function() {
  944. var self = this;
  945. // FIX ME TODO
  946. each(this._plugins, function(_, plugin) {
  947. var installer = plugin[0];
  948. var args = plugin[1];
  949. installer.apply(self, [self].concat(args));
  950. });
  951. },
  952. _parseDSN: function(str) {
  953. var m = dsnPattern.exec(str),
  954. dsn = {},
  955. i = 7;
  956. try {
  957. while (i--) dsn[dsnKeys[i]] = m[i] || '';
  958. } catch(e) {
  959. throw new RavenConfigError('Invalid DSN: ' + str);
  960. }
  961. if (dsn.pass && !this._globalOptions.allowSecretKey) {
  962. throw new RavenConfigError('Do not specify your secret key in the DSN. See: http://bit.ly/raven-secret-key');
  963. }
  964. return dsn;
  965. },
  966. _getGlobalServer: function(uri) {
  967. // assemble the endpoint from the uri pieces
  968. var globalServer = '//' + uri.host +
  969. (uri.port ? ':' + uri.port : '');
  970. if (uri.protocol) {
  971. globalServer = uri.protocol + ':' + globalServer;
  972. }
  973. return globalServer;
  974. },
  975. _handleOnErrorStackInfo: function() {
  976. // if we are intentionally ignoring errors via onerror, bail out
  977. if (!this._ignoreOnError) {
  978. this._handleStackInfo.apply(this, arguments);
  979. }
  980. },
  981. _handleStackInfo: function(stackInfo, options) {
  982. var self = this;
  983. var frames = [];
  984. if (stackInfo.stack && stackInfo.stack.length) {
  985. each(stackInfo.stack, function(i, stack) {
  986. var frame = self._normalizeFrame(stack);
  987. if (frame) {
  988. frames.push(frame);
  989. }
  990. });
  991. }
  992. this._triggerEvent('handle', {
  993. stackInfo: stackInfo,
  994. options: options
  995. });
  996. this._processException(
  997. stackInfo.name,
  998. stackInfo.message,
  999. stackInfo.url,
  1000. stackInfo.lineno,
  1001. frames.slice(0, this._globalOptions.stackTraceLimit),
  1002. options
  1003. );
  1004. },
  1005. _normalizeFrame: function(frame) {
  1006. if (!frame.url) return;
  1007. // normalize the frames data
  1008. var normalized = {
  1009. filename: frame.url,
  1010. lineno: frame.line,
  1011. colno: frame.column,
  1012. 'function': frame.func || '?'
  1013. };
  1014. normalized.in_app = !( // determine if an exception came from outside of our app
  1015. // first we check the global includePaths list.
  1016. !!this._globalOptions.includePaths.test && !this._globalOptions.includePaths.test(normalized.filename) ||
  1017. // Now we check for fun, if the function name is Raven or TraceKit
  1018. /(Raven|TraceKit)\./.test(normalized['function']) ||
  1019. // finally, we do a last ditch effort and check for raven.min.js
  1020. /raven\.(min\.)?js$/.test(normalized.filename)
  1021. );
  1022. return normalized;
  1023. },
  1024. _processException: function(type, message, fileurl, lineno, frames, options) {
  1025. var stacktrace;
  1026. if (!!this._globalOptions.ignoreErrors.test && this._globalOptions.ignoreErrors.test(message)) return;
  1027. message += '';
  1028. if (frames && frames.length) {
  1029. fileurl = frames[0].filename || fileurl;
  1030. // Sentry expects frames oldest to newest
  1031. // and JS sends them as newest to oldest
  1032. frames.reverse();
  1033. stacktrace = {frames: frames};
  1034. } else if (fileurl) {
  1035. stacktrace = {
  1036. frames: [{
  1037. filename: fileurl,
  1038. lineno: lineno,
  1039. in_app: true
  1040. }]
  1041. };
  1042. }
  1043. if (!!this._globalOptions.ignoreUrls.test && this._globalOptions.ignoreUrls.test(fileurl)) return;
  1044. if (!!this._globalOptions.whitelistUrls.test && !this._globalOptions.whitelistUrls.test(fileurl)) return;
  1045. var data = objectMerge({
  1046. // sentry.interfaces.Exception
  1047. exception: {
  1048. values: [{
  1049. type: type,
  1050. value: message,
  1051. stacktrace: stacktrace
  1052. }]
  1053. },
  1054. culprit: fileurl
  1055. }, options);
  1056. // Fire away!
  1057. this._send(data);
  1058. },
  1059. _trimPacket: function(data) {
  1060. // For now, we only want to truncate the two different messages
  1061. // but this could/should be expanded to just trim everything
  1062. var max = this._globalOptions.maxMessageLength;
  1063. if (data.message) {
  1064. data.message = truncate(data.message, max);
  1065. }
  1066. if (data.exception) {
  1067. var exception = data.exception.values[0];
  1068. exception.value = truncate(exception.value, max);
  1069. }
  1070. return data;
  1071. },
  1072. _getHttpData: function() {
  1073. if (!this._hasDocument || !document.location || !document.location.href) {
  1074. return;
  1075. }
  1076. var httpData = {
  1077. headers: {
  1078. 'User-Agent': navigator.userAgent
  1079. }
  1080. };
  1081. httpData.url = document.location.href;
  1082. if (document.referrer) {
  1083. httpData.headers.Referer = document.referrer;
  1084. }
  1085. return httpData;
  1086. },
  1087. _send: function(data) {
  1088. var globalOptions = this._globalOptions;
  1089. var baseData = {
  1090. project: this._globalProject,
  1091. logger: globalOptions.logger,
  1092. platform: 'javascript'
  1093. }, httpData = this._getHttpData();
  1094. if (httpData) {
  1095. baseData.request = httpData;
  1096. }
  1097. data = objectMerge(baseData, data);
  1098. // Merge in the tags and extra separately since objectMerge doesn't handle a deep merge
  1099. data.tags = objectMerge(objectMerge({}, this._globalContext.tags), data.tags);
  1100. data.extra = objectMerge(objectMerge({}, this._globalContext.extra), data.extra);
  1101. // Send along our own collected metadata with extra
  1102. data.extra['session:duration'] = now() - this._startTime;
  1103. if (this._breadcrumbs && this._breadcrumbs.length > 0) {
  1104. // intentionally make shallow copy so that additions
  1105. // to breadcrumbs aren't accidentally sent in this request
  1106. data.breadcrumbs = {
  1107. values: [].slice.call(this._breadcrumbs, 0)
  1108. };
  1109. }
  1110. // If there are no tags/extra, strip the key from the payload alltogther.
  1111. if (isEmptyObject(data.tags)) delete data.tags;
  1112. if (this._globalContext.user) {
  1113. // sentry.interfaces.User
  1114. data.user = this._globalContext.user;
  1115. }
  1116. // Include the environment if it's defined in globalOptions
  1117. if (globalOptions.environment) data.environment = globalOptions.environment;
  1118. // Include the release if it's defined in globalOptions
  1119. if (globalOptions.release) data.release = globalOptions.release;
  1120. // Include server_name if it's defined in globalOptions
  1121. if (globalOptions.serverName) data.server_name = globalOptions.serverName;
  1122. if (isFunction(globalOptions.dataCallback)) {
  1123. data = globalOptions.dataCallback(data) || data;
  1124. }
  1125. // Why??????????
  1126. if (!data || isEmptyObject(data)) {
  1127. return;
  1128. }
  1129. // Check if the request should be filtered or not
  1130. if (isFunction(globalOptions.shouldSendCallback) && !globalOptions.shouldSendCallback(data)) {
  1131. return;
  1132. }
  1133. this._sendProcessedPayload(data);
  1134. },
  1135. _sendProcessedPayload: function(data, callback) {
  1136. var self = this;
  1137. var globalOptions = this._globalOptions;
  1138. // Send along an event_id if not explicitly passed.
  1139. // This event_id can be used to reference the error within Sentry itself.
  1140. // Set lastEventId after we know the error should actually be sent
  1141. this._lastEventId = data.event_id || (data.event_id = uuid4());
  1142. // Try and clean up the packet before sending by truncating long values
  1143. data = this._trimPacket(data);
  1144. this._logDebug('debug', 'Raven about to send:', data);
  1145. if (!this.isSetup()) return;
  1146. var auth = {
  1147. sentry_version: '7',
  1148. sentry_client: 'raven-js/' + this.VERSION,
  1149. sentry_key: this._globalKey
  1150. };
  1151. if (this._globalSecret) {
  1152. auth.sentry_secret = this._globalSecret;
  1153. }
  1154. var exception = data.exception && data.exception.values[0];
  1155. this.captureBreadcrumb({
  1156. category: 'sentry',
  1157. message: exception
  1158. ? (exception.type ? exception.type + ': ' : '') + exception.value
  1159. : data.message,
  1160. event_id: data.event_id,
  1161. level: data.level || 'error' // presume error unless specified
  1162. });
  1163. var url = this._globalEndpoint;
  1164. (globalOptions.transport || this._makeRequest).call(this, {
  1165. url: url,
  1166. auth: auth,
  1167. data: data,
  1168. options: globalOptions,
  1169. onSuccess: function success() {
  1170. self._triggerEvent('success', {
  1171. data: data,
  1172. src: url
  1173. });
  1174. callback && callback();
  1175. },
  1176. onError: function failure(error) {
  1177. self._triggerEvent('failure', {
  1178. data: data,
  1179. src: url
  1180. });
  1181. error = error || new Error('Raven send failed (no additional details provided)');
  1182. callback && callback(error);
  1183. }
  1184. });
  1185. },
  1186. _makeRequest: function(opts) {
  1187. var request = new XMLHttpRequest();
  1188. // if browser doesn't support CORS (e.g. IE7), we are out of luck
  1189. var hasCORS =
  1190. 'withCredentials' in request ||
  1191. typeof XDomainRequest !== 'undefined';
  1192. if (!hasCORS) return;
  1193. var url = opts.url;
  1194. function handler() {
  1195. if (request.status === 200) {
  1196. if (opts.onSuccess) {
  1197. opts.onSuccess();
  1198. }
  1199. } else if (opts.onError) {
  1200. opts.onError(new Error('Sentry error code: ' + request.status));
  1201. }
  1202. }
  1203. if ('withCredentials' in request) {
  1204. request.onreadystatechange = function () {
  1205. if (request.readyState !== 4) {
  1206. return;
  1207. }
  1208. handler();
  1209. };
  1210. } else {
  1211. request = new XDomainRequest();
  1212. // xdomainrequest cannot go http -> https (or vice versa),
  1213. // so always use protocol relative
  1214. url = url.replace(/^https?:/, '');
  1215. // onreadystatechange not supported by XDomainRequest
  1216. request.onload = handler;
  1217. }
  1218. // NOTE: auth is intentionally sent as part of query string (NOT as custom
  1219. // HTTP header) so as to avoid preflight CORS requests
  1220. request.open('POST', url + '?' + urlencode(opts.auth));
  1221. request.send(stringify(opts.data));
  1222. },
  1223. _logDebug: function(level) {
  1224. if (this._originalConsoleMethods[level] && this.debug) {
  1225. // In IE<10 console methods do not have their own 'apply' method
  1226. Function.prototype.apply.call(
  1227. this._originalConsoleMethods[level],
  1228. this._originalConsole,
  1229. [].slice.call(arguments, 1)
  1230. );
  1231. }
  1232. },
  1233. _mergeContext: function(key, context) {
  1234. if (isUndefined(context)) {
  1235. delete this._globalContext[key];
  1236. } else {
  1237. this._globalContext[key] = objectMerge(this._globalContext[key] || {}, context);
  1238. }
  1239. }
  1240. };
  1241. // Deprecations
  1242. Raven.prototype.setUser = Raven.prototype.setUserContext;
  1243. Raven.prototype.setReleaseContext = Raven.prototype.setRelease;
  1244. module.exports = Raven;
  1245. },{"1":1,"2":2,"3":3,"6":6,"7":7}],5:[function(_dereq_,module,exports){
  1246. /**
  1247. * Enforces a single instance of the Raven client, and the
  1248. * main entry point for Raven. If you are a consumer of the
  1249. * Raven library, you SHOULD load this file (vs raven.js).
  1250. **/
  1251. 'use strict';
  1252. var RavenConstructor = _dereq_(4);
  1253. var _Raven = window.Raven;
  1254. var Raven = new RavenConstructor();
  1255. /*
  1256. * Allow multiple versions of Raven to be installed.
  1257. * Strip Raven from the global context and returns the instance.
  1258. *
  1259. * @return {Raven}
  1260. */
  1261. Raven.noConflict = function () {
  1262. window.Raven = _Raven;
  1263. return Raven;
  1264. };
  1265. Raven.afterLoad();
  1266. module.exports = Raven;
  1267. },{"4":4}],6:[function(_dereq_,module,exports){
  1268. /*eslint no-extra-parens:0*/
  1269. 'use strict';
  1270. var objectPrototype = Object.prototype;
  1271. function isUndefined(what) {
  1272. return what === void 0;
  1273. }
  1274. function isFunction(what) {
  1275. return typeof what === 'function';
  1276. }
  1277. function isString(what) {
  1278. return objectPrototype.toString.call(what) === '[object String]';
  1279. }
  1280. function isObject(what) {
  1281. return typeof what === 'object' && what !== null;
  1282. }
  1283. function isEmptyObject(what) {
  1284. for (var _ in what) return false; // eslint-disable-line guard-for-in, no-unused-vars
  1285. return true;
  1286. }
  1287. // Sorta yanked from https://github.com/joyent/node/blob/aa3b4b4/lib/util.js#L560
  1288. // with some tiny modifications
  1289. function isError(what) {
  1290. var toString = objectPrototype.toString.call(what);
  1291. return isObject(what) &&
  1292. toString === '[object Error]' ||
  1293. toString === '[object Exception]' || // Firefox NS_ERROR_FAILURE Exceptions
  1294. what instanceof Error;
  1295. }
  1296. function each(obj, callback) {
  1297. var i, j;
  1298. if (isUndefined(obj.length)) {
  1299. for (i in obj) {
  1300. if (hasKey(obj, i)) {
  1301. callback.call(null, i, obj[i]);
  1302. }
  1303. }
  1304. } else {
  1305. j = obj.length;
  1306. if (j) {
  1307. for (i = 0; i < j; i++) {
  1308. callback.call(null, i, obj[i]);
  1309. }
  1310. }
  1311. }
  1312. }
  1313. function objectMerge(obj1, obj2) {
  1314. if (!obj2) {
  1315. return obj1;
  1316. }
  1317. each(obj2, function(key, value){
  1318. obj1[key] = value;
  1319. });
  1320. return obj1;
  1321. }
  1322. function truncate(str, max) {
  1323. return !max || str.length <= max ? str : str.substr(0, max) + '\u2026';
  1324. }
  1325. /**
  1326. * hasKey, a better form of hasOwnProperty
  1327. * Example: hasKey(MainHostObject, property) === true/false
  1328. *
  1329. * @param {Object} host object to check property
  1330. * @param {string} key to check
  1331. */
  1332. function hasKey(object, key) {
  1333. return objectPrototype.hasOwnProperty.call(object, key);
  1334. }
  1335. function joinRegExp(patterns) {
  1336. // Combine an array of regular expressions and strings into one large regexp
  1337. // Be mad.
  1338. var sources = [],
  1339. i = 0, len = patterns.length,
  1340. pattern;
  1341. for (; i < len; i++) {
  1342. pattern = patterns[i];
  1343. if (isString(pattern)) {
  1344. // If it's a string, we need to escape it
  1345. // Taken from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
  1346. sources.push(pattern.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1'));
  1347. } else if (pattern && pattern.source) {
  1348. // If it's a regexp already, we want to extract the source
  1349. sources.push(pattern.source);
  1350. }
  1351. // Intentionally skip other cases
  1352. }
  1353. return new RegExp(sources.join('|'), 'i');
  1354. }
  1355. function urlencode(o) {
  1356. var pairs = [];
  1357. each(o, function(key, value) {
  1358. pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
  1359. });
  1360. return pairs.join('&');
  1361. }
  1362. // borrowed from https://tools.ietf.org/html/rfc3986#appendix-B
  1363. // intentionally using regex and not <a/> href parsing trick because React Native and other
  1364. // environments where DOM might not be available
  1365. function parseUrl(url) {
  1366. var match = url.match(/^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/);
  1367. if (!match) return {};
  1368. // coerce to undefined values to empty string so we don't get 'undefined'
  1369. var query = match[6] || '';
  1370. var fragment = match[8] || '';
  1371. return {
  1372. protocol: match[2],
  1373. host: match[4],
  1374. path: match[5],
  1375. relative: match[5] + query + fragment // everything minus origin
  1376. };
  1377. }
  1378. function uuid4() {
  1379. var crypto = window.crypto || window.msCrypto;
  1380. if (!isUndefined(crypto) && crypto.getRandomValues) {
  1381. // Use window.crypto API if available
  1382. var arr = new Uint16Array(8);
  1383. crypto.getRandomValues(arr);
  1384. // set 4 in byte 7
  1385. arr[3] = arr[3] & 0xFFF | 0x4000;
  1386. // set 2 most significant bits of byte 9 to '10'
  1387. arr[4] = arr[4] & 0x3FFF | 0x8000;
  1388. var pad = function(num) {
  1389. var v = num.toString(16);
  1390. while (v.length < 4) {
  1391. v = '0' + v;
  1392. }
  1393. return v;
  1394. };
  1395. return pad(arr[0]) + pad(arr[1]) + pad(arr[2]) + pad(arr[3]) + pad(arr[4]) +
  1396. pad(arr[5]) + pad(arr[6]) + pad(arr[7]);
  1397. } else {
  1398. // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523
  1399. return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
  1400. var r = Math.random()*16|0,
  1401. v = c === 'x' ? r : r&0x3|0x8;
  1402. return v.toString(16);
  1403. });
  1404. }
  1405. }
  1406. /**
  1407. * Given a child DOM element, returns a query-selector statement describing that
  1408. * and its ancestors
  1409. * e.g. [HTMLElement] => body > div > input#foo.btn[name=baz]
  1410. * @param elem
  1411. * @returns {string}
  1412. */
  1413. function htmlTreeAsString(elem) {
  1414. var MAX_TRAVERSE_HEIGHT = 5,
  1415. MAX_OUTPUT_LEN = 80,
  1416. out = [],
  1417. height = 0,
  1418. len = 0,
  1419. separator = ' > ',
  1420. sepLength = separator.length,
  1421. nextStr;
  1422. while (elem && height++ < MAX_TRAVERSE_HEIGHT) {
  1423. nextStr = htmlElementAsString(elem);
  1424. // bail out if
  1425. // - nextStr is the 'html' element
  1426. // - the length of the string that would be created exceeds MAX_OUTPUT_LEN
  1427. // (ignore this limit if we are on the first iteration)
  1428. if (nextStr === 'html' || height > 1 && len + (out.length * sepLength) + nextStr.length >= MAX_OUTPUT_LEN) {
  1429. break;
  1430. }
  1431. out.push(nextStr);
  1432. len += nextStr.length;
  1433. elem = elem.parentNode;
  1434. }
  1435. return out.reverse().join(separator);
  1436. }
  1437. /**
  1438. * Returns a simple, query-selector representation of a DOM element
  1439. * e.g. [HTMLElement] => input#foo.btn[name=baz]
  1440. * @param HTMLElement
  1441. * @returns {string}
  1442. */
  1443. function htmlElementAsString(elem) {
  1444. var out = [],
  1445. className,
  1446. classes,
  1447. key,
  1448. attr,
  1449. i;
  1450. if (!elem || !elem.tagName) {
  1451. return '';
  1452. }
  1453. out.push(elem.tagName.toLowerCase());
  1454. if (elem.id) {
  1455. out.push('#' + elem.id);
  1456. }
  1457. className = elem.className;
  1458. if (className && isString(className)) {
  1459. classes = className.split(' ');
  1460. for (i = 0; i < classes.length; i++) {
  1461. out.push('.' + classes[i]);
  1462. }
  1463. }
  1464. var attrWhitelist = ['type', 'name', 'title', 'alt'];
  1465. for (i = 0; i < attrWhitelist.length; i++) {
  1466. key = attrWhitelist[i];
  1467. attr = elem.getAttribute(key);
  1468. if (attr) {
  1469. out.push('[' + key + '="' + attr + '"]');
  1470. }
  1471. }
  1472. return out.join('');
  1473. }
  1474. /**
  1475. * Polyfill a method
  1476. * @param obj object e.g. `document`
  1477. * @param name method name present on object e.g. `addEventListener`
  1478. * @param replacement replacement function
  1479. * @param track {optional} record instrumentation to an array
  1480. */
  1481. function fill(obj, name, replacement, track) {
  1482. var orig = obj[name];
  1483. obj[name] = replacement(orig);
  1484. if (track) {
  1485. track.push([obj, name, orig]);
  1486. }
  1487. }
  1488. module.exports = {
  1489. isUndefined: isUndefined,
  1490. isFunction: isFunction,
  1491. isString: isString,
  1492. isObject: isObject,
  1493. isEmptyObject: isEmptyObject,
  1494. isError: isError,
  1495. each: each,
  1496. objectMerge: objectMerge,
  1497. truncate: truncate,
  1498. hasKey: hasKey,
  1499. joinRegExp: joinRegExp,
  1500. urlencode: urlencode,
  1501. uuid4: uuid4,
  1502. htmlTreeAsString: htmlTreeAsString,
  1503. htmlElementAsString: htmlElementAsString,
  1504. parseUrl: parseUrl,
  1505. fill: fill
  1506. };
  1507. },{}],7:[function(_dereq_,module,exports){
  1508. 'use strict';
  1509. var utils = _dereq_(6);
  1510. var hasKey = utils.hasKey;
  1511. var isString = utils.isString;
  1512. var isUndefined = utils.isUndefined;
  1513. /*
  1514. TraceKit - Cross brower stack traces - github.com/occ/TraceKit
  1515. MIT license
  1516. */
  1517. var TraceKit = {
  1518. collectWindowErrors: true,
  1519. debug: false
  1520. };
  1521. // global reference to slice
  1522. var _slice = [].slice;
  1523. var UNKNOWN_FUNCTION = '?';
  1524. // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#Error_types
  1525. var ERROR_TYPES_RE = /^(?:Uncaught (?:exception: )?)?((?:Eval|Internal|Range|Reference|Syntax|Type|URI)Error): ?(.*)$/;
  1526. function getLocationHref() {
  1527. if (typeof document === 'undefined')
  1528. return '';
  1529. return document.location.href;
  1530. }
  1531. /**
  1532. * TraceKit.report: cross-browser processing of unhandled exceptions
  1533. *
  1534. * Syntax:
  1535. * TraceKit.report.subscribe(function(stackInfo) { ... })
  1536. * TraceKit.report.unsubscribe(function(stackInfo) { ... })
  1537. * TraceKit.report(exception)
  1538. * try { ...code... } catch(ex) { TraceKit.report(ex); }
  1539. *
  1540. * Supports:
  1541. * - Firefox: full stack trace with line numbers, plus column number
  1542. * on top frame; column number is not guaranteed
  1543. * - Opera: full stack trace with line and column numbers
  1544. * - Chrome: full stack trace with line and column numbers
  1545. * - Safari: line and column number for the top frame only; some frames
  1546. * may be missing, and column number is not guaranteed
  1547. * - IE: line and column number for the top frame only; some frames
  1548. * may be missing, and column number is not guaranteed
  1549. *
  1550. * In theory, TraceKit should work on all of the following versions:
  1551. * - IE5.5+ (only 8.0 tested)
  1552. * - Firefox 0.9+ (only 3.5+ tested)
  1553. * - Opera 7+ (only 10.50 tested; versions 9 and earlier may require
  1554. * Exceptions Have Stacktrace to be enabled in opera:config)
  1555. * - Safari 3+ (only 4+ tested)
  1556. * - Chrome 1+ (only 5+ tested)
  1557. * - Konqueror 3.5+ (untested)
  1558. *
  1559. * Requires TraceKit.computeStackTrace.
  1560. *
  1561. * Tries to catch all unhandled exceptions and report them to the
  1562. * subscribed handlers. Please note that TraceKit.report will rethrow the
  1563. * exception. This is REQUIRED in order to get a useful stack trace in IE.
  1564. * If the exception does not reach the top of the browser, you will only
  1565. * get a stack trace from the point where TraceKit.report was called.
  1566. *
  1567. * Handlers receive a stackInfo object as described in the
  1568. * TraceKit.computeStackTrace docs.
  1569. */
  1570. TraceKit.report = (function reportModuleWrapper() {
  1571. var handlers = [],
  1572. lastArgs = null,
  1573. lastException = null,
  1574. lastExceptionStack = null;
  1575. /**
  1576. * Add a crash handler.
  1577. * @param {Function} handler
  1578. */
  1579. function subscribe(handler) {
  1580. installGlobalHandler();
  1581. handlers.push(handler);
  1582. }
  1583. /**
  1584. * Remove a crash handler.
  1585. * @param {Function} handler
  1586. */
  1587. function unsubscribe(handler) {
  1588. for (var i = handlers.length - 1; i >= 0; --i) {
  1589. if (handlers[i] === handler) {
  1590. handlers.splice(i, 1);
  1591. }
  1592. }
  1593. }
  1594. /**
  1595. * Remove all crash handlers.
  1596. */
  1597. function unsubscribeAll() {
  1598. uninstallGlobalHandler();
  1599. handlers = [];
  1600. }
  1601. /**
  1602. * Dispatch stack information to all handlers.
  1603. * @param {Object.<string, *>} stack
  1604. */
  1605. function notifyHandlers(stack, isWindowError) {
  1606. var exception = null;
  1607. if (isWindowError && !TraceKit.collectWindowErrors) {
  1608. return;
  1609. }
  1610. for (var i in handlers) {
  1611. if (hasKey(handlers, i)) {
  1612. try {
  1613. handlers[i].apply(null, [stack].concat(_slice.call(arguments, 2)));
  1614. } catch (inner) {
  1615. exception = inner;
  1616. }
  1617. }
  1618. }
  1619. if (exception) {
  1620. throw exception;
  1621. }
  1622. }
  1623. var _oldOnerrorHandler, _onErrorHandlerInstalled;
  1624. /**
  1625. * Ensures all global unhandled exceptions are recorded.
  1626. * Supported by Gecko and IE.
  1627. * @param {string} message Error message.
  1628. * @param {string} url URL of script that generated the exception.
  1629. * @param {(number|string)} lineNo The line number at which the error
  1630. * occurred.
  1631. * @param {?(number|string)} colNo The column number at which the error
  1632. * occurred.
  1633. * @param {?Error} ex The actual Error object.
  1634. */
  1635. function traceKitWindowOnError(message, url, lineNo, colNo, ex) {
  1636. var stack = null;
  1637. if (lastExceptionStack) {
  1638. TraceKit.computeStackTrace.augmentStackTraceWithInitialElement(lastExceptionStack, url, lineNo, message);
  1639. processLastException();
  1640. } else if (ex) {
  1641. // New chrome and blink send along a real error object
  1642. // Let's just report that like a normal error.
  1643. // See: https://mikewest.org/2013/08/debugging-runtime-errors-with-window-onerror
  1644. stack = TraceKit.computeStackTrace(ex);
  1645. notifyHandlers(stack, true);
  1646. } else {
  1647. var location = {
  1648. 'url': url,
  1649. 'line': lineNo,
  1650. 'column': colNo
  1651. };
  1652. var name = undefined;
  1653. var msg = message; // must be new var or will modify original `arguments`
  1654. var groups;
  1655. if (isString(message)) {
  1656. var groups = message.match(ERROR_TYPES_RE);
  1657. if (groups) {
  1658. name = groups[1];
  1659. msg = groups[2];
  1660. }
  1661. }
  1662. location.func = UNKNOWN_FUNCTION;
  1663. stack = {
  1664. 'name': name,
  1665. 'message': msg,
  1666. 'url': getLocationHref(),
  1667. 'stack': [location]
  1668. };
  1669. notifyHandlers(stack, true);
  1670. }
  1671. if (_oldOnerrorHandler) {
  1672. return _oldOnerrorHandler.apply(this, arguments);
  1673. }
  1674. return false;
  1675. }
  1676. function installGlobalHandler ()
  1677. {
  1678. if (_onErrorHandlerInstalled) {
  1679. return;
  1680. }
  1681. _oldOnerrorHandler = window.onerror;
  1682. window.onerror = traceKitWindowOnError;
  1683. _onErrorHandlerInstalled = true;
  1684. }
  1685. function uninstallGlobalHandler ()
  1686. {
  1687. if (!_onErrorHandlerInstalled) {
  1688. return;
  1689. }
  1690. window.onerror = _oldOnerrorHandler;
  1691. _onErrorHandlerInstalled = false;
  1692. _oldOnerrorHandler = undefined;
  1693. }
  1694. function processLastException() {
  1695. var _lastExceptionStack = lastExceptionStack,
  1696. _lastArgs = lastArgs;
  1697. lastArgs = null;
  1698. lastExceptionStack = null;
  1699. lastException = null;
  1700. notifyHandlers.apply(null, [_lastExceptionStack, false].concat(_lastArgs));
  1701. }
  1702. /**
  1703. * Reports an unhandled Error to TraceKit.
  1704. * @param {Error} ex
  1705. * @param {?boolean} rethrow If false, do not re-throw the exception.
  1706. * Only used for window.onerror to not cause an infinite loop of
  1707. * rethrowing.
  1708. */
  1709. function report(ex, rethrow) {
  1710. var args = _slice.call(arguments, 1);
  1711. if (lastExceptionStack) {
  1712. if (lastException === ex) {
  1713. return; // already caught by an inner catch block, ignore
  1714. } else {
  1715. processLastException();
  1716. }
  1717. }
  1718. var stack = TraceKit.computeStackTrace(ex);
  1719. lastExceptionStack = stack;
  1720. lastException = ex;
  1721. lastArgs = args;
  1722. // If the stack trace is incomplete, wait for 2 seconds for
  1723. // slow slow IE to see if onerror occurs or not before reporting
  1724. // this exception; otherwise, we will end up with an incomplete
  1725. // stack trace
  1726. window.setTimeout(function () {
  1727. if (lastException === ex) {
  1728. processLastException();
  1729. }
  1730. }, (stack.incomplete ? 2000 : 0));
  1731. if (rethrow !== false) {
  1732. throw ex; // re-throw to propagate to the top level (and cause window.onerror)
  1733. }
  1734. }
  1735. report.subscribe = subscribe;
  1736. report.unsubscribe = unsubscribe;
  1737. report.uninstall = unsubscribeAll;
  1738. return report;
  1739. }());
  1740. /**
  1741. * TraceKit.computeStackTrace: cross-browser stack traces in JavaScript
  1742. *
  1743. * Syntax:
  1744. * s = TraceKit.computeStackTrace(exception) // consider using TraceKit.report instead (see below)
  1745. * Returns:
  1746. * s.name - exception name
  1747. * s.message - exception message
  1748. * s.stack[i].url - JavaScript or HTML file URL
  1749. * s.stack[i].func - function name, or empty for anonymous functions (if guessing did not work)
  1750. * s.stack[i].args - arguments passed to the function, if known
  1751. * s.stack[i].line - line number, if known
  1752. * s.stack[i].column - column number, if known
  1753. *
  1754. * Supports:
  1755. * - Firefox: full stack trace with line numbers and unreliable column
  1756. * number on top frame
  1757. * - Opera 10: full stack trace with line and column numbers
  1758. * - Opera 9-: full stack trace with line numbers
  1759. * - Chrome: full stack trace with line and column numbers
  1760. * - Safari: line and column number for the topmost stacktrace element
  1761. * only
  1762. * - IE: no line numbers whatsoever
  1763. *
  1764. * Tries to guess names of anonymous functions by looking for assignments
  1765. * in the source code. In IE and Safari, we have to guess source file names
  1766. * by searching for function bodies inside all page scripts. This will not
  1767. * work for scripts that are loaded cross-domain.
  1768. * Here be dragons: some function names may be guessed incorrectly, and
  1769. * duplicate functions may be mismatched.
  1770. *
  1771. * TraceKit.computeStackTrace should only be used for tracing purposes.
  1772. * Logging of unhandled exceptions should be done with TraceKit.report,
  1773. * which builds on top of TraceKit.computeStackTrace and provides better
  1774. * IE support by utilizing the window.onerror event to retrieve information
  1775. * about the top of the stack.
  1776. *
  1777. * Note: In IE and Safari, no stack trace is recorded on the Error object,
  1778. * so computeStackTrace instead walks its *own* chain of callers.
  1779. * This means that:
  1780. * * in Safari, some methods may be missing from the stack trace;
  1781. * * in IE, the topmost function in the stack trace will always be the
  1782. * caller of computeStackTrace.
  1783. *
  1784. * This is okay for tracing (because you are likely to be calling
  1785. * computeStackTrace from the function you want to be the topmost element
  1786. * of the stack trace anyway), but not okay for logging unhandled
  1787. * exceptions (because your catch block will likely be far away from the
  1788. * inner function that actually caused the exception).
  1789. *
  1790. */
  1791. TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
  1792. /**
  1793. * Escapes special characters, except for whitespace, in a string to be
  1794. * used inside a regular expression as a string literal.
  1795. * @param {string} text The string.
  1796. * @return {string} The escaped string literal.
  1797. */
  1798. function escapeRegExp(text) {
  1799. return text.replace(/[\-\[\]{}()*+?.,\\\^$|#]/g, '\\$&');
  1800. }
  1801. /**
  1802. * Escapes special characters in a string to be used inside a regular
  1803. * expression as a string literal. Also ensures that HTML entities will
  1804. * be matched the same as their literal friends.
  1805. * @param {string} body The string.
  1806. * @return {string} The escaped string.
  1807. */
  1808. function escapeCodeAsRegExpForMatchingInsideHTML(body) {
  1809. return escapeRegExp(body).replace('<', '(?:<|&lt;)').replace('>', '(?:>|&gt;)').replace('&', '(?:&|&amp;)').replace('"', '(?:"|&quot;)').replace(/\s+/g, '\\s+');
  1810. }
  1811. // Contents of Exception in various browsers.
  1812. //
  1813. // SAFARI:
  1814. // ex.message = Can't find variable: qq
  1815. // ex.line = 59
  1816. // ex.sourceId = 580238192
  1817. // ex.sourceURL = http://...
  1818. // ex.expressionBeginOffset = 96
  1819. // ex.expressionCaretOffset = 98
  1820. // ex.expressionEndOffset = 98
  1821. // ex.name = ReferenceError
  1822. //
  1823. // FIREFOX:
  1824. // ex.message = qq is not defined
  1825. // ex.fileName = http://...
  1826. // ex.lineNumber = 59
  1827. // ex.columnNumber = 69
  1828. // ex.stack = ...stack trace... (see the example below)
  1829. // ex.name = ReferenceError
  1830. //
  1831. // CHROME:
  1832. // ex.message = qq is not defined
  1833. // ex.name = ReferenceError
  1834. // ex.type = not_defined
  1835. // ex.arguments = ['aa']
  1836. // ex.stack = ...stack trace...
  1837. //
  1838. // INTERNET EXPLORER:
  1839. // ex.message = ...
  1840. // ex.name = ReferenceError
  1841. //
  1842. // OPERA:
  1843. // ex.message = ...message... (see the example below)
  1844. // ex.name = ReferenceError
  1845. // ex.opera#sourceloc = 11 (pretty much useless, duplicates the info in ex.message)
  1846. // ex.stacktrace = n/a; see 'opera:config#UserPrefs|Exceptions Have Stacktrace'
  1847. /**
  1848. * Computes stack trace information from the stack property.
  1849. * Chrome and Gecko use this property.
  1850. * @param {Error} ex
  1851. * @return {?Object.<string, *>} Stack trace information.
  1852. */
  1853. function computeStackTraceFromStackProp(ex) {
  1854. if (isUndefined(ex.stack) || !ex.stack) return;
  1855. var chrome = /^\s*at (.*?) ?\(((?:file|https?|blob|chrome-extension|native|eval|<anonymous>).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i,
  1856. gecko = /^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|\[native).*?)(?::(\d+))?(?::(\d+))?\s*$/i,
  1857. winjs = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx|https?|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i,
  1858. lines = ex.stack.split('\n'),
  1859. stack = [],
  1860. parts,
  1861. element,
  1862. reference = /^(.*) is undefined$/.exec(ex.message);
  1863. for (var i = 0, j = lines.length; i < j; ++i) {
  1864. if ((parts = chrome.exec(lines[i]))) {
  1865. var isNative = parts[2] && parts[2].indexOf('native') !== -1;
  1866. element = {
  1867. 'url': !isNative ? parts[2] : null,
  1868. 'func': parts[1] || UNKNOWN_FUNCTION,
  1869. 'args': isNative ? [parts[2]] : [],
  1870. 'line': parts[3] ? +parts[3] : null,
  1871. 'column': parts[4] ? +parts[4] : null
  1872. };
  1873. } else if ( parts = winjs.exec(lines[i]) ) {
  1874. element = {
  1875. 'url': parts[2],
  1876. 'func': parts[1] || UNKNOWN_FUNCTION,
  1877. 'args': [],
  1878. 'line': +parts[3],
  1879. 'column': parts[4] ? +parts[4] : null
  1880. };
  1881. } else if ((parts = gecko.exec(lines[i]))) {
  1882. element = {
  1883. 'url': parts[3],
  1884. 'func': parts[1] || UNKNOWN_FUNCTION,
  1885. 'args': parts[2] ? parts[2].split(',') : [],
  1886. 'line': parts[4] ? +parts[4] : null,
  1887. 'column': parts[5] ? +parts[5] : null
  1888. };
  1889. } else {
  1890. continue;
  1891. }
  1892. if (!element.func && element.line) {
  1893. element.func = UNKNOWN_FUNCTION;
  1894. }
  1895. stack.push(element);
  1896. }
  1897. if (!stack.length) {
  1898. return null;
  1899. }
  1900. if (!stack[0].column && !isUndefined(ex.columnNumber)) {
  1901. // FireFox uses this awesome columnNumber property for its top frame
  1902. // Also note, Firefox's column number is 0-based and everything else expects 1-based,
  1903. // so adding 1
  1904. stack[0].column = ex.columnNumber + 1;
  1905. }
  1906. return {
  1907. 'name': ex.name,
  1908. 'message': ex.message,
  1909. 'url': getLocationHref(),
  1910. 'stack': stack
  1911. };
  1912. }
  1913. /**
  1914. * Computes stack trace information from the stacktrace property.
  1915. * Opera 10 uses this property.
  1916. * @param {Error} ex
  1917. * @return {?Object.<string, *>} Stack trace information.
  1918. */
  1919. function computeStackTraceFromStacktraceProp(ex) {
  1920. // Access and store the stacktrace property before doing ANYTHING
  1921. // else to it because Opera is not very good at providing it
  1922. // reliably in other circumstances.
  1923. var stacktrace = ex.stacktrace;
  1924. if (isUndefined(ex.stacktrace) || !ex.stacktrace) return;
  1925. var opera10Regex = / line (\d+).*script (?:in )?(\S+)(?:: in function (\S+))?$/i,
  1926. opera11Regex = / line (\d+), column (\d+)\s*(?:in (?:<anonymous function: ([^>]+)>|([^\)]+))\((.*)\))? in (.*):\s*$/i,
  1927. lines = stacktrace.split('\n'),
  1928. stack = [],
  1929. parts;
  1930. for (var line = 0; line < lines.length; line += 2) {
  1931. var element = null;
  1932. if ((parts = opera10Regex.exec(lines[line]))) {
  1933. element = {
  1934. 'url': parts[2],
  1935. 'line': +parts[1],
  1936. 'column': null,
  1937. 'func': parts[3],
  1938. 'args':[]
  1939. };
  1940. } else if ((parts = opera11Regex.exec(lines[line]))) {
  1941. element = {
  1942. 'url': parts[6],
  1943. 'line': +parts[1],
  1944. 'column': +parts[2],
  1945. 'func': parts[3] || parts[4],
  1946. 'args': parts[5] ? parts[5].split(',') : []
  1947. };
  1948. }
  1949. if (element) {
  1950. if (!element.func && element.line) {
  1951. element.func = UNKNOWN_FUNCTION;
  1952. }
  1953. stack.push(element);
  1954. }
  1955. }
  1956. if (!stack.length) {
  1957. return null;
  1958. }
  1959. return {
  1960. 'name': ex.name,
  1961. 'message': ex.message,
  1962. 'url': getLocationHref(),
  1963. 'stack': stack
  1964. };
  1965. }
  1966. /**
  1967. * NOT TESTED.
  1968. * Computes stack trace information from an error message that includes
  1969. * the stack trace.
  1970. * Opera 9 and earlier use this method if the option to show stack
  1971. * traces is turned on in opera:config.
  1972. * @param {Error} ex
  1973. * @return {?Object.<string, *>} Stack information.
  1974. */
  1975. function computeStackTraceFromOperaMultiLineMessage(ex) {
  1976. // Opera includes a stack trace into the exception message. An example is:
  1977. //
  1978. // Statement on line 3: Undefined variable: undefinedFunc
  1979. // Backtrace:
  1980. // Line 3 of linked script file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.js: In function zzz
  1981. // undefinedFunc(a);
  1982. // Line 7 of inline#1 script in file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.html: In function yyy
  1983. // zzz(x, y, z);
  1984. // Line 3 of inline#1 script in file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.html: In function xxx
  1985. // yyy(a, a, a);
  1986. // Line 1 of function script
  1987. // try { xxx('hi'); return false; } catch(ex) { TraceKit.report(ex); }
  1988. // ...
  1989. var lines = ex.message.split('\n');
  1990. if (lines.length < 4) {
  1991. return null;
  1992. }
  1993. var lineRE1 = /^\s*Line (\d+) of linked script ((?:file|https?|blob)\S+)(?:: in function (\S+))?\s*$/i,
  1994. lineRE2 = /^\s*Line (\d+) of inline#(\d+) script in ((?:file|https?|blob)\S+)(?:: in function (\S+))?\s*$/i,
  1995. lineRE3 = /^\s*Line (\d+) of function script\s*$/i,
  1996. stack = [],
  1997. scripts = document.getElementsByTagName('script'),
  1998. parts;
  1999. for (var line = 2; line < lines.length; line += 2) {
  2000. var item = null;
  2001. if ((parts = lineRE1.exec(lines[line]))) {
  2002. item = {
  2003. 'url': parts[2],
  2004. 'func': parts[3],
  2005. 'args': [],
  2006. 'line': +parts[1],
  2007. 'column': null
  2008. };
  2009. } else if ((parts = lineRE2.exec(lines[line]))) {
  2010. item = {
  2011. 'url': parts[3],
  2012. 'func': parts[4],
  2013. 'args': [],
  2014. 'line': +parts[1],
  2015. 'column': null // TODO: Check to see if inline#1 (+parts[2]) points to the script number or column number.
  2016. };
  2017. var relativeLine = (+parts[1]); // relative to the start of the <SCRIPT> block
  2018. } else if ((parts = lineRE3.exec(lines[line]))) {
  2019. var url = window.location.href.replace(/#.*$/, '');
  2020. item = {
  2021. 'url': url,
  2022. 'func': '',
  2023. 'args': [],
  2024. 'line': parts[1],
  2025. 'column': null
  2026. };
  2027. }
  2028. if (item) {
  2029. if (!item.func) {
  2030. item.func = UNKNOWN_FUNCTION;
  2031. }
  2032. stack.push(item);
  2033. }
  2034. }
  2035. if (!stack.length) {
  2036. return null; // could not parse multiline exception message as Opera stack trace
  2037. }
  2038. return {
  2039. 'name': ex.name,
  2040. 'message': lines[0],
  2041. 'url': getLocationHref(),
  2042. 'stack': stack
  2043. };
  2044. }
  2045. /**
  2046. * Adds information about the first frame to incomplete stack traces.
  2047. * Safari and IE require this to get complete data on the first frame.
  2048. * @param {Object.<string, *>} stackInfo Stack trace information from
  2049. * one of the compute* methods.
  2050. * @param {string} url The URL of the script that caused an error.
  2051. * @param {(number|string)} lineNo The line number of the script that
  2052. * caused an error.
  2053. * @param {string=} message The error generated by the browser, which
  2054. * hopefully contains the name of the object that caused the error.
  2055. * @return {boolean} Whether or not the stack information was
  2056. * augmented.
  2057. */
  2058. function augmentStackTraceWithInitialElement(stackInfo, url, lineNo, message) {
  2059. var initial = {
  2060. 'url': url,
  2061. 'line': lineNo
  2062. };
  2063. if (initial.url && initial.line) {
  2064. stackInfo.incomplete = false;
  2065. if (!initial.func) {
  2066. initial.func = UNKNOWN_FUNCTION;
  2067. }
  2068. if (stackInfo.stack.length > 0) {
  2069. if (stackInfo.stack[0].url === initial.url) {
  2070. if (stackInfo.stack[0].line === initial.line) {
  2071. return false; // already in stack trace
  2072. } else if (!stackInfo.stack[0].line && stackInfo.stack[0].func === initial.func) {
  2073. stackInfo.stack[0].line = initial.line;
  2074. return false;
  2075. }
  2076. }
  2077. }
  2078. stackInfo.stack.unshift(initial);
  2079. stackInfo.partial = true;
  2080. return true;
  2081. } else {
  2082. stackInfo.incomplete = true;
  2083. }
  2084. return false;
  2085. }
  2086. /**
  2087. * Computes stack trace information by walking the arguments.caller
  2088. * chain at the time the exception occurred. This will cause earlier
  2089. * frames to be missed but is the only way to get any stack trace in
  2090. * Safari and IE. The top frame is restored by
  2091. * {@link augmentStackTraceWithInitialElement}.
  2092. * @param {Error} ex
  2093. * @return {?Object.<string, *>} Stack trace information.
  2094. */
  2095. function computeStackTraceByWalkingCallerChain(ex, depth) {
  2096. var functionName = /function\s+([_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*)?\s*\(/i,
  2097. stack = [],
  2098. funcs = {},
  2099. recursion = false,
  2100. parts,
  2101. item,
  2102. source;
  2103. for (var curr = computeStackTraceByWalkingCallerChain.caller; curr && !recursion; curr = curr.caller) {
  2104. if (curr === computeStackTrace || curr === TraceKit.report) {
  2105. // console.log('skipping internal function');
  2106. continue;
  2107. }
  2108. item = {
  2109. 'url': null,
  2110. 'func': UNKNOWN_FUNCTION,
  2111. 'line': null,
  2112. 'column': null
  2113. };
  2114. if (curr.name) {
  2115. item.func = curr.name;
  2116. } else if ((parts = functionName.exec(curr.toString()))) {
  2117. item.func = parts[1];
  2118. }
  2119. if (typeof item.func === 'undefined') {
  2120. try {
  2121. item.func = parts.input.substring(0, parts.input.indexOf('{'));
  2122. } catch (e) { }
  2123. }
  2124. if (funcs['' + curr]) {
  2125. recursion = true;
  2126. }else{
  2127. funcs['' + curr] = true;
  2128. }
  2129. stack.push(item);
  2130. }
  2131. if (depth) {
  2132. // console.log('depth is ' + depth);
  2133. // console.log('stack is ' + stack.length);
  2134. stack.splice(0, depth);
  2135. }
  2136. var result = {
  2137. 'name': ex.name,
  2138. 'message': ex.message,
  2139. 'url': getLocationHref(),
  2140. 'stack': stack
  2141. };
  2142. augmentStackTraceWithInitialElement(result, ex.sourceURL || ex.fileName, ex.line || ex.lineNumber, ex.message || ex.description);
  2143. return result;
  2144. }
  2145. /**
  2146. * Computes a stack trace for an exception.
  2147. * @param {Error} ex
  2148. * @param {(string|number)=} depth
  2149. */
  2150. function computeStackTrace(ex, depth) {
  2151. var stack = null;
  2152. depth = (depth == null ? 0 : +depth);
  2153. try {
  2154. // This must be tried first because Opera 10 *destroys*
  2155. // its stacktrace property if you try to access the stack
  2156. // property first!!
  2157. stack = computeStackTraceFromStacktraceProp(ex);
  2158. if (stack) {
  2159. return stack;
  2160. }
  2161. } catch (e) {
  2162. if (TraceKit.debug) {
  2163. throw e;
  2164. }
  2165. }
  2166. try {
  2167. stack = computeStackTraceFromStackProp(ex);
  2168. if (stack) {
  2169. return stack;
  2170. }
  2171. } catch (e) {
  2172. if (TraceKit.debug) {
  2173. throw e;
  2174. }
  2175. }
  2176. try {
  2177. stack = computeStackTraceFromOperaMultiLineMessage(ex);
  2178. if (stack) {
  2179. return stack;
  2180. }
  2181. } catch (e) {
  2182. if (TraceKit.debug) {
  2183. throw e;
  2184. }
  2185. }
  2186. try {
  2187. stack = computeStackTraceByWalkingCallerChain(ex, depth + 1);
  2188. if (stack) {
  2189. return stack;
  2190. }
  2191. } catch (e) {
  2192. if (TraceKit.debug) {
  2193. throw e;
  2194. }
  2195. }
  2196. return {
  2197. 'name': ex.name,
  2198. 'message': ex.message,
  2199. 'url': getLocationHref()
  2200. };
  2201. }
  2202. computeStackTrace.augmentStackTraceWithInitialElement = augmentStackTraceWithInitialElement;
  2203. computeStackTrace.computeStackTraceFromStackProp = computeStackTraceFromStackProp;
  2204. return computeStackTrace;
  2205. }());
  2206. module.exports = TraceKit;
  2207. },{"6":6}]},{},[5])(5)
  2208. });