|
import Tool from '../DevTools/Tool' |
|
import LunaObjectViewer from 'luna-object-viewer' |
|
import Settings from '../Settings/Settings' |
|
import ajax from 'licia/ajax' |
|
import each from 'licia/each' |
|
import isStr from 'licia/isStr' |
|
import escape from 'licia/escape' |
|
import truncate from 'licia/truncate' |
|
import replaceAll from 'licia/replaceAll' |
|
import highlight from 'licia/highlight' |
|
import LunaTextViewer from 'luna-text-viewer' |
|
import evalCss from '../lib/evalCss' |
|
import { classPrefix as c } from '../lib/util' |
|
|
|
export default class Sources extends Tool { |
|
constructor() { |
|
super() |
|
|
|
this._style = evalCss(require('./Sources.scss')) |
|
|
|
this.name = 'sources' |
|
this.displayName = 'ソース' |
|
this._showLineNum = true |
|
} |
|
init($el, container) { |
|
super.init($el) |
|
|
|
this._container = container |
|
this._bindEvent() |
|
this._initCfg() |
|
} |
|
destroy() { |
|
super.destroy() |
|
|
|
evalCss.remove(this._style) |
|
this._rmCfg() |
|
} |
|
set(type, val) { |
|
if (type === 'img') { |
|
this._isFetchingData = true |
|
|
|
const img = new Image() |
|
|
|
const self = this |
|
|
|
img.onload = function () { |
|
self._isFetchingData = false |
|
self._data = { |
|
type: 'img', |
|
val: { |
|
width: this.width, |
|
height: this.height, |
|
src: val, |
|
}, |
|
} |
|
|
|
self._render() |
|
} |
|
img.onerror = function () { |
|
self._isFetchingData = false |
|
} |
|
|
|
img.src = val |
|
|
|
return |
|
} |
|
|
|
this._data = { type, val } |
|
|
|
this._render() |
|
|
|
return this |
|
} |
|
show() { |
|
super.show() |
|
|
|
if (!this._data && !this._isFetchingData) { |
|
this._renderDef() |
|
} |
|
|
|
return this |
|
} |
|
_renderDef() { |
|
if (this._html) { |
|
this._data = { |
|
type: 'html', |
|
val: this._html, |
|
} |
|
|
|
return this._render() |
|
} |
|
|
|
if (this._isGettingHtml) return |
|
this._isGettingHtml = true |
|
|
|
ajax({ |
|
url: location.href, |
|
success: (data) => (this._html = data), |
|
error: () => (this._html = 'ソースコードを取得できません'), |
|
complete: () => { |
|
this._isGettingHtml = false |
|
this._renderDef() |
|
}, |
|
dataType: 'raw', |
|
}) |
|
} |
|
_bindEvent() { |
|
this._container.on('showTool', (name, lastTool) => { |
|
if (name !== this.name && lastTool.name === this.name) { |
|
delete this._data |
|
} |
|
}) |
|
} |
|
_rmCfg() { |
|
const cfg = this.config |
|
|
|
const settings = this._container.get('settings') |
|
|
|
if (!settings) return |
|
|
|
settings.remove(cfg, 'showLineNum').remove('Sources') |
|
} |
|
_initCfg() { |
|
const cfg = (this.config = Settings.createCfg('sources', { |
|
showLineNum: true, |
|
})) |
|
|
|
if (!cfg.get('showLineNum')) this._showLineNum = false |
|
|
|
cfg.on('change', (key, val) => { |
|
switch (key) { |
|
case 'showLineNum': |
|
this._showLineNum = val |
|
return |
|
} |
|
}) |
|
|
|
const settings = this._container.get('settings') |
|
settings |
|
.text('ソース') |
|
.switch(cfg, 'showLineNum', '行番号を表示') |
|
.separator() |
|
} |
|
_render() { |
|
this._isInit = true |
|
|
|
const data = this._data |
|
|
|
switch (data.type) { |
|
case 'html': |
|
case 'js': |
|
case 'css': |
|
return this._renderCode() |
|
case 'img': |
|
return this._renderImg() |
|
case 'object': |
|
return this._renderObj() |
|
case 'raw': |
|
return this._renderRaw() |
|
case 'iframe': |
|
return this._renderIframe() |
|
} |
|
} |
|
_renderImg() { |
|
const { width, height, src } = this._data.val |
|
|
|
this._renderHtml(`<div class="${c('image')}"> |
|
<div class="${c('breadcrumb')}">${escape(src)}</div> |
|
<div class="${c('img-container')}" data-exclude="true"> |
|
<img src="${escape(src)}"> |
|
</div> |
|
<div class="${c('img-info')}">${escape(width)} × ${escape(height)}</div> |
|
</div>`) |
|
} |
|
_renderCode() { |
|
const data = this._data |
|
|
|
this._renderHtml( |
|
`<div class="${c('code')}" data-type="${data.type}"></div>`, |
|
false |
|
) |
|
|
|
let code = data.val |
|
const len = data.val.length |
|
|
|
if (len > MAX_RAW_LEN) { |
|
code = truncate(code, MAX_RAW_LEN) |
|
} |
|
|
|
|
|
if (len < MAX_BEAUTIFY_LEN) { |
|
code = highlight(code, data.type, { |
|
comment: '', |
|
string: '', |
|
number: '', |
|
keyword: '', |
|
operator: '', |
|
}) |
|
each(['comment', 'string', 'number', 'keyword', 'operator'], (type) => { |
|
code = replaceAll(code, `class="${type}"`, `class="${c(type)}"`) |
|
}) |
|
} else { |
|
code = escape(code) |
|
} |
|
|
|
const container = this._$el.find(c('.code')).get(0) |
|
new LunaTextViewer(container, { |
|
text: code, |
|
escape: false, |
|
wrapLongLines: true, |
|
showLineNumbers: data.val.length < MAX_LINE_NUM_LEN && this._showLineNum, |
|
}) |
|
} |
|
_renderObj() { |
|
|
|
this._renderHtml(`<ul class="${c('json')}"></ul>`, false) |
|
|
|
let val = this._data.val |
|
|
|
try { |
|
if (isStr(val)) { |
|
val = JSON.parse(val) |
|
} |
|
} catch { |
|
|
|
} |
|
|
|
const objViewer = new LunaObjectViewer( |
|
this._$el.find('.eruda-json').get(0), |
|
{ |
|
unenumerable: true, |
|
accessGetter: true, |
|
prototype: false, |
|
} |
|
) |
|
objViewer.set(val) |
|
} |
|
_renderRaw() { |
|
const data = this._data |
|
|
|
this._renderHtml(`<div class="${c('raw-wrapper')}"> |
|
<div class="${c('raw')}"></div> |
|
</div>`) |
|
|
|
let val = data.val |
|
const container = this._$el.find(c('.raw')).get(0) |
|
if (val.length > MAX_RAW_LEN) { |
|
val = truncate(val, MAX_RAW_LEN) |
|
} |
|
|
|
new LunaTextViewer(container, { |
|
text: val, |
|
wrapLongLines: true, |
|
showLineNumbers: val.length < MAX_LINE_NUM_LEN && this._showLineNum, |
|
}) |
|
} |
|
_renderIframe() { |
|
this._renderHtml(`<iframe src="${escape(this._data.val)}"></iframe>`) |
|
} |
|
_renderHtml(html, cache = true) { |
|
if (cache && html === this._lastHtml) return |
|
this._lastHtml = html |
|
this._$el.html(html) |
|
|
|
setTimeout(() => (this._$el.get(0).scrollTop = 0), 0) |
|
} |
|
} |
|
|
|
const MAX_BEAUTIFY_LEN = 30000 |
|
const MAX_LINE_NUM_LEN = 80000 |
|
const MAX_RAW_LEN = 100000 |
|
|