cross-origin-storage

跨域本地存储

สคริปต์นี้ไม่ควรถูกติดตั้งโดยตรง มันเป็นคลังสำหรับสคริปต์อื่น ๆ เพื่อบรรจุด้วยคำสั่งเมทา // @require https://update.greatest.deepsurf.us/scripts/473442/1384389/cross-origin-storage.js

  1. // @name cross-origin-storage
  2. // @name:zh 跨域本地存储
  3. // @namespace https://github.com/pansong291/
  4. // @version 1.0.4
  5. // @author paso
  6. // @license Apache-2.0
  7.  
  8. ;(function() {
  9. 'use strict'
  10.  
  11. const __msgType = 'cross-origin-storage'
  12.  
  13. /**
  14. * 生成随机ID
  15. * @returns {string}
  16. */
  17. function uuid() {
  18. return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
  19. const r = (Math.random() * 16) | 0,
  20. v = c === 'x' ? r : (r & 0x3) | 0x8
  21. return v.toString(16)
  22. })
  23. }
  24.  
  25. /**
  26. * @param {WindowProxy} win
  27. * @param {*} msg
  28. */
  29. function sendMsgTo(win, msg) {
  30. win.postMessage(msg, '*')
  31. }
  32.  
  33. /**
  34. * @param {(e: MessageEvent) => void} handler
  35. */
  36. function onReceive(handler) {
  37. window.addEventListener('message', handler)
  38. }
  39.  
  40. /**
  41. * @param {string} serverUrl
  42. */
  43. function createStorageClient(serverUrl) {
  44. const serverIframe = document.createElement('iframe')
  45. serverIframe.src = serverUrl
  46. serverIframe.setAttribute('style', 'display: none !important;')
  47. window.document.body.appendChild(serverIframe)
  48. return startStorageClient(serverIframe.contentWindow, 10_000)
  49. }
  50.  
  51. /**
  52. * @param {WindowProxy} serverWindow
  53. * @param {number} [timeout]
  54. */
  55. function startStorageClient(serverWindow, timeout) {
  56. // 所有请求消息数据映射
  57. const _requests = {}
  58. const _cache = {
  59. // 开始建立连接的时间
  60. startTime: 0,
  61. // 与 Server 的连接是否已建立完成
  62. connected: false,
  63. // 连接是否超时
  64. timeout: false,
  65. // 缓存的请求队列
  66. queue: []
  67. }
  68. // 监听 Server 发来的消息
  69. onReceive((e) => {
  70. if (e?.data?.__msgType !== __msgType) return
  71. if (e.data.connected) {
  72. // 连接已建立完成, 发送队列中的全部请求
  73. _cache.connected = true
  74. while (_cache.queue.length) {
  75. sendMsgTo(serverWindow, _cache.queue.shift())
  76. }
  77. return
  78. }
  79. const { id, response } = e.data
  80. // 找到消息对应的回调函数,调用并传递数据
  81. _requests[id]?.resolve(response)
  82. delete _requests[id]
  83. })
  84.  
  85. // 请求与 Server 建立连接
  86. const loopId = setInterval(() => {
  87. if (_cache.connected) {
  88. clearInterval(loopId)
  89. return
  90. }
  91. if (!_cache.startTime) {
  92. _cache.startTime = Date.now()
  93. } else if (timeout && timeout > 0) {
  94. if (Date.now() - _cache.startTime > timeout) {
  95. _cache.timeout = true
  96. clearInterval(loopId)
  97. while (_cache.queue.length) {
  98. const reqId = _cache.queue.shift().id
  99. _requests[reqId]?.reject('connection timeout')
  100. delete _requests[reqId]
  101. }
  102. return
  103. }
  104. }
  105. sendMsgTo(serverWindow, { connect: 1, __msgType })
  106. }, 500)
  107.  
  108. /**
  109. * 发起请求函数
  110. * @param method 请求方式
  111. * @param key
  112. * @param value
  113. */
  114. function _requestFn(method, key, value) {
  115. return new Promise((resolve, reject) => {
  116. const req = {
  117. id: uuid(),
  118. method,
  119. key,
  120. value,
  121. __msgType
  122. }
  123.  
  124. // 请求唯一标识 id 和回调函数的映射
  125. _requests[req.id] = { resolve, reject }
  126.  
  127. if (_cache.connected) {
  128. // 连接建立完成时直接发请求
  129. sendMsgTo(serverWindow, req)
  130. } else if (_cache.timeout) {
  131. // 连接超时拒绝请求
  132. reject('connection timeout')
  133. } else {
  134. // 连接未建立则把请求放入队列
  135. _cache.queue.push(req)
  136. }
  137. })
  138. }
  139.  
  140. return {
  141. /**
  142. * 获取存储数据
  143. * @param {Iterable | Object | string} key
  144. */
  145. getItem(key) {
  146. return _requestFn('get', key)
  147. },
  148. /**
  149. * 更新存储数据
  150. * @param {Object | string} key
  151. * @param {Object | string} [value = undefined]
  152. */
  153. setItem(key, value = void 0) {
  154. return _requestFn('set', key, value)
  155. },
  156. /**
  157. * 删除数据
  158. * @param {Iterable | Object | string} key
  159. */
  160. delItem(key) {
  161. return _requestFn('delete', key)
  162. },
  163. /**
  164. * 清除数据
  165. */
  166. clear() {
  167. return _requestFn('clear')
  168. }
  169. }
  170. }
  171.  
  172. function startStorageServer() {
  173. const functionMap = {
  174. /**
  175. * 设置数据
  176. * @param {Object | string} key
  177. * @param {?Object | ?string} value
  178. */
  179. setStore(key, value = void 0) {
  180. if (!key) return
  181. if (typeof key === 'string') {
  182. return localStorage.setItem(key, typeof value === 'object' ? JSON.stringify(value) : value)
  183. }
  184. Object.keys(key).forEach((dataKey) => {
  185. let dataValue = typeof key[dataKey] === 'object' ? JSON.stringify(key[dataKey]) : key[dataKey]
  186. localStorage.setItem(dataKey, dataValue)
  187. })
  188. },
  189.  
  190. /**
  191. * 获取数据
  192. * @param {Iterable | Object | string} key
  193. */
  194. getStore(key) {
  195. if (!key) return
  196. if (typeof key === 'string') return localStorage.getItem(key)
  197. let dataRes = {}
  198. const keys = key[Symbol.iterator] ? key : Object.keys(key)
  199. for (const dataKey of keys) {
  200. dataRes[dataKey] = localStorage.getItem(dataKey) || null
  201. }
  202. return dataRes
  203. },
  204.  
  205. /**
  206. * 删除数据
  207. * @param {Iterable | Object | string} key
  208. */
  209. deleteStore(key) {
  210. if (!key) return
  211. if (typeof key === 'string') return localStorage.removeItem(key)
  212. const keys = key[Symbol.iterator] ? key : Object.keys(key)
  213. for (const dataKey of keys) {
  214. localStorage.removeItem(dataKey)
  215. }
  216. },
  217.  
  218. /**
  219. * 清空
  220. */
  221. clearStore() {
  222. localStorage.clear()
  223. }
  224. }
  225. const clients = new Set()
  226.  
  227. // 监听 Client 消息
  228. onReceive((e) => {
  229. if (e?.data?.__msgType !== __msgType) return
  230. if (e.data.connect) {
  231. clients.add(e.source)
  232. // 通知 Client, 连接建立完成
  233. sendMsgTo(e.source, { connected: true, __msgType })
  234. return
  235. }
  236. const { method, key, value, id = 'default' } = e.data
  237.  
  238. // 获取方法
  239. const func = functionMap[`${method}Store`]
  240.  
  241. // 取出本地的数据
  242. const response = {
  243. data: func?.(key, value)
  244. }
  245. if (!func) response.errorMsg = 'Request method error!'
  246.  
  247. // 发送给 Client
  248. const resultMsg = { id, request: e.data, response, __msgType }
  249. clients.forEach((c) => sendMsgTo(c, resultMsg))
  250. })
  251. }
  252.  
  253. if (!window.paso || !(window.paso instanceof Object)) window.paso = {}
  254. window.paso.crossOriginStorage = {
  255. startStorageServer,
  256. startStorageClient,
  257. createStorageClient
  258. }
  259. })()