Files
eruda/src/Console/Console.js
2018-08-18 12:48:55 +08:00

302 lines
7.7 KiB
JavaScript

import Logger from './Logger'
import Tool from '../DevTools/Tool'
import { noop, evalCss, $, Emitter } from '../lib/util'
import emitter from '../lib/emitter'
import Settings from '../Settings/Settings'
import stringify from './stringify'
export default class Console extends Tool {
constructor() {
super()
Emitter.mixin(this)
this.name = 'console'
this._scale = 1
this._registerListener()
}
init($el, container) {
super.init($el)
this._appendTpl()
this._initLogger()
this._exposeLogger()
this._rejectionHandler = e => this._logger.error(e.reason)
this._initCfg(container)
this._bindEvent(container)
}
show() {
super.show()
this._logger.render()
}
overrideConsole() {
let origConsole = (this._origConsole = {}),
winConsole = window.console
CONSOLE_METHOD.forEach(name => {
let origin = (origConsole[name] = noop)
if (winConsole[name])
origin = origConsole[name] = winConsole[name].bind(winConsole)
winConsole[name] = (...args) => {
this[name](...args)
origin(...args)
}
})
return this
}
restoreConsole() {
if (!this._origConsole) return this
CONSOLE_METHOD.forEach(
name => (window.console[name] = this._origConsole[name])
)
delete this._origConsole
return this
}
catchGlobalErr() {
this._origOnerror = window.onerror
window.onerror = (errMsg, url, lineNum, column, errObj) =>
this._logger.error(errObj ? errObj : errMsg)
window.addEventListener('unhandledrejection', this._rejectionHandler)
return this
}
ignoreGlobalErr() {
if (this._origOnerror) {
window.onerror = this._origOnerror
delete this._origOnerror
}
window.removeEventListener('unhandledrejection', this._rejectionHandler)
return this
}
destroy() {
this._logger.destroy()
super.destroy()
evalCss.remove(this._style)
this.ignoreGlobalErr()
this.restoreConsole()
this._unregisterListener()
}
_registerListener() {
this._scaleListener = scale => (this._scale = scale)
emitter.on(emitter.SCALE, this._scaleListener)
}
_unregisterListener() {
emitter.off(emitter.SCALE, this._scaleListener)
}
_appendTpl() {
let $el = this._$el
this._style = evalCss(require('./Console.scss'))
$el.append(require('./Console.hbs')())
let _$inputContainer = $el.find('.eruda-js-input'),
_$input = _$inputContainer.find('textarea'),
_$inputBtns = _$inputContainer.find('.eruda-buttons')
Object.assign(this, {
_$control: $el.find('.eruda-control'),
_$logs: $el.find('.eruda-logs'),
_$inputContainer,
_$input,
_$inputBtns
})
}
_initLogger() {
let $filter = this._$control.find('.eruda-filter'),
logger = (this._logger = new Logger(this._$logs, this))
logger.on('filter', filter =>
$filter.each(function() {
let $this = $(this),
isMatch = $this.data('filter') === filter
$this[isMatch ? 'addClass' : 'rmClass']('eruda-active')
})
)
}
_exposeLogger() {
let logger = this._logger,
methods = ['filter', 'html'].concat(CONSOLE_METHOD)
methods.forEach(
name =>
(this[name] = (...args) => {
logger[name](...args)
this.emit(name, ...args)
return this
})
)
}
_bindEvent(container) {
let $input = this._$input,
$inputBtns = this._$inputBtns,
$control = this._$control,
logger = this._logger,
config = this.config
$control
.on('click', '.eruda-clear-console', () => logger.clear())
.on('click', '.eruda-filter', function() {
logger.filter($(this).data('filter'))
})
.on('click', '.eruda-help', () => logger.help())
$inputBtns
.on('click', '.eruda-cancel', () => this._hideInput())
.on('click', '.eruda-execute', () => {
let jsInput = $input.val().trim()
if (jsInput === '') return
logger.input(jsInput)
$input
.val('')
.get(0)
.blur()
this._hideInput()
})
$input.on('focusin', () => this._showInput())
logger
.on('viewJson', data => {
let sources = container.get('sources')
if (!sources) return
sources.set('json', data)
container.showTool('sources')
})
.on('insert', log => {
let autoShow = log.type === 'error' && config.get('displayIfErr')
if (autoShow) container.showTool('console').show()
})
}
_hideInput() {
this._$inputContainer.css({
paddingTop: 0,
height: 40 * this._scale
})
this._$inputBtns.hide()
}
_showInput() {
this._$inputContainer.css({
paddingTop: 40 * this._scale,
height: '100%'
})
this._$inputBtns.show()
}
_initCfg(container) {
let sources = container.get('sources'),
logger = this._logger
let cfg = (this.config = Settings.createCfg('console', {
catchGlobalErr: true,
overrideConsole: true,
displayExtraInfo: false,
displayUnenumerable: true,
displayGetterVal: false,
lazyEvaluation: true,
viewLogInSources: false,
displayIfErr: false,
useWorker: true,
maxLogNum: 'infinite'
}))
let isWorkerSupported = !!window.Worker
let maxLogNum = cfg.get('maxLogNum')
maxLogNum = maxLogNum === 'infinite' ? maxLogNum : +maxLogNum
if (cfg.get('catchGlobalErr')) this.catchGlobalErr()
if (cfg.get('overrideConsole')) this.overrideConsole()
if (cfg.get('useWorker') && isWorkerSupported) stringify.useWorker = true
logger.displayHeader(cfg.get('displayExtraInfo'))
logger.displayUnenumerable(cfg.get('displayUnenumerable'))
logger.displayGetterVal(cfg.get('displayGetterVal'))
logger.lazyEvaluation(cfg.get('lazyEvaluation'))
if (sources) logger.viewLogInSources(cfg.get('viewLogInSources'))
logger.maxNum(maxLogNum)
cfg.on('change', (key, val) => {
switch (key) {
case 'catchGlobalErr':
return val ? this.catchGlobalErr() : this.ignoreGlobalErr()
case 'overrideConsole':
return val ? this.overrideConsole() : this.restoreConsole()
case 'maxLogNum':
return logger.maxNum(val === 'infinite' ? val : +val)
case 'displayExtraInfo':
return logger.displayHeader(val)
case 'displayUnenumerable':
return logger.displayUnenumerable(val)
case 'displayGetterVal':
return logger.displayGetterVal(val)
case 'lazyEvaluation':
return logger.lazyEvaluation(val)
case 'viewLogInSources':
return logger.viewLogInSources(val)
case 'useWorker':
stringify.useWorker = val
return
}
})
let settings = container.get('settings')
settings
.text('Console')
.switch(cfg, 'catchGlobalErr', 'Catch Global Errors')
.switch(cfg, 'overrideConsole', 'Override Console')
.switch(cfg, 'displayIfErr', 'Auto Display If Error Occurs')
.switch(cfg, 'displayExtraInfo', 'Display Extra Information')
.switch(cfg, 'displayUnenumerable', 'Display Unenumerable Properties')
.switch(cfg, 'displayGetterVal', 'Access Getter Value')
.switch(cfg, 'lazyEvaluation', 'Lazy Evaluation')
if (isWorkerSupported) settings.switch(cfg, 'useWorker', 'Use Web Worker')
if (sources)
settings.switch(cfg, 'viewLogInSources', 'View Log In Sources Panel')
settings
.select(cfg, 'maxLogNum', 'Max Log Number', [
'infinite',
'250',
'125',
'100',
'50',
'10'
])
.separator()
}
}
const CONSOLE_METHOD = [
'log',
'error',
'info',
'warn',
'dir',
'time',
'timeEnd',
'clear',
'table',
'assert',
'count',
'debug'
]