raven.js 64 KB

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