@cocreate/text 1.26.5 → 1.27.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 +15 -0
- package/demo/test-webpage.html +1 -3
- package/docs/index.html +9 -23
- package/package.json +8 -8
- package/src/index.js +7 -1
- package/src/updateDom.js +209 -202
package/CHANGELOG.md
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
+
# [1.27.0](https://github.com/CoCreate-app/CoCreate-text/compare/v1.26.5...v1.27.0) (2024-06-12)
|
2
|
+
|
3
|
+
|
4
|
+
### Bug Fixes
|
5
|
+
|
6
|
+
* bump cocreate dependencies ([5f04198](https://github.com/CoCreate-app/CoCreate-text/commit/5f0419854eca2a8707a0140d31547308fb1ae604))
|
7
|
+
* dispatch input event when value is set in contenteditable elements ([1509928](https://github.com/CoCreate-app/CoCreate-text/commit/1509928a5df4d2d70d7d0d06d1124ae924901ae6))
|
8
|
+
* formating ([5f751e7](https://github.com/CoCreate-app/CoCreate-text/commit/5f751e718f71b07b754022fea8d4f55e84b11158))
|
9
|
+
* svg icon class ([3716b92](https://github.com/CoCreate-app/CoCreate-text/commit/3716b929a9806d585ba16c40c015a3cb469d4ab4))
|
10
|
+
|
11
|
+
|
12
|
+
### Features
|
13
|
+
|
14
|
+
* set activeSelection on document ([e85b59a](https://github.com/CoCreate-app/CoCreate-text/commit/e85b59a89af4a2951e63600e88ea3a7927b33581))
|
15
|
+
|
1
16
|
## [1.26.5](https://github.com/CoCreate-app/CoCreate-text/compare/v1.26.4...v1.26.5) (2024-05-08)
|
2
17
|
|
3
18
|
|
package/demo/test-webpage.html
CHANGED
@@ -348,9 +348,7 @@
|
|
348
348
|
href="https://github.com/CoCreate-app/CoCreate-website/tree/master/src/index.html?message=docs%3A%20describe%20your%20change..."
|
349
349
|
target="_blank"
|
350
350
|
class="position:fixed bottom:15px right:15px padding:15px background:dodgerblue color:#fff font-size:1.5rem grow-hover border-radius:50% border-width:0 box-shadow:0px_2px_10px_0px_rgba(0,_0,_0,_0.4)">
|
351
|
-
<i
|
352
|
-
class="height:20px fill:#505050"
|
353
|
-
src="/assets/svg/pencil-alt.svg"></i>
|
351
|
+
<i src="/assets/svg/pencil-alt.svg"></i>
|
354
352
|
</button>
|
355
353
|
</main>
|
356
354
|
|
package/docs/index.html
CHANGED
@@ -72,41 +72,31 @@
|
|
72
72
|
href="https://github.com/CoCreate-app/CoCreate-text"
|
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>
|
@@ -224,7 +214,7 @@
|
|
224
214
|
toggle="code-height"
|
225
215
|
toggle-selector="#demo-code"
|
226
216
|
><i
|
227
|
-
class="height:18px
|
217
|
+
class="height:18px"
|
228
218
|
src="/assets/svg/eye.svg"></i
|
229
219
|
></a>
|
230
220
|
<a
|
@@ -235,18 +225,14 @@
|
|
235
225
|
hide="#eye-slash"
|
236
226
|
toggle="code-height"
|
237
227
|
toggle-selector="#demo-code"
|
238
|
-
><i
|
239
|
-
class="height:20px fill:#505050"
|
240
|
-
src="/assets/svg/eye-slash.svg"></i
|
228
|
+
><i src="/assets/svg/eye-slash.svg"></i
|
241
229
|
></a>
|
242
230
|
<a
|
243
231
|
class="margin-right:10px"
|
244
232
|
id="code"
|
245
233
|
show="#code-slash"
|
246
234
|
hide="#code, #demo-code"
|
247
|
-
><i
|
248
|
-
class="height:20px fill:#505050"
|
249
|
-
src="/assets/svg/code.svg"></i
|
235
|
+
><i src="/assets/svg/code.svg"></i
|
250
236
|
></a>
|
251
237
|
<a
|
252
238
|
class="margin-right:10px"
|
@@ -255,7 +241,7 @@
|
|
255
241
|
show="#code, #demo-code"
|
256
242
|
hide="#code-slash"
|
257
243
|
><i
|
258
|
-
class="
|
244
|
+
class="height:18px"
|
259
245
|
src="/assets/svg/code.svg"></i
|
260
246
|
></a>
|
261
247
|
<a
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@cocreate/text",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.27.0",
|
4
4
|
"description": "A simple text component in vanilla javascript. Easily configured using HTML5 attributes and/or JavaScript API.",
|
5
5
|
"keywords": [
|
6
6
|
"text",
|
@@ -58,12 +58,12 @@
|
|
58
58
|
"webpack-log": "^3.0.1"
|
59
59
|
},
|
60
60
|
"dependencies": {
|
61
|
-
"@cocreate/actions": "^1.18.
|
62
|
-
"@cocreate/crdt": "^1.27.
|
63
|
-
"@cocreate/cursors": "^1.23.
|
64
|
-
"@cocreate/observer": "^1.16.
|
65
|
-
"@cocreate/selection": "^1.12.
|
66
|
-
"@cocreate/utils": "^1.33.
|
67
|
-
"@cocreate/uuid": "^1.11.
|
61
|
+
"@cocreate/actions": "^1.18.2",
|
62
|
+
"@cocreate/crdt": "^1.27.4",
|
63
|
+
"@cocreate/cursors": "^1.23.4",
|
64
|
+
"@cocreate/observer": "^1.16.1",
|
65
|
+
"@cocreate/selection": "^1.12.2",
|
66
|
+
"@cocreate/utils": "^1.33.7",
|
67
|
+
"@cocreate/uuid": "^1.11.2"
|
68
68
|
}
|
69
69
|
}
|
package/src/index.js
CHANGED
@@ -128,8 +128,10 @@ function initDocument(doc) {
|
|
128
128
|
let element = doc.activeElement;
|
129
129
|
let { isRealtime, isCrdt } = getAttributes(element);
|
130
130
|
|
131
|
-
if (isRealtime && isCrdt)
|
131
|
+
if (isRealtime && isCrdt) {
|
132
|
+
doc.activeSelection = getSelection(element);
|
132
133
|
sendPosition(element);
|
134
|
+
}
|
133
135
|
});
|
134
136
|
}
|
135
137
|
}
|
@@ -294,6 +296,7 @@ export function sendPosition(element) {
|
|
294
296
|
if (JSON.stringify(currentPosition) === JSON.stringify(previousPosition))
|
295
297
|
return;
|
296
298
|
previousPosition = currentPosition;
|
299
|
+
// console.log('activeElement: ', element)
|
297
300
|
element.activeElement = element;
|
298
301
|
window.activeElement = element;
|
299
302
|
cursors.sendPosition({ array, object, key, start, end });
|
@@ -307,6 +310,9 @@ function updateText({ element, value, start, end, range, undoRedo }) {
|
|
307
310
|
if (element.tagName == 'HTML' && !element.hasAttribute('array'))
|
308
311
|
element = element.ownerDocument.defaultView.frameElement;
|
309
312
|
}
|
313
|
+
|
314
|
+
if (!element) return
|
315
|
+
|
310
316
|
const { array, object, key, isCrud, isCrdt, isSave } = getAttributes(element);
|
311
317
|
if (isCrdt == "false" || !array || !object || !key) return;
|
312
318
|
|
package/src/updateDom.js
CHANGED
@@ -1,209 +1,216 @@
|
|
1
|
-
import {sendPosition, _dispatchInputEvent} from './index';
|
2
|
-
import {getSelection, processSelection, getElementPosition} from '@cocreate/selection';
|
3
|
-
import {domParser} from '@cocreate/utils';
|
4
|
-
|
5
|
-
export function updateDom({domTextEditor, value, start, end, html}) {
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
let {element, path, position, type} = getElementPosition(domTextEditor.htmlString, start, end);
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
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
|
-
|
1
|
+
import { sendPosition, _dispatchInputEvent } from './index';
|
2
|
+
import { getSelection, processSelection, getElementPosition } from '@cocreate/selection';
|
3
|
+
import { domParser } from '@cocreate/utils';
|
4
|
+
|
5
|
+
export function updateDom({ domTextEditor, value, start, end, html }) {
|
6
|
+
if (!domTextEditor.htmlString)
|
7
|
+
domTextEditor.htmlString = html;
|
8
|
+
if (start < 0 || start > domTextEditor.htmlString.length)
|
9
|
+
throw new Error('position is out of range');
|
10
|
+
|
11
|
+
let { element, path, position, type } = getElementPosition(domTextEditor.htmlString, start, end);
|
12
|
+
parseHtml(domTextEditor, html);
|
13
|
+
|
14
|
+
let domEl, oldEl, curCaret, newEl;
|
15
|
+
// console.log('element', element)
|
16
|
+
// console.log('path', path)
|
17
|
+
|
18
|
+
try {
|
19
|
+
newEl = domTextEditor.newHtml.querySelector(path);
|
20
|
+
} catch (err) {
|
21
|
+
console.log('error', err)
|
22
|
+
}
|
23
|
+
|
24
|
+
if (path && !newEl) {
|
25
|
+
let index
|
26
|
+
do {
|
27
|
+
index = path.lastIndexOf(' >')
|
28
|
+
if (index != -1)
|
29
|
+
path = path.slice(0, index)
|
30
|
+
newEl = domTextEditor.newHtml.querySelector(path);
|
31
|
+
} while (!newEl && index != -1)
|
32
|
+
}
|
33
|
+
|
34
|
+
if (!newEl) {
|
35
|
+
// console.log("no newEL", path)
|
36
|
+
newEl = domTextEditor.cloneNode(true);
|
37
|
+
if (html != undefined)
|
38
|
+
newEl.innerHTML = html;
|
39
|
+
else
|
40
|
+
newEl.innerHTML = domTextEditor.htmlString;
|
41
|
+
domEl = domTextEditor;
|
42
|
+
type = 'innerHTML';
|
43
|
+
}
|
44
|
+
else if (element.tagName == 'HTML') {
|
45
|
+
// console.log('element = html')
|
46
|
+
domEl = domTextEditor;
|
47
|
+
type = 'innerHTML';
|
48
|
+
}
|
49
|
+
else if (path) {
|
50
|
+
// console.log("else path", path)
|
51
|
+
domEl = domTextEditor.querySelector(path);
|
52
|
+
// if (!domEl || !oldEl){
|
53
|
+
// let eid = newEl.getAttribute('eid');
|
54
|
+
// if (!domEl && eid){
|
55
|
+
// domEl = domTextEditor.querySelector(`[eid='${eid}']`);
|
56
|
+
// }
|
57
|
+
// if (!oldEl && eid){
|
58
|
+
// oldEl = domTextEditor.oldHtml.querySelector(`[eid='${eid}']`);
|
59
|
+
// }
|
60
|
+
// }
|
61
|
+
}
|
62
|
+
|
63
|
+
if (!domEl) {
|
64
|
+
// console.log('no domEl')
|
65
|
+
let index
|
66
|
+
do {
|
67
|
+
index = path.lastIndexOf(' >')
|
68
|
+
if (index != -1)
|
69
|
+
path = path.slice(0, index)
|
70
|
+
domEl = domTextEditor.querySelector(path);
|
71
|
+
} while (!domEl && index != -1)
|
72
|
+
|
73
|
+
if (domEl) {
|
74
|
+
newEl = domTextEditor.newHtml.querySelector(path);
|
75
|
+
}
|
76
|
+
|
77
|
+
if (!domEl || !newEl) {
|
78
|
+
newEl = domTextEditor.cloneNode(true);
|
79
|
+
if (html != undefined)
|
80
|
+
newEl.innerHTML = html;
|
81
|
+
else
|
82
|
+
newEl.innerHTML = domTextEditor.htmlString;
|
83
|
+
domEl = domTextEditor;
|
84
|
+
type = 'innerHTML';
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
88
|
+
if (domEl && newEl) {
|
89
|
+
let activeElement = domEl.ownerDocument.activeElement;
|
90
|
+
if (activeElement == domEl)
|
91
|
+
curCaret = getSelection(activeElement);
|
92
|
+
else if (activeElement && activeElement.tagName == 'BODY')
|
93
|
+
curCaret = getSelection(domEl);
|
94
|
+
else
|
95
|
+
curCaret = getSelection(activeElement);
|
96
|
+
|
97
|
+
|
98
|
+
if (!value && type != 'isStartTag' && type != 'textNode') {
|
99
|
+
type = 'innerHTML';
|
100
|
+
}
|
101
|
+
|
102
|
+
// console.log('domEl', domEl)
|
103
|
+
// console.log('newEl', newEl)
|
104
|
+
if (start != end && type == 'innerHTML') {
|
105
|
+
domTextEditor.htmlString = html;
|
106
|
+
if (domEl.tagName != 'HTML') {
|
107
|
+
if (newEl.parentElement) {
|
108
|
+
domEl.parentElement.replaceChildren(...newEl.parentElement.childNodes);
|
109
|
+
// console.log('parent', domEl.parentElement)
|
110
|
+
} else {
|
111
|
+
domEl.replaceChildren(...newEl.childNodes);
|
112
|
+
// console.log('domEl', domEl)
|
113
|
+
}
|
114
|
+
}
|
115
|
+
else {
|
116
|
+
domEl.replaceChildren(...newEl.childNodes);
|
117
|
+
// console.log('Html tag', domEl)
|
118
|
+
}
|
119
|
+
if (curCaret && curCaret.range) {
|
120
|
+
curCaret.range.startContainer = domEl;
|
121
|
+
curCaret.range.endContainer = domEl;
|
122
|
+
}
|
123
|
+
}
|
124
|
+
else if (type == 'isStartTag') {
|
125
|
+
oldEl = domTextEditor.oldHtml.querySelector(path);
|
126
|
+
if (!oldEl && domEl.tagName == 'HTML')
|
127
|
+
oldEl = domTextEditor.oldHtml
|
128
|
+
assignAttributes(newEl, oldEl, domEl);
|
129
|
+
// console.log('isStartTag', domEl, newEl)
|
130
|
+
|
131
|
+
}
|
132
|
+
else if (type == 'insertAdjacent') {
|
133
|
+
domEl.insertAdjacentHTML(position, value);
|
134
|
+
// console.log('insertAdjacent', domEl, value)
|
135
|
+
}
|
136
|
+
else if (type == 'textNode') {
|
137
|
+
if (start != end)
|
138
|
+
domTextEditor.htmlString = html;
|
139
|
+
domEl.innerHTML = newEl.innerHTML;
|
140
|
+
// console.log('textnode', domEl.innerHTML, newEl.innerHTML)
|
141
|
+
|
142
|
+
}
|
143
|
+
else if (type == 'innerHTML') {
|
144
|
+
domEl.replaceChildren(...newEl.childNodes);
|
145
|
+
// console.log('innerHtml', domEl, newEl)
|
146
|
+
}
|
147
|
+
domTextEditor.htmlString = html;
|
148
|
+
|
149
|
+
if (curCaret && start >= 0 && end >= 0) {
|
150
|
+
if (curCaret.range && curCaret.range.startContainer == domEl) {
|
151
|
+
if (curCaret.start >= curCaret.range.startOffset) {
|
152
|
+
let p = processSelection(domEl, value, curCaret.start, curCaret.end, start, end, curCaret.range);
|
153
|
+
sendPosition(domEl);
|
154
|
+
_dispatchInputEvent(p.element, p.value, p.start, p.end, p.prev_start, p.prev_end);
|
155
|
+
}
|
156
|
+
} else {
|
157
|
+
let p = processSelection(domEl, value, curCaret.start, curCaret.end, start, end, curCaret.range);
|
158
|
+
_dispatchInputEvent(p.element, p.value, p.start, p.end, p.prev_start, p.prev_end);
|
159
|
+
|
160
|
+
}
|
161
|
+
} else {
|
162
|
+
_dispatchInputEvent(domTextEditor);
|
163
|
+
}
|
164
|
+
|
165
|
+
if (['HTML', 'HEAD', 'BODY', 'SCRIPT'].includes(newEl.tagName)) {
|
166
|
+
let scripts;
|
167
|
+
if (newEl.tagName == 'SCRIPT') {
|
168
|
+
scripts = [newEl];
|
169
|
+
}
|
170
|
+
else {
|
171
|
+
scripts = domEl.querySelectorAll('script');
|
172
|
+
}
|
173
|
+
for (let script of scripts) {
|
174
|
+
let newScript = domEl.ownerDocument.createElement('script');
|
175
|
+
for (let attribute of script.attributes) {
|
176
|
+
newScript.setAttribute(attribute.name, attribute.value);
|
177
|
+
}
|
178
|
+
newScript.innerHTML = script.innerHTML;
|
179
|
+
script.replaceWith(newScript);
|
180
|
+
}
|
181
|
+
}
|
182
|
+
}
|
176
183
|
}
|
177
184
|
|
178
185
|
function parseHtml(domTextEditor, html) {
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
+
var dom = domParser(html);
|
187
|
+
if (domTextEditor.newHtml) {
|
188
|
+
domTextEditor.oldHtml = domTextEditor.newHtml;
|
189
|
+
} else {
|
190
|
+
domTextEditor.oldHtml = dom;
|
191
|
+
}
|
192
|
+
domTextEditor.newHtml = dom;
|
186
193
|
}
|
187
194
|
|
188
195
|
function assignAttributes(newEl, oldEl, domEl) {
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
196
|
+
if (!oldEl) return;
|
197
|
+
for (let newElAtt of newEl.attributes) {
|
198
|
+
if (!oldEl.attributes[newElAtt.name] || oldEl.attributes[newElAtt.name].value !== newElAtt.value)
|
199
|
+
try {
|
200
|
+
domEl.setAttribute(newElAtt.name, newElAtt.value);
|
201
|
+
}
|
202
|
+
catch (err) {
|
203
|
+
throw new Error("assignAttributes: " + err.message, err.name);
|
204
|
+
}
|
205
|
+
}
|
206
|
+
|
207
|
+
if (newEl.attributes.length !== oldEl.attributes.length) {
|
208
|
+
for (let i = 0, len = oldEl.attributes.length; i < len; i++) {
|
209
|
+
let oldElAtt = oldEl.attributes[i];
|
210
|
+
if (!newEl.attributes[oldElAtt.name]) {
|
211
|
+
domEl.removeAttribute(oldElAtt.name);
|
212
|
+
i--, len--;
|
213
|
+
}
|
214
|
+
}
|
215
|
+
}
|
209
216
|
}
|