1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 | import * as map from './map.js'
|
21 | import * as set from './set.js'
|
22 | import * as buffer from './buffer.js'
|
23 | import * as storage from './storage.js'
|
24 |
|
25 | /**
|
26 | * @typedef {Object} Channel
|
27 | * @property {Set<function(any, any):any>} Channel.subs
|
28 | * @property {any} Channel.bc
|
29 | */
|
30 |
|
31 |
|
32 |
|
33 |
|
34 | const channels = new Map()
|
35 |
|
36 |
|
37 | class LocalStoragePolyfill {
|
38 | |
39 |
|
40 |
|
41 | constructor (room) {
|
42 | this.room = room
|
43 | |
44 |
|
45 |
|
46 | this.onmessage = null
|
47 | |
48 |
|
49 |
|
50 | this._onChange = e => e.key === room && this.onmessage !== null && this.onmessage({ data: buffer.fromBase64(e.newValue || '') })
|
51 | storage.onChange(this._onChange)
|
52 | }
|
53 |
|
54 | |
55 |
|
56 |
|
57 | postMessage (buf) {
|
58 | storage.varStorage.setItem(this.room, buffer.toBase64(buffer.createUint8ArrayFromArrayBuffer(buf)))
|
59 | }
|
60 |
|
61 | close () {
|
62 | storage.offChange(this._onChange)
|
63 | }
|
64 | }
|
65 |
|
66 |
|
67 |
|
68 |
|
69 | const BC = typeof BroadcastChannel === 'undefined' ? LocalStoragePolyfill : BroadcastChannel
|
70 |
|
71 |
|
72 |
|
73 |
|
74 |
|
75 | const getChannel = room =>
|
76 | map.setIfUndefined(channels, room, () => {
|
77 | const subs = set.create()
|
78 | const bc = new BC(room)
|
79 | /**
|
80 | * @param {{data:ArrayBuffer}} e
|
81 | */
|
82 |
|
83 | bc.onmessage = e => subs.forEach(sub => sub(e.data, 'broadcastchannel'))
|
84 | return {
|
85 | bc, subs
|
86 | }
|
87 | })
|
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 |
|
94 |
|
95 |
|
96 | export const subscribe = (room, f) => {
|
97 | getChannel(room).subs.add(f)
|
98 | return f
|
99 | }
|
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 |
|
108 | export const unsubscribe = (room, f) => {
|
109 | const channel = getChannel(room)
|
110 | const unsubscribed = channel.subs.delete(f)
|
111 | if (unsubscribed && channel.subs.size === 0) {
|
112 | channel.bc.close()
|
113 | channels.delete(room)
|
114 | }
|
115 | return unsubscribed
|
116 | }
|
117 |
|
118 |
|
119 |
|
120 |
|
121 |
|
122 |
|
123 |
|
124 |
|
125 |
|
126 | export const publish = (room, data, origin = null) => {
|
127 | const c = getChannel(room)
|
128 | c.bc.postMessage(data)
|
129 | c.subs.forEach(sub => sub(data, origin))
|
130 | }
|