1 |
|
2 | (function (exports) {
|
3 | 'use strict';
|
4 |
|
5 | function getScreenSize() {
|
6 | return [screen.width, screen.height, screen.colorDepth].join('×');
|
7 | }
|
8 | function getScreenOrientation() {
|
9 | return typeof screen.orientation === 'string' ? screen.orientation : screen.orientation.type;
|
10 | }
|
11 | function copyTextToClipboard(text) {
|
12 | var textarea = document.createElement('textarea');
|
13 | textarea.value = text;
|
14 | document.body.appendChild(textarea);
|
15 | try {
|
16 | textarea.select();
|
17 | document.execCommand('copy');
|
18 | }
|
19 | catch (e) {
|
20 | alert('Copying text is not supported in this browser.');
|
21 | }
|
22 | document.body.removeChild(textarea);
|
23 | }
|
24 | function injectStyle(style) {
|
25 | var styleNode = document.createElement('style');
|
26 | document.body.appendChild(styleNode);
|
27 | styleNode.textContent = style;
|
28 | return styleNode;
|
29 | }
|
30 |
|
31 | function createElem(data) {
|
32 | var elem = document.createElement(data.tag || 'div');
|
33 | if (data.props) {
|
34 | addProps(elem, data.props);
|
35 | }
|
36 | elem.className = buildElemClass(data.name);
|
37 | data.container.appendChild(elem);
|
38 | return elem;
|
39 | }
|
40 | function addProps(elem, props) {
|
41 | Object.keys(props).forEach(function (key) {
|
42 | elem[key] = props[key];
|
43 | });
|
44 | }
|
45 | function buildElemClass(name, mod) {
|
46 | var elemName = 'show-js-error';
|
47 | if (name) {
|
48 | elemName += '__' + name;
|
49 | }
|
50 | var className = elemName;
|
51 | if (mod) {
|
52 | Object.keys(mod).forEach(function (modName) {
|
53 | var modValue = mod[modName];
|
54 | if (modValue === false || modValue === null || modValue === undefined || modValue === '') {
|
55 | return;
|
56 | }
|
57 | if (mod[modName] === true) {
|
58 | className += ' ' + elemName + '_' + modName;
|
59 | }
|
60 | else {
|
61 | className += ' ' + elemName + '_' + modName + '_' + modValue;
|
62 | }
|
63 | });
|
64 | }
|
65 | return className;
|
66 | }
|
67 |
|
68 | function getStack(error) {
|
69 | return error && error.stack || '';
|
70 | }
|
71 | function getMessage(error) {
|
72 | return error && error.message || '';
|
73 | }
|
74 | function getValue(value, defaultValue) {
|
75 | return typeof value === 'undefined' ? defaultValue : value;
|
76 | }
|
77 | function getFilenameWithPosition(error) {
|
78 | if (!error) {
|
79 | return '';
|
80 | }
|
81 | var text = error.filename || '';
|
82 | if (typeof error.lineno !== 'undefined') {
|
83 | text += ':' + getValue(error.lineno, '');
|
84 | if (typeof error.colno !== 'undefined') {
|
85 | text += ':' + getValue(error.colno, '');
|
86 | }
|
87 | }
|
88 | return text;
|
89 | }
|
90 |
|
91 | var STYLE = '.show-js-error{background:#ffc1cc;bottom:15px;color:#000;font-family:Arial,sans-serif;font-size:13px;left:15px;max-width:90vw;min-width:15em;opacity:1;position:fixed;transition:opacity .2s ease-out;transition-delay:0s;visibility:visible;z-index:10000000}.show-js-error_size_big{transform:scale(2) translate(25%,-25%)}.show-js-error_hidden{opacity:0;transition:opacity .3s,visibility 0s linear .3s;visibility:hidden}.show-js-error__title{background:#f66;color:#fff;font-weight:700;padding:4px 30px 4px 7px}.show-js-error__title_no-errors{background:#6b6}.show-js-error__message{cursor:pointer;display:inline}.show-js-error__message:before{background-color:#eee;border-radius:10px;content:"+";display:inline-block;font-size:10px;height:10px;line-height:10px;margin-bottom:2px;margin-right:5px;text-align:center;vertical-align:middle;width:10px}.show-js-error__body_detailed .show-js-error__message:before{content:"-"}.show-js-error__body_no-stack .show-js-error__message:before{display:none}.show-js-error__body_detailed .show-js-error__filename{display:block}.show-js-error__body_no-stack .show-js-error__filename{display:none}.show-js-error__close{color:#fff;cursor:pointer;font-size:20px;line-height:20px;padding:3px;position:absolute;right:2px;top:0}.show-js-error__body{line-height:19px;padding:5px 8px}.show-js-error__body_hidden{display:none}.show-js-error__filename{background:#ffe1ec;border:1px solid #faa;display:none;margin:3px 0 3px -2px;max-height:15em;overflow-y:auto;padding:5px;white-space:pre-wrap}.show-js-error__actions{border-top:1px solid #faa;margin-top:5px;padding:5px 0 3px}.show-js-error__actions_hidden{display:none}.show-js-error__arrows{margin-left:8px;white-space:nowrap}.show-js-error__arrows_hidden{display:none}.show-js-error__copy,.show-js-error__next,.show-js-error__num,.show-js-error__prev,.show-js-error__report{font-size:12px}.show-js-error__report_hidden{display:none}.show-js-error__next{margin-left:1px}.show-js-error__num{margin-left:5px;margin-right:5px}.show-js-error__copy,.show-js-error__report{margin-right:3px}.show-js-error input{padding:1px 2px}.show-js-error a,.show-js-error a:visited{color:#000;text-decoration:underline}.show-js-error a:hover{text-decoration:underline}';
|
92 | var ShowJSError = (function () {
|
93 | function ShowJSError() {
|
94 | var _this = this;
|
95 | this.elems = {};
|
96 | this.state = {
|
97 | appended: false,
|
98 | detailed: false,
|
99 | errorIndex: 0,
|
100 | errorBuffer: [],
|
101 | };
|
102 | this.onerror = function (event) {
|
103 | var error = event.error ? event.error : event;
|
104 | console.log(1, event);
|
105 | _this.pushError({
|
106 | title: 'JavaScript Error',
|
107 | message: error.message,
|
108 | filename: error.filename,
|
109 | colno: error.colno,
|
110 | lineno: error.lineno,
|
111 | stack: error.stack,
|
112 | });
|
113 | };
|
114 | this.onsecuritypolicyviolation = function (error) {
|
115 | _this.pushError({
|
116 | title: 'CSP Error',
|
117 | message: "blockedURI: ".concat(error.blockedURI || '', "\n violatedDirective: ").concat(error.violatedDirective, " || ''\n originalPolicy: ").concat(error.originalPolicy || ''),
|
118 | colno: error.columnNumber,
|
119 | filename: error.sourceFile,
|
120 | lineno: error.lineNumber,
|
121 | });
|
122 | };
|
123 | this.onunhandledrejection = function (error) {
|
124 | _this.pushError({
|
125 | title: 'Unhandled promise rejection',
|
126 | message: error.reason.message,
|
127 | colno: error.reason.colno,
|
128 | filename: error.reason.filename,
|
129 | lineno: error.reason.lineno,
|
130 | stack: error.reason.stack,
|
131 | });
|
132 | };
|
133 | this.appendToBody = function () {
|
134 | document.removeEventListener('DOMContentLoaded', _this.appendToBody, false);
|
135 | if (_this.elems.container) {
|
136 | _this.styleNode = injectStyle(STYLE);
|
137 | document.body.appendChild(_this.elems.container);
|
138 | }
|
139 | };
|
140 | this.settings = this.prepareSettings();
|
141 | window.addEventListener('error', this.onerror, false);
|
142 | window.addEventListener('unhandledrejection', this.onunhandledrejection, false);
|
143 | document.addEventListener('securitypolicyviolation', this.onsecuritypolicyviolation, false);
|
144 | }
|
145 | ShowJSError.prototype.destruct = function () {
|
146 | var _a;
|
147 | window.removeEventListener('error', this.onerror, false);
|
148 | window.removeEventListener('unhandledrejection', this.onunhandledrejection, false);
|
149 | document.removeEventListener('securitypolicyviolation', this.onsecuritypolicyviolation, false);
|
150 | document.removeEventListener('DOMContentLoaded', this.appendToBody, false);
|
151 | if (document.body && this.elems.container) {
|
152 | document.body.removeChild(this.elems.container);
|
153 | }
|
154 | this.state.errorBuffer = [];
|
155 | this.elems = {};
|
156 | if (this.styleNode) {
|
157 | (_a = this.styleNode.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(this.styleNode);
|
158 | this.styleNode = undefined;
|
159 | }
|
160 | };
|
161 | ShowJSError.prototype.setSettings = function (settings) {
|
162 | this.settings = this.prepareSettings(settings);
|
163 | if (this.state.appended) {
|
164 | this.updateUI();
|
165 | }
|
166 | };
|
167 | |
168 |
|
169 |
|
170 | ShowJSError.prototype.show = function (error) {
|
171 | if (!error) {
|
172 | this.showUI();
|
173 | return;
|
174 | }
|
175 | if (typeof error === 'string') {
|
176 | this.pushError({ message: error });
|
177 | }
|
178 | else {
|
179 | this.pushError(typeof error === 'object' ?
|
180 | error :
|
181 | new Error(error));
|
182 | }
|
183 | };
|
184 | |
185 |
|
186 |
|
187 | ShowJSError.prototype.hide = function () {
|
188 | if (this.elems.container) {
|
189 | this.elems.container.className = buildElemClass('', {
|
190 | size: this.settings.size,
|
191 | hidden: true
|
192 | });
|
193 | }
|
194 | };
|
195 | |
196 |
|
197 |
|
198 | ShowJSError.prototype.clear = function () {
|
199 | this.state.errorBuffer = [];
|
200 | this.state.detailed = false;
|
201 | this.setCurrentError(0);
|
202 | };
|
203 | |
204 |
|
205 |
|
206 | ShowJSError.prototype.toggleView = function () {
|
207 | this.state.detailed = !this.state.detailed;
|
208 | this.updateUI();
|
209 | };
|
210 | ShowJSError.prototype.prepareSettings = function (rawSettings) {
|
211 | var settings = rawSettings || {};
|
212 | return {
|
213 | size: settings.size || 'normal',
|
214 | reportUrl: settings.reportUrl || '',
|
215 | templateDetailedMessage: settings.templateDetailedMessage || '',
|
216 | };
|
217 | };
|
218 | ShowJSError.prototype.pushError = function (error) {
|
219 | this.state.errorBuffer.push(error);
|
220 | this.state.errorIndex = this.state.errorBuffer.length - 1;
|
221 | this.updateUI();
|
222 | };
|
223 | ShowJSError.prototype.appendUI = function () {
|
224 | var _this = this;
|
225 | var container = document.createElement('div');
|
226 | container.className = buildElemClass('', {
|
227 | size: this.settings.size,
|
228 | });
|
229 | this.elems.container = container;
|
230 | this.elems.close = createElem({
|
231 | name: 'close',
|
232 | props: {
|
233 | innerText: '×',
|
234 | onclick: function () {
|
235 | _this.hide();
|
236 | }
|
237 | },
|
238 | container: container
|
239 | });
|
240 | this.elems.title = createElem({
|
241 | name: 'title',
|
242 | props: {
|
243 | innerText: this.getTitle()
|
244 | },
|
245 | container: container
|
246 | });
|
247 | var body = createElem({
|
248 | name: 'body',
|
249 | container: container
|
250 | });
|
251 | this.elems.body = body;
|
252 | this.elems.message = createElem({
|
253 | name: 'message',
|
254 | props: {
|
255 | onclick: function () {
|
256 | _this.toggleView();
|
257 | }
|
258 | },
|
259 | container: body
|
260 | });
|
261 | this.elems.filename = createElem({
|
262 | name: 'filename',
|
263 | container: body
|
264 | });
|
265 | this.createActions(body);
|
266 | if (document.body) {
|
267 | document.body.appendChild(container);
|
268 | this.styleNode = injectStyle(STYLE);
|
269 | }
|
270 | else {
|
271 | document.addEventListener('DOMContentLoaded', this.appendToBody, false);
|
272 | }
|
273 | };
|
274 | ShowJSError.prototype.createActions = function (container) {
|
275 | var _this = this;
|
276 | var actions = createElem({
|
277 | name: 'actions',
|
278 | container: container
|
279 | });
|
280 | this.elems.actions = actions;
|
281 | createElem({
|
282 | tag: 'input',
|
283 | name: 'copy',
|
284 | props: {
|
285 | type: 'button',
|
286 | value: 'Copy',
|
287 | onclick: function () {
|
288 | var error = _this.getCurrentError();
|
289 | copyTextToClipboard(_this.getDetailedMessage(error));
|
290 | }
|
291 | },
|
292 | container: actions
|
293 | });
|
294 | var reportLink = createElem({
|
295 | tag: 'a',
|
296 | name: 'report-link',
|
297 | props: {
|
298 | href: '',
|
299 | target: '_blank'
|
300 | },
|
301 | container: actions
|
302 | });
|
303 | this.elems.reportLink = reportLink;
|
304 | this.elems.report = createElem({
|
305 | tag: 'input',
|
306 | name: 'report',
|
307 | props: {
|
308 | type: 'button',
|
309 | value: 'Report'
|
310 | },
|
311 | container: reportLink
|
312 | });
|
313 | this.createArrows(actions);
|
314 | };
|
315 | ShowJSError.prototype.createArrows = function (container) {
|
316 | var _this = this;
|
317 | var arrows = createElem({
|
318 | tag: 'span',
|
319 | name: 'arrows',
|
320 | container: container
|
321 | });
|
322 | this.elems.arrows = arrows;
|
323 | this.elems.prev = createElem({
|
324 | tag: 'input',
|
325 | name: 'prev',
|
326 | props: {
|
327 | type: 'button',
|
328 | value: '←',
|
329 | onclick: function () {
|
330 | _this.setCurrentError(_this.state.errorIndex - 1);
|
331 | }
|
332 | },
|
333 | container: arrows
|
334 | });
|
335 | this.elems.num = createElem({
|
336 | tag: 'span',
|
337 | name: 'num',
|
338 | props: {
|
339 | innerText: this.state.errorIndex + 1
|
340 | },
|
341 | container: arrows
|
342 | });
|
343 | this.elems.next = createElem({
|
344 | tag: 'input',
|
345 | name: 'next',
|
346 | props: {
|
347 | type: 'button',
|
348 | value: '→',
|
349 | onclick: function () {
|
350 | _this.setCurrentError(_this.state.errorIndex + 1);
|
351 | }
|
352 | },
|
353 | container: arrows
|
354 | });
|
355 | };
|
356 | ShowJSError.prototype.getDetailedMessage = function (error) {
|
357 | var text = [
|
358 | ['Title', this.getTitle(error)],
|
359 | ['Message', getMessage(error)],
|
360 | ['Filename', getFilenameWithPosition(error)],
|
361 | ['Stack', getStack(error)],
|
362 | ['Page url', window.location.href],
|
363 | ['Refferer', document.referrer],
|
364 | ['User-agent', navigator.userAgent],
|
365 | ['Screen size', getScreenSize()],
|
366 | ['Screen orientation', getScreenOrientation()],
|
367 | ['Cookie enabled', navigator.cookieEnabled]
|
368 | ].map(function (item) { return (item[0] + ': ' + item[1] + '\n'); }).join('');
|
369 | if (this.settings.templateDetailedMessage) {
|
370 | text = this.settings.templateDetailedMessage.replace(/\{message\}/, text);
|
371 | }
|
372 | return text;
|
373 | };
|
374 | ShowJSError.prototype.getTitle = function (error) {
|
375 | return error ? (error.title || 'Error') : 'No errors';
|
376 | };
|
377 | ShowJSError.prototype.showUI = function () {
|
378 | if (this.elems.container) {
|
379 | this.elems.container.className = buildElemClass('', {
|
380 | size: this.settings.size,
|
381 | });
|
382 | }
|
383 | };
|
384 | ShowJSError.prototype.hasStack = function () {
|
385 | var error = this.getCurrentError();
|
386 | return error && (error.stack || error.filename);
|
387 | };
|
388 | ShowJSError.prototype.getCurrentError = function () {
|
389 | return this.state.errorBuffer[this.state.errorIndex];
|
390 | };
|
391 | ShowJSError.prototype.setCurrentError = function (index) {
|
392 | var length = this.state.errorBuffer.length;
|
393 | var newIndex = index;
|
394 | if (newIndex > length - 1) {
|
395 | newIndex = length - 1;
|
396 | }
|
397 | else if (newIndex < 0) {
|
398 | newIndex = 0;
|
399 | }
|
400 | this.state.errorIndex = newIndex;
|
401 | this.updateUI();
|
402 | };
|
403 | ShowJSError.prototype.updateUI = function () {
|
404 | var error = this.getCurrentError();
|
405 | if (!this.state.appended) {
|
406 | this.state.appended = true;
|
407 | this.appendUI();
|
408 | }
|
409 | if (this.elems.body) {
|
410 | this.elems.body.className = buildElemClass('body', {
|
411 | detailed: this.state.detailed,
|
412 | 'no-stack': !this.hasStack(),
|
413 | hidden: !error,
|
414 | });
|
415 | }
|
416 | if (this.elems.title) {
|
417 | this.elems.title.innerText = this.getTitle(error);
|
418 | this.elems.title.className = buildElemClass('title', {
|
419 | 'no-errors': !error
|
420 | });
|
421 | }
|
422 | if (this.elems.message) {
|
423 | this.elems.message.innerText = getMessage(error);
|
424 | }
|
425 | if (this.elems.actions) {
|
426 | this.elems.actions.className = buildElemClass('actions', { hidden: !error });
|
427 | }
|
428 | if (this.elems.reportLink) {
|
429 | this.elems.reportLink.className = buildElemClass('report', {
|
430 | hidden: !this.settings.reportUrl
|
431 | });
|
432 | }
|
433 | if (this.elems.reportLink) {
|
434 | this.elems.reportLink.href = this.settings.reportUrl
|
435 | .replace(/\{title\}/, encodeURIComponent(getMessage(error)))
|
436 | .replace(/\{body\}/, encodeURIComponent(this.getDetailedMessage(error)));
|
437 | }
|
438 | if (this.elems.filename) {
|
439 | this.elems.filename.className = buildElemClass('filename', { hidden: !error });
|
440 | this.elems.filename.innerText = getStack(error) || getFilenameWithPosition(error);
|
441 | }
|
442 | this.updateArrows(error);
|
443 | this.showUI();
|
444 | };
|
445 | ShowJSError.prototype.updateArrows = function (error) {
|
446 | var length = this.state.errorBuffer.length;
|
447 | var errorIndex = this.state.errorIndex;
|
448 | if (this.elems.arrows) {
|
449 | this.elems.arrows.className = buildElemClass('arrows', { hidden: !error });
|
450 | }
|
451 | if (this.elems.prev) {
|
452 | this.elems.prev.disabled = !errorIndex;
|
453 | }
|
454 | if (this.elems.num) {
|
455 | this.elems.num.innerText = (errorIndex + 1) + '\u2009/\u2009' + length;
|
456 | }
|
457 | if (this.elems.next) {
|
458 | this.elems.next.disabled = errorIndex === length - 1;
|
459 | }
|
460 | };
|
461 | return ShowJSError;
|
462 | }());
|
463 |
|
464 | var showJSError = new ShowJSError();
|
465 | window.showJSError = showJSError;
|
466 |
|
467 | exports.showJSError = showJSError;
|
468 |
|
469 | return exports;
|
470 |
|
471 | })({});
|