mini mvvm

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

目前為 2022-05-04 提交的版本,檢視 最新版本

此腳本不應該直接安裝,它是一個供其他腳本使用的函式庫。欲使用本函式庫,請在腳本 metadata 寫上: // @require https://update.cn-greasyfork.org/scripts/444466/1047054/mini%20mvvm.js

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

作者
ayan0312
版本
0.0.1
建立日期
2022-05-04
更新日期
2022-05-04
尺寸
23.3 KB
授權條款
未知

用例

const TIME_OUT = 60 * 1000
const FILTER_URLS = [
  'https://static.myfigurecollection.net/ressources/nsfw.png',
  'https://static.myfigurecollection.net/ressources/spoiler.png'
]

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

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,{
    normal:0,
    loading:0,
    error:0,
    timeout:0,
    downloaded:0,
  })
  const states = globalState.downloadStates
  Object.keys(newVal).forEach(key=>{
    const status = newVal[key]
    if(states[status] != null)
        states[status]+=1
  })
},true)

function beforeDownload(){
  if(GM_getValue('groupCount',1) != globalState.group){
    const curCount = GM_getValue('picCount',1) + 1
    globalState.group = GM_getValue('groupCount',1)
    globalState.count = curCount
  }else{
    globalState.group = GM_getValue('groupCount',1)
    globalState.count = GM_getValue('picCount',1) + 1
  }

  return {group:globalState.group,count:globalState.count}
}

const DownloadSequence = {
  template: `
    <span>当前组:</span>
    <button v-on:click="decreaseGroup">-</button>
    <span>{{global.group}}</span>
    <button v-on:click="increaseGroup">+</button>
    <span>当前图片:</span>
    <button v-on:click="decreaseCount">-</button>
    <span>{{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 REQEUST_BUTTON_STYLES = {
  normal:{},
  loading: { background: 'white',color:'black',cursor:'wait'},
  error:{background:'red',color:'white'},
  timeout:{background:'yellow',color:'black'},
  downloaded: {background:'green',color:'white'}
}

const DownloadButton = {
    template: `
      <button v-on:click="download" v-style="downloadBtnStyle">
        {{downloadedMsg}}
      </button>
    `,
    data(){
      return {
        oldStatus:'normal',
        downloadStatus:'normal' // 'normal' 'loading' 'error' 'timeout' 'downloaded'
      }
    },
    computed:{
      downloadBtnStyle(){
        return REQEUST_BUTTON_STYLES[this.downloadStatus]
      },
      downloadedMsg(){
        const messages = {
          normal:'下载原图' ,
          loading: '正在下载...',
          error:'下载失败',
          timeout:'下载超时',
          downloaded:  '重新下载'
        }
        return messages[this.downloadStatus]
      }
    },
    watch:{
      downloadStatus(newStatus,oldStatus){
        this.oldStatus = oldStatus
        globalState.componentDownloadStatus[this.cid] = newStatus
      }
    },
    created(){
      globalState.componentDownloadStatus[this.cid] = this.downloadStatus
    },
    destoryed(){
      delete globalState.componentDownloadStatus[this.cid]
    },
    methods:{
      download(){
        if(this.downloadStatus === 'loading') return
        this.downloadStatus = 'loading'
        if(this.oldStatus !== 'error' && this.oldStatus !== 'timeout')
          this.value = beforeDownload()
        this.$emit('download',this.value)
      },
    }
}

const DownloadState = {
  template:`
    <div style="display:flex;flex-direction:row;padding:5px">
      <div style="margin-right:5px;color:grey">
        <span style="">未下载:</span>
        <span>{{states.normal}}</span>
      </div>
      <div style="margin-right:5px;color:green">
        <span style="">已下载:</span>
        <span>{{states.downloaded}}</span>
      </div>
      <div style="margin-right:5px;color:black">
        <span style="">正在下载:</span>
        <span>{{states.loading}}</span>
      </div>
      <div style="margin-right:5px;color:brown">
        <span style="">下载超时:</span>
        <span>{{states.timeout}}</span>
      </div>
      <div style="color:red">
        <span>下载报错:</span>
        <span>{{states.error}}</span>
      </div>
    </div>
  `,
  data(){
    return {
      states:globalState.downloadStates
    }
  },
  methods:{
    download(value){
      this.$emit('download',value)
    }
  }
}

const PictureDownload = {
  components:{
    'download-button':DownloadButton,
    'download-sequence':DownloadSequence,
  },
  template:`
    <div>
      <download-sequence></download-sequence>
      <span v-show:downloaded style="margin:0 10px">{{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 `${this.group}.${this.count}`
    }
  },
  mounted(){
      const value = GM_getValue(window.location.href)
      if(!value) return
      this.$refs.downloadButton.downloadStatus = value.downloadStatus
      this.$refs.downloadButton.value = {
        group: value.group,
        count: value.count
      }
      this.downloaded = true
      this.group = value.group
      this.count = value.count
  },
  methods:{
    download(value){
      this.downloaded = true
      this.group = value.group
      this.count = value.count
      this.$emit('download',value)
    }
  }
}

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

function mountVM(node,component){
  const vm = new MVVMComponent(component)
  vm.$mount(node)
  return vm
}

function download({vm,picture,group,count,origin}){
  const values = origin.split('/')
  const fileType = values[values.length - 1].split('.')[1]
  const name = `${group}.${count}.${fileType}`
  const end = (status)=>{
    return ()=>{
      vm.downloadStatus = status
      GM_setValue(picture,{origin,group,count,downloadStatus:status})
    }
  }

  GM_download({
    url:origin,
    name,
    timeout:TIME_OUT,
    onload:end('downloaded'),
    onerror:end('error'),
    ontimeout:end('timeout'),
  })
}

function renderPictureExtension(objectMeta,thePicture){
  const origin = thePicture.href
  const div = document.createElement('div')
  objectMeta.appendChild(div)
  const vm = mountVM(div,PictureDownload)
  vm.$on('download',(value)=>{
    download({
      ...value,
      origin,
      picture:window.location.href,
      vm:vm.$refs.downloadButton,
    })
  })
}

const createPictruePreview = ({thumb,origin,picture}) => {
  const commonStyle={
    'margin':'10px 10px',
    'border':'1px solid #000',
    'padding':'10px',
    'border-radius':'5px',
    'background':'#fff',
    'transition':'all 0.5s',
    'box-sizing': 'border-box'
  }

  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>组:</span>
                  <span >{{group}}</span>
                  <span>图片:</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,
          picture,
          refresh:FILTER_URLS.includes(origin),
          originalImage:false,
          group:0,
          count:0,
          downloaded:false,
          refreshStatus:'normal' // 'normal' 'loading' 'error' 'timeout' 'downloaded'
        }
      },
      computed:{
        src(){
          return this.originalImage ? this.origin : this.thumb
        },
        msg(){
          return this.originalImage ? '关闭预览' : '预览原图'
        },
        containerStyle(){
          return Object.assign({},commonStyle,this.originalImage ? {
            width:'100%'
          }:{} ) 
        },
        refreshBtnStyle(){
          return REQEUST_BUTTON_STYLES[this.refreshStatus]
        },
        refreshMsg(){
          const messages = {
            normal:'获取隐藏图片' ,
            loading: '正在获取...',
            error:'获取失败',
            timeout:'获取超时',
            downloaded:  '重新获取'
          }
          return messages[this.refreshStatus]
        }
      },
      mounted(){
        const value = GM_getValue(picture)
        if(!value) return
        this.$refs.downloadButton.downloadStatus = value.downloadStatus
        this.$refs.downloadButton.value = {
          group: value.group,
          count: value.count
        }
        this.downloaded = true
        this.group = value.group
        this.count = value.count
      },
      methods:{
        download(value){
          this.downloaded = true
          this.group = value.group
          this.count = value.count

          if(this.refresh && this.refreshStatus !== 'downloaded')
            this.refreshOrigin()
              .then(()=>{
                this.$emit('download',{...value,origin:this.origin})
              })
          else
            this.$emit('download',{...value,origin:this.origin})
        },
        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:this.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()
              },
              onerror:()=>{
                this.refreshStatus = 'error'
                reject()
              },
              ontimeout:()=>{
                this.refreshStatus = 'timeout'
                reject()
              }
            })
          })
        },
        openPicturePage(){
          window.open(picture)
        }
      }
  }
}

function parseOriginalImageURL(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
}

function getImageURLs(node){
  const picture = node.querySelector('a').href
  const viewport = node.querySelector('.viewport')
  const thumb = viewport.style.background.split('"')[1]
  let origin = thumb
  if(FILTER_URLS.includes(origin))
    origin = thumb
  else
    origin = parseOriginalImageURL(origin)

  return {thumb,origin,picture}
}

function renderPicturesExtension(thumbs){
  function _initParentNode(parentNode){
    parentNode.innerHTML = ''
    parentNode.style.setProperty('display','flex')
    parentNode.style.setProperty('flex-direction','row')
    parentNode.style.setProperty('flex-wrap','wrap')
    parentNode.style.setProperty('justify-content','center')
    parentNode.style.setProperty('align-items','center')
    const div1 = document.createElement('div')
    parentNode.parentNode.insertBefore(div1,parentNode)
    mountVM(div1,DownloadSequence)
    const div2 = document.createElement('div')
    const pageCount = document.querySelector('.listing-count-pages') || parentNode
    pageCount.parentNode.insertBefore(div2,pageCount)
    mountVM(div2,DownloadState)
  }

  const parentNode = thumbs[0].parentNode
  _initParentNode(parentNode)
  thumbs.forEach(thumb_node=>{
    const imageURLs = getImageURLs(thumb_node)
    const preview = createPictruePreview(imageURLs)
    const div3 = document.createElement('div')
    parentNode.appendChild(div3)
    const previewVM = mountVM(div3,preview)
    previewVM.$on('download',(value)=>{
      download({
        ...value,
        picture:imageURLs.picture,
        vm:previewVM.$refs.downloadButton,
      })
    })
  })
}

function render(){
  const objectMeta = document.querySelector('.object-meta')
  const thePicture = document.querySelector('.the-picture>a')
  if(objectMeta && thePicture)
    renderPictureExtension(objectMeta,thePicture)

  const thumbs = []
  document.querySelectorAll('.picture-icon.tbx-tooltip').forEach(thumb=>{
    thumbs.push(thumb)  
  })
  if(thumbs.length > 0)
    renderPicturesExtension(thumbs.reverse())
}

render()