@cocreate/cursors 1.24.0 → 1.24.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/docs/index.html +237 -237
- package/package.json +11 -15
- package/src/index.js +493 -424
- package/webpack.config.js +65 -90
package/src/index.js
CHANGED
|
@@ -21,14 +21,14 @@
|
|
|
21
21
|
// For details, visit <https://cocreate.app/licenses/> or contact us at sales@cocreate.app.
|
|
22
22
|
|
|
23
23
|
/*globals ResizeObserver*/
|
|
24
|
-
import observer from
|
|
25
|
-
import { getAttributeNames } from
|
|
26
|
-
import socket from
|
|
27
|
-
import uuid from
|
|
28
|
-
import localStorage from
|
|
29
|
-
import { getElementPosition } from
|
|
30
|
-
import { randomColor } from
|
|
31
|
-
import
|
|
24
|
+
import observer from "@cocreate/observer";
|
|
25
|
+
import { getAttributeNames } from "@cocreate/utils";
|
|
26
|
+
import socket from "@cocreate/socket-client";
|
|
27
|
+
import uuid from "@cocreate/uuid";
|
|
28
|
+
import localStorage from "@cocreate/local-storage";
|
|
29
|
+
import { getElementPosition } from "@cocreate/selection";
|
|
30
|
+
import { randomColor } from "@cocreate/random-color";
|
|
31
|
+
import "./index.css";
|
|
32
32
|
|
|
33
33
|
const clientId = socket.clientId || uuid.generate(12);
|
|
34
34
|
const cursorBackground = randomColor();
|
|
@@ -38,465 +38,534 @@ let environment_prod = true;
|
|
|
38
38
|
let selector = "[array][object][key]:not([contentEditable='false'])";
|
|
39
39
|
|
|
40
40
|
function init() {
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
let elements = document.querySelectorAll(selector);
|
|
42
|
+
initElements(elements);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
function initElements(elements) {
|
|
46
|
-
|
|
47
|
-
initElement(element);
|
|
46
|
+
for (let element of elements) initElement(element);
|
|
48
47
|
}
|
|
49
48
|
|
|
50
49
|
function initElement(element) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
50
|
+
let realtime = element.getAttribute("realtime");
|
|
51
|
+
let cursors = element.getAttribute("cursors");
|
|
52
|
+
if (!realtime || realtime === "false" || cursors === "false") return;
|
|
53
|
+
if (element.hasAttribute("actions")) return;
|
|
54
|
+
if (element.cursor) return;
|
|
55
|
+
element.cursor = true;
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
function initDocument(doc) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
59
|
+
let documents;
|
|
60
|
+
try {
|
|
61
|
+
documents = window.top.cursorDocuments;
|
|
62
|
+
} catch (e) {
|
|
63
|
+
console.log("cross-origin failed");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!documents) {
|
|
67
|
+
documents = new Map();
|
|
68
|
+
try {
|
|
69
|
+
window.top.cursorDocuments = documents;
|
|
70
|
+
} catch (e) {
|
|
71
|
+
console.log("cross-origin failed");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (!documents.has(doc)) {
|
|
75
|
+
initResizeObserver(doc.documentElement);
|
|
76
|
+
documents.set(doc);
|
|
77
|
+
}
|
|
79
78
|
}
|
|
80
79
|
|
|
81
80
|
function drawCursors(selection) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
81
|
+
const socket_id = selection["socketId"];
|
|
82
|
+
const array = selection["array"];
|
|
83
|
+
const object = selection["object"];
|
|
84
|
+
const key = selection["key"];
|
|
85
|
+
let elements = selection.element;
|
|
86
|
+
if (!elements) {
|
|
87
|
+
let selector =
|
|
88
|
+
'[array="' +
|
|
89
|
+
array +
|
|
90
|
+
'"][object="' +
|
|
91
|
+
object +
|
|
92
|
+
'"][key="' +
|
|
93
|
+
key +
|
|
94
|
+
'"]';
|
|
95
|
+
selector += ":not([actions])";
|
|
96
|
+
elements = document.querySelectorAll(selector);
|
|
97
|
+
}
|
|
98
|
+
for (let element of elements) {
|
|
99
|
+
let start = selection["start"];
|
|
100
|
+
let end = selection["end"];
|
|
101
|
+
if (
|
|
102
|
+
window.activeElement == element &&
|
|
103
|
+
socket.frameId === selection.frameId
|
|
104
|
+
) {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
let realtime = element.getAttribute("realtime");
|
|
108
|
+
let cursors = element.getAttribute("cursors");
|
|
109
|
+
let contenteditable = element.getAttribute("contenteditable");
|
|
110
|
+
if (!realtime || realtime == "false" || cursors == "false") continue;
|
|
111
|
+
if (element.tagName != "INPUT" && element.tagName != "TEXTAREA")
|
|
112
|
+
if (contenteditable == "false" || contenteditable === null) {
|
|
113
|
+
if (cursors != "true") continue;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
let domTextEditor = element;
|
|
117
|
+
|
|
118
|
+
if (element.hasAttribute("contenteditable")) {
|
|
119
|
+
contenteditable = true;
|
|
120
|
+
// let domTextEditor = element;
|
|
121
|
+
if (element.tagName == "IFRAME") {
|
|
122
|
+
// let frameClientId = element.contentDocument.defaultView.CoCreateSockets.id;
|
|
123
|
+
// if (frameClientId == selection.socketId) continue;
|
|
124
|
+
domTextEditor = element =
|
|
125
|
+
element.contentDocument.documentElement;
|
|
126
|
+
|
|
127
|
+
let pos = getElementPosition(
|
|
128
|
+
domTextEditor.htmlString,
|
|
129
|
+
start,
|
|
130
|
+
end
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
if (pos.path) {
|
|
134
|
+
element = element.querySelector(pos.path);
|
|
135
|
+
if (pos.start) {
|
|
136
|
+
let endPos = end - start;
|
|
137
|
+
if (endPos > 0) end = pos.start + endPos;
|
|
138
|
+
else end = pos.start;
|
|
139
|
+
start = pos.start;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (!domTextEditor.htmlString) continue;
|
|
145
|
+
|
|
146
|
+
// let pos = getElementPosition(domTextEditor.htmlString, start, end);
|
|
147
|
+
|
|
148
|
+
// if (pos.path) {
|
|
149
|
+
// element = domTextEditor.querySelector(pos.path);
|
|
150
|
+
// if (pos.start) {
|
|
151
|
+
// let endPos = end - start;
|
|
152
|
+
// if (endPos > 0)
|
|
153
|
+
// end = pos.start + endPos;
|
|
154
|
+
// else
|
|
155
|
+
// end = pos.start;
|
|
156
|
+
// start = pos.start;
|
|
157
|
+
// }
|
|
158
|
+
// }
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (!element) continue;
|
|
162
|
+
let document = element.ownerDocument;
|
|
163
|
+
let mirrorDiv;
|
|
164
|
+
let id_mirror = element.getAttribute("mirror_id");
|
|
165
|
+
if (!id_mirror) {
|
|
166
|
+
id_mirror = uuid.generate(8);
|
|
167
|
+
element.setAttribute("mirror_id", id_mirror);
|
|
168
|
+
|
|
169
|
+
mirrorDiv = document.createElement("cursor-container");
|
|
170
|
+
mirrorDiv.id = id_mirror;
|
|
171
|
+
mirrorDiv.className = environment_prod
|
|
172
|
+
? "mirror mirror_scroll mirror_color"
|
|
173
|
+
: "mirror mirror_scroll";
|
|
174
|
+
mirrorDiv.contentEditable = false;
|
|
175
|
+
mirrorDiv.element = element;
|
|
176
|
+
if (element.tagName === "HTML")
|
|
177
|
+
element.insertAdjacentElement("beforeend", mirrorDiv);
|
|
178
|
+
else element.insertAdjacentElement("afterend", mirrorDiv);
|
|
179
|
+
// let parent = element.parentElement;
|
|
180
|
+
// let parentComputed = getComputedStyle(parent);
|
|
181
|
+
// let parentStylePosition = parentComputed['position'];
|
|
182
|
+
// if (parentStylePosition == 'static')
|
|
183
|
+
// parent.style.position = 'relative';
|
|
184
|
+
// let parentStyleDisplay = parentComputed['display'];
|
|
185
|
+
// if (parentStyleDisplay == 'inline')
|
|
186
|
+
// parent.style.display = 'inline';
|
|
187
|
+
|
|
188
|
+
_initEvents(element);
|
|
189
|
+
initDocument(document);
|
|
190
|
+
} else mirrorDiv = document.getElementById(id_mirror);
|
|
191
|
+
|
|
192
|
+
let computed = getComputedStyle(element);
|
|
193
|
+
let rect = element.getBoundingClientRect();
|
|
194
|
+
|
|
195
|
+
let borderWidth = parseInt(computed["border-width"]) * 2;
|
|
196
|
+
let scrollBarWidth = 0;
|
|
197
|
+
if (element.clientWidth != 0)
|
|
198
|
+
scrollBarWidth =
|
|
199
|
+
element.offsetWidth - element.clientWidth - borderWidth;
|
|
200
|
+
|
|
201
|
+
let style = mirrorDiv.style;
|
|
202
|
+
style.position = "absolute";
|
|
203
|
+
style.width = rect.width + "px";
|
|
204
|
+
style.height = rect.height + "px";
|
|
205
|
+
style.margin = "0px";
|
|
206
|
+
style.borderColor = "transparent";
|
|
207
|
+
style.outlineColor = "transparent";
|
|
208
|
+
|
|
209
|
+
style.overflowX = computed["overflowX"];
|
|
210
|
+
style.overflowY = computed["overflowY"];
|
|
211
|
+
style.padding = computed["padding"];
|
|
212
|
+
style.border = computed["border"];
|
|
213
|
+
style.outline = computed["outline"];
|
|
214
|
+
|
|
215
|
+
if (
|
|
216
|
+
element.parentElement &&
|
|
217
|
+
element.parentElement.style["display"] == "inline"
|
|
218
|
+
) {
|
|
219
|
+
style.top = element.clientTop + "px";
|
|
220
|
+
style.left = element.clientLeft + "px";
|
|
221
|
+
} else {
|
|
222
|
+
style.top = element.offsetTop + "px";
|
|
223
|
+
style.left = element.offsetLeft + "px";
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (contenteditable) {
|
|
227
|
+
const actualStyles = getActualStyles(element);
|
|
228
|
+
for (const [prop, value] of Object.entries(actualStyles)) {
|
|
229
|
+
style[prop] = value;
|
|
230
|
+
}
|
|
231
|
+
} else {
|
|
232
|
+
let properties = [
|
|
233
|
+
"boxSizing",
|
|
234
|
+
"fontStyle",
|
|
235
|
+
"fontVariant",
|
|
236
|
+
"fontWeight",
|
|
237
|
+
"fontStretch",
|
|
238
|
+
"fontSize",
|
|
239
|
+
"fontFamily",
|
|
240
|
+
"letterSpacing",
|
|
241
|
+
"lineHeight",
|
|
242
|
+
"textAlign",
|
|
243
|
+
"textTransform",
|
|
244
|
+
"textIndent",
|
|
245
|
+
"textDecoration",
|
|
246
|
+
"textRendering",
|
|
247
|
+
"textTransform",
|
|
248
|
+
"textIndent",
|
|
249
|
+
"overflowWrap",
|
|
250
|
+
"tabSize",
|
|
251
|
+
"webkitWritingMode",
|
|
252
|
+
"whiteSpace",
|
|
253
|
+
"wordSpacing"
|
|
254
|
+
];
|
|
255
|
+
properties.forEach(function (prop) {
|
|
256
|
+
style[prop] = computed[prop];
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
let paddingBottom = parseFloat(
|
|
260
|
+
computed["padding-bottom"].replace("px", "")
|
|
261
|
+
);
|
|
262
|
+
style.height = rect.height - paddingBottom + "px";
|
|
263
|
+
|
|
264
|
+
if (element.nodeName === "INPUT") style.whiteSpace = "pre";
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
let cursor, cursorFlag;
|
|
268
|
+
let details = { array, object, key };
|
|
269
|
+
if (socket_id) {
|
|
270
|
+
let selection_user = mirrorDiv.querySelector(
|
|
271
|
+
`selection[socket_id="${socket_id}"]`
|
|
272
|
+
);
|
|
273
|
+
if (selection_user && selection_user.details != details)
|
|
274
|
+
selection_user.remove();
|
|
275
|
+
|
|
276
|
+
selection_user = document.createElement("selection");
|
|
277
|
+
selection_user.setAttribute("socket_id", socket_id);
|
|
278
|
+
selection_user.style["position"] = "absolute";
|
|
279
|
+
let style_mirror = getComputedStyle(mirrorDiv);
|
|
280
|
+
selection_user.style["top"] = style_mirror.paddingTop;
|
|
281
|
+
selection_user.style["left"] = style_mirror.paddingLeft;
|
|
282
|
+
selection_user.style["width"] =
|
|
283
|
+
mirrorDiv.clientWidth -
|
|
284
|
+
parseInt(computed["padding-left"]) -
|
|
285
|
+
parseInt(computed["padding-right"]) -
|
|
286
|
+
scrollBarWidth +
|
|
287
|
+
"px";
|
|
288
|
+
selection_user.details = { array, object, key };
|
|
289
|
+
|
|
290
|
+
let value_element =
|
|
291
|
+
["TEXTAREA", "INPUT"].indexOf(element.nodeName) == -1
|
|
292
|
+
? element.innerHTML
|
|
293
|
+
: element.value;
|
|
294
|
+
if (contenteditable) {
|
|
295
|
+
selection_user.innerHTML = value_element.substring(0, start);
|
|
296
|
+
} else
|
|
297
|
+
selection_user.textContent = value_element.substring(0, start);
|
|
298
|
+
|
|
299
|
+
let selectionElement;
|
|
300
|
+
if (contenteditable) {
|
|
301
|
+
selectionElement = getElementPosition(
|
|
302
|
+
domTextEditor.htmlString,
|
|
303
|
+
selection.start,
|
|
304
|
+
selection.end
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
let lastChildTagName, lastChildElement;
|
|
309
|
+
if (
|
|
310
|
+
selectionElement &&
|
|
311
|
+
selectionElement.element &&
|
|
312
|
+
selectionElement.position !== "afterend"
|
|
313
|
+
) {
|
|
314
|
+
lastChildElement = selection_user.lastElementChild;
|
|
315
|
+
if (lastChildElement)
|
|
316
|
+
lastChildTagName = lastChildElement.tagName.toLowerCase();
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
let selectedText = document.createElement("selected-text");
|
|
320
|
+
selectedText.style["backgroundColor"] = selection.background;
|
|
321
|
+
|
|
322
|
+
let setCursorInLastChild = false;
|
|
323
|
+
if (contenteditable) {
|
|
324
|
+
if (lastChildTagName && start !== end) {
|
|
325
|
+
let selectedTextFrag =
|
|
326
|
+
document.createElement("selected-text");
|
|
327
|
+
selectedTextFrag.style["backgroundColor"] =
|
|
328
|
+
selection.background;
|
|
329
|
+
|
|
330
|
+
const closingTagRegex = new RegExp(
|
|
331
|
+
"</\\s*" + lastChildTagName + "\\s*>",
|
|
332
|
+
"i"
|
|
333
|
+
);
|
|
334
|
+
let secondHalfString =
|
|
335
|
+
value_element.substring(start, end) || "";
|
|
336
|
+
const closingTagMatch =
|
|
337
|
+
secondHalfString.match(closingTagRegex);
|
|
338
|
+
// console.log('secondHalfString', secondHalfString)
|
|
339
|
+
|
|
340
|
+
let closingTagEnd;
|
|
341
|
+
if (closingTagMatch) {
|
|
342
|
+
closingTagEnd =
|
|
343
|
+
closingTagMatch.index + closingTagMatch[0].length;
|
|
344
|
+
// console.log('closingTagEnd', closingTagEnd)
|
|
345
|
+
|
|
346
|
+
selectedTextFrag.innerHTML =
|
|
347
|
+
secondHalfString.substring(0, closingTagEnd) || "";
|
|
348
|
+
lastChildElement.appendChild(selectedTextFrag);
|
|
349
|
+
// console.log('selectedTextFrag', selectedTextFrag)
|
|
350
|
+
|
|
351
|
+
selectedText.innerHTML =
|
|
352
|
+
value_element.substring(
|
|
353
|
+
start + closingTagEnd,
|
|
354
|
+
end
|
|
355
|
+
) || "";
|
|
356
|
+
// console.log('selectedText', selectedText)
|
|
357
|
+
} else {
|
|
358
|
+
selectedText.innerHTML =
|
|
359
|
+
value_element.substring(start, end) || "";
|
|
360
|
+
setCursorInLastChild = true;
|
|
361
|
+
}
|
|
362
|
+
} else {
|
|
363
|
+
if (lastChildTagName) setCursorInLastChild = true;
|
|
364
|
+
|
|
365
|
+
selectedText.innerHTML =
|
|
366
|
+
value_element.substring(start, end) || "";
|
|
367
|
+
}
|
|
368
|
+
} else {
|
|
369
|
+
selectedText.textContent =
|
|
370
|
+
value_element.substring(start, end) || "";
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
cursor = document.createElement("cursor");
|
|
374
|
+
cursor.setAttribute("socket_id", socket_id);
|
|
375
|
+
cursor.dataset.start = start;
|
|
376
|
+
cursor.dataset.end = end;
|
|
377
|
+
cursor.setAttribute("user-name", selection.userName);
|
|
378
|
+
cursor.setAttribute("user-background", selection.background);
|
|
379
|
+
cursor.setAttribute("user-color", selection.color);
|
|
380
|
+
cursor.style["background"] = selection.background;
|
|
381
|
+
cursor.details = { array, object, key };
|
|
382
|
+
cursor.textContent = " ";
|
|
383
|
+
|
|
384
|
+
cursorFlag = document.createElement("cursor-flag");
|
|
385
|
+
cursorFlag.setAttribute("array", "users");
|
|
386
|
+
cursorFlag.setAttribute("object", selection.user_id);
|
|
387
|
+
cursorFlag.setAttribute("key", "name");
|
|
388
|
+
cursorFlag.style["background"] = selection.background;
|
|
389
|
+
cursorFlag.innerHTML = selection.userName;
|
|
390
|
+
cursor.appendChild(cursorFlag);
|
|
391
|
+
|
|
392
|
+
if (setCursorInLastChild) {
|
|
393
|
+
lastChildElement.appendChild(selectedText);
|
|
394
|
+
lastChildElement.appendChild(cursor);
|
|
395
|
+
} else {
|
|
396
|
+
selection_user.appendChild(selectedText);
|
|
397
|
+
selection_user.appendChild(cursor);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
let span_end = document.createElement("span-end");
|
|
401
|
+
let value_end = value_element.substring(end) || "";
|
|
402
|
+
if (contenteditable) span_end.innerHTML = value_end;
|
|
403
|
+
else span_end.textContent = value_end;
|
|
404
|
+
|
|
405
|
+
selection_user.appendChild(span_end);
|
|
406
|
+
|
|
407
|
+
mirrorDiv.appendChild(selection_user);
|
|
408
|
+
}
|
|
409
|
+
scrollMirror(element);
|
|
410
|
+
}
|
|
346
411
|
}
|
|
347
412
|
|
|
348
413
|
function getActualStyles(element) {
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
414
|
+
const actualStyles = {};
|
|
415
|
+
|
|
416
|
+
// Copy styles that are directly applied to the element (inline styles)
|
|
417
|
+
for (const prop in element.style) {
|
|
418
|
+
if (element.style[prop]) {
|
|
419
|
+
actualStyles[prop] = element.style[prop];
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Get styles from classes, IDs, attributes, and tag names
|
|
424
|
+
for (const sheet of document.styleSheets) {
|
|
425
|
+
try {
|
|
426
|
+
for (const rule of sheet.cssRules) {
|
|
427
|
+
if (element.matches(rule.selectorText)) {
|
|
428
|
+
for (const prop of rule.style) {
|
|
429
|
+
actualStyles[prop] = rule.style.getPropertyValue(prop);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
} catch (e) {
|
|
434
|
+
console.warn("Unable to access stylesheet:", e);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
return actualStyles;
|
|
374
439
|
}
|
|
375
440
|
|
|
376
441
|
function updateCursors(element) {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
}
|
|
442
|
+
let mirrorDivs = [];
|
|
443
|
+
let id_mirror = element.getAttribute("mirror_id");
|
|
444
|
+
if (id_mirror) {
|
|
445
|
+
let mirrorDiv = element.ownerDocument.getElementById(id_mirror);
|
|
446
|
+
if (mirrorDiv) mirrorDivs = [mirrorDiv];
|
|
447
|
+
} else {
|
|
448
|
+
mirrorDivs = element.querySelectorAll(".mirror");
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
for (let mirrorDiv of mirrorDivs) {
|
|
452
|
+
element = mirrorDiv.element;
|
|
453
|
+
let cursors = mirrorDiv.querySelectorAll("cursor");
|
|
454
|
+
|
|
455
|
+
cursors.forEach(function (child_cursor, index, array) {
|
|
456
|
+
let socketId = child_cursor.getAttribute("socket_id");
|
|
457
|
+
let userName = child_cursor.getAttribute("user-name");
|
|
458
|
+
let background = child_cursor.getAttribute("user-background");
|
|
459
|
+
let color = child_cursor.getAttribute("user-color");
|
|
460
|
+
let start = child_cursor.getAttribute("data-start");
|
|
461
|
+
let end = child_cursor.getAttribute("data-end");
|
|
462
|
+
let selection = {
|
|
463
|
+
element: [element],
|
|
464
|
+
start,
|
|
465
|
+
end,
|
|
466
|
+
socketId,
|
|
467
|
+
userName,
|
|
468
|
+
background,
|
|
469
|
+
color
|
|
470
|
+
};
|
|
471
|
+
drawCursors(selection);
|
|
472
|
+
});
|
|
473
|
+
}
|
|
410
474
|
}
|
|
411
475
|
|
|
412
476
|
function removeCursor(socketId) {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
477
|
+
let elements = document.querySelectorAll(`cursor[socket_id="${socketId}"]`);
|
|
478
|
+
elements.forEach(function (element, index, array) {
|
|
479
|
+
element.parentNode.removeChild(element);
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
let sel_elements = document.querySelectorAll(
|
|
483
|
+
`selection[socket_id="${socketId}"]`
|
|
484
|
+
);
|
|
485
|
+
sel_elements.forEach(function (sel_element, index, array) {
|
|
486
|
+
sel_element.parentNode.removeChild(sel_element);
|
|
487
|
+
});
|
|
422
488
|
}
|
|
423
489
|
|
|
424
490
|
function _initEvents(element) {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
491
|
+
element.addEventListener("keydown", function (event) {
|
|
492
|
+
scrollMirror(element);
|
|
493
|
+
});
|
|
428
494
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
495
|
+
element.addEventListener("scroll", function () {
|
|
496
|
+
scrollMirror(element);
|
|
497
|
+
});
|
|
432
498
|
|
|
433
|
-
|
|
499
|
+
initResizeObserver(element);
|
|
434
500
|
}
|
|
435
501
|
|
|
436
502
|
function scrollMirror(element) {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
503
|
+
let id_mirror = element.getAttribute("mirror_id");
|
|
504
|
+
let elementMirror = document.getElementById(id_mirror);
|
|
505
|
+
if (elementMirror)
|
|
506
|
+
elementMirror.scrollTo(element.scrollLeft, element.scrollTop);
|
|
441
507
|
}
|
|
442
508
|
|
|
443
|
-
|
|
444
509
|
function initResizeObserver(element) {
|
|
445
|
-
|
|
446
|
-
|
|
510
|
+
const watch = new ResizeObserver(() => updateCursors(element));
|
|
511
|
+
watch.observe(element);
|
|
447
512
|
}
|
|
448
513
|
|
|
449
514
|
function sendPosition(info) {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
515
|
+
try {
|
|
516
|
+
socket.send({
|
|
517
|
+
method: "cursor",
|
|
518
|
+
data: {
|
|
519
|
+
array: info.array,
|
|
520
|
+
object: info.object,
|
|
521
|
+
key: info.key,
|
|
522
|
+
start: info.start,
|
|
523
|
+
end: info.end,
|
|
524
|
+
color: info.color || localStorage.getItem("cursorColor"),
|
|
525
|
+
background:
|
|
526
|
+
info.background ||
|
|
527
|
+
localStorage.getItem("cursorBackground") ||
|
|
528
|
+
cursorBackground,
|
|
529
|
+
userName:
|
|
530
|
+
info.userName ||
|
|
531
|
+
localStorage.getItem("userName") ||
|
|
532
|
+
clientId,
|
|
533
|
+
user_id:
|
|
534
|
+
info.user_id || localStorage.getItem("user_id") || clientId
|
|
535
|
+
},
|
|
536
|
+
broadcastBrowser: true
|
|
537
|
+
});
|
|
538
|
+
} catch (e) {
|
|
539
|
+
console.error(e);
|
|
540
|
+
}
|
|
470
541
|
}
|
|
471
542
|
|
|
472
|
-
|
|
473
|
-
socket.
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
else
|
|
481
|
-
removeCursor(selection.socketId);
|
|
543
|
+
socket.listen("cursor", function (response) {
|
|
544
|
+
// if (socket.has(selection.socketId)) return;
|
|
545
|
+
let selection = response.data;
|
|
546
|
+
selection.socketId = response.socketId;
|
|
547
|
+
selection.frameId = response.frameId;
|
|
548
|
+
if (selection.start != null && selection.end != null)
|
|
549
|
+
drawCursors(selection);
|
|
550
|
+
else removeCursor(selection.socketId);
|
|
482
551
|
});
|
|
483
552
|
|
|
484
553
|
observer.init({
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
554
|
+
name: "CoCreateCursor",
|
|
555
|
+
types: ["addedNodes"],
|
|
556
|
+
selector: "[array][object][key]",
|
|
557
|
+
callback: function (mutation) {
|
|
558
|
+
initElement(mutation.target);
|
|
559
|
+
}
|
|
491
560
|
});
|
|
492
561
|
|
|
493
562
|
observer.init({
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
563
|
+
name: "CoCreateCursorAtt",
|
|
564
|
+
types: ["attributes"],
|
|
565
|
+
attributeFilter: getAttributeNames(["array", "object", "key"]),
|
|
566
|
+
callback: function (mutation) {
|
|
567
|
+
initElement(mutation.target);
|
|
568
|
+
}
|
|
500
569
|
});
|
|
501
570
|
|
|
502
571
|
init();
|