// ==UserScript==
// @name 清水河畔表情包计划
// @namespace http://tampermonkey.net/
// @version 0.3.8
// @description 请自己研究尝试
// @author DARK-FLAME-MASTER FROM RIVERSIDE
// @match *://*.uestc.edu.cn/forum.php?mod=viewthread*
// @match *://*.uestc.edu.cn/forum.php?mod=post*
// @match *://*.uestc.edu.cn/home.php?mod=space&do=pm&subop=view*
// @icon https://www.google.com/s2/favicons?sz=64&domain=uestc.edu.cn
// @require https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.slim.min.js
// @require https://cdn.jsdelivr.net/npm/cfb@1.2.2/dist/cfb.min.js
// @require https://cdn.jsdelivr.net/npm/vue@3.2.41/dist/vue.global.min.js
// @require https://cdn.bootcdn.net/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js
// @require https://cdn.bootcdn.net/ajax/libs/jszip/3.5.0/jszip.min.js
// @license WTFPL
// @run-at document-body
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @grant GM_listValues
// @grant unsafeWindow
// @downloadURL https://update.greasyfork.icu/scripts/452543/%E6%B8%85%E6%B0%B4%E6%B2%B3%E7%95%94%E8%A1%A8%E6%83%85%E5%8C%85%E8%AE%A1%E5%88%92.user.js
// @updateURL https://update.greasyfork.icu/scripts/452543/%E6%B8%85%E6%B0%B4%E6%B2%B3%E7%95%94%E8%A1%A8%E6%83%85%E5%8C%85%E8%AE%A1%E5%88%92.meta.js
// ==/UserScript==
(function () {
'use strict';
unsafeWindow.Vue = Vue
let setAddedImg;
let storageVar = "emojiSet";
let storageAlbum = "emojiAlbum"
let storageGroup = "selectedGroup"
let defaultEmoji = [{ tag: "default", tagImg: { id: 'default', src: 'data/attachment/common/star/common_25_icon.png' }, images: [] }]
//GM_setValue('version','0.1')
let version = GM_getValue('version', 'NULL')
if (version != '0.3' && version != 'NULL') {
let emoji_set = GM_getValue(storageVar)
for (let group of emoji_set)
for (let imgs of group.images)
imgs.size = 0
GM_setValue(storageVar, emoji_set)
}
GM_setValue('version', '0.3')
document.addEventListener('DOMContentLoaded', function () {
let post_params = unsafeWindow.upload?unsafeWindow.upload.settings.post_params:undefined;
let emoji = GM_getValue(storageVar, defaultEmoji)
let emojiAlbum = GM_getValue(storageAlbum, -1)
let selectedGroup = GM_getValue(storageGroup, 0)
let formhash = $('#modactions :nth-child(1)').attr('value');
$('body').prepend(`
` )
let app = Vue.createApp({
data() {
return {
//version,
menu:
{
left: 0,
top: 0,
visible: false
},
exportProgress:0,
emojiSize: [64, 128, 512],
emojiVisible: false,
emojiChangePosInfo: { index: -1, flag: false },
settingVisible: false,
addedImg: { id: 'default', src: 'data/attachment/common/star/common_25_icon.png' },
uploadTotalCount: 0,
uploadNowCount: 0,
renameGroup: false,
emojiAlbum: emojiAlbum,
selectedGroup: selectedGroup < 0 ? 0 : selectedGroup,
selectedEmoji: -1,
formhash: formhash,
post_params: post_params,
emojiTemp: [],
emoji: emoji.length == 0 ? defaultEmoji : emoji
}
},
methods: {
switchTag(index) {
this.selectedGroup = index
},
switchEmoji(index) {
this.selectedEmoji = index
},
delEmoji(index, group = this.selectedGroup) {
this.emoji[group].images.splice(index, 1)
},
addEmoji(emoji, group = this.selectedGroup, notice = false) {
this.emoji[group].images.unshift(emoji)
if (notice) this.notice(`已将表情添加至${this.emoji[group].tag}`)
},
addGroup(group) {
this.emoji.push(group)
return this.emoji.length - 1
},
delGroup(index) {
if (confirm(`您确定要删除分组${this.emoji[index].tag}吗?`)) {
if (index == this.emoji.length - 1 && index != 0) this.selectedGroup -= 1
this.emoji.splice(index, 1)
this.notice("已删除该分组")
}
},
emoAsTagImg(img, group = this.selectedGroup) {
this.emoji[group].tagImg = img
},
changeEmoSize(index) {
let imgs = this.emoji[this.selectedGroup].images
imgs[index].size = (imgs[index].size + 1) % this.emojiSize.length
},
changeEmoPos(index, event) {
this.emojiChangePosInfo.index = index
this.emojiChangePosInfo.flag = true
},
changeEmoPosInGroup(index, event) {
let images = this.emoji[this.selectedGroup].images
if (event.deltaY < -100)
if (index != 0) {
images[index] = images.splice(index - 1, 1, images[index])[0];
this.emojiChangePosInfo.index--
}
if (event.deltaY > 100)
if (index != images.length - 1) {
images[index] = images.splice(index + 1, 1, images[index])[0];
this.emojiChangePosInfo.index++
}
},
insertEmojiInForum(emoji, event) {
let img = event.currentTarget
let h = img.naturalHeight
let w = img.naturalWidth
let size = this.emojiSize[emoji.size]
let min = Math.max(Math.min(h, w), size)
h = Math.ceil(h / min * size)
w = Math.ceil(w / min * size)
eval(`if(typeof insertHrImage == 'undefined')
if(document.getElementById('pmform') != null)
seditor_insertunit('reply','[img=${w},${h}]${'https://bbs.uestc.edu.cn/'+emoji.src}[/img]')
else
seditor_insertunit(document.getElementById('postat') != null ? 'post' : 'fastpost' ,'[img=${w},${h}]${emoji.src}[/img]')
else
insertText('
')
`)
},
notice(message) {
Notification.requestPermission().then((result) => { if (result === 'granted') { let n = new Notification(message); setTimeout(n.close.bind(n), 1800) } })
},
setEmojiAlbum() {
emojiAlbum = prompt('请输入表情保存的相册ID:')
if (emojiAlbum != null) {
this.emojiAlbum = emojiAlbum
GM_setValue(storageAlbum, emojiAlbum)
notice("相册设置成功")
}
},
rebuildEmojiByAlbum(params) {
let uid = params.split(' ')[0]
let album = params.split(' ')[1]
let group = this.addGroup({ tag: album, tagImg: { id: 'default', src: 'data/attachment/common/star/common_25_icon.png' }, images: [] })
fetch(`/home.php?mod=space&uid=${uid}&do=album&id=${album}`)
.then(data => data.text())
.then(data => {
let regex = /共 (\d*) 张图片/
let num = parseInt(data.match(regex)[1])
for (let i = 0; i <= (num - 1) / 20; ++i) {
fetch(`/home.php?mod=space&uid=${uid}&do=album&id=${album}&page=${i + 1}`)
.then(data => data.text())
.then(data => {
let doc = new DOMParser().parseFromString(data, 'text/html');
if (i != parseInt(num / 20)) {
for (let j = 1; j <= 20; ++j)
this.addEmoji({ id: i * 20 + j, src: doc.querySelector(`#ct > div.mn > div > div.bm_c > ul > li:nth-child(${j}) > a > img`).src, size: 0 }, group)
} else {
for (let j = 1; j <= num % 20; ++j)
this.addEmoji({ id: i * 20 + j, src: doc.querySelector(`#ct > div.mn > div > div.bm_c > ul > li:nth-child(${j}) > a > img`).src, size: 0 }, group)
}
})
}
})
},
getFiles(event) {
return event.target.files
},
uploadFiles(files, group) {
this.uploadTotalCount += 2 * files.length;
for (let i = 0; i < files.length; ++i) {
let file = {
name: files[i].name,
size: files[i].size,
type: files[i].type,
dom: files[i],
};
let data = new FormData();
for (let k in this.post_params)
data.append(k, this.post_params[k]);
data.append('type', 'image');
data.append('filetype', file.type)
data.append('Filename', file.name);
data.append('Filedata', file.dom);
let pid, src;
fetch("/misc.php?mod=swfupload&action=swfupload&operation=album", {
"headers": {
},
"method": "POST",
"mode": "cors",
"body": data,
"credentials": "include",
}).then((res) => res.json()).then((data) => {
this.uploadNowCount += 1;
pid = data.picid;
src = data.bigimg;
return fetch("/home.php?mod=spacecp&ac=upload", {
"headers": {
"content-type": "application/x-www-form-urlencoded",
},
"body": "title[" + pid + "]=&albumid=" + this.emojiAlbum + "&albumsubmit=true&albumsubmit_btn=true&formhash=" + this.formhash,
"method": "POST",
"mode": "cors",
"credentials": "include"
})
}).then((data) => { this.emojiTemp.push({ group: group, img: { id: pid, src: src, size: 0 } }); this.uploadNowCount += 1; })
}
},
uploadEif(files, allowedExts = []) {
let f = new FileReader()
f.readAsBinaryString(files[0]);
let addGroup = this.addGroup
let uploadFiles = this.uploadFiles
f.onload = function () {
let eif = CFB.read(this.result, { type: 'binary' })
let validPaths = []
let validContent = eif.FileIndex.filter((e, i) => {
if (e.size > 0) {
validPaths.push(eif.FullPaths[i])
return true
}
return false
})
let s = new Set()
let fileList = {}
for (let idx in validContent) {
let entry = validContent[idx]
let extName = entry.name.substring(entry.name.lastIndexOf(".") + 1)
if (entry.type == 2 && (!allowedExts || allowedExts.includes(extName))) {
let name = validPaths[idx].substring(0, validPaths[idx].lastIndexOf("."))
if (!s.has(name)) {
let group = validPaths[idx].match(/Entry\/(\d+)\//)[1]
let f = new File([new Uint8Array(entry.content)], entry.name, { type: "image/" + extName })
if (fileList[group]) {
fileList[group].push(f)
} else {
fileList[group] = [f]
}
s.add(name + 'fix')
}
}
}
for (let group in fileList) {
uploadFiles(fileList[group], addGroup({ tag: group, tagImg: { id: 'default', src: 'data/attachment/common/star/common_25_icon.png' }, images: [] }))
}
}
},
uploadJson(files) {
const reader = new FileReader()
reader.readAsText(files[0], 'UTF-8')
reader.onload = function (e) {
const emojis = JSON.parse(e.target.result)
emoji.push(...emojis)
GM_setValue(storageVar, emoji)
}
},
exportAllJson() {
saveAs(new Blob([JSON.stringify(this.emoji)], { type: "text/plain;charset=utf-8" }), '总.json')
},
exportGroupJson(group) {
saveAs(new Blob([JSON.stringify([this.emoji[group]])], { type: "text/plain;charset=utf-8" }), this.emoji[group].tag + '.json')
},
exportAll() {
let zip = new JSZip()
let createFile = (emoji, tag) => fetch(emoji.src).then(data => data.blob()).then(img => zip.folder(tag).file(emoji.id, img, { binary: true }))
let createFiles = this.emoji.map((group) => group.images.map(emoji => createFile(emoji, group.tag))).reduce((now, group) => now.concat(group), [])
Promise.all(createFiles)
.then(() => zip.generateAsync({ type: "blob" }, (metadata) =>this.exportProgress = metadata.percent/100))
.then(content => saveAs(content, '总.zip'))
.then(()=>this.exportProgress = 0)
},
exportGroup(group) {
let zip = new JSZip()
let createFile = emoji => fetch(emoji.src).then(data => data.blob()).then(img => zip.file(emoji.id+emoji.src.substring(emoji.src.lastIndexOf(".")), img, { binary: true }))
var createFiles = this.emoji[group].images.map(emoji => createFile(emoji))
Promise.all(createFiles)
.then(() => zip.generateAsync({ type: "blob" },(metadata) =>this.exportProgress = metadata.percent/100))
.then(content => saveAs(content, this.emoji[group].tag + '.zip'))
.then(()=>this.exportProgress = 0)
},
/* exportAllEif() {
let eif = CFB.utils.cfb_new()
let createFile = (emoji, tag) => fetch(emoji.src).then(data => data.blob()).then(data => data.arrayBuffer()).then(img => CFB.utils.cfb_add(eif, tag + '/' +emoji.id, new Uint8Array(img) ))
let createFiles = this.emoji.map((group) => group.images.map(emoji => createFile(emoji, group.tag))).reduce((now, group) => now.concat(group), [])
Promise.all(createFiles)
.then(() => CFB.write(eif,{type: 'binary'}))
.then(content => saveAs(new Blob([content]), '总.eif'))
},
exportGroupEif(group) {
let eif = CFB.utils.cfb_new()
let createFile = emoji => fetch(emoji.src).then(data => data.blob()).then(data => data.arrayBuffer()).then(img => CFB.utils.cfb_add(eif, emoji.id, new Uint8Array(img) , {unsafe:true} ))
var createFiles = this.emoji[group].images.map(emoji => createFile(emoji))
Promise.all(createFiles)
.then(() => CFB.write(eif,{type: 'binary'}))
.then(content => saveAs(new Blob([content]), this.emoji[group].tag + '.eif'))
}, */
setAddedImg(img) {
this.addedImg = img
},
openMenu(e, item) {
this.rightClickItem = item;
let x = e.pageX;
let y = e.pageY;
this.menu.top = y;
this.menu.left = x;
this.menu.visible = true;
},
closeMenu() {
this.menu.visible = false;
},
closeSetting() {
this.settingVisible = false;
},
},
watch: {
uploadNowCount(newCount) {
if (newCount == this.uploadTotalCount) {
for (let data of this.emojiTemp) {
this.addEmoji(data.img, data.group)
}
this.emojiTemp = []
this.notice("表情上传完成")
this.uploadNowCount = 0
this.uploadTotalCount = 0
}
},
'menu.visible'(value) {
if (value) {
document.body.addEventListener('click', this.closeMenu)
} else {
document.body.removeEventListener('click', this.closeMenu)
}
},
/* settingVisible(value) {
if (value) {
document.body.addEventListener('click', this.closeSetting)
} else {
document.body.removeEventListener('click', this.closeSetting)
}
}, */
selectedGroup(newIndex) {
GM_setValue(storageGroup, newIndex)
},
emoji: {
handler(newEmoji, oldEmoji) {
if (newEmoji.length == 0)
this.emoji = newEmoji = defaultEmoji
GM_setValue(storageVar, newEmoji)
},
deep: true
}
},
mounted() {
setInterval(() => console.log("运行中"), 1000)
this.prompt = prompt
setAddedImg = this.setAddedImg
console.log(this.emoji)
},
computed: {
progress() {
return this.uploadTotalCount != 0 ? this.uploadNowCount / this.uploadTotalCount : 0
},
position() {
return this.emojiVisible ? { left: this.$refs.mine.getBoundingClientRect().left + 'px', bottom: unsafeWindow.innerHeight - this.$refs.mine.getBoundingClientRect().top - document.documentElement.scrollTop + 'px' } : {}
},
}
});
app.component('progresscircle', {
props:['progress','size'],
template:` `,
/*data() {
return {
progress: 0,
size: 55,
}
},
render() {
return h('svg', { width: size, height: size, viewBox: "0 0 200 200" },
[
h('circle', { id: "circleBg", transform: "rotate(-90 100 100)", cx: "100", cy: "100", r: "70", fill: "none", 'stroke-width': "30", stroke: "gray", 'stroke-dasharray': "434", 'stroke-dashoffset': 434 - progress * 434 }),
h('circle', { id: "circle", 'stroke-linecap': "round", transform: "rotate(-90 100 100)", cx: "100", cy: "100", r: "70", fill: "none", 'stroke-width': "30", stroke: "green", 'stroke-dasharray': "434", 'stroke-dashoffset': 434 - progress * 434 }),
h('text', { x: "100", y: "100", fill: "#6b778c", 'text-anchor': "middle", 'dominant-baseline': "central", 'font-size': "32" }, [h('tspan', { id: "percent", innerHTML: parseInt(progress * 100) })])
])
} */
})
app.mount('#emojiTemplate')
})
function makeReplyEmoji() {
document.addEventListener('DOMContentLoaded', () => $('#fastpostat').before($('#mine')))
setTimeout(function () {
$('.t_f>img.zoom').each(function (i) { $(this.previousSibling).after($('').append($(this))) })
$('ignore_js_op>img').each(function (i) { $(this.previousSibling).after($('').append($(this))) })
$('.mbn>img').each(function (i) { $(this.previousSibling).after($('').append($(this))) })
$('.emojiImg').mouseenter(function () {
let length = Math.min(this.firstChild.clientWidth, this.firstChild.clientHeight)
$('#likeEmo').css({
'font-size': length * 0.15 + 'px',
'left': 0,
'top': -this.firstChild.clientHeight + 10 + 'px',
'position': 'absolute',
'cursor': 'pointer',
'display': 'inline',
})
setAddedImg({ id: $(this.firstChild).attr('id'), src: $(this.firstChild).attr('src'), size: 0 })
$(this).append($('#likeEmo'))
})
$('.emojiImg').mouseleave(function () {
$('#likeEmo').css('display', 'none')
$('#mine_menu').append($('#likeEmo'))
})
$('#fastpostsubmit').click(function () { if (!$('#mine_menu')[0].style.display) $('#mine').trigger('click') })
}, 500)
return Promise.resolve()
}
function makePostEmoji() {
document.addEventListener('DOMContentLoaded', () => {
$("#e_sml").after($('#mine'))
$('#mine').css('font-size', '25px')
})
return Promise.resolve()
}
function makePrivateEmoji() {
document.addEventListener('DOMContentLoaded', () => {
$("#replysml").after($('#mine'))
$('#mine').css('font-size', '15px')
})
return Promise.resolve()
}
let trueHideWindow = unsafeWindow.hideWindow
unsafeWindow.hideWindow = function (k, all, clear) {
if (k == 'reply') {
$('#fastpostat').before($('#mine'))
if (!$('#mine_menu')[0].style.display) $('#mine').trigger('click')
}
trueHideWindow(k, all, clear)
}
let trueShowMenu = unsafeWindow.showMenu
unsafeWindow.showMenu = function (v) {
trueShowMenu(v)
if (v.menuid == 'fwin_reply')
$('#postat').before($('#mine'))
}
let trueDoane = unsafeWindow.doane;
unsafeWindow.doane = function (e, preventDefault, stopPropagation) {
stopPropagation = isUndefined(stopPropagation) ? 0 : stopPropagation;
return trueDoane(e, preventDefault, stopPropagation)
}
let emojiHandler = [
{
regex: /https?:\/\/(?:bbs\.uestc\.edu\.cn|bbs-uestc-edu-cn-s\.vpn\.uestc\.edu\.cn(?::8118)?)\/forum\.php\?mod=viewthread.*/i,
handler: makeReplyEmoji,
},
{
regex: /https?:\/\/(?:bbs\.uestc\.edu\.cn|bbs-uestc-edu-cn-s\.vpn\.uestc\.edu\.cn(?::8118)?)\/forum\.php\?mod=post.*/i,
handler: makePostEmoji,
},
{
regex: /https?:\/\/(?:bbs\.uestc\.edu\.cn|bbs-uestc-edu-cn-s\.vpn\.uestc\.edu\.cn(?::8118)?)\/home\.php\?mod=space.*/i,
handler: makePrivateEmoji,
},
]
function matchHandlers(url) {
for (let { regex, handler } of emojiHandler) {
let match = url.match(regex);
if (match) {
return handler();
}
}
return Promise.reject();
}
matchHandlers(unsafeWindow.location.href)
//setInterval(function(){$('#postat').before($('#mine'))},1000)
})();