@cocreate/cursors 1.10.15 → 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,39 @@
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
+
15
+ ## [1.10.18](https://github.com/CoCreate-app/CoCreate-cursors/compare/v1.10.17...v1.10.18) (2021-12-08)
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * check contenteditable element then iframe ([29bd7b4](https://github.com/CoCreate-app/CoCreate-cursors/commit/29bd7b4e7460c478cec37916fe9bb7439d2b8805))
21
+ * use attribute for socket_id instead of id ([db901da](https://github.com/CoCreate-app/CoCreate-cursors/commit/db901daa270551cd6b1b6b95124d785ed81643c9))
22
+
23
+ ## [1.10.17](https://github.com/CoCreate-app/CoCreate-cursors/compare/v1.10.16...v1.10.17) (2021-11-27)
24
+
25
+
26
+ ### Bug Fixes
27
+
28
+ * update dependencies ([4532eb5](https://github.com/CoCreate-app/CoCreate-cursors/commit/4532eb5470d6a8a76c22ed636ba83e9db43d2064))
29
+
30
+ ## [1.10.16](https://github.com/CoCreate-app/CoCreate-cursors/compare/v1.10.15...v1.10.16) (2021-11-27)
31
+
32
+
33
+ ### Bug Fixes
34
+
35
+ * update dependencies ([000e805](https://github.com/CoCreate-app/CoCreate-cursors/commit/000e80564cfa029a60d0891da18a4076edb44ff2))
36
+
1
37
  ## [1.10.15](https://github.com/CoCreate-app/CoCreate-cursors/compare/v1.10.14...v1.10.15) (2021-11-27)
2
38
 
3
39
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cocreate/cursors",
3
- "version": "1.10.15",
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.34",
66
- "@cocreate/docs": "^1.2.56",
67
- "@cocreate/message-client": "^1.1.52",
68
- "@cocreate/observer": "^1.3.48",
69
- "@cocreate/random-color": "^1.2.10",
70
- "@cocreate/selection": "^1.2.15",
71
- "@cocreate/uuid": "^1.1.45"
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,13 +27,8 @@
27
27
  white-space: nowrap;
28
28
  }
29
29
 
30
- .selection {
31
- background-color: #ecd44;
32
- opacity: 0.6;
33
- }
34
-
35
- .cursor-selection {
36
- position: absolute;
30
+ selected-text {
31
+ opacity: 0.4;
37
32
  }
38
33
 
39
34
  .mirror {
@@ -49,7 +44,3 @@
49
44
  background: rgba(0, 0, 0, 0.00);
50
45
  width: 0px;
51
46
  }
52
-
53
- .users_selections {
54
- opacity: 0.4;
55
- }
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('.users_selections');
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,11 +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
- if (element.tagName == 'IFRAME') {
169
- let domTextEditor = element.contentDocument.documentElement;
68
+ if (element.hasAttribute('contenteditable')) {
69
+ let domTextEditor = element;
70
+ if (element.tagName == 'IFRAME'){
71
+ let frameClientId = element.contentDocument.defaultView.CoCreateSockets.clientId;
72
+ if (frameClientId == selection.clientId) continue;
73
+ domTextEditor = element.contentDocument.documentElement;
74
+ }
170
75
  let pos = getElementPosition(domTextEditor.htmlString, start, end);
171
76
  element = domTextEditor.querySelector(pos.path);
172
77
  let endPos = end - start;
@@ -176,85 +81,129 @@ function drawCursors(selection) {
176
81
  end = pos.start;
177
82
  start = pos.start;
178
83
  }
179
- if(!element) return;
84
+ if(!element) continue;
180
85
  let document = element.ownerDocument;
86
+ let mirrorDiv;
181
87
  let id_mirror = element.getAttribute('mirror_id');
182
- if (!id_mirror)
183
- element.setAttribute('mirror_id', uuid.generate(8));
184
- id_mirror = element.getAttribute('mirror_id');
185
- let coordinates = getCoordinates(element, start, end);
186
- if (!coordinates) return false;
187
-
188
- let elementMirror = document.getElementById(id_mirror);
189
- let cursor;
190
- // let selection_user = false;
191
- // let user = (typeof(selection) != 'undefined' && selection.hasOwnProperty('user')) ? selection.user : false;
192
- // let user_id = (typeof(selection) != 'undefined' && selection.hasOwnProperty('user_id')) ? user.user_id : false;
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;
151
+ let details = {collection, document_id, name};
193
152
  if (socket_id) {
194
- var cursores_other_elements = document.querySelectorAll('#socket_' + socket_id);
195
- cursores_other_elements.forEach(function(child_cursor, index, array) {
196
- if (child_cursor.parentElement.getAttribute('id') != id_mirror) {
197
- child_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();
198
158
  }
199
- });
200
- cursor = elementMirror.querySelector('.cursor-container#socket_' + socket_id);
201
- if (!cursor) {
202
- let cursor_template = '<div class="cursor-container" \
203
- id="socket_' + 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;
211
159
  }
212
- }
213
- cursor = elementMirror.querySelector('.cursor-container#socket_' + 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 my_cursor = cursor.querySelector('.cursor');
219
- let cursorFlag = cursor.querySelector('.cursor-flag');
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);
172
+ }
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);
220
183
  cursor.dataset.start = start;
221
184
  cursor.dataset.end = end;
222
- cursor.setAttribute('socket_id', socket_id);
223
185
  cursor.setAttribute('user-name', selection.userName);
224
186
  cursor.setAttribute('user-background', selection.background);
225
187
  cursor.setAttribute('user-color', selection.color);
226
- cursor.style["height"] = cursor_height + "px";
227
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');
228
202
  cursorFlag.style["background"] = selection.background;
229
203
  cursorFlag.innerHTML = selection.userName;
230
- cursor.style["left"] = coordinates.end.left + "px";
231
-
232
- let selection_user = document.getElementById('sel-' + socket_id);
233
- if (selection_user) {
234
- selection_user.remove();
235
- }
236
- if ((start != end)) {
237
- var scrollwidth = element.offsetWidth - element.scrollWidth;
238
- var padding_right = parseInt(getComputedStyle(element)["paddingRight"]);
239
-
240
- selection_user = document.createElement('span');
241
- selection_user.id = 'sel-' + socket_id;
242
- selection_user.className = 'users_selections';
243
- let style_mirror = getComputedStyle(elementMirror);
244
- selection_user.style["position"] = "absolute";
245
- selection_user.style["top"] = style_mirror.paddingTop;
246
- selection_user.style["left"] = style_mirror.paddingLeft;
247
- selection_user.style["padding-right"] = scrollwidth + padding_right + "px";
248
- elementMirror.insertBefore(selection_user, elementMirror.firstChild);
249
- let selection_span_by_user = document.createElement('span');
250
- selection_span_by_user.id = 'selection-' + socket_id;
251
- selection_span_by_user.style.backgroundColor = selection.background;
252
- let value_element = (['TEXTAREA', 'INPUT'].indexOf(element.nodeName) == -1) ? element.innerHTML : element.value;
253
- selection_user.textContent = value_element.substring(0, start);
254
- let value_span_selection = value_element.substring(start, end) || '';
255
- selection_span_by_user.textContent = value_span_selection;
256
- selection_user.appendChild(selection_span_by_user);
257
- }
204
+ cursor.appendChild(cursorFlag);
205
+
206
+ selection_user.appendChild(span_end);
258
207
  }
259
208
  scrollMirror(element);
260
209
  }
@@ -273,9 +222,9 @@ function updateCursors(element) {
273
222
  }
274
223
  for (let mirrorDiv of mirrorDivs) {
275
224
  element = mirrorDiv.element;
276
- let cursor_containers = mirrorDiv.querySelectorAll('.cursor-container');
225
+ let cursors = mirrorDiv.querySelectorAll('cursor');
277
226
 
278
- cursor_containers.forEach(function(child_cursor, index, array) {
227
+ cursors.forEach(function(child_cursor, index, array) {
279
228
  let clientId = child_cursor.getAttribute('socket_id');
280
229
  let userName = child_cursor.getAttribute('user-name');
281
230
  let background = child_cursor.getAttribute('user-background');
@@ -297,12 +246,12 @@ function updateCursors(element) {
297
246
  }
298
247
 
299
248
  function removeCursor(clientId) {
300
- let elements = document.querySelectorAll('[id*="socket_' + clientId + '"]');
249
+ let elements = document.querySelectorAll(`cursor[socket_id="${clientId}"]`);
301
250
  elements.forEach(function(element, index, array) {
302
251
  element.parentNode.removeChild(element);
303
252
  });
304
253
 
305
- let sel_elements = document.querySelectorAll('[id*="sel-' + clientId + '"]');
254
+ let sel_elements = document.querySelectorAll(`selection[socket_id="${clientId}"]`);
306
255
  sel_elements.forEach(function(sel_element, index, array) {
307
256
  sel_element.parentNode.removeChild(sel_element);
308
257
  });
@@ -361,7 +310,6 @@ function sendPosition(info) {
361
310
 
362
311
 
363
312
  message.listen('cursor', function(selection) {
364
- if (selection.clientId == clientId) return;
365
313
  if (selection.start != null && selection.end != null)
366
314
  drawCursors(selection);
367
315
  else