You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
320 lines
9.1 KiB
320 lines
9.1 KiB
/* global TraceKit */ |
|
// js/vendor/tracekit.js |
|
|
|
/** |
|
* general function, usually for data manipulation pages |
|
* |
|
*/ |
|
var ErrorReport = { |
|
/** |
|
* @var {object}, stores the last exception info |
|
*/ |
|
lastException: null, |
|
|
|
/** |
|
* @var object stores the Error Report Data to prevent unnecessary data fetching |
|
*/ |
|
errorReportData: null, |
|
|
|
/** |
|
* @var object maintains unique keys already used |
|
*/ |
|
keyDict: {}, |
|
|
|
/** |
|
* handles thrown error exceptions based on user preferences |
|
* |
|
* @param {object} data |
|
* @param {any} exception |
|
* @return {void} |
|
*/ |
|
errorDataHandler: function (data, exception) { |
|
if (data.success !== true) { |
|
Functions.ajaxShowMessage(data.error, false); |
|
return; |
|
} |
|
|
|
if (data.report_setting === 'ask') { |
|
ErrorReport.showErrorNotification(); |
|
} else if (data.report_setting === 'always') { |
|
var reportData = ErrorReport.getReportData(exception); |
|
var postData = $.extend(reportData, { |
|
'send_error_report': true, |
|
'automatic': true |
|
}); |
|
$.post('index.php?route=/error-report', postData, function (data) { |
|
if (data.success === false) { |
|
// in the case of an error, show the error message returned. |
|
Functions.ajaxShowMessage(data.error, false); |
|
} else { |
|
Functions.ajaxShowMessage(data.message, false); |
|
} |
|
}); |
|
} |
|
}, |
|
errorHandler: function (exception) { |
|
// issue: 14359 |
|
if (JSON.stringify(ErrorReport.lastException) === JSON.stringify(exception)) { |
|
return; |
|
} |
|
|
|
if (exception.name === null || typeof exception.name === 'undefined') { |
|
exception.name = ErrorReport.extractExceptionName(exception); |
|
} |
|
|
|
ErrorReport.lastException = exception; |
|
|
|
if (ErrorReport.errorReportData === null) { |
|
$.post('index.php?route=/error-report', { |
|
'ajax_request': true, |
|
'server': CommonParams.get('server'), |
|
'get_settings': true, |
|
'exception_type': 'js' |
|
}, function (data) { |
|
ErrorReport.errorReportData = data; |
|
ErrorReport.errorDataHandler(data, exception); |
|
}); |
|
} else { |
|
ErrorReport.errorDataHandler(ErrorReport.errorReportData, exception); |
|
} |
|
}, |
|
|
|
/** |
|
* Shows the modal dialog previewing the report |
|
* |
|
* @param exception object error report info |
|
* |
|
* @return {void} |
|
*/ |
|
showReportDialog: function (exception) { |
|
const reportData = ErrorReport.getReportData(exception); |
|
|
|
const sendErrorReport = function () { |
|
const postData = $.extend(reportData, { |
|
'send_error_report': true, |
|
'description': $('#errorReportDescription').val(), |
|
'always_send': $('#errorReportAlwaysSendCheckbox')[0].checked |
|
}); |
|
$.post('index.php?route=/error-report', postData, function (data) { |
|
if (data.success === false) { |
|
Functions.ajaxShowMessage(data.error, false); |
|
} else { |
|
Functions.ajaxShowMessage(data.message, 3000); |
|
} |
|
}); |
|
$('#errorReportModal').modal('hide'); |
|
}; |
|
|
|
$.post('index.php?route=/error-report', reportData).done(function (data) { |
|
// Delete the modal to refresh it in case the user changed SendErrorReports value |
|
if (document.getElementById('errorReportModal') !== null) { |
|
$('#errorReportModal').remove(); |
|
} |
|
|
|
$('body').append($(data.report_modal)); |
|
const $errorReportModal = $('#errorReportModal'); |
|
$errorReportModal.on('show.bs.modal', function () { |
|
// Prevents multiple onClick events |
|
$('#errorReportModalConfirm').off('click', sendErrorReport); |
|
$('#errorReportModalConfirm').on('click', sendErrorReport); |
|
$('#errorReportModal .modal-body').html(data.message); |
|
}); |
|
$errorReportModal.modal('show'); |
|
}); |
|
}, |
|
|
|
/** |
|
* Shows the small notification that asks for user permission |
|
* |
|
* @return {void} |
|
*/ |
|
showErrorNotification: function () { |
|
var key = Math.random().toString(36).substring(2, 12); |
|
|
|
while (key in ErrorReport.keyDict) { |
|
key = Math.random().toString(36).substring(2, 12); |
|
} |
|
|
|
ErrorReport.keyDict[key] = 1; |
|
var $div = $('<div class="alert alert-danger" role="alert" id="error_notification_' + key + '"></div>').append(Functions.getImage('s_error') + Messages.strErrorOccurred); |
|
var $buttons = $('<div class="float-end"></div>'); |
|
var buttonHtml = '<button class="btn btn-primary" id="show_error_report_' + key + '">'; |
|
buttonHtml += Messages.strShowReportDetails; |
|
buttonHtml += '</button>'; |
|
var settingsUrl = 'index.php?route=/preferences/features&server=' + CommonParams.get('server'); |
|
buttonHtml += '<a class="ajax" href="' + settingsUrl + '">'; |
|
buttonHtml += Functions.getImage('s_cog', Messages.strChangeReportSettings); |
|
buttonHtml += '</a>'; |
|
buttonHtml += '<a href="#" id="ignore_error_' + key + '" data-notification-id="' + key + '">'; |
|
buttonHtml += Functions.getImage('b_close', Messages.strIgnore); |
|
buttonHtml += '</a>'; |
|
$buttons.html(buttonHtml); |
|
$div.append($buttons); // eslint-disable-next-line compat/compat |
|
|
|
$div.appendTo(document.body); |
|
$(document).on('click', '#show_error_report_' + key, ErrorReport.createReportDialog); |
|
$(document).on('click', '#ignore_error_' + key, ErrorReport.removeErrorNotification); |
|
}, |
|
|
|
/** |
|
* Removes the notification if it was displayed before |
|
* |
|
* @param {Event} e |
|
* @return {void} |
|
*/ |
|
removeErrorNotification: function (e) { |
|
if (e) { |
|
// don't remove the hash fragment by navigating to # |
|
e.preventDefault(); |
|
} |
|
|
|
$('#error_notification_' + $(this).data('notification-id')).fadeOut(function () { |
|
$(this).remove(); |
|
}); |
|
}, |
|
|
|
/** |
|
* Extracts Exception name from message if it exists |
|
* |
|
* @param exception |
|
* @return {string} |
|
*/ |
|
extractExceptionName: function (exception) { |
|
if (exception.message === null || typeof exception.message === 'undefined') { |
|
return ''; |
|
} |
|
|
|
var reg = /([a-zA-Z]+):/; |
|
var regexResult = reg.exec(exception.message); |
|
|
|
if (regexResult && regexResult.length === 2) { |
|
return regexResult[1]; |
|
} |
|
|
|
return ''; |
|
}, |
|
|
|
/** |
|
* Shows the modal dialog previewing the report |
|
* |
|
* @return {void} |
|
*/ |
|
createReportDialog: function () { |
|
ErrorReport.removeErrorNotification(); |
|
ErrorReport.showReportDialog(ErrorReport.lastException); |
|
}, |
|
|
|
/** |
|
* Returns the report data to send to the server |
|
* |
|
* @param exception object exception info |
|
* |
|
* @return {object} |
|
*/ |
|
getReportData: function (exception) { |
|
if (exception && exception.stack && exception.stack.length) { |
|
for (var i = 0; i < exception.stack.length; i++) { |
|
var stack = exception.stack[i]; |
|
|
|
if (stack.context && stack.context.length) { |
|
for (var j = 0; j < stack.context.length; j++) { |
|
if (stack.context[j].length > 80) { |
|
stack.context[j] = stack.context[j].substring(-1, 75) + '//...'; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
var reportData = { |
|
'server': CommonParams.get('server'), |
|
'ajax_request': true, |
|
'exception': exception, |
|
'url': window.location.href, |
|
'exception_type': 'js' |
|
}; |
|
|
|
if (AJAX.scriptHandler.scripts.length > 0) { |
|
reportData.scripts = AJAX.scriptHandler.scripts.map(function (script) { |
|
return script; |
|
}); |
|
} |
|
|
|
return reportData; |
|
}, |
|
|
|
/** |
|
* Wraps given function in error reporting code and returns wrapped function |
|
* |
|
* @param {Function} func function to be wrapped |
|
* |
|
* @return {Function} |
|
*/ |
|
wrapFunction: function (func) { |
|
if (!func.wrapped) { |
|
var newFunc = function () { |
|
try { |
|
return func.apply(this, arguments); |
|
} catch (x) { |
|
TraceKit.report(x); |
|
} |
|
}; |
|
|
|
newFunc.wrapped = true; // Set guid of wrapped function same as original function, so it can be removed |
|
// See bug#4146 (problem with jquery draggable and sortable) |
|
|
|
newFunc.guid = func.guid = func.guid || newFunc.guid || jQuery.guid++; |
|
return newFunc; |
|
} else { |
|
return func; |
|
} |
|
}, |
|
|
|
/** |
|
* Automatically wraps the callback in AJAX.registerOnload |
|
* |
|
* @return {void} |
|
*/ |
|
wrapAjaxOnloadCallback: function () { |
|
var oldOnload = AJAX.registerOnload; |
|
|
|
AJAX.registerOnload = function (file, func) { |
|
var wrappedFunction = ErrorReport.wrapFunction(func); |
|
oldOnload.call(this, file, wrappedFunction); |
|
}; |
|
}, |
|
|
|
/** |
|
* Automatically wraps the callback in $.fn.on |
|
* |
|
* @return {void} |
|
*/ |
|
wrapJqueryOnCallback: function () { |
|
var oldOn = $.fn.on; |
|
|
|
$.fn.on = function () { |
|
for (var i = 1; i <= 3; i++) { |
|
if (typeof arguments[i] === 'function') { |
|
arguments[i] = ErrorReport.wrapFunction(arguments[i]); |
|
break; |
|
} |
|
} |
|
|
|
return oldOn.apply(this, arguments); |
|
}; |
|
}, |
|
|
|
/** |
|
* Wraps the callback in AJAX.registerOnload automatically |
|
* |
|
* @return {void} |
|
*/ |
|
setUpErrorReporting: function () { |
|
ErrorReport.wrapAjaxOnloadCallback(); |
|
ErrorReport.wrapJqueryOnCallback(); |
|
} |
|
}; |
|
AJAX.registerOnload('error_report.js', function () { |
|
TraceKit.report.subscribe(ErrorReport.errorHandler); |
|
ErrorReport.setUpErrorReporting(); |
|
}); |