Unified · Split
| @@ -1,4 +1,4 @@ | ||
| 1 | −/*! Raven.js 1.1.18 (8ad15bc) | github.com/getsentry/raven-js */ | |
| 1 | +/*! Raven.js 1.3.0 (768fdca) | github.com/getsentry/raven-js */ | |
| 2 | 2 | |
| 3 | 3 | /* |
| 4 | 4 | * Includes TraceKit |
| @@ -21,7 +21,8 @@ var TraceKit = { · 16 unmodified lines | ||
| 21 | 21 | remoteFetching: false, |
| 22 | 22 | collectWindowErrors: true, |
| 23 | 23 | // 3 lines before, the offending line, 3 lines after |
| 24 | − linesOfContext: 7 | |
| 24 | + linesOfContext: 7, | |
| 25 | + debug: false | |
| 25 | 26 | }; |
| 26 | 27 | |
| 27 | 28 | // global reference to slice |
| @@ -29,23 +30,11 @@ var _slice = [].slice; · 1 unmodified line | ||
| 29 | 30 | var UNKNOWN_FUNCTION = '?'; |
| 30 | 31 | |
| 31 | 32 | |
| 32 | −/** | |
| 33 | − * TraceKit.wrap: Wrap any function in a TraceKit reporter | |
| 34 | − * Example: func = TraceKit.wrap(func); | |
| 35 | − * | |
| 36 | − * @param {Function} func Function to be wrapped | |
| 37 | − * @return {Function} The wrapped func | |
| 38 | − */ | |
| 39 | −TraceKit.wrap = function traceKitWrapper(func) { | |
| 40 | − function wrapped() { | |
| 41 | − try { | |
| 42 | − return func.apply(this, arguments); | |
| 43 | − } catch (e) { | |
| 44 | − TraceKit.report(e); | |
| 45 | − throw e; | |
| 46 | − } | |
| 47 | − } | |
| 48 | − return wrapped; | |
| 33 | +function getLocationHref() { | |
| 34 | + if (typeof document === 'undefined') | |
| 35 | + return ''; | |
| 36 | + | |
| 37 | + return document.location.href; | |
| 49 | 38 | }; |
| 50 | 39 | |
| 51 | 40 | /** |
| @@ -181,7 +170,7 @@ TraceKit.report = (function reportModuleWrapper() { · 129 unmodified lines | ||
| 181 | 170 | location.context = TraceKit.computeStackTrace.gatherContext(location.url, location.line); |
| 182 | 171 | stack = { |
| 183 | 172 | 'message': message, |
| 184 | − 'url': document.location.href, | |
| 173 | + 'url': getLocationHref(), | |
| 185 | 174 | 'stack': [location] |
| 186 | 175 | }; |
| 187 | 176 | notifyHandlers(stack, true); |
| @@ -319,8 +308,7 @@ TraceKit.report = (function reportModuleWrapper() { · 131 unmodified lines | ||
| 319 | 308 | * |
| 320 | 309 | */ |
| 321 | 310 | TraceKit.computeStackTrace = (function computeStackTraceWrapper() { |
| 322 | − var debug = false, | |
| 323 | − sourceCache = {}; | |
| 311 | + var sourceCache = {}; | |
| 324 | 312 | |
| 325 | 313 | /** |
| 326 | 314 | * Attempts to retrieve source code via XMLHttpRequest, which is used |
| @@ -362,7 +350,9 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { · 35 unmodified lines | ||
| 362 | 350 | // URL needs to be able to fetched within the acceptable domain. Otherwise, |
| 363 | 351 | // cross-domain errors will be triggered. |
| 364 | 352 | var source = ''; |
| 365 | − if (url.indexOf(document.domain) !== -1) { | |
| 353 | + var domain = ''; | |
| 354 | + try { domain = document.domain; } catch (e) {} | |
| 355 | + if (url.indexOf(domain) !== -1) { | |
| 366 | 356 | source = loadSource(url); |
| 367 | 357 | } |
| 368 | 358 | sourceCache[url] = source ? source.split('\n') : []; |
| @@ -524,6 +514,9 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { · 155 unmodified lines | ||
| 524 | 514 | * the url, line, and column number of the defined function. |
| 525 | 515 | */ |
| 526 | 516 | function findSourceByFunctionBody(func) { |
| 517 | + if (typeof document === 'undefined') | |
| 518 | + return; | |
| 519 | + | |
| 527 | 520 | var urls = [window.location.href], |
| 528 | 521 | scripts = document.getElementsByTagName('script'), |
| 529 | 522 | body, |
| @@ -627,12 +620,11 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { · 97 unmodified lines | ||
| 627 | 620 | * @return {?Object.<string, *>} Stack trace information. |
| 628 | 621 | */ |
| 629 | 622 | function computeStackTraceFromStackProp(ex) { |
| 630 | − if (!ex.stack) { | |
| 631 | − return null; | |
| 632 | − } | |
| 623 | + if (isUndefined(ex.stack) || !ex.stack) return; | |
| 633 | 624 | |
| 634 | − var chrome = /^\s*at (.*?) ?\(?((?:file|https?|chrome-extension):.*?):(\d+)(?::(\d+))?\)?\s*$/i, | |
| 625 | + var chrome = /^\s*at (.*?) ?\(?((?:(?:file|https?|chrome-extension):.*?)|<anonymous>):(\d+)(?::(\d+))?\)?\s*$/i, | |
| 635 | 626 | gecko = /^\s*(.*?)(?:\((.*?)\))?@((?:file|https?|chrome).*?):(\d+)(?::(\d+))?\s*$/i, |
| 627 | + winjs = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:ms-appx|http|https):.*?):(\d+)(?::(\d+))?\)?\s*$/i, | |
| 636 | 628 | lines = ex.stack.split('\n'), |
| 637 | 629 | stack = [], |
| 638 | 630 | parts, |
| @@ -655,6 +647,13 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { · 16 unmodified lines | ||
| 655 | 647 | 'line': +parts[3], |
| 656 | 648 | 'column': parts[4] ? +parts[4] : null |
| 657 | 649 | }; |
| 650 | + } else if ((parts = winjs.exec(lines[i]))) { | |
| 651 | + element = { | |
| 652 | + 'url': parts[2], | |
| 653 | + 'func': parts[1] || UNKNOWN_FUNCTION, | |
| 654 | + 'line': +parts[3], | |
| 655 | + 'column': parts[4] ? +parts[4] : null | |
| 656 | + }; | |
| 658 | 657 | } else { |
| 659 | 658 | continue; |
| 660 | 659 | } |
| @@ -686,7 +685,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { · 25 unmodified lines | ||
| 686 | 685 | return { |
| 687 | 686 | 'name': ex.name, |
| 688 | 687 | 'message': ex.message, |
| 689 | − 'url': document.location.href, | |
| 688 | + 'url': getLocationHref(), | |
| 690 | 689 | 'stack': stack |
| 691 | 690 | }; |
| 692 | 691 | } |
| @@ -702,6 +701,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { · 9 unmodified lines | ||
| 702 | 701 | // else to it because Opera is not very good at providing it |
| 703 | 702 | // reliably in other circumstances. |
| 704 | 703 | var stacktrace = ex.stacktrace; |
| 704 | + if (isUndefined(ex.stacktrace) || !ex.stacktrace) return; | |
| 705 | 705 | |
| 706 | 706 | var testRE = / line (\d+), column (\d+) in (?:<anonymous function: ([^>]+)>|([^\)]+))\((.*)\) in (.*):\s*$/i, |
| 707 | 707 | lines = stacktrace.split('\n'), |
| @@ -742,7 +742,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { · 34 unmodified lines | ||
| 742 | 742 | return { |
| 743 | 743 | 'name': ex.name, |
| 744 | 744 | 'message': ex.message, |
| 745 | − 'url': document.location.href, | |
| 745 | + 'url': getLocationHref(), | |
| 746 | 746 | 'stack': stack |
| 747 | 747 | }; |
| 748 | 748 | } |
| @@ -852,7 +852,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { · 103 unmodified lines | ||
| 852 | 852 | return { |
| 853 | 853 | 'name': ex.name, |
| 854 | 854 | 'message': lines[0], |
| 855 | − 'url': document.location.href, | |
| 855 | + 'url': getLocationHref(), | |
| 856 | 856 | 'stack': stack |
| 857 | 857 | }; |
| 858 | 858 | } |
| @@ -951,6 +951,12 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { · 92 unmodified lines | ||
| 951 | 951 | item.func = parts[1]; |
| 952 | 952 | } |
| 953 | 953 | |
| 954 | + if (typeof item.func === 'undefined') { | |
| 955 | + try { | |
| 956 | + item.func = parts.input.substring(0, parts.input.indexOf('{')); | |
| 957 | + } catch (e) { } | |
| 958 | + } | |
| 959 | + | |
| 954 | 960 | if ((source = findSourceByFunctionBody(curr))) { |
| 955 | 961 | item.url = source.url; |
| 956 | 962 | item.line = source.line; |
| @@ -983,7 +989,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { · 26 unmodified lines | ||
| 983 | 989 | var result = { |
| 984 | 990 | 'name': ex.name, |
| 985 | 991 | 'message': ex.message, |
| 986 | − 'url': document.location.href, | |
| 992 | + 'url': getLocationHref(), | |
| 987 | 993 | 'stack': stack |
| 988 | 994 | }; |
| 989 | 995 | augmentStackTraceWithInitialElement(result, ex.sourceURL || ex.fileName, ex.line || ex.lineNumber, ex.message || ex.description); |
| @@ -1008,7 +1014,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { · 18 unmodified lines | ||
| 1008 | 1014 | return stack; |
| 1009 | 1015 | } |
| 1010 | 1016 | } catch (e) { |
| 1011 | − if (debug) { | |
| 1017 | + if (TraceKit.debug) { | |
| 1012 | 1018 | throw e; |
| 1013 | 1019 | } |
| 1014 | 1020 | } |
| @@ -1019,7 +1025,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { · 4 unmodified lines | ||
| 1019 | 1025 | return stack; |
| 1020 | 1026 | } |
| 1021 | 1027 | } catch (e) { |
| 1022 | − if (debug) { | |
| 1028 | + if (TraceKit.debug) { | |
| 1023 | 1029 | throw e; |
| 1024 | 1030 | } |
| 1025 | 1031 | } |
| @@ -1030,7 +1036,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { · 4 unmodified lines | ||
| 1030 | 1036 | return stack; |
| 1031 | 1037 | } |
| 1032 | 1038 | } catch (e) { |
| 1033 | − if (debug) { | |
| 1039 | + if (TraceKit.debug) { | |
| 1034 | 1040 | throw e; |
| 1035 | 1041 | } |
| 1036 | 1042 | } |
| @@ -1041,12 +1047,16 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { · 4 unmodified lines | ||
| 1041 | 1047 | return stack; |
| 1042 | 1048 | } |
| 1043 | 1049 | } catch (e) { |
| 1044 | − if (debug) { | |
| 1050 | + if (TraceKit.debug) { | |
| 1045 | 1051 | throw e; |
| 1046 | 1052 | } |
| 1047 | 1053 | } |
| 1048 | 1054 | |
| 1049 | − return {}; | |
| 1055 | + return { | |
| 1056 | + 'name': ex.name, | |
| 1057 | + 'message': ex.message, | |
| 1058 | + 'url': getLocationHref() | |
| 1059 | + }; | |
| 1050 | 1060 | } |
| 1051 | 1061 | |
| 1052 | 1062 | computeStackTrace.augmentStackTraceWithInitialElement = augmentStackTraceWithInitialElement; |
| @@ -1064,38 +1074,45 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { · 11 unmodified lines | ||
| 1064 | 1074 | // since JSON is required to encode the payload |
| 1065 | 1075 | var _Raven = window.Raven, |
| 1066 | 1076 | hasJSON = !!(typeof JSON === 'object' && JSON.stringify), |
| 1077 | + // Raven can run in contexts where there's no document (react-native) | |
| 1078 | + hasDocument = typeof document !== 'undefined', | |
| 1067 | 1079 | lastCapturedException, |
| 1068 | 1080 | lastEventId, |
| 1069 | 1081 | globalServer, |
| 1070 | − globalUser, | |
| 1071 | 1082 | globalKey, |
| 1072 | 1083 | globalProject, |
| 1084 | + globalContext = {}, | |
| 1073 | 1085 | globalOptions = { |
| 1074 | 1086 | logger: 'javascript', |
| 1075 | 1087 | ignoreErrors: [], |
| 1076 | 1088 | ignoreUrls: [], |
| 1077 | 1089 | whitelistUrls: [], |
| 1078 | 1090 | includePaths: [], |
| 1091 | + crossOrigin: 'anonymous', | |
| 1079 | 1092 | collectWindowErrors: true, |
| 1080 | − tags: {}, | |
| 1081 | − maxMessageLength: 100, | |
| 1082 | − extra: {} | |
| 1093 | + maxMessageLength: 100 | |
| 1083 | 1094 | }, |
| 1084 | − authQueryString, | |
| 1085 | 1095 | isRavenInstalled = false, |
| 1086 | − | |
| 1087 | 1096 | objectPrototype = Object.prototype, |
| 1097 | + // capture references to window.console *and* all its methods first | |
| 1098 | + // before the console plugin has a chance to monkey patch | |
| 1099 | + originalConsole = window.console || {}, | |
| 1100 | + originalConsoleMethods = {}, | |
| 1101 | + plugins = [], | |
| 1088 | 1102 | startTime = now(); |
| 1089 | 1103 | |
| 1104 | +for (var method in originalConsole) { | |
| 1105 | + originalConsoleMethods[method] = originalConsole[method]; | |
| 1106 | +} | |
| 1090 | 1107 | /* |
| 1091 | 1108 | * The core Raven singleton |
| 1092 | 1109 | * |
| 1093 | 1110 | * @this {Raven} |
| 1094 | 1111 | */ |
| 1095 | 1112 | var Raven = { |
| 1096 | − VERSION: '1.1.18', | |
| 1113 | + VERSION: '1.3.0', | |
| 1097 | 1114 | |
| 1098 | − debug: true, | |
| 1115 | + debug: false, | |
| 1099 | 1116 | |
| 1100 | 1117 | /* |
| 1101 | 1118 | * Allow multiple versions of Raven to be installed. |
| @@ -1129,7 +1146,12 @@ var Raven = { · 27 unmodified lines | ||
| 1129 | 1146 | // merge in options |
| 1130 | 1147 | if (options) { |
| 1131 | 1148 | each(options, function(key, value){ |
| 1132 | − globalOptions[key] = value; | |
| 1149 | + // tags and extra are special and need to be put into context | |
| 1150 | + if (key == 'tags' || key == 'extra') { | |
| 1151 | + globalContext[key] = value; | |
| 1152 | + } else { | |
| 1153 | + globalOptions[key] = value; | |
| 1154 | + } | |
| 1133 | 1155 | }); |
| 1134 | 1156 | } |
| 1135 | 1157 | |
| @@ -1166,8 +1188,6 @@ var Raven = { · 30 unmodified lines | ||
| 1166 | 1188 | |
| 1167 | 1189 | TraceKit.collectWindowErrors = !!globalOptions.collectWindowErrors; |
| 1168 | 1190 | |
| 1169 | − setAuthQueryString(); | |
| 1170 | − | |
| 1171 | 1191 | // return for chaining |
| 1172 | 1192 | return Raven; |
| 1173 | 1193 | }, |
| @@ -1183,6 +1203,12 @@ var Raven = { · 9 unmodified lines | ||
| 1183 | 1203 | install: function() { |
| 1184 | 1204 | if (isSetup() && !isRavenInstalled) { |
| 1185 | 1205 | TraceKit.report.subscribe(handleStackInfo); |
| 1206 | + | |
| 1207 | + // Install all of the plugins | |
| 1208 | + each(plugins, function(_, plugin) { | |
| 1209 | + plugin(); | |
| 1210 | + }); | |
| 1211 | + | |
| 1186 | 1212 | isRavenInstalled = true; |
| 1187 | 1213 | } |
| 1188 | 1214 | |
| @@ -1261,6 +1287,7 @@ var Raven = { · 72 unmodified lines | ||
| 1261 | 1287 | wrapped[property] = func[property]; |
| 1262 | 1288 | } |
| 1263 | 1289 | } |
| 1290 | + wrapped.prototype = func.prototype; | |
| 1264 | 1291 | |
| 1265 | 1292 | // Signal that this function has been wrapped already |
| 1266 | 1293 | // for both debugging and to prevent it to being wrapped twice |
| @@ -1302,7 +1329,8 @@ var Raven = { · 35 unmodified lines | ||
| 1302 | 1329 | // raises an exception different from the one we asked to |
| 1303 | 1330 | // report on. |
| 1304 | 1331 | try { |
| 1305 | − TraceKit.report(ex, options); | |
| 1332 | + var stack = TraceKit.computeStackTrace(ex); | |
| 1333 | + handleStackInfo(stack, options); | |
| 1306 | 1334 | } catch(ex1) { |
| 1307 | 1335 | if(ex !== ex1) { |
| 1308 | 1336 | throw ex1; |
| @@ -1337,6 +1365,12 @@ var Raven = { · 28 unmodified lines | ||
| 1337 | 1365 | return Raven; |
| 1338 | 1366 | }, |
| 1339 | 1367 | |
| 1368 | + addPlugin: function(plugin) { | |
| 1369 | + plugins.push(plugin); | |
| 1370 | + if (isRavenInstalled) plugin(); | |
| 1371 | + return Raven; | |
| 1372 | + }, | |
| 1373 | + | |
| 1340 | 1374 | /* |
| 1341 | 1375 | * Set/clear a user to be sent along with the payload. |
| 1342 | 1376 | * |
| @@ -1344,47 +1378,110 @@ var Raven = { · 1 unmodified line | ||
| 1344 | 1378 | * @return {Raven} |
| 1345 | 1379 | */ |
| 1346 | 1380 | setUserContext: function(user) { |
| 1347 | − globalUser = user; | |
| 1381 | + // Intentionally do not merge here since that's an unexpected behavior. | |
| 1382 | + globalContext.user = user; | |
| 1348 | 1383 | |
| 1349 | 1384 | return Raven; |
| 1350 | 1385 | }, |
| 1351 | 1386 | |
| 1352 | 1387 | /* |
| 1353 | − * Set extra attributes to be sent along with the payload. | |
| 1388 | + * Merge extra attributes to be sent along with the payload. | |
| 1354 | 1389 | * |
| 1355 | 1390 | * @param {object} extra An object representing extra data [optional] |
| 1356 | 1391 | * @return {Raven} |
| 1357 | 1392 | */ |
| 1358 | 1393 | setExtraContext: function(extra) { |
| 1359 | − globalOptions.extra = extra || {}; | |
| 1394 | + mergeContext('extra', extra); | |
| 1360 | 1395 | |
| 1361 | 1396 | return Raven; |
| 1362 | 1397 | }, |
| 1363 | 1398 | |
| 1364 | 1399 | /* |
| 1365 | − * Set tags to be sent along with the payload. | |
| 1400 | + * Merge tags to be sent along with the payload. | |
| 1366 | 1401 | * |
| 1367 | 1402 | * @param {object} tags An object representing tags [optional] |
| 1368 | 1403 | * @return {Raven} |
| 1369 | 1404 | */ |
| 1370 | 1405 | setTagsContext: function(tags) { |
| 1371 | − globalOptions.tags = tags || {}; | |
| 1406 | + mergeContext('tags', tags); | |
| 1372 | 1407 | |
| 1373 | 1408 | return Raven; |
| 1374 | 1409 | }, |
| 1375 | 1410 | |
| 1411 | + /* | |
| 1412 | + * Clear all of the context. | |
| 1413 | + * | |
| 1414 | + * @return {Raven} | |
| 1415 | + */ | |
| 1416 | + clearContext: function() { | |
| 1417 | + globalContext = {}; | |
| 1418 | + | |
| 1419 | + return Raven; | |
| 1420 | + }, | |
| 1421 | + | |
| 1422 | + /* | |
| 1423 | + * Get a copy of the current context. This cannot be mutated. | |
| 1424 | + * | |
| 1425 | + * @return {object} copy of context | |
| 1426 | + */ | |
| 1427 | + getContext: function() { | |
| 1428 | + // lol javascript | |
| 1429 | + return JSON.parse(JSON.stringify(globalContext)); | |
| 1430 | + }, | |
| 1431 | + | |
| 1376 | 1432 | /* |
| 1377 | 1433 | * Set release version of application |
| 1378 | 1434 | * |
| 1379 | 1435 | * @param {string} release Typically something like a git SHA to identify version |
| 1380 | 1436 | * @return {Raven} |
| 1381 | 1437 | */ |
| 1382 | − setReleaseContext: function(release) { | |
| 1438 | + setRelease: function(release) { | |
| 1383 | 1439 | globalOptions.release = release; |
| 1384 | 1440 | |
| 1385 | 1441 | return Raven; |
| 1386 | 1442 | }, |
| 1387 | 1443 | |
| 1444 | + /* | |
| 1445 | + * Set the dataCallback option | |
| 1446 | + * | |
| 1447 | + * @param {function} callback The callback to run which allows the | |
| 1448 | + * data blob to be mutated before sending | |
| 1449 | + * @return {Raven} | |
| 1450 | + */ | |
| 1451 | + setDataCallback: function(callback) { | |
| 1452 | + globalOptions.dataCallback = callback; | |
| 1453 | + | |
| 1454 | + return Raven; | |
| 1455 | + }, | |
| 1456 | + | |
| 1457 | + /* | |
| 1458 | + * Set the shouldSendCallback option | |
| 1459 | + * | |
| 1460 | + * @param {function} callback The callback to run which allows | |
| 1461 | + * introspecting the blob before sending | |
| 1462 | + * @return {Raven} | |
| 1463 | + */ | |
| 1464 | + setShouldSendCallback: function(callback) { | |
| 1465 | + globalOptions.shouldSendCallback = callback; | |
| 1466 | + | |
| 1467 | + return Raven; | |
| 1468 | + }, | |
| 1469 | + | |
| 1470 | + /** | |
| 1471 | + * Override the default HTTP transport mechanism that transmits data | |
| 1472 | + * to the Sentry server. | |
| 1473 | + * | |
| 1474 | + * @param {function} transport Function invoked instead of the default | |
| 1475 | + * `makeRequest` handler. | |
| 1476 | + * | |
| 1477 | + * @return {Raven} | |
| 1478 | + */ | |
| 1479 | + setTransport: function(transport) { | |
| 1480 | + globalOptions.transport = transport; | |
| 1481 | + | |
| 1482 | + return Raven; | |
| 1483 | + }, | |
| 1484 | + | |
| 1388 | 1485 | /* |
| 1389 | 1486 | * Get the latest raw exception that was captured by Raven. |
| 1390 | 1487 | * |
| @@ -1413,41 +1510,47 @@ var Raven = { · 22 unmodified lines | ||
| 1413 | 1510 | } |
| 1414 | 1511 | }; |
| 1415 | 1512 | |
| 1416 | −Raven.setUser = Raven.setUserContext; // To be deprecated | |
| 1513 | +// Deprecations | |
| 1514 | +Raven.setUser = Raven.setUserContext; | |
| 1515 | +Raven.setReleaseContext = Raven.setRelease; | |
| 1417 | 1516 | |
| 1418 | 1517 | function triggerEvent(eventType, options) { |
| 1419 | − var event, key; | |
| 1518 | + // NOTE: `event` is a native browser thing, so let's avoid conflicting wiht it | |
| 1519 | + var evt, key; | |
| 1520 | + | |
| 1521 | + if (!hasDocument) | |
| 1522 | + return; | |
| 1420 | 1523 | |
| 1421 | 1524 | options = options || {}; |
| 1422 | 1525 | |
| 1423 | 1526 | eventType = 'raven' + eventType.substr(0,1).toUpperCase() + eventType.substr(1); |
| 1424 | 1527 | |
| 1425 | 1528 | if (document.createEvent) { |
| 1426 | − event = document.createEvent('HTMLEvents'); | |
| 1427 | − event.initEvent(eventType, true, true); | |
| 1529 | + evt = document.createEvent('HTMLEvents'); | |
| 1530 | + evt.initEvent(eventType, true, true); | |
| 1428 | 1531 | } else { |
| 1429 | − event = document.createEventObject(); | |
| 1430 | − event.eventType = eventType; | |
| 1532 | + evt = document.createEventObject(); | |
| 1533 | + evt.eventType = eventType; | |
| 1431 | 1534 | } |
| 1432 | 1535 | |
| 1433 | 1536 | for (key in options) if (hasKey(options, key)) { |
| 1434 | − event[key] = options[key]; | |
| 1537 | + evt[key] = options[key]; | |
| 1435 | 1538 | } |
| 1436 | 1539 | |
| 1437 | 1540 | if (document.createEvent) { |
| 1438 | 1541 | // IE9 if standards |
| 1439 | − document.dispatchEvent(event); | |
| 1542 | + document.dispatchEvent(evt); | |
| 1440 | 1543 | } else { |
| 1441 | 1544 | // IE8 regardless of Quirks or Standards |
| 1442 | 1545 | // IE9 if quirks |
| 1443 | 1546 | try { |
| 1444 | − document.fireEvent('on' + event.eventType.toLowerCase(), event); | |
| 1547 | + document.fireEvent('on' + evt.eventType.toLowerCase(), evt); | |
| 1445 | 1548 | } catch(e) {} |
| 1446 | 1549 | } |
| 1447 | 1550 | } |
| 1448 | 1551 | |
| 1449 | 1552 | var dsnKeys = 'source protocol user pass host port path'.split(' '), |
| 1450 | − dsnPattern = /^(?:(\w+):)?\/\/(\w+)(:\w+)?@([\w\.-]+)(?::(\d+))?(\/.*)/; | |
| 1553 | + dsnPattern = /^(?:(\w+):)?\/\/(?:(\w+)(:\w+)?@)?([\w\.-]+)(?::(\d+))?(\/.*)/; | |
| 1451 | 1554 | |
| 1452 | 1555 | function RavenConfigError(message) { |
| 1453 | 1556 | this.name = 'RavenConfigError'; |
| @@ -1475,7 +1578,7 @@ function parseDSN(str) { · 21 unmodified lines | ||
| 1475 | 1578 | } |
| 1476 | 1579 | |
| 1477 | 1580 | function isUndefined(what) { |
| 1478 | − return typeof what === 'undefined'; | |
| 1581 | + return what === void 0; | |
| 1479 | 1582 | } |
| 1480 | 1583 | |
| 1481 | 1584 | function isFunction(what) { |
| @@ -1483,7 +1586,7 @@ function isFunction(what) { · 1 unmodified line | ||
| 1483 | 1586 | } |
| 1484 | 1587 | |
| 1485 | 1588 | function isString(what) { |
| 1486 | − return typeof what === 'string'; | |
| 1589 | + return objectPrototype.toString.call(what) === '[object String]'; | |
| 1487 | 1590 | } |
| 1488 | 1591 | |
| 1489 | 1592 | function isObject(what) { |
| @@ -1533,15 +1636,6 @@ function each(obj, callback) { · 43 unmodified lines | ||
| 1533 | 1636 | } |
| 1534 | 1637 | } |
| 1535 | 1638 | |
| 1536 | − | |
| 1537 | −function setAuthQueryString() { | |
| 1538 | − authQueryString = | |
| 1539 | − '?sentry_version=4' + | |
| 1540 | − '&sentry_client=raven-js/' + Raven.VERSION + | |
| 1541 | − '&sentry_key=' + globalKey; | |
| 1542 | −} | |
| 1543 | − | |
| 1544 | − | |
| 1545 | 1639 | function handleStackInfo(stackInfo, options) { |
| 1546 | 1640 | var frames = []; |
| 1547 | 1641 | |
| @@ -1588,7 +1682,7 @@ function normalizeFrame(frame) { · 40 unmodified lines | ||
| 1588 | 1682 | |
| 1589 | 1683 | normalized.in_app = !( // determine if an exception came from outside of our app |
| 1590 | 1684 | // first we check the global includePaths list. |
| 1591 | − !globalOptions.includePaths.test(normalized.filename) || | |
| 1685 | + (!!globalOptions.includePaths.test && !globalOptions.includePaths.test(normalized.filename)) || | |
| 1592 | 1686 | // Now we check for fun, if the function name is Raven or TraceKit |
| 1593 | 1687 | /(Raven|TraceKit)\./.test(normalized['function']) || |
| 1594 | 1688 | // finally, we do a last ditch effort and check for raven.min.js |
| @@ -1638,20 +1732,12 @@ function extractContextFromFrame(frame) { · 43 unmodified lines | ||
| 1638 | 1732 | } |
| 1639 | 1733 | |
| 1640 | 1734 | function processException(type, message, fileurl, lineno, frames, options) { |
| 1641 | − var stacktrace, label, i; | |
| 1735 | + var stacktrace, i, fullMessage; | |
| 1642 | 1736 | |
| 1643 | − // In some instances message is not actually a string, no idea why, | |
| 1644 | − // so we want to always coerce it to one. | |
| 1645 | − message += ''; | |
| 1646 | − | |
| 1647 | − // Sometimes an exception is getting logged in Sentry as | |
| 1648 | − // <no message value> | |
| 1649 | − // This can only mean that the message was falsey since this value | |
| 1650 | − // is hardcoded into Sentry itself. | |
| 1651 | − // At this point, if the message is falsey, we bail since it's useless | |
| 1652 | − if (type === 'Error' && !message) return; | |
| 1737 | + if (!!globalOptions.ignoreErrors.test && globalOptions.ignoreErrors.test(message)) return; | |
| 1653 | 1738 | |
| 1654 | − if (globalOptions.ignoreErrors.test(message)) return; | |
| 1739 | + message += ''; | |
| 1740 | + fullMessage = type + ': ' + message; | |
| 1655 | 1741 | |
| 1656 | 1742 | if (frames && frames.length) { |
| 1657 | 1743 | fileurl = frames[0].filename || fileurl; |
| @@ -1669,26 +1755,22 @@ function processException(type, message, fileurl, lineno, frames, options) { · 11 unmodified lines | ||
| 1669 | 1755 | }; |
| 1670 | 1756 | } |
| 1671 | 1757 | |
| 1672 | − // Truncate the message to a max of characters | |
| 1673 | − message = truncate(message, globalOptions.maxMessageLength); | |
| 1674 | − | |
| 1675 | − if (globalOptions.ignoreUrls && globalOptions.ignoreUrls.test(fileurl)) return; | |
| 1676 | − if (globalOptions.whitelistUrls && !globalOptions.whitelistUrls.test(fileurl)) return; | |
| 1677 | − | |
| 1678 | − label = lineno ? message + ' at ' + lineno : message; | |
| 1758 | + if (!!globalOptions.ignoreUrls.test && globalOptions.ignoreUrls.test(fileurl)) return; | |
| 1759 | + if (!!globalOptions.whitelistUrls.test && !globalOptions.whitelistUrls.test(fileurl)) return; | |
| 1679 | 1760 | |
| 1680 | 1761 | // Fire away! |
| 1681 | 1762 | send( |
| 1682 | 1763 | objectMerge({ |
| 1683 | 1764 | // sentry.interfaces.Exception |
| 1684 | 1765 | exception: { |
| 1685 | − type: type, | |
| 1686 | − value: message | |
| 1766 | + values: [{ | |
| 1767 | + type: type, | |
| 1768 | + value: message, | |
| 1769 | + stacktrace: stacktrace | |
| 1770 | + }] | |
| 1687 | 1771 | }, |
| 1688 | − // sentry.interfaces.Stacktrace | |
| 1689 | − stacktrace: stacktrace, | |
| 1690 | 1772 | culprit: fileurl, |
| 1691 | − message: label | |
| 1773 | + message: fullMessage | |
| 1692 | 1774 | }, options) |
| 1693 | 1775 | ); |
| 1694 | 1776 | } |
| @@ -1707,58 +1789,83 @@ function truncate(str, max) { · 12 unmodified lines | ||
| 1707 | 1789 | return str.length <= max ? str : str.substr(0, max) + '\u2026'; |
| 1708 | 1790 | } |
| 1709 | 1791 | |
| 1792 | +function trimPacket(data) { | |
| 1793 | + // For now, we only want to truncate the two different messages | |
| 1794 | + // but this could/should be expanded to just trim everything | |
| 1795 | + var max = globalOptions.maxMessageLength; | |
| 1796 | + data.message = truncate(data.message, max); | |
| 1797 | + if (data.exception) { | |
| 1798 | + var exception = data.exception.values[0]; | |
| 1799 | + exception.value = truncate(exception.value, max); | |
| 1800 | + } | |
| 1801 | + | |
| 1802 | + return data; | |
| 1803 | +} | |
| 1804 | + | |
| 1710 | 1805 | function now() { |
| 1711 | 1806 | return +new Date(); |
| 1712 | 1807 | } |
| 1713 | 1808 | |
| 1714 | 1809 | function getHttpData() { |
| 1715 | − var http = { | |
| 1716 | − url: document.location.href, | |
| 1810 | + if (!hasDocument || !document.location || !document.location.href) { | |
| 1811 | + return; | |
| 1812 | + } | |
| 1813 | + | |
| 1814 | + var httpData = { | |
| 1717 | 1815 | headers: { |
| 1718 | 1816 | 'User-Agent': navigator.userAgent |
| 1719 | 1817 | } |
| 1720 | 1818 | }; |
| 1721 | 1819 | |
| 1820 | + httpData.url = document.location.href; | |
| 1821 | + | |
| 1722 | 1822 | if (document.referrer) { |
| 1723 | − http.headers.Referer = document.referrer; | |
| 1823 | + httpData.headers.Referer = document.referrer; | |
| 1724 | 1824 | } |
| 1725 | 1825 | |
| 1726 | − return http; | |
| 1826 | + return httpData; | |
| 1727 | 1827 | } |
| 1728 | 1828 | |
| 1729 | 1829 | function send(data) { |
| 1730 | − if (!isSetup()) return; | |
| 1731 | − | |
| 1732 | − data = objectMerge({ | |
| 1830 | + var baseData = { | |
| 1733 | 1831 | project: globalProject, |
| 1734 | 1832 | logger: globalOptions.logger, |
| 1735 | − platform: 'javascript', | |
| 1736 | − // sentry.interfaces.Http | |
| 1737 | − request: getHttpData() | |
| 1738 | − }, data); | |
| 1833 | + platform: 'javascript' | |
| 1834 | + }, httpData = getHttpData(); | |
| 1835 | + | |
| 1836 | + if (httpData) { | |
| 1837 | + baseData.request = httpData; | |
| 1838 | + } | |
| 1839 | + | |
| 1840 | + data = objectMerge(baseData, data); | |
| 1739 | 1841 | |
| 1740 | 1842 | // Merge in the tags and extra separately since objectMerge doesn't handle a deep merge |
| 1741 | − data.tags = objectMerge(objectMerge({}, globalOptions.tags), data.tags); | |
| 1742 | − data.extra = objectMerge(objectMerge({}, globalOptions.extra), data.extra); | |
| 1843 | + data.tags = objectMerge(objectMerge({}, globalContext.tags), data.tags); | |
| 1844 | + data.extra = objectMerge(objectMerge({}, globalContext.extra), data.extra); | |
| 1743 | 1845 | |
| 1744 | 1846 | // Send along our own collected metadata with extra |
| 1745 | − data.extra = objectMerge({ | |
| 1746 | − 'session:duration': now() - startTime | |
| 1747 | − }, data.extra); | |
| 1847 | + data.extra['session:duration'] = now() - startTime; | |
| 1748 | 1848 | |
| 1749 | 1849 | // If there are no tags/extra, strip the key from the payload alltogther. |
| 1750 | 1850 | if (isEmptyObject(data.tags)) delete data.tags; |
| 1751 | 1851 | |
| 1752 | − if (globalUser) { | |
| 1852 | + if (globalContext.user) { | |
| 1753 | 1853 | // sentry.interfaces.User |
| 1754 | − data.user = globalUser; | |
| 1854 | + data.user = globalContext.user; | |
| 1755 | 1855 | } |
| 1756 | 1856 | |
| 1757 | − // Include the release iff it's defined in globalOptions | |
| 1857 | + // Include the release if it's defined in globalOptions | |
| 1758 | 1858 | if (globalOptions.release) data.release = globalOptions.release; |
| 1859 | + // Include server_name if it's defined in globalOptions | |
| 1860 | + if (globalOptions.serverName) data.server_name = globalOptions.serverName; | |
| 1759 | 1861 | |
| 1760 | 1862 | if (isFunction(globalOptions.dataCallback)) { |
| 1761 | − data = globalOptions.dataCallback(data); | |
| 1863 | + data = globalOptions.dataCallback(data) || data; | |
| 1864 | + } | |
| 1865 | + | |
| 1866 | + // Why?????????? | |
| 1867 | + if (!data || isEmptyObject(data)) { | |
| 1868 | + return; | |
| 1762 | 1869 | } |
| 1763 | 1870 | |
| 1764 | 1871 | // Check if the request should be filtered or not |
| @@ -1771,34 +1878,68 @@ function send(data) { · 6 unmodified lines | ||
| 1771 | 1878 | // Set lastEventId after we know the error should actually be sent |
| 1772 | 1879 | lastEventId = data.event_id || (data.event_id = uuid4()); |
| 1773 | 1880 | |
| 1774 | − makeRequest(data); | |
| 1881 | + // Try and clean up the packet before sending by truncating long values | |
| 1882 | + data = trimPacket(data); | |
| 1883 | + | |
| 1884 | + logDebug('debug', 'Raven about to send:', data); | |
| 1885 | + | |
| 1886 | + if (!isSetup()) return; | |
| 1887 | + | |
| 1888 | + (globalOptions.transport || makeRequest)({ | |
| 1889 | + url: globalServer, | |
| 1890 | + auth: { | |
| 1891 | + sentry_version: '7', | |
| 1892 | + sentry_client: 'raven-js/' + Raven.VERSION, | |
| 1893 | + sentry_key: globalKey | |
| 1894 | + }, | |
| 1895 | + data: data, | |
| 1896 | + options: globalOptions, | |
| 1897 | + onSuccess: function success() { | |
| 1898 | + triggerEvent('success', { | |
| 1899 | + data: data, | |
| 1900 | + src: globalServer | |
| 1901 | + }); | |
| 1902 | + }, | |
| 1903 | + onError: function failure() { | |
| 1904 | + triggerEvent('failure', { | |
| 1905 | + data: data, | |
| 1906 | + src: globalServer | |
| 1907 | + }); | |
| 1908 | + } | |
| 1909 | + }); | |
| 1775 | 1910 | } |
| 1776 | 1911 | |
| 1912 | +function makeRequest(opts) { | |
| 1913 | + // Tack on sentry_data to auth options, which get urlencoded | |
| 1914 | + opts.auth.sentry_data = JSON.stringify(opts.data); | |
| 1777 | 1915 | |
| 1778 | −function makeRequest(data) { | |
| 1779 | − var img = new Image(), | |
| 1780 | − src = globalServer + authQueryString + '&sentry_data=' + encodeURIComponent(JSON.stringify(data)); | |
| 1916 | + var img = newImage(), | |
| 1917 | + src = opts.url + '?' + urlencode(opts.auth), | |
| 1918 | + crossOrigin = opts.options.crossOrigin; | |
| 1781 | 1919 | |
| 1782 | − img.crossOrigin = 'anonymous'; | |
| 1783 | − img.onload = function success() { | |
| 1784 | − triggerEvent('success', { | |
| 1785 | − data: data, | |
| 1786 | − src: src | |
| 1787 | − }); | |
| 1788 | − }; | |
| 1789 | − img.onerror = img.onabort = function failure() { | |
| 1790 | − triggerEvent('failure', { | |
| 1791 | − data: data, | |
| 1792 | − src: src | |
| 1793 | − }); | |
| 1794 | − }; | |
| 1920 | + if (crossOrigin || crossOrigin === '') { | |
| 1921 | + img.crossOrigin = crossOrigin; | |
| 1922 | + } | |
| 1923 | + img.onload = opts.onSuccess; | |
| 1924 | + img.onerror = img.onabort = opts.onError; | |
| 1795 | 1925 | img.src = src; |
| 1796 | 1926 | } |
| 1797 | 1927 | |
| 1928 | +// Note: this is shitty, but I can't figure out how to get | |
| 1929 | +// sinon to stub document.createElement without breaking everything | |
| 1930 | +// so this wrapper is just so I can stub it for tests. | |
| 1931 | +function newImage() { | |
| 1932 | + return document.createElement('img'); | |
| 1933 | +} | |
| 1934 | + | |
| 1935 | +var ravenNotConfiguredError; | |
| 1936 | + | |
| 1798 | 1937 | function isSetup() { |
| 1799 | 1938 | if (!hasJSON) return false; // needs JSON support |
| 1800 | 1939 | if (!globalServer) { |
| 1801 | − logDebug('error', 'Error: Raven has not been configured.'); | |
| 1940 | + if (!ravenNotConfiguredError) | |
| 1941 | + logDebug('error', 'Error: Raven has not been configured.'); | |
| 1942 | + ravenNotConfiguredError = true; | |
| 1802 | 1943 | return false; |
| 1803 | 1944 | } |
| 1804 | 1945 | return true; |
| @@ -1826,18 +1967,44 @@ function joinRegExp(patterns) { · 21 unmodified lines | ||
| 1826 | 1967 | return new RegExp(sources.join('|'), 'i'); |
| 1827 | 1968 | } |
| 1828 | 1969 | |
| 1829 | −// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523 | |
| 1830 | 1970 | function uuid4() { |
| 1831 | − return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) { | |
| 1832 | − var r = Math.random()*16|0, | |
| 1833 | − v = c == 'x' ? r : (r&0x3|0x8); | |
| 1834 | − return v.toString(16); | |
| 1835 | − }); | |
| 1971 | + var crypto = window.crypto || window.msCrypto; | |
| 1972 | + | |
| 1973 | + if (!isUndefined(crypto) && crypto.getRandomValues) { | |
| 1974 | + // Use window.crypto API if available | |
| 1975 | + var arr = new Uint16Array(8); | |
| 1976 | + crypto.getRandomValues(arr); | |
| 1977 | + | |
| 1978 | + // set 4 in byte 7 | |
| 1979 | + arr[3] = arr[3] & 0xFFF | 0x4000; | |
| 1980 | + // set 2 most significant bits of byte 9 to '10' | |
| 1981 | + arr[4] = arr[4] & 0x3FFF | 0x8000; | |
| 1982 | + | |
| 1983 | + var pad = function(num) { | |
| 1984 | + var v = num.toString(16); | |
| 1985 | + while (v.length < 4) { | |
| 1986 | + v = '0' + v; | |
| 1987 | + } | |
| 1988 | + return v; | |
| 1989 | + }; | |
| 1990 | + | |
| 1991 | + return (pad(arr[0]) + pad(arr[1]) + pad(arr[2]) + pad(arr[3]) + pad(arr[4]) + | |
| 1992 | + pad(arr[5]) + pad(arr[6]) + pad(arr[7])); | |
| 1993 | + } else { | |
| 1994 | + // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523 | |
| 1995 | + return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) { | |
| 1996 | + var r = Math.random()*16|0, | |
| 1997 | + v = c == 'x' ? r : (r&0x3|0x8); | |
| 1998 | + return v.toString(16); | |
| 1999 | + }); | |
| 2000 | + } | |
| 1836 | 2001 | } |
| 1837 | 2002 | |
| 1838 | −function logDebug(level, message) { | |
| 1839 | − if (window.console && console[level] && Raven.debug) { | |
| 1840 | − console[level](message); | |
| 2003 | +function logDebug(level) { | |
| 2004 | + if (originalConsoleMethods[level] && Raven.debug) { | |
| 2005 | + // _slice is coming from vendor/TraceKit/tracekit.js | |
| 2006 | + // so it's accessible globally | |
| 2007 | + originalConsoleMethods[level].apply(originalConsole, _slice.call(arguments, 1)); | |
| 1841 | 2008 | } |
| 1842 | 2009 | } |
| 1843 | 2010 | |
| @@ -1848,12 +2015,32 @@ function afterLoad() { · 4 unmodified lines | ||
| 1848 | 2015 | Raven.config(RavenConfig.dsn, RavenConfig.config).install(); |
| 1849 | 2016 | } |
| 1850 | 2017 | } |
| 2018 | + | |
| 2019 | +function urlencode(o) { | |
| 2020 | + var pairs = []; | |
| 2021 | + each(o, function(key, value) { | |
| 2022 | + pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(value)); | |
| 2023 | + }); | |
| 2024 | + return pairs.join('&'); | |
| 2025 | +} | |
| 2026 | + | |
| 2027 | +function mergeContext(key, context) { | |
| 2028 | + if (isUndefined(context)) { | |
| 2029 | + delete globalContext[key]; | |
| 2030 | + } else { | |
| 2031 | + globalContext[key] = objectMerge(globalContext[key] || {}, context); | |
| 2032 | + } | |
| 2033 | +} | |
| 2034 | + | |
| 1851 | 2035 | afterLoad(); |
| 1852 | 2036 | |
| 2037 | +// This is being exposed no matter what because there are too many weird | |
| 2038 | +// usecases for how people use Raven. If this is really a problem, I'm sorry. | |
| 2039 | +window.Raven = Raven; | |
| 2040 | + | |
| 1853 | 2041 | // Expose Raven to the world |
| 1854 | 2042 | if (typeof define === 'function' && define.amd) { |
| 1855 | 2043 | // AMD |
| 1856 | − window.Raven = Raven; | |
| 1857 | 2044 | define('raven', [], function() { |
| 1858 | 2045 | return Raven; |
| 1859 | 2046 | }); |
| @@ -1863,9 +2050,6 @@ if (typeof define === 'function' && define.amd) { · 3 unmodified lines | ||
| 1863 | 2050 | } else if (typeof exports === 'object') { |
| 1864 | 2051 | // CommonJS |
| 1865 | 2052 | exports = Raven; |
| 1866 | −} else { | |
| 1867 | − // Everything else | |
| 1868 | − window.Raven = Raven; | |
| 1869 | 2053 | } |
| 1870 | 2054 | |
| 1871 | −})(window); | |
| 2055 | +})(typeof window !== 'undefined' ? window : this); | |