diff --git a/modules/system/assets/css/eventlogs/exception-beautifier.css b/modules/system/assets/css/eventlogs/exception-beautifier.css
new file mode 100644
index 000000000..3e2c4dff1
--- /dev/null
+++ b/modules/system/assets/css/eventlogs/exception-beautifier.css
@@ -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;
+}
\ No newline at end of file
diff --git a/modules/system/assets/css/eventlogs/preview.css b/modules/system/assets/css/eventlogs/preview.css
deleted file mode 100644
index 041e8b89e..000000000
--- a/modules/system/assets/css/eventlogs/preview.css
+++ /dev/null
@@ -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;
-}
\ No newline at end of file
diff --git a/modules/system/assets/js/eventlogs/exception-beautifier.js b/modules/system/assets/js/eventlogs/exception-beautifier.js
new file mode 100644
index 000000000..b1f86c51a
--- /dev/null
+++ b/modules/system/assets/js/eventlogs/exception-beautifier.js
@@ -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
and tab/spaces indentation to
+ */
+ 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, '
')
+ .replace(/\{x-tabulation\}/g, ' ')
+ }
+
+ 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)
\ No newline at end of file
diff --git a/modules/system/assets/js/eventlogs/exception-beautifier.links.js b/modules/system/assets/js/eventlogs/exception-beautifier.links.js
new file mode 100644
index 000000000..20aa25893
--- /dev/null
+++ b/modules/system/assets/js/eventlogs/exception-beautifier.links.js
@@ -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 = $(' \
+