@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 +30 -0
- package/demo/index.html +1 -3
- package/demo/potential-selection-method.html +168 -0
- package/docs/index.html +9 -23
- package/package.json +8 -8
- package/prettier.config.js +16 -0
- package/src/index.css +10 -2
- package/src/index.js +183 -76
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
|
@@ -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
|
|
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="
|
|
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.
|
|
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.
|
|
63
|
-
"@cocreate/observer": "^1.16.
|
|
64
|
-
"@cocreate/random-color": "^1.10.
|
|
65
|
-
"@cocreate/selection": "^1.12.
|
|
66
|
-
"@cocreate/socket-client": "^1.39.
|
|
67
|
-
"@cocreate/utils": "^1.33.
|
|
68
|
-
"@cocreate/uuid": "^1.11.
|
|
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
|
}
|
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(
|
|
89
|
+
selector += ':not([actions])';
|
|
92
90
|
elements = document.querySelectorAll(selector);
|
|
93
91
|
}
|
|
94
92
|
for (let element of elements) {
|
|
95
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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.
|
|
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 (
|
|
189
|
-
|
|
190
|
-
|
|
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
|
-
|
|
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
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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 (
|
|
220
|
-
selection_user
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|
-
|
|
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 = "
|
|
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
|
-
|
|
487
|
+
selector: '[array][object][key]',
|
|
381
488
|
callback: function (mutation) {
|
|
382
489
|
initElement(mutation.target);
|
|
383
490
|
}
|