raven.js 80 KB

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