Rich text console logging
สคริปต์นี้ไม่ควรถูกติดตั้งโดยตรง มันเป็นคลังสำหรับสคริปต์อื่น ๆ เพื่อบรรจุด้วยคำสั่งเมทา // @require https://update.greasyfork.org/scripts/370255/612436/consolemessage.js
// ==UserScript== // @name console.message // @description Rich text console logging // @version 0.1.0 // ==/UserScript== ; (function () { var cssNumbers = { columnCount: true, fillOpacity: true, flexGrow: true, flexShrink: true, fontWeight: true, lineHeight: true, opacity: true, order: true, orphans: true, widows: true, zIndex: true, zoom: true }; var support = (function () { // Taken from https://github.com/jquery/jquery-migrate/blob/master/src/core.js function uaMatch(ua) { ua = ua.toLowerCase(); var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || /(webkit)[ \/]([\w.]+)/.exec(ua) || /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || /(msie) ([\w.]+)/.exec(ua) || ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || []; return { browser: match[1] || "", version: match[2] || "0" }; } var browserData = uaMatch(navigator.userAgent); return { isIE: browserData.browser == 'msie' || (browserData.browser == 'mozilla' && parseInt(browserData.version, 10) == 11) }; })(); function ConsoleMessage() { if (!ConsoleMessage.prototype.isPrototypeOf(this)) { return new ConsoleMessage(); } this._rootSpan = { styles: {}, children: [], parent: null }; this._currentSpan = this._rootSpan; this._waiting = 0; this._readyCallback = null; } ConsoleMessage.prototype = { /** * Begins a group. By default the group is expanded. Provide false if you want the group to be collapsed. * @param {boolean} [expanded = true] - * @returns {ConsoleMessage} - Returns the message object itself to allow chaining. */ group: function (expanded) { this._currentSpan.children.push({ type: expanded === false ? 'groupCollapsed' : 'group', parent: this._currentSpan }); return this; }, /** * Ends the group and returns to writing to the parent message. * @returns {ConsoleMessage} - Returns the message object itself to allow chaining. */ groupEnd: function () { this._currentSpan.children.push({ type: 'groupEnd', parent: this._currentSpan }); return this; }, /** * Starts a span with particular style and all appended text after it will use the style. * @param {Object} styles - The CSS styles to be applied to all text until endSpan() is called * @returns {ConsoleMessage} - Returns the message object itself to allow chaining. */ span: function (styles) { var span = { type: 'span', styles: apply(styles || {}, this._currentSpan.styles), children: [], parent: this._currentSpan }; this._currentSpan.children.push(span); this._currentSpan = span; return this; }, /** * Ends the current span styles and backs to the previous styles or the root if there are no other parents. * @returns {ConsoleMessage} - Returns the message object itself to allow chaining. */ spanEnd: function () { this._currentSpan = this._currentSpan.parent || this._currentSpan; return this; }, /** * Appends a text to the current message. All styles in the current span are applied. * @param {string} text - The text to be appended * @returns {ConsoleMessage} - Returns the message object itself to allow chaining. */ text: function (text, styles) { this.span(styles); this._currentSpan.children.push({ type: 'text', message: text, parent: this._currentSpan }); return this.spanEnd(); }, /** * Adds a new line to the output. * @returns {ConsoleMessage} - Returns the message object itself to allow chaining. */ line: function (type) { this._currentSpan.children.push({ type: type || 'log', parent: this._currentSpan }); return this; }, /** * Adds an interactive DOM element to the output. * @param {HTMLElement} element - The DOM element to be added. * @returns {ConsoleMessage} - Returns the message object itself to allow chaining. */ element: function (element) { this._currentSpan.children.push({ type: 'element', element: element, parent: this._currentSpan }); return this; }, /** * Adds an interactive object tree to the output. * @param {*} object - A value to be added to the output. * @returns {ConsoleMessage} - Returns the message object itself to allow chaining. */ object: function (object) { this._currentSpan.children.push({ type: 'object', object: object, parent: this._currentSpan }); return this; }, /** * Prints the message to the console. * Until print() is called there will be no r###lt to the console. */ print: function () { if (typeof console != 'undefined') { var messages = [this._newMessage()]; var message; this._printSpan(this._rootSpan, messages); for (var i = 0; i < messages.length; i++) { message = messages[i]; if (message.text && message.text != '%c' && console[message.type]) { this._printMessage(message); } } } return new ConsoleMessage(); }, _printMessage: function (message) { Function.prototype.apply.call( console[message.type], console, [message.text].concat(message.args) ); }, _printSpan: function (span, messages) { var children = span.children; var message = messages[messages.length - 1]; this._addSpanData(span, message); for (var i = 0; i < children.length; i++) { this._handleChild(children[i], messages); } }, _handleChild: function (child, messages) { var message = messages[messages.length - 1]; switch (child.type) { case 'group': messages.push(this._newMessage('group')); break; case 'groupCollapsed': messages.push(this._newMessage('groupCollapsed')); break; case 'groupEnd': message = this._newMessage('groupEnd'); message.text = ' '; messages.push(message); messages.push(this._newMessage()); break; case 'span': this._printSpan(child, messages); this._addSpanData(child, message); this._addSpanData(child.parent, message); break; case 'text': message.text += child.message; break; case 'element': message.text += '%o'; message.args.push(child.element); break; case 'object': message.text += '%O'; message.args.push(child.object); break; case 'log': messages.push(this._newMessage(child.type)); break; } }, _addSpanData: function (span, message) { if (!support.isIE) { if (message.text.substring(message.text.length - 2) == '%c') { message.args[message.args.length - 1] = this._stylesString(span.styles); } else { message.text += '%c'; message.args.push(this._stylesString(span.styles)); } } }, _newMessage: function (type) { return { type: type || 'log', text: '', args: [] }; }, _stylesString: function (styles) { var r###lt = ''; var value; var key; for (key in styles) { value = styles[key]; key = this._fixCssStyleKey(key); if (typeof value === 'number' && !cssNumbers[key]) { value += 'px'; } r###lt += this._toDashKey(key) + ':' + value + ';'; } return r###lt; }, _fixCssStyleKey: function (key) { return key.replace(/-\w/g, function (match) { return match.charAt(1).toUpperCase(); }); }, _toDashKey: function (key) { return key.replace(/[A-Z]/g, function (match) { return '-' + match.toLowerCase(); }); } }; function apply(options, object) { for (var key in object) { if (options[key] === undefined) { options[key] = object[key]; } } return options; } if (typeof window != 'undefined') { if (!window.console) { window.console = {}; } /** * Creates a message object. * @returns {ConsoleMessage} - The message object */ window.console.message = ConsoleMessage; } })()