megatron 0.2.36 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (152) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +21 -0
  3. data/{README.rdoc → README.md} +3 -3
  4. data/app/assets/images/{400.svg → megatron/400.svg} +0 -0
  5. data/app/assets/images/{403.svg → megatron/403.svg} +0 -0
  6. data/app/assets/images/{404.svg → megatron/404.svg} +0 -0
  7. data/app/assets/images/{408.svg → megatron/408.svg} +0 -0
  8. data/app/assets/images/{422.svg → megatron/422.svg} +0 -0
  9. data/app/assets/images/{500.svg → megatron/500.svg} +0 -0
  10. data/app/assets/images/{502.svg → megatron/502.svg} +0 -0
  11. data/app/assets/images/{503.svg → megatron/503.svg} +0 -0
  12. data/app/assets/images/{504.svg → megatron/504.svg} +0 -0
  13. data/app/assets/images/{888.svg → megatron/888.svg} +0 -0
  14. data/{public/assets → app/assets/images}/megatron/favicon.ico +0 -0
  15. data/{public/assets → app/assets/images}/megatron/logo.svg +0 -0
  16. data/app/assets/javascripts/megatron/_svg.js +55 -0
  17. data/app/assets/javascripts/megatron/{index.js → megatron.js} +4 -2
  18. data/app/assets/javascripts/megatron/vendor/bugsnag.js +1256 -0
  19. data/app/assets/{esvg → svgs}/megatron/account-circle-fill.svg +0 -0
  20. data/app/assets/{esvg → svgs}/megatron/account-circle.svg +0 -0
  21. data/app/assets/{esvg → svgs}/megatron/alerts.svg +0 -0
  22. data/app/assets/{esvg → svgs}/megatron/api.svg +0 -0
  23. data/app/assets/{esvg → svgs}/megatron/arrow_left_circle.svg +0 -0
  24. data/app/assets/{esvg → svgs}/megatron/arrow_left_circle_fill.svg +0 -0
  25. data/app/assets/{esvg → svgs}/megatron/arrow_right_circle.svg +0 -0
  26. data/app/assets/{esvg → svgs}/megatron/arrow_right_circle_fill.svg +0 -0
  27. data/app/assets/{esvg → svgs}/megatron/backups.svg +0 -0
  28. data/app/assets/{esvg → svgs}/megatron/bell.svg +0 -0
  29. data/app/assets/{esvg → svgs}/megatron/bubble.svg +0 -0
  30. data/app/assets/{esvg → svgs}/megatron/bubble_fill.svg +0 -0
  31. data/app/assets/{esvg → svgs}/megatron/check.svg +0 -0
  32. data/app/assets/{esvg → svgs}/megatron/check_circle.svg +0 -0
  33. data/app/assets/{esvg → svgs}/megatron/check_circle_fill.svg +0 -0
  34. data/app/assets/{esvg → svgs}/megatron/check_thin.svg +0 -0
  35. data/app/assets/{esvg → svgs}/megatron/chevron.svg +0 -0
  36. data/app/assets/{esvg → svgs}/megatron/chevron_thin.svg +0 -0
  37. data/app/assets/{esvg → svgs}/megatron/circle-fill.svg +0 -0
  38. data/app/assets/{esvg → svgs}/megatron/circle.svg +0 -0
  39. data/app/assets/{esvg → svgs}/megatron/cli.svg +0 -0
  40. data/app/assets/{esvg → svgs}/megatron/clock_fill.svg +0 -0
  41. data/app/assets/{esvg → svgs}/megatron/clock_line.svg +0 -0
  42. data/app/assets/{esvg → svgs}/megatron/cluster-hosts.svg +0 -0
  43. data/app/assets/{esvg → svgs}/megatron/cluster.svg +0 -0
  44. data/app/assets/{esvg → svgs}/megatron/collections.svg +0 -0
  45. data/app/assets/{esvg → svgs}/megatron/copy.svg +0 -0
  46. data/app/assets/{esvg → svgs}/megatron/dash_circle.svg +0 -0
  47. data/app/assets/{esvg → svgs}/megatron/dash_circle_fill.svg +0 -0
  48. data/app/assets/{esvg → svgs}/megatron/dash_circle_fill_thin.svg +0 -0
  49. data/app/assets/{esvg → svgs}/megatron/dash_circle_thin.svg +0 -0
  50. data/app/assets/{esvg → svgs}/megatron/database_small.svg +0 -0
  51. data/app/assets/{esvg → svgs}/megatron/databases.svg +0 -0
  52. data/app/assets/{esvg → svgs}/megatron/databases_line.svg +0 -0
  53. data/app/assets/{esvg → svgs}/megatron/deployment-beta-line.svg +0 -0
  54. data/app/assets/{esvg → svgs}/megatron/deployment-cassandra.svg +0 -0
  55. data/app/assets/{esvg → svgs}/megatron/deployment-disque.svg +0 -0
  56. data/app/assets/{esvg → svgs}/megatron/deployment-elasticsearch.svg +0 -0
  57. data/app/assets/{esvg → svgs}/megatron/deployment-etcd.svg +0 -0
  58. data/app/assets/{esvg → svgs}/megatron/deployment-influxdb.svg +0 -0
  59. data/app/assets/{esvg → svgs}/megatron/deployment-mongodb-classic.svg +0 -0
  60. data/app/assets/{esvg → svgs}/megatron/deployment-mongodb.svg +0 -0
  61. data/app/assets/{esvg → svgs}/megatron/deployment-mysql.svg +0 -0
  62. data/app/assets/{esvg → svgs}/megatron/deployment-postgresql.svg +0 -0
  63. data/app/assets/{esvg → svgs}/megatron/deployment-rabbitmq.svg +0 -0
  64. data/app/assets/{esvg → svgs}/megatron/deployment-redis.svg +0 -0
  65. data/app/assets/{esvg → svgs}/megatron/deployment-rethinkdb.svg +0 -0
  66. data/app/assets/{esvg → svgs}/megatron/deployment-scylladb.svg +0 -0
  67. data/app/assets/{esvg → svgs}/megatron/deployment.svg +0 -0
  68. data/app/assets/{esvg → svgs}/megatron/deployments.svg +0 -0
  69. data/app/assets/{esvg → svgs}/megatron/download.svg +0 -0
  70. data/app/assets/{esvg → svgs}/megatron/eye.svg +0 -0
  71. data/app/assets/{esvg → svgs}/megatron/eye_closed.svg +0 -0
  72. data/app/assets/{esvg → svgs}/megatron/face-disappointed.svg +0 -0
  73. data/app/assets/{esvg → svgs}/megatron/face-happy.svg +0 -0
  74. data/app/assets/{esvg → svgs}/megatron/gear.svg +0 -0
  75. data/app/assets/{esvg → svgs}/megatron/gear_line.svg +0 -0
  76. data/app/assets/{esvg → svgs}/megatron/gear_round.svg +0 -0
  77. data/app/assets/{esvg → svgs}/megatron/globe.svg +0 -0
  78. data/app/assets/{esvg → svgs}/megatron/help.svg +0 -0
  79. data/app/assets/{esvg → svgs}/megatron/home.svg +0 -0
  80. data/app/assets/{esvg → svgs}/megatron/hourglass.svg +0 -0
  81. data/app/assets/{esvg → svgs}/megatron/info.svg +0 -0
  82. data/app/assets/{esvg → svgs}/megatron/jobs.svg +0 -0
  83. data/app/assets/{esvg → svgs}/megatron/keys.svg +0 -0
  84. data/app/assets/{esvg → svgs}/megatron/lock.svg +0 -0
  85. data/app/assets/{esvg → svgs}/megatron/locked.svg +0 -0
  86. data/app/assets/{esvg → svgs}/megatron/logs.svg +0 -0
  87. data/app/assets/{esvg → svgs}/megatron/minus.svg +0 -0
  88. data/app/assets/{esvg → svgs}/megatron/monitoring.svg +0 -0
  89. data/app/assets/{esvg → svgs}/megatron/payment.svg +0 -0
  90. data/app/assets/{esvg → svgs}/megatron/plus.svg +0 -0
  91. data/app/assets/{esvg → svgs}/megatron/plus_circle.svg +0 -0
  92. data/app/assets/{esvg → svgs}/megatron/plus_circle_fill.svg +0 -0
  93. data/app/assets/{esvg → svgs}/megatron/power.svg +0 -0
  94. data/app/assets/{esvg → svgs}/megatron/processing.svg +0 -0
  95. data/app/assets/{esvg → svgs}/megatron/puzzle.svg +0 -0
  96. data/app/assets/{esvg → svgs}/megatron/question-line.svg +0 -0
  97. data/app/assets/{esvg → svgs}/megatron/question_full.svg +0 -0
  98. data/app/assets/{esvg → svgs}/megatron/restore.svg +0 -0
  99. data/app/assets/{esvg → svgs}/megatron/rocket.svg +0 -0
  100. data/app/assets/{esvg → svgs}/megatron/sandbox.svg +0 -0
  101. data/app/assets/{esvg → svgs}/megatron/scale.svg +0 -0
  102. data/app/assets/{esvg → svgs}/megatron/star.svg +0 -0
  103. data/app/assets/{esvg → svgs}/megatron/tools.svg +0 -0
  104. data/app/assets/{esvg → svgs}/megatron/transporter.svg +0 -0
  105. data/app/assets/{esvg → svgs}/megatron/unlocked.svg +0 -0
  106. data/app/assets/{esvg → svgs}/megatron/users-circle-fill.svg +0 -0
  107. data/app/assets/{esvg → svgs}/megatron/users-circle.svg +0 -0
  108. data/app/assets/{esvg → svgs}/megatron/users.svg +0 -0
  109. data/app/assets/{esvg → svgs}/megatron/warning-circle-fill-thin.svg +0 -0
  110. data/app/assets/{esvg → svgs}/megatron/warning.svg +0 -0
  111. data/app/assets/{esvg → svgs}/megatron/warning_circle.svg +0 -0
  112. data/app/assets/{esvg → svgs}/megatron/warning_circle_fill.svg +0 -0
  113. data/app/assets/{esvg → svgs}/megatron/warning_circle_thin.svg +0 -0
  114. data/app/assets/{esvg → svgs}/megatron/watch.svg +0 -0
  115. data/app/assets/{esvg → svgs}/megatron/x.svg +0 -0
  116. data/app/assets/{esvg → svgs}/megatron/x_circle.svg +0 -0
  117. data/app/assets/{esvg → svgs}/megatron/x_circle_fill.svg +0 -0
  118. data/app/assets/{esvg → svgs}/megatron/x_circle_fill_thin.svg +0 -0
  119. data/app/assets/{esvg → svgs}/megatron/x_circle_thin.svg +0 -0
  120. data/app/controllers/megatron/errors_controller.rb +3 -0
  121. data/app/helpers/megatron/application_helper.rb +0 -95
  122. data/app/helpers/megatron/icon_helper.rb +11 -7
  123. data/app/helpers/megatron/layout_helper.rb +2 -10
  124. data/app/views/layouts/megatron/default.html.slim +76 -0
  125. data/app/views/layouts/megatron/errors.html.slim +8 -8
  126. data/app/views/megatron/shared/_flash_messages.html.slim +6 -0
  127. data/app/views/megatron/shared/_head.html.slim +23 -0
  128. data/app/views/megatron/shared/_main_content.html.slim +6 -3
  129. data/lib/megatron/engine.rb +0 -7
  130. data/lib/megatron/slim_code_filter.rb +21 -0
  131. data/lib/megatron/version.rb +1 -1
  132. data/lib/megatron.rb +10 -6
  133. data/public/{assets/megatron/megatron-0.2.36.css → megatron-0.3.0.css} +1825 -1879
  134. data/public/megatron-0.3.0.css.gz +0 -0
  135. data/public/{assets/megatron/megatron-0.2.36.js → megatron-0.3.0.js} +7 -7
  136. data/public/megatron-0.3.0.js.gz +0 -0
  137. data/public/megatron-0.3.0.map.json +1 -0
  138. data/public/megatron-error-pages-0.3.0.css +901 -0
  139. data/public/megatron-error-pages-0.3.0.css.gz +0 -0
  140. metadata +149 -135
  141. data/MIT-LICENSE +0 -20
  142. data/Rakefile +0 -209
  143. data/app/assets/javascripts/megatron/esvg.js +0 -55
  144. data/app/views/layouts/megatron/application.html.slim +0 -67
  145. data/config/esvg.yml +0 -8
  146. data/config/routes.rb +0 -2
  147. data/public/assets/megatron/megatron-0.2.36.css.gz +0 -0
  148. data/public/assets/megatron/megatron-0.2.36.css.map +0 -7
  149. data/public/assets/megatron/megatron-0.2.36.js.gz +0 -0
  150. data/public/assets/megatron/megatron-0.2.36.map.json +0 -1
  151. data/public/assets/megatron/megatron-error-pages-0.2.36.css +0 -1
  152. data/public/assets/megatron/megatron-error-pages-0.2.36.css.gz +0 -0
@@ -0,0 +1,1256 @@
1
+ //
2
+ // Version 3.0.7
3
+ //
4
+ // https://github.com/bugsnag/bugsnag-js/releases/tag/v3.0.7
5
+ //
6
+ // **Bugsnag.js** is the official JavaScript notifier for
7
+ // [Bugsnag](https://bugsnag.com).
8
+ //
9
+ // Bugsnag gives you instant notification of errors and
10
+ // exceptions in your website's JavaScript code.
11
+ //
12
+ // Bugsnag.js is incredibly small, and has no external dependencies (not even
13
+ // jQuery!) so you can safely use it on any website.
14
+ //
15
+
16
+ // The `Bugsnag` object is the only globally exported variable
17
+ (function (window, old) {
18
+ var self = {},
19
+ lastScript,
20
+ previousNotification,
21
+ shouldCatch = true,
22
+ ignoreOnError = 0,
23
+ breadcrumbs = [],
24
+ breadcrumbHardLimit = 40,
25
+ placeholderErrorName = "BugsnagNotify",
26
+
27
+ // We've seen cases where individual clients can infinite loop sending us errors
28
+ // (in some cases 10,000+ errors per page). This limit is at the point where
29
+ // you've probably learned everything useful there is to debug the problem,
30
+ // and we're happy to under-estimate the count to save the client (and Bugsnag's) resources.
31
+ eventsRemaining = 10,
32
+ // The default depth of attached metadata which is parsed before truncation. It
33
+ // is configurable via the `maxDepth` setting.
34
+ maxPayloadDepth = 5;
35
+
36
+
37
+ // Set default breadcrumbLimit to 20, so we don't send a giant payload.
38
+ // This can be overridden up to the breadcrumbHardLimit
39
+ self.breadcrumbLimit = 20;
40
+
41
+ // #### Bugsnag.noConflict
42
+ //
43
+ // This is obsolete with UMD, as we cannot assume the global scope is polluted with
44
+ // the Bugsnag object anyway. In this case, it's up to the host Javascript file to
45
+ // correctly utilise this functionality.
46
+ //
47
+ // Maybe it's worth removing all together, if we're loading via any UMD method.
48
+ self.noConflict = function() {
49
+ window.Bugsnag = old;
50
+ if (typeof old === "undefined") {
51
+ delete window.Bugsnag;
52
+ }
53
+ return self;
54
+ };
55
+
56
+ // ### Bugsnag.refresh
57
+ //
58
+ // Resets the Bugsnag rate limit. If you have a large single-page app, you may
59
+ // wish to call this in your router to avoid exception reports being thrown
60
+ // away.
61
+ //
62
+ // By default Bugsnag aggressively limits the number of exception reports from
63
+ // one page load. This protects both the client's browser and our servers in
64
+ // cases where exceptions are thrown in tight loops or scroll handlers.
65
+ self.refresh = function() {
66
+ eventsRemaining = 10;
67
+ };
68
+
69
+ //
70
+ // ### Manual error notification (public methods)
71
+ //
72
+
73
+ // #### Bugsnag.notifyException
74
+ //
75
+ // Notify Bugsnag about a given `exception`, typically that you've caught
76
+ // with a `try/catch` statement or that you've generated yourself.
77
+ //
78
+ // It's almost always better to let an exception bubble rather than catching
79
+ // it, as that gives more consistent behaviour across browsers. Consider
80
+ // re-throwing instead of calling .notifyException.
81
+ //
82
+ // Since most JavaScript exceptions use the `Error` class, we also allow
83
+ // you to provide a custom error name when calling `notifyException`.
84
+ //
85
+ // The default value is "warning" and "error" and "info" are also supported by the
86
+ // backend, all other values cause the notification to be dropped; and you
87
+ // will not see it in your dashboard.
88
+ self.notifyException = function (exception, name, metaData, severity) {
89
+ if (!exception) {
90
+ var message = "Bugsnag.notifyException() was called with no arguments";
91
+ log(message);
92
+ self.notify(placeholderErrorName, message);
93
+ return;
94
+ }
95
+
96
+ if (typeof exception === "string") {
97
+ log(
98
+ "Bugsnag.notifyException() was called with a string. Expected instance of Error. " +
99
+ "To send a custom message instantiate a new Error or use Bugsnag.notify('<string>')." +
100
+ " see https://docs.bugsnag.com/platforms/browsers/#reporting-handled-exceptions"
101
+ );
102
+ // pass through to notify()
103
+ self.notify.apply(null, arguments);
104
+ return;
105
+ }
106
+
107
+ if (name && typeof name !== "string") {
108
+ metaData = name;
109
+ name = undefined;
110
+ }
111
+ if (!metaData) {
112
+ metaData = {};
113
+ }
114
+ addScriptToMetaData(metaData);
115
+
116
+ sendToBugsnag({
117
+ name: name || exception.name,
118
+ message: exception.message || exception.description,
119
+ stacktrace: stacktraceFromException(exception) || generateStacktrace(),
120
+ file: exception.fileName || exception.sourceURL,
121
+ lineNumber: exception.lineNumber || exception.line,
122
+ columnNumber: exception.columnNumber ? exception.columnNumber + 1 : undefined,
123
+ severity: severity || "warning"
124
+ }, metaData);
125
+ };
126
+
127
+ // #### Bugsnag.notify
128
+ //
129
+ // Notify Bugsnag about an error by passing in a `name` and `message`,
130
+ // without requiring an exception.
131
+ self.notify = function (name, message, metaData, severity) {
132
+ if (!name) {
133
+ name = placeholderErrorName;
134
+ message = "Bugsnag.notify() was called with no arguments";
135
+ log(message);
136
+ }
137
+
138
+ sendToBugsnag({
139
+ name: name,
140
+ message: message,
141
+ stacktrace: generateStacktrace(),
142
+ // These are defaults so that 'bugsnag.notify()' calls show up in old IE,
143
+ // newer browsers get a legit stacktrace from generateStacktrace().
144
+ file: window.location.toString(),
145
+ lineNumber: 1,
146
+ severity: severity || "warning"
147
+ }, metaData);
148
+ };
149
+
150
+ // #### Bugsnag.leaveBreadcrumb(value, [metaData])
151
+ //
152
+ // Add a breadcrumb to the array of breadcrumbs to be sent to Bugsnag when the next exception occurs
153
+ // - `value` (string|object) If this is an object, it will be used as the entire breadcrumb object
154
+ // and any missing `type`, `name` or `timestamp` fields will get default values.
155
+ // if this is a string and metaData is provided, then `value` will be used as the `name` of the
156
+ // breadcrumb.
157
+ // if `value` is a string and is the only argument the breadcrumb will have `manual` type and
158
+ // the value will be used as the `message` field of `metaData`.
159
+ //
160
+ // - `metadata` (optional, object) - Additional information about the breadcrumb. Values limited to 140 characters.
161
+ self.leaveBreadcrumb = function(value, metaData) {
162
+ var DEFAULT_TYPE = "manual";
163
+
164
+ // default crumb
165
+ var crumb = {
166
+ type: DEFAULT_TYPE,
167
+ name: "Manual",
168
+ timestamp: new Date().getTime()
169
+ };
170
+
171
+ switch (typeof value) {
172
+ case "object":
173
+ crumb = merge(crumb, value);
174
+ break;
175
+ case "string":
176
+ if (metaData && typeof metaData === "object") {
177
+ crumb = merge(crumb, {
178
+ name: value,
179
+ metaData: metaData
180
+ });
181
+ } else {
182
+ crumb.metaData = { message: value };
183
+ }
184
+ break;
185
+ default:
186
+ log("expecting 1st argument to leaveBreadcrumb to be a 'string' or 'object', got " + typeof value);
187
+ return;
188
+ }
189
+
190
+ // Validate breadcrumb type and replace invalid type with default.
191
+ var VALID_TYPES = [DEFAULT_TYPE, "error", "log", "navigation", "process", "request", "state", "user"];
192
+ var validType = false;
193
+ for (var i = 0; i < VALID_TYPES.length; i++) {
194
+ if (VALID_TYPES[i] === crumb.type) {
195
+ validType = true;
196
+ break;
197
+ }
198
+ }
199
+ if (!validType) {
200
+ log("Converted invalid breadcrumb type '" + crumb.type + "' to '" + DEFAULT_TYPE + "'");
201
+ crumb.type = DEFAULT_TYPE;
202
+ }
203
+
204
+ var lastCrumb = breadcrumbs.slice(-1)[0];
205
+ if (breadcrumbsAreEqual(crumb, lastCrumb)) {
206
+ lastCrumb.count = lastCrumb.count || 1;
207
+ lastCrumb.count++;
208
+ } else {
209
+ var breadcrumbLimit = Math.min(self.breadcrumbLimit, breadcrumbHardLimit);
210
+ crumb.name = truncate(crumb.name, 32);
211
+ breadcrumbs.push(truncateDeep(crumb, 140));
212
+
213
+ // limit breadcrumb trail length, so the payload doesn't get too large
214
+ if (breadcrumbs.length > breadcrumbLimit) {
215
+ breadcrumbs = breadcrumbs.slice(-breadcrumbLimit);
216
+ }
217
+ }
218
+ };
219
+
220
+ function breadcrumbsAreEqual(crumb1, crumb2) {
221
+ return crumb1 && crumb2 &&
222
+ crumb1.type === crumb2.type &&
223
+ crumb1.name === crumb2.name &&
224
+ isEqual(crumb1.metaData, crumb2.metaData);
225
+ }
226
+
227
+ // Return a function acts like the given function, but reports
228
+ // any exceptions to Bugsnag before re-throwing them.
229
+ //
230
+ // This is not a public function because it can only be used if
231
+ // the exception is not caught after being thrown out of this function.
232
+ //
233
+ // If you call wrap twice on the same function, it'll give you back the
234
+ // same wrapped function. This lets removeEventListener to continue to
235
+ // work.
236
+ function wrap(_super) {
237
+ try {
238
+ if (typeof _super !== "function") {
239
+ return _super;
240
+ }
241
+ if (!_super.bugsnag) {
242
+ var currentScript = getCurrentScript();
243
+ _super.bugsnag = function () {
244
+ lastScript = currentScript;
245
+
246
+ // We set shouldCatch to false on IE < 10 because catching the error ruins the file/line as reported in window.onerror,
247
+ // We set shouldCatch to false on Chrome/Safari because it interferes with "break on unhandled exception"
248
+ // All other browsers need shouldCatch to be true, as they don't pass the exception object to window.onerror
249
+ if (shouldCatch) {
250
+ try {
251
+ return _super.apply(this, arguments);
252
+ } catch (e) {
253
+ if (getSetting("autoNotify", true)) {
254
+ self.notifyException(e, null, null, "error");
255
+ ignoreNextOnError();
256
+ }
257
+ throw e;
258
+ } finally {
259
+ lastScript = null;
260
+ }
261
+ } else {
262
+ var ret = _super.apply(this, arguments);
263
+ // in case of error, this is set to null in window.onerror
264
+ lastScript = null;
265
+ return ret;
266
+ }
267
+ };
268
+ _super.bugsnag.bugsnag = _super.bugsnag;
269
+ }
270
+ return _super.bugsnag;
271
+
272
+ // This can happen if _super is not a normal javascript function.
273
+ // For example, see https://github.com/bugsnag/bugsnag-js/issues/28
274
+ } catch (e) {
275
+ return _super;
276
+ }
277
+ }
278
+
279
+ var _hasAddEventListener = (typeof window.addEventListener !== "undefined");
280
+
281
+ // Setup breadcrumbs for click events
282
+ function setupClickBreadcrumbs() {
283
+ if (!_hasAddEventListener) {
284
+ return;
285
+ }
286
+
287
+ var callback = function(event) {
288
+ if(!getBreadcrumbSetting("autoBreadcrumbsClicks")) {
289
+ return;
290
+ }
291
+
292
+ var targetText, targetSelector;
293
+ // Cross origin security might prevent us from accessing the event target
294
+
295
+ try {
296
+ targetText = nodeText(event.target);
297
+ targetSelector = nodeLabel(event.target);
298
+ } catch (e) {
299
+ targetText = "[hidden]";
300
+ targetSelector = "[hidden]";
301
+ log("Cross domain error when tracking click event. See https://docs.bugsnag.com/platforms/browsers/faq/#3-cross-origin-script-errors");
302
+ }
303
+
304
+ self.leaveBreadcrumb({
305
+ type: "user",
306
+ name: "UI click",
307
+ metaData: {
308
+ targetText: targetText,
309
+ targetSelector: targetSelector
310
+ }
311
+ });
312
+ };
313
+
314
+ window.addEventListener("click", callback, true);
315
+ }
316
+
317
+ // stub functions for old browsers
318
+ self.enableAutoBreadcrumbsConsole = function() {};
319
+ self.disableAutoBreadcrumbsConsole = function() {};
320
+
321
+ // Setup breadcrumbs for console.log, console.warn, console.error
322
+ function setupConsoleBreadcrumbs(){
323
+
324
+ function trackLog(severity, args) {
325
+ if(!getBreadcrumbSetting("autoBreadcrumbsConsole")) {
326
+ return;
327
+ }
328
+
329
+ self.leaveBreadcrumb({
330
+ type: "log",
331
+ name: "Console output",
332
+ metaData: {
333
+ severity: severity,
334
+ message: Array.prototype.slice.call(args).join(", ")
335
+ }
336
+ });
337
+ }
338
+
339
+ // feature detection for console.log
340
+ if(typeof window.console === "undefined") {
341
+ return;
342
+ }
343
+
344
+ // keep track of functions that we will need to hijack
345
+ var nativeLog = console.log,
346
+ nativeWarn = console.warn,
347
+ nativeError = console.error;
348
+
349
+ self.enableAutoBreadcrumbsConsole = function() {
350
+ self.autoBreadcrumbsConsole = true;
351
+
352
+ enhance(console, "log", function() {
353
+ trackLog("log", arguments);
354
+ });
355
+
356
+ enhance(console, "warn", function() {
357
+ trackLog("warn", arguments);
358
+ });
359
+
360
+ enhance(console, "error", function() {
361
+ trackLog("error", arguments);
362
+ });
363
+ };
364
+
365
+ self.disableAutoBreadcrumbsConsole = function() {
366
+ self.autoBreadcrumbsConsole = false;
367
+
368
+ console.log = nativeLog;
369
+ console.warn = nativeWarn;
370
+ console.error = nativeError;
371
+ };
372
+
373
+ if(getBreadcrumbSetting("autoBreadcrumbsConsole")) {
374
+ self.enableAutoBreadcrumbsConsole();
375
+ }
376
+ }
377
+
378
+ // stub functions for old browsers
379
+ self.enableAutoBreadcrumbsNavigation = function() {};
380
+ self.disableAutoBreadcrumbsNavigation = function() {};
381
+
382
+ // Setup breadcrumbs for history navigation events
383
+ function setupNavigationBreadcrumbs() {
384
+
385
+ function parseHash(url) {
386
+ return url.split("#")[1] || "";
387
+ }
388
+
389
+ function buildHashChange(event) {
390
+ var oldURL = event.oldURL,
391
+ newURL = event.newURL,
392
+ metaData = {};
393
+
394
+ // not supported in old browsers
395
+ if (oldURL && newURL) {
396
+ metaData.from = parseHash(oldURL);
397
+ metaData.to = parseHash(newURL);
398
+ } else {
399
+ metaData.to = location.hash;
400
+ }
401
+
402
+ return {
403
+ type: "navigation",
404
+ name: "Hash changed",
405
+ metaData: metaData
406
+ };
407
+ }
408
+
409
+ function buildPopState() {
410
+ return {
411
+ type: "navigation",
412
+ name: "Navigated back"
413
+ };
414
+ }
415
+
416
+ function buildPageHide() {
417
+ return {
418
+ type: "navigation",
419
+ name: "Page hidden"
420
+ };
421
+ }
422
+
423
+ function buildPageShow() {
424
+ return {
425
+ type: "navigation",
426
+ name: "Page shown"
427
+ };
428
+ }
429
+
430
+ function buildLoad() {
431
+ return {
432
+ type: "navigation",
433
+ name: "Page loaded"
434
+ };
435
+ }
436
+
437
+ function buildDOMContentLoaded() {
438
+ return {
439
+ type: "navigation",
440
+ name: "DOMContentLoaded"
441
+ };
442
+ }
443
+
444
+ function buildStateChange(name, state, title, url) {
445
+ var currentPath = location.pathname + location.search + location.hash;
446
+ return {
447
+ type: "navigation",
448
+ name: "History " + name,
449
+ metaData: {
450
+ from: currentPath,
451
+ to: url || currentPath,
452
+ prevState: history.state,
453
+ nextState: state
454
+ }
455
+ };
456
+ }
457
+
458
+ function buildPushState(state, title, url) {
459
+ return buildStateChange("pushState", state, title, url);
460
+ }
461
+
462
+ function buildReplaceState(state, title, url) {
463
+ return buildStateChange("replaceState", state, title, url);
464
+ }
465
+
466
+ // functional fu to make it easier to setup event listeners
467
+ function wrapBuilder(builder) {
468
+ return function() {
469
+ if(!getBreadcrumbSetting("autoBreadcrumbsNavigation")) {
470
+ return;
471
+ }
472
+
473
+ self.leaveBreadcrumb(builder.apply(null, arguments));
474
+ };
475
+ }
476
+
477
+ // check for browser support
478
+ if (!_hasAddEventListener ||
479
+ !window.history ||
480
+ !window.history.state ||
481
+ !window.history.pushState ||
482
+ !window.history.pushState.bind
483
+ ) {
484
+ return;
485
+ }
486
+
487
+ // keep track of native functions
488
+ var nativePushState = history.pushState,
489
+ nativeReplaceState = history.replaceState;
490
+
491
+ // create enable function
492
+ self.enableAutoBreadcrumbsNavigation = function() {
493
+ self.autoBreadcrumbsNavigation = true;
494
+ // create hooks for pushstate and replaceState
495
+ enhance(history, "pushState", wrapBuilder(buildPushState));
496
+ enhance(history, "replaceState", wrapBuilder(buildReplaceState));
497
+ };
498
+
499
+ // create disable function
500
+ self.disableAutoBreadcrumbsNavigation = function() {
501
+ self.autoBreadcrumbsNavigation = false;
502
+ // restore native functions
503
+ history.pushState = nativePushState;
504
+ history.replaceState = nativeReplaceState;
505
+ };
506
+
507
+ window.addEventListener("hashchange", wrapBuilder(buildHashChange), true);
508
+ window.addEventListener("popstate", wrapBuilder(buildPopState), true);
509
+ window.addEventListener("pagehide", wrapBuilder(buildPageHide), true);
510
+ window.addEventListener("pageshow", wrapBuilder(buildPageShow), true);
511
+ window.addEventListener("load", wrapBuilder(buildLoad), true);
512
+ window.addEventListener("DOMContentLoaded", wrapBuilder(buildDOMContentLoaded), true);
513
+
514
+ if(getBreadcrumbSetting("autoBreadcrumbsNavigation")) {
515
+ self.enableAutoBreadcrumbsNavigation();
516
+ }
517
+ }
518
+
519
+ self.enableAutoBreadcrumbsErrors = function() {
520
+ self.autoBreadcrumbsErrors = true;
521
+ };
522
+
523
+ self.disableAutoBreadcrumbsErrors = function() {
524
+ self.autoBreadcrumbsErrors = false;
525
+ };
526
+
527
+ self.enableAutoBreadcrumbsClicks = function() {
528
+ self.autoBreadcrumbsClicks = true;
529
+ };
530
+
531
+ self.disableAutoBreadcrumbsClicks = function() {
532
+ self.autoBreadcrumbsClicks = false;
533
+ };
534
+
535
+ self.enableAutoBreadcrumbs = function() {
536
+ self.enableAutoBreadcrumbsClicks();
537
+ self.enableAutoBreadcrumbsConsole();
538
+ self.enableAutoBreadcrumbsErrors();
539
+ self.enableAutoBreadcrumbsNavigation();
540
+ };
541
+
542
+ self.disableAutoBreadcrumbs = function() {
543
+ self.disableAutoBreadcrumbsClicks();
544
+ self.disableAutoBreadcrumbsConsole();
545
+ self.disableAutoBreadcrumbsErrors();
546
+ self.disableAutoBreadcrumbsNavigation();
547
+ };
548
+
549
+ //
550
+ // ### Script tag tracking
551
+ //
552
+
553
+ // To emulate document.currentScript we use document.scripts.last.
554
+ // This only works while synchronous scripts are running, so we track
555
+ // that here.
556
+ var synchronousScriptsRunning = document.readyState !== "complete";
557
+ function loadCompleted() {
558
+ synchronousScriptsRunning = false;
559
+ }
560
+
561
+ // from jQuery. We don't have quite such tight bounds as they do if
562
+ // we end up on the window.onload event as we don't try and hack
563
+ // the .scrollLeft() fix in because it doesn't work in frames so
564
+ // we'd need these fallbacks anyway.
565
+ // The worst that can happen is we group an event handler that fires
566
+ // before us into the last script tag.
567
+ if (document.addEventListener) {
568
+ document.addEventListener("DOMContentLoaded", loadCompleted, true);
569
+ window.addEventListener("load", loadCompleted, true);
570
+ } else {
571
+ window.attachEvent("onload", loadCompleted);
572
+ }
573
+
574
+ function getCurrentScript() {
575
+ var script = document.currentScript || lastScript;
576
+
577
+ if (!script && synchronousScriptsRunning) {
578
+ var scripts = document.scripts || document.getElementsByTagName("script");
579
+ script = scripts[scripts.length - 1];
580
+ }
581
+
582
+ return script;
583
+ }
584
+
585
+ function addScriptToMetaData(metaData) {
586
+ var script = getCurrentScript();
587
+
588
+ if (script) {
589
+ metaData.script = {
590
+ src: script.src,
591
+ content: getSetting("inlineScript", true) ? script.innerHTML : ""
592
+ };
593
+ }
594
+ }
595
+
596
+ //
597
+ // ### Helpers & Setup
598
+ //
599
+
600
+ // Compile regular expressions upfront.
601
+ var API_KEY_REGEX = /^[0-9a-f]{32}$/i;
602
+ var FUNCTION_REGEX = /function\s*([\w\-$]+)?\s*\(/i;
603
+
604
+ // Set up default notifier settings.
605
+ var DEFAULT_BASE_ENDPOINT = "https://notify.bugsnag.com/";
606
+ var DEFAULT_NOTIFIER_ENDPOINT = DEFAULT_BASE_ENDPOINT + "js";
607
+ var NOTIFIER_VERSION = "3.0.7";
608
+
609
+ // Keep a reference to the currently executing script in the DOM.
610
+ // We'll use this later to extract settings from attributes.
611
+ var scripts = document.getElementsByTagName("script");
612
+ var thisScript = scripts[scripts.length - 1];
613
+
614
+ // Replace existing function on object with custom one, but still call the original afterwards
615
+ // example:
616
+ // enhance(console, 'log', function() {
617
+ // /* custom behavior */
618
+ // })
619
+ function enhance(object, property, newFunction) {
620
+ var oldFunction = object[property];
621
+ object[property] = function() {
622
+ newFunction.apply(this, arguments);
623
+ if (typeof oldFunction === "function") {
624
+ oldFunction.apply(this, arguments);
625
+ }
626
+ };
627
+ }
628
+
629
+ // Simple logging function that wraps `console.log` if available.
630
+ // This is useful for warning about configuration issues
631
+ // eg. forgetting to set an API key.
632
+ function log(msg) {
633
+ var disableLog = getSetting("disableLog");
634
+
635
+ var console = window.console;
636
+ if (console !== undefined && console.log !== undefined && !disableLog) {
637
+ console.log("[Bugsnag] " + msg);
638
+ }
639
+ }
640
+
641
+ // Compare if two objects are equal.
642
+ function isEqual(obj1, obj2) {
643
+ return serialize(obj1) === serialize(obj2);
644
+ }
645
+
646
+ // extract text content from a element
647
+ function nodeText(el) {
648
+ var text = el.textContent || el.innerText || "";
649
+
650
+ if (el.type === "submit" || el.type === "button") {
651
+ text = el.value;
652
+ }
653
+
654
+ text = text.replace(/^\s+|\s+$/g, ""); // trim whitespace
655
+ return truncate(text, 140);
656
+ }
657
+
658
+ // Create a label from tagname, id and css class of the element
659
+ function nodeLabel(el) {
660
+ var parts = [el.tagName];
661
+
662
+ if (el.id) {
663
+ parts.push("#" + el.id);
664
+ }
665
+
666
+ if (el.className && el.className.length) {
667
+ var classString = "." + el.className.split(" ").join(".");
668
+ parts.push(classString);
669
+ }
670
+
671
+ var label = parts.join("");
672
+
673
+ if (!document.querySelectorAll || !Array.prototype.indexOf) {
674
+ // can't get much more advanced with the current browser
675
+ return label;
676
+ }
677
+
678
+ try {
679
+ if (document.querySelectorAll(label).length === 1) {
680
+ return label;
681
+ }
682
+ } catch (e) {
683
+ // sometime the query selector can be invalid, for example, if the id attribute is anumber.
684
+ // in these cases, just return the label as is.
685
+ return label;
686
+ }
687
+
688
+ // try to get a more specific selector if this one matches more than one element
689
+ if (el.parentNode.childNodes.length > 1) {
690
+ var index = Array.prototype.indexOf.call(el.parentNode.childNodes, el) + 1;
691
+ label = label + ":nth-child(" + index + ")";
692
+ }
693
+
694
+ if (document.querySelectorAll(label).length === 1) {
695
+ return label;
696
+ }
697
+
698
+ // try prepending the parent node selector
699
+ if (el.parentNode) {
700
+ return nodeLabel(el.parentNode) + " > " + label;
701
+ }
702
+
703
+ return label;
704
+ }
705
+
706
+ function truncate(value, length) {
707
+ var OMISSION = "(...)";
708
+
709
+ if (value && value.length > length) {
710
+ return value.slice(0, length - OMISSION.length) + OMISSION;
711
+ } else {
712
+ return value;
713
+ }
714
+ }
715
+
716
+ function isArray(arg) {
717
+ return Object.prototype.toString.call(arg) === "[object Array]";
718
+ }
719
+
720
+ // truncate all string values in nested object
721
+ function truncateDeep(object, length, depth) {
722
+ var newDepth = (depth || 0) + 1;
723
+ var setting = getSetting("maxDepth", maxPayloadDepth);
724
+
725
+ if (depth > setting) {
726
+ return "[RECURSIVE]";
727
+ }
728
+
729
+ // Handle truncating strings
730
+ if (typeof object === "string") {
731
+ return truncate(object, length);
732
+
733
+ // Handle truncating array contents
734
+ } else if (isArray(object)) {
735
+ var newArray = [];
736
+ for (var i = 0; i < object.length; i++) {
737
+ newArray[i] = truncateDeep(object[i], length, newDepth);
738
+ }
739
+ return newArray;
740
+
741
+ // Handle truncating object keys
742
+ } if (typeof object === "object" && object != null) {
743
+ var newObject = {};
744
+ for (var key in object) {
745
+ if (object.hasOwnProperty(key)) {
746
+ newObject[key] = truncateDeep(object[key], length, newDepth);
747
+ }
748
+ }
749
+ return newObject;
750
+
751
+ // Just return everything else (numbers, booleans, functions, etc.)
752
+ } else {
753
+ return object;
754
+ }
755
+ }
756
+
757
+ // Deeply serialize an object into a query string. We use the PHP-style
758
+ // nested object syntax, `nested[keys]=val`, to support heirachical
759
+ // objects. Similar to jQuery's `$.param` method.
760
+ function serialize(obj, prefix, depth) {
761
+ var maxDepth = getSetting("maxDepth", maxPayloadDepth);
762
+
763
+ if (depth >= maxDepth) {
764
+ return encodeURIComponent(prefix) + "=[RECURSIVE]";
765
+ }
766
+ depth = depth + 1 || 1;
767
+
768
+ try {
769
+ if (window.Node && obj instanceof window.Node) {
770
+ return encodeURIComponent(prefix) + "=" + encodeURIComponent(targetToString(obj));
771
+ }
772
+
773
+ var str = [];
774
+ for (var p in obj) {
775
+ if (obj.hasOwnProperty(p) && p != null && obj[p] != null) {
776
+ var k = prefix ? prefix + "[" + p + "]" : p, v = obj[p];
777
+ str.push(typeof v === "object" ? serialize(v, k, depth) : encodeURIComponent(k) + "=" + encodeURIComponent(v));
778
+ }
779
+ }
780
+ return str.sort().join("&");
781
+ } catch (e) {
782
+ return encodeURIComponent(prefix) + "=" + encodeURIComponent("" + e);
783
+ }
784
+ }
785
+
786
+
787
+ // Deep-merge the `source` object into the `target` object and return
788
+ // the `target`. Properties in source that will overwrite those in target.
789
+ // Similar to jQuery's `$.extend` method.
790
+ function merge(target, source, depth) {
791
+ if (source == null) {
792
+ return target;
793
+ } else if (depth >= getSetting("maxDepth", maxPayloadDepth)) {
794
+ return "[RECURSIVE]";
795
+ }
796
+
797
+ target = target || {};
798
+ for (var key in source) {
799
+ if (source.hasOwnProperty(key)) {
800
+ try {
801
+ if (source[key].constructor === Object) {
802
+ target[key] = merge(target[key], source[key], depth + 1 || 1);
803
+ } else {
804
+ target[key] = source[key];
805
+ }
806
+ } catch (e) {
807
+ target[key] = source[key];
808
+ }
809
+ }
810
+ }
811
+
812
+ return target;
813
+ }
814
+
815
+ // Make a HTTP request with given `url` and `params` object.
816
+ // For maximum browser compatibility and cross-domain support, requests are
817
+ // made by creating a temporary JavaScript `Image` object.
818
+ // Additionally the request can be done via XHR (needed for Chrome apps and extensions)
819
+ // To set the script to use XHR, you can specify data-notifyhandler attribute in the script tag
820
+ // Eg. `<script data-notifyhandler="xhr">` - the request defaults to image if attribute is not set
821
+ function request(url, params) {
822
+ url += "?" + serialize(params) + "&ct=img&cb=" + new Date().getTime();
823
+ if (typeof BUGSNAG_TESTING !== "undefined" && self.testRequest) {
824
+ self.testRequest(url, params);
825
+ } else {
826
+ var notifyHandler = getSetting("notifyHandler");
827
+ if (notifyHandler === "xhr") {
828
+ var xhr = new XMLHttpRequest();
829
+ xhr.open("GET", url, true);
830
+ xhr.send();
831
+ } else {
832
+ var img = new Image();
833
+ img.src = url;
834
+ }
835
+ }
836
+ }
837
+
838
+ // Extract all `data-*` attributes from a DOM element and return them as an
839
+ // object. This is used to allow Bugsnag settings to be set via attributes
840
+ // on the `script` tag, eg. `<script data-apikey="xyz">`.
841
+ // Similar to jQuery's `$(el).data()` method.
842
+ function getData(node) {
843
+ var dataAttrs = {};
844
+ var dataRegex = /^data\-([\w\-]+)$/;
845
+
846
+ // If the node doesn't exist due to being loaded as a commonjs module,
847
+ // then return an empty object and fallback to self[].
848
+ if (node) {
849
+ var attrs = node.attributes;
850
+ for (var i = 0; i < attrs.length; i++) {
851
+ var attr = attrs[i];
852
+ if (dataRegex.test(attr.nodeName)) {
853
+ var key = attr.nodeName.match(dataRegex)[1];
854
+ dataAttrs[key] = attr.value || attr.nodeValue;
855
+ }
856
+ }
857
+ }
858
+
859
+ return dataAttrs;
860
+ }
861
+
862
+ // Get configuration settings from either `self` (the `Bugsnag` object)
863
+ // or `data` (the `data-*` attributes).
864
+ var data;
865
+ function getSetting(name, fallback) {
866
+ data = data || getData(thisScript);
867
+ var setting = self[name] !== undefined ? self[name] : data[name.toLowerCase()];
868
+ if (setting === "false") {
869
+ setting = false;
870
+ }
871
+ return setting !== undefined ? setting : fallback;
872
+ }
873
+
874
+ // Validate a Bugsnag API key exists and is of the correct format.
875
+ function validateApiKey(apiKey) {
876
+ if (!apiKey || !apiKey.match(API_KEY_REGEX)) {
877
+ log("Invalid API key '" + apiKey + "'");
878
+ return false;
879
+ }
880
+
881
+ return true;
882
+ }
883
+
884
+ // get breadcrumb specific setting. When autoBreadcrumbs is true, all individual events are defaulted
885
+ // to true. Otherwise they will all default to false. You can set any event specifically and it will override
886
+ // the default.
887
+ function getBreadcrumbSetting(name) {
888
+ var fallback = getSetting("autoBreadcrumbs", true);
889
+ return getSetting(name, fallback);
890
+ }
891
+
892
+ // Send an error to Bugsnag.
893
+ function sendToBugsnag(details, metaData) {
894
+ // Validate the configured API key.
895
+ var apiKey = getSetting("apiKey");
896
+ if (!validateApiKey(apiKey) || !eventsRemaining) {
897
+ return;
898
+ }
899
+
900
+ eventsRemaining -= 1;
901
+
902
+ // Check if we should notify for this release stage.
903
+ var releaseStage = getSetting("releaseStage", "production");
904
+ var notifyReleaseStages = getSetting("notifyReleaseStages");
905
+ if (notifyReleaseStages) {
906
+ var shouldNotify = false;
907
+ for (var i = 0; i < notifyReleaseStages.length; i++) {
908
+ if (releaseStage === notifyReleaseStages[i]) {
909
+ shouldNotify = true;
910
+ break;
911
+ }
912
+ }
913
+
914
+ if (!shouldNotify) {
915
+ return;
916
+ }
917
+ }
918
+
919
+ // Don't send multiple copies of the same error.
920
+ // This fixes a problem when a client goes into an infinite loop,
921
+ // and starts wasting all their bandwidth sending messages to bugsnag.
922
+ var deduplicate = [details.name, details.message, details.stacktrace].join("|");
923
+ if (deduplicate === previousNotification) {
924
+ return;
925
+ } else {
926
+ previousNotification = deduplicate;
927
+ }
928
+
929
+ var defaultEventMetaData = {
930
+ device: { time: (new Date()).getTime() }
931
+ };
932
+
933
+ // Build the request payload by combining error information with other data
934
+ // such as user-agent and locale, `metaData` and settings.
935
+ var payload = {
936
+ notifierVersion: NOTIFIER_VERSION,
937
+
938
+ apiKey: apiKey,
939
+ projectRoot: getSetting("projectRoot") || window.location.protocol + "//" + window.location.host,
940
+ context: getSetting("context") || window.location.pathname,
941
+ user: getSetting("user"),
942
+ metaData: merge(merge(defaultEventMetaData, getSetting("metaData")), metaData),
943
+ releaseStage: releaseStage,
944
+ appVersion: getSetting("appVersion"),
945
+
946
+ url: window.location.href,
947
+ userAgent: navigator.userAgent,
948
+ language: navigator.language || navigator.userLanguage,
949
+
950
+ severity: details.severity,
951
+
952
+ name: details.name,
953
+ message: details.message,
954
+ stacktrace: details.stacktrace,
955
+ file: details.file,
956
+ lineNumber: details.lineNumber,
957
+ columnNumber: details.columnNumber,
958
+ breadcrumbs: breadcrumbs,
959
+ payloadVersion: "3"
960
+ };
961
+
962
+ // Run any `beforeNotify` function
963
+ var beforeNotify = self.beforeNotify;
964
+ if (typeof(beforeNotify) === "function") {
965
+ var retVal = beforeNotify(payload, payload.metaData);
966
+ if (retVal === false) {
967
+ return;
968
+ }
969
+ }
970
+
971
+ if (payload.lineNumber === 0 && (/Script error\.?/).test(payload.message)) {
972
+ return log("Ignoring cross-domain or eval script error. See https://docs.bugsnag.com/platforms/browsers/faq/#3-cross-origin-script-errors");
973
+ }
974
+
975
+ // Make the HTTP request
976
+ request(getSetting("endpoint") || DEFAULT_NOTIFIER_ENDPOINT, payload);
977
+ }
978
+
979
+ // Generate a browser stacktrace (or approximation) from the current stack.
980
+ // This is used to add a stacktrace to `Bugsnag.notify` calls, and to add a
981
+ // stacktrace approximation where we can't get one from an exception.
982
+ function generateStacktrace() {
983
+ var generated, stacktrace;
984
+ var MAX_FAKE_STACK_SIZE = 10;
985
+ var ANONYMOUS_FUNCTION_PLACEHOLDER = "[anonymous]";
986
+
987
+ // Try to generate a real stacktrace (most browsers, except IE9 and below).
988
+ try {
989
+ throw new Error("");
990
+ } catch (exception) {
991
+ generated = "<generated>\n";
992
+ stacktrace = stacktraceFromException(exception);
993
+ }
994
+
995
+ // Otherwise, build a fake stacktrace from the list of method names by
996
+ // looping through the list of functions that called this one (and skip
997
+ // whoever called us).
998
+ if (!stacktrace) {
999
+ generated = "<generated-ie>\n";
1000
+ var functionStack = [];
1001
+ try {
1002
+ var curr = arguments.callee.caller.caller;
1003
+ while (curr && functionStack.length < MAX_FAKE_STACK_SIZE) {
1004
+ var fn = FUNCTION_REGEX.test(curr.toString()) ? RegExp.$1 || ANONYMOUS_FUNCTION_PLACEHOLDER : ANONYMOUS_FUNCTION_PLACEHOLDER;
1005
+ functionStack.push(fn);
1006
+ curr = curr.caller;
1007
+ }
1008
+ } catch (e) {
1009
+ log(e);
1010
+ }
1011
+ stacktrace = functionStack.join("\n");
1012
+ }
1013
+
1014
+ // Tell the backend to ignore the first two lines in the stack-trace.
1015
+ // generateStacktrace() + window.onerror,
1016
+ // generateStacktrace() + notify,
1017
+ // generateStacktrace() + notifyException
1018
+ return generated + stacktrace;
1019
+ }
1020
+
1021
+ // Get the stacktrace string from an exception
1022
+ function stacktraceFromException(exception) {
1023
+ return exception.stack || exception.backtrace || exception.stacktrace;
1024
+ }
1025
+
1026
+ // Convert a DOM element into a string suitable for passing to Bugsnag.
1027
+ function targetToString(target) {
1028
+ if (target) {
1029
+ var attrs = target.attributes;
1030
+
1031
+ if (attrs) {
1032
+ var ret = "<" + target.nodeName.toLowerCase();
1033
+ for (var i = 0; i < attrs.length; i++) {
1034
+ if (attrs[i].value && attrs[i].value.toString() !== "null") {
1035
+ ret += " " + attrs[i].name + "=\"" + attrs[i].value + "\"";
1036
+ }
1037
+ }
1038
+ return ret + ">";
1039
+ } else {
1040
+ // e.g. #document
1041
+ return target.nodeName;
1042
+ }
1043
+ }
1044
+ }
1045
+
1046
+ // If we've notified bugsnag of an exception in wrap, we don't want to
1047
+ // re-notify when it hits window.onerror after we re-throw it.
1048
+ function ignoreNextOnError() {
1049
+ ignoreOnError += 1;
1050
+ window.setTimeout(function () {
1051
+ ignoreOnError -= 1;
1052
+ });
1053
+ }
1054
+
1055
+ // Disable catching on IE < 10 as it destroys stack-traces from generateStackTrace()
1056
+ if (!window.atob) {
1057
+ shouldCatch = false;
1058
+
1059
+ // Disable catching on browsers that support HTML5 ErrorEvents properly.
1060
+ // This lets debug on unhandled exceptions work.
1061
+ } else if (window.ErrorEvent) {
1062
+ try {
1063
+ if (new window.ErrorEvent("test").colno === 0) {
1064
+ shouldCatch = false;
1065
+ }
1066
+ } catch(e){ /* No action needed */ }
1067
+ }
1068
+
1069
+
1070
+ //
1071
+ // ### Polyfilling
1072
+ //
1073
+
1074
+ // Add a polyFill to an object
1075
+ function polyFill(obj, name, makeReplacement) {
1076
+ var original = obj[name];
1077
+ var replacement = makeReplacement(original);
1078
+ obj[name] = replacement;
1079
+
1080
+ if (typeof BUGSNAG_TESTING !== "undefined" && window.undo) {
1081
+ window.undo.push(function () {
1082
+ obj[name] = original;
1083
+ });
1084
+ }
1085
+ }
1086
+
1087
+ if (getSetting("autoNotify", true)) {
1088
+ //
1089
+ // ### Automatic error notification
1090
+ //
1091
+ // Attach to `window.onerror` events and notify Bugsnag when they happen.
1092
+ // These are mostly js compile/parse errors, but on some browsers all
1093
+ // "uncaught" exceptions will fire this event.
1094
+ //
1095
+ polyFill(window, "onerror", function (_super) {
1096
+ // Keep a reference to any existing `window.onerror` handler
1097
+ if (typeof BUGSNAG_TESTING !== "undefined") {
1098
+ self._onerror = _super;
1099
+ }
1100
+
1101
+ return function bugsnag(message, url, lineNo, charNo, exception) {
1102
+ var shouldNotify = getSetting("autoNotify", true);
1103
+ var metaData = {};
1104
+
1105
+ // IE 6+ support.
1106
+ if (!charNo && window.event) {
1107
+ charNo = window.event.errorCharacter;
1108
+ }
1109
+
1110
+ addScriptToMetaData(metaData);
1111
+ lastScript = null;
1112
+
1113
+ if (shouldNotify && !ignoreOnError) {
1114
+ var name = exception && exception.name || "window.onerror";
1115
+ sendToBugsnag({
1116
+ name: name,
1117
+ message: message,
1118
+ file: url,
1119
+ lineNumber: lineNo,
1120
+ columnNumber: charNo,
1121
+ stacktrace: (exception && stacktraceFromException(exception)) || generateStacktrace(),
1122
+ severity: "error"
1123
+ }, metaData);
1124
+
1125
+ // add the error to the breadcrumbs
1126
+ if (getBreadcrumbSetting("autoBreadcrumbsErrors")) {
1127
+ self.leaveBreadcrumb({
1128
+ type: "error",
1129
+ name: name,
1130
+ metaData: {
1131
+ severity: "error",
1132
+ file: url,
1133
+ message: message,
1134
+ line: lineNo
1135
+ }
1136
+ });
1137
+ }
1138
+ }
1139
+
1140
+ if (typeof BUGSNAG_TESTING !== "undefined") {
1141
+ _super = self._onerror;
1142
+ }
1143
+
1144
+ // Fire the existing `window.onerror` handler, if one exists
1145
+ if (_super) {
1146
+ _super(message, url, lineNo, charNo, exception);
1147
+ }
1148
+ };
1149
+ });
1150
+
1151
+ var hijackTimeFunc = function (_super) {
1152
+ // Note, we don't do `_super.call` because that doesn't work on IE 8,
1153
+ // luckily this is implicitly window so it just works everywhere.
1154
+ //
1155
+ // setTimout in all browsers except IE <9 allows additional parameters
1156
+ // to be passed, so in order to support these without resorting to call/apply
1157
+ // we need an extra layer of wrapping.
1158
+ return function (f, t) {
1159
+ if (typeof f === "function") {
1160
+ f = wrap(f);
1161
+ var args = Array.prototype.slice.call(arguments, 2);
1162
+ return _super(function () {
1163
+ f.apply(this, args);
1164
+ }, t);
1165
+ } else {
1166
+ return _super(f, t);
1167
+ }
1168
+ };
1169
+ };
1170
+
1171
+ polyFill(window, "setTimeout", hijackTimeFunc);
1172
+ polyFill(window, "setInterval", hijackTimeFunc);
1173
+
1174
+ if (window.requestAnimationFrame) {
1175
+ polyFill(window, "requestAnimationFrame", function (_super) {
1176
+ return function (callback) {
1177
+ return _super(wrap(callback));
1178
+ };
1179
+ });
1180
+ }
1181
+
1182
+ if (window.setImmediate) {
1183
+ polyFill(window, "setImmediate", function (_super) {
1184
+ return function () {
1185
+ var args = Array.prototype.slice.call(arguments);
1186
+ args[0] = wrap(args[0]);
1187
+ return _super.apply(this, args);
1188
+ };
1189
+ });
1190
+ }
1191
+
1192
+ // EventTarget is all that's required in modern chrome/opera
1193
+ // EventTarget + Window + ModalWindow is all that's required in modern FF (there are a few Moz prefixed ones that we're ignoring)
1194
+ // The rest is a collection of stuff for Safari and IE 11. (Again ignoring a few MS and WebKit prefixed things)
1195
+ "EventTarget Window Node ApplicationCache AudioTrackList ChannelMergerNode CryptoOperation EventSource FileReader HTMLUnknownElement IDBDatabase IDBRequest IDBTransaction KeyOperation MediaController MessagePort ModalWindow Notification SVGElementInstance Screen TextTrack TextTrackCue TextTrackList WebSocket WebSocketWorker Worker XMLHttpRequest XMLHttpRequestEventTarget XMLHttpRequestUpload".replace(/\w+/g, function (global) {
1196
+ var prototype = window[global] && window[global].prototype;
1197
+ if (prototype && prototype.hasOwnProperty && prototype.hasOwnProperty("addEventListener")) {
1198
+ polyFill(prototype, "addEventListener", function (_super) {
1199
+ return function (e, f, capture, secure) {
1200
+ // HTML lets event-handlers be objects with a handlEvent function,
1201
+ // we need to change f.handleEvent here, as self.wrap will ignore f.
1202
+ try {
1203
+ if (f && f.handleEvent) {
1204
+ f.handleEvent = wrap(f.handleEvent, {eventHandler: true});
1205
+ }
1206
+ } catch (err) {
1207
+ // When selenium is installed, we sometimes get 'Permission denied to access property "handleEvent"'
1208
+ // Because this catch is around Bugsnag library code, it won't catch any user errors
1209
+ log(err);
1210
+ }
1211
+ return _super.call(this, e, wrap(f, {eventHandler: true}), capture, secure);
1212
+ };
1213
+ });
1214
+
1215
+ // We also need to hack removeEventListener so that you can remove any
1216
+ // event listeners.
1217
+ polyFill(prototype, "removeEventListener", function (_super) {
1218
+ return function (e, f, capture, secure) {
1219
+ _super.call(this, e, f, capture, secure);
1220
+ return _super.call(this, e, wrap(f), capture, secure);
1221
+ };
1222
+ });
1223
+ }
1224
+ });
1225
+ }
1226
+
1227
+ // setup auto breadcrumb tracking
1228
+ setupClickBreadcrumbs();
1229
+ setupConsoleBreadcrumbs();
1230
+ setupNavigationBreadcrumbs();
1231
+
1232
+ // Leave the initial breadcrumb
1233
+ if (getSetting("autoBreadcrumbs", true)) {
1234
+ self.leaveBreadcrumb({ type: "navigation", name: "Bugsnag Loaded" });
1235
+ }
1236
+
1237
+ window.Bugsnag = self;
1238
+ // If people are using a javascript loader, we should integrate with it.
1239
+ // We don't want to defer instrumenting their code with callbacks however,
1240
+ // so we slightly abuse the intent and continue with our plan of polyfilling
1241
+ // the browser whether or not they ever actually require the module.
1242
+ // This has the nice side-effect of continuing to work when people are using
1243
+ // AMD but loading Bugsnag via a CDN.
1244
+ // It has the not-so-nice side-effect of polluting the global namespace, but
1245
+ // you can easily call Bugsnag.noConflict() to fix that.
1246
+ if (typeof define === "function" && define.amd) {
1247
+ // AMD
1248
+ define([], function () {
1249
+ return self;
1250
+ });
1251
+ } else if (typeof module === "object" && typeof module.exports === "object") {
1252
+ // CommonJS/Browserify
1253
+ module.exports = self;
1254
+ }
1255
+ })(window, window.Bugsnag);
1256
+