Files
eruda/src/Console/Logger.js
2018-01-09 18:44:21 +08:00

374 lines
7.8 KiB
JavaScript

import Log from './Log';
import {
Emitter,
evalCss,
isNum,
isUndef,
perfNow,
startWith,
escapeRegExp,
isStr,
extend,
uniqId,
isRegExp,
isFn,
stripHtmlTag,
loadJs,
$
} from '../lib/util';
export default class Logger extends Emitter
{
constructor($el, container)
{
super();
this._style = evalCss(require('./Logger.scss'));
this._$el = $el;
this._container = container;
this._logs = [];
this._timer = {};
this._count = {};
this._lastLog = {};
this._filter = 'all';
this._maxNum = 'infinite';
this._displayHeader = false;
this._bindEvent();
}
displayHeader(flag)
{
this._displayHeader = flag;
}
maxNum(val)
{
let logs = this._logs;
this._maxNum = val;
if (isNum(val) && logs.length > val)
{
this._logs = logs.slice(logs.length - val);
this.render();
}
}
displayUnenumerable(flag)
{
Log.showUnenumerable = flag;
}
displayGetterVal(flag)
{
Log.showGetterVal = flag;
}
viewLogInSources(flag)
{
Log.showSrcInSources = flag;
}
destroy()
{
evalCss.remove(this._style);
}
filter(val)
{
this._filter = val;
this.emit('filter', val);
return this.render();
}
count(label)
{
let count = this._count;
!isUndef(count[label]) ? count[label]++ : count[label] = 1;
return this.html(`<div class="eruda-blue">${label}: ${count[label]}</div>`);
}
assert(...args)
{
if (args.length === 0) return;
let exp = args.shift();
if (!exp)
{
args.unshift('Assertion failed: ');
return this.insert('error', args);
}
}
log(...args)
{
this.insert('log', args);
return this;
}
debug(...args)
{
this.insert('debug', args);
return this;
}
dir(...args)
{
this.insert('dir', args);
return this;
}
table(...args)
{
this.insert('table', args);
return this;
}
time(name)
{
this._timer[name] = perfNow();
return this;
}
timeEnd(name)
{
let startTime = this._timer[name];
if (!startTime) return;
delete this._timer[name];
return this.html(`<div class="eruda-blue">${name}: ${perfNow() - startTime}ms</div>`);
}
clear()
{
this._logs = [];
this._lastLog = {};
return this.render();
}
info(...args)
{
return this.insert('info', args);
}
error(...args)
{
return this.insert('error', args);
}
warn(...args)
{
return this.insert('warn', args);
}
input(jsCode)
{
if (startWith(jsCode, ':'))
{
this._runCmd(jsCode.slice(1));
return this;
} else if (startWith(jsCode, '/'))
{
return this.filter(new RegExp(escapeRegExp(jsCode.slice(1))));
}
this.insert({
type: 'input',
args: [jsCode],
ignoreFilter: true
});
try {
this.output(evalJs(jsCode));
} catch (e)
{
this.insert({
type: 'error',
ignoreFilter: true,
args: [e]
});
}
return this;
}
output(val)
{
return this.insert({
type: 'output',
args: [val],
ignoreFilter: true
});
}
html(...args)
{
return this.insert('html', args);
}
help()
{
return this.insert({
type: 'html',
args: [helpMsg],
ignoreFilter: true
});
}
render()
{
let html = '',
logs = this._logs;
logs = this._filterLogs(logs);
for (let i = 0, len = logs.length; i < len; i++)
{
html += logs[i].formattedMsg;
}
this._$el.html(html);
this.scrollToBottom();
return this;
}
insert(type, args)
{
let logs = this._logs,
$el = this._$el,
el = $el.get(0);
let isAtBottom = (el.scrollTop === el.scrollHeight - el.offsetHeight);
let options = isStr(type) ? {type, args} : type;
extend(options, {
id: uniqId('log'),
displayHeader: this._displayHeader
});
let log = new Log(options);
let lastLog = this._lastLog;
if (log.type !== 'html' &&
lastLog.type === log.type &&
lastLog.value === log.value)
{
lastLog.addCount();
if (log.time) lastLog.updateTime(log.time);
$el.find('li').last().remove();
log = lastLog;
} else
{
logs.push(log);
this._lastLog = log;
}
if (this._maxNum !== 'infinite' && logs.length > this._maxNum)
{
$el.find('li').first().remove();
logs.shift();
}
if (this._filterLog(log) && this._container.active) $el.append(log.formattedMsg);
this.emit('insert', log);
if (isAtBottom) this.scrollToBottom();
return this;
}
scrollToBottom()
{
let el = this._$el.get(0);
el.scrollTop = el.scrollHeight - el.offsetHeight;
}
_filterLogs(logs)
{
let filter = this._filter;
if (filter === 'all') return logs;
let isFilterRegExp = isRegExp(filter),
isFilterFn = isFn(filter);
return logs.filter(log =>
{
if (log.ignoreFilter) return true;
if (isFilterFn) return filter(log);
if (isFilterRegExp) return filter.test(stripHtmlTag(log.formattedMsg));
return log.type === filter;
});
}
_filterLog(log)
{
let filter = this._filter;
if (filter === 'all') return true;
let isFilterRegExp = isRegExp(filter),
isFilterFn = isFn(filter);
if (log.ignoreFilter) return true;
if (isFilterFn) return filter(log);
if (isFilterRegExp) return filter.test(stripHtmlTag(log.formattedMsg));
return log.type === filter;
}
_loadJs(name)
{
loadJs(libraries[name], (result) =>
{
if (result) return this.log(`${name} is loaded`);
this.warn(`Failed to load ${name}`);
});
}
_runCmd(cmd)
{
switch (cmd.trim())
{
case '$': return this._loadJs('jQuery');
case '_': return this._loadJs('underscore');
default:
this.warn('Unknown command').help();
}
}
_bindEvent()
{
let self = this;
this._$el.on('click', '.eruda-log-item', function ()
{
let $el = $(this),
id = $el.data('id'),
type = $el.data('type'),
logs = self._logs,
log;
for (let i = 0, len = logs.length; i < len; i++)
{
log = logs[i];
if (log.id === id) break;
}
if (!log) return;
let action = Log.click(type, log, $el);
switch (action)
{
case 'viewSrc':
self.emit('viewJson', log.src);
break;
}
});
}
}
let cmdList = require('./cmdList.json'),
helpMsg = require('./help.hbs')({commands: cmdList}),
libraries = require('./libraries.json');
let evalJs = jsInput =>
{
let ret;
try
{
ret = eval.call(window, `(${jsInput})`);
} catch (e)
{
ret = eval.call(window, jsInput);
}
return ret;
};