- // ==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 result 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 result = '';
- var value;
- var key;
-
- for (key in styles) {
- value = styles[key];
- key = this._fixCssStyleKey(key);
-
- if (typeof value === 'number' && !cssNumbers[key]) {
- value += 'px';
- }
- result += this._toDashKey(key) + ':' + value + ';';
- }
-
- return result;
- },
-
- _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;
- }
- })()