// ==UserScript==
// @name 我的白菜菊花
// @namespace http://tampermonkey.net/
// @version 0.2
// @description Cloud sync for bcjh.xyz
// @author You
// @match https://bcjh.gitee.io/
// @icon https://www.google.com/s2/favicons?domain=gitee.io
// @grant GM_addStyle
// @grant GM_xmlhttpRequest
// @run-at document-start
// ==/UserScript==
GM_addStyle ( `
.el-input input {
border: 1px solid #dcdfe6;
box-sizing: border-box;
border-radius: 4px;
height: 26px;
line-height: 24px;
padding-left: 10px;
padding-right: 23px;
color: #606266;
}
.sync-indicator {
position: absolute;
right:0;
top:0;
width: 10px;
height: 10px;
opacity: 0.8;
border-radius: 50%;
background: gray;
}
.sync-indicator.unsync {
background: red;
}
.sync-indicator.sync {
background: green;
}
.sync-indicator.syncing {
background: yellow !important;
}
` );
const server = "https://keystore.mactype.win:10512"
let localstore = {
_setItem: window.localStorage.setItem,
_getItem: window.localStorage.getItem,
setItem(name, value) {
return this._setItem.call(window.localStorage, name, value)
},
getItem(name) {
return this._getItem.call(window.localStorage, name)
}
}
let lastUser = localStorage.getItem("fishcloud") || ""
let vueRoot = null, importCloud = false
let cloudServer = {
delayedupload() {
let value = window.localStorage.getItem("data")
syncIndicator.syncing()
console.log("Changes detected, uploading...")
GM_xmlhttpRequest({
url: server + "/put",
method: "post",
data: JSON.stringify({
user: lastUser,
key: "data",
value: value
}),
onload: ()=>{
syncIndicator.synced()
}
})
},
t: null,
upload() {
if (!lastUser) return
syncIndicator.syncing()
if (this.t) {
clearTimeout(this.t)
}
this.t = setTimeout(()=>{
this.t = null
this.delayedupload()
}, 1000)
},
download(quiet = true) {
if (!lastUser) return
syncIndicator.syncing()
GM_xmlhttpRequest({
url: server + "/get",
method: "post",
data: JSON.stringify({
user: lastUser,
key: "data"
}),
onload: ({responseText})=>{
if (responseText) {
importCloud = true
localstore.setItem("data", responseText)
vueRoot && vueRoot.getUserData()
setTimeout(()=>{
importCloud = false
}, 500)
}
syncIndicator.synced()
if (!quiet) {
vueRoot && vueRoot.$message({
message: "数据已同步",
type: "success"
})
}
},
onerror: err=>{
syncIndicator.fail()
if (!quiet) {
alert("同步失败")
}
}
})
}
}
var syncIndicator = {
el: null,
create() {
$("body").append("<div class='sync-indicator'></div>")
this.el = $(".sync-indicator")
},
syncing() {
this.el.addClass("syncing")
},
fail() {
this.el.removeClass("syncing sync").addClass("unsync")
},
synced() {
this.el.removeClass("syncing unsync").addClass("sync")
},
nop() {
this.el.removeClass("syncing unsync sync")
}
}
function updateUser(newUser) {
lastUser = newUser
localstore.setItem("fishcloud", newUser)
}
function hookUltimateNavi(e) {
if (document.getElementById("mycloud")) return
let box = document.getElementsByClassName("ultimate-box")
if (box.length) {
$(box).prepend('<div class="title" id="mycloud">大鱼云同步</div><div class="hr"></div><div class="box-body"><div class="el-input"><span style="margin-right:5px">请输入雨云用户名:</span><input type="text" value="'+lastUser+'" name="fishcloud"></div></div>')
$(box).find("input[name=fishcloud]").on("change", async function (){
let newUser = this.value
if (newUser !== lastUser && lastUser !=="") {
try {
let action = await vueRoot.$confirm("确定要从用户 \""+lastUser+"\" 切换到用户 \""+newUser+"\" 吗?", '切换账户', {
type: 'warning'
})
} catch {
this.value = lastUser
return
}
}
updateUser(newUser)
vueRoot.$confirm("您已登录为用户 "+newUser+",请选择同步操作。如误操作请关闭对话框。", '提示', {
confirmButtonText: '下载',
cancelButtonText: '上传',
distinguishCancelAndClose: true,
type: 'success'
}).then(()=>{
cloudServer.download(false)
}).catch((action)=>{
if (action === 'cancel') {
cloudServer.upload()
} else {
this.value = ""// 登出用户
updateUser("")
syncIndicator.nop()
}
})
})
}
}
function hookOpIcon(e) {
let navs = document.getElementsByClassName("nav")
if (navs.length) {
console.log("found navigators")
$(e.currentTarget).off("click", hookOpIcon)
$(navs[8]).on("click", hookUltimateNavi)
}
}
var i = 0
function hookLocalStorage() {
window.localStorage.__proto__.setItem = function(name, value) {
let result = localstore.setItem(name, value)
if (vueRoot && !vueRoot.loading && !importCloud) { // 忽略所有中途读写
cloudServer.upload()
}
return result
}
}
function hookVue() {
let pVue = null
Object.defineProperty(unsafeWindow,'Vue',{
get: function(){
return pVue
},
set: function(val){
pVue = new Proxy(val, {
construct: function(target, args) {
var obj = new target(...args)
if (args.length && typeof args[0]==="object" && args[0].el==="#main") {
vueRoot = obj
}
return obj
},
})
},
configurable: true,
});
}
function oneTimeSyncOnLoad() {
if (!lastUser) return
if (!vueRoot || vueRoot.loading) {
setTimeout(oneTimeSyncOnLoad, 1000)
} else {
console.log("Syncing cloud profile...")
cloudServer.download()
}
}
(function() {
'use strict';
console.log("Welcome to MyBCJHCloud 1.0")
hookLocalStorage()
hookVue()
window.addEventListener('load', ()=>{
new Promise(r=>{
let checker = ()=>{
let opIcon = document.getElementsByClassName("el-icon-s-operation icon-btn")
if (opIcon.length) {
r(opIcon)
} else {
setTimeout(checker, 500)
}
}
checker()
}).then((icon)=>{
$(icon).on("click", hookOpIcon)
syncIndicator.create()
oneTimeSyncOnLoad()
})
})
})();