馃悕馃悕馃悕
1let transmitters: { [id: string]: Transmitter } = {};
2
3let receivers: { [id: string]: Receiver } = {};
4
5let workspaces: { [id: string]: Workspace } = {};
6
7function registerWorkspace(element: Element, renderWires: Function | null = null) {
8 if (workspaces[element.id] === undefined) {
9 workspaces[element.id] = {
10 id: element.id,
11 element,
12 transmitters: {},
13 receivers: {},
14 renderWires
15 };
16 } else {
17 if (renderWires !== null) {
18 workspaces[element.id].renderWires = renderWires;
19 }
20 }
21}
22
23export interface Workspace {
24 id: string;
25 element: Element;
26 transmitters: { [id: string]: Transmitter; };
27 receivers: { [id: string]: Receiver; };
28 renderWires: Function | null;
29}
30
31export interface Transmitter {
32 id: string;
33 element: Element;
34 rerender: Function;
35 schemas: string[];
36 connections: Receiver[];
37 workspace: Workspace | null;
38 dirty: boolean;
39}
40
41export interface Receiver {
42 id: string;
43 element: Element;
44 schemas: string[];
45 onSignal: Function;
46 connections: Transmitter[];
47 workspace: Workspace | null;
48 dirty: boolean;
49}
50
51function registerTransmitter(id: string, element: HTMLElement, rerender: Function, schemas: string[], connections: Receiver[]): void {
52 let transmitter: Transmitter = { id, element, rerender, schemas, connections, workspace: null, dirty: true };
53
54 transmitters[id] = transmitter;
55
56 let workspaceElement = seekParent(element, prefixes.workspace);
57 if (workspaceElement === null) {
58 return;
59 }
60
61 if (workspaces[workspaceElement.id] === undefined) {
62 registerWorkspace(workspaceElement);
63 }
64
65 workspaces[workspaceElement.id].transmitters[id] = transmitter;
66 transmitter.workspace = workspaces[workspaceElement.id];
67}
68
69function registerReceiver(id: string, element: HTMLElement, schemas: string[], onSignal: Function, connections: Transmitter[]) {
70 let receiver: Receiver = { id, element, schemas, onSignal, connections, workspace: null, dirty: true };
71
72 receivers[id] = receiver;
73
74 let workspace = seekParent(element, prefixes.workspace);
75 if (workspace === null) {
76 return;
77 }
78
79 if (workspaces[workspace.id] === undefined) {
80 registerWorkspace(workspace);
81 }
82
83 workspaces[workspace.id].receivers[id] = receiver;
84 receiver.workspace = workspaces[workspace.id];
85}
86
87function removeWorkspace(id: string) {
88 let workspace = workspaces[id];
89 for (const transmitterId in workspace.transmitters) {
90 removeTransmitter(transmitterId, true);
91 }
92 for (const receiverId in workspace.receivers) {
93 removeReceiver(receiverId, true);
94 }
95 delete workspaces[id];
96}
97
98function removeTransmitter(id: string, skipWorkspace: boolean = false) {
99 let transmitter = transmitters[id];
100
101 if (transmitter === undefined) {
102 // This happens sometimes because onDestroy gets called before onMount for some transmitters and receivers when the app starts up.
103 return;
104 }
105
106 for (const receiver of transmitter.connections) {
107 let index = receiver.connections.indexOf(transmitter);
108 receiver.connections.splice(index, 1);
109 receiver.dirty = true;
110 }
111
112 if (!skipWorkspace && transmitter.workspace !== null) {
113 delete transmitter.workspace.transmitters[id];
114 }
115
116 delete transmitters[id];
117}
118
119function removeReceiver(id: string, skipWorkspace: boolean = false) {
120 let receiver = receivers[id];
121
122 if (receiver === undefined) {
123 // This happens sometimes because onDestroy gets called before onMount for some transmitters and receivers when the app starts up.
124 return;
125 }
126
127 for (const transmitter of receiver.connections) {
128 let index = transmitter.connections.indexOf(receiver);
129 transmitter.connections.splice(index, 1);
130 transmitter.dirty = true;
131 }
132
133 if (!skipWorkspace && receiver.workspace !== null) {
134 delete receiver.workspace.receivers[id];
135 }
136
137 delete receivers[id];
138}
139
140function seekParent(element: Element, idPrefix: string): Element | null {
141 if (element === undefined) { return null; }
142 let currentElement = element.parentElement;
143
144 while (currentElement) {
145 if (currentElement.id.startsWith(idPrefix)) {
146 return currentElement;
147 }
148
149 currentElement = currentElement.parentElement;
150 }
151
152 return null;
153}
154
155function connect(transmitterId: string, receiverId: string) {
156 let transmitter = transmitters[transmitterId];
157 let receiver = receivers[receiverId];
158
159 transmitter.connections.push(receiver);
160 receiver.connections.push(transmitter);
161
162 transmitter.dirty = true;
163 receiver.dirty = true;
164
165 if (transmitter.workspace?.renderWires !== null) {
166 transmitter.workspace?.renderWires();
167 }
168
169 if (receiver.workspace?.renderWires !== null) {
170 if (receiver.workspace !== transmitter.workspace) {
171 receiver.workspace?.renderWires();
172 }
173 }
174
175}
176
177function childTransceivers(element: Element) {
178 let receivers: string[] = [];
179 let transmitters: string[] = [];
180
181 if (element === undefined) return { receivers, transmitters };
182
183 Array.from(element.children).forEach(child => {
184 if (child.id.startsWith(prefixes.receiver)) {
185 receivers.push(child.id);
186 }
187 if (child.id.startsWith(prefixes.transmitter)) {
188 transmitters.push(child.id);
189 }
190 let recurseResult = childTransceivers(child);
191 receivers.push(...recurseResult.receivers);
192 transmitters.push(...recurseResult.transmitters);
193 });
194
195 return { receivers, transmitters };
196}
197
198function idAtLocation(x: number, y: number, idPrefix: string): string | null {
199 var stack = [];
200 var el;
201 var result = null;
202 do {
203 el = document.elementFromPoint(x, y);
204 if (el === null) {
205 break;
206 }
207 if (el.id.startsWith(idPrefix)) {
208 result = el.id;
209 break;
210 }
211 stack.push(el);
212 el.classList.add('pointerEventsNone');
213 }while(el && el.tagName !== 'HTML');
214
215 // clean up
216 for(var i = 0; i < stack.length; i += 1)
217 stack[i].classList.remove('pointerEventsNone');
218
219 return result;
220}
221
222let prefixes = {
223 workspace: "WORKSPACE",
224 transmitter: "TRANSMITTER",
225 receiver: "RECEIVER"
226}
227
228export default {
229 receivers,
230 transmitters,
231 workspaces,
232 prefixes,
233 connect,
234 registerWorkspace,
235 registerTransmitter,
236 registerReceiver,
237 removeWorkspace,
238 removeTransmitter,
239 removeReceiver,
240 seekParent,
241 childTransceivers,
242 idAtLocation
243}