@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/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 '@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';
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
- let elements = document.querySelectorAll(selector);
42
- initElements(elements);
41
+ let elements = document.querySelectorAll(selector);
42
+ initElements(elements);
43
43
  }
44
44
 
45
45
  function initElements(elements) {
46
- for (let element of elements)
47
- initElement(element);
46
+ for (let element of elements) initElement(element);
48
47
  }
49
48
 
50
49
  function initElement(element) {
51
- let realtime = element.getAttribute('realtime');
52
- let cursors = element.getAttribute('cursors');
53
- if (!realtime || realtime === 'false' || cursors === 'false') return;
54
- if (element.hasAttribute('actions')) return;
55
- if (element.cursor) return;
56
- element.cursor = true;
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
- let documents;
61
- try {
62
- documents = window.top.cursorDocuments;
63
- } catch (e) {
64
- console.log('cross-origin failed')
65
- }
66
-
67
- if (!documents) {
68
- documents = new Map();
69
- try {
70
- window.top.cursorDocuments = documents;
71
- } catch (e) {
72
- console.log('cross-origin failed')
73
- }
74
- }
75
- if (!documents.has(doc)) {
76
- initResizeObserver(doc.documentElement);
77
- documents.set(doc);
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
- const socket_id = selection['socketId'];
83
- const array = selection['array'];
84
- const object = selection['object'];
85
- const key = selection['key'];
86
- let elements = selection.element;
87
- if (!elements) {
88
- let selector = '[array="' + array + '"][object="' + object + '"][key="' + key + '"]';
89
- selector += ':not([actions])';
90
- elements = document.querySelectorAll(selector);
91
- }
92
- for (let element of elements) {
93
- let start = selection['start']
94
- let end = selection['end']
95
- if (window.activeElement == element && socket.frameId === selection.frameId) {
96
- continue;
97
- }
98
- let realtime = element.getAttribute('realtime');
99
- let cursors = element.getAttribute('cursors');
100
- let contenteditable = element.getAttribute('contenteditable');
101
- if (!realtime || realtime == 'false' || cursors == 'false') continue;
102
- if (element.tagName != 'INPUT' && element.tagName != 'TEXTAREA')
103
- if (contenteditable == 'false' || contenteditable === null) {
104
- if (cursors != 'true') continue;
105
- }
106
-
107
- let domTextEditor = element;
108
-
109
- if (element.hasAttribute('contenteditable')) {
110
- contenteditable = true
111
- // let domTextEditor = element;
112
- if (element.tagName == 'IFRAME') {
113
- // let frameClientId = element.contentDocument.defaultView.CoCreateSockets.id;
114
- // if (frameClientId == selection.socketId) continue;
115
- domTextEditor = element = element.contentDocument.documentElement;
116
-
117
- let pos = getElementPosition(domTextEditor.htmlString, start, end);
118
-
119
- if (pos.path) {
120
- element = element.querySelector(pos.path);
121
- if (pos.start) {
122
- let endPos = end - start;
123
- if (endPos > 0)
124
- end = pos.start + endPos;
125
- else
126
- end = pos.start;
127
- start = pos.start;
128
- }
129
- }
130
-
131
- }
132
-
133
- if (!domTextEditor.htmlString)
134
- continue
135
-
136
- // let pos = getElementPosition(domTextEditor.htmlString, start, end);
137
-
138
- // if (pos.path) {
139
- // element = domTextEditor.querySelector(pos.path);
140
- // if (pos.start) {
141
- // let endPos = end - start;
142
- // if (endPos > 0)
143
- // end = pos.start + endPos;
144
- // else
145
- // end = pos.start;
146
- // start = pos.start;
147
- // }
148
- // }
149
- }
150
-
151
- if (!element) continue;
152
- let document = element.ownerDocument;
153
- let mirrorDiv;
154
- let id_mirror = element.getAttribute('mirror_id');
155
- if (!id_mirror) {
156
- id_mirror = uuid.generate(8);
157
- element.setAttribute('mirror_id', id_mirror);
158
-
159
- mirrorDiv = document.createElement('cursor-container');
160
- mirrorDiv.id = id_mirror;
161
- mirrorDiv.className = (environment_prod) ? 'mirror mirror_scroll mirror_color' : 'mirror mirror_scroll';
162
- mirrorDiv.contentEditable = false;
163
- mirrorDiv.element = element;
164
- if (element.tagName === "HTML")
165
- element.insertAdjacentElement('beforeend', mirrorDiv);
166
- else
167
- element.insertAdjacentElement('afterend', mirrorDiv);
168
- // let parent = element.parentElement;
169
- // let parentComputed = getComputedStyle(parent);
170
- // let parentStylePosition = parentComputed['position'];
171
- // if (parentStylePosition == 'static')
172
- // parent.style.position = 'relative';
173
- // let parentStyleDisplay = parentComputed['display'];
174
- // if (parentStyleDisplay == 'inline')
175
- // parent.style.display = 'inline';
176
-
177
- _initEvents(element);
178
- initDocument(document);
179
- } else
180
- mirrorDiv = document.getElementById(id_mirror);
181
-
182
-
183
- let computed = getComputedStyle(element);
184
- let rect = element.getBoundingClientRect();
185
-
186
- let borderWidth = parseInt(computed['border-width']) * 2;
187
- let scrollBarWidth = 0;
188
- if (element.clientWidth != 0)
189
- scrollBarWidth = element.offsetWidth - element.clientWidth - borderWidth;
190
-
191
- let style = mirrorDiv.style;
192
- style.position = 'absolute';
193
- style.width = rect.width + 'px';
194
- style.height = rect.height + 'px';
195
- style.margin = '0px';
196
- style.borderColor = 'transparent';
197
- style.outlineColor = 'transparent';
198
-
199
- style.overflowX = computed['overflowX'];
200
- style.overflowY = computed['overflowY'];
201
- style.padding = computed['padding'];
202
- style.border = computed['border'];
203
- style.outline = computed['outline'];
204
-
205
- if (element.parentElement && element.parentElement.style['display'] == 'inline') {
206
- style.top = element.clientTop + 'px';
207
- style.left = element.clientLeft + 'px';
208
- } else {
209
- style.top = element.offsetTop + 'px';
210
- style.left = element.offsetLeft + 'px';
211
- }
212
-
213
- if (contenteditable) {
214
- const actualStyles = getActualStyles(element);
215
- for (const [prop, value] of Object.entries(actualStyles)) {
216
- style[prop] = value;
217
- }
218
- } else {
219
- let properties = ['boxSizing', 'fontStyle', 'fontVariant', 'fontWeight', 'fontStretch', 'fontSize', 'fontFamily', 'letterSpacing', 'lineHeight', 'textAlign', 'textTransform', 'textIndent', 'textDecoration', 'textRendering', 'textTransform', 'textIndent', 'overflowWrap', 'tabSize', 'webkitWritingMode', 'whiteSpace', 'wordSpacing'];
220
- properties.forEach(function (prop) {
221
- style[prop] = computed[prop];
222
- });
223
-
224
- let paddingBottom = parseFloat(computed['padding-bottom'].replace('px', ''))
225
- style.height = rect.height - paddingBottom + 'px';
226
-
227
- if (element.nodeName === 'INPUT')
228
- style.whiteSpace = 'pre';
229
- }
230
-
231
- let cursor, cursorFlag;
232
- let details = { array, object, key };
233
- if (socket_id) {
234
- let selection_user = mirrorDiv.querySelector(`selection[socket_id="${socket_id}"]`);
235
- if (selection_user && selection_user.details != details)
236
- selection_user.remove();
237
-
238
- selection_user = document.createElement('selection');
239
- selection_user.setAttribute('socket_id', socket_id);
240
- selection_user.style["position"] = "absolute";
241
- let style_mirror = getComputedStyle(mirrorDiv);
242
- selection_user.style["top"] = style_mirror.paddingTop;
243
- selection_user.style["left"] = style_mirror.paddingLeft;
244
- selection_user.style["width"] = mirrorDiv.clientWidth - parseInt(computed['padding-left']) - parseInt(computed['padding-right']) - scrollBarWidth + 'px';
245
- selection_user.details = { array, object, key };
246
-
247
- let value_element = (['TEXTAREA', 'INPUT'].indexOf(element.nodeName) == -1) ? element.innerHTML : element.value;
248
- if (contenteditable) {
249
- selection_user.innerHTML = value_element.substring(0, start);
250
- } else
251
- selection_user.textContent = value_element.substring(0, start);
252
-
253
- let selectionElement
254
- if (contenteditable) {
255
- selectionElement = getElementPosition(domTextEditor.htmlString, selection.start, selection.end);
256
- }
257
-
258
- let lastChildTagName, lastChildElement
259
- if (selectionElement && selectionElement.element && selectionElement.position !== 'afterend') {
260
- lastChildElement = selection_user.lastElementChild;
261
- if (lastChildElement)
262
- lastChildTagName = lastChildElement.tagName.toLowerCase()
263
- }
264
-
265
- let selectedText = document.createElement('selected-text');
266
- selectedText.style["backgroundColor"] = selection.background;
267
-
268
- let setCursorInLastChild = false
269
- if (contenteditable) {
270
- if (lastChildTagName && start !== end) {
271
- let selectedTextFrag = document.createElement('selected-text');
272
- selectedTextFrag.style["backgroundColor"] = selection.background;
273
-
274
- const closingTagRegex = new RegExp("</\\s*" + lastChildTagName + "\\s*>", "i");
275
- let secondHalfString = value_element.substring(start, end) || '';
276
- const closingTagMatch = secondHalfString.match(closingTagRegex);
277
- // console.log('secondHalfString', secondHalfString)
278
-
279
- let closingTagEnd
280
- if (closingTagMatch) {
281
- closingTagEnd = closingTagMatch.index + closingTagMatch[0].length;
282
- // console.log('closingTagEnd', closingTagEnd)
283
-
284
- selectedTextFrag.innerHTML = secondHalfString.substring(0, closingTagEnd) || '';
285
- lastChildElement.appendChild(selectedTextFrag);
286
- // console.log('selectedTextFrag', selectedTextFrag)
287
-
288
- selectedText.innerHTML = value_element.substring(start + closingTagEnd, end) || '';
289
- // console.log('selectedText', selectedText)
290
-
291
- } else {
292
- selectedText.innerHTML = value_element.substring(start, end) || '';
293
- setCursorInLastChild = true
294
- }
295
- } else {
296
- if (lastChildTagName)
297
- setCursorInLastChild = true
298
-
299
- selectedText.innerHTML = value_element.substring(start, end) || '';
300
- }
301
- } else {
302
- selectedText.textContent = value_element.substring(start, end) || '';
303
- }
304
-
305
-
306
- cursor = document.createElement('cursor');
307
- cursor.setAttribute('socket_id', socket_id);
308
- cursor.dataset.start = start;
309
- cursor.dataset.end = end;
310
- cursor.setAttribute('user-name', selection.userName);
311
- cursor.setAttribute('user-background', selection.background);
312
- cursor.setAttribute('user-color', selection.color);
313
- cursor.style["background"] = selection.background;
314
- cursor.details = { array, object, key };
315
- cursor.textContent = " ";
316
-
317
- cursorFlag = document.createElement('cursor-flag');
318
- cursorFlag.setAttribute('array', 'users');
319
- cursorFlag.setAttribute('object', selection.user_id);
320
- cursorFlag.setAttribute('key', 'name');
321
- cursorFlag.style["background"] = selection.background;
322
- cursorFlag.innerHTML = selection.userName;
323
- cursor.appendChild(cursorFlag);
324
-
325
- if (setCursorInLastChild) {
326
- lastChildElement.appendChild(selectedText);
327
- lastChildElement.appendChild(cursor);
328
- } else {
329
- selection_user.appendChild(selectedText);
330
- selection_user.appendChild(cursor);
331
- }
332
-
333
- let span_end = document.createElement('span-end');
334
- let value_end = value_element.substring(end) || '';
335
- if (contenteditable)
336
- span_end.innerHTML = value_end
337
- else
338
- span_end.textContent = value_end
339
-
340
- selection_user.appendChild(span_end);
341
-
342
- mirrorDiv.appendChild(selection_user);
343
- }
344
- scrollMirror(element);
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
- const actualStyles = {};
350
-
351
- // Copy styles that are directly applied to the element (inline styles)
352
- for (const prop in element.style) {
353
- if (element.style[prop]) {
354
- actualStyles[prop] = element.style[prop];
355
- }
356
- }
357
-
358
- // Get styles from classes, IDs, attributes, and tag names
359
- for (const sheet of document.styleSheets) {
360
- try {
361
- for (const rule of sheet.cssRules) {
362
- if (element.matches(rule.selectorText)) {
363
- for (const prop of rule.style) {
364
- actualStyles[prop] = rule.style.getPropertyValue(prop);
365
- }
366
- }
367
- }
368
- } catch (e) {
369
- console.warn("Unable to access stylesheet:", e);
370
- }
371
- }
372
-
373
- return actualStyles;
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
- let mirrorDivs = [];
378
- let id_mirror = element.getAttribute('mirror_id');
379
- if (id_mirror) {
380
- let mirrorDiv = element.ownerDocument.getElementById(id_mirror);
381
- if (mirrorDiv)
382
- mirrorDivs = [mirrorDiv];
383
- } else {
384
- mirrorDivs = element.querySelectorAll('.mirror');
385
- }
386
-
387
- for (let mirrorDiv of mirrorDivs) {
388
- element = mirrorDiv.element;
389
- let cursors = mirrorDiv.querySelectorAll('cursor');
390
-
391
- cursors.forEach(function (child_cursor, index, array) {
392
- let socketId = child_cursor.getAttribute('socket_id');
393
- let userName = child_cursor.getAttribute('user-name');
394
- let background = child_cursor.getAttribute('user-background');
395
- let color = child_cursor.getAttribute('user-color');
396
- let start = child_cursor.getAttribute('data-start');
397
- let end = child_cursor.getAttribute('data-end');
398
- let selection = {
399
- element: [element],
400
- start,
401
- end,
402
- socketId,
403
- userName,
404
- background,
405
- color
406
- };
407
- drawCursors(selection);
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
- let elements = document.querySelectorAll(`cursor[socket_id="${socketId}"]`);
414
- elements.forEach(function (element, index, array) {
415
- element.parentNode.removeChild(element);
416
- });
417
-
418
- let sel_elements = document.querySelectorAll(`selection[socket_id="${socketId}"]`);
419
- sel_elements.forEach(function (sel_element, index, array) {
420
- sel_element.parentNode.removeChild(sel_element);
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
- element.addEventListener('keydown', function (event) {
426
- scrollMirror(element);
427
- });
491
+ element.addEventListener("keydown", function (event) {
492
+ scrollMirror(element);
493
+ });
428
494
 
429
- element.addEventListener('scroll', function () {
430
- scrollMirror(element);
431
- });
495
+ element.addEventListener("scroll", function () {
496
+ scrollMirror(element);
497
+ });
432
498
 
433
- initResizeObserver(element);
499
+ initResizeObserver(element);
434
500
  }
435
501
 
436
502
  function scrollMirror(element) {
437
- let id_mirror = element.getAttribute('mirror_id');
438
- let elementMirror = document.getElementById(id_mirror);
439
- if (elementMirror)
440
- elementMirror.scrollTo(element.scrollLeft, element.scrollTop);
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
- const watch = new ResizeObserver(() => updateCursors(element));
446
- watch.observe(element);
510
+ const watch = new ResizeObserver(() => updateCursors(element));
511
+ watch.observe(element);
447
512
  }
448
513
 
449
514
  function sendPosition(info) {
450
- try {
451
- socket.send({
452
- method: "cursor",
453
- data: {
454
- array: info.array,
455
- object: info.object,
456
- key: info.key,
457
- start: info.start,
458
- end: info.end,
459
- color: info.color || localStorage.getItem("cursorColor"),
460
- background: info.background || localStorage.getItem("cursorBackground") || cursorBackground,
461
- userName: info.userName || localStorage.getItem("userName") || clientId,
462
- user_id: info.user_id || localStorage.getItem("user_id") || clientId
463
- },
464
- broadcastBrowser: true
465
- });
466
- }
467
- catch (e) {
468
- console.error(e);
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.listen('cursor', function (response) {
474
- // if (socket.has(selection.socketId)) return;
475
- let selection = response.data
476
- selection.socketId = response.socketId
477
- selection.frameId = response.frameId
478
- if (selection.start != null && selection.end != null)
479
- drawCursors(selection);
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
- name: 'CoCreateCursor',
486
- observe: ['addedNodes'],
487
- selector: '[array][object][key]',
488
- callback: function (mutation) {
489
- initElement(mutation.target);
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
- name: 'CoCreateCursorAtt',
495
- observe: ['attributes'],
496
- attributeName: getAttributeNames(['array', 'object', 'key']),
497
- callback: function (mutation) {
498
- initElement(mutation.target);
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();