@cocreate/cursors 1.16.12 → 1.16.13

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
@@ -1,376 +1,376 @@
1
- /*globals ResizeObserver*/
2
- import observer from '@cocreate/observer';
3
- import crud from '@cocreate/crud-client';
4
- import message from '@cocreate/message-client';
5
- import uuid from '@cocreate/uuid';
6
- import localStorage from '@cocreate/local-storage';
7
- import { getElementPosition } from '@cocreate/selection';
8
- import {randomColor} from '@cocreate/random-color';
9
- import './index.css';
10
-
11
- const clientId = message.socket.clientId || uuid.generate(12);
12
- const cursorBackground = randomColor();
13
-
14
- let environment_prod = true;
15
-
16
- let selector = "[collection][document_id][name]:not([contentEditable='false'])";
17
-
18
- function init() {
19
- let elements = document.querySelectorAll(selector);
20
- initElements(elements);
21
- }
22
-
23
- function initElements(elements) {
24
- for (let element of elements)
25
- initElement(element);
26
- }
27
-
28
- function initElement(element) {
29
- let realtime = element.getAttribute('realtime');
30
- let cursors = element.getAttribute('cursors');
31
- if (realtime == 'false' || cursors == 'false') return;
32
- if (element.hasAttribute('actions')) return;
33
- if (element.cursor) return;
34
- element.cursor = true;
35
- }
36
-
37
- function initDocument(doc) {
38
- let documents;
39
- try {
40
- documents = window.top.cursorDocuments;
41
- } catch(e) {
42
- console.log('cross-origin failed')
43
- }
44
-
45
- if (!documents){
46
- documents = new Map();
47
- try {
48
- window.top.cursorDocuments = documents;
49
- } catch(e) {
50
- console.log('cross-origin failed')
51
- }
52
- }
53
- if (!documents.has(doc)) {
54
- initResizeObserver(doc.documentElement);
55
- documents.set(doc);
56
- }
57
- }
58
-
59
- function drawCursors(selection) {
60
- const socket_id = selection['clientId'];
61
- const collection = selection['collection'];
62
- const document_id = selection['document_id'];
63
- const name = selection['name'];
64
- let start = selection['start'];
65
- let end = selection['end'];
66
- let elements = selection.element;
67
- if (!elements) {
68
- let selector = '[collection="' + collection + '"][document_id="' + document_id + '"][name="' + name + '"]';
69
- selector += ':not(.codemirror, .monaco, [actions])';
70
- elements = document.querySelectorAll(selector);
71
- }
72
- for (let element of elements) {
73
- if (window.activeElement == element && selection.clientId == clientId) {
74
- continue;
75
- }
76
- let realtime = element.getAttribute('realtime');
77
- let cursors = element.getAttribute('cursors');
78
- let contenteditable = element.getAttribute('contenteditable');
79
- if (realtime == 'false' || cursors == 'false') continue;
80
- if (element.tagName != 'INPUT' && element.tagName != 'TEXTAREA')
81
- if (contenteditable == 'false' || contenteditable == null)
82
- if (cursors != 'true') continue;
83
-
84
- if (element.hasAttribute('contenteditable')) {
85
- let domTextEditor = element;
86
- if (element.tagName == 'IFRAME'){
87
- // let frameClientId = element.contentDocument.defaultView.CoCreateSockets.clientId;
88
- // if (frameClientId == selection.clientId) continue;
89
- domTextEditor = element.contentDocument.documentElement;
90
- }
91
- if (!domTextEditor.htmlString)
92
- continue
93
- let pos = getElementPosition(domTextEditor.htmlString, start, end);
94
- if (pos.start) {
95
- if (pos.path)
96
- element = domTextEditor.querySelector(pos.path);
97
- let endPos = end - start;
98
- if (endPos > 0)
99
- end = pos.start + endPos;
100
- else
101
- end = pos.start;
102
- start = pos.start;
103
- }
104
- }
105
-
106
- if (!element) continue;
107
- let document = element.ownerDocument;
108
- let mirrorDiv;
109
- let id_mirror = element.getAttribute('mirror_id');
110
- if (!id_mirror){
111
- id_mirror = uuid.generate(8);
112
- element.setAttribute('mirror_id', id_mirror);
113
-
114
- mirrorDiv = document.createElement('cursor-container');
115
- mirrorDiv.id = id_mirror;
116
- mirrorDiv.className = (environment_prod) ? 'mirror mirror_scroll mirror_color' : 'mirror mirror_scroll';
117
- mirrorDiv.contentEditable = false;
118
- mirrorDiv.element = element;
119
- element.insertAdjacentElement('afterend', mirrorDiv);
120
- // let parent = element.parentElement;
121
- // let parentComputed = getComputedStyle(parent);
122
- // let parentStylePosition = parentComputed['position'];
123
- // if (parentStylePosition == 'static')
124
- // parent.style.position = 'relative';
125
- // let parentStyleDisplay = parentComputed['display'];
126
- // if (parentStyleDisplay == 'inline')
127
- // parent.style.display = 'inline';
128
-
129
- _initEvents(element);
130
- initDocument(document);
131
- }
132
- else
133
- mirrorDiv = document.getElementById(id_mirror);
134
-
135
- let computed = getComputedStyle(element);
136
- let rect = element.getBoundingClientRect();
137
-
138
- let borderWidth = parseInt(computed['border-width']) * 2;
139
- let scrollBarWidth = 0;
140
- if (element.clientWidth != 0)
141
- scrollBarWidth = element.offsetWidth - element.clientWidth - borderWidth;
142
-
143
- let style = mirrorDiv.style;
144
- style.position = 'absolute';
145
- style.width = rect.width + 'px';
146
- style.height = rect.height + 'px';
147
- style.overflowX = computed['overflowX'];
148
- style.overflowY = computed['overflowY'];
149
- style.margin = '0px';
150
- style.padding = computed['padding'];
151
- style.border = computed['border'];
152
- style.borderColor = 'transparent';
153
- style.outline = computed['outline'];
154
- style.outlineColor = 'transparent';
155
- style.boxSizing = computed['boxSizing'];
156
- style.lineHeight = computed['lineHeight'];
157
-
158
- if (element.parentElement.style['display'] == 'inline') {
159
- style.top = element.clientTop + 'px';
160
- style.left = element.clientLeft + 'px';
161
- } else {
162
- style.top = element.offsetTop + 'px';
163
- style.left = element.offsetLeft + 'px';
164
- }
165
-
166
- if (element.nodeName !== 'INPUT') {
167
- style.wordWrap = 'break-word';
168
- style.whiteSpace = 'pre-wrap';
169
- } else {
170
- style.whiteSpace = 'pre';
171
- }
172
-
173
- let properties = ['fontStyle', 'fontVariant', 'fontWeight', 'fontStretch', 'fontSize', 'fontFamily', 'letterSpacing', 'lineHeight', 'textAlign', 'textTransform', 'textIndent', 'textDecoration', 'textRendering', 'textTransform', 'textIndent', 'overflowWrap', 'tabSize', 'webkitWritingMode', 'wordSpacing'];
174
-
175
- properties.forEach(function(prop) {
176
- if (['left', 'top'].indexOf(prop.toLowerCase()) === -1)
177
- style[prop] = computed[prop];
178
- });
179
-
180
- let spanText = mirrorDiv.querySelector('span-text');
181
- if (!spanText)
182
- spanText = document.createElement('span-text');
183
- spanText.style['display'] = 'block';
184
- let value_element = (['TEXTAREA', 'INPUT'].indexOf(element.nodeName) == -1) ? element.innerHTML : element.value;
185
-
186
- spanText.textContent = value_element;
187
- if (element.nodeName === 'INPUT')
188
- spanText.textContent = mirrorDiv.textContent.replace(/\s/g, " ");
189
- mirrorDiv.appendChild(spanText);
190
- let cursor, cursorFlag;
191
- let details = {collection, document_id, name};
192
- if (socket_id) {
193
- let userSelection = mirrorDiv.querySelector(`selection[socket_id="${socket_id}"]`);
194
- if (userSelection && userSelection.details != details)
195
- userSelection.remove();
196
- let selection_user = mirrorDiv.querySelector(`selection[socket_id="${socket_id}"]`);
197
- if (!selection_user){
198
- selection_user = document.createElement('selection');
199
- selection_user.setAttribute('socket_id', socket_id);
200
- selection_user.style["position"] = "absolute";
201
- let style_mirror = getComputedStyle(mirrorDiv);
202
- selection_user.style["top"] = style_mirror.paddingTop;
203
- selection_user.style["left"] = style_mirror.paddingLeft;
204
- selection_user.style["width"] = mirrorDiv.clientWidth - parseInt(computed['padding-left']) - parseInt(computed['padding-right']) - scrollBarWidth + 'px';
205
- selection_user.details = {collection, document_id, name};
206
-
207
- mirrorDiv.appendChild(selection_user);
208
- }
209
-
210
- let value_element = (['TEXTAREA', 'INPUT'].indexOf(element.nodeName) == -1) ? element.innerHTML : element.value;
211
- selection_user.textContent = value_element.substring(0, start);
212
-
213
- let value_end = value_element.substring(end) || '';
214
- let span_end = document.createElement('span-end');
215
- span_end.textContent = value_end;
216
-
217
- cursor = document.createElement('cursor');
218
- cursor.setAttribute('socket_id', socket_id);
219
- cursor.dataset.start = start;
220
- cursor.dataset.end = end;
221
- cursor.setAttribute('user-name', selection.userName);
222
- cursor.setAttribute('user-background', selection.background);
223
- cursor.setAttribute('user-color', selection.color);
224
- cursor.style["background"] = selection.background;
225
- cursor.details = {collection, document_id, name};
226
- cursor.textContent = "1";
227
- span_end.prepend(cursor);
228
-
229
- let selectedText = document.createElement('selected-text');
230
- selectedText.style["backgroundColor"] = selection.background;
231
- selectedText.textContent = value_element.substring(start, end) || '';
232
- span_end.prepend(selectedText);
233
-
234
- cursorFlag = document.createElement('cursor-flag');
235
- cursorFlag.setAttribute('collection', 'users');
236
- cursorFlag.setAttribute('document_id', selection.user_id);
237
- cursorFlag.setAttribute('name', 'name');
238
- cursorFlag.style["background"] = selection.background;
239
- cursorFlag.innerHTML = selection.userName;
240
- cursor.appendChild(cursorFlag);
241
-
242
- selection_user.appendChild(span_end);
243
- }
244
- scrollMirror(element);
245
- }
246
- }
247
-
248
- function updateCursors(element) {
249
- let mirrorDivs = [];
250
- let id_mirror = element.getAttribute('mirror_id');
251
- if (id_mirror) {
252
- let mirrorDiv = element.ownerDocument.getElementById(id_mirror);
253
- if (mirrorDiv)
254
- mirrorDivs = [mirrorDiv];
255
- }
256
- else {
257
- mirrorDivs = element.querySelectorAll('.mirror');
258
- }
259
- for (let mirrorDiv of mirrorDivs) {
260
- element = mirrorDiv.element;
261
- let cursors = mirrorDiv.querySelectorAll('cursor');
262
-
263
- cursors.forEach(function(child_cursor, index, array) {
264
- let clientId = child_cursor.getAttribute('socket_id');
265
- let userName = child_cursor.getAttribute('user-name');
266
- let background = child_cursor.getAttribute('user-background');
267
- let color = child_cursor.getAttribute('user-color');
268
- let start = child_cursor.getAttribute('data-start');
269
- let end = child_cursor.getAttribute('data-end');
270
- let selection = {
271
- element: [element],
272
- start,
273
- end,
274
- clientId,
275
- userName,
276
- background,
277
- color
278
- };
279
- drawCursors(selection);
280
- });
281
- }
282
- }
283
-
284
- function removeCursor(clientId) {
285
- let elements = document.querySelectorAll(`cursor[socket_id="${clientId}"]`);
286
- elements.forEach(function(element, index, array) {
287
- element.parentNode.removeChild(element);
288
- });
289
-
290
- let sel_elements = document.querySelectorAll(`selection[socket_id="${clientId}"]`);
291
- sel_elements.forEach(function(sel_element, index, array) {
292
- sel_element.parentNode.removeChild(sel_element);
293
- });
294
- }
295
-
296
- function _initEvents(element) {
297
- element.addEventListener('keydown', function(event) {
298
- scrollMirror(element);
299
- });
300
-
301
- element.addEventListener('scroll', function() {
302
- scrollMirror(element);
303
- });
304
-
305
- initResizeObserver(element);
306
- }
307
-
308
- function scrollMirror(element) {
309
- let id_mirror = element.getAttribute('mirror_id');
310
- let elementMirror = document.getElementById(id_mirror);
311
- if (elementMirror)
312
- elementMirror.scrollTo(element.scrollLeft, element.scrollTop);
313
- }
314
-
315
-
316
- function initResizeObserver(element) {
317
- const watch = new ResizeObserver(() => updateCursors(element));
318
- watch.observe(element);
319
- }
320
-
321
- function sendPosition(info) {
322
- try {
323
- message.send({
324
- room: "",
325
- message: "cursor",
326
- data: {
327
- collection: info.collection,
328
- document_id: info.document_id,
329
- name: info.name,
330
- start: info.start,
331
- end: info.end,
332
- clientId: clientId,
333
- color: info.color || localStorage.getItem("cursorColor"),
334
- background: info.background || localStorage.getItem("cursorBackground") || cursorBackground,
335
- userName: info.userName || localStorage.getItem("userName") || clientId,
336
- user_id: info.user_id || localStorage.getItem("user_id") || clientId
337
- },
338
- broadcastBrowser: 'once'
339
- });
340
- }
341
- catch (e) {
342
- console.error(e);
343
- }
344
- }
345
-
346
-
347
- message.listen('cursor', function(response) {
348
- // if (selection.clientId == clientId) return;
349
- let selection = response.data
350
- if (selection.start != null && selection.end != null)
351
- drawCursors(selection);
352
- else
353
- removeCursor(selection.clientId);
354
- });
355
-
356
- observer.init({
357
- name: 'CoCreateCursor',
358
- observe: ['addedNodes'],
359
- target: '[collection][document_id][name]',
360
- callback: function(mutation) {
361
- initElement(mutation.target);
362
- }
363
- });
364
-
365
- observer.init({
366
- name: 'CoCreateCursorAtt',
367
- observe: ['attributes'],
368
- attributeName: crud.getAttributeNames(['collection', 'document_id', 'name']),
369
- callback: function(mutation) {
370
- initElement(mutation.target);
371
- }
372
- });
373
-
374
- init();
375
-
376
- export default { drawCursors, updateCursors, removeCursor, sendPosition };
1
+ /*globals ResizeObserver*/
2
+ import observer from '@cocreate/observer';
3
+ import crud from '@cocreate/crud-client';
4
+ import message from '@cocreate/message-client';
5
+ import uuid from '@cocreate/uuid';
6
+ import localStorage from '@cocreate/local-storage';
7
+ import { getElementPosition } from '@cocreate/selection';
8
+ import {randomColor} from '@cocreate/random-color';
9
+ import './index.css';
10
+
11
+ const clientId = message.socket.clientId || uuid.generate(12);
12
+ const cursorBackground = randomColor();
13
+
14
+ let environment_prod = true;
15
+
16
+ let selector = "[collection][document_id][name]:not([contentEditable='false'])";
17
+
18
+ function init() {
19
+ let elements = document.querySelectorAll(selector);
20
+ initElements(elements);
21
+ }
22
+
23
+ function initElements(elements) {
24
+ for (let element of elements)
25
+ initElement(element);
26
+ }
27
+
28
+ function initElement(element) {
29
+ let realtime = element.getAttribute('realtime');
30
+ let cursors = element.getAttribute('cursors');
31
+ if (realtime == 'false' || cursors == 'false') return;
32
+ if (element.hasAttribute('actions')) return;
33
+ if (element.cursor) return;
34
+ element.cursor = true;
35
+ }
36
+
37
+ function initDocument(doc) {
38
+ let documents;
39
+ try {
40
+ documents = window.top.cursorDocuments;
41
+ } catch(e) {
42
+ console.log('cross-origin failed')
43
+ }
44
+
45
+ if (!documents){
46
+ documents = new Map();
47
+ try {
48
+ window.top.cursorDocuments = documents;
49
+ } catch(e) {
50
+ console.log('cross-origin failed')
51
+ }
52
+ }
53
+ if (!documents.has(doc)) {
54
+ initResizeObserver(doc.documentElement);
55
+ documents.set(doc);
56
+ }
57
+ }
58
+
59
+ function drawCursors(selection) {
60
+ const socket_id = selection['clientId'];
61
+ const collection = selection['collection'];
62
+ const document_id = selection['document_id'];
63
+ const name = selection['name'];
64
+ let start = selection['start'];
65
+ let end = selection['end'];
66
+ let elements = selection.element;
67
+ if (!elements) {
68
+ let selector = '[collection="' + collection + '"][document_id="' + document_id + '"][name="' + name + '"]';
69
+ selector += ':not(.codemirror, .monaco, [actions])';
70
+ elements = document.querySelectorAll(selector);
71
+ }
72
+ for (let element of elements) {
73
+ if (window.activeElement == element && selection.clientId == clientId) {
74
+ continue;
75
+ }
76
+ let realtime = element.getAttribute('realtime');
77
+ let cursors = element.getAttribute('cursors');
78
+ let contenteditable = element.getAttribute('contenteditable');
79
+ if (realtime == 'false' || cursors == 'false') continue;
80
+ if (element.tagName != 'INPUT' && element.tagName != 'TEXTAREA')
81
+ if (contenteditable == 'false' || contenteditable == null)
82
+ if (cursors != 'true') continue;
83
+
84
+ if (element.hasAttribute('contenteditable')) {
85
+ let domTextEditor = element;
86
+ if (element.tagName == 'IFRAME'){
87
+ // let frameClientId = element.contentDocument.defaultView.CoCreateSockets.clientId;
88
+ // if (frameClientId == selection.clientId) continue;
89
+ domTextEditor = element.contentDocument.documentElement;
90
+ }
91
+ if (!domTextEditor.htmlString)
92
+ continue
93
+ let pos = getElementPosition(domTextEditor.htmlString, start, end);
94
+ if (pos.start) {
95
+ if (pos.path)
96
+ element = domTextEditor.querySelector(pos.path);
97
+ let endPos = end - start;
98
+ if (endPos > 0)
99
+ end = pos.start + endPos;
100
+ else
101
+ end = pos.start;
102
+ start = pos.start;
103
+ }
104
+ }
105
+
106
+ if (!element) continue;
107
+ let document = element.ownerDocument;
108
+ let mirrorDiv;
109
+ let id_mirror = element.getAttribute('mirror_id');
110
+ if (!id_mirror){
111
+ id_mirror = uuid.generate(8);
112
+ element.setAttribute('mirror_id', id_mirror);
113
+
114
+ mirrorDiv = document.createElement('cursor-container');
115
+ mirrorDiv.id = id_mirror;
116
+ mirrorDiv.className = (environment_prod) ? 'mirror mirror_scroll mirror_color' : 'mirror mirror_scroll';
117
+ mirrorDiv.contentEditable = false;
118
+ mirrorDiv.element = element;
119
+ element.insertAdjacentElement('afterend', mirrorDiv);
120
+ // let parent = element.parentElement;
121
+ // let parentComputed = getComputedStyle(parent);
122
+ // let parentStylePosition = parentComputed['position'];
123
+ // if (parentStylePosition == 'static')
124
+ // parent.style.position = 'relative';
125
+ // let parentStyleDisplay = parentComputed['display'];
126
+ // if (parentStyleDisplay == 'inline')
127
+ // parent.style.display = 'inline';
128
+
129
+ _initEvents(element);
130
+ initDocument(document);
131
+ }
132
+ else
133
+ mirrorDiv = document.getElementById(id_mirror);
134
+
135
+ let computed = getComputedStyle(element);
136
+ let rect = element.getBoundingClientRect();
137
+
138
+ let borderWidth = parseInt(computed['border-width']) * 2;
139
+ let scrollBarWidth = 0;
140
+ if (element.clientWidth != 0)
141
+ scrollBarWidth = element.offsetWidth - element.clientWidth - borderWidth;
142
+
143
+ let style = mirrorDiv.style;
144
+ style.position = 'absolute';
145
+ style.width = rect.width + 'px';
146
+ style.height = rect.height + 'px';
147
+ style.overflowX = computed['overflowX'];
148
+ style.overflowY = computed['overflowY'];
149
+ style.margin = '0px';
150
+ style.padding = computed['padding'];
151
+ style.border = computed['border'];
152
+ style.borderColor = 'transparent';
153
+ style.outline = computed['outline'];
154
+ style.outlineColor = 'transparent';
155
+ style.boxSizing = computed['boxSizing'];
156
+ style.lineHeight = computed['lineHeight'];
157
+
158
+ if (element.parentElement.style['display'] == 'inline') {
159
+ style.top = element.clientTop + 'px';
160
+ style.left = element.clientLeft + 'px';
161
+ } else {
162
+ style.top = element.offsetTop + 'px';
163
+ style.left = element.offsetLeft + 'px';
164
+ }
165
+
166
+ if (element.nodeName !== 'INPUT') {
167
+ style.wordWrap = 'break-word';
168
+ style.whiteSpace = 'pre-wrap';
169
+ } else {
170
+ style.whiteSpace = 'pre';
171
+ }
172
+
173
+ let properties = ['fontStyle', 'fontVariant', 'fontWeight', 'fontStretch', 'fontSize', 'fontFamily', 'letterSpacing', 'lineHeight', 'textAlign', 'textTransform', 'textIndent', 'textDecoration', 'textRendering', 'textTransform', 'textIndent', 'overflowWrap', 'tabSize', 'webkitWritingMode', 'wordSpacing'];
174
+
175
+ properties.forEach(function(prop) {
176
+ if (['left', 'top'].indexOf(prop.toLowerCase()) === -1)
177
+ style[prop] = computed[prop];
178
+ });
179
+
180
+ let spanText = mirrorDiv.querySelector('span-text');
181
+ if (!spanText)
182
+ spanText = document.createElement('span-text');
183
+ spanText.style['display'] = 'block';
184
+ let value_element = (['TEXTAREA', 'INPUT'].indexOf(element.nodeName) == -1) ? element.innerHTML : element.value;
185
+
186
+ spanText.textContent = value_element;
187
+ if (element.nodeName === 'INPUT')
188
+ spanText.textContent = mirrorDiv.textContent.replace(/\s/g, " ");
189
+ mirrorDiv.appendChild(spanText);
190
+ let cursor, cursorFlag;
191
+ let details = {collection, document_id, name};
192
+ if (socket_id) {
193
+ let userSelection = mirrorDiv.querySelector(`selection[socket_id="${socket_id}"]`);
194
+ if (userSelection && userSelection.details != details)
195
+ userSelection.remove();
196
+ let selection_user = mirrorDiv.querySelector(`selection[socket_id="${socket_id}"]`);
197
+ if (!selection_user){
198
+ selection_user = document.createElement('selection');
199
+ selection_user.setAttribute('socket_id', socket_id);
200
+ selection_user.style["position"] = "absolute";
201
+ let style_mirror = getComputedStyle(mirrorDiv);
202
+ selection_user.style["top"] = style_mirror.paddingTop;
203
+ selection_user.style["left"] = style_mirror.paddingLeft;
204
+ selection_user.style["width"] = mirrorDiv.clientWidth - parseInt(computed['padding-left']) - parseInt(computed['padding-right']) - scrollBarWidth + 'px';
205
+ selection_user.details = {collection, document_id, name};
206
+
207
+ mirrorDiv.appendChild(selection_user);
208
+ }
209
+
210
+ let value_element = (['TEXTAREA', 'INPUT'].indexOf(element.nodeName) == -1) ? element.innerHTML : element.value;
211
+ selection_user.textContent = value_element.substring(0, start);
212
+
213
+ let value_end = value_element.substring(end) || '';
214
+ let span_end = document.createElement('span-end');
215
+ span_end.textContent = value_end;
216
+
217
+ cursor = document.createElement('cursor');
218
+ cursor.setAttribute('socket_id', socket_id);
219
+ cursor.dataset.start = start;
220
+ cursor.dataset.end = end;
221
+ cursor.setAttribute('user-name', selection.userName);
222
+ cursor.setAttribute('user-background', selection.background);
223
+ cursor.setAttribute('user-color', selection.color);
224
+ cursor.style["background"] = selection.background;
225
+ cursor.details = {collection, document_id, name};
226
+ cursor.textContent = "1";
227
+ span_end.prepend(cursor);
228
+
229
+ let selectedText = document.createElement('selected-text');
230
+ selectedText.style["backgroundColor"] = selection.background;
231
+ selectedText.textContent = value_element.substring(start, end) || '';
232
+ span_end.prepend(selectedText);
233
+
234
+ cursorFlag = document.createElement('cursor-flag');
235
+ cursorFlag.setAttribute('collection', 'users');
236
+ cursorFlag.setAttribute('document_id', selection.user_id);
237
+ cursorFlag.setAttribute('name', 'name');
238
+ cursorFlag.style["background"] = selection.background;
239
+ cursorFlag.innerHTML = selection.userName;
240
+ cursor.appendChild(cursorFlag);
241
+
242
+ selection_user.appendChild(span_end);
243
+ }
244
+ scrollMirror(element);
245
+ }
246
+ }
247
+
248
+ function updateCursors(element) {
249
+ let mirrorDivs = [];
250
+ let id_mirror = element.getAttribute('mirror_id');
251
+ if (id_mirror) {
252
+ let mirrorDiv = element.ownerDocument.getElementById(id_mirror);
253
+ if (mirrorDiv)
254
+ mirrorDivs = [mirrorDiv];
255
+ }
256
+ else {
257
+ mirrorDivs = element.querySelectorAll('.mirror');
258
+ }
259
+ for (let mirrorDiv of mirrorDivs) {
260
+ element = mirrorDiv.element;
261
+ let cursors = mirrorDiv.querySelectorAll('cursor');
262
+
263
+ cursors.forEach(function(child_cursor, index, array) {
264
+ let clientId = child_cursor.getAttribute('socket_id');
265
+ let userName = child_cursor.getAttribute('user-name');
266
+ let background = child_cursor.getAttribute('user-background');
267
+ let color = child_cursor.getAttribute('user-color');
268
+ let start = child_cursor.getAttribute('data-start');
269
+ let end = child_cursor.getAttribute('data-end');
270
+ let selection = {
271
+ element: [element],
272
+ start,
273
+ end,
274
+ clientId,
275
+ userName,
276
+ background,
277
+ color
278
+ };
279
+ drawCursors(selection);
280
+ });
281
+ }
282
+ }
283
+
284
+ function removeCursor(clientId) {
285
+ let elements = document.querySelectorAll(`cursor[socket_id="${clientId}"]`);
286
+ elements.forEach(function(element, index, array) {
287
+ element.parentNode.removeChild(element);
288
+ });
289
+
290
+ let sel_elements = document.querySelectorAll(`selection[socket_id="${clientId}"]`);
291
+ sel_elements.forEach(function(sel_element, index, array) {
292
+ sel_element.parentNode.removeChild(sel_element);
293
+ });
294
+ }
295
+
296
+ function _initEvents(element) {
297
+ element.addEventListener('keydown', function(event) {
298
+ scrollMirror(element);
299
+ });
300
+
301
+ element.addEventListener('scroll', function() {
302
+ scrollMirror(element);
303
+ });
304
+
305
+ initResizeObserver(element);
306
+ }
307
+
308
+ function scrollMirror(element) {
309
+ let id_mirror = element.getAttribute('mirror_id');
310
+ let elementMirror = document.getElementById(id_mirror);
311
+ if (elementMirror)
312
+ elementMirror.scrollTo(element.scrollLeft, element.scrollTop);
313
+ }
314
+
315
+
316
+ function initResizeObserver(element) {
317
+ const watch = new ResizeObserver(() => updateCursors(element));
318
+ watch.observe(element);
319
+ }
320
+
321
+ function sendPosition(info) {
322
+ try {
323
+ message.send({
324
+ room: "",
325
+ message: "cursor",
326
+ data: {
327
+ collection: info.collection,
328
+ document_id: info.document_id,
329
+ name: info.name,
330
+ start: info.start,
331
+ end: info.end,
332
+ clientId: clientId,
333
+ color: info.color || localStorage.getItem("cursorColor"),
334
+ background: info.background || localStorage.getItem("cursorBackground") || cursorBackground,
335
+ userName: info.userName || localStorage.getItem("userName") || clientId,
336
+ user_id: info.user_id || localStorage.getItem("user_id") || clientId
337
+ },
338
+ broadcastBrowser: 'once'
339
+ });
340
+ }
341
+ catch (e) {
342
+ console.error(e);
343
+ }
344
+ }
345
+
346
+
347
+ message.listen('cursor', function(response) {
348
+ // if (selection.clientId == clientId) return;
349
+ let selection = response.data
350
+ if (selection.start != null && selection.end != null)
351
+ drawCursors(selection);
352
+ else
353
+ removeCursor(selection.clientId);
354
+ });
355
+
356
+ observer.init({
357
+ name: 'CoCreateCursor',
358
+ observe: ['addedNodes'],
359
+ target: '[collection][document_id][name]',
360
+ callback: function(mutation) {
361
+ initElement(mutation.target);
362
+ }
363
+ });
364
+
365
+ observer.init({
366
+ name: 'CoCreateCursorAtt',
367
+ observe: ['attributes'],
368
+ attributeName: crud.getAttributeNames(['collection', 'document_id', 'name']),
369
+ callback: function(mutation) {
370
+ initElement(mutation.target);
371
+ }
372
+ });
373
+
374
+ init();
375
+
376
+ export default { drawCursors, updateCursors, removeCursor, sendPosition };