@cocreate/cursors 1.23.5 → 1.24.1
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 +22 -0
- package/demo/potential-selection-method.html +168 -0
- package/package.json +1 -1
- package/prettier.config.js +16 -0
- package/src/index.css +1 -1
- package/src/index.js +493 -401
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
## [1.24.1](https://github.com/CoCreate-app/CoCreate-cursors/compare/v1.24.0...v1.24.1) (2025-04-11)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* update observer observe param to type and and attributeName to attributeFilter ([924d448](https://github.com/CoCreate-app/CoCreate-cursors/commit/924d448b2e575cefc01aead3640f53e7095b34f0))
|
|
7
|
+
|
|
8
|
+
# [1.24.0](https://github.com/CoCreate-app/CoCreate-cursors/compare/v1.23.5...v1.24.0) (2024-11-04)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* frameELement handlning ([d6e22b2](https://github.com/CoCreate-app/CoCreate-cursors/commit/d6e22b2629f6cbbdee62d74737e1a167cecad371))
|
|
14
|
+
* mirror_id specificity ([af25adb](https://github.com/CoCreate-app/CoCreate-cursors/commit/af25adbe2399534c95dec3b22d0c9c135eeff945))
|
|
15
|
+
* observer taget has been renamed to selector ([69a331b](https://github.com/CoCreate-app/CoCreate-cursors/commit/69a331bde18eb94ff038e025cb3bf2a59a33dc8d))
|
|
16
|
+
* pretier.config.js and file formating ([0424309](https://github.com/CoCreate-app/CoCreate-cursors/commit/042430940a6fc0fd3bdaa758de39058f4cf4b7d3))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Features
|
|
20
|
+
|
|
21
|
+
* add prettier.config.js and format files ([592805d](https://github.com/CoCreate-app/CoCreate-cursors/commit/592805d42049e75c5ffa8a6ef74ef64960bd0be3))
|
|
22
|
+
|
|
1
23
|
## [1.23.5](https://github.com/CoCreate-app/CoCreate-cursors/compare/v1.23.4...v1.23.5) (2024-06-12)
|
|
2
24
|
|
|
3
25
|
|
|
@@ -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/package.json
CHANGED
package/src/index.css
CHANGED
package/src/index.js
CHANGED
|
@@ -21,14 +21,14 @@
|
|
|
21
21
|
// For details, visit <https://cocreate.app/licenses/> or contact us at sales@cocreate.app.
|
|
22
22
|
|
|
23
23
|
/*globals ResizeObserver*/
|
|
24
|
-
import observer from
|
|
25
|
-
import { getAttributeNames } from
|
|
26
|
-
import socket from
|
|
27
|
-
import uuid from
|
|
28
|
-
import localStorage from
|
|
29
|
-
import { getElementPosition } from
|
|
30
|
-
import { randomColor } from
|
|
31
|
-
import
|
|
24
|
+
import observer from "@cocreate/observer";
|
|
25
|
+
import { getAttributeNames } from "@cocreate/utils";
|
|
26
|
+
import socket from "@cocreate/socket-client";
|
|
27
|
+
import uuid from "@cocreate/uuid";
|
|
28
|
+
import localStorage from "@cocreate/local-storage";
|
|
29
|
+
import { getElementPosition } from "@cocreate/selection";
|
|
30
|
+
import { randomColor } from "@cocreate/random-color";
|
|
31
|
+
import "./index.css";
|
|
32
32
|
|
|
33
33
|
const clientId = socket.clientId || uuid.generate(12);
|
|
34
34
|
const cursorBackground = randomColor();
|
|
@@ -38,442 +38,534 @@ let environment_prod = true;
|
|
|
38
38
|
let selector = "[array][object][key]:not([contentEditable='false'])";
|
|
39
39
|
|
|
40
40
|
function init() {
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
let elements = document.querySelectorAll(selector);
|
|
42
|
+
initElements(elements);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
function initElements(elements) {
|
|
46
|
-
|
|
47
|
-
initElement(element);
|
|
46
|
+
for (let element of elements) initElement(element);
|
|
48
47
|
}
|
|
49
48
|
|
|
50
49
|
function initElement(element) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
50
|
+
let realtime = element.getAttribute("realtime");
|
|
51
|
+
let cursors = element.getAttribute("cursors");
|
|
52
|
+
if (!realtime || realtime === "false" || cursors === "false") return;
|
|
53
|
+
if (element.hasAttribute("actions")) return;
|
|
54
|
+
if (element.cursor) return;
|
|
55
|
+
element.cursor = true;
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
function initDocument(doc) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
59
|
+
let documents;
|
|
60
|
+
try {
|
|
61
|
+
documents = window.top.cursorDocuments;
|
|
62
|
+
} catch (e) {
|
|
63
|
+
console.log("cross-origin failed");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!documents) {
|
|
67
|
+
documents = new Map();
|
|
68
|
+
try {
|
|
69
|
+
window.top.cursorDocuments = documents;
|
|
70
|
+
} catch (e) {
|
|
71
|
+
console.log("cross-origin failed");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (!documents.has(doc)) {
|
|
75
|
+
initResizeObserver(doc.documentElement);
|
|
76
|
+
documents.set(doc);
|
|
77
|
+
}
|
|
79
78
|
}
|
|
80
79
|
|
|
81
80
|
function drawCursors(selection) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
81
|
+
const socket_id = selection["socketId"];
|
|
82
|
+
const array = selection["array"];
|
|
83
|
+
const object = selection["object"];
|
|
84
|
+
const key = selection["key"];
|
|
85
|
+
let elements = selection.element;
|
|
86
|
+
if (!elements) {
|
|
87
|
+
let selector =
|
|
88
|
+
'[array="' +
|
|
89
|
+
array +
|
|
90
|
+
'"][object="' +
|
|
91
|
+
object +
|
|
92
|
+
'"][key="' +
|
|
93
|
+
key +
|
|
94
|
+
'"]';
|
|
95
|
+
selector += ":not([actions])";
|
|
96
|
+
elements = document.querySelectorAll(selector);
|
|
97
|
+
}
|
|
98
|
+
for (let element of elements) {
|
|
99
|
+
let start = selection["start"];
|
|
100
|
+
let end = selection["end"];
|
|
101
|
+
if (
|
|
102
|
+
window.activeElement == element &&
|
|
103
|
+
socket.frameId === selection.frameId
|
|
104
|
+
) {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
let realtime = element.getAttribute("realtime");
|
|
108
|
+
let cursors = element.getAttribute("cursors");
|
|
109
|
+
let contenteditable = element.getAttribute("contenteditable");
|
|
110
|
+
if (!realtime || realtime == "false" || cursors == "false") continue;
|
|
111
|
+
if (element.tagName != "INPUT" && element.tagName != "TEXTAREA")
|
|
112
|
+
if (contenteditable == "false" || contenteditable === null) {
|
|
113
|
+
if (cursors != "true") continue;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
let domTextEditor = element;
|
|
117
|
+
|
|
118
|
+
if (element.hasAttribute("contenteditable")) {
|
|
119
|
+
contenteditable = true;
|
|
120
|
+
// let domTextEditor = element;
|
|
121
|
+
if (element.tagName == "IFRAME") {
|
|
122
|
+
// let frameClientId = element.contentDocument.defaultView.CoCreateSockets.id;
|
|
123
|
+
// if (frameClientId == selection.socketId) continue;
|
|
124
|
+
domTextEditor = element =
|
|
125
|
+
element.contentDocument.documentElement;
|
|
126
|
+
|
|
127
|
+
let pos = getElementPosition(
|
|
128
|
+
domTextEditor.htmlString,
|
|
129
|
+
start,
|
|
130
|
+
end
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
if (pos.path) {
|
|
134
|
+
element = element.querySelector(pos.path);
|
|
135
|
+
if (pos.start) {
|
|
136
|
+
let endPos = end - start;
|
|
137
|
+
if (endPos > 0) end = pos.start + endPos;
|
|
138
|
+
else end = pos.start;
|
|
139
|
+
start = pos.start;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (!domTextEditor.htmlString) continue;
|
|
145
|
+
|
|
146
|
+
// let pos = getElementPosition(domTextEditor.htmlString, start, end);
|
|
147
|
+
|
|
148
|
+
// if (pos.path) {
|
|
149
|
+
// element = domTextEditor.querySelector(pos.path);
|
|
150
|
+
// if (pos.start) {
|
|
151
|
+
// let endPos = end - start;
|
|
152
|
+
// if (endPos > 0)
|
|
153
|
+
// end = pos.start + endPos;
|
|
154
|
+
// else
|
|
155
|
+
// end = pos.start;
|
|
156
|
+
// start = pos.start;
|
|
157
|
+
// }
|
|
158
|
+
// }
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (!element) continue;
|
|
162
|
+
let document = element.ownerDocument;
|
|
163
|
+
let mirrorDiv;
|
|
164
|
+
let id_mirror = element.getAttribute("mirror_id");
|
|
165
|
+
if (!id_mirror) {
|
|
166
|
+
id_mirror = uuid.generate(8);
|
|
167
|
+
element.setAttribute("mirror_id", id_mirror);
|
|
168
|
+
|
|
169
|
+
mirrorDiv = document.createElement("cursor-container");
|
|
170
|
+
mirrorDiv.id = id_mirror;
|
|
171
|
+
mirrorDiv.className = environment_prod
|
|
172
|
+
? "mirror mirror_scroll mirror_color"
|
|
173
|
+
: "mirror mirror_scroll";
|
|
174
|
+
mirrorDiv.contentEditable = false;
|
|
175
|
+
mirrorDiv.element = element;
|
|
176
|
+
if (element.tagName === "HTML")
|
|
177
|
+
element.insertAdjacentElement("beforeend", mirrorDiv);
|
|
178
|
+
else element.insertAdjacentElement("afterend", mirrorDiv);
|
|
179
|
+
// let parent = element.parentElement;
|
|
180
|
+
// let parentComputed = getComputedStyle(parent);
|
|
181
|
+
// let parentStylePosition = parentComputed['position'];
|
|
182
|
+
// if (parentStylePosition == 'static')
|
|
183
|
+
// parent.style.position = 'relative';
|
|
184
|
+
// let parentStyleDisplay = parentComputed['display'];
|
|
185
|
+
// if (parentStyleDisplay == 'inline')
|
|
186
|
+
// parent.style.display = 'inline';
|
|
187
|
+
|
|
188
|
+
_initEvents(element);
|
|
189
|
+
initDocument(document);
|
|
190
|
+
} else mirrorDiv = document.getElementById(id_mirror);
|
|
191
|
+
|
|
192
|
+
let computed = getComputedStyle(element);
|
|
193
|
+
let rect = element.getBoundingClientRect();
|
|
194
|
+
|
|
195
|
+
let borderWidth = parseInt(computed["border-width"]) * 2;
|
|
196
|
+
let scrollBarWidth = 0;
|
|
197
|
+
if (element.clientWidth != 0)
|
|
198
|
+
scrollBarWidth =
|
|
199
|
+
element.offsetWidth - element.clientWidth - borderWidth;
|
|
200
|
+
|
|
201
|
+
let style = mirrorDiv.style;
|
|
202
|
+
style.position = "absolute";
|
|
203
|
+
style.width = rect.width + "px";
|
|
204
|
+
style.height = rect.height + "px";
|
|
205
|
+
style.margin = "0px";
|
|
206
|
+
style.borderColor = "transparent";
|
|
207
|
+
style.outlineColor = "transparent";
|
|
208
|
+
|
|
209
|
+
style.overflowX = computed["overflowX"];
|
|
210
|
+
style.overflowY = computed["overflowY"];
|
|
211
|
+
style.padding = computed["padding"];
|
|
212
|
+
style.border = computed["border"];
|
|
213
|
+
style.outline = computed["outline"];
|
|
214
|
+
|
|
215
|
+
if (
|
|
216
|
+
element.parentElement &&
|
|
217
|
+
element.parentElement.style["display"] == "inline"
|
|
218
|
+
) {
|
|
219
|
+
style.top = element.clientTop + "px";
|
|
220
|
+
style.left = element.clientLeft + "px";
|
|
221
|
+
} else {
|
|
222
|
+
style.top = element.offsetTop + "px";
|
|
223
|
+
style.left = element.offsetLeft + "px";
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (contenteditable) {
|
|
227
|
+
const actualStyles = getActualStyles(element);
|
|
228
|
+
for (const [prop, value] of Object.entries(actualStyles)) {
|
|
229
|
+
style[prop] = value;
|
|
230
|
+
}
|
|
231
|
+
} else {
|
|
232
|
+
let properties = [
|
|
233
|
+
"boxSizing",
|
|
234
|
+
"fontStyle",
|
|
235
|
+
"fontVariant",
|
|
236
|
+
"fontWeight",
|
|
237
|
+
"fontStretch",
|
|
238
|
+
"fontSize",
|
|
239
|
+
"fontFamily",
|
|
240
|
+
"letterSpacing",
|
|
241
|
+
"lineHeight",
|
|
242
|
+
"textAlign",
|
|
243
|
+
"textTransform",
|
|
244
|
+
"textIndent",
|
|
245
|
+
"textDecoration",
|
|
246
|
+
"textRendering",
|
|
247
|
+
"textTransform",
|
|
248
|
+
"textIndent",
|
|
249
|
+
"overflowWrap",
|
|
250
|
+
"tabSize",
|
|
251
|
+
"webkitWritingMode",
|
|
252
|
+
"whiteSpace",
|
|
253
|
+
"wordSpacing"
|
|
254
|
+
];
|
|
255
|
+
properties.forEach(function (prop) {
|
|
256
|
+
style[prop] = computed[prop];
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
let paddingBottom = parseFloat(
|
|
260
|
+
computed["padding-bottom"].replace("px", "")
|
|
261
|
+
);
|
|
262
|
+
style.height = rect.height - paddingBottom + "px";
|
|
263
|
+
|
|
264
|
+
if (element.nodeName === "INPUT") style.whiteSpace = "pre";
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
let cursor, cursorFlag;
|
|
268
|
+
let details = { array, object, key };
|
|
269
|
+
if (socket_id) {
|
|
270
|
+
let selection_user = mirrorDiv.querySelector(
|
|
271
|
+
`selection[socket_id="${socket_id}"]`
|
|
272
|
+
);
|
|
273
|
+
if (selection_user && selection_user.details != details)
|
|
274
|
+
selection_user.remove();
|
|
275
|
+
|
|
276
|
+
selection_user = document.createElement("selection");
|
|
277
|
+
selection_user.setAttribute("socket_id", socket_id);
|
|
278
|
+
selection_user.style["position"] = "absolute";
|
|
279
|
+
let style_mirror = getComputedStyle(mirrorDiv);
|
|
280
|
+
selection_user.style["top"] = style_mirror.paddingTop;
|
|
281
|
+
selection_user.style["left"] = style_mirror.paddingLeft;
|
|
282
|
+
selection_user.style["width"] =
|
|
283
|
+
mirrorDiv.clientWidth -
|
|
284
|
+
parseInt(computed["padding-left"]) -
|
|
285
|
+
parseInt(computed["padding-right"]) -
|
|
286
|
+
scrollBarWidth +
|
|
287
|
+
"px";
|
|
288
|
+
selection_user.details = { array, object, key };
|
|
289
|
+
|
|
290
|
+
let value_element =
|
|
291
|
+
["TEXTAREA", "INPUT"].indexOf(element.nodeName) == -1
|
|
292
|
+
? element.innerHTML
|
|
293
|
+
: element.value;
|
|
294
|
+
if (contenteditable) {
|
|
295
|
+
selection_user.innerHTML = value_element.substring(0, start);
|
|
296
|
+
} else
|
|
297
|
+
selection_user.textContent = value_element.substring(0, start);
|
|
298
|
+
|
|
299
|
+
let selectionElement;
|
|
300
|
+
if (contenteditable) {
|
|
301
|
+
selectionElement = getElementPosition(
|
|
302
|
+
domTextEditor.htmlString,
|
|
303
|
+
selection.start,
|
|
304
|
+
selection.end
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
let lastChildTagName, lastChildElement;
|
|
309
|
+
if (
|
|
310
|
+
selectionElement &&
|
|
311
|
+
selectionElement.element &&
|
|
312
|
+
selectionElement.position !== "afterend"
|
|
313
|
+
) {
|
|
314
|
+
lastChildElement = selection_user.lastElementChild;
|
|
315
|
+
if (lastChildElement)
|
|
316
|
+
lastChildTagName = lastChildElement.tagName.toLowerCase();
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
let selectedText = document.createElement("selected-text");
|
|
320
|
+
selectedText.style["backgroundColor"] = selection.background;
|
|
321
|
+
|
|
322
|
+
let setCursorInLastChild = false;
|
|
323
|
+
if (contenteditable) {
|
|
324
|
+
if (lastChildTagName && start !== end) {
|
|
325
|
+
let selectedTextFrag =
|
|
326
|
+
document.createElement("selected-text");
|
|
327
|
+
selectedTextFrag.style["backgroundColor"] =
|
|
328
|
+
selection.background;
|
|
329
|
+
|
|
330
|
+
const closingTagRegex = new RegExp(
|
|
331
|
+
"</\\s*" + lastChildTagName + "\\s*>",
|
|
332
|
+
"i"
|
|
333
|
+
);
|
|
334
|
+
let secondHalfString =
|
|
335
|
+
value_element.substring(start, end) || "";
|
|
336
|
+
const closingTagMatch =
|
|
337
|
+
secondHalfString.match(closingTagRegex);
|
|
338
|
+
// console.log('secondHalfString', secondHalfString)
|
|
339
|
+
|
|
340
|
+
let closingTagEnd;
|
|
341
|
+
if (closingTagMatch) {
|
|
342
|
+
closingTagEnd =
|
|
343
|
+
closingTagMatch.index + closingTagMatch[0].length;
|
|
344
|
+
// console.log('closingTagEnd', closingTagEnd)
|
|
345
|
+
|
|
346
|
+
selectedTextFrag.innerHTML =
|
|
347
|
+
secondHalfString.substring(0, closingTagEnd) || "";
|
|
348
|
+
lastChildElement.appendChild(selectedTextFrag);
|
|
349
|
+
// console.log('selectedTextFrag', selectedTextFrag)
|
|
350
|
+
|
|
351
|
+
selectedText.innerHTML =
|
|
352
|
+
value_element.substring(
|
|
353
|
+
start + closingTagEnd,
|
|
354
|
+
end
|
|
355
|
+
) || "";
|
|
356
|
+
// console.log('selectedText', selectedText)
|
|
357
|
+
} else {
|
|
358
|
+
selectedText.innerHTML =
|
|
359
|
+
value_element.substring(start, end) || "";
|
|
360
|
+
setCursorInLastChild = true;
|
|
361
|
+
}
|
|
362
|
+
} else {
|
|
363
|
+
if (lastChildTagName) setCursorInLastChild = true;
|
|
364
|
+
|
|
365
|
+
selectedText.innerHTML =
|
|
366
|
+
value_element.substring(start, end) || "";
|
|
367
|
+
}
|
|
368
|
+
} else {
|
|
369
|
+
selectedText.textContent =
|
|
370
|
+
value_element.substring(start, end) || "";
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
cursor = document.createElement("cursor");
|
|
374
|
+
cursor.setAttribute("socket_id", socket_id);
|
|
375
|
+
cursor.dataset.start = start;
|
|
376
|
+
cursor.dataset.end = end;
|
|
377
|
+
cursor.setAttribute("user-name", selection.userName);
|
|
378
|
+
cursor.setAttribute("user-background", selection.background);
|
|
379
|
+
cursor.setAttribute("user-color", selection.color);
|
|
380
|
+
cursor.style["background"] = selection.background;
|
|
381
|
+
cursor.details = { array, object, key };
|
|
382
|
+
cursor.textContent = " ";
|
|
383
|
+
|
|
384
|
+
cursorFlag = document.createElement("cursor-flag");
|
|
385
|
+
cursorFlag.setAttribute("array", "users");
|
|
386
|
+
cursorFlag.setAttribute("object", selection.user_id);
|
|
387
|
+
cursorFlag.setAttribute("key", "name");
|
|
388
|
+
cursorFlag.style["background"] = selection.background;
|
|
389
|
+
cursorFlag.innerHTML = selection.userName;
|
|
390
|
+
cursor.appendChild(cursorFlag);
|
|
391
|
+
|
|
392
|
+
if (setCursorInLastChild) {
|
|
393
|
+
lastChildElement.appendChild(selectedText);
|
|
394
|
+
lastChildElement.appendChild(cursor);
|
|
395
|
+
} else {
|
|
396
|
+
selection_user.appendChild(selectedText);
|
|
397
|
+
selection_user.appendChild(cursor);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
let span_end = document.createElement("span-end");
|
|
401
|
+
let value_end = value_element.substring(end) || "";
|
|
402
|
+
if (contenteditable) span_end.innerHTML = value_end;
|
|
403
|
+
else span_end.textContent = value_end;
|
|
404
|
+
|
|
405
|
+
selection_user.appendChild(span_end);
|
|
406
|
+
|
|
407
|
+
mirrorDiv.appendChild(selection_user);
|
|
408
|
+
}
|
|
409
|
+
scrollMirror(element);
|
|
410
|
+
}
|
|
323
411
|
}
|
|
324
412
|
|
|
325
413
|
function getActualStyles(element) {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
414
|
+
const actualStyles = {};
|
|
415
|
+
|
|
416
|
+
// Copy styles that are directly applied to the element (inline styles)
|
|
417
|
+
for (const prop in element.style) {
|
|
418
|
+
if (element.style[prop]) {
|
|
419
|
+
actualStyles[prop] = element.style[prop];
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Get styles from classes, IDs, attributes, and tag names
|
|
424
|
+
for (const sheet of document.styleSheets) {
|
|
425
|
+
try {
|
|
426
|
+
for (const rule of sheet.cssRules) {
|
|
427
|
+
if (element.matches(rule.selectorText)) {
|
|
428
|
+
for (const prop of rule.style) {
|
|
429
|
+
actualStyles[prop] = rule.style.getPropertyValue(prop);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
} catch (e) {
|
|
434
|
+
console.warn("Unable to access stylesheet:", e);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
return actualStyles;
|
|
351
439
|
}
|
|
352
440
|
|
|
353
441
|
function updateCursors(element) {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
}
|
|
442
|
+
let mirrorDivs = [];
|
|
443
|
+
let id_mirror = element.getAttribute("mirror_id");
|
|
444
|
+
if (id_mirror) {
|
|
445
|
+
let mirrorDiv = element.ownerDocument.getElementById(id_mirror);
|
|
446
|
+
if (mirrorDiv) mirrorDivs = [mirrorDiv];
|
|
447
|
+
} else {
|
|
448
|
+
mirrorDivs = element.querySelectorAll(".mirror");
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
for (let mirrorDiv of mirrorDivs) {
|
|
452
|
+
element = mirrorDiv.element;
|
|
453
|
+
let cursors = mirrorDiv.querySelectorAll("cursor");
|
|
454
|
+
|
|
455
|
+
cursors.forEach(function (child_cursor, index, array) {
|
|
456
|
+
let socketId = child_cursor.getAttribute("socket_id");
|
|
457
|
+
let userName = child_cursor.getAttribute("user-name");
|
|
458
|
+
let background = child_cursor.getAttribute("user-background");
|
|
459
|
+
let color = child_cursor.getAttribute("user-color");
|
|
460
|
+
let start = child_cursor.getAttribute("data-start");
|
|
461
|
+
let end = child_cursor.getAttribute("data-end");
|
|
462
|
+
let selection = {
|
|
463
|
+
element: [element],
|
|
464
|
+
start,
|
|
465
|
+
end,
|
|
466
|
+
socketId,
|
|
467
|
+
userName,
|
|
468
|
+
background,
|
|
469
|
+
color
|
|
470
|
+
};
|
|
471
|
+
drawCursors(selection);
|
|
472
|
+
});
|
|
473
|
+
}
|
|
387
474
|
}
|
|
388
475
|
|
|
389
476
|
function removeCursor(socketId) {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
477
|
+
let elements = document.querySelectorAll(`cursor[socket_id="${socketId}"]`);
|
|
478
|
+
elements.forEach(function (element, index, array) {
|
|
479
|
+
element.parentNode.removeChild(element);
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
let sel_elements = document.querySelectorAll(
|
|
483
|
+
`selection[socket_id="${socketId}"]`
|
|
484
|
+
);
|
|
485
|
+
sel_elements.forEach(function (sel_element, index, array) {
|
|
486
|
+
sel_element.parentNode.removeChild(sel_element);
|
|
487
|
+
});
|
|
399
488
|
}
|
|
400
489
|
|
|
401
490
|
function _initEvents(element) {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
491
|
+
element.addEventListener("keydown", function (event) {
|
|
492
|
+
scrollMirror(element);
|
|
493
|
+
});
|
|
405
494
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
495
|
+
element.addEventListener("scroll", function () {
|
|
496
|
+
scrollMirror(element);
|
|
497
|
+
});
|
|
409
498
|
|
|
410
|
-
|
|
499
|
+
initResizeObserver(element);
|
|
411
500
|
}
|
|
412
501
|
|
|
413
502
|
function scrollMirror(element) {
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
503
|
+
let id_mirror = element.getAttribute("mirror_id");
|
|
504
|
+
let elementMirror = document.getElementById(id_mirror);
|
|
505
|
+
if (elementMirror)
|
|
506
|
+
elementMirror.scrollTo(element.scrollLeft, element.scrollTop);
|
|
418
507
|
}
|
|
419
508
|
|
|
420
|
-
|
|
421
509
|
function initResizeObserver(element) {
|
|
422
|
-
|
|
423
|
-
|
|
510
|
+
const watch = new ResizeObserver(() => updateCursors(element));
|
|
511
|
+
watch.observe(element);
|
|
424
512
|
}
|
|
425
513
|
|
|
426
514
|
function sendPosition(info) {
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
515
|
+
try {
|
|
516
|
+
socket.send({
|
|
517
|
+
method: "cursor",
|
|
518
|
+
data: {
|
|
519
|
+
array: info.array,
|
|
520
|
+
object: info.object,
|
|
521
|
+
key: info.key,
|
|
522
|
+
start: info.start,
|
|
523
|
+
end: info.end,
|
|
524
|
+
color: info.color || localStorage.getItem("cursorColor"),
|
|
525
|
+
background:
|
|
526
|
+
info.background ||
|
|
527
|
+
localStorage.getItem("cursorBackground") ||
|
|
528
|
+
cursorBackground,
|
|
529
|
+
userName:
|
|
530
|
+
info.userName ||
|
|
531
|
+
localStorage.getItem("userName") ||
|
|
532
|
+
clientId,
|
|
533
|
+
user_id:
|
|
534
|
+
info.user_id || localStorage.getItem("user_id") || clientId
|
|
535
|
+
},
|
|
536
|
+
broadcastBrowser: true
|
|
537
|
+
});
|
|
538
|
+
} catch (e) {
|
|
539
|
+
console.error(e);
|
|
540
|
+
}
|
|
447
541
|
}
|
|
448
542
|
|
|
449
|
-
|
|
450
|
-
socket.
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
else
|
|
458
|
-
removeCursor(selection.socketId);
|
|
543
|
+
socket.listen("cursor", function (response) {
|
|
544
|
+
// if (socket.has(selection.socketId)) return;
|
|
545
|
+
let selection = response.data;
|
|
546
|
+
selection.socketId = response.socketId;
|
|
547
|
+
selection.frameId = response.frameId;
|
|
548
|
+
if (selection.start != null && selection.end != null)
|
|
549
|
+
drawCursors(selection);
|
|
550
|
+
else removeCursor(selection.socketId);
|
|
459
551
|
});
|
|
460
552
|
|
|
461
553
|
observer.init({
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
554
|
+
name: "CoCreateCursor",
|
|
555
|
+
types: ["addedNodes"],
|
|
556
|
+
selector: "[array][object][key]",
|
|
557
|
+
callback: function (mutation) {
|
|
558
|
+
initElement(mutation.target);
|
|
559
|
+
}
|
|
468
560
|
});
|
|
469
561
|
|
|
470
562
|
observer.init({
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
563
|
+
name: "CoCreateCursorAtt",
|
|
564
|
+
types: ["attributes"],
|
|
565
|
+
attributeFilter: getAttributeNames(["array", "object", "key"]),
|
|
566
|
+
callback: function (mutation) {
|
|
567
|
+
initElement(mutation.target);
|
|
568
|
+
}
|
|
477
569
|
});
|
|
478
570
|
|
|
479
571
|
init();
|