@cocreate/cursors 1.23.4 → 1.24.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,33 @@
1
+ # [1.24.0](https://github.com/CoCreate-app/CoCreate-cursors/compare/v1.23.5...v1.24.0) (2024-11-04)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * frameELement handlning ([d6e22b2](https://github.com/CoCreate-app/CoCreate-cursors/commit/d6e22b2629f6cbbdee62d74737e1a167cecad371))
7
+ * mirror_id specificity ([af25adb](https://github.com/CoCreate-app/CoCreate-cursors/commit/af25adbe2399534c95dec3b22d0c9c135eeff945))
8
+ * observer taget has been renamed to selector ([69a331b](https://github.com/CoCreate-app/CoCreate-cursors/commit/69a331bde18eb94ff038e025cb3bf2a59a33dc8d))
9
+ * pretier.config.js and file formating ([0424309](https://github.com/CoCreate-app/CoCreate-cursors/commit/042430940a6fc0fd3bdaa758de39058f4cf4b7d3))
10
+
11
+
12
+ ### Features
13
+
14
+ * add prettier.config.js and format files ([592805d](https://github.com/CoCreate-app/CoCreate-cursors/commit/592805d42049e75c5ffa8a6ef74ef64960bd0be3))
15
+
16
+ ## [1.23.5](https://github.com/CoCreate-app/CoCreate-cursors/compare/v1.23.4...v1.23.5) (2024-06-12)
17
+
18
+
19
+ ### Bug Fixes
20
+
21
+ * bump cocreate dependencies ([004badc](https://github.com/CoCreate-app/CoCreate-cursors/commit/004badc58b9eaec4b3b7c9e7f4a6933da5420121))
22
+ * contenteditable element selections ([8f16a74](https://github.com/CoCreate-app/CoCreate-cursors/commit/8f16a7465c027e78b63fa450675877ac827fcdc8))
23
+ * cursor selections on contenteditable elements ([8fc58e0](https://github.com/CoCreate-app/CoCreate-cursors/commit/8fc58e0dce113844876858244160de6c1ed3efaf))
24
+ * fix cursors and selection between contenteditable and textareas ([2e12fec](https://github.com/CoCreate-app/CoCreate-cursors/commit/2e12fece9f37a54d82731ef59bf03536b845a3b0))
25
+ * improved handling of contenteditable cursors using innerHtml ([36b0b40](https://github.com/CoCreate-app/CoCreate-cursors/commit/36b0b4073eb46763dfec1d955aae2cdcf707430c))
26
+ * improved whitespace handling ([0fe857e](https://github.com/CoCreate-app/CoCreate-cursors/commit/0fe857ef526b19e65f524ca2b0adc88bc6f807eb))
27
+ * set transparent!important ([e728c56](https://github.com/CoCreate-app/CoCreate-cursors/commit/e728c5692d5022d839e41375339089e93da3bb3c))
28
+ * svg icon class ([b7512a5](https://github.com/CoCreate-app/CoCreate-cursors/commit/b7512a5d272dfb62e0a4537c96c426f879b73340))
29
+ * use framId to determin if should continue element loop ([bb865cf](https://github.com/CoCreate-app/CoCreate-cursors/commit/bb865cf859a0ae319f45330bec9ba8532ab5cd91))
30
+
1
31
  ## [1.23.4](https://github.com/CoCreate-app/CoCreate-cursors/compare/v1.23.3...v1.23.4) (2024-04-29)
2
32
 
3
33
 
package/demo/index.html CHANGED
@@ -39,9 +39,7 @@
39
39
 
40
40
  <button
41
41
  class="submitBtn position:fixed bottom:15px right:15px bg-blue grow-hover round">
42
- <i
43
- class="height:20px fill:#505050"
44
- src="/assets/svg/save.svg"></i>
42
+ <i src="/assets/svg/save.svg"></i>
45
43
  </button>
46
44
  </form>
47
45
 
@@ -0,0 +1,168 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <style>
7
+ .highlight {
8
+ position: absolute;
9
+ background-color: yellow;
10
+ opacity: 0.2;
11
+ pointer-events: none;
12
+ z-index: 1000;
13
+ }
14
+ </style>
15
+ <title>Highlight Selected Text</title>
16
+ </head>
17
+ <body>
18
+ <div class="markdown-body padding:0_20px width:100%">
19
+ <h2>Introduction</h2>
20
+ <p>
21
+ Choosing a career is one of the most significant decisions we
22
+ face in our lives. It's not just about picking a job—it's about
23
+ finding a path that aligns with our passions, strengths, and
24
+ values. However, with so many options and paths available, the
25
+ process can be overwhelming. In this blog post, we'll delve into
26
+ strategies for finding your career path, from self-reflection to
27
+ taking concrete action.
28
+ </p>
29
+ <h2>Self-Reflection: Know Thyself</h2>
30
+ <h3>Assess Your Interests and Skills</h3>
31
+ <p>
32
+ Start by asking yourself what you enjoy doing. What are your
33
+ hobbies? What subjects do you find fascinating? Also, consider
34
+ your skills. Are there tasks you excel at or that come naturally
35
+ to you? Make a list of your interests and skills to guide your
36
+ career exploration.
37
+ </p>
38
+ <h3>Understand Your Values</h3>
39
+ <p>
40
+ Your values are the principles that matter most to you in life.
41
+ Do you value work-life balance, or is career progression your
42
+ top priority? Do you want to make a difference in the world, or
43
+ is financial security more important? Knowing what you value can
44
+ help you find careers that align with your principles.
45
+ </p>
46
+ <h3>Consider Your Personality</h3>
47
+ <p>
48
+ Personality can play a big role in job satisfaction. Are you an
49
+ extrovert who thrives in social environments, or are you more
50
+ introverted and prefer solitary work? Personality assessments,
51
+ like the Myers-Briggs Type Indicator or the Big Five, can
52
+ provide insights into careers that might suit you.
53
+ </p>
54
+ <h2>Exploration: Broaden Your Horizons</h2>
55
+ <h3>Research Potential Careers</h3>
56
+ <p>
57
+ Once you have a better understanding of your interests, skills,
58
+ and values, start researching careers that align with them. Look
59
+ into job descriptions, required qualifications, and potential
60
+ career paths. Informational interviews with professionals in the
61
+ field can also provide valuable insights.
62
+ </p>
63
+ <h3>Get Experience</h3>
64
+ <p>
65
+ Internships, part-time jobs, and volunteer opportunities are
66
+ excellent ways to gain experience and learn more about different
67
+ careers. These experiences can help you understand what you like
68
+ and don't like in a work environment.
69
+ </p>
70
+ <h3>Education and Training</h3>
71
+ <p>
72
+ Sometimes, the career you're interested in requires specific
73
+ education or training. Research the necessary qualifications and
74
+ consider whether you're willing to undertake the required
75
+ education or certification processes.
76
+ </p>
77
+ <h2>Evaluating Options: Making the Choice</h2>
78
+ <h3>Pros and Cons</h3>
79
+ <p>
80
+ For each career option you're considering, list the pros and
81
+ cons. This can help you visualize which career aligns best with
82
+ your interests, skills, and values.
83
+ </p>
84
+ <h3>Long-Term Outlook</h3>
85
+ <p>
86
+ Consider the long-term prospects of each career. Is the field
87
+ growing, or is it in decline? Will there be opportunities for
88
+ advancement? A career with a positive outlook can offer more
89
+ stability and growth potential.
90
+ </p>
91
+ <h3>Work Environment</h3>
92
+ <p>
93
+ Think about the type of work environment you thrive in. Do you
94
+ prefer a structured corporate setting, a creative agency, or
95
+ perhaps the flexibility of freelancing? Make sure your chosen
96
+ career can provide the environment you're looking for.
97
+ </p>
98
+ <h2>Taking Action: Moving Forward</h2>
99
+ <h3>Set Goals</h3>
100
+ <p>
101
+ Once you've decided on a career path, set short-term and
102
+ long-term goals. What steps do you need to take to enter your
103
+ chosen field? This could include further education, networking,
104
+ or gaining more experience.
105
+ </p>
106
+ <h3>Create a Plan</h3>
107
+ <p>
108
+ Develop a detailed plan of how you'll reach your goals. Break
109
+ down your plan into manageable steps, and set deadlines to keep
110
+ yourself accountable.
111
+ </p>
112
+ <h3>Stay Flexible</h3>
113
+ <p>
114
+ Remember that career paths can change. Stay open to new
115
+ opportunities, and don't be afraid to pivot if your interests or
116
+ circumstances change.
117
+ </p>
118
+ <h2>Conclusion</h2>
119
+ <p>
120
+ Choosing a career is a journey that requires introspection,
121
+ exploration, and a willingness to adapt. By understanding
122
+ yourself, researching options, evaluating potential paths, and
123
+ taking action, you can find a career that is fulfilling and
124
+ rewarding. Remember that it's okay to change directions, and the
125
+ most important thing is to find a path that feels right for you.
126
+ </p>
127
+
128
+ <input value="hello today good sir" />
129
+ </div>
130
+ <script>
131
+ document.addEventListener("selectionchange", () => {
132
+ // Remove existing highlights
133
+ document
134
+ .querySelectorAll(".highlight")
135
+ .forEach((el) => el.remove());
136
+
137
+ // Get the selection object
138
+ const selection = window.getSelection();
139
+
140
+ // Check if there's any text selected
141
+ if (!selection.isCollapsed) {
142
+ // Iterate over all ranges in the selection
143
+ for (let i = 0; i < selection.rangeCount; i++) {
144
+ const range = selection.getRangeAt(i);
145
+ const rects = range.getClientRects();
146
+
147
+ // Iterate over all rects in the current range
148
+ for (let rect of rects) {
149
+ const highlightSpan = document.createElement("div");
150
+ highlightSpan.className = "highlight";
151
+
152
+ highlightSpan.style.left = `${
153
+ rect.left + window.scrollX
154
+ }px`;
155
+ highlightSpan.style.top = `${
156
+ rect.top + window.scrollY
157
+ }px`;
158
+ highlightSpan.style.width = `${rect.width}px`;
159
+ highlightSpan.style.height = `${rect.height}px`;
160
+
161
+ document.body.appendChild(highlightSpan);
162
+ }
163
+ }
164
+ }
165
+ });
166
+ </script>
167
+ </body>
168
+ </html>
package/docs/index.html CHANGED
@@ -72,41 +72,31 @@
72
72
  href="https://github.com/CoCreate-app/CoCreate-cursor"
73
73
  target="_blank"
74
74
  class="margin-right:15px"
75
- ><i
76
- class="height:20px fill:#505050"
77
- src="/assets/svg/github.svg"></i
75
+ ><i src="/assets/svg/github.svg"></i
78
76
  ></a>
79
77
  <a
80
78
  class="margin-right:15px share"
81
79
  share-network="twitter"
82
80
  title="Share on twitter"
83
- ><i
84
- class="height:20px fill:#505050"
85
- src="/assets/svg/twitter.svg"></i
81
+ ><i src="/assets/svg/twitter.svg"></i
86
82
  ></a>
87
83
  <a
88
84
  class="margin-right:15px share"
89
85
  share-network="facebook"
90
86
  title="Share on Facebook"
91
- ><i
92
- class="height:20px fill:#505050"
93
- src="/assets/svg/facebook.svg"></i
87
+ ><i src="/assets/svg/facebook.svg"></i
94
88
  ></a>
95
89
  <a
96
90
  class="margin-right:15px share"
97
91
  share-network="instagram"
98
92
  title="Share on instagram"
99
- ><i
100
- class="height:20px fill:#505050"
101
- src="/assets/svg/instagram.svg"></i
93
+ ><i src="/assets/svg/instagram.svg"></i
102
94
  ></a>
103
95
  <a
104
96
  class="margin-right:15px share"
105
97
  share-network="share"
106
98
  title="Share on share"
107
- ><i
108
- class="height:20px fill:#505050"
109
- src="/assets/svg/share-alt.svg"></i
99
+ ><i src="/assets/svg/share-alt.svg"></i
110
100
  ></a>
111
101
  </div>
112
102
  </div>
@@ -223,7 +213,7 @@
223
213
  toggle="code-height"
224
214
  toggle-selector="#demo-code"
225
215
  ><i
226
- class="height:18px fill:#505050"
216
+ class="height:18px"
227
217
  src="/assets/svg/eye.svg"></i
228
218
  ></a>
229
219
  <a
@@ -234,18 +224,14 @@
234
224
  hide="#eye-slash"
235
225
  toggle="code-height"
236
226
  toggle-selector="#demo-code"
237
- ><i
238
- class="height:20px fill:#505050"
239
- src="/assets/svg/eye-slash.svg"></i
227
+ ><i src="/assets/svg/eye-slash.svg"></i
240
228
  ></a>
241
229
  <a
242
230
  class="margin-right:10px"
243
231
  id="code"
244
232
  show="#code-slash"
245
233
  hide="#code, #demo-code"
246
- ><i
247
- class="height:20px fill:#505050"
248
- src="/assets/svg/code.svg"></i
234
+ ><i src="/assets/svg/code.svg"></i
249
235
  ></a>
250
236
  <a
251
237
  class="margin-right:10px"
@@ -254,7 +240,7 @@
254
240
  show="#code, #demo-code"
255
241
  hide="#code-slash"
256
242
  ><i
257
- class="display:flex height:18px fill:#505050"
243
+ class="height:18px"
258
244
  src="/assets/svg/code.svg"></i
259
245
  ></a>
260
246
  <a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cocreate/cursors",
3
- "version": "1.23.4",
3
+ "version": "1.24.0",
4
4
  "description": "Collaborative user cursor position for inputs, textarea's and contenteditable elements.",
5
5
  "keywords": [
6
6
  "cursors",
@@ -59,12 +59,12 @@
59
59
  "webpack-log": "^3.0.1"
60
60
  },
61
61
  "dependencies": {
62
- "@cocreate/local-storage": "^1.14.3",
63
- "@cocreate/observer": "^1.16.0",
64
- "@cocreate/random-color": "^1.10.0",
65
- "@cocreate/selection": "^1.12.1",
66
- "@cocreate/socket-client": "^1.39.0",
67
- "@cocreate/utils": "^1.33.6",
68
- "@cocreate/uuid": "^1.11.1"
62
+ "@cocreate/local-storage": "^1.15.0",
63
+ "@cocreate/observer": "^1.16.1",
64
+ "@cocreate/random-color": "^1.10.1",
65
+ "@cocreate/selection": "^1.12.2",
66
+ "@cocreate/socket-client": "^1.39.1",
67
+ "@cocreate/utils": "^1.33.7",
68
+ "@cocreate/uuid": "^1.11.2"
69
69
  }
70
70
  }
@@ -0,0 +1,16 @@
1
+ module.exports = {
2
+ tabWidth: 4,
3
+ semi: true,
4
+ trailingComma: "none",
5
+ bracketSameLine: true,
6
+ useTabs: true,
7
+ overrides: [
8
+ {
9
+ files: ["*.json", "*.yml", "*.yaml"],
10
+ options: {
11
+ tabWidth: 2,
12
+ useTabs: false
13
+ },
14
+ }
15
+ ],
16
+ };
package/src/index.css CHANGED
@@ -3,6 +3,7 @@ cursor {
3
3
  display: inline-block;
4
4
  width: 2px;
5
5
  background-color: dodgerblue;
6
+ white-space: pre-wrap
6
7
  /*z-index:10;*/
7
8
  }
8
9
 
@@ -36,11 +37,18 @@ selected-text {
36
37
  }
37
38
 
38
39
  .mirror_color {
39
- color: transparent;
40
- pointer-events: none;
40
+ color: transparent!important;
41
41
  }
42
42
 
43
43
  .mirror_scroll::-webkit-scrollbar {
44
44
  background: rgba(0, 0, 0, 0.00);
45
45
  width: 0px;
46
46
  }
47
+
48
+ .mirror[mirror_id][contenteditable] {
49
+ white-space: pre-wrap
50
+ }
51
+
52
+ selected-text * {
53
+ background-color: inherit;
54
+ }
package/src/index.js CHANGED
@@ -83,16 +83,16 @@ function drawCursors(selection) {
83
83
  const array = selection['array'];
84
84
  const object = selection['object'];
85
85
  const key = selection['key'];
86
- let start = selection['start'];
87
- let end = selection['end'];
88
86
  let elements = selection.element;
89
87
  if (!elements) {
90
88
  let selector = '[array="' + array + '"][object="' + object + '"][key="' + key + '"]';
91
- selector += ':not(.codemirror, .monaco, [actions])';
89
+ selector += ':not([actions])';
92
90
  elements = document.querySelectorAll(selector);
93
91
  }
94
92
  for (let element of elements) {
95
- if (window.activeElement == element && socket.has(selection.socketId)) {
93
+ let start = selection['start']
94
+ let end = selection['end']
95
+ if (window.activeElement == element && socket.frameId === selection.frameId) {
96
96
  continue;
97
97
  }
98
98
  let realtime = element.getAttribute('realtime');
@@ -100,29 +100,52 @@ function drawCursors(selection) {
100
100
  let contenteditable = element.getAttribute('contenteditable');
101
101
  if (!realtime || realtime == 'false' || cursors == 'false') continue;
102
102
  if (element.tagName != 'INPUT' && element.tagName != 'TEXTAREA')
103
- if (contenteditable == 'false' || contenteditable == null)
103
+ if (contenteditable == 'false' || contenteditable === null) {
104
104
  if (cursors != 'true') continue;
105
+ }
106
+
107
+ let domTextEditor = element;
105
108
 
106
109
  if (element.hasAttribute('contenteditable')) {
107
- let domTextEditor = element;
110
+ contenteditable = true
111
+ // let domTextEditor = element;
108
112
  if (element.tagName == 'IFRAME') {
109
113
  // let frameClientId = element.contentDocument.defaultView.CoCreateSockets.id;
110
114
  // if (frameClientId == selection.socketId) continue;
111
- domTextEditor = element.contentDocument.documentElement;
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
+
112
131
  }
132
+
113
133
  if (!domTextEditor.htmlString)
114
134
  continue
115
- let pos = getElementPosition(domTextEditor.htmlString, start, end);
116
- if (pos.start) {
117
- if (pos.path)
118
- element = domTextEditor.querySelector(pos.path);
119
- let endPos = end - start;
120
- if (endPos > 0)
121
- end = pos.start + endPos;
122
- else
123
- end = pos.start;
124
- start = pos.start;
125
- }
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
+ // }
126
149
  }
127
150
 
128
151
  if (!element) continue;
@@ -138,7 +161,10 @@ function drawCursors(selection) {
138
161
  mirrorDiv.className = (environment_prod) ? 'mirror mirror_scroll mirror_color' : 'mirror mirror_scroll';
139
162
  mirrorDiv.contentEditable = false;
140
163
  mirrorDiv.element = element;
141
- element.insertAdjacentElement('afterend', mirrorDiv);
164
+ if (element.tagName === "HTML")
165
+ element.insertAdjacentElement('beforeend', mirrorDiv);
166
+ else
167
+ element.insertAdjacentElement('afterend', mirrorDiv);
142
168
  // let parent = element.parentElement;
143
169
  // let parentComputed = getComputedStyle(parent);
144
170
  // let parentStylePosition = parentComputed['position'];
@@ -150,10 +176,10 @@ function drawCursors(selection) {
150
176
 
151
177
  _initEvents(element);
152
178
  initDocument(document);
153
- }
154
- else
179
+ } else
155
180
  mirrorDiv = document.getElementById(id_mirror);
156
181
 
182
+
157
183
  let computed = getComputedStyle(element);
158
184
  let rect = element.getBoundingClientRect();
159
185
 
@@ -166,18 +192,17 @@ function drawCursors(selection) {
166
192
  style.position = 'absolute';
167
193
  style.width = rect.width + 'px';
168
194
  style.height = rect.height + 'px';
195
+ style.margin = '0px';
196
+ style.borderColor = 'transparent';
197
+ style.outlineColor = 'transparent';
198
+
169
199
  style.overflowX = computed['overflowX'];
170
200
  style.overflowY = computed['overflowY'];
171
- style.margin = '0px';
172
201
  style.padding = computed['padding'];
173
202
  style.border = computed['border'];
174
- style.borderColor = 'transparent';
175
203
  style.outline = computed['outline'];
176
- style.outlineColor = 'transparent';
177
- style.boxSizing = computed['boxSizing'];
178
- style.lineHeight = computed['lineHeight'];
179
204
 
180
- if (element.parentElement.style['display'] == 'inline') {
205
+ if (element.parentElement && element.parentElement.style['display'] == 'inline') {
181
206
  style.top = element.clientTop + 'px';
182
207
  style.left = element.clientLeft + 'px';
183
208
  } else {
@@ -185,56 +210,98 @@ function drawCursors(selection) {
185
210
  style.left = element.offsetLeft + 'px';
186
211
  }
187
212
 
188
- if (element.nodeName !== 'INPUT') {
189
- style.wordWrap = 'break-word';
190
- style.whiteSpace = 'pre-wrap';
213
+ if (contenteditable) {
214
+ const actualStyles = getActualStyles(element);
215
+ for (const [prop, value] of Object.entries(actualStyles)) {
216
+ style[prop] = value;
217
+ }
191
218
  } else {
192
- style.whiteSpace = 'pre';
193
- }
194
-
195
- let properties = ['fontStyle', 'fontVariant', 'fontWeight', 'fontStretch', 'fontSize', 'fontFamily', 'letterSpacing', 'lineHeight', 'textAlign', 'textTransform', 'textIndent', 'textDecoration', 'textRendering', 'textTransform', 'textIndent', 'overflowWrap', 'tabSize', 'webkitWritingMode', 'wordSpacing'];
196
-
197
- properties.forEach(function (prop) {
198
- if (['left', 'top'].indexOf(prop.toLowerCase()) === -1)
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) {
199
221
  style[prop] = computed[prop];
200
- });
222
+ });
223
+
224
+ let paddingBottom = parseFloat(computed['padding-bottom'].replace('px', ''))
225
+ style.height = rect.height - paddingBottom + 'px';
201
226
 
202
- let spanText = mirrorDiv.querySelector('span-text');
203
- if (!spanText)
204
- spanText = document.createElement('span-text');
205
- spanText.style['display'] = 'block';
206
- let value_element = (['TEXTAREA', 'INPUT'].indexOf(element.nodeName) == -1) ? element.innerHTML : element.value;
227
+ if (element.nodeName === 'INPUT')
228
+ style.whiteSpace = 'pre';
229
+ }
207
230
 
208
- spanText.textContent = value_element;
209
- if (element.nodeName === 'INPUT')
210
- spanText.textContent = mirrorDiv.textContent.replace(/\s/g, " ");
211
- mirrorDiv.appendChild(spanText);
212
231
  let cursor, cursorFlag;
213
232
  let details = { array, object, key };
214
233
  if (socket_id) {
215
- let userSelection = mirrorDiv.querySelector(`selection[socket_id="${socket_id}"]`);
216
- if (userSelection && userSelection.details != details)
217
- userSelection.remove();
218
234
  let selection_user = mirrorDiv.querySelector(`selection[socket_id="${socket_id}"]`);
219
- if (!selection_user) {
220
- selection_user = document.createElement('selection');
221
- selection_user.setAttribute('socket_id', socket_id);
222
- selection_user.style["position"] = "absolute";
223
- let style_mirror = getComputedStyle(mirrorDiv);
224
- selection_user.style["top"] = style_mirror.paddingTop;
225
- selection_user.style["left"] = style_mirror.paddingLeft;
226
- selection_user.style["width"] = mirrorDiv.clientWidth - parseInt(computed['padding-left']) - parseInt(computed['padding-right']) - scrollBarWidth + 'px';
227
- selection_user.details = { array, object, key };
228
-
229
- mirrorDiv.appendChild(selection_user);
230
- }
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 };
231
246
 
232
247
  let value_element = (['TEXTAREA', 'INPUT'].indexOf(element.nodeName) == -1) ? element.innerHTML : element.value;
233
- selection_user.textContent = value_element.substring(0, start);
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
+ }
234
304
 
235
- let value_end = value_element.substring(end) || '';
236
- let span_end = document.createElement('span-end');
237
- span_end.textContent = value_end;
238
305
 
239
306
  cursor = document.createElement('cursor');
240
307
  cursor.setAttribute('socket_id', socket_id);
@@ -245,13 +312,7 @@ function drawCursors(selection) {
245
312
  cursor.setAttribute('user-color', selection.color);
246
313
  cursor.style["background"] = selection.background;
247
314
  cursor.details = { array, object, key };
248
- cursor.textContent = "1";
249
- span_end.prepend(cursor);
250
-
251
- let selectedText = document.createElement('selected-text');
252
- selectedText.style["backgroundColor"] = selection.background;
253
- selectedText.textContent = value_element.substring(start, end) || '';
254
- span_end.prepend(selectedText);
315
+ cursor.textContent = " ";
255
316
 
256
317
  cursorFlag = document.createElement('cursor-flag');
257
318
  cursorFlag.setAttribute('array', 'users');
@@ -261,12 +322,57 @@ function drawCursors(selection) {
261
322
  cursorFlag.innerHTML = selection.userName;
262
323
  cursor.appendChild(cursorFlag);
263
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
+
264
340
  selection_user.appendChild(span_end);
341
+
342
+ mirrorDiv.appendChild(selection_user);
265
343
  }
266
344
  scrollMirror(element);
267
345
  }
268
346
  }
269
347
 
348
+ 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;
374
+ }
375
+
270
376
  function updateCursors(element) {
271
377
  let mirrorDivs = [];
272
378
  let id_mirror = element.getAttribute('mirror_id');
@@ -274,10 +380,10 @@ function updateCursors(element) {
274
380
  let mirrorDiv = element.ownerDocument.getElementById(id_mirror);
275
381
  if (mirrorDiv)
276
382
  mirrorDivs = [mirrorDiv];
277
- }
278
- else {
383
+ } else {
279
384
  mirrorDivs = element.querySelectorAll('.mirror');
280
385
  }
386
+
281
387
  for (let mirrorDiv of mirrorDivs) {
282
388
  element = mirrorDiv.element;
283
389
  let cursors = mirrorDiv.querySelectorAll('cursor');
@@ -368,6 +474,7 @@ socket.listen('cursor', function (response) {
368
474
  // if (socket.has(selection.socketId)) return;
369
475
  let selection = response.data
370
476
  selection.socketId = response.socketId
477
+ selection.frameId = response.frameId
371
478
  if (selection.start != null && selection.end != null)
372
479
  drawCursors(selection);
373
480
  else
@@ -377,7 +484,7 @@ socket.listen('cursor', function (response) {
377
484
  observer.init({
378
485
  name: 'CoCreateCursor',
379
486
  observe: ['addedNodes'],
380
- target: '[array][object][key]',
487
+ selector: '[array][object][key]',
381
488
  callback: function (mutation) {
382
489
  initElement(mutation.target);
383
490
  }