UNPKG

didi-console

Version:
505 lines (461 loc) 13.4 kB
/** * vConsole Basic Log Tab * * @author WechatFE */ /** * vConsole Basic Log Tab * * 2017.04.02 扩展 * @author startheart (sgoddon@163.com) */ import * as tool from '../lib/tool.js'; import $ from '../lib/query.js'; import DidiConsolePlugin from '../lib/plugin.js'; import tplItem from './item.html'; import tplFold from './item_fold.html'; import tplFoldCode from './item_fold_code.html'; class DidiConsoleLogTab extends DidiConsolePlugin { constructor(...args) { super(...args); this.tplTabbox = ''; // MUST be overwrite in child class this.allowUnformattedLog = true; // `[xxx]` format log this.isReady = false; this.isShow = false; this.$tabbox = null; this.console = {}; this.logList = []; this.isInBottom = true; // whether the panel is in the bottom this.mockConsole(); } /** * when didiConsole is ready, * this event will be triggered (after 'add' event) * @public */ onInit() { this.$tabbox = $.render(this.tplTabbox, {}); } /** * this event will make this plugin be registered as a tab * @public */ onRenderTab(callback) { callback(this.$tabbox); } onAddTopBar(callback) { let that = this; let types = ['All', 'Log', 'Info', 'Warn', 'Error']; let btnList = []; for (let i = 0; i < types.length; i++) { btnList.push({ name: types[i], data: { type: types[i].toLowerCase() }, className: '', onClick: function() { if (!$.hasClass(this, 'vc-actived')) { that.showLogType(this.dataset.type || 'all'); } else { return false; } } }); } btnList[0].className = 'vc-actived'; callback(btnList); } onAddTool(callback) { let that = this; let toolList = [{ name: 'Clear', global: false, onClick: function() { that.clearLog(); } }]; callback(toolList); } /** * after init * @public */ onReady() { let that = this; that.isReady = true; // log type filter let $subTabs = $.all('.vc-subtab', that.$tabbox); $.bind($subTabs, 'click', function(e) { e.preventDefault(); if ($.hasClass(this, 'vc-actived')) { return false; } $.removeClass($subTabs, 'vc-actived'); $.addClass(this, 'vc-actived'); let logType = this.dataset.type, $log = $.one('.vc-log', that.$tabbox); $.removeClass($log, 'vc-log-partly-log'); $.removeClass($log, 'vc-log-partly-info'); $.removeClass($log, 'vc-log-partly-warn'); $.removeClass($log, 'vc-log-partly-error'); if (logType == 'all') { $.removeClass($log, 'vc-log-partly'); } else { $.addClass($log, 'vc-log-partly'); $.addClass($log, 'vc-log-partly-' + logType); } }); let $content = $.one('.vc-content'); $.bind($content, 'scroll', function(e) { if (!that.isShow) { return; } if ($content.scrollTop + $content.offsetHeight >= $content.scrollHeight) { that.isInBottom = true; } else { that.isInBottom = false; } }); for (let i=0; i<that.logList.length; i++) { that.printLog(that.logList[i]); } that.logList = []; } /** * before remove * @public */ onRemove() { // recover original console window.console.log = this.console.log; window.console.info = this.console.info; window.console.warn = this.console.warn; window.console.debug = this.console.debug; window.console.error = this.console.error; this.console = {}; } onShow() { this.isShow = true; if (this.isInBottom == true) { this.scrollToBottom(); } } onHide() { this.isShow = false; } onShowConsole() { if (this.isInBottom == true) { this.scrollToBottom(); } } showLogType(logType) { let $log = $.one('.vc-log', this.$tabbox); $.removeClass($log, 'vc-log-partly-log'); $.removeClass($log, 'vc-log-partly-info'); $.removeClass($log, 'vc-log-partly-warn'); $.removeClass($log, 'vc-log-partly-error'); if (logType == 'all') { $.removeClass($log, 'vc-log-partly'); } else { $.addClass($log, 'vc-log-partly'); $.addClass($log, 'vc-log-partly-' + logType); } } scrollToBottom() { let $content = $.one('.vc-content'); $content.scrollTop = $content.scrollHeight - $content.offsetHeight; } /** * replace window.console with didiConsole method * @private */ mockConsole() { let that = this; if (!window.console) { window.console = {}; } else { this.console.log = window.console.log; this.console.info = window.console.info; this.console.warn = window.console.warn; this.console.debug = window.console.debug; this.console.error = window.console.error; } window.console.log = function() { that.printLog({ logType: 'log', logs: arguments }); }; window.console.info = function() { that.printLog({ logType: 'info', logs: arguments }); }; window.console.warn = function() { that.printLog({ logType: 'warn', logs: arguments }); }; window.console.debug = function() { that.printLog({ logType: 'debug', logs: arguments }); }; window.console.error = function() { that.printLog({ logType: 'error', logs: arguments }); }; } clearLog() { $.one('.vc-log', this.$tabbox).innerHTML = ''; } /** * print log to origin console * @protected */ printOriginLog(item) { if (typeof this.console[item.logType] === 'function') { this.console[item.logType].apply(window.console, item.logs); } } /** * print a log to log box * @protected * @param string tabName auto|default|system * @param string logType log|info|debug|error|warn * @param array logs `logs` or `content` can't be empty * @param object content `logs` or `content` can't be empty * @param boolean noOrigin * @param boolean noMeta * @param int date * @param string style * @param string meta */ printLog(item) { let logs = item.logs || []; if (!logs.length && !item.content) { return; } // convert logs to a real array logs = [].slice.call(logs || []); // check `[default]` format let shouldBeHere = true; let pattern = /^\[(\w+)\] ?/i; let targetTabName = ''; if (tool.isString(logs[0])) { let match = logs[0].match(pattern); if (match !== null && match.length > 0) { targetTabName = match[1].toLowerCase(); } } if (targetTabName) { shouldBeHere = (targetTabName == this.id); } else if (this.allowUnformattedLog == false) { shouldBeHere = false; } if (!shouldBeHere) { // ignore this log and throw it to origin console if (!item.noOrigin) { this.printOriginLog(item); } return; } // save log date if (!item.date) { item.date = (+new Date()); } // if didiConsole is not ready, save current log to logList if (!this.isReady) { this.logList.push(item); return; } // remove `[xxx]` format if (tool.isString(logs[0])) { logs[0] = logs[0].replace(pattern, ''); if (logs[0] === '') { logs.shift(); } } // use date as meta if (!item.meta) { let date = tool.getDate(item.date); item.meta = date.hour + ':' + date.minute + ':' + date.second; } // create line let $line = $.render(tplItem, { logType: item.logType, noMeta: !!item.noMeta, meta: item.meta, style: item.style || '' }); let $content = $.one('.vc-item-content', $line); // generate content from item.logs for (let i=0; i<logs.length; i++) { let log; try { if (logs[i] === '') { // ignore empty string continue; } else if (tool.isFunction(logs[i])) { // convert function to string log = '<span> ' + logs[i].toString() + '</span>'; } else if (tool.isObject(logs[i]) || tool.isArray(logs[i])) { // object or array log = this.getFoldedLine(logs[i]); } else { // default log = '<span> ' + tool.htmlEncode(logs[i]).replace(/\n/g, '<br/>') + '</span>'; } } catch (e) { log = '<span> [' + (typeof logs[i]) + ']</span>'; } if (log) { if (typeof log === 'string') $content.insertAdjacentHTML('beforeend', log); else $content.insertAdjacentElement('beforeend', log); } } // generate content from item.content if (tool.isObject(item.content)) { $content.insertAdjacentElement('beforeend', item.content); } // render to panel $.one('.vc-log', this.$tabbox).insertAdjacentElement('beforeend', $line); // scroll to bottom if it is in the bottom before if (this.isInBottom) { this.scrollToBottom(); } // print log to origin console if (!item.noOrigin) { this.printOriginLog(item); } } /** * generate the HTML element of a folded line * @protected */ getFoldedLine(obj, outer) { let that = this; if (!outer) { let json = tool.JSONStringify(obj); let preview = json.substr(0, 26); outer = tool.getObjName(obj); if (json.length > 26) { preview += '...'; } outer += ' ' + preview; } let $line = $.render(tplFold, { outer: outer, lineType: 'obj' }); $.bind($.one('.vc-fold-outer', $line), 'click', function(e) { e.preventDefault(); e.stopPropagation(); if ($.hasClass($line, 'vc-toggle')) { $.removeClass($line, 'vc-toggle'); $.removeClass($.one('.vc-fold-inner', $line), 'vc-toggle'); $.removeClass($.one('.vc-fold-outer', $line), 'vc-toggle'); } else { $.addClass($line, 'vc-toggle'); $.addClass($.one('.vc-fold-inner', $line), 'vc-toggle'); $.addClass($.one('.vc-fold-outer', $line), 'vc-toggle'); } let $content = $.one('.vc-fold-inner', $line); if ($content.children.length == 0 && !!obj) { // render object's keys let keys = tool.getObjAllKeys(obj); for (let i = 0; i < keys.length; i++) { let val = obj[keys[i]], valueType = 'undefined', keyType = '', $line; // handle value if (tool.isString(val)) { valueType = 'string'; val = '"' + val + '"'; } else if (tool.isNumber(val)) { valueType = 'number'; } else if (tool.isBoolean(val)) { valueType = 'boolean'; } else if (tool.isNull(val)) { valueType = 'null'; val = 'null'; } else if (tool.isUndefined(val)) { valueType = 'undefined'; val = 'undefined'; } else if (tool.isFunction(val)) { valueType = 'function'; val = 'function()'; } else if (tool.isSymbol(val)) { valueType = 'symbol'; } // render let $sub; if (tool.isArray(val)) { let name = tool.getObjName(val) + '[' + val.length + ']'; $sub = that.getFoldedLine(val, $.render(tplFoldCode, { key: keys[i], keyType: keyType, value: name, valueType: 'array' }, true)); } else if (tool.isObject(val)) { let name = tool.getObjName(val); $sub = that.getFoldedLine(val, $.render(tplFoldCode, { key: tool.htmlEncode(keys[i]), keyType: keyType, value: name, valueType: 'object' }, true)); } else { if (obj.hasOwnProperty && !obj.hasOwnProperty(keys[i])) { keyType = 'private'; } let renderData = { lineType: 'kv', key: tool.htmlEncode(keys[i]), keyType: keyType, value: tool.htmlEncode(val), valueType: valueType }; $sub = $.render(tplFold, renderData); } $content.insertAdjacentElement('beforeend', $sub); } // render object's prototype if (tool.isObject(obj)) { let proto = obj.__proto__, $proto; if (tool.isObject(proto)) { $proto = that.getFoldedLine(proto, $.render(tplFoldCode, { key: '__proto__', keyType: 'private', value: tool.getObjName(proto), valueType: 'object' }, true)); } else { // if proto is not an object, it should be `null` $proto = $.render(tplFoldCode, { key: '__proto__', keyType: 'private', value: 'null', valueType: 'null' }); } $content.insertAdjacentElement('beforeend', $proto); } } return false; }); return $line; } } // END class export default DidiConsoleLogTab;