util
for (const [name, t] of [['GM_log', typeof GM_log], ['GM_xmlhttpRequest', typeof GM_xmlhttpRequest], ['GM_getValue', typeof GM_getValue], ['GM_setValue', typeof GM_setValue]]) {
if (t === "undefined") {
const s = `${name}不存在!请检查是否声明@grant ${name}`
console.log(`%c${s}`, `color: #f0ad4e; font-size: 15px`)
}
}
const gmlog = Object.freeze({
fontSize: 16,
consoleOutput: true,
log(s, level) {
if (typeof GM_log === 'function') {
GM_log(`${s}`, ['debug', 'info', 'warn', 'error'].includes(level) ? level : 'debug')
} else {
console.error(`%cGM_log函数不存在,请检查是否申明@grant GM_log`, 'color: red; background: black; font-size: 18px')
}
},
debug(s) {
const output = s instanceof Error ? s.stack : s
if (this.consoleOutput) {
console.debug(`%c${output}`, `color: #aaaaaa; font-size: ${this.fontSize}px`)
}
this.log(s, 'debug')
},
info(s) {
const output = s instanceof Error ? s.stack : s
if (this.consoleOutput) {
console.info(`%c${output}`, `color: #5bc0de; font-size: ${this.fontSize}px`)
}
this.log(s, 'info')
},
success(s) {
const output = s instanceof Error ? s.stack : s
if (this.consoleOutput) {
console.info(`%c${output}`, `color: #22bb33; font-size: ${this.fontSize}px`)
}
this.log(s, 'info')
},
warn(s) {
const output = s instanceof Error ? s.stack : s
if (this.consoleOutput) {
console.warn(`%c${output}`, `color: #f0ad4e; font-size: ${this.fontSize}px`)
}
this.log(s, 'warn')
},
error(s) {
const output = s instanceof Error ? s.stack : s
if (this.consoleOutput) {
console.error(`%c${output}`, `color: #bb2124; font-size: ${this.fontSize}px`)
}
this.log(s, 'error')
}
})
const apiClient = Object.freeze({
request(method, url, options = {}) {
options = Object.assign({}, options)
if (Object.prototype.toString.call(options.params) === '[object Object]') {
const params = options.params
const urlObj = new URL(url)
for (const [name, value] of Object.entries(params)) {
urlObj.searchParams.set(name, value)
}
url = urlObj.toString()
delete options['params']
}
switch (method) {
case 'PUT':
case 'POST': {
const headers = Object.assign({}, options.headers)
const hasContentType = Object.entries(headers).some(([key, value]) => key.toLowerCase() == "content-type")
if (!hasContentType) {
let contentType = null
if (Object.prototype.toString.call(options.data) === '[object Object]') {
options.data = (new URLSearchParams(options.data)).toString()
contentType = "application/x-www-form-urlencoded; charset=UTF-8"
} else if (typeof options.data === "string") {
try {
JSON.parse(options.data)
contentType = "application/json; charset=UTF-8"
} catch (error) {
contentType = "text/plain; charset=UTF-8"
}
}
if (contentType) {
headers["content-type"] = contentType
options.headers = headers
}
}
break
}
}
return new Promise((resolve, reject) => {
options = Object.assign(options, {
method: method,
url: url,
onload: function (res) {
let respJson = undefined
try {
respJson = JSON.parse(res.responseText)
} catch (e) { }
const statusOk = !(res.status >= 400 && res.status < 600)
resolve(Object.assign(res, { json: respJson, ok: statusOk }))
},
onerror: e => {
reject(`请求: ${url} 出错了:${e}`)
},
ontimeout: () => {
reject(`请求: ${url} 已超时`)
},
onabort: () => {
reject(`请求: ${url} 已终止`)
}
})
GM_xmlhttpRequest(options)
})
},
okrequest(method, url, options = {}) {
return new Promise((resolve, reject) => {
this.request(method, url, options).then(resp => {
errorMsg = ''
if (resp.status >= 400 && resp.status < 500) {
errorMsg = `${url} ${resp.status} 客户端错误:${resp.responseText || ''}`
} else if (resp.status >= 500 && resp.status < 600) {
errorMsg = `${url} ${resp.status} 服务器错误:${resp.responseText || ''}`
}
if (errorMsg) {
reject(errorMsg)
} else {
resolve(resp)
}
}).catch(e => {
reject(e)
})
})
},
get(url, options = {}) {
return this.request('GET', url, options)
},
head(url, options = {}) {
return this.request('HEAD', url, options)
},
post(url, options = {}) {
return this.request('POST', url, options)
},
okget(url, options = {}) {
return this.okrequest('GET', url, options)
},
okhead(url, options = {}) {
return this.okrequest('HEAD', url, options)
},
okpost(url, options = {}) {
return this.okrequest('POST', url, options)
}
})
const storageManager = Object.freeze({
_getItem(key) {
return GM_getValue(key)
},
_setItem(key, obj) {
GM_setValue(key, obj)
},
itemExists(key) {
return GM_getValue(key) !== undefined
},
getItemProxy(itemKey) {
let record = this._getItem(itemKey)
if (Object.prototype.toString.call(record) !== '[object Object]') {
record = {}
}
let timerId = 0
const saveRecord = () => {
clearTimeout(timerId)
timerId = setTimeout(() => {
this._setItem(itemKey, record)
}, 0)
}
function createProxy(obj) {
const handler = {
deleteProperty(target, name) {
const result = Reflect.deleteProperty(target, name)
if (result) {
saveRecord()
}
return result
},
get(target, key, receiver) {
if (typeof target[key] === 'object' && target[key] !== null) {
return new Proxy(target[key], handler)
}
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver)
if (result) {
saveRecord()
}
return result
}
}
return new Proxy(obj, handler)
}
return createProxy(record)
}
})