import Tool from '../DevTools/Tool' import Settings from '../Settings/Settings' import $ from 'licia/$' import escape from 'licia/escape' import isEmpty from 'licia/isEmpty' import contain from 'licia/contain' import unique from 'licia/unique' import each from 'licia/each' import sameOrigin from 'licia/sameOrigin' import ajax from 'licia/ajax' import MutationObserver from 'licia/MutationObserver' import toArr from 'licia/toArr' import concat from 'licia/concat' import map from 'licia/map' import { isErudaEl, classPrefix as c } from '../lib/util' import evalCss from '../lib/evalCss' import Storage from './Storage' import Cookie from './Cookie' import { setState, getState } from './util' export default class Resources extends Tool { constructor() { super() this._style = evalCss(require('./Resources.scss')) this.name = 'resources' this.displayName = 'リソース' this._hideErudaSetting = false this._observeElement = true } init($el, container) { super.init($el) this._container = container this._initTpl() this._localStorage = new Storage( this._$localStorage, container, this, 'local' ) this._sessionStorage = new Storage( this._$sessionStorage, container, this, 'session' ) this._cookie = new Cookie(this._$cookie, container) this._bindEvent() this._initObserver() this._initCfg() } refresh() { return this.refreshLocalStorage() .refreshSessionStorage() .refreshCookie() .refreshScript() .refreshStylesheet() .refreshIframe() .refreshImage() } destroy() { super.destroy() this._localStorage.destroy() this._sessionStorage.destroy() this._disableObserver() evalCss.remove(this._style) this._rmCfg() } refreshScript() { let scriptData = [] $('script').each(function () { const src = this.src if (src !== '') scriptData.push(src) }) scriptData = unique(scriptData) const scriptState = getState('script', scriptData.length) let scriptDataHtml = '
  • なし
  • ' if (!isEmpty(scriptData)) { scriptDataHtml = map(scriptData, (script) => { script = escape(script) return `
  • ${script}
  • ` }).join('') } const scriptHtml = `

    スクリプト

    ` const $script = this._$script setState($script, scriptState) $script.html(scriptHtml) return this } refreshStylesheet() { let stylesheetData = [] $('link').each(function () { if (this.rel !== 'stylesheet') return stylesheetData.push(this.href) }) stylesheetData = unique(stylesheetData) const stylesheetState = getState('stylesheet', stylesheetData.length) let stylesheetDataHtml = '
  • なし
  • ' if (!isEmpty(stylesheetData)) { stylesheetDataHtml = map(stylesheetData, (stylesheet) => { stylesheet = escape(stylesheet) return `
  • ${stylesheet}
  • ` }).join('') } const stylesheetHtml = `

    スタイルシート

    ` const $stylesheet = this._$stylesheet setState($stylesheet, stylesheetState) $stylesheet.html(stylesheetHtml) return this } refreshIframe() { let iframeData = [] $('iframe').each(function () { const $this = $(this) const src = $this.attr('src') if (src) iframeData.push(src) }) iframeData = unique(iframeData) let iframeDataHtml = '
  • なし
  • ' if (!isEmpty(iframeData)) { iframeDataHtml = map(iframeData, (iframe) => { iframe = escape(iframe) return `
  • ${iframe}
  • ` }).join('') } const iframeHtml = `

    Iframe

    ` this._$iframe.html(iframeHtml) return this } refreshLocalStorage() { this._localStorage.refresh() return this } refreshSessionStorage() { this._sessionStorage.refresh() return this } refreshCookie() { this._cookie.refresh() return this } refreshImage() { let imageData = [] const performance = (this._performance = window.webkitPerformance || window.performance) if (performance && performance.getEntries) { const entries = this._performance.getEntries() entries.forEach((entry) => { if (entry.initiatorType === 'img' || isImg(entry.name)) { if (contain(entry.name, 'exclude=true')) { return } imageData.push(entry.name) } }) } else { $('img').each(function () { const $this = $(this) const src = $this.attr('src') if ($this.data('exclude') === 'true') { return } imageData.push(src) }) } imageData = unique(imageData) imageData.sort() const imageState = getState('image', imageData.length) let imageDataHtml = '
  • なし
  • ' if (!isEmpty(imageData)) { // prettier-ignore imageDataHtml = map(imageData, (image) => { return `
  • ` }).join('') } const imageHtml = `

    画像

    ` const $image = this._$image setState($image, imageState) $image.html(imageHtml) return this } show() { super.show() if (this._observeElement) this._enableObserver() return this.refresh() } hide() { this._disableObserver() return super.hide() } _initTpl() { const $el = this._$el $el.html( c(`
    `) ) this._$localStorage = $el.find(c('.local-storage')) this._$sessionStorage = $el.find(c('.session-storage')) this._$cookie = $el.find(c('.cookie')) this._$script = $el.find(c('.script')) this._$stylesheet = $el.find(c('.stylesheet')) this._$iframe = $el.find(c('.iframe')) this._$image = $el.find(c('.image')) } _bindEvent() { const $el = this._$el const container = this._container $el .on('click', '.eruda-refresh-script', () => { container.notify('更新しました', { icon: 'success' }) this.refreshScript() }) .on('click', '.eruda-refresh-stylesheet', () => { container.notify('更新しました', { icon: 'success' }) this.refreshStylesheet() }) .on('click', '.eruda-refresh-iframe', () => { container.notify('更新しました', { icon: 'success' }) this.refreshIframe() }) .on('click', '.eruda-refresh-image', () => { container.notify('更新しました', { icon: 'success' }) this.refreshImage() }) .on('click', '.eruda-img-link', function () { const src = $(this).attr('src') showSources('img', src) }) .on('click', '.eruda-css-link', linkFactory('css')) .on('click', '.eruda-js-link', linkFactory('js')) .on('click', '.eruda-iframe-link', linkFactory('iframe')) function showSources(type, data) { const sources = container.get('sources') if (!sources) return sources.set(type, data) container.showTool('sources') return true } function linkFactory(type) { return function (e) { if (!container.get('sources')) return e.preventDefault() const url = $(this).attr('href') if (type === 'iframe' || !sameOrigin(location.href, url)) { showSources('iframe', url) } else { ajax({ url, success: (data) => { showSources(type, data) }, dataType: 'raw', }) } } } } _rmCfg() { const cfg = this.config const settings = this._container.get('settings') if (!settings) return settings .remove(cfg, 'hideErudaSetting') .remove(cfg, 'observeElement') .remove('Resources') } _initCfg() { const cfg = (this.config = Settings.createCfg('resources', { hideErudaSetting: true, observeElement: true, })) if (cfg.get('hideErudaSetting')) this._hideErudaSetting = true if (!cfg.get('observeElement')) this._observeElement = false cfg.on('change', (key, val) => { switch (key) { case 'hideErudaSetting': this._hideErudaSetting = val return case 'observeElement': this._observeElement = val return val ? this._enableObserver() : this._disableObserver() } }) const settings = this._container.get('settings') settings .text('リソース') .switch(cfg, 'hideErudaSetting', 'Eruda設定を非表示にする') .switch(cfg, 'observeElement', '自動リフレッシュ要素') .separator() } _initObserver() { this._observer = new MutationObserver((mutations) => { each(mutations, (mutation) => { this._handleMutation(mutation) }) }) } _handleMutation(mutation) { if (isErudaEl(mutation.target)) return const checkEl = (el) => { const tagName = getLowerCaseTagName(el) switch (tagName) { case 'script': this.refreshScript() break case 'img': this.refreshImage() break case 'link': this.refreshStylesheet() break } } if (mutation.type === 'attributes') { checkEl(mutation.target) } else if (mutation.type === 'childList') { checkEl(mutation.target) let nodes = toArr(mutation.addedNodes) nodes = concat(nodes, toArr(mutation.removedNodes)) for (const node of nodes) { checkEl(node) } } } _enableObserver() { this._observer.observe(document.documentElement, { attributes: true, childList: true, subtree: true, }) } _disableObserver() { this._observer.disconnect() } } function getLowerCaseTagName(el) { if (!el.tagName) return '' return el.tagName.toLowerCase() } const regImg = /\.(jpeg|jpg|gif|png)$/ const isImg = (url) => regImg.test(url)