@cocreate/cursors 1.10.18 → 1.11.0

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 CHANGED
@@ -1,3 +1,17 @@
1
+ # [1.11.0](https://github.com/CoCreate-app/CoCreate-cursors/compare/v1.10.18...v1.11.0) (2021-12-14)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * add style top to cursor ([1e6d26c](https://github.com/CoCreate-app/CoCreate-cursors/commit/1e6d26c31919700399b3473a938f49f3cc2978b0))
7
+ * update dependencies ([9895f14](https://github.com/CoCreate-app/CoCreate-cursors/commit/9895f14bf1de1c133f0477134ccb654fb31f79d0))
8
+ * update dependencies ([2307656](https://github.com/CoCreate-app/CoCreate-cursors/commit/2307656877e1936cb8b5d978414df898a1de0b27))
9
+
10
+
11
+ ### Features
12
+
13
+ * multi cursor support between many elements in window.top ([3b2dab9](https://github.com/CoCreate-app/CoCreate-cursors/commit/3b2dab9e1a6886026b7976eed44180d89a70ad83))
14
+
1
15
  ## [1.10.18](https://github.com/CoCreate-app/CoCreate-cursors/compare/v1.10.17...v1.10.18) (2021-12-08)
2
16
 
3
17
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cocreate/cursors",
3
- "version": "1.10.18",
3
+ "version": "1.11.0",
4
4
  "description": "Collaborative user cursor position for inputs, textarea's and contenteditable elements.",
5
5
  "keywords": [
6
6
  "cursors",
@@ -62,12 +62,12 @@
62
62
  "webpack-log": "^3.0.1"
63
63
  },
64
64
  "dependencies": {
65
- "@cocreate/crud-client": "^1.4.38",
66
- "@cocreate/docs": "^1.2.60",
67
- "@cocreate/message-client": "^1.1.56",
68
- "@cocreate/observer": "^1.3.52",
69
- "@cocreate/random-color": "^1.2.13",
70
- "@cocreate/selection": "^1.2.19",
71
- "@cocreate/uuid": "^1.1.49"
65
+ "@cocreate/crud-client": "^1.4.39",
66
+ "@cocreate/docs": "^1.2.61",
67
+ "@cocreate/message-client": "^1.1.57",
68
+ "@cocreate/observer": "^1.3.53",
69
+ "@cocreate/random-color": "^1.2.14",
70
+ "@cocreate/selection": "^1.3.0",
71
+ "@cocreate/uuid": "^1.1.50"
72
72
  }
73
73
  }
package/src/index.css CHANGED
@@ -1,4 +1,4 @@
1
- .cursor-container {
1
+ cursor {
2
2
  position: absolute;
3
3
  display: inline-block;
4
4
  width: 2px;
@@ -6,7 +6,7 @@
6
6
  /*z-index:10;*/
7
7
  }
8
8
 
9
- .cursor-flag {
9
+ cursor-flag {
10
10
  position: absolute;
11
11
  background-color: inherit;
12
12
  top: 0px;
@@ -18,7 +18,7 @@
18
18
  pointer-events: auto;
19
19
  }
20
20
 
21
- .cursor-flag:hover {
21
+ cursor-flag:hover {
22
22
  height: 14px;
23
23
  width: unset;
24
24
  padding: 1px 5px 0px 5px;
@@ -27,8 +27,7 @@
27
27
  white-space: nowrap;
28
28
  }
29
29
 
30
- .cursor-selection {
31
- position: absolute;
30
+ selected-text {
32
31
  opacity: 0.4;
33
32
  }
34
33
 
package/src/index.js CHANGED
@@ -10,7 +10,6 @@ const clientId = window.CoCreateSockets.clientId || uuid.generate(12);
10
10
  const cursorBackground = randomColor();
11
11
 
12
12
  let enviroment_prod = true;
13
- let documents = new Map();
14
13
 
15
14
  let selector = "[collection][document_id][name]:not([contentEditable='false'])";
16
15
 
@@ -34,120 +33,17 @@ function initElement(element) {
34
33
  }
35
34
 
36
35
  function initDocument(doc) {
37
- if (!documents.has(doc.window)) {
36
+ let documents = window.top.cursorDocuments;
37
+ if (!documents){
38
+ documents = new Map();
39
+ window.top.cursorDocuments = documents;
40
+ }
41
+ if (!documents.has(doc)) {
38
42
  initResizeObserver(doc.documentElement);
39
43
  documents.set(doc);
40
44
  }
41
45
  }
42
46
 
43
- var getCoordinates = function(element, position_start, position_end) {
44
- var mirrorDiv;
45
- let ID_MIRROR = element.getAttribute('mirror_id');
46
- let document = element.ownerDocument;
47
- mirrorDiv = document.getElementById(ID_MIRROR);
48
-
49
- if (!mirrorDiv) {
50
- mirrorDiv = document.createElement('div');
51
- mirrorDiv.id = ID_MIRROR;
52
- mirrorDiv.className = (enviroment_prod) ? 'mirror mirror_scroll mirror_color' : 'mirror mirror_scroll';
53
- mirrorDiv.contentEditable = false;
54
- mirrorDiv.element = element;
55
- element.insertAdjacentElement('afterend', mirrorDiv);
56
- _initEvents(element);
57
- initDocument(document);
58
- }
59
-
60
- let computed = getComputedStyle(element);
61
- let rect = element.getBoundingClientRect();
62
- let scrollBarWidth = 0;
63
- if (element.clientWidth != 0)
64
- scrollBarWidth = element.offsetWidth - element.clientWidth;
65
- let style = mirrorDiv.style;
66
-
67
- style.position = 'absolute';
68
- style.top = element.offsetTop + 'px';
69
- style.left = element.offsetLeft + 'px';
70
- style.width = rect.width + 'px';
71
- style.height = rect.height + 'px';
72
- style.visibility = 'visible';
73
- style.overflowX = computed['overflowX'];
74
- style.overflowY = computed['overflowY'];
75
- style.margin = '0px';
76
- style.paddingRight = parseInt(computed['paddingRight']) + scrollBarWidth + 'px';
77
- style.border = computed['border'];
78
- style.borderColor = 'transparent';
79
-
80
- if (element.nodeName !== 'INPUT') {
81
- style.wordWrap = 'break-word';
82
- style.whiteSpace = 'pre-wrap';
83
- }
84
- else {
85
- style.whiteSpace = 'pre';
86
- }
87
-
88
- let properties = ['boxSizing', 'borderTopWidth', 'borderRightWidth', 'borderBottomWidth', 'borderLeftWidth', 'paddingTop', 'paddingBottom', 'paddingLeft', 'fontStyle', 'fontVariant', 'fontWeight', 'fontStretch', 'fontSize', 'lineHeight', 'fontFamily', 'textAlign', 'textTransform', 'textIndent', 'textDecoration', 'letterSpacing', 'wordSpacing', 'textRendering', 'webkitWritingMode', 'textTransform', 'textIndent', 'overflowWrap'];
89
-
90
- properties.forEach(function(prop) {
91
- if (['left', 'top'].indexOf(prop.toLowerCase()) === -1)
92
- style[prop] = computed[prop];
93
- });
94
-
95
-
96
- let cursor_container = mirrorDiv.querySelectorAll('.cursor-container');
97
- let users_selections = mirrorDiv.querySelectorAll('.cursor-selection');
98
-
99
- let value_element = (['TEXTAREA', 'INPUT'].indexOf(element.nodeName) == -1) ? element.innerHTML : element.value;
100
- mirrorDiv.textContent = value_element.substring(0, position_start);
101
-
102
- if (element.nodeName === 'INPUT')
103
- mirrorDiv.textContent = mirrorDiv.textContent.replace(/\s/g, " ");
104
-
105
-
106
- var span = document.createElement('span');
107
- span.id = element.nodeName + 'span_selections';
108
- let value_span = value_element.substring(position_start, position_end) || '';
109
- span.textContent = value_span;
110
- mirrorDiv.appendChild(span);
111
-
112
- if (cursor_container) {
113
- cursor_container.forEach(function(child_cursor, index, array) {
114
- mirrorDiv.appendChild(child_cursor);
115
- });
116
- }
117
-
118
- if (users_selections) {
119
- users_selections.forEach(function(child_selection, index, array) {
120
- mirrorDiv.appendChild(child_selection);
121
- });
122
- }
123
-
124
- // create mirror text end
125
- let value_end = value_element.substring(position_end) || '';
126
- var span_end = document.createElement('span');
127
- mirrorDiv.appendChild(span_end);
128
- span_end.textContent = value_end;
129
-
130
-
131
- var coordinates = {
132
- start: {
133
- top: span.offsetTop,
134
- left: span.offsetLeft
135
- },
136
- end: {
137
- top: span_end.offsetTop,
138
- left: span_end.offsetLeft
139
- }
140
- };
141
-
142
- return coordinates;
143
- };
144
-
145
- function getStyle(el, styleProp) {
146
- if (window.getComputedStyle)
147
- var y = document.defaultView.getComputedStyle(el, null).getPropertyValue(styleProp);
148
- return y;
149
- }
150
-
151
47
  function drawCursors(selection) {
152
48
  const socket_id = selection['clientId'];
153
49
  const collection = selection['collection'];
@@ -162,13 +58,20 @@ function drawCursors(selection) {
162
58
  elements = document.querySelectorAll(selector);
163
59
  }
164
60
  for (let element of elements) {
61
+ if (element.activeElement) {
62
+ element.activeElement = '';
63
+ continue;
64
+ }
165
65
  let realtime = element.getAttribute('realtime');
166
66
  let cursors = element.getAttribute('cursors');
167
67
  if (realtime == 'false' || cursors == 'false') continue;
168
68
  if (element.hasAttribute('contenteditable')) {
169
69
  let domTextEditor = element;
170
- if (element.tagName == 'IFRAME')
70
+ if (element.tagName == 'IFRAME'){
71
+ let frameClientId = element.contentDocument.defaultView.CoCreateSockets.clientId;
72
+ if (frameClientId == selection.clientId) continue;
171
73
  domTextEditor = element.contentDocument.documentElement;
74
+ }
172
75
  let pos = getElementPosition(domTextEditor.htmlString, start, end);
173
76
  element = domTextEditor.querySelector(pos.path);
174
77
  let endPos = end - start;
@@ -178,87 +81,129 @@ function drawCursors(selection) {
178
81
  end = pos.start;
179
82
  start = pos.start;
180
83
  }
181
- if(!element) return;
84
+ if(!element) continue;
182
85
  let document = element.ownerDocument;
86
+ let mirrorDiv;
183
87
  let id_mirror = element.getAttribute('mirror_id');
184
- if (!id_mirror)
185
- element.setAttribute('mirror_id', uuid.generate(8));
186
- id_mirror = element.getAttribute('mirror_id');
187
- let coordinates = getCoordinates(element, start, end);
188
- if (!coordinates) return false;
189
-
190
- let elementMirror = document.getElementById(id_mirror);
191
- let cursor;
88
+ if (!id_mirror){
89
+ id_mirror = uuid.generate(8);
90
+ element.setAttribute('mirror_id', id_mirror);
91
+
92
+ mirrorDiv = document.createElement('cursor-container');
93
+ mirrorDiv.id = id_mirror;
94
+ mirrorDiv.className = (enviroment_prod) ? 'mirror mirror_scroll mirror_color' : 'mirror mirror_scroll';
95
+ mirrorDiv.contentEditable = false;
96
+ mirrorDiv.element = element;
97
+ element.insertAdjacentElement('afterend', mirrorDiv);
98
+ _initEvents(element);
99
+ initDocument(document);
100
+ }
101
+ else
102
+ mirrorDiv = document.getElementById(id_mirror);
103
+
104
+ let computed = getComputedStyle(element);
105
+ let rect = element.getBoundingClientRect();
106
+ let borderWidth = parseInt(computed['border-width']) * 2;
107
+ let scrollBarWidth = 0;
108
+ if (element.clientWidth != 0)
109
+ scrollBarWidth = element.offsetWidth - element.clientWidth - borderWidth;
110
+ let style = mirrorDiv.style;
111
+
112
+ style.position = 'absolute';
113
+ style.top = element.offsetTop + 'px';
114
+ style.left = element.offsetLeft + 'px';
115
+ style.width = rect.width + 'px';
116
+ style.height = rect.height + 'px';
117
+ style.visibility = 'visible';
118
+ style.overflowX = computed['overflowX'];
119
+ style.overflowY = computed['overflowY'];
120
+ style.margin = '0px';
121
+ style.paddingRight = parseInt(computed['paddingRight']) + scrollBarWidth + 'px';
122
+ style.border = computed['border'];
123
+ style.borderColor = 'transparent';
124
+
125
+ if (element.nodeName !== 'INPUT') {
126
+ style.wordWrap = 'break-word';
127
+ style.whiteSpace = 'pre-wrap';
128
+ }
129
+ else {
130
+ style.whiteSpace = 'pre';
131
+ }
132
+
133
+ let properties = ['boxSizing', 'borderTopWidth', 'borderRightWidth', 'borderBottomWidth', 'borderLeftWidth', 'paddingTop', 'paddingBottom', 'paddingLeft', 'fontStyle', 'fontVariant', 'fontWeight', 'fontStretch', 'fontSize', 'lineHeight', 'fontFamily', 'textAlign', 'textTransform', 'textIndent', 'textDecoration', 'letterSpacing', 'wordSpacing', 'textRendering', 'webkitWritingMode', 'textTransform', 'textIndent', 'overflowWrap'];
134
+
135
+ properties.forEach(function(prop) {
136
+ if (['left', 'top'].indexOf(prop.toLowerCase()) === -1)
137
+ style[prop] = computed[prop];
138
+ });
139
+
140
+ let spanText = mirrorDiv.querySelector('span-text');
141
+ if (!spanText)
142
+ spanText = document.createElement('span-text');
143
+ spanText.style['display'] = 'block';
144
+ let value_element = (['TEXTAREA', 'INPUT'].indexOf(element.nodeName) == -1) ? element.innerHTML : element.value;
145
+
146
+ spanText.textContent = value_element;
147
+ if (element.nodeName === 'INPUT')
148
+ spanText.textContent = mirrorDiv.textContent.replace(/\s/g, " ");
149
+ mirrorDiv.appendChild(spanText);
150
+ let cursor, cursorFlag;
192
151
  let details = {collection, document_id, name};
193
152
  if (socket_id) {
194
- let cursores_other_elements = document.querySelectorAll(`.cursor-container[socket_id="${socket_id}"]`);
195
- if (cursores_other_elements) {
196
- for (cursor of cursores_other_elements){
197
- if (cursor.details != details)
198
- cursor.remove();
153
+ let userSelections = document.querySelectorAll(`selection[socket_id="${socket_id}"]`);
154
+ if (userSelections) {
155
+ for (let userSelection of userSelections){
156
+ if (userSelection.details != details)
157
+ userSelection.remove();
199
158
  }
200
159
  }
201
- cursor = elementMirror.querySelector(`.cursor-container[socket_id="${socket_id}"]`);
202
- if (!cursor) {
203
- let cursor_template = '<div class="cursor-container" socket_id="' + socket_id + '" \
204
- style="background-color:' + selection.background + '"> \
205
- <div class="cursor-flag" \
206
- collection="users" \
207
- document_id="' + selection.user_id + '" \
208
- name="name">' + selection.userName + '</div>\
209
- </div>';
210
- elementMirror.innerHTML = cursor_template + elementMirror.innerHTML;
160
+ let selection_user = mirrorDiv.querySelector(`selection[socket_id="${socket_id}"]`);
161
+ if (!selection_user){
162
+ selection_user = document.createElement('selection');
163
+ selection_user.setAttribute('socket_id', socket_id);
164
+ selection_user.style["position"] = "absolute";
165
+ let style_mirror = getComputedStyle(mirrorDiv);
166
+ selection_user.style["top"] = style_mirror.paddingTop;
167
+ selection_user.style["left"] = style_mirror.paddingLeft;
168
+ selection_user.style["width"] = mirrorDiv.clientWidth - parseInt(computed['padding-left']) - parseInt(computed['padding-right']) - scrollBarWidth + 'px';
169
+ selection_user.details = {collection, document_id, name};
170
+
171
+ mirrorDiv.appendChild(selection_user);
211
172
  }
212
- }
213
- cursor = elementMirror.querySelector(`.cursor-container[socket_id="${socket_id}"]`);
214
- if (cursor) {
215
- let font_size = getStyle(element, 'font-size');
216
- font_size = parseFloat(font_size.substring(0, font_size.length - 2));
217
- let cursor_height = ((font_size * 112.5) / 100);
218
- let cursorFlag = cursor.querySelector('.cursor-flag');
173
+
174
+ let value_element = (['TEXTAREA', 'INPUT'].indexOf(element.nodeName) == -1) ? element.innerHTML : element.value;
175
+ selection_user.textContent = value_element.substring(0, start);
176
+
177
+ let value_end = value_element.substring(end) || '';
178
+ let span_end = document.createElement('span-end');
179
+ span_end.textContent = value_end;
180
+
181
+ cursor = document.createElement('cursor');
182
+ cursor.setAttribute('socket_id', socket_id);
219
183
  cursor.dataset.start = start;
220
184
  cursor.dataset.end = end;
221
185
  cursor.setAttribute('user-name', selection.userName);
222
186
  cursor.setAttribute('user-background', selection.background);
223
187
  cursor.setAttribute('user-color', selection.color);
224
- cursor.style["height"] = cursor_height + "px";
225
188
  cursor.style["background"] = selection.background;
189
+ cursor.details = {collection, document_id, name};
190
+ cursor.textContent = "1";
191
+ span_end.prepend(cursor);
192
+
193
+ let selectedText = document.createElement('selected-text');
194
+ selectedText.style["backgroundColor"] = selection.background;
195
+ selectedText.textContent = value_element.substring(start, end) || '';
196
+ span_end.prepend(selectedText);
197
+
198
+ cursorFlag = document.createElement('cursor-flag');
199
+ cursorFlag.setAttribute('collection', 'users');
200
+ cursorFlag.setAttribute('document_id', selection.user_id);
201
+ cursorFlag.setAttribute('name', 'name');
226
202
  cursorFlag.style["background"] = selection.background;
227
203
  cursorFlag.innerHTML = selection.userName;
228
- cursor.style["left"] = coordinates.end.left + "px";
229
- cursor.details = {collection, document_id, name};
230
-
231
- let userSelections = document.querySelectorAll(`.cursor-selection[socket_id="${socket_id}"]`);
232
- if (userSelections) {
233
- for (selection of userSelections){
234
- if (selection.details != details)
235
- selection.remove();
236
- }
237
- }
238
- if ((start != end)) {
239
- var scrollwidth = element.offsetWidth - element.scrollWidth;
240
- var padding_right = parseInt(getComputedStyle(element)["paddingRight"]);
241
-
242
- let selection_user = document.createElement('span');
243
- selection_user.id = 'sel-' + socket_id;
244
- selection_user.setAttribute('socket_id', socket_id);
245
- selection_user.className = 'cursor-selection';
246
- let style_mirror = getComputedStyle(elementMirror);
247
- selection_user.style["position"] = "absolute";
248
- selection_user.style["top"] = style_mirror.paddingTop;
249
- selection_user.style["left"] = style_mirror.paddingLeft;
250
- selection_user.style["padding-right"] = scrollwidth + padding_right + "px";
251
- selection_user.details = {collection, document_id, name};
252
- elementMirror.insertBefore(selection_user, elementMirror.firstChild);
253
-
254
- let selection_span_by_user = document.createElement('span');
255
- selection_span_by_user.style.backgroundColor = selection.background;
256
- let value_element = (['TEXTAREA', 'INPUT'].indexOf(element.nodeName) == -1) ? element.innerHTML : element.value;
257
- selection_user.textContent = value_element.substring(0, start);
258
- let value_span_selection = value_element.substring(start, end) || '';
259
- selection_span_by_user.textContent = value_span_selection;
260
- selection_user.appendChild(selection_span_by_user);
261
- }
204
+ cursor.appendChild(cursorFlag);
205
+
206
+ selection_user.appendChild(span_end);
262
207
  }
263
208
  scrollMirror(element);
264
209
  }
@@ -277,9 +222,9 @@ function updateCursors(element) {
277
222
  }
278
223
  for (let mirrorDiv of mirrorDivs) {
279
224
  element = mirrorDiv.element;
280
- let cursor_containers = mirrorDiv.querySelectorAll('.cursor-container');
225
+ let cursors = mirrorDiv.querySelectorAll('cursor');
281
226
 
282
- cursor_containers.forEach(function(child_cursor, index, array) {
227
+ cursors.forEach(function(child_cursor, index, array) {
283
228
  let clientId = child_cursor.getAttribute('socket_id');
284
229
  let userName = child_cursor.getAttribute('user-name');
285
230
  let background = child_cursor.getAttribute('user-background');
@@ -301,12 +246,12 @@ function updateCursors(element) {
301
246
  }
302
247
 
303
248
  function removeCursor(clientId) {
304
- let elements = document.querySelectorAll(`.cursor-container[socket_id="${clientId}"]`);
249
+ let elements = document.querySelectorAll(`cursor[socket_id="${clientId}"]`);
305
250
  elements.forEach(function(element, index, array) {
306
251
  element.parentNode.removeChild(element);
307
252
  });
308
253
 
309
- let sel_elements = document.querySelectorAll('[id*="sel-' + clientId + '"]');
254
+ let sel_elements = document.querySelectorAll(`selection[socket_id="${clientId}"]`);
310
255
  sel_elements.forEach(function(sel_element, index, array) {
311
256
  sel_element.parentNode.removeChild(sel_element);
312
257
  });
@@ -365,7 +310,6 @@ function sendPosition(info) {
365
310
 
366
311
 
367
312
  message.listen('cursor', function(selection) {
368
- if (selection.clientId == clientId) return;
369
313
  if (selection.start != null && selection.end != null)
370
314
  drawCursors(selection);
371
315
  else