User:Irukaza/common.js

H萌娘,万物皆可H的百科全书!
< User:Irukaza
imported>=海豚=2020年4月14日 (二) 11:15的版本
跳到导航 跳到搜索

注意:这类代码页面在保存之后,您可能需要清除浏览器缓存才能看到所作出的变更的影响。

  • 按住CTRL+SHIFT+DEL 或 ⌘-Shift-R来清除缓存!
  • 或尝试在地址栏的地址最后添加代码?_=1来访问最新页面。
    添加代码后的本页地址如下:-{R|https://hmoegirl.com/User:Irukaza/common.js?_=1}-
  • 你还可以在设置中勾选小工具在页面右上角添加清除缓存按钮!
/*
	该插件为一个快捷的文件上传工具,免去先进入图站再上传的步骤。
	同时支持拖拽上传、批量上传、添加前缀等。
	注意:批量上传时,单次上传的所有文件将共享设置的分类和前缀。
	因为测试需要上传图片,上传后还得提请挂删,该插件没有经过充分的测试,遭遇一些问题后可能会没有对应的提示,
	因此,上传发生异常时请进入图站的监视列表进行确认,一切以图站的数据为准。
*/
$(function () {
  if(typeof Promise == 'undefined'){
  	mw.notify('你的浏览器不支持该插件,请升级浏览器或将该插件移除')
  	return
  }
	
  function request(data) {
    data.origin = "https://zh.moegirl.org"
    return new Promise(function (resolve, reject) {
      $.ajax({
        url: 'https://commons.moegirl.org/api.php',
        type: 'post',
        timeout: 5000,
        xhrFields: { withCredentials: true },
        data: data
      }).done(resolve).fail(reject)
    })
  }

  function getHints(word) {
    return request({
      "action": "query",
      "format": "json",
      "list": "search",
      "srsearch": word,
      "srnamespace": "14",
      "srlimit": "7"
    })
  }

  function getToken() {
    return request({
      "action": "query",
      "format": "json",
      "meta": "tokens",
    }).then(function (data) {
      return data.query.tokens.csrftoken
    })
  }

  function upload(file, name, comment) {
    return new Promise(function (resolve, reject) {
      getToken().then(function (token) {
        var data = {
          filename: name,
          comment: comment,
          action: 'upload',
          ignorewarnings: true,
          token: token,
          origin: "https://zh.moegirl.org"
        }
        
        if(typeof file == 'string'){
        	data.url = file
        }else{
        	data.file = file
        }

        var formData = new FormData()
        Object.keys(data).forEach(function (key) {
          formData.append(key, data[key])
        })

        $.ajax({
          url: 'https://commons.moegirl.org/api.php',
          type: 'post',
          timeout: 5000,
          xhrFields: { withCredentials: true },
          contentType: false,
          processData: false,
          data: formData
        }).done(resolve).fail(reject)
      })
    })
  }

  function checkFileName(name) {
    return request({
      action: 'query',
      format: 'json',
      formatversion: 2,
      titles: 'File:' + name,
      prop: 'imageinfo',
      iiprop: 'uploadwarning',
      errorformat: 'html',
      errorlang: 'zh - hans'
    }).then(function (data) {
      return data.query.pages[0].missing
    })
  }

  var bodyHTML = [' <div id="widget-fileUploader" style="display:none">',
    '    <input type="file" id="file-uploader" multiple="multiple" accept=".ogg, .ogv, .oga, .flac, .opus, .wav, .webm, .mp3, .png, .gif, .jpg, .jpeg, .webp, .svg, .pdf, .ppt, .jp2, .doc, .docx, .xls, .xlsx, .psd, .sai, .swf, .mp4">',
    '    <div id="file-close-btn">&times;</div>',
    '    <div class="main">',
    '      <label for="file-uploader">',
    '        <div class="view" id="file-view">',
    '          <div class="promptbox" id="file-promptbox">',
    '            <div class="prompt">点此添加文件,或将文件拖放至此</div>',
    '          </div>',
    '          <div class="images-view" id="file-images"></div>',
    '        </div>',
    '      </label>',
    '      <div class="form">',
    '        <div class="row">',
    '          <div class="input-container">',
    '            <label for="file-filename">文件名:</label>',
    '            <input type="text" id="file-filename" data-type="name">',
    '          </div>',
    '          <div class="input-container">',
    '            <label for="file-categories-input">分&emsp;类:</label>',
    '            <input type="text" id="file-category-input">',
    '            <div class="input-hint">按下回车添加分类</div>',
    '            <div id="file-category-hints" tabindex="0" style="display:none"></div>',
    '          </div>',
    '          <div id="file-categories"></div>',
    '        </div>',
    '        <div class="row">',
    '          <div class="input-container">',
    '            <label for="file-charaname">角色名:</label>',
    '            <input type="text" id="file-charaname" data-type="charaName">',
    '          </div>',
    '          <div class="input-container">',
    '            <label for="file-author">作&emsp;者:</label>',
    '            <input type="text" id="file-author" data-type="author">',
    '          </div>',
    '          <div class="input-container">',
    '            <label for="file-source">源地址:</label>',
    '            <input type="text" id="file-source" data-type="source">',
    '          </div>',
    '        </div>',
    '        <div class="row" style="flex-direction:column; justify-content:space-around;">',
    '          <div class="input-container">',
    '            <label for="file-prefix">添加前缀:</label>',
    '            <input type="text" id="file-prefix" data-type="prefix" style="width:calc(100% - 6em)">',
    '          </div>',    
    '          <button id="file-addUrlFile">添加源地址文件</button>',
    '          <button id="file-submit">提交</button>',
    '        </div>',
    '      </div>',
    '    </div>',
    '  </div>',
    '  <style>',
    '    #widget-fileUploader{'<a id="wgCopyURL-url-copy-<!--{$id}-->" href="javascript:void(0);" onclick="copyURL_<!--{$id}-->(this)" data-clipboard-text=""><!--{$title}--><span style="display:none" id="wgCopyURL-url-<!--{$id}-->"><!--{$url}--></span></a>
<script>
clip_board = new ClipboardJS('#wgCopyURL-url-copy-<!--{$id}-->');
function copyURL_<!--{$id}-->(btn)
{
	var copyText = document.getElementById("wgCopyURL-url-<!--{$id}-->").innerHTML.replace(/&amp;/g, "&");
	btn.setAttribute("data-clipboard-text", copyText);
	alert("链接已复制: " + copyText);
}
</script>
    '    }',
    '    #file-close-btn{',
    '      font-size: 30px;',
    '      font-weight: bold;',
    '      color: white;',
    '      font-family: SimSun;',
    '      position: fixed;',
    '      top: 10px;',
    '      right: 20px;',
    '      transition: transform 0.3s;',
    '      z-index: 10001;',
    '      cursor: pointer;     ',
    '    }',
    '    #file-close-btn:hover{',
    '      transform: rotate(90deg);',
    '    }',
    '    #widget-fileUploader .main{',
    '      width: 60%;',
    '      min-width: 650px;',
    '      height: 500px;',
    '      background: white;',
    '      border-radius: 10px;',
    '      border: 5px #eee solid;',
    '      position: absolute;',
    '      top: 0; left: 0; bottom: 0; right: 0;',
    '      margin: auto;',
    '    }',
    '    #widget-fileUploader .view{',
    '      height: 70%;',
    '      background: white;',
    '      border-radius: 10px 10px 0 0;',
    '      position: relative;',
    '      border-bottom: 3px #ccc solid;',
    '      box-sizing: border-box;',
    '      overflow: auto;',
    '      cursor: pointer;',
    '    }',
    '    #widget-fileUploader .view .promptbox{',
    '      position: absolute;',
    '      width: 100%;',
    '      height: 100%;',
    '      top: 0;',
    '      left: 0;',
    '    }',
    '    #widget-fileUploader .view .promptbox::before,',
    '    #widget-fileUploader .view .promptbox::after{',
    '      content: \'\';',
    '      width: 40px;',
    '      height: 150px;',
    '      background: #ddd;',
    '      position: absolute;',
    '      top: 0; left: 0; bottom: 0; right: 0;',
    '      margin: auto;      ',
    '    }',
    '    #widget-fileUploader .view .promptbox::after{',
    '      width: 150px;',
    '      height: 40px;',
    '    }',
    '    #widget-fileUploader .view .prompt{',
    '      font-size: 22px;',
    '      color: #ddd;',
    '      position: absolute;',
    '      left: 50%;',
    '      transform: translateX(-50%);',
    '      bottom: 10px;',
    '      white-space: nowrap;',
    '    }',
    '    #file-uploader{',
    '      display: none;',
    '    }',
    '    #file-images{',
    '      height: 100%;',
    '      overflow: auto;',
    '      box-sizing: border-box;',
    '      padding: 10px;',
    '    }',
    '    #file-images .imagebox,',
    '    #file-images .lastbox{',
    '      width: 200px;',
    '      height: 150px;',
    '      box-sizing: border-box;',
    '      background: white;',
    '      border: 1px #ccc solid;',
    '      margin: 10px;',
    '      display: inline-block;',
    '      position: relative;',
    '      cursor: pointer;',
    '      vertical-align: middle;',
    '    }',
    '    #file-images .typebox{',
    '      width: 100%;',
    '      height: 100%;',
    '      display: flex;',
    '      justify-content: center;',
    '      align-items: center;',
    '      font-size: 17px;',
    '      color: #666;',
    '	}',
    '    #file-images .lastbox::before,',
    '    #file-images .lastbox::after{',
    '      content: \'\';',
    '      width: 15px;',
    '      height: 60px;',
    '      background: #ddd;',
    '      position: absolute;',
    '      top: 0; left: 0; bottom: 0; right: 0;',
    '      margin: auto;      ',
    '    }',
    '    #file-images .lastbox::after{',
    '      width: 60px;',
    '      height: 15px;',
    '    }',
    '    #file-images .imagebox.selected::after{',
    '      content: \'\';',
    '      display: block;',
    '      position: absolute;',
    '      width: 100%;',
    '      height: 100%;',
    '      top: 0;',
    '      left: 0;',
    '      box-sizing: border-box;',
    '      border: 3px #ccc solid;',
    '      pointer-events: none;',
    '    }',
    '    .file-remove-btn{',
    '      width: 20px;',
    '      height: 20px;',
    '      border-radius: 50%;',
    '      text-align: center;',
    '      line-height: 20px;',
    '      font-weight: bold;',
    '      font-family: \'黑体\';',
    '      position: absolute;',
    '      top: 5px;',
    '      right: 5px;',
    '    }',
    '    .file-remove-btn:hover{',
    '      background: #666;',
    '      color: white;',
    '    }',
    '    #file-images .imagebox > img{',
    '      width: 100%;',
    '      height: 100%;',
    '      padding: 5px;',
    '      box-sizing: border-box;',
    '      object-fit: scale-down;',
    '    }',
    '    #file-images .imagebox::before{',
    '      content: attr(title);',
    '      display: block;',
    '      width: 100%;',
    '      position: absolute;',
    '      bottom: 0;',
    '      left: 0;',
    '      background: rgba(0, 0, 0, 0.5);',
    '      color: white;',
    '      font-size: 13px;',
    '      text-align: center;',
    '      line-height: 25px;',
    '    }',
    '    #widget-fileUploader .form{',
    '      height: calc(30% - 20px);',
    '      padding: 10px;',
    '      display: flex;',
    '    }',
    '    #widget-fileUploader .form .row{',
    '      display: flex;',
    '      flex: 1;',
    '      flex-wrap: wrap;',
    '      align-items: center;',
    '      height: 100%;',
    '      padding: 0 10px;',
    '    }',
    '    #widget-fileUploader .form .row .input-container{',
    '      min-width: 240px;',
    '      position: relative;',
    '    }',
    '    #widget-fileUploader .form .row .input-container > *{',
    '      vertical-align: middle;',
    '      font-size: 14px;',
    '    }',
    '    #widget-fileUploader .form .row .input-container input{',
    '      box-sizing: border-box;',
    '      width: calc(100% - 5em);',
    '      min-width: 150px;',
    '    }',
    '    #widget-fileUploader .input-container .input-hint{',
    '      opacity: 0;',
    '      transition: opacity 0.2s;',	
    '      background: #fffeee;',	
    '      border: 1px #ccc solid;',	
    '      padding: 2px 10px;',	
    '      position: absolute;',	
    '      bottom: calc(100% - 7px);',	
    '      left: calc(100% - 7px);',	
    '      z-index: 1;',	
    '      border-radius: 5px;',	
    '      white-space: nowrap;',	
    '    }',
    '    #widget-fileUploader .input-container input:focus + .input-hint{',
    '      opacity: 1;',	
    '    }',
    '    #file-categories{',
    '      width: 100%;',
    '      height: 23px;',
    '      border: 1px #ccc solid;',
    '      border-radius: 5px;',
    '      overflow: auto;',
    '      margin-right: 5px;',
    '      box-sizing: border-box;',
    '    }',
    '    #file-category-hints{',
    '      min-width: 170px;',
    '      max-height: 140px;',
    '      background: white;',
    '      white-space: nowrap;',
    '      overflow: auto;',
    '      position: absolute;',
    '      right: 9px;',
    '      bottom: 100%;',
    '      border: 1px #666 solid;',
    '      box-sizing: border-box;',
    '      border-bottom: none;',
    '    }',
    '    #file-category-hints .category-hint{',
    '      line-height: 20px;',
    '      box-sizing: border-box;',
    '      padding: 0 5px;',
    '      width: 100%;',
    '      overflow: hidden;',
    '	   text-overflow: ellipsis;',
    '	   white-space: nowrap;',
    '    }',
    '    #file-category-hints .category-hint.selected{',
    '      background: #ccc;',
    '    }',
    '    #file-categories .categorybox{',
    '      display: inline-block;',
    '      line-height: 15px;',
    '      text-align: center;',
    '      border: 1px #666 solid;',
    '      background: #eee;',
    '      margin: 2px 3px;',
    '      padding: 0 5px;',
    '	   font-size: 14px',
    '    }',
    '    #file-submit{',
    '      min-width: 70px;',
    '      text-align: center;',
    '      background: #eee;',
    '      border: 1px #ccc solid;',
    '      color: #666;',
    '      padding: 5 0px;',
    '      font-size: 16px;',
    '      cursor: pointer;',
    '    }',
    '    #file-submit:hover{',
    '      opacity: 0.8;',
    '    }',
    '  </style>'].join('');

  $('body').append(bodyHTML)
  $('#p-cactions ul').append('<li id="btn-fileUploader"><a title="上传文件">上传文件</a></li>')
  $('#btn-fileUploader').click(function () {
    $('#widget-fileUploader').fadeIn(200)
  })

  var filename = $('#file-filename'),
    categoryInput = $('#file-category-input'),
    categoryHint = $('#file-category-hints'),
    categories = $('#file-categories'),
    charaName = $('#file-charaname'),
    author = $('#file-author'),
    source = $('#file-source'),
    submitBtn = $('#file-submit'),
    view = $('#file-view'),
    promptbox = $('#file-promptbox'),
    imagesbox = $('#file-images'),
    uploader = $('#file-uploader'),
    addUrlFileBtn = $('#file-addUrlFile'),
    closeBtn = $('#file-close-btn'),
    body = $('#widget-fileUploader')

  var hasFile = false,
    selectedItem = -1,
    forceOpen = false	// 防止loaded文件后无法再打开上传窗口

  var categoriesHandler = {
    data: [],
    updateView: function () {
      var _this = this
      categories.empty()
      this.data.forEach(function (val, ind) {
        var block = $('<div class="categorybox">' + val + '</div>')
        block.click(function () {
          _this.remove(ind)
        })
        categories.append(block)
      })
    },

    push: function (val) {
      var _this = this
      this.data.some(function (cate, ind) {
        if (cate == val) {
          _this.data.splice(ind, 1)
          return true
        }
      })
      this.data.push(val)
      this.updateView()
    },

    remove: function (ind) {
      this.data.splice(ind, 1)
      this.updateView()
    },

    format: function () {
      return this.data.map(function (val) { return '[[分类:' + val + ']]' }).join(' ')
    }
  }

  var categoryHintHandler = {
    data: [],
    updateView: function () {
      categoryHint.empty()
      this.data.length ? categoryHint.show() : categoryHint.hide()
      this.data.forEach(function (val) {
        var item = $('<div class="category-hint">').text(val)
        item.click(function () {
          categoryInput.val('').focus()
          categoriesHandler.push(val)
          categoryHint.hide()
        })
        categoryHint.append(item)
      })
    },

    write: function (data) {
      this.data = data
      this.updateView()
    },

    clear: function () {
      this.data = []
      this.updateView()
    }
  }

  var filesHandler = {
    data: [],
    updateView: function () {
      imagesbox.empty()
      if (this.data.length) {
        hasFile = true
        promptbox.hide()
        var _this = this
        this.data.forEach(function (file, ind) {
          var box = $('<div class="imagebox">').attr('title', file.info.name)
          if (typeof file.body == 'string') {
            if (/\.(jpe?g|png|gif|webp|svg)$/.test(file.body)) {
              box.append($('<img>').attr('src', file.body))
            } else {
              box.append($('<div class="typebox">').text('不支持预览的文件类型'))
            }
          } else {
            if (file.body.type.split('/')[0] == 'image') {
              var url = URL.createObjectURL(file.body)
              box.append($('<img>').attr('src', url))
            } else {
              box.append($('<div class="typebox">').text(file.body.type.split('/')[1] + '文件'))
            }
          }


          box.click(function () {
            selectedItem = $(this).index()
            imagesbox.find('.imagebox').removeClass('selected')
            $(this).addClass('selected')
            filename.val(file.info.name)
            charaName.val(file.info.charaName)
            author.val(file.info.author)
            source.val(file.info.source)
          })

          var removeBtn = $('<div class="file-remove-btn">&times;</div>').click(function (e) {
            _this.remove(ind)
          })
          box.append(removeBtn)
          imagesbox.append(box)

        })

        var lastbox = $('<label class="lastbox" for="file-uploader">')
        lastbox.mousedown(function () {
          forceOpen = true
        })

        imagesbox.append(lastbox)
      } else {
        setTimeout(function () {
          hasFile = false
        }, 1)
        promptbox.show()
      }
    },

    push: function (file) {
      this.data.push({
        body: file,
        info: {
          name: typeof file == 'string' ? file.replace(/^.+\/(.+)$/, '$1') : file.name,
          charaName: '',
          author: '',
          source: ''
        }
      })

      this.updateView()
    },

    remove: function (index) {
      this.data.splice(index, 1)
      this.updateView()
    },

    clear: function () {
      this.data = []
      this.updateView()
    },

    format: function () {
      return this.data.map(function (file) {
        var comment =
          (file.info.charaName ? '[[分类:' + file.info.charaName + ']]' : '')
          + (file.info.author ? '[[分类:作者:' + file.info.author + ']]' : '')
          + (file.info.source ? '源地址:' + file.info.source : '')

        return {
          file: file.body,
          filename: $('#file-prefix').val().trim() + file.info.name,
          comment: comment,
        }
      })
    },
    
    selectLast: function(){
    	$(Array.from($('#file-images > .imagebox').removeClass('selected')).pop()).click().get(0).scrollIntoView()
    }
  }


  closeBtn.click(function () {
    body.fadeOut(200)
  })

  // 拖拽上传
  $(view).on('dragenter dragover drop', function (e) {
    e.preventDefault()
  })

  view[0].addEventListener('drop', function (e) {
    var original = e.dataTransfer.files
    Array.prototype.forEach.call(original, function (file) {
      if (['ogg', 'ogv', 'oga', 'flac', 'opus', 'wav', 'webm', 'mp3', 'png', 'gif', 'jpg', 'jpeg', 'webp', 'svg', 'pdf', 'ppt', 'jp2', 'doc', 'docx', 'xls', 'xlsx', 'psd', 'sai', 'swf', 'mp4'].includes(file.type.split('/')[1])) {
        filesHandler.push(file)
      }
    })

    filesHandler.selectLast()
    uploader.val('')
  })

  uploader.click(function (e) {
    if (hasFile && !forceOpen) {
      e.preventDefault()
      console.log('123')
    }
    forceOpen = false
  })

  uploader.on('change', function (e) {
    var original = e.target.files
    Array.prototype.forEach.call(original, function (file) {
      filesHandler.push(file)
    })

	filesHandler.selectLast()
    uploader.val('')
  })

  new Array(filename, charaName, author, source).forEach(function (ele) {
    ele.on('input', function (e) {
      if (selectedItem >= 0) {
        var value = e.target.value.trim()
        ele.attr('id') == 'file-filename' && $('#file-images > .imagebox').eq(selectedItem).attr('title', value)
        filesHandler.data[selectedItem].info[this.dataset.type] = value
      }
    })
  })

  var setTimeouKey = 0
  categoryInput.on('input', function (e) {
    if (!e.target.value) { return }
    var word = e.target.value.trim()
    clearTimeout(setTimeouKey)
    setTimeoutKey = setTimeout(function () {
      getHints(word).then(function (data) {
        var hints = data.query.search.map(function (val) { return val.title.split('Category:')[1] })
        categoryHintHandler.write(hints)
      })
    }, 500)
  })

  categoryInput.keydown(function (e) {
    if (!e.target.value) { return }
    if (e.keyCode == 13) {
      categoriesHandler.push(e.target.value)
      $(this).val('')
    }
    if (e.keyCode == 38) {
      e.preventDefault()
      categoryHint.focus()
      categoryHint.find('.category-hint:last-child').addClass('selected')
    }
  })

  $(document).click(function () {
    categoryHint.hide()
  })

  categoryHint.keydown(function (e) {
    if (e.keyCode == 38) {
      e.preventDefault()
      var index = $(this).find('.category-hint.selected').index() - 1
      if (index < 0) { index = categoryHintHandler.data.length - 1 }
      $(this).find('.category-hint').removeClass('selected').eq(index).addClass('selected')
    }
    if (e.keyCode == 40) {
      e.preventDefault()
      var index = $(this).find('.category-hint.selected').index() + 1
      $(this).find('.category-hint').removeClass('selected')
      if (index > categoryHintHandler.data.length - 1) {
        categoryInput.focus()
      } else {
        $(this).find('.category-hint').eq(index).addClass('selected')
      }
    }
    if (e.keyCode == 13) {
      $(this).find('.category-hint.selected').click()
    }
  })

  addUrlFileBtn.click(function () {
    var url = (prompt('请输入文件地址:') || '').trim()
    if (!url) { return }
    filesHandler.push(url)
  })

  submitBtn.click(function () {
    var requests = filesHandler.format()
    if (!requests.length) {
      mw.notify('您还没有上传任何文件', { type: 'warn' })
      return
    }
    requests.forEach(function (val) {
      if (!(val.filename || '').trim()) {
        mw.notify('存在文件名为空的文件', { type: 'warn' })
        return
      }
    })
    
    var confirm = window.confirm('确定要开始上传吗?')
    if(!confirm){ return }

    var report = {
      content: [],
      push: function (val) {
        this.content.push(val)
        if (this.content.length == requests.length) {
          var log = this.content.join('\n')
          console.log(log)
          setTimeout(function () {
            alert(log)
          }, 1000)
          submitBtn.removeAttr('disabled')
        }
      }
    }

    var filenames = requests.map(function (val) { return val.filename })
    var duplicatedFile = ''
    var isDuplication = filenames.some(function (val) {
      var count = 0
      filenames.forEach(function (val2) {
        if (val == val2) {
          count++
        }
      })

      if (count > 1) {
        duplicatedFile = val
        return true
      }
    })
    if (isDuplication) {
      mw.notify('名为【' + duplicatedFile + '】的文件发生了重复,请不要给要上传的图片设置相同的名称', { type: 'error' })
      return
    }

    mw.notify('开始上传...')
    submitBtn.attr('disabled', 'disabled')

    Promise.all(requests.map(function (file) { return checkFileName(file.filename) }))
      .then(function (results) {
        var isAllMessing = results.every(function (messing, index) {
          !messing && mw.notify('已经存在名为【' + requests[index].filename + '】的文件,请更换名称后再次上传')
          return messing
        })
        if (!isAllMessing) { return }

        Promise.all(requests.map(function (file) {
          return new Promise(function (resolve) {
            upload(file.file, file.filename, file.comment + categoriesHandler.format())
              .then(function () { mw.notify('【' + file.filename + '】上传成功'); resolve({ name: file.filename, result: true }) })
              .catch(function () { mw.notify('【' + file.filename + '】上传失败'); resolve({ name: file.filename, result: false }) })
          })
        })).then(function (results) {
          alert('全部上传结果:\n' + results.map(function(item, index){
          	return ++index + '. 【' + item.name + '】 上传' + (item.result ? '成功' : '失败')
          }).join('\n'))

          submitBtn.removeAttr('disabled')
        })
      })
  })
})