Greasy Fork is available in English.

cross-origin-storage

跨域本地存储

Ovu skriptu ne treba izravno instalirati. To je biblioteka za druge skripte koje se uključuju u meta direktivu // @require https://update.greasyfork.org/scripts/473442/1384389/cross-origin-storage.js

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// @name            cross-origin-storage
// @name:zh         跨域本地存储
// @namespace       https://github.com/pansong291/
// @version         1.0.4
// @author          paso
// @license         Apache-2.0

;(function() {
  'use strict'

  const __msgType = 'cross-origin-storage'

  /**
   * 生成随机ID
   * @returns {string}
   */
  function uuid() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
      const r = (Math.random() * 16) | 0,
        v = c === 'x' ? r : (r & 0x3) | 0x8
      return v.toString(16)
    })
  }

  /**
   * @param {WindowProxy} win
   * @param {*} msg
   */
  function sendMsgTo(win, msg) {
    win.postMessage(msg, '*')
  }

  /**
   * @param {(e: MessageEvent) => void} handler
   */
  function onReceive(handler) {
    window.addEventListener('message', handler)
  }

  /**
   * @param {string} serverUrl
   */
  function createStorageClient(serverUrl) {
    const serverIframe = document.createElement('iframe')
    serverIframe.src = serverUrl
    serverIframe.setAttribute('style', 'display: none !important;')
    window.document.body.appendChild(serverIframe)
    return startStorageClient(serverIframe.contentWindow, 10_000)
  }

  /**
   * @param {WindowProxy} serverWindow
   * @param {number} [timeout]
   */
  function startStorageClient(serverWindow, timeout) {
    // 所有请求消息数据映射
    const _requests = {}
    const _cache = {
      // 开始建立连接的时间
      startTime: 0,
      // 与 Server 的连接是否已建立完成
      connected: false,
      // 连接是否超时
      timeout: false,
      // 缓存的请求队列
      queue: []
    }
    // 监听 Server 发来的消息
    onReceive((e) => {
      if (e?.data?.__msgType !== __msgType) return
      if (e.data.connected) {
        // 连接已建立完成, 发送队列中的全部请求
        _cache.connected = true
        while (_cache.queue.length) {
          sendMsgTo(serverWindow, _cache.queue.shift())
        }
        return
      }
      const { id, response } = e.data
      // 找到消息对应的回调函数,调用并传递数据
      _requests[id]?.resolve(response)
      delete _requests[id]
    })

    // 请求与 Server 建立连接
    const loopId = setInterval(() => {
      if (_cache.connected) {
        clearInterval(loopId)
        return
      }
      if (!_cache.startTime) {
        _cache.startTime = Date.now()
      } else if (timeout && timeout > 0) {
        if (Date.now() - _cache.startTime > timeout) {
          _cache.timeout = true
          clearInterval(loopId)
          while (_cache.queue.length) {
            const reqId = _cache.queue.shift().id
            _requests[reqId]?.reject('connection timeout')
            delete _requests[reqId]
          }
          return
        }
      }
      sendMsgTo(serverWindow, { connect: 1, __msgType })
    }, 500)

    /**
     * 发起请求函数
     * @param method 请求方式
     * @param key
     * @param value
     */
    function _requestFn(method, key, value) {
      return new Promise((resolve, reject) => {
        const req = {
          id: uuid(),
          method,
          key,
          value,
          __msgType
        }

        // 请求唯一标识 id 和回调函数的映射
        _requests[req.id] = { resolve, reject }

        if (_cache.connected) {
          // 连接建立完成时直接发请求
          sendMsgTo(serverWindow, req)
        } else if (_cache.timeout) {
          // 连接超时拒绝请求
          reject('connection timeout')
        } else {
          // 连接未建立则把请求放入队列
          _cache.queue.push(req)
        }
      })
    }

    return {
      /**
       * 获取存储数据
       * @param {Iterable | Object | string} key
       */
      getItem(key) {
        return _requestFn('get', key)
      },
      /**
       * 更新存储数据
       * @param {Object | string} key
       * @param {Object | string} [value = undefined]
       */
      setItem(key, value = void 0) {
        return _requestFn('set', key, value)
      },
      /**
       * 删除数据
       * @param {Iterable | Object | string} key
       */
      delItem(key) {
        return _requestFn('delete', key)
      },
      /**
       * 清除数据
       */
      clear() {
        return _requestFn('clear')
      }
    }
  }

  function startStorageServer() {
    const functionMap = {
      /**
       * 设置数据
       * @param {Object | string} key
       * @param {?Object | ?string} value
       */
      setStore(key, value = void 0) {
        if (!key) return
        if (typeof key === 'string') {
          return localStorage.setItem(key, typeof value === 'object' ? JSON.stringify(value) : value)
        }
        Object.keys(key).forEach((dataKey) => {
          let dataValue = typeof key[dataKey] === 'object' ? JSON.stringify(key[dataKey]) : key[dataKey]
          localStorage.setItem(dataKey, dataValue)
        })
      },

      /**
       * 获取数据
       * @param {Iterable | Object | string} key
       */
      getStore(key) {
        if (!key) return
        if (typeof key === 'string') return localStorage.getItem(key)
        let dataRes = {}
        const keys = key[Symbol.iterator] ? key : Object.keys(key)
        for (const dataKey of keys) {
          dataRes[dataKey] = localStorage.getItem(dataKey) || null
        }
        return dataRes
      },

      /**
       * 删除数据
       * @param {Iterable | Object | string} key
       */
      deleteStore(key) {
        if (!key) return
        if (typeof key === 'string') return localStorage.removeItem(key)
        const keys = key[Symbol.iterator] ? key : Object.keys(key)
        for (const dataKey of keys) {
          localStorage.removeItem(dataKey)
        }
      },

      /**
       * 清空
       */
      clearStore() {
        localStorage.clear()
      }
    }
    const clients = new Set()

    // 监听 Client 消息
    onReceive((e) => {
      if (e?.data?.__msgType !== __msgType) return
      if (e.data.connect) {
        clients.add(e.source)
        // 通知 Client, 连接建立完成
        sendMsgTo(e.source, { connected: true, __msgType })
        return
      }
      const { method, key, value, id = 'default' } = e.data

      // 获取方法
      const func = functionMap[`${method}Store`]

      // 取出本地的数据
      const response = {
        data: func?.(key, value)
      }
      if (!func) response.errorMsg = 'Request method error!'

      // 发送给 Client
      const resultMsg = { id, request: e.data, response, __msgType }
      clients.forEach((c) => sendMsgTo(c, resultMsg))
    })
  }

  if (!window.paso || !(window.paso instanceof Object)) window.paso = {}
  window.paso.crossOriginStorage = {
    startStorageServer,
    startStorageClient,
    createStorageClient
  }
})()