mini mvvm

自用,bug较多,if和for指令不能使用

目前为 2022-05-07 提交的版本。查看 最新版本

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.gf.qytechs.cn/scripts/444466/1047822/mini%20mvvm.js

作者
ayan0312
版本
0.0.3
创建于
2022-05-04
更新于
2022-05-07
大小
23.6 KB
许可证
暂无

指令

<div v-on:click="myClick"></div>
<div v-style="myStyle"></div>
<div v-show="display"></div>
<div v-show:display></div>
<div v-class="myClass"></div>
<div v-model="myModel"></div>
<div v-ref="myRef"></div>
<div v-text="myText"></div>
<div v-html="myHTML"></div>

全局

const state=observe({
  count:GM_getValue('count',1),
})

new Watcher(null,()=>{
  return state.count
},(newVal)=>{
  GM_setValue('count',newVal)
})

const Counter = {
  template: `
    <button v-on:click="decrease">-</button>
    <span>{{global.count}}</span>
    <button v-on:click="increase">+</button>
  `,
  data(){
    return {
      global:state,
    }
  },
  methods:{
    increase(){
      this.global.count+=1
    },
    decrease(){
      this.global.count-=1
    },
  }
}

用例

// ==UserScript==
// @name        MyFigureCollection: 图片下载器
// @name:en     MyFigureCollection: Image Downloader
// @name:zh-CN  MyFigureCollection: 图片下载器
// @name:zh-TW  MyFigureCollection: 图片下载器
// @description 下载原始图片,相册下拉自动加载
// @description:en The original fullsize images downloader. Album pull down to autoload.
// @description:zh-CN 下载原始图片,相册下拉自动加载
// @description:zh-TW 下载原始图片,相册下拉自动加载
// @namespace   http://tampermonkey.net/
// @match       https://myfigurecollection.net/item/*
// @match       https://myfigurecollection.net/picture/*
// @match       https://myfigurecollection.net/pictures.php*
// @match       https://myfigurecollection.net/browse.v4.php*
// @grant       GM_download
// @grant       GM_xmlhttpRequest
// @grant       GM_getValue
// @grant       GM_setValue
// @require     https://gf.qytechs.cn/scripts/444466-mini-mvvm/code/mini%20mvvm.js?version=1047420
// @license     GPL-3.0
// @compatible  Chrome
// @version     1.5.9
// @author      ayan0312
// ==/UserScript==
const TIME_OUT = 30 * 1000
const REQUEST_URL = ''
const FILTER_URLS = [
    'https://static.myfigurecollection.net/ressources/nsfw.png',
    'https://static.myfigurecollection.net/ressources/spoiler.png'
]
const LOCATION_ITEM_ID = (new URL(location.href)).searchParams.get('itemId') || location.href.split('/item/')[1] || ''

const logger = {
    info(...args) {
        console.log('[Image Downloader]:', ...args)
    },
    warn(...args) {
        console.log('%c[Image Downloader]:', "color: brown; font-weight: bold", ...args)
    },
    error(...args) {
        console.log('%c[Image Downloader]:', "color: red; font-weight: bold", ...args)
    },
    success(...args) {
        console.log('%c[Image Downloader]:', "color: green; font-weight: bold", ...args)
    },
}

function downloadImage(opts) {
    return new Promise((resolve, reject) => {
        if (!REQUEST_URL) {
            GM_download({
                url: opts.url,
                name: opts.name,
                timeout: TIME_OUT,
                onload: () => {
                    resolve(null)
                },
                onerror: (err) => reject({
                    status: 'error',
                    err,
                }),
                ontimeout: (err) => reject({
                    status: 'timeout',
                    err
                }),
            })
            return
        }

        GM_xmlhttpRequest({
            url: `${REQUEST_URL}?opts=${btoa(JSON.stringify(opts))}`,
            responseType: 'json',
            onload: ({ response }) => {
                if (response.success) {
                    resolve(response)
                    return
                }

                if (response.code === 3) {
                    reject({
                        status: 'timeout',
                        err: response.message
                    })
                    return
                }

                reject({
                    status: 'error',
                    err: response.message,
                })
            },
            onerror: (err) => reject({
                status: 'error',
                err,
            }),
            ontimeout: (err) => reject({
                status: 'timeout',
                err
            })
        })
    })
}

function download({ downloadButton, picture, group, count, origin }) {
    const end = (status) => {
        downloadButton.downloadStatus = status
        GM_setValue(picture.split('&')[0], { origin, group, count, downloadStatus: status })
    }
    const [originName,fileType] = origin.split('/').pop().split('.')
    let name = `${group}_${count}_${originName}`

    if (!REQUEST_URL)
        name = `${name}.${fileType}`

    downloadImage({
        url: origin,
        name,
    })
        .then(() => {
            end('downloaded')
        })
        .catch(({ status, err }) => {
            end(status)
            logger.warn(err)
        })
}

const downloadBtnVMs = {}
let waitRedownloadVMs = []

const globalState = observe({
    group: GM_getValue('groupCount', 1),
    count: GM_getValue('picCount', 0),
    componentDownloadStatus: {},
    downloadStates: {
        total: 0,
        normal: 0,
        loading: 0,
        error: 0,
        timeout: 0,
        downloaded: 0,
    },
    autoloadStatus: 'normal'
})

window.onbeforeunload = (e) => {
    const { loading, error, timeout } = globalState.downloadStates
    if (loading || error || timeout)
        e.returnValue = '1'
}

new Watcher(null, () => {
    return globalState.count
}, (newVal) => {
    GM_setValue('picCount', newVal)
})

new Watcher(null, () => {
    return globalState.group
}, (newVal) => {
    GM_setValue('groupCount', newVal)
    globalState.count = 0
})

new Watcher(null, () => {
    return globalState.componentDownloadStatus
}, (newVal) => {
    Object.assign(globalState.downloadStates, {
        total: 0,
        normal: 0,
        loading: 0,
        error: 0,
        timeout: 0,
        downloaded: 0,
    })
    const states = globalState.downloadStates
    waitRedownloadVMs = []
    Object.keys(newVal).forEach(key => {
        const status = newVal[key]
        if (states[status] != null) {
            states[status] += 1
            states.total += 1
        }

        if (status === 'timeout' || status === 'error')
            waitRedownloadVMs.push(downloadBtnVMs[key])
    })
}, true)

const DownloadSequence = {
    template: `
    <span>Group: </span>
    <button v-on:click="decreaseGroup">-</button>
    <span style="margin:0 10px">{{global.group}}</span>
    <button v-on:click="increaseGroup">+</button>
    <span style="margin-left:10px">Item: </span>
    <button v-on:click="decreaseCount">-</button>
    <span style="margin:0 10px">{{global.count}}</span>
    <button v-on:click="increaseCount">+</button>
  `,
    data() {
        return {
            global: globalState,
        }
    },
    methods: {
        increaseCount() {
            this.global.count += 1
        },
        decreaseCount() {
            this.global.count -= 1
        },
        increaseGroup() {
            this.global.group += 1
        },
        decreaseGroup() {
            this.global.group -= 1
        }
    }
}

const DownloadButton = {
    template: `
      <button v-on:click="download" v-style="downloadBtnStyle">
        {{downloadedMsg}}
      </button>
    `,
    data() {
        return {
            oldStatus: 'normal',
            downloadStatus: 'normal', // 'normal' 'loading' 'error' 'timeout' 'downloaded'
            requestButtonStyles: {
                normal: {},
                loading: { background: 'white', color: 'black', cursor: 'wait' },
                error: { background: 'red', color: 'white' },
                timeout: { background: 'yellow', color: 'black' },
                downloaded: { background: 'green', color: 'white' }
            },
            group: globalState.group,
            count: globalState.count,
        }
    },
    computed: {
        downloadBtnStyle() {
            return this.requestButtonStyles[this.downloadStatus]
        },
        downloadedMsg() {
            const messages = {
                normal: 'Download',
                loading: 'Downloading...',
                error: 'Failed',
                timeout: 'Timeout',
                downloaded: 'Redownload'
            }
            return messages[this.downloadStatus]
        }
    },
    watch: {
        downloadStatus(newStatus, oldStatus) {
            this.oldStatus = oldStatus
            globalState.componentDownloadStatus[this.cid] = newStatus
        }
    },
    created() {
        globalState.componentDownloadStatus[this.cid] = this.downloadStatus
        downloadBtnVMs[this.cid] = this
    },
    destoryed() {
        delete globalState.componentDownloadStatus[this.cid]
        delete downloadBtnVMs[this.cid]
    },
    methods: {
        download() {
            if (this.downloadStatus === 'loading') return
            this.downloadStatus = 'loading'

            if (LOCATION_ITEM_ID && !GM_getValue(LOCATION_ITEM_ID)) {
                GM_setValue(LOCATION_ITEM_ID, true)
                this.first = true
            }
            if (this.oldStatus !== 'error' && this.oldStatus !== 'timeout')
                this.refreshGroupAndCount()

            this.$emit('download', { group: this.group, count: this.count })
        },
        refreshGroupAndCount() {
            const newestGroup = GM_getValue('groupCount', 1)
            const newestCount = GM_getValue('picCount', 0)
            if (globalState.group !== newestGroup)
                globalState.group = newestGroup
            if (globalState.count !== newestCount)
                globalState.count = newestCount

            if (this.first && globalState.count > 0)
                globalState.group += 1

            globalState.count += 1

            this.group = globalState.group
            this.count = globalState.count
        }
    }
}

const DownloadState = {
    template: `
    <div style="display:flex;flex-direction:row;padding:10px;flex-wrap:wrap;align-items:center">
      <div style="margin-right:15px;color:black">
        <span style="">Total:</span>
        <span>{{states.total}}</span>
      </div>
      <div style="margin-right:15px;color:green">
        <span style="">Downloaded:</span>
        <span>{{states.downloaded}}</span>
      </div>
      <div style="margin-right:15px;color:grey">
        <span style="">Downloading:</span>
        <span>{{states.loading}}</span>
      </div>
      <div style="margin-right:15px;color:brown">
        <span style="">Timeout:</span>
        <span>{{states.timeout}}</span>
      </div>
      <div style="color:red">
        <span>Failed:</span>
        <span>{{states.error}}</span>
      </div>
      <button style="margin-left:10px" v-show="waitCount" v-on:click="redownload">Redownload Timeout&Error</button>
    </div>
  `,
    data() {
        return {
            states: globalState.downloadStates
        }
    },
    computed: {
        waitCount() {
            return this.states.timeout + this.states.error > 0
        }
    },
    methods: {
        redownload() {
            waitRedownloadVMs.forEach(vm => vm.download())
        }
    }
}

const createPictureDownload = (origin) => {
    return {
        components: {
            'download-button': DownloadButton,
            'download-sequence': DownloadSequence,
        },
        template: `
        <div>
          <download-sequence></download-sequence>
          <span v-show:downloaded>{{msg}}</span>
          <download-button v-ref="downloadButton" v-on:download="download"></download-button>
        </div>
      `,
        data() {
            return {
                group: 0,
                count: 0,
                downloaded: false
            }
        },
        computed: {
            msg() {
                return `Group: ${this.group} Item: ${this.count}`
            }
        },
        mounted() {
            const value = GM_getValue(window.location.href.split('&')[0])
            if (!value) return
            this.$refs.downloadButton.downloadStatus = value.downloadStatus
            this.$refs.downloadButton.group = value.group
            this.$refs.downloadButton.count = value.count

            this.downloaded = true
            this.group = value.group
            this.count = value.count
        },
        methods: {
            download({ group, count }) {
                this.downloaded = true
                this.group = group
                this.count = count

                download({
                    group,
                    count,
                    origin,
                    picture: window.location.href,
                    downloadButton: this.$refs.downloadButton,
                })
            }
        }
    }
}

const createPictrueComponent = ({ thumb, origin, picture }) => {
    return {
        components: {
            'download-button': DownloadButton
        },
        template: `
          <div v-style="containerStyle">
            <div style="margin-bottom:10px;display:flex;flex-direction:row;justify-content:center;align-items:center;width:100%;">
              <div style="margin:0 10px;flex-shrink:0"><img style="cursor:pointer" v-on:click="openPicturePage" [src]="thumb" /></div>
              <div style="flex-shrink:1" v-show="originalImage"><img style="width:100%" [src]="src" /></div>
            </div>
            <div style="display:flex;justify-content:center;align-items:center;flex-direction:column">
                <download-button v-ref="downloadButton" v-on:download="download"></download-button>
                <br v-show:downloaded />
                <div v-show:downloaded>
                  <span>Group:</span>
                  <span >{{group}}</span>
                  <span>Item:</span>
                  <span >{{count}}</span>
                </div>
                <br />
                <button v-on:click="toggle">{{msg}}</button>
                <br />
                <button v-show:refresh v-on:click="refreshOrigin" v-style="refreshBtnStyle">
                  {{refreshMsg}}
                </button>
            </div>
          </div>
      `,
        data() {
            return {
                thumb,
                origin,
                refresh: FILTER_URLS.includes(origin),
                originalImage: false,
                group: 0,
                count: 0,
                downloaded: false,
                refreshStatus: 'normal',
                downloadStatus: 'normal',
                requestBorderStyle: {
                    normal: { border: '2px solid black' },
                    loading: { border: '2px solid grey' },
                    error: { border: '2px solid red' },
                    timeout: { border: '2px solid yellow' },
                    downloaded: { border: '2px dashed green' }
                },
                requestButtonStyles: {
                    normal: {},
                    loading: { background: 'white', color: 'black', cursor: 'wait' },
                    error: { background: 'red', color: 'white' },
                    timeout: { background: 'yellow', color: 'black' },
                    downloaded: { background: 'green', color: 'white' }
                },
                commonStyle: {
                    'margin': '10px 10px',
                    'padding': '10px',
                    'border-radius': '5px',
                    'background': '#fff',
                    'transition': 'all 0.5s',
                    'box-sizing': 'border-box'
                }
            }
        },
        computed: {
            src() {
                return this.originalImage ? this.origin : this.thumb
            },
            msg() {
                return this.originalImage ? 'Close Preview' : 'Preview'
            },
            containerStyle() {
                const borderStyle = this.requestBorderStyle[this.downloadStatus]
                return Object.assign({}, borderStyle, this.commonStyle, this.originalImage ? {
                    width: '100%'
                } : {})
            },
            refreshBtnStyle() {
                return this.requestButtonStyles[this.refreshStatus]
            },
            refreshMsg() {
                const messages = {
                    normal: 'Show Spoiler/NSFW',
                    loading: 'Showing...',
                    error: 'Failed',
                    timeout: 'Timeout',
                    downloaded: 'Reshow'
                }
                return messages[this.refreshStatus]
            }
        },
        mounted() {
            this.$watch(() => this.$refs.downloadButton.downloadStatus, (newVal) => {
                this.downloadStatus = newVal
            })

            const value = GM_getValue(picture.split('&')[0])
            if (!value) return
            this.$refs.downloadButton.downloadStatus = value.downloadStatus
            this.$refs.downloadButton.group = value.group
            this.$refs.downloadButton.count = value.count

            this.downloaded = true
            this.group = value.group
            this.count = value.count
        },
        methods: {
            download({ group, count }) {
                this.downloaded = true
                this.group = group
                this.count = count

                const params = {
                    group,
                    count,
                    origin: this.origin,
                    picture: picture,
                    downloadButton: this.$refs.downloadButton
                }

                if (this.refresh && this.refreshStatus !== 'downloaded') {
                    this.refreshOrigin()
                        .then(() => {
                            params.origin = this.origin
                            download(params)
                        })
                    return
                }

                download(params)
            },
            toggle() {
                if (this.refresh && !this.originalImage && this.refreshStatus !== 'downloaded')
                    this.refreshOrigin()

                this.originalImage = !this.originalImage
            },
            refreshOrigin() {
                if (this.refreshStatus === 'loading') return
                this.refreshStatus = 'loading'
                return new Promise((resolve, reject) => {
                    GM_xmlhttpRequest({
                        url: picture,
                        responseType: 'document',
                        timeout: TIME_OUT,
                        onload: (data) => {
                            const doc = data.response
                            const a = doc.querySelector('.the-picture>a')
                            if (a) {
                                this.origin = a.href
                                const thumb = a.href.split('/')
                                thumb.splice(thumb.length - 1, 0, 'thumbnails')
                                this.thumb = thumb.join('/')
                                this.refreshStatus = 'downloaded'
                                resolve()
                                return
                            }
                            this.refreshStatus = 'error'
                            reject({
                                status: 'error',
                                err: data
                            })
                        },
                        onerror: (err) => {
                            this.refreshStatus = 'error'
                            reject({
                                status: 'error',
                                err,
                            })
                        },
                        ontimeout: (err) => {
                            this.refreshStatus = 'timeout'
                            reject({
                                status: 'timeout',
                                err,
                            })
                        }
                    })
                })
            },
            openPicturePage() {
                window.open(picture)
            }
        }
    }
}

const AutoloadMessageBox = {
    template: `
        <div style="width:100%;box-sizing:border-box;text-align:center;padding:15px">{{msg}}</div>
    `,
    data() {
        return {}
    },
    computed: {
        msg() {
            const messages = {
                normal: 'Pull Down',
                loading: 'Auto Loading...',
                error: 'Failed',
                timeout: 'Timeout',
                downloaded: 'Loaded',
                empty: 'Loaded All'
            }

            return messages[globalState.autoloadStatus]
        }
    },
}

const createItemTips = (itemId) => {
    return {
        template: `
            <span v-show="existItemId">Have been downloaded</span>
        `,
        data() {
            return {
            }
        },
        computed: {
            existItemId() {
                return !!GM_getValue(itemId)
            }
        }
    }
}

class DocumentUtility {
    static getElements(doc, selector) {
        const thumbs = []
        const pics = doc.querySelectorAll(selector) || []
        pics.forEach(thumb => {
            thumbs.push(thumb)
        })
        return thumbs
    }

    static insertAfter(targetNode, afterNode) {
        const parentNode = afterNode.parentNode
        const beforeNode = afterNode.nextElementSibling
        if (beforeNode == null)
            parentNode.appendChild(targetNode)
        else
            parentNode.insertBefore(targetNode, beforeNode)
    }
}

class Renderer {
    render() {
        throw new Error('Not implemented')
    }

    disposeRenderError(err, name) {
        logger.error(err || 'Unknown error')
        logger.error('Fail to render extension of ' + name)
    }
}

class PictureRenderer extends Renderer {
    constructor() {
        super()
        this.objectMetaNode = document.querySelector('.object-meta')
        this.pictureNode = document.querySelector('.the-picture>a')
    }

    render() {
        if (this.objectMetaNode && this.pictureNode) {
            try {
                this.renderThePictureExtension()
            } catch (err) {
                this.disposeRenderError(err, 'picture')
            }
        }
    }

    renderThePictureExtension() {
        const div = document.createElement('div')
        this.objectMetaNode.appendChild(div)
        mountComponent(div, createPictureDownload(this.pictureNode.href))
    }
}

class AutoloadListRenderer extends Renderer {
    constructor(listNode, callback) {
        super()
        this.listNode = listNode
        this.nextURL = this._getNextPageURL()
        this.callback = callback
        this.scrollingElement = document.scrollingElement
    }

    render() {
        if (this.listNode) {
            try {
                this.renderAutoloadListExtension()
            } catch (err) {
                this.disposeRenderError(err, 'autoloading list')
            }
        }
    }

    renderAutoloadListExtension() {
        let wait = false
        const complete = (status) => {
            wait = false
            globalState.autoloadStatus = status
        }

        if (!this.nextURL) {
            complete('empty')
            return
        }

        this._mountAutoloadMessageBox()
        window.onscroll = (e) => {
            const bottom = this.listNode.offsetTop + this.listNode.clientHeight + 50
            const top = this.scrollingElement.scrollTop + window.screen.height
            if (top < bottom || wait || !this.nextURL) return
            wait = true
            globalState.autoloadStatus = 'loading'
            GM_xmlhttpRequest({
                url: this.nextURL,
                responseType: 'document',
                timeout: TIME_OUT,
                onload: (data) => {
                    const doc = data.response
                    this.callback(doc)
                    this.nextURL = this._getNextPageURL(doc)
                    if (!this.nextURL) {
                        complete('empty')
                        return
                    }

                    complete('downloaded')
                },
                onerror: () => complete('error'),
                ontimeout: () => complete('timeout')
            })
        }
    }

    _getNextPageURL(doc = document) {
        const nextAnchor = doc.querySelector('.nav-next.nav-end')
        return nextAnchor ? nextAnchor.href : ''
    }

    _mountAutoloadMessageBox() {
        const countPages = document.querySelector('.listing-count-pages')
        if (countPages) {
            this.listNode.parentNode.insertBefore(countPages.cloneNode(true), this.listNode)
            mountComponent(countPages, AutoloadMessageBox)
        }
    }
}

class PicturesRenderer extends Renderer {
    constructor() {
        super()
        this.pictureNodes = DocumentUtility.getElements(document, '.picture-icon.tbx-tooltip')
        this.autoloadList = new AutoloadListRenderer(document.querySelector('.listing-item'), (doc) => {
            const nextPictureNodes = DocumentUtility.getElements(doc, '.picture-icon.tbx-tooltip')
            this._rewritePictureNodes(nextPictureNodes)
        })
    }

    render() {
        if (this.pictureNodes.length > 0) {
            try {
                this.renderPicturesExtension()
            } catch (err) {
                this.disposeRenderError(err, 'pictures')
            }
        }
    }

    renderPicturesExtension() {
        this.picturesParent = this.pictureNodes[0].parentNode
        this._initPicturesParent()
        this._rewritePictureNodes(this.pictureNodes)
        this.autoloadList.render()
    }

    _initPicturesParent() {
        this.picturesParent.innerHTML = ''
        this.picturesParent.style.setProperty('display', 'flex')
        this.picturesParent.style.setProperty('flex-direction', 'row')
        this.picturesParent.style.setProperty('flex-wrap', 'wrap')
        this.picturesParent.style.setProperty('justify-content', 'center')
        this.picturesParent.style.setProperty('align-items', 'center')
        const div1 = document.createElement('div')
        const div2 = document.createElement('div')
        div1.appendChild(div2)
        div1.style.padding = '10px'
        this.picturesParent.parentNode.insertBefore(div1, this.picturesParent)
        mountComponent(div2, DownloadSequence)
        this._renderDownloadState()
    }

    _renderDownloadState() {
        const div = document.createElement('div')
        const ds = document.createElement('ds')
        div.appendChild(ds)
        div.style.position = 'fixed'
        div.style.bottom = 0
        div.style.right = 0
        div.style.backgroundColor = 'white'
        document.body.appendChild(div)
        mountComponent(ds, DownloadState)
    }

    _rewritePictureNodes(pictures) {
        pictures.forEach(picture_node => {
            this._mountPictureComponent(this._createPictureComponent(picture_node))
        })
    }

    _createPictureComponent(pictureNode) {
        return createPictrueComponent(this._getImageURLs(pictureNode))
    }

    _getImageURLs(picture_node) {
        const picture = picture_node.querySelector('a').href
        const viewport = picture_node.querySelector('.viewport')
        const thumb = viewport.style.background.split('"')[1]
        const origin = FILTER_URLS.includes(thumb) ? thumb : this._parseOriginalImageURL(thumb)
        return { thumb, origin, picture }
    }

    _parseOriginalImageURL(thumb_url) {
        let url = thumb_url
        if (thumb_url.indexOf('thumbnails/') > -1) {
            url = thumb_url.split('thumbnails/').join('')
        } else {
            const paths = thumb_url.split('pictures/')[1].split('/')
            if (paths.length > 2) {
                paths.splice(3, 1)
                url = [thumb_url.split('pictures/')[0], paths.join('/')].join('pictures/')
            }
        }
        return url
    }

    _mountPictureComponent(component) {
        const div = document.createElement('div')
        this.picturesParent.appendChild(div)
        return mountComponent(div, component)
    }
}

class DiaporamasRenderer extends Renderer {
    constructor() {
        super()
        this.diaporamaNodes = DocumentUtility.getElements(document, '.diaporama')
    }

    render() {
        if (this.diaporamaNodes.length > 0) {
            try {
                this.renderDiaporamasExtension()
            } catch (err) {
                this.disposeRenderError(err, 'diaporamas')
            }
        }
    }

    renderDiaporamasExtension() {
        this._updateDiaporamaNodes()
    }

    _updateDiaporamaNodes(diaporamaNodes) {
        this.diaporamaNodes.forEach(diaporamaNode => {
            const a = diaporamaNode.querySelector('a')
            const itemId = a.href.split('/item/')[1]
            const div = document.createElement('div')
            a.appendChild(div)
            mountComponent(div, createItemTips(itemId))
        })
    }
}

class ItemRenderer extends Renderer {
    constructor() {
        super()
        this.itemNode = document.querySelector('.split-left.righter')
    }

    render() {
        if (this.itemNode) {
            try {
                this.renderItemExtension()
            } catch (err) {
                this.disposeRenderError(err, 'item')
            }
        }
    }

    renderItemExtension() {
        this._moveRelatedItems()
        this._initItemNode()
        this._mountPicturesNavigator()
        this._overwritePicturesNavigatingParameters()
    }

    _moveRelatedItems() {
        DocumentUtility.insertAfter(document.querySelector('.tbx-target-ITEMS'), document.querySelector('.tbx-target-LISTS'))
    }

    _initItemNode() {
        this.itemNode.style.display = 'flex'
        this.itemNode.style.flexDirection = 'column'
        this.itemNode.style.justifyContent = 'center'
        this.itemNode.style.alignItems = 'center'
    }

    _mountPicturesNavigator() {
        const div = document.createElement('div')
        this.itemNode.appendChild(div)
        mountComponent(div, createItemTips(LOCATION_ITEM_ID))
    }

    _overwritePicturesNavigatingParameters() {
        const navigator = document.querySelector('.icon.icon-camera+a.count')
        navigator.href = `/pictures.php?itemId=${LOCATION_ITEM_ID}&sort=date&order=asc`
    }
}

class ExtensionRenderer extends Renderer {
    static renderer = new ExtensionRenderer()
    static rendered = false

    static render() {
        if (!this.rendered) {
            this.rendered = true
            this.renderer.render()
        }
    }

    constructor() {
        super()
        this.item = new ItemRenderer()
        this.picture = new PictureRenderer()
        this.pictures = new PicturesRenderer()
        this.diaporamas = new DiaporamasRenderer()
    }

    render() {
        this.item.render()
        this.picture.render()
        this.pictures.render()
        this.diaporamas.render()
    }

}

ExtensionRenderer.render()

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址