User:Irukaza/common.js
跳到导航
跳到搜索
注意:这类代码页面在保存之后,您可能需要清除浏览器缓存才能看到所作出的变更的影响。
- 按住
CTRL+SHIFT+DEL 或 ⌘-Shift-R
来清除缓存! - 或尝试在地址栏的地址最后添加代码
?_=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">×</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">分 类:</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">作 者:</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(/&/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">×</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')
})
})
})
})