Merge pull request #2038 from gabsource/feature/exception-beautifier

Follow up #2035 - Improve backend exception beautifier
This commit is contained in:
Samuel Georges 2016-05-25 05:42:12 +10:00
commit bfb010bc8c
9 changed files with 554 additions and 412 deletions

View File

@ -0,0 +1,99 @@
[data-plugin="exception-beautifier"] {
width: auto !important;
}
.plugin-exception-beautifier .beautifier-name {
display: block;
font-weight: bold;
font-size: 1.2em;
}
.plugin-exception-beautifier .beautifier-message {
display: block;
padding: 15px 0;
font-size: 1.1em;
}
.plugin-exception-beautifier .beautifier-stacktrace-title {
display: block;
margin-top: 15px;
font-weight: bold;
}
.plugin-exception-beautifier .beautifier-stacktrace-line {
display: block;
white-space: nowrap;
padding: 10px 0;
}
.plugin-exception-beautifier .beautifier-stacktrace-line:nth-child(even) {
background-color: #ffffff;
}
.plugin-exception-beautifier .beautifier-stacktrace-line:nth-child(odd) {
background-color: #f2f2f2;
}
.plugin-exception-beautifier .beautifier-stacktrace-line-number {
display: inline-block;
width: 40px;
margin-right: 15px;
text-align: right;
color: #000000;
}
.plugin-exception-beautifier .beautifier-stacktrace-line-internal {
color: #2C3E50;
}
.plugin-exception-beautifier .beautifier-stacktrace-line-function {
display: block;
margin-left: 55px;
}
.plugin-exception-beautifier .beautifier-file {
color: #204bff;
}
.plugin-exception-beautifier .beautifier-line-number {
color: #af0016;
}
.plugin-exception-beautifier .beautifier-string,
.plugin-exception-beautifier .beautifier-number,
.plugin-exception-beautifier .beautifier-class,
.plugin-exception-beautifier .beautifier-code,
.plugin-exception-beautifier .beautifier-function,
.plugin-exception-beautifier .beautifier-system-function {
font-family: monospace;
}
.plugin-exception-beautifier .beautifier-class,
.plugin-exception-beautifier .beautifier-function,
.plugin-exception-beautifier .beautifier-system-function {
font-weight: bold;
}
.plugin-exception-beautifier .beautifier-class {
color: #252623;
}
.plugin-exception-beautifier .beautifier-function {
color: #cc204d;
}
.plugin-exception-beautifier .beautifier-system-function {
color: #e33a37;
}
.plugin-exception-beautifier .beautifier-code {
color: #736b88;
}
.plugin-exception-beautifier .beautifier-string {
color: #0EA804;
}
.plugin-exception-beautifier .beautifier-number {
color: #2196F3;
}

View File

@ -1,83 +0,0 @@
[data-plugin="exception-beautifier"] {
width: auto !important;
}
.markup_exception_container {
overflow-x: scroll;
}
.markup_exception_name {
display: block;
font-weight: bold;
font-size: 1.2em;
}
.markup_exception_message {
display: block;
padding: 15px 0;
font-size: 1.1em;
}
.markup_exception_stacktrace_title {
display: block;
margin-top: 15px;
font-weight: bold;
}
.markup_exception_stacktrace_line {
display: block;
white-space: nowrap;
padding: 8px 0;
}
.markup_exception_stacktrace_line:nth-child(even) {
background-color: #ffffff;
}
.markup_exception_stacktrace_line:nth-child(odd) {
background-color: #f2f2f2;
}
.markup_exception_stacktrace_line_number {
display: inline-block;
width: 40px;
margin-right: 15px;
text-align: right;
color: black;
}
.markup_exception_stacktrace_line_internal {
color: #2C3E50;
}
.markup_exception_stacktrace_line_function {
display: block;
margin-left: 55px;
}
.markup_exception_file {
color: #0c7eff;
}
.markup_exception_line_number {
color: #af0016;
}
.markup_exception_class {
font-weight: bold;
font-family: monospace;
}
.markup_exception_function {
font-weight: bold;
color: #4d9a98;
font-family: monospace;
}
.markup_exception_function.--system {
color: #3d7a78;
}
.markup_exception_sign {
color: #4d9a98;
}

View File

@ -0,0 +1,300 @@
/*
* Exception Beautifier plugin
*/
+function ($) {
"use strict";
var ExceptionBeautifier = function (el, options) {
var self = this
self.$el = $(el)
self.options = options || {}
// Init
self.init()
}
ExceptionBeautifier.DEFAULTS = {}
ExceptionBeautifier.REGEX = {
phpline: /^(#[0-9]+)\s+(.+\.php)(?:\(([0-9]+)\))?\s*:(.*)/,
artisan: /^(#[0-9]+)\s+(.+artisan)(?:\(([0-9]+)\))?\s*:(.*)/,
internalLine: /^(#[0-9]+)\s+(\[internal function\]\s*:)(.*)/,
defaultLine: /^(#[0-9]+)\s*(.*)/,
className: /([a-z0-9]+\\[a-z0-9\\]+(?:\.\.\.)?)/gi,
filePath: /((?:[A-Z]:[/\\]|\/)?[\w/\\]+\.php)(\(([0-9]+)\)|:([0-9]+))?/gi,
staticCall: /::([^( ]+)\(([^()]*|(?:[^(]*\(.+\)[^)]*))\)/,
functionCall: /->([^(]+)\(([^()]*|(?:[^(]*\(.+\)[^)]*))\)/,
closureCall: /\{closure\}\(([^()]*|(?:[^(]*\(.+\)[^)]*))\)/
}
ExceptionBeautifier.extensions = []
ExceptionBeautifier.prototype.init = function () {
var self = this
ExceptionBeautifier.extensions.forEach(function (extension) {
if (typeof extension.onInit === 'function') {
extension.onInit(self)
}
})
self.$el.addClass('plugin-exception-beautifier')
self.$el.html(self.parseSource()).find('.beautifier-message-container').addClass('form-control')
}
ExceptionBeautifier.prototype.parseSource = function () {
var self = this,
source = self.$el.text(),
markup = {lines: []},
start = 0,
end
/*
* We only heavily parse stacktrace messages.
* Standard messages are only applied a simple transform : newline to <br> and tab/spaces indentation to &nbsp;
*/
if (source.indexOf('Stack trace:') < 0) {
source = '{exception-beautifier-message-container}{exception-beautifier-message}' + self.formatMessage(source) + '{/exception-beautifier-message}{/exception-beautifier-message-container}'
} else {
end = source.indexOf(':', start) + 1
markup.name = source.substring(start, end)
start = end
end = source.indexOf('Stack trace:', start)
markup.message = source.substring(start, end)
start = source.indexOf('#', end)
while ((end = source.indexOf('#', start + 1)) > 0) {
markup.lines.push(self.parseLine(source.substring(start, end)))
start = end
}
markup.lines.push(self.parseLine(source.substring(start)))
source = '{exception-beautifier-message-container}' +
'{exception-beautifier-name}' + markup.name + '{/exception-beautifier-name}' +
'{exception-beautifier-message}' + self.formatMessage(markup.message) + '{/exception-beautifier-message}' +
'{/exception-beautifier-message-container}' +
'{exception-beautifier-stacktrace-title}Stack trace:{/exception-beautifier-stacktrace-title}'
markup.lines.forEach(function (line) {
source += '{exception-beautifier-stacktrace-line}' + self.formatStackTraceLine(line) + '{/exception-beautifier-stacktrace-line}'
})
ExceptionBeautifier.extensions.forEach(function (extension) {
if (typeof extension.onParse === 'function') {
extension.onParse(self)
}
})
}
return self.buildMarkup('{exception-beautifier-container}' + source + '{/exception-beautifier-container}')
}
ExceptionBeautifier.prototype.parseLine = function (str) {
var line = {},
matches
if ((matches = str.match(ExceptionBeautifier.REGEX.phpline)) || (matches = str.match(ExceptionBeautifier.REGEX.artisan))) {
line.type = 'phpline'
line.number = $.trim(matches[1])
line.file = $.trim(matches[2])
line.lineNumber = $.trim(matches[3])
line.function = $.trim(matches[4])
}
else if (matches = str.match(ExceptionBeautifier.REGEX.internalLine)) {
line.type = 'internal'
line.number = $.trim(matches[1])
line.internal = $.trim(matches[2])
line.function = $.trim(matches[3])
} else if (matches = str.match(ExceptionBeautifier.REGEX.defaultLine)) {
line.type = 'default'
line.number = $.trim(matches[1])
line.function = $.trim(matches[2])
}
return line
}
ExceptionBeautifier.prototype.formatMessage = function (str) {
var self = this
return self.formatLineCode(
str
.replace(/^\s+/, '')
.replace(/\r|\n|\r\n/g, '{x-newline}')
.replace(/\t| {2}/g, '{x-tabulation}')
)
}
ExceptionBeautifier.prototype.formatFilePath = function (path, line) {
return '{exception-beautifier-file}' + path + '{/exception-beautifier-file}'
}
ExceptionBeautifier.prototype.formatStackTraceLine = function (line) {
var self = this
if (line.function) {
line.function = self.formatLineCode(line.function)
}
switch (line.type) {
case 'phpline':
return '{exception-beautifier-stacktrace-line-number}' + line.number + '{/exception-beautifier-stacktrace-line-number}' +
self.formatFilePath(line.file, line.lineNumber) +
'{exception-beautifier-line-number}(' + line.lineNumber + '){/exception-beautifier-line-number}: ' +
'{exception-beautifier-stacktrace-line-function}' + line.function + '{/exception-beautifier-stacktrace-line-function}'
case 'internal':
return '{exception-beautifier-stacktrace-line-number}' + line.number + '{/exception-beautifier-stacktrace-line-number}' +
'{exception-beautifier-stacktrace-line-internal}' + line.internal + '{/exception-beautifier-stacktrace-line-internal}' +
'{exception-beautifier-stacktrace-line-function}' + line.function + '{/exception-beautifier-stacktrace-line-function}'
case 'default':
return '{exception-beautifier-stacktrace-line-number}' + line.number + '{/exception-beautifier-stacktrace-line-number}' +
'{exception-beautifier-stacktrace-line-function}' + line.function + '{/exception-beautifier-stacktrace-line-function}'
}
return ''
}
ExceptionBeautifier.prototype.formatLineCode = function (str) {
var self = this
if (str.match(/^\s*(call_user_func|spl_autoload_call)/)) {
str = str.replace(/^\s*(?:call_user_func|spl_autoload_call)([^(]*)\((.*)\)/, function (str, suffix, parameters) {
return '{exception-beautifier-system-function}call_user_func' + suffix + '({/exception-beautifier-system-function}' +
self.formatFunctionParameters(parameters) +
'{exception-beautifier-system-function}){/exception-beautifier-system-function}'
})
} else if (str.match(ExceptionBeautifier.REGEX.closureCall)) {
str = str.replace(ExceptionBeautifier.REGEX.closureCall, function (str, parameters) {
return '{exception-beautifier-function}{closure}({/exception-beautifier-function}' +
self.formatFunctionParameters(parameters) +
'{exception-beautifier-function}){/exception-beautifier-function}'
})
} else if (str.match(ExceptionBeautifier.REGEX.functionCall)) {
str = str.replace(ExceptionBeautifier.REGEX.functionCall, function (str, functionName, parameters) {
return '{exception-beautifier-function}→' + functionName + '({/exception-beautifier-function}' +
self.formatFunctionParameters(parameters) +
'{exception-beautifier-function}){/exception-beautifier-function}'
})
} else if (str.match(ExceptionBeautifier.REGEX.staticCall)) {
str = str.replace(ExceptionBeautifier.REGEX.staticCall, function (str, functionName, parameters) {
return '{exception-beautifier-function}::' + functionName + '({/exception-beautifier-function}' +
self.formatFunctionParameters(parameters) +
'{exception-beautifier-function}){/exception-beautifier-function}'
})
}
str = str.replace(ExceptionBeautifier.REGEX.filePath, function (str, path, line, lineNumber, altLineNumber) {
return self.formatFilePath(path, (lineNumber || '') + (altLineNumber || '')) +
'{exception-beautifier-line-number}' + line + '{/exception-beautifier-line-number}'
})
str = str.replace(ExceptionBeautifier.REGEX.className, function (str, name) {
return '{exception-beautifier-class}' + name + '{/exception-beautifier-class}'
})
return str
}
ExceptionBeautifier.prototype.formatFunctionParameters = function (parameters) {
return parameters
.replace(/^([0-9]+)|([^a-z\\])([0-9]+)$|^([0-9]+)([^a-z\\])|([^a-z\\])([0-9]+)([^a-z\\])/g, '$2$6{exception-beautifier-number}$1$3$4$7{/exception-beautifier-number}$5$8')
.replace(/^Array$|([^a-z\\])Array$|^Array([^a-z\\])|([^a-z\\])Array([^a-z\\])/g, '$1$3{exception-beautifier-code}Array{/exception-beautifier-code}$2$4')
.replace(/^Closure$|(\()Closure(\))/g, '$1{exception-beautifier-code}Closure{/exception-beautifier-code}$2')
.replace(/Object\(([^)]+)\)/g, '{exception-beautifier-code}Object({/exception-beautifier-code}$1{exception-beautifier-code}){/exception-beautifier-code}')
.replace(/"((?:\\.|[^"])*)"/g, '{exception-beautifier-string}"$1"{/exception-beautifier-string}')
.replace(/'((?:\\.|[^'])*)'/g, '{exception-beautifier-string}\'$1\'{/exception-beautifier-string}')
}
ExceptionBeautifier.prototype.buildMarkup = function (str) {
var self = this,
start = str.indexOf('{exception-beautifier-'),
cssOffset = 'exception-beautifier-'.length,
end, endtag, tmp, matches, tag, html, css, attrs, markup = ''
if (start >= 0) {
if (start > 0) {
markup += self.buildMarkup(str.substring(0, start))
}
while (start >= 0) {
end = endtag = str.indexOf('}', start)
if ((tmp = str.indexOf(' ', start)) >= 0) {
end = Math.min(end, tmp)
}
tag = str.substring(start + 1, end)
end = str.indexOf('{/' + tag + '}', start)
start = str.indexOf('}', start)
if (end < 0) {
throw 'Markup error tag {' + tag + '} not closed'
}
html = 'span'
attrs = ''
css = tag
if (matches = tag.match(/(.+)#([a-z]+)$/)) {
css = matches[1]
html = matches[2]
}
css = 'beautifier-' + css.substr(cssOffset)
if (tmp >= 0 && tmp < endtag) {
attrs = str.substring(tmp, endtag)
}
markup += '<' + html + ' class="' + css + '"' + attrs + '>'
markup += self.buildMarkup(str.substring(start + 1, end))
markup += '</' + html + '>'
end = end + ('{/' + tag + '}').length
start = str.indexOf('{exception-beautifier-', end)
if (start > end || start < 0) {
markup += self.buildMarkup(str.substring(end, start < 0 ? undefined : start))
}
}
} else {
markup += $.oc.escapeHtmlString(str)
.replace(/\{x-newline\}/g, '<br>')
.replace(/\{x-tabulation\}/g, '&nbsp;&nbsp;')
}
return markup
}
// EXCEPTION BEAUTIFIER PLUGIN DEFINITION
// ============================
$.fn.exceptionBeautifier = function (option) {
var args = arguments,
result
this.each(function () {
var $this = $(this)
var data = $this.data('oc.exceptionBeautifier')
var options = $.extend({}, ExceptionBeautifier.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('oc.exceptionBeautifier', (data = new ExceptionBeautifier(this, options)))
if (typeof option == 'string') result = data[option].call($this)
if (typeof result != 'undefined') return false
})
return result ? result : this
}
$.fn.exceptionBeautifier.Constructor = ExceptionBeautifier
$(document).render(function () {
$('[data-plugin="exception-beautifier"]').exceptionBeautifier()
})
}(window.jQuery)

View File

@ -0,0 +1,141 @@
/*
* Exception Beautifier plugin - Links extension
*/
+function ($) {
"use strict";
var ExceptionBeautifier = $.fn.exceptionBeautifier.Constructor
ExceptionBeautifier.EDITORS = {
subl: {scheme: 'subl://open?url=file://%file&line=%line', name: 'Sublime (subl://)'},
txmt: {scheme: 'txmt://open/?url=file://%file&line=%line', name: 'TextMate (txmt://)'},
mvim: {scheme: 'mvim://open/?url=file://%file&line=%line', name: 'MacVim (mvim://)'},
phpstorm: {scheme: 'phpstorm://open?file=%file&line=%line', name: 'PhpStorm (phpstorm://)'},
editor: {scheme: 'editor://open/?file=%file&line=%line', name: 'Custom (editor://)'}
}
ExceptionBeautifier.REGEX.editor = /idelink:\/\/([^#]+)&([0-9]+)?/
ExceptionBeautifier.LINKER_POPUP_CONTENT = null
ExceptionBeautifier.extensions.push({
onInit: function (exceptionBeautfier) {
exceptionBeautfier.initEditorPopup()
},
onParse: function (exceptionBeautfier) {
exceptionBeautfier.$el.on('click', 'a', function () {
exceptionBeautfier.openWithEditor($(this).data('href'))
})
}
})
ExceptionBeautifier.prototype.initEditorPopup = function () {
if (!ExceptionBeautifier.LINKER_POPUP_CONTENT) {
var title = $.oc.lang.get('eventlog.editor.title'),
description = $.oc.lang.get('eventlog.editor.description'),
openWith = $.oc.lang.get('eventlog.editor.openWith'),
rememberChoice = $.oc.lang.get('eventlog.editor.rememberChoice'),
open = $.oc.lang.get('eventlog.editor.open'),
cancel = $.oc.lang.get('eventlog.editor.cancel'),
popup = $(' \
<div class="control-popup modal fade" id="exception-link-editor" tabindex="-1" role="dialog"> \
<div class="modal-dialog"> \
<div class="modal-content"> \
<div class="modal-header"> \
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button> \
<h4 class="modal-title">' + title + '</h4> \
</div> \
<div class="modal-body"> \
<p>' + description + '</p> \
<div class="form-group"> \
<label class="control-label">' + openWith + ':</label> \
<select class="form-control" name="select-exception-link-editor"></select> \
</div> \
<div class="checkbox custom-checkbox"> \
<input name="checkbox" value="1" type="checkbox" id="editor-remember-choice" /> \
<label for="editor-remember-choice">' + rememberChoice + '</label> \
</div> \
</div> \
<div class="modal-footer"> \ \
<button type="button" class="btn btn-primary" \
data-action="submit" data-dismiss="modal">' + open + '</button> \
<button type="button" class="btn btn-default" data-dismiss="popup">' + cancel + '</button> \
</div> \
</div> \
</div> \
</div>'
),
select = $('select', popup)
for (var key in ExceptionBeautifier.EDITORS) {
if (ExceptionBeautifier.EDITORS.hasOwnProperty(key)) {
select.append('<option value="' + key + '">' + $.oc.escapeHtmlString(ExceptionBeautifier.EDITORS[key].name) + '</option>')
}
}
ExceptionBeautifier.LINKER_POPUP_CONTENT = popup.html()
}
}
ExceptionBeautifier.prototype.openWithEditor = function (link) {
var self = this,
matches,
open = function (value) {
window.open(link.replace(
ExceptionBeautifier.REGEX.editor,
ExceptionBeautifier.EDITORS[value].scheme
.replace(/%file/, matches[1])
.replace(/%line/, matches[2])
), '_self')
}
if (matches = link.match(ExceptionBeautifier.REGEX.editor)) {
if (window.sessionStorage && window.sessionStorage['oc-exception-beautifier-editor']) {
open(window.sessionStorage['oc-exception-beautifier-editor'])
} else {
$.popup({content: ExceptionBeautifier.LINKER_POPUP_CONTENT})
.on('shown.oc.popup', function (event, source, popup) {
var select = $('select', popup)
self.initCustomSelect(select)
$('[data-action="submit"]', popup).on('click', function () {
if ($('#editor-remember-choice').prop('checked') && window.sessionStorage) {
window.sessionStorage['oc-exception-beautifier-editor'] = select.val()
}
open(select.val())
})
})
.on('hide.oc.popup', function (event, source, popup) {
$('[data-action]', popup).off('click')
})
}
}
}
ExceptionBeautifier.prototype.formatFilePath = function (path, line) {
var self = this
return '{exception-beautifier-file#a href="javascript:" data-href="idelink://' + encodeURIComponent(self.rewritePath(path)) + '&' + line + '"}' + path + '{/exception-beautifier-file#a}'
}
ExceptionBeautifier.prototype.rewritePath = function (path) {
return path
}
ExceptionBeautifier.prototype.initCustomSelect = function (select) {
if (Modernizr.touch) {
return
}
var options = {
minimumResultsForSearch: Infinity,
escapeMarkup: function (m) {
return m
}
}
select.select2(options)
}
}(window.jQuery)

View File

@ -1,208 +0,0 @@
/*
* Previews class
*/
+function ($) {
"use strict";
var ExceptionBeautifier = function (el, options) {
var self = this
self.$el = $(el)
self.options = options || {}
// Init
self.init()
}
ExceptionBeautifier.DEFAULTS = {
editor: 'phpstorm'
}
ExceptionBeautifier.REGEX = {
phpLine: /^(#[0-9]+)\s+(.+\.php)(?:\(([0-9]+)\))?\s*:(.*)/,
internalLine: /^(#[0-9]+)\s+(\[internal function\]\s*:)(.*)/,
defaultLine: /^(#[0-9]+)\s*(.*)/,
className: /([a-z0-9]+\\[a-z0-9\\]+)/gi,
filePath: /((?:[A-Z]:[/\\]|\/)?[\w/\\]+\.php)(\(([0-9]+)\)|:([0-9]+))?/gi,
functionCall: /->([^(]+)\((.*)\)/i
}
ExceptionBeautifier.extensions = []
ExceptionBeautifier.prototype.init = function () {
var self = this
ExceptionBeautifier.extensions.forEach(function (extension) {
extension.onInit(self);
})
self.$el.html(self.parseSource())
}
ExceptionBeautifier.prototype.parseSource = function () {
var self = this,
source = self.$el.text(),
markup = {lines: []},
start = 0,
end,
matches
// We only parse stack trace message. Just transform newline to <br> and format spaces for standard messages.
if (source.indexOf('Stack trace:') < 0) {
return '<div class="form-control">' + self.formatMessage(source) + '</div>';
}
end = source.indexOf(':', start) + 1
markup.name = source.substring(start, end)
start = end
end = source.indexOf('Stack trace:', start)
markup.message = source.substring(start, end)
start = source.indexOf('#', end)
while ((end = source.indexOf('#', start + 1)) > 0) {
markup.lines.push(self.parseLine(source.substring(start, end)))
start = end
}
markup.lines.push(self.parseLine(source.substring(start)))
source = '<div class="form-control">' +
'<span class="markup_exception_name">' + markup.name + '</span>' +
'<span class="markup_exception_message">' + self.formatMessage(markup.message) + '</span>' +
'</div>' +
'<span class="markup_exception_stacktrace_title">Stack trace:</span>'
markup.lines.forEach(function (line) {
source += '<span class="markup_exception_stacktrace_line">' + self.formatLine(line) + '</span>'
})
ExceptionBeautifier.extensions.forEach(function (extension) {
extension.onParse(self);
})
return '<div class="markup_exception_container">' + source + '</div>'
}
ExceptionBeautifier.prototype.parseLine = function (str) {
var self = this,
line = {},
matches
if (matches = str.match(ExceptionBeautifier.REGEX.phpLine)) {
line.type = 'phpLine'
line.number = $.trim(matches[1])
line.file = $.trim(matches[2])
line.lineNumber = $.trim(matches[3])
line.function = $.trim(matches[4])
}
else if (matches = str.match(ExceptionBeautifier.REGEX.internalLine)) {
line.type = 'internal'
line.number = $.trim(matches[1])
line.internal = $.trim(matches[2])
line.function = $.trim(matches[3])
} else if (matches = str.match(ExceptionBeautifier.REGEX.defaultLine)){
line.type = 'default'
line.number = $.trim(matches[1])
line.function = $.trim(matches[2])
}
return line
}
ExceptionBeautifier.prototype.formatLine = function (line) {
var self = this
if (line.function) {
line.function = self.formatClasses(line.function)
}
switch (line.type) {
case 'phpLine':
return '<span class="markup_exception_stacktrace_line_number">' + line.number + '</span>' +
self.formatFilePath(line.file, line.lineNumber) +
'<span class="markup_exception_line_number">(' + line.lineNumber + ')</span>: ' +
'<span class="markup_exception_stacktrace_line_function">' + line.function + '</span>'
case 'internal':
return '<span class="markup_exception_stacktrace_line_number">' + line.number + '</span>' +
'<span class="markup_exception_stacktrace_line_internal">' + line.internal + '</span>' +
'<span class="markup_exception_stacktrace_line_function">' + line.function + '</span>'
case 'default':
return '<span class="markup_exception_stacktrace_line_number">' + line.number + '</span>' +
'<span class="markup_exception_stacktrace_line_function">' + line.function + '</span>'
}
return ''
}
ExceptionBeautifier.prototype.formatClasses = function (str) {
var self = this
str = str.replace(ExceptionBeautifier.REGEX.className, '<span class="markup_exception_class">$1</span>')
str = str.replace(ExceptionBeautifier.REGEX.filePath, self.formatFilePath('$1', '$3$4') +
'<span class="markup_exception_line_number">$2</span>')
if (str.match(/^\s*(call_user_func|spl_autoload_call)/)) {
str = str.replace(/^\s*(?:call_user_func|spl_autoload_call)([^(]*)\((.*)\)/,
'<span class="markup_exception_function --system">call_user_func$1</span>' +
'<span class="markup_exception_sign">(</span>' +
'$2' +
'<span class="markup_exception_sign">)</span>')
} else {
str = str.replace(ExceptionBeautifier.REGEX.functionCall,
'<span class="markup_exception_sign">-></span>' +
'<span class="markup_exception_function">$1</span>' +
'<span class="markup_exception_sign">(</span>' +
'$2' +
'<span class="markup_exception_sign">)</span>')
}
return str
}
ExceptionBeautifier.prototype.formatFilePath = function (path, line) {
var self = this
return '<span class="markup_exception_file"">' + path + '</span>'
}
ExceptionBeautifier.prototype.formatMessage = function (str) {
var self = this
return self.formatClasses(str)
.replace(/^\s+/, '')
.replace(/\r|\n|\r\r/g, '<br>')
.replace(/\r| {4}/g, '&nbsp;&nbsp;&nbsp;&nbsp;')
}
// EXCEPTION BEAUTIFIER PLUGIN DEFINITION
// ============================
$.fn.exceptionBeautifier = function (option) {
var args = arguments,
result
this.each(function () {
var $this = $(this)
var data = $this.data('oc.exceptionBeautifier')
var options = $.extend({}, ExceptionBeautifier.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('oc.exceptionBeautifier', (data = new ExceptionBeautifier(this, options)))
if (typeof option == 'string') result = data[option].call($this)
if (typeof result != 'undefined') return false
})
return result ? result : this
}
$.fn.exceptionBeautifier.Constructor = ExceptionBeautifier
$(document).render(function () {
$('[data-plugin="exception-beautifier"]').exceptionBeautifier()
})
}(window.jQuery)

View File

@ -1,109 +0,0 @@
/*
* Previews class
*/
+function ($) {
"use strict";
var ExceptionBeautifier = $.fn.exceptionBeautifier.Constructor;
ExceptionBeautifier.EDITORS = {
subl: {scheme: 'subl://open?url=file://%file&line=%line', name: 'Sublime (subl://)'},
txmt: {scheme: 'txmt://open/?url=file://%file&line=%line', name: 'TextMate (txmt://)'},
mvim: {scheme: 'mvim://open/?url=file://%file&line=%line', name: 'MacVim (mvim://)'},
phpstorm: {scheme: 'phpstorm://open?file=%file&line=%line', name: 'PhpStorm (phpstorm://)'},
editor: {scheme: 'editor://open/?file=%file&line=%line', name: 'Custom (editor://)'}
}
ExceptionBeautifier.REGEX.editor = /idelink:\/\/([^#]+)&([0-9]+)?/
ExceptionBeautifier.extensions.push({
onInit: function (exceptionBeautfier) {
exceptionBeautfier.initEditorPopup();
},
onParse: function (exceptionBeautfier) {
exceptionBeautfier.$el.on('click', 'a', function () {
exceptionBeautfier.openWithEditor($(this).data('href'));
})
}
})
ExceptionBeautifier.prototype.initEditorPopup = function () {
var self = this,
title = $.oc.lang.get('eventlog.editor.title'),
description = $.oc.lang.get('eventlog.editor.description'),
openWith = $.oc.lang.get('eventlog.editor.openWith'),
saveRememberClose = $.oc.lang.get('eventlog.editor.openAndRemember'),
openClose = $.oc.lang.get('eventlog.editor.open')
self.$el.parent().append(' \
<div class="control-popup modal fade" id="exception-link-editor" tabindex="-1" role="dialog"> \
<div class="modal-dialog"><div class="modal-content"><div class="modal-header"> \
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button> \
<h4 class="modal-title">' + title + '</h4> \
</div> \
<div class="modal-body"><p>' + description + '</p> \
<div class="form-group"> \
<label class="control-label">' + openWith + ':</label> \
<select class="form-control" name="select-exception-link-editor"></select> \
</div> \
</div> \
<div class="modal-footer"> \
<button type="button" class="btn btn-default" data-trigger="editor-open-save" \
data-dismiss="modal">' + saveRememberClose + '</button> \
<button type="button" class="btn btn-primary" data-trigger="editor-open-close" \
data-dismiss="modal">' + openClose + '</button> \
</div></div></div></div>')
}
ExceptionBeautifier.prototype.openWithEditor = function (link) {
var self = this,
matches,
open = function (value) {
window.open(link.replace(
ExceptionBeautifier.REGEX.editor,
ExceptionBeautifier.EDITORS[value].scheme
.replace(/%file/, matches[1])
.replace(/%line/, matches[2])
), '_self')
}
if (matches = link.match(ExceptionBeautifier.REGEX.editor)) {
if (window.sessionStorage && window.sessionStorage['oc-exception-editor']) {
open(window.sessionStorage['oc-exception-editor']);
} else {
$('#exception-link-editor')
.popup({content: $('#exception-link-editor').html()})
.on('shown.oc.popup', function (event, source, popup) {
var $select = $('select', popup).empty()
if (!window.sessionStorage) {
$('[data-trigger="editor-open-save"]', popup).hide();
}
$('[data-trigger="editor-open-close"]', popup).on('click', function () {
open($select.val());
})
$('[data-trigger="editor-open-save"]', popup).on('click', function () {
window.sessionStorage['oc-exception-editor'] = $select.val();
open($select.val());
})
for (var key in ExceptionBeautifier.EDITORS) {
$select.append('<option value="' + key + '">' + ExceptionBeautifier.EDITORS[key].name + '</option>')
}
})
.on('hide.oc.popup', function (event, source, popup) {
$('[data-trigger]', popup).off('click')
})
}
}
}
ExceptionBeautifier.prototype.formatFilePath = function (path, line) {
return '<a class="markup_exception_file" href="javascript:" data-href="idelink://' + path + '&' + line + '">' + path + '</a>'
}
}(window.jQuery)

View File

@ -74,11 +74,11 @@ class EventLogs extends Controller
public function preview($id)
{
$this->addCss('/modules/system/assets/css/eventlogs/preview.css', 'core');
$this->addJs('/modules/system/assets/js/eventlogs/preview.js', 'core');
$this->addCss('/modules/system/assets/css/eventlogs/exception-beautifier.css', 'core');
$this->addJs('/modules/system/assets/js/eventlogs/exception-beautifier.js', 'core');
if (in_array(App::environment(), ['dev', 'local'])) {
$this->addJs('/modules/system/assets/js/eventlogs/preview.links.js', 'core');
$this->addJs('/modules/system/assets/js/eventlogs/exception-beautifier.links.js', 'core');
}
return $this->asExtension('FormController')->preview($id);

View File

@ -76,10 +76,11 @@ return [
'eventlog' => [
'editor' => [
'title' => 'Select the source code editor to use',
'description' => 'Your environnement must be configured to listen to one of these URL schemes.',
'description' => 'Your OS environnement must be configured to listen to one of these URL schemes.',
'openWith' => 'Open with',
'openAndRemember' => 'Open & Remember for this session',
'open' => 'Open'
'rememberChoice' => 'Remember the selected option for this browser session',
'open' => 'Open',
'cancel' => 'Cancel'
]
]
];

View File

@ -59,7 +59,7 @@ return [
'nextMonth' => 'Mois suivant',
'months' => ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
'weekdays' => ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi'],
'weekdaysShort' => ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam']
'weekdaysShort' => ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam'],
],
'filter' => [
@ -73,17 +73,18 @@ return [
'date_placeholder' => 'Date',
'after_placeholder' => 'Après le',
'before_placeholder' => 'Avant le',
]
],
],
'eventlog' => [
'editor' => [
'title' => 'Sélectionnez léditeur de code source à utiliser',
'description' => 'Votre environnement doit être configuré pour ouvrir lun des schéma dURL ci-dessous.',
'description' => 'Lenvironnement de votre système dexploitation doit être configuré pour ouvrir lun des schémas dURL ci-dessous.',
'openWith' => 'Ouvrir avec',
'openAndRemember' => 'Ouvrir et enregistrer pour la session',
'open' => 'Ouvrir'
]
'rememberChoice' => 'Se souvenir de la sélection pour la durée de la session dans ce navigateur',
'open' => 'Ouvrir',
'cancel' => 'Annuler',
],
]
];