A Map sub-class backed by a persistant store
สคริปต์นี้ไม่ควรถูกติดตั้งโดยตรง มันเป็นคลังสำหรับสคริปต์อื่น ๆ เพื่อบรรจุด้วยคำสั่งเมทา // @require https://update.greasyfork.org/scripts/31448/206062/data-cachejs.js
// ==UserScript== // @name data-cache.js // @version 1.0.0 // @description A Map sub-class backed by a persistant store // ==/UserScript== /* jshint asi: true, esnext: true */ DataCache = (() => { const NORMAL = 'font-weight: normal; text-decoration: none; color: black' const ERROR = 'font-weight: bold; color: #f4f' const LINK = 'color: #05f; text-decoration: underline' const BOLD = 'font-weight: bold' const BLUE = 'color: #05f' // -------------------------------------------------------------------- function* entries(v) { if (typeof v !== 'object') { throw new TypeError(`Non-object argument passed to entries(v)`, v) } if (typeof v.entries === 'function') { yield* v.entries() } else { for (const k of Object.keys(v)) { const val = [ k, v[k] ] yield val } } } // -------------------------------------------------------------------------- class DataCache extends Map { constructor({ getter, setter = null, key = null, debug = false }) { super() setter = setter || getter if (key) { this._getter = () => getter(key) this._setter = value => setter(key, value) } else { this._getter = getter this._setter = setter || getter } this._key = key this._debug = typeof setter === 'boolean' ? setter : debug this.load() } // ------------------------------------------ _set(k, v) { if (v === super.get(k)) { return false } super.set(k, v) return true } // ------------------------------------------ set(k, v) { if (this._set(k, v)) { this.save() } } // ------------------------------------------ delete(k) { if (super.has(k)) { super.delete(k) this.save() } } // ------------------------------------------ clear() { if (this.size > 0) { super.clear() this.save() } } // ------------------------------------------ update(obj) { let changed = false for (const [ k, v ] of entries(obj)) { changed = this._set(k, v) || changed } if (changed) { this.save() } return changed } // ------------------------------------------ replace(obj) { let changed = false if (this.size > 0) { super.clear() changed = true } for (const [ k, v ] of entries(obj)) { changed = this._set(k, v) || changed } if (changed) { this.save() } return changed } // ------------------------------------------ toJSON() { return [...this.entries()] .reduce((obj, [ k, v ]) => Object.assign(obj, { [k]: v }), {}) } // ------------------------------------------ save() { try { const json = this.toJSON() const jstr = JSON.stringify(json, null, this._debug * 2) // GM_setValue(this._key, jstr) this._setter(json) this._log(`save(): ${this.size} items\n\n${jstr}`) } catch (ex) { this._log(`save(): error: ${ex}`) } } // ------------------------------------------ load(json) { super.clear() try { // json = json || JSON.parse(GM_getValue(this._key)) json = json || this._getter() for (const [ k, v ] of entries(json)) { super.set(k, v) } this._log(`load(): ${this.size} items\n\n${JSON.stringify(json, null, 2)}`) } catch (ex) { this._log(`load(): error: ${ex}`) } } // ------------------------------------------ edit(text) { const res = window.prompt(text, JSON.stringify(this.toJSON(), null, 2)) if (typeof res !== 'string') return try { const json = JSON.parse(res) if (this.replace(json)) { this._log(`edit(): ${this.size} items\n\n${JSON.stringify(json, null, 2)}`) } } catch (ex) { this._log(`edit(): error: ${ex}`) } } // ------------------------------------------ _log(msg) { if (this._debug) { const ks = this._key ? `key: %c'${this._key}'%c` : '%c%c' console.info(`DataCache(${ks}): ${msg}`, LINK, NORMAL) } } // ------------------------------------------ toString() { const ks = this._key ? `key: '${this._key}', ` : '' const kl = [...this.keys()].map(String).join(', ') return `DataCache(${ks}debug: ${this._debug}): [ ${kl} ]` } // ------------------------------------------ dump() { // this._log(JSON.stringify(this.toJSON(), null, 2)) const el = [...this.entries()] .map(([ key, value ]) => ({ key, value })) console.table(el) } } DataCache[Symbol.toStringTag] = 'DataCache' // -------------------------------------------------------------------------- return DataCache })()