data-cache.js

A Map sub-class backed by a persistant store

目前为 2017-07-14 提交的版本。查看 最新版本

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.cn-greasyfork.org/scripts/31448/206062/data-cachejs.js

  1. // ==UserScript==
  2. // @name data-cache.js
  3. // @version 1.0.0
  4. // @description A Map sub-class backed by a persistant store
  5. // ==/UserScript==
  6.  
  7. /* jshint asi: true, esnext: true */
  8.  
  9. DataCache = (() => {
  10.  
  11. const NORMAL = 'font-weight: normal; text-decoration: none; color: black'
  12. const ERROR = 'font-weight: bold; color: #f4f'
  13. const LINK = 'color: #05f; text-decoration: underline'
  14. const BOLD = 'font-weight: bold'
  15. const BLUE = 'color: #05f'
  16.  
  17. // --------------------------------------------------------------------
  18.  
  19. function* entries(v) {
  20. if (typeof v !== 'object') {
  21. throw new TypeError(`Non-object argument passed to entries(v)`, v)
  22. }
  23.  
  24. if (typeof v.entries === 'function') {
  25. yield* v.entries()
  26. } else {
  27. for (const k of Object.keys(v)) {
  28. const val = [ k, v[k] ]
  29. yield val
  30. }
  31. }
  32. }
  33.  
  34. // --------------------------------------------------------------------------
  35.  
  36. class DataCache extends Map {
  37. constructor({ getter, setter = null, key = null, debug = false }) {
  38. super()
  39.  
  40. setter = setter || getter
  41.  
  42. if (key) {
  43. this._getter = () => getter(key)
  44. this._setter = value => setter(key, value)
  45. } else {
  46. this._getter = getter
  47. this._setter = setter || getter
  48. }
  49.  
  50. this._key = key
  51. this._debug = typeof setter === 'boolean' ? setter : debug
  52.  
  53. this.load()
  54. }
  55.  
  56. // ------------------------------------------
  57.  
  58. _set(k, v) {
  59. if (v === super.get(k)) {
  60. return false
  61. }
  62.  
  63. super.set(k, v)
  64. return true
  65. }
  66.  
  67. // ------------------------------------------
  68.  
  69. set(k, v) {
  70. if (this._set(k, v)) {
  71. this.save()
  72. }
  73. }
  74.  
  75. // ------------------------------------------
  76.  
  77. delete(k) {
  78. if (super.has(k)) {
  79. super.delete(k)
  80. this.save()
  81. }
  82. }
  83.  
  84. // ------------------------------------------
  85.  
  86. clear() {
  87. if (this.size > 0) {
  88. super.clear()
  89. this.save()
  90. }
  91. }
  92.  
  93. // ------------------------------------------
  94.  
  95. update(obj) {
  96. let changed = false
  97.  
  98. for (const [ k, v ] of entries(obj)) {
  99. changed = this._set(k, v) || changed
  100. }
  101.  
  102. if (changed) {
  103. this.save()
  104. }
  105.  
  106. return changed
  107. }
  108.  
  109. // ------------------------------------------
  110.  
  111. replace(obj) {
  112. let changed = false
  113.  
  114. if (this.size > 0) {
  115. super.clear()
  116. changed = true
  117. }
  118.  
  119. for (const [ k, v ] of entries(obj)) {
  120. changed = this._set(k, v) || changed
  121. }
  122.  
  123. if (changed) {
  124. this.save()
  125. }
  126.  
  127. return changed
  128. }
  129.  
  130. // ------------------------------------------
  131.  
  132. toJSON() {
  133. return [...this.entries()]
  134. .reduce((obj, [ k, v ]) => Object.assign(obj, { [k]: v }), {})
  135. }
  136.  
  137. // ------------------------------------------
  138.  
  139. save() {
  140. try {
  141. const json = this.toJSON()
  142. const jstr = JSON.stringify(json, null, this._debug * 2)
  143.  
  144. // GM_setValue(this._key, jstr)
  145. this._setter(json)
  146.  
  147. this._log(`save(): ${this.size} items\n\n${jstr}`)
  148. } catch (ex) {
  149. this._log(`save(): error: ${ex}`)
  150. }
  151. }
  152.  
  153. // ------------------------------------------
  154.  
  155. load(json) {
  156. super.clear()
  157.  
  158. try {
  159. // json = json || JSON.parse(GM_getValue(this._key))
  160. json = json || this._getter()
  161.  
  162. for (const [ k, v ] of entries(json)) {
  163. super.set(k, v)
  164. }
  165.  
  166. this._log(`load(): ${this.size} items\n\n${JSON.stringify(json, null, 2)}`)
  167. }
  168. catch (ex) {
  169. this._log(`load(): error: ${ex}`)
  170. }
  171. }
  172.  
  173. // ------------------------------------------
  174.  
  175. edit(text) {
  176. const res = window.prompt(text, JSON.stringify(this.toJSON(), null, 2))
  177.  
  178. if (typeof res !== 'string') return
  179.  
  180. try {
  181. const json = JSON.parse(res)
  182.  
  183. if (this.replace(json)) {
  184. this._log(`edit(): ${this.size} items\n\n${JSON.stringify(json, null, 2)}`)
  185. }
  186. } catch (ex) {
  187. this._log(`edit(): error: ${ex}`)
  188. }
  189. }
  190.  
  191. // ------------------------------------------
  192.  
  193. _log(msg) {
  194. if (this._debug) {
  195. const ks = this._key ? `key: %c'${this._key}'%c` : '%c%c'
  196. console.info(`DataCache(${ks}): ${msg}`, LINK, NORMAL)
  197. }
  198. }
  199.  
  200. // ------------------------------------------
  201.  
  202. toString() {
  203. const ks = this._key ? `key: '${this._key}', ` : ''
  204. const kl = [...this.keys()].map(String).join(', ')
  205.  
  206. return `DataCache(${ks}debug: ${this._debug}): [ ${kl} ]`
  207. }
  208.  
  209. // ------------------------------------------
  210.  
  211. dump() {
  212. // this._log(JSON.stringify(this.toJSON(), null, 2))
  213. const el = [...this.entries()]
  214. .map(([ key, value ]) => ({ key, value }))
  215.  
  216. console.table(el)
  217. }
  218. }
  219.  
  220. DataCache[Symbol.toStringTag] = 'DataCache'
  221.  
  222. // --------------------------------------------------------------------------
  223.  
  224. return DataCache
  225.  
  226. })()