/**
* general function, usually for data manipulation pages
*
*/
import { AJAX } from '../ajax';
import { PMA_Messages as PMA_messages } from '../variables/export_variables';
import PMA_commonParams from '../variables/common_params';
import { PMA_ajaxShowMessage } from '../utils/show_ajax_messages';
import { PMA_getImage } from '../functions/get_image';
import TraceKit from 'tracekit';
// import { jQuery as $ } from '../utils/extend_jquery';
/**
* This Object uses the library TraceKit to generate the backtrace of the
* error but since we are using webpack and the development and production
* mode have different url leading to cross origin query, the error
* report cannot be generated in development mode with context.
* The context will be null in deveopment mode.
*
* @see TraceKit url checking implemented to avoid Cross Origin
* https://github.com/csnover/TraceKit/blob/d38d48765f089309e4f4c7a1baacd96dd0534187/tracekit.js#L445
*
* @see TraceKit How context of the error is being gennerated
* https://github.com/csnover/TraceKit/blob/d38d48765f089309e4f4c7a1baacd96dd0534187/tracekit.js#L499
*
* Due to usage of webpack, the production output is creating backtrace context
* containing single line only and that single line is the complete generated
* webpack bundle in the uglify mode.
*/
var ErrorReport = {
/**
* @var object stores the last exception info
*/
_last_exception: null,
/**
* handles thrown error exceptions based on user preferences
*
* @return void
*/
error_handler: function (exception) {
console.error(exception);
if (exception.name === null || typeof(exception.name) === 'undefined') {
exception.name = ErrorReport._extractExceptionName(exception);
}
ErrorReport._last_exception = exception;
$.get('error_report.php', {
ajax_request: true,
server: PMA_commonParams.get('server'),
get_settings: true,
exception_type: 'js'
}, function (data) {
if (data.success !== true) {
PMA_ajaxShowMessage(data.error, false);
return;
}
if (data.report_setting === 'ask') {
ErrorReport._showErrorNotification();
} else if (data.report_setting === 'always') {
var report_data = ErrorReport._get_report_data(exception);
var post_data = $.extend(report_data, {
send_error_report: true,
automatic: true
});
$.post('error_report.php', post_data, function (data) {
if (data.success === false) {
// in the case of an error, show the error message returned.
PMA_ajaxShowMessage(data.error, false);
} else {
PMA_ajaxShowMessage(data.message, false);
}
});
}
});
},
/**
* Shows the modal dialog previewing the report
*
* @param exception object error report info
*
* @return void
*/
_showReportDialog: function (exception) {
var report_data = ErrorReport._get_report_data(exception);
/* Remove the hidden dialogs if there are*/
if ($('#error_report_dialog').length !== 0) {
$('#error_report_dialog').remove();
}
var $div = $('<div id="error_report_dialog"></div>');
$div.css('z-index', '1000');
var button_options = {};
button_options[PMA_messages.strSendErrorReport] = function () {
var $dialog = $(this);
var post_data = $.extend(report_data, {
send_error_report: true,
description: $('#report_description').val(),
always_send: $('#always_send_checkbox')[0].checked
});
$.post('error_report.php', post_data, function (data) {
$dialog.dialog('close');
if (data.success === false) {
// in the case of an error, show the error message returned.
PMA_ajaxShowMessage(data.error, false);
} else {
PMA_ajaxShowMessage(data.message, 3000);
}
});
};
button_options[PMA_messages.strCancel] = function () {
$(this).dialog('close');
};
$.post('error_report.php', report_data, function (data) {
if (data.success === false) {
// in the case of an error, show the error message returned.
PMA_ajaxShowMessage(data.error, false);
} else {
// Show dialog if the request was successful
$div
.append(data.message)
.dialog({
title: PMA_messages.strSubmitErrorReport,
width: 650,
modal: true,
buttons: button_options,
close: function () {
$(this).remove();
}
});
}
}); // end $.get()
},
/**
* Shows the small notification that asks for user permission
*
* @return void
*/
_showErrorNotification: function () {
ErrorReport._removeErrorNotification();
var $div = $(
'<div style="position:fixed;bottom:0;left:0;right:0;margin:0;' +
'z-index:1000" class="error" id="error_notification"></div>'
).append(
PMA_getImage('s_error') + PMA_messages.strErrorOccurred
);
var $buttons = $('<div class="floatright"></div>');
var button_html = '<button id="show_error_report">';
button_html += PMA_messages.strShowReportDetails;
button_html += '</button>';
button_html += '<a id="change_error_settings">';
button_html += PMA_getImage('s_cog', PMA_messages.strChangeReportSettings);
button_html += '</a>';
button_html += '<a href="#" id="ignore_error">';
button_html += PMA_getImage('b_close', PMA_messages.strIgnore);
button_html += '</a>';
$buttons.html(button_html);
$div.append($buttons);
$div.appendTo(document.body);
$(document).on('click', '#change_error_settings', ErrorReport._redirect_to_settings);
$(document).on('click', '#show_error_report', ErrorReport._createReportDialog);
$(document).on('click', '#ignore_error', ErrorReport._removeErrorNotification);
},
/**
* Removes the notification if it was displayed before
*
* @return void
*/
_removeErrorNotification: function (e) {
if (e) {
// don't remove the hash fragment by navigating to #
e.preventDefault();
}
$('#error_notification').fadeOut(function () {
$(this).remove();
});
},
/**
* Extracts Exception name from message if it exists
*
* @return String
*/
_extractExceptionName: function (exception) {
if (exception.message === null || typeof(exception.message) === 'undefined') {
return '';
}
var reg = /([a-zA-Z]+):/;
var regex_result = reg.exec(exception.message);
if (regex_result && regex_result.length === 2) {
return regex_result[1];
}
return '';
},
/**
* Shows the modal dialog previewing the report
*
* @return void
*/
_createReportDialog: function () {
ErrorReport._removeErrorNotification();
ErrorReport._showReportDialog(ErrorReport._last_exception);
},
/**
* Redirects to the settings page containing error report
* preferences
*
* @return void
*/
_redirect_to_settings: function () {
window.location.href = 'prefs_forms.php?form=Features';
},
/**
* Returns the report data to send to the server
*
* @param exception object exception info
*
* @return object
*/
_get_report_data: function (exception) {
var report_data = {
'ajax_request': true,
'exception': exception,
'current_url': window.location.href,
'exception_type': 'js'
};
if (AJAX.scriptHandler._scripts.length > 0) {
report_data.scripts = AJAX.scriptHandler._scripts.map(
function (script) {
return script;
}
);
}
return report_data;
},
/**
* Wraps all global functions that start with PMA_
*
* @return void
*
* This function needs to be changes as no objects are available
* in the global scope so window does not contain any function
*/
wrap_global_functions: function () {
for (var key in window) {
if (key.indexOf('PMA_') === 0) {
var global = window[key];
if (typeof(global) === 'function') {
window[key] = ErrorReport.wrap_function(global);
}
}
}
},
/**
* Wraps given function in error reporting code and returns wrapped function
*
* @param func function to be wrapped
*
* @return function
*/
wrap_function: function (func) {
if (!func.wrapped) {
var new_func = function () {
try {
return func.apply(this, arguments);
} catch (x) {
TraceKit.report(x);
}
};
new_func.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)
new_func.guid = func.guid = func.guid || new_func.guid || jQuery.guid++;
return new_func;
} else {
return func;
}
},
/**
* Automatically wraps the callback in AJAX.registerOnload
*
* @return void
*/
_wrap_ajax_onload_callback: function () {
var oldOnload = AJAX.registerOnload;
AJAX.registerOnload = function (file, func) {
func = ErrorReport.wrap_function(func);
oldOnload.call(this, file, func);
};
},
/**
* Automatically wraps the callback in $.fn.on
*
* @return void
*
* Since we are exporting the instance of jQuery from
* '/util/extend_jquery', therefore this method will work
* fine and wrap the callback of $.fn.on of jQuery instance.
*/
_wrap_$_on_callback: function () {
var oldOn = $.fn.on;
$.fn.on = function () {
for (var i = 1; i <= 3; i++) {
if (typeof(arguments[i]) === 'function') {
arguments[i] = ErrorReport.wrap_function(arguments[i]);
break;
}
}
return oldOn.apply(this, arguments);
};
},
/**
* Wraps all global functions that start with PMA_
* also automatically wraps the callback in AJAX.registerOnload
*
* @return void
*/
set_up_error_reporting: function () {
ErrorReport.wrap_global_functions();
ErrorReport._wrap_ajax_onload_callback();
ErrorReport._wrap_$_on_callback();
}
};
export default ErrorReport;