@y/y 14.0.0-16 → 14.0.0-17
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/dist/{index-DyTeTfmj.js → index-BV-j5wdP.js} +2 -2
- package/dist/{index-R7GxO-36.js.map → index-BV-j5wdP.js.map} +1 -1
- package/dist/{internals.mjs → internals.js} +1 -1
- package/dist/internals.js.map +1 -0
- package/dist/{testHelper.mjs → testHelper.js} +2 -2
- package/dist/testHelper.js.map +1 -0
- package/dist/{yjs.mjs → yjs.js} +2 -2
- package/dist/yjs.js.map +1 -0
- package/package.json +8 -17
- package/dist/Skip-j0kX7pdq.js +0 -12173
- package/dist/Skip-j0kX7pdq.js.map +0 -1
- package/dist/index-DyTeTfmj.js.map +0 -1
- package/dist/index-R7GxO-36.js +0 -165
- package/dist/internals.cjs +0 -286
- package/dist/internals.cjs.map +0 -1
- package/dist/internals.mjs.map +0 -1
- package/dist/testHelper.cjs +0 -780
- package/dist/testHelper.cjs.map +0 -1
- package/dist/testHelper.mjs.map +0 -1
- package/dist/yjs.cjs +0 -151
- package/dist/yjs.cjs.map +0 -1
- package/dist/yjs.mjs.map +0 -1
- package/src/index.js +0 -153
- package/src/internals.js +0 -44
- package/src/structs/AbstractStruct.js +0 -59
- package/src/structs/ContentAny.js +0 -115
- package/src/structs/ContentBinary.js +0 -93
- package/src/structs/ContentDeleted.js +0 -101
- package/src/structs/ContentDoc.js +0 -141
- package/src/structs/ContentEmbed.js +0 -98
- package/src/structs/ContentFormat.js +0 -105
- package/src/structs/ContentJSON.js +0 -119
- package/src/structs/ContentString.js +0 -113
- package/src/structs/ContentType.js +0 -176
- package/src/structs/GC.js +0 -80
- package/src/structs/Item.js +0 -845
- package/src/structs/Skip.js +0 -75
- package/src/types/AbstractType.js +0 -1434
- package/src/types/YArray.js +0 -270
- package/src/types/YMap.js +0 -244
- package/src/types/YText.js +0 -934
- package/src/types/YXmlElement.js +0 -227
- package/src/types/YXmlFragment.js +0 -266
- package/src/types/YXmlHook.js +0 -68
- package/src/types/YXmlText.js +0 -66
- package/src/utils/AbstractConnector.js +0 -25
- package/src/utils/AttributionManager.js +0 -619
- package/src/utils/Doc.js +0 -372
- package/src/utils/EventHandler.js +0 -87
- package/src/utils/ID.js +0 -89
- package/src/utils/IdMap.js +0 -629
- package/src/utils/IdSet.js +0 -823
- package/src/utils/RelativePosition.js +0 -352
- package/src/utils/Snapshot.js +0 -220
- package/src/utils/StructSet.js +0 -137
- package/src/utils/StructStore.js +0 -289
- package/src/utils/Transaction.js +0 -489
- package/src/utils/UndoManager.js +0 -391
- package/src/utils/UpdateDecoder.js +0 -281
- package/src/utils/UpdateEncoder.js +0 -320
- package/src/utils/YEvent.js +0 -216
- package/src/utils/delta-helpers.js +0 -54
- package/src/utils/encoding.js +0 -623
- package/src/utils/isParentOf.js +0 -21
- package/src/utils/logging.js +0 -21
- package/src/utils/types.js +0 -28
- package/src/utils/updates.js +0 -715
- package/tests/testHelper.js +0 -600
package/src/types/YText.js
DELETED
|
@@ -1,934 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module YText
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
AbstractType,
|
|
7
|
-
getItemCleanStart,
|
|
8
|
-
getState,
|
|
9
|
-
createID,
|
|
10
|
-
YTextRefID,
|
|
11
|
-
transact,
|
|
12
|
-
ContentEmbed,
|
|
13
|
-
GC,
|
|
14
|
-
ContentFormat,
|
|
15
|
-
ContentString,
|
|
16
|
-
iterateStructsByIdSet,
|
|
17
|
-
findMarker,
|
|
18
|
-
typeMapDelete,
|
|
19
|
-
typeMapSet,
|
|
20
|
-
typeMapGet,
|
|
21
|
-
typeMapGetAll,
|
|
22
|
-
updateMarkerChanges,
|
|
23
|
-
ContentType,
|
|
24
|
-
warnPrematureAccess,
|
|
25
|
-
noAttributionsManager, AbstractAttributionManager, ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Item, Transaction, // eslint-disable-line
|
|
26
|
-
createIdSet,
|
|
27
|
-
equalAttrs
|
|
28
|
-
} from '../internals.js'
|
|
29
|
-
|
|
30
|
-
import * as math from 'lib0/math'
|
|
31
|
-
import * as traits from 'lib0/traits'
|
|
32
|
-
import * as map from 'lib0/map'
|
|
33
|
-
import * as error from 'lib0/error'
|
|
34
|
-
|
|
35
|
-
export class ItemTextListPosition {
|
|
36
|
-
/**
|
|
37
|
-
* @param {Item|null} left
|
|
38
|
-
* @param {Item|null} right
|
|
39
|
-
* @param {number} index
|
|
40
|
-
* @param {Map<string,any>} currentAttributes
|
|
41
|
-
* @param {AbstractAttributionManager} am
|
|
42
|
-
*/
|
|
43
|
-
constructor (left, right, index, currentAttributes, am) {
|
|
44
|
-
this.left = left
|
|
45
|
-
this.right = right
|
|
46
|
-
this.index = index
|
|
47
|
-
this.currentAttributes = currentAttributes
|
|
48
|
-
this.am = am
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Only call this if you know that this.right is defined
|
|
53
|
-
*/
|
|
54
|
-
forward () {
|
|
55
|
-
if (this.right === null) {
|
|
56
|
-
error.unexpectedCase()
|
|
57
|
-
}
|
|
58
|
-
switch (this.right.content.constructor) {
|
|
59
|
-
case ContentFormat:
|
|
60
|
-
if (!this.right.deleted) {
|
|
61
|
-
updateCurrentAttributes(this.currentAttributes, /** @type {ContentFormat} */ (this.right.content))
|
|
62
|
-
}
|
|
63
|
-
break
|
|
64
|
-
default:
|
|
65
|
-
this.index += this.am.contentLength(this.right)
|
|
66
|
-
break
|
|
67
|
-
}
|
|
68
|
-
this.left = this.right
|
|
69
|
-
this.right = this.right.right
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* @param {Transaction} transaction
|
|
74
|
-
* @param {import('../utils/types.js').YType} parent
|
|
75
|
-
* @param {number} length
|
|
76
|
-
* @param {Object<string,any>} attributes
|
|
77
|
-
*
|
|
78
|
-
* @function
|
|
79
|
-
*/
|
|
80
|
-
formatText (transaction, parent, length, attributes) {
|
|
81
|
-
const doc = transaction.doc
|
|
82
|
-
const ownClientId = doc.clientID
|
|
83
|
-
minimizeAttributeChanges(this, attributes)
|
|
84
|
-
const negatedAttributes = insertAttributes(transaction, parent, this, attributes)
|
|
85
|
-
// iterate until first non-format or null is found
|
|
86
|
-
// delete all formats with attributes[format.key] != null
|
|
87
|
-
// also check the attributes after the first non-format as we do not want to insert redundant negated attributes there
|
|
88
|
-
// eslint-disable-next-line no-labels
|
|
89
|
-
iterationLoop: while (
|
|
90
|
-
this.right !== null &&
|
|
91
|
-
(length > 0 ||
|
|
92
|
-
(
|
|
93
|
-
negatedAttributes.size > 0 &&
|
|
94
|
-
((this.right.deleted && this.am.contentLength(this.right) === 0) || this.right.content.constructor === ContentFormat)
|
|
95
|
-
)
|
|
96
|
-
)
|
|
97
|
-
) {
|
|
98
|
-
switch (this.right.content.constructor) {
|
|
99
|
-
case ContentFormat: {
|
|
100
|
-
if (!this.right.deleted) {
|
|
101
|
-
const { key, value } = /** @type {ContentFormat} */ (this.right.content)
|
|
102
|
-
const attr = attributes[key]
|
|
103
|
-
if (attr !== undefined) {
|
|
104
|
-
if (equalAttrs(attr, value)) {
|
|
105
|
-
negatedAttributes.delete(key)
|
|
106
|
-
} else {
|
|
107
|
-
if (length === 0) {
|
|
108
|
-
// no need to further extend negatedAttributes
|
|
109
|
-
// eslint-disable-next-line no-labels
|
|
110
|
-
break iterationLoop
|
|
111
|
-
}
|
|
112
|
-
negatedAttributes.set(key, value)
|
|
113
|
-
}
|
|
114
|
-
this.right.delete(transaction)
|
|
115
|
-
} else {
|
|
116
|
-
this.currentAttributes.set(key, value)
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
break
|
|
120
|
-
}
|
|
121
|
-
default: {
|
|
122
|
-
const item = this.right
|
|
123
|
-
const rightLen = this.am.contentLength(item)
|
|
124
|
-
if (length < rightLen) {
|
|
125
|
-
/**
|
|
126
|
-
* @type {Array<import('../internals.js').AttributedContent<any>>}
|
|
127
|
-
*/
|
|
128
|
-
const contents = []
|
|
129
|
-
this.am.readContent(contents, item.id.client, item.id.clock, item.deleted, item.content, 0)
|
|
130
|
-
let i = 0
|
|
131
|
-
for (; i < contents.length && length > 0; i++) {
|
|
132
|
-
const c = contents[i]
|
|
133
|
-
if ((!c.deleted || c.attrs != null) && c.content.isCountable()) {
|
|
134
|
-
length -= c.content.getLength()
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
if (length < 0 || (length === 0 && i !== contents.length)) {
|
|
138
|
-
const c = contents[--i]
|
|
139
|
-
getItemCleanStart(transaction, createID(item.id.client, c.clock + c.content.getLength() + length))
|
|
140
|
-
}
|
|
141
|
-
} else {
|
|
142
|
-
length -= rightLen
|
|
143
|
-
}
|
|
144
|
-
break
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
this.forward()
|
|
148
|
-
}
|
|
149
|
-
// Quill just assumes that the editor starts with a newline and that it always
|
|
150
|
-
// ends with a newline. We only insert that newline when a new newline is
|
|
151
|
-
// inserted - i.e when length is bigger than type.length
|
|
152
|
-
if (length > 0) {
|
|
153
|
-
let newlines = ''
|
|
154
|
-
for (; length > 0; length--) {
|
|
155
|
-
newlines += '\n'
|
|
156
|
-
}
|
|
157
|
-
this.right = new Item(createID(ownClientId, getState(doc.store, ownClientId)), this.left, this.left && this.left.lastId, this.right, this.right && this.right.id, parent, null, new ContentString(newlines))
|
|
158
|
-
this.right.integrate(transaction, 0)
|
|
159
|
-
this.forward()
|
|
160
|
-
}
|
|
161
|
-
insertNegatedAttributes(transaction, parent, this, negatedAttributes)
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* @param {Transaction} transaction
|
|
167
|
-
* @param {ItemTextListPosition} pos
|
|
168
|
-
* @param {number} count steps to move forward
|
|
169
|
-
* @return {ItemTextListPosition}
|
|
170
|
-
*
|
|
171
|
-
* @private
|
|
172
|
-
* @function
|
|
173
|
-
*/
|
|
174
|
-
const findNextPosition = (transaction, pos, count) => {
|
|
175
|
-
while (pos.right !== null && count > 0) {
|
|
176
|
-
switch (pos.right.content.constructor) {
|
|
177
|
-
case ContentFormat:
|
|
178
|
-
if (!pos.right.deleted) {
|
|
179
|
-
updateCurrentAttributes(pos.currentAttributes, /** @type {ContentFormat} */ (pos.right.content))
|
|
180
|
-
}
|
|
181
|
-
break
|
|
182
|
-
default:
|
|
183
|
-
if (!pos.right.deleted) {
|
|
184
|
-
if (count < pos.right.length) {
|
|
185
|
-
// split right
|
|
186
|
-
getItemCleanStart(transaction, createID(pos.right.id.client, pos.right.id.clock + count))
|
|
187
|
-
}
|
|
188
|
-
pos.index += pos.right.length
|
|
189
|
-
count -= pos.right.length
|
|
190
|
-
}
|
|
191
|
-
break
|
|
192
|
-
}
|
|
193
|
-
pos.left = pos.right
|
|
194
|
-
pos.right = pos.right.right
|
|
195
|
-
// pos.forward() - we don't forward because that would halve the performance because we already do the checks above
|
|
196
|
-
}
|
|
197
|
-
return pos
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* @param {Transaction} transaction
|
|
202
|
-
* @param {import('../utils/types.js').YType} parent
|
|
203
|
-
* @param {number} index
|
|
204
|
-
* @param {boolean} useSearchMarker
|
|
205
|
-
* @return {ItemTextListPosition}
|
|
206
|
-
*
|
|
207
|
-
* @private
|
|
208
|
-
* @function
|
|
209
|
-
*/
|
|
210
|
-
const findPosition = (transaction, parent, index, useSearchMarker) => {
|
|
211
|
-
const currentAttributes = new Map()
|
|
212
|
-
const marker = useSearchMarker ? findMarker(parent, index) : null
|
|
213
|
-
if (marker) {
|
|
214
|
-
const pos = new ItemTextListPosition(marker.p.left, marker.p, marker.index, currentAttributes, noAttributionsManager)
|
|
215
|
-
return findNextPosition(transaction, pos, index - marker.index)
|
|
216
|
-
} else {
|
|
217
|
-
const pos = new ItemTextListPosition(null, parent._start, 0, currentAttributes, noAttributionsManager)
|
|
218
|
-
return findNextPosition(transaction, pos, index)
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Negate applied formats
|
|
224
|
-
*
|
|
225
|
-
* @param {Transaction} transaction
|
|
226
|
-
* @param {import('../utils/types.js').YType} parent
|
|
227
|
-
* @param {ItemTextListPosition} currPos
|
|
228
|
-
* @param {Map<string,any>} negatedAttributes
|
|
229
|
-
*
|
|
230
|
-
* @private
|
|
231
|
-
* @function
|
|
232
|
-
*/
|
|
233
|
-
const insertNegatedAttributes = (transaction, parent, currPos, negatedAttributes) => {
|
|
234
|
-
// check if we really need to remove attributes
|
|
235
|
-
while (
|
|
236
|
-
currPos.right !== null && (
|
|
237
|
-
(currPos.right.deleted && (currPos.am === noAttributionsManager || currPos.am.contentLength(currPos.right) === 0)) || (
|
|
238
|
-
currPos.right.content.constructor === ContentFormat &&
|
|
239
|
-
equalAttrs(negatedAttributes.get(/** @type {ContentFormat} */ (currPos.right.content).key), /** @type {ContentFormat} */ (currPos.right.content).value)
|
|
240
|
-
)
|
|
241
|
-
)
|
|
242
|
-
) {
|
|
243
|
-
if (!currPos.right.deleted) {
|
|
244
|
-
negatedAttributes.delete(/** @type {ContentFormat} */ (currPos.right.content).key)
|
|
245
|
-
}
|
|
246
|
-
currPos.forward()
|
|
247
|
-
}
|
|
248
|
-
const doc = transaction.doc
|
|
249
|
-
const ownClientId = doc.clientID
|
|
250
|
-
negatedAttributes.forEach((val, key) => {
|
|
251
|
-
const left = currPos.left
|
|
252
|
-
const right = currPos.right
|
|
253
|
-
const nextFormat = new Item(createID(ownClientId, getState(doc.store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, new ContentFormat(key, val))
|
|
254
|
-
nextFormat.integrate(transaction, 0)
|
|
255
|
-
currPos.right = nextFormat
|
|
256
|
-
currPos.forward()
|
|
257
|
-
})
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* @param {Map<string,any>} currentAttributes
|
|
262
|
-
* @param {ContentFormat} format
|
|
263
|
-
*
|
|
264
|
-
* @private
|
|
265
|
-
* @function
|
|
266
|
-
*/
|
|
267
|
-
const updateCurrentAttributes = (currentAttributes, format) => {
|
|
268
|
-
const { key, value } = format
|
|
269
|
-
if (value === null) {
|
|
270
|
-
currentAttributes.delete(key)
|
|
271
|
-
} else {
|
|
272
|
-
currentAttributes.set(key, value)
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* @param {ItemTextListPosition} currPos
|
|
278
|
-
* @param {Object<string,any>} attributes
|
|
279
|
-
*
|
|
280
|
-
* @private
|
|
281
|
-
* @function
|
|
282
|
-
*/
|
|
283
|
-
const minimizeAttributeChanges = (currPos, attributes) => {
|
|
284
|
-
// go right while attributes[right.key] === right.value (or right is deleted)
|
|
285
|
-
while (true) {
|
|
286
|
-
if (currPos.right === null) {
|
|
287
|
-
break
|
|
288
|
-
} else if (currPos.right.deleted ? (currPos.am.contentLength(currPos.right) === 0) : (!currPos.right.deleted && currPos.right.content.constructor === ContentFormat && equalAttrs(attributes[(/** @type {ContentFormat} */ (currPos.right.content)).key] ?? null, /** @type {ContentFormat} */ (currPos.right.content).value))) {
|
|
289
|
-
//
|
|
290
|
-
} else {
|
|
291
|
-
break
|
|
292
|
-
}
|
|
293
|
-
currPos.forward()
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
/**
|
|
298
|
-
* @param {Transaction} transaction
|
|
299
|
-
* @param {import('../utils/types.js').YType} parent
|
|
300
|
-
* @param {ItemTextListPosition} currPos
|
|
301
|
-
* @param {Object<string,any>} attributes
|
|
302
|
-
* @return {Map<string,any>}
|
|
303
|
-
*
|
|
304
|
-
* @private
|
|
305
|
-
* @function
|
|
306
|
-
**/
|
|
307
|
-
const insertAttributes = (transaction, parent, currPos, attributes) => {
|
|
308
|
-
const doc = transaction.doc
|
|
309
|
-
const ownClientId = doc.clientID
|
|
310
|
-
const negatedAttributes = new Map()
|
|
311
|
-
// insert format-start items
|
|
312
|
-
for (const key in attributes) {
|
|
313
|
-
const val = attributes[key]
|
|
314
|
-
const currentVal = currPos.currentAttributes.get(key) ?? null
|
|
315
|
-
if (!equalAttrs(currentVal, val)) {
|
|
316
|
-
// save negated attribute (set null if currentVal undefined)
|
|
317
|
-
negatedAttributes.set(key, currentVal)
|
|
318
|
-
const { left, right } = currPos
|
|
319
|
-
currPos.right = new Item(createID(ownClientId, getState(doc.store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, new ContentFormat(key, val))
|
|
320
|
-
currPos.right.integrate(transaction, 0)
|
|
321
|
-
currPos.forward()
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
return negatedAttributes
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* @param {Transaction} transaction
|
|
329
|
-
* @param {import('../utils/types.js').YType} parent
|
|
330
|
-
* @param {ItemTextListPosition} currPos
|
|
331
|
-
* @param {string|object|import('../utils/types.js').YType} text
|
|
332
|
-
* @param {Object<string,any>} attributes
|
|
333
|
-
*
|
|
334
|
-
* @private
|
|
335
|
-
* @function
|
|
336
|
-
**/
|
|
337
|
-
export const insertText = (transaction, parent, currPos, text, attributes) => {
|
|
338
|
-
currPos.currentAttributes.forEach((_val, key) => {
|
|
339
|
-
if (attributes[key] === undefined) {
|
|
340
|
-
attributes[key] = null
|
|
341
|
-
}
|
|
342
|
-
})
|
|
343
|
-
const doc = transaction.doc
|
|
344
|
-
const ownClientId = doc.clientID
|
|
345
|
-
minimizeAttributeChanges(currPos, attributes)
|
|
346
|
-
const negatedAttributes = insertAttributes(transaction, parent, currPos, attributes)
|
|
347
|
-
// insert content
|
|
348
|
-
const content = text.constructor === String ? new ContentString(/** @type {string} */ (text)) : (text instanceof AbstractType ? new ContentType(text) : new ContentEmbed(text))
|
|
349
|
-
let { left, right, index } = currPos
|
|
350
|
-
if (parent._searchMarker) {
|
|
351
|
-
updateMarkerChanges(parent._searchMarker, currPos.index, content.getLength())
|
|
352
|
-
}
|
|
353
|
-
right = new Item(createID(ownClientId, getState(doc.store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, content)
|
|
354
|
-
right.integrate(transaction, 0)
|
|
355
|
-
currPos.right = right
|
|
356
|
-
currPos.index = index
|
|
357
|
-
currPos.forward()
|
|
358
|
-
insertNegatedAttributes(transaction, parent, currPos, negatedAttributes)
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
/**
|
|
362
|
-
* Call this function after string content has been deleted in order to
|
|
363
|
-
* clean up formatting Items.
|
|
364
|
-
*
|
|
365
|
-
* @param {Transaction} transaction
|
|
366
|
-
* @param {Item} start
|
|
367
|
-
* @param {Item|null} curr exclusive end, automatically iterates to the next Content Item
|
|
368
|
-
* @param {Map<string,any>} startAttributes
|
|
369
|
-
* @param {Map<string,any>} currAttributes
|
|
370
|
-
* @return {number} The amount of formatting Items deleted.
|
|
371
|
-
*
|
|
372
|
-
* @function
|
|
373
|
-
*/
|
|
374
|
-
const cleanupFormattingGap = (transaction, start, curr, startAttributes, currAttributes) => {
|
|
375
|
-
if (!transaction.doc.cleanupFormatting) return 0
|
|
376
|
-
/**
|
|
377
|
-
* @type {Item|null}
|
|
378
|
-
*/
|
|
379
|
-
let end = start
|
|
380
|
-
/**
|
|
381
|
-
* @type {Map<string,ContentFormat>}
|
|
382
|
-
*/
|
|
383
|
-
const endFormats = map.create()
|
|
384
|
-
while (end && (!end.countable || end.deleted)) {
|
|
385
|
-
if (!end.deleted && end.content.constructor === ContentFormat) {
|
|
386
|
-
const cf = /** @type {ContentFormat} */ (end.content)
|
|
387
|
-
endFormats.set(cf.key, cf)
|
|
388
|
-
}
|
|
389
|
-
end = end.right
|
|
390
|
-
}
|
|
391
|
-
let cleanups = 0
|
|
392
|
-
let reachedCurr = false
|
|
393
|
-
while (start !== end) {
|
|
394
|
-
if (curr === start) {
|
|
395
|
-
reachedCurr = true
|
|
396
|
-
}
|
|
397
|
-
if (!start.deleted) {
|
|
398
|
-
const content = start.content
|
|
399
|
-
switch (content.constructor) {
|
|
400
|
-
case ContentFormat: {
|
|
401
|
-
const { key, value } = /** @type {ContentFormat} */ (content)
|
|
402
|
-
const startAttrValue = startAttributes.get(key) ?? null
|
|
403
|
-
if (endFormats.get(key) !== content || startAttrValue === value) {
|
|
404
|
-
// Either this format is overwritten or it is not necessary because the attribute already existed.
|
|
405
|
-
start.delete(transaction)
|
|
406
|
-
transaction.cleanUps.add(start.id.client, start.id.clock, start.length)
|
|
407
|
-
cleanups++
|
|
408
|
-
if (!reachedCurr && (currAttributes.get(key) ?? null) === value && startAttrValue !== value) {
|
|
409
|
-
if (startAttrValue === null) {
|
|
410
|
-
currAttributes.delete(key)
|
|
411
|
-
} else {
|
|
412
|
-
currAttributes.set(key, startAttrValue)
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
if (!reachedCurr && !start.deleted) {
|
|
417
|
-
updateCurrentAttributes(currAttributes, /** @type {ContentFormat} */ (content))
|
|
418
|
-
}
|
|
419
|
-
break
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
start = /** @type {Item} */ (start.right)
|
|
424
|
-
}
|
|
425
|
-
return cleanups
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
/**
|
|
429
|
-
* @param {Transaction} transaction
|
|
430
|
-
* @param {Item | null} item
|
|
431
|
-
*/
|
|
432
|
-
const cleanupContextlessFormattingGap = (transaction, item) => {
|
|
433
|
-
if (!transaction.doc.cleanupFormatting) return 0
|
|
434
|
-
// iterate until item.right is null or content
|
|
435
|
-
while (item && item.right && (item.right.deleted || !item.right.countable)) {
|
|
436
|
-
item = item.right
|
|
437
|
-
}
|
|
438
|
-
const attrs = new Set()
|
|
439
|
-
// iterate back until a content item is found
|
|
440
|
-
while (item && (item.deleted || !item.countable)) {
|
|
441
|
-
if (!item.deleted && item.content.constructor === ContentFormat) {
|
|
442
|
-
const key = /** @type {ContentFormat} */ (item.content).key
|
|
443
|
-
if (attrs.has(key)) {
|
|
444
|
-
item.delete(transaction)
|
|
445
|
-
transaction.cleanUps.add(item.id.client, item.id.clock, item.length)
|
|
446
|
-
} else {
|
|
447
|
-
attrs.add(key)
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
item = item.left
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
/**
|
|
455
|
-
* This function is experimental and subject to change / be removed.
|
|
456
|
-
*
|
|
457
|
-
* Ideally, we don't need this function at all. Formatting attributes should be cleaned up
|
|
458
|
-
* automatically after each change. This function iterates twice over the complete YText type
|
|
459
|
-
* and removes unnecessary formatting attributes. This is also helpful for testing.
|
|
460
|
-
*
|
|
461
|
-
* This function won't be exported anymore as soon as there is confidence that the YText type works as intended.
|
|
462
|
-
*
|
|
463
|
-
* @param {YText<any>} type
|
|
464
|
-
* @return {number} How many formatting attributes have been cleaned up.
|
|
465
|
-
*/
|
|
466
|
-
export const cleanupYTextFormatting = type => {
|
|
467
|
-
if (!type.doc?.cleanupFormatting) return 0
|
|
468
|
-
let res = 0
|
|
469
|
-
transact(/** @type {Doc} */ (type.doc), transaction => {
|
|
470
|
-
let start = /** @type {Item} */ (type._start)
|
|
471
|
-
let end = type._start
|
|
472
|
-
let startAttributes = map.create()
|
|
473
|
-
const currentAttributes = map.copy(startAttributes)
|
|
474
|
-
while (end) {
|
|
475
|
-
if (end.deleted === false) {
|
|
476
|
-
switch (end.content.constructor) {
|
|
477
|
-
case ContentFormat:
|
|
478
|
-
updateCurrentAttributes(currentAttributes, /** @type {ContentFormat} */ (end.content))
|
|
479
|
-
break
|
|
480
|
-
default:
|
|
481
|
-
res += cleanupFormattingGap(transaction, start, end, startAttributes, currentAttributes)
|
|
482
|
-
startAttributes = map.copy(currentAttributes)
|
|
483
|
-
start = end
|
|
484
|
-
break
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
end = end.right
|
|
488
|
-
}
|
|
489
|
-
})
|
|
490
|
-
return res
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
/**
|
|
494
|
-
* This will be called by the transaction once the event handlers are called to potentially cleanup
|
|
495
|
-
* formatting attributes.
|
|
496
|
-
*
|
|
497
|
-
* @param {Transaction} transaction
|
|
498
|
-
*/
|
|
499
|
-
export const cleanupYTextAfterTransaction = transaction => {
|
|
500
|
-
/**
|
|
501
|
-
* @type {Set<YText<any>>}
|
|
502
|
-
*/
|
|
503
|
-
const needFullCleanup = new Set()
|
|
504
|
-
// check if another formatting item was inserted
|
|
505
|
-
const doc = transaction.doc
|
|
506
|
-
iterateStructsByIdSet(transaction, transaction.insertSet, (item) => {
|
|
507
|
-
if (
|
|
508
|
-
!item.deleted && /** @type {Item} */ (item).content.constructor === ContentFormat && item.constructor !== GC
|
|
509
|
-
) {
|
|
510
|
-
needFullCleanup.add(/** @type {any} */ (item).parent)
|
|
511
|
-
}
|
|
512
|
-
})
|
|
513
|
-
// cleanup in a new transaction
|
|
514
|
-
transact(doc, (t) => {
|
|
515
|
-
iterateStructsByIdSet(transaction, transaction.deleteSet, item => {
|
|
516
|
-
if (item instanceof GC || !(/** @type {YText<any>} */ (item.parent)._hasFormatting) || needFullCleanup.has(/** @type {YText<any>} */ (item.parent))) {
|
|
517
|
-
return
|
|
518
|
-
}
|
|
519
|
-
const parent = /** @type {YText<any>} */ (item.parent)
|
|
520
|
-
if (item.content.constructor === ContentFormat) {
|
|
521
|
-
needFullCleanup.add(parent)
|
|
522
|
-
} else {
|
|
523
|
-
// If no formatting attribute was inserted or deleted, we can make due with contextless
|
|
524
|
-
// formatting cleanups.
|
|
525
|
-
// Contextless: it is not necessary to compute currentAttributes for the affected position.
|
|
526
|
-
cleanupContextlessFormattingGap(t, item)
|
|
527
|
-
}
|
|
528
|
-
})
|
|
529
|
-
// If a formatting item was inserted, we simply clean the whole type.
|
|
530
|
-
// We need to compute currentAttributes for the current position anyway.
|
|
531
|
-
for (const yText of needFullCleanup) {
|
|
532
|
-
cleanupYTextFormatting(yText)
|
|
533
|
-
}
|
|
534
|
-
})
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
/**
|
|
538
|
-
* @param {Transaction} transaction
|
|
539
|
-
* @param {ItemTextListPosition} currPos
|
|
540
|
-
* @param {number} length
|
|
541
|
-
* @return {ItemTextListPosition}
|
|
542
|
-
*
|
|
543
|
-
* @private
|
|
544
|
-
* @function
|
|
545
|
-
*/
|
|
546
|
-
export const deleteText = (transaction, currPos, length) => {
|
|
547
|
-
const startLength = length
|
|
548
|
-
const startAttrs = map.copy(currPos.currentAttributes)
|
|
549
|
-
const start = currPos.right
|
|
550
|
-
while (length > 0 && currPos.right !== null) {
|
|
551
|
-
if (!currPos.right.deleted) {
|
|
552
|
-
switch (currPos.right.content.constructor) {
|
|
553
|
-
case ContentType:
|
|
554
|
-
case ContentEmbed:
|
|
555
|
-
case ContentString:
|
|
556
|
-
if (length < currPos.right.length) {
|
|
557
|
-
getItemCleanStart(transaction, createID(currPos.right.id.client, currPos.right.id.clock + length))
|
|
558
|
-
}
|
|
559
|
-
length -= currPos.right.length
|
|
560
|
-
currPos.right.delete(transaction)
|
|
561
|
-
break
|
|
562
|
-
}
|
|
563
|
-
} else if (currPos.am !== noAttributionsManager) {
|
|
564
|
-
const item = currPos.right
|
|
565
|
-
/**
|
|
566
|
-
* @type {Array<import('../internals.js').AttributedContent<any>>}
|
|
567
|
-
*/
|
|
568
|
-
const contents = []
|
|
569
|
-
currPos.am.readContent(contents, item.id.client, item.id.clock, true, item.content, 0)
|
|
570
|
-
for (let i = 0; i < contents.length; i++) {
|
|
571
|
-
const c = contents[i]
|
|
572
|
-
if (c.content.isCountable() && c.attrs != null) {
|
|
573
|
-
// deleting already deleted content. store that information in a meta property, but do
|
|
574
|
-
// nothing
|
|
575
|
-
const contentLen = math.min(c.content.getLength(), length)
|
|
576
|
-
map.setIfUndefined(transaction.meta, 'attributedDeletes', createIdSet).add(item.id.client, c.clock, contentLen)
|
|
577
|
-
length -= contentLen
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
const lastContent = contents.length > 0 ? contents[contents.length - 1] : null
|
|
581
|
-
const nextItemClock = item.id.clock + item.length
|
|
582
|
-
const nextContentClock = lastContent != null ? lastContent.clock + lastContent.content.getLength() : nextItemClock
|
|
583
|
-
if (nextContentClock < nextItemClock) {
|
|
584
|
-
getItemCleanStart(transaction, createID(item.id.client, nextContentClock))
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
currPos.forward()
|
|
588
|
-
}
|
|
589
|
-
if (start) {
|
|
590
|
-
cleanupFormattingGap(transaction, start, currPos.right, startAttrs, currPos.currentAttributes)
|
|
591
|
-
}
|
|
592
|
-
const parent = /** @type {AbstractType<any>} */ (/** @type {Item} */ (currPos.left || currPos.right).parent)
|
|
593
|
-
if (parent._searchMarker) {
|
|
594
|
-
updateMarkerChanges(parent._searchMarker, currPos.index, -startLength + length)
|
|
595
|
-
}
|
|
596
|
-
return currPos
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
/**
|
|
600
|
-
* The Quill Delta format represents changes on a text document with
|
|
601
|
-
* formatting information. For more information visit {@link https://quilljs.com/docs/delta/|Quill Delta}
|
|
602
|
-
*
|
|
603
|
-
* @example
|
|
604
|
-
* {
|
|
605
|
-
* ops: [
|
|
606
|
-
* { insert: 'Gandalf', attributes: { bold: true } },
|
|
607
|
-
* { insert: ' the ' },
|
|
608
|
-
* { insert: 'Grey', attributes: { color: '#cccccc' } }
|
|
609
|
-
* ]
|
|
610
|
-
* }
|
|
611
|
-
*
|
|
612
|
-
*/
|
|
613
|
-
|
|
614
|
-
/**
|
|
615
|
-
* Attributes that can be assigned to a selection of text.
|
|
616
|
-
*
|
|
617
|
-
* @example
|
|
618
|
-
* {
|
|
619
|
-
* bold: true,
|
|
620
|
-
* font-size: '40px'
|
|
621
|
-
* }
|
|
622
|
-
*
|
|
623
|
-
* @typedef {Object} TextAttributes
|
|
624
|
-
*/
|
|
625
|
-
|
|
626
|
-
/**
|
|
627
|
-
* Type that represents text with formatting information.
|
|
628
|
-
*
|
|
629
|
-
* This type replaces y-richtext as this implementation is able to handle
|
|
630
|
-
* block formats (format information on a paragraph), embeds (complex elements
|
|
631
|
-
* like pictures and videos), and text formats (**bold**, *italic*).
|
|
632
|
-
*
|
|
633
|
-
* @template {{ [key:string]:any } | import('../utils/types.js').YType} [Embeds={ [key:string]:any } | import('../utils/types.js').YType]
|
|
634
|
-
* @extends {AbstractType<import('lib0/delta').TextDelta<Embeds>>}
|
|
635
|
-
*/
|
|
636
|
-
export class YText extends AbstractType {
|
|
637
|
-
/**
|
|
638
|
-
* @param {String} [string] The initial value of the YText.
|
|
639
|
-
*/
|
|
640
|
-
constructor (string) {
|
|
641
|
-
super()
|
|
642
|
-
/**
|
|
643
|
-
* Array of pending operations on this type
|
|
644
|
-
* @type {Array<function():void>?}
|
|
645
|
-
*/
|
|
646
|
-
this._pending = string !== undefined ? [() => this.insert(0, string)] : []
|
|
647
|
-
/**
|
|
648
|
-
* @type {Array<ArraySearchMarker>|null}
|
|
649
|
-
*/
|
|
650
|
-
this._searchMarker = []
|
|
651
|
-
/**
|
|
652
|
-
* Whether this YText contains formatting attributes.
|
|
653
|
-
* This flag is updated when a formatting item is integrated (see ContentFormat.integrate)
|
|
654
|
-
*/
|
|
655
|
-
this._hasFormatting = false
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
/**
|
|
659
|
-
* Number of characters of this text type.
|
|
660
|
-
*
|
|
661
|
-
* @type {number}
|
|
662
|
-
*/
|
|
663
|
-
get length () {
|
|
664
|
-
this.doc ?? warnPrematureAccess()
|
|
665
|
-
return this._length
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
/**
|
|
669
|
-
* @param {Doc} y
|
|
670
|
-
* @param {Item?} item
|
|
671
|
-
*/
|
|
672
|
-
_integrate (y, item) {
|
|
673
|
-
super._integrate(y, item)
|
|
674
|
-
try {
|
|
675
|
-
/** @type {Array<function>} */ (this._pending).forEach(f => f())
|
|
676
|
-
} catch (e) {
|
|
677
|
-
console.error(e)
|
|
678
|
-
}
|
|
679
|
-
this._pending = null
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
/**
|
|
683
|
-
* Makes a copy of this data type that can be included somewhere else.
|
|
684
|
-
*
|
|
685
|
-
* Note that the content is only readable _after_ it has been included somewhere in the Ydoc.
|
|
686
|
-
*
|
|
687
|
-
* @return {YText<Embeds>}
|
|
688
|
-
*/
|
|
689
|
-
clone () {
|
|
690
|
-
/**
|
|
691
|
-
* @type {YText<Embeds>}
|
|
692
|
-
*/
|
|
693
|
-
const text = /** @type {any} */ (new YText())
|
|
694
|
-
text.applyDelta(this.getContent())
|
|
695
|
-
return text
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
/**
|
|
699
|
-
* Creates YTextEvent and calls observers.
|
|
700
|
-
*
|
|
701
|
-
* @param {Transaction} transaction
|
|
702
|
-
* @param {Set<null|string>} parentSubs Keys changed on this type. `null` if list was modified.
|
|
703
|
-
*/
|
|
704
|
-
_callObserver (transaction, parentSubs) {
|
|
705
|
-
super._callObserver(transaction, parentSubs)
|
|
706
|
-
// If a remote change happened, we try to cleanup potential formatting duplicates.
|
|
707
|
-
if (!transaction.local && this._hasFormatting) {
|
|
708
|
-
transaction._needFormattingCleanup = true
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
/**
|
|
713
|
-
* Returns the unformatted string representation of this YText type.
|
|
714
|
-
*
|
|
715
|
-
* @public
|
|
716
|
-
*/
|
|
717
|
-
toString () {
|
|
718
|
-
this.doc ?? warnPrematureAccess()
|
|
719
|
-
let str = ''
|
|
720
|
-
/**
|
|
721
|
-
* @type {Item|null}
|
|
722
|
-
*/
|
|
723
|
-
let n = this._start
|
|
724
|
-
while (n !== null) {
|
|
725
|
-
if (!n.deleted && n.countable && n.content.constructor === ContentString) {
|
|
726
|
-
str += /** @type {ContentString} */ (n.content).str
|
|
727
|
-
}
|
|
728
|
-
n = n.right
|
|
729
|
-
}
|
|
730
|
-
return str
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
/**
|
|
734
|
-
* Returns the unformatted string representation of this YText type.
|
|
735
|
-
*
|
|
736
|
-
* @return {string}
|
|
737
|
-
* @public
|
|
738
|
-
*/
|
|
739
|
-
toJSON () {
|
|
740
|
-
return this.toString()
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
/**
|
|
744
|
-
* Insert text at a given index.
|
|
745
|
-
*
|
|
746
|
-
* @param {number} index The index at which to start inserting.
|
|
747
|
-
* @param {String} text The text to insert at the specified position.
|
|
748
|
-
* @param {TextAttributes} [attributes] Optionally define some formatting
|
|
749
|
-
* information to apply on the inserted
|
|
750
|
-
* Text.
|
|
751
|
-
* @public
|
|
752
|
-
*/
|
|
753
|
-
insert (index, text, attributes) {
|
|
754
|
-
if (text.length <= 0) {
|
|
755
|
-
return
|
|
756
|
-
}
|
|
757
|
-
const y = this.doc
|
|
758
|
-
if (y !== null) {
|
|
759
|
-
transact(y, transaction => {
|
|
760
|
-
const pos = findPosition(transaction, this, index, !attributes)
|
|
761
|
-
if (!attributes) {
|
|
762
|
-
attributes = {}
|
|
763
|
-
// @ts-ignore
|
|
764
|
-
pos.currentAttributes.forEach((v, k) => { attributes[k] = v })
|
|
765
|
-
}
|
|
766
|
-
insertText(transaction, this, pos, text, attributes)
|
|
767
|
-
})
|
|
768
|
-
} else {
|
|
769
|
-
/** @type {Array<function>} */ (this._pending).push(() => this.insert(index, text, attributes))
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
/**
|
|
774
|
-
* Inserts an embed at a index.
|
|
775
|
-
*
|
|
776
|
-
* @param {number} index The index to insert the embed at.
|
|
777
|
-
* @param {Object | AbstractType<any>} embed The Object that represents the embed.
|
|
778
|
-
* @param {TextAttributes} [attributes] Attribute information to apply on the
|
|
779
|
-
* embed
|
|
780
|
-
*
|
|
781
|
-
* @public
|
|
782
|
-
*/
|
|
783
|
-
insertEmbed (index, embed, attributes) {
|
|
784
|
-
const y = this.doc
|
|
785
|
-
if (y !== null) {
|
|
786
|
-
transact(y, transaction => {
|
|
787
|
-
const pos = findPosition(transaction, this, index, !attributes)
|
|
788
|
-
insertText(transaction, this, pos, embed, attributes || {})
|
|
789
|
-
})
|
|
790
|
-
} else {
|
|
791
|
-
/** @type {Array<function>} */ (this._pending).push(() => this.insertEmbed(index, embed, attributes || {}))
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
/**
|
|
796
|
-
* Deletes text starting from an index.
|
|
797
|
-
*
|
|
798
|
-
* @param {number} index Index at which to start deleting.
|
|
799
|
-
* @param {number} length The number of characters to remove. Defaults to 1.
|
|
800
|
-
*
|
|
801
|
-
* @public
|
|
802
|
-
*/
|
|
803
|
-
delete (index, length) {
|
|
804
|
-
if (length === 0) {
|
|
805
|
-
return
|
|
806
|
-
}
|
|
807
|
-
const y = this.doc
|
|
808
|
-
if (y !== null) {
|
|
809
|
-
transact(y, transaction => {
|
|
810
|
-
deleteText(transaction, findPosition(transaction, this, index, true), length)
|
|
811
|
-
})
|
|
812
|
-
} else {
|
|
813
|
-
/** @type {Array<function>} */ (this._pending).push(() => this.delete(index, length))
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
/**
|
|
818
|
-
* Assigns properties to a range of text.
|
|
819
|
-
*
|
|
820
|
-
* @param {number} index The position where to start formatting.
|
|
821
|
-
* @param {number} length The amount of characters to assign properties to.
|
|
822
|
-
* @param {TextAttributes} attributes Attribute information to apply on the
|
|
823
|
-
* text.
|
|
824
|
-
*
|
|
825
|
-
* @public
|
|
826
|
-
*/
|
|
827
|
-
format (index, length, attributes) {
|
|
828
|
-
if (length === 0) {
|
|
829
|
-
return
|
|
830
|
-
}
|
|
831
|
-
const y = this.doc
|
|
832
|
-
if (y !== null) {
|
|
833
|
-
transact(y, transaction => {
|
|
834
|
-
const pos = findPosition(transaction, this, index, false)
|
|
835
|
-
if (pos.right === null) {
|
|
836
|
-
return
|
|
837
|
-
}
|
|
838
|
-
pos.formatText(transaction, this, length, attributes)
|
|
839
|
-
})
|
|
840
|
-
} else {
|
|
841
|
-
/** @type {Array<function>} */ (this._pending).push(() => this.format(index, length, attributes))
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
|
|
845
|
-
/**
|
|
846
|
-
* Removes an attribute.
|
|
847
|
-
*
|
|
848
|
-
* @note Xml-Text nodes don't have attributes. You can use this feature to assign properties to complete text-blocks.
|
|
849
|
-
*
|
|
850
|
-
* @param {String} attributeName The attribute name that is to be removed.
|
|
851
|
-
*
|
|
852
|
-
* @public
|
|
853
|
-
*/
|
|
854
|
-
removeAttribute (attributeName) {
|
|
855
|
-
if (this.doc !== null) {
|
|
856
|
-
transact(this.doc, transaction => {
|
|
857
|
-
typeMapDelete(transaction, this, attributeName)
|
|
858
|
-
})
|
|
859
|
-
} else {
|
|
860
|
-
/** @type {Array<function>} */ (this._pending).push(() => this.removeAttribute(attributeName))
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
/**
|
|
865
|
-
* Sets or updates an attribute.
|
|
866
|
-
*
|
|
867
|
-
* @note Xml-Text nodes don't have attributes. You can use this feature to assign properties to complete text-blocks.
|
|
868
|
-
*
|
|
869
|
-
* @param {String} attributeName The attribute name that is to be set.
|
|
870
|
-
* @param {any} attributeValue The attribute value that is to be set.
|
|
871
|
-
*
|
|
872
|
-
* @public
|
|
873
|
-
*/
|
|
874
|
-
setAttribute (attributeName, attributeValue) {
|
|
875
|
-
if (this.doc !== null) {
|
|
876
|
-
transact(this.doc, transaction => {
|
|
877
|
-
typeMapSet(transaction, this, attributeName, attributeValue)
|
|
878
|
-
})
|
|
879
|
-
} else {
|
|
880
|
-
/** @type {Array<function>} */ (this._pending).push(() => this.setAttribute(attributeName, attributeValue))
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
/**
|
|
885
|
-
* Returns an attribute value that belongs to the attribute name.
|
|
886
|
-
*
|
|
887
|
-
* @note Xml-Text nodes don't have attributes. You can use this feature to assign properties to complete text-blocks.
|
|
888
|
-
*
|
|
889
|
-
* @param {String} attributeName The attribute name that identifies the
|
|
890
|
-
* queried value.
|
|
891
|
-
* @return {any} The queried attribute value.
|
|
892
|
-
*
|
|
893
|
-
* @public
|
|
894
|
-
*/
|
|
895
|
-
getAttribute (attributeName) {
|
|
896
|
-
return /** @type {any} */ (typeMapGet(this, attributeName))
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
/**
|
|
900
|
-
* Returns all attribute name/value pairs in a JSON Object.
|
|
901
|
-
*
|
|
902
|
-
* @note Xml-Text nodes don't have attributes. You can use this feature to assign properties to complete text-blocks.
|
|
903
|
-
*
|
|
904
|
-
* @return {Object<string, any>} A JSON Object that describes the attributes.
|
|
905
|
-
*
|
|
906
|
-
* @public
|
|
907
|
-
*/
|
|
908
|
-
getAttributes () {
|
|
909
|
-
return typeMapGetAll(this)
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
/**
|
|
913
|
-
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
|
914
|
-
*/
|
|
915
|
-
_write (encoder) {
|
|
916
|
-
encoder.writeTypeRef(YTextRefID)
|
|
917
|
-
}
|
|
918
|
-
|
|
919
|
-
/**
|
|
920
|
-
* @param {this} other
|
|
921
|
-
*/
|
|
922
|
-
[traits.EqualityTraitSymbol] (other) {
|
|
923
|
-
return this.getContent().equals(other.getContent())
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
/**
|
|
928
|
-
* @param {UpdateDecoderV1 | UpdateDecoderV2} _decoder
|
|
929
|
-
* @return {import('../utils/types.js').YType}
|
|
930
|
-
*
|
|
931
|
-
* @private
|
|
932
|
-
* @function
|
|
933
|
-
*/
|
|
934
|
-
export const readYText = _decoder => new YText()
|