@y/y 14.0.0-16 → 14.0.0-18

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.
Files changed (68) hide show
  1. package/dist/{index-DyTeTfmj.js → index-BV-j5wdP.js} +2 -2
  2. package/dist/{index-R7GxO-36.js.map → index-BV-j5wdP.js.map} +1 -1
  3. package/dist/{internals.mjs → internals.js} +1 -1
  4. package/dist/internals.js.map +1 -0
  5. package/dist/{testHelper.mjs → testHelper.js} +2 -2
  6. package/dist/testHelper.js.map +1 -0
  7. package/dist/{yjs.mjs → yjs.js} +2 -2
  8. package/dist/yjs.js.map +1 -0
  9. package/package.json +9 -18
  10. package/dist/Skip-j0kX7pdq.js +0 -12173
  11. package/dist/Skip-j0kX7pdq.js.map +0 -1
  12. package/dist/index-DyTeTfmj.js.map +0 -1
  13. package/dist/index-R7GxO-36.js +0 -165
  14. package/dist/internals.cjs +0 -286
  15. package/dist/internals.cjs.map +0 -1
  16. package/dist/internals.mjs.map +0 -1
  17. package/dist/testHelper.cjs +0 -780
  18. package/dist/testHelper.cjs.map +0 -1
  19. package/dist/testHelper.mjs.map +0 -1
  20. package/dist/yjs.cjs +0 -151
  21. package/dist/yjs.cjs.map +0 -1
  22. package/dist/yjs.mjs.map +0 -1
  23. package/src/index.js +0 -153
  24. package/src/internals.js +0 -44
  25. package/src/structs/AbstractStruct.js +0 -59
  26. package/src/structs/ContentAny.js +0 -115
  27. package/src/structs/ContentBinary.js +0 -93
  28. package/src/structs/ContentDeleted.js +0 -101
  29. package/src/structs/ContentDoc.js +0 -141
  30. package/src/structs/ContentEmbed.js +0 -98
  31. package/src/structs/ContentFormat.js +0 -105
  32. package/src/structs/ContentJSON.js +0 -119
  33. package/src/structs/ContentString.js +0 -113
  34. package/src/structs/ContentType.js +0 -176
  35. package/src/structs/GC.js +0 -80
  36. package/src/structs/Item.js +0 -845
  37. package/src/structs/Skip.js +0 -75
  38. package/src/types/AbstractType.js +0 -1434
  39. package/src/types/YArray.js +0 -270
  40. package/src/types/YMap.js +0 -244
  41. package/src/types/YText.js +0 -934
  42. package/src/types/YXmlElement.js +0 -227
  43. package/src/types/YXmlFragment.js +0 -266
  44. package/src/types/YXmlHook.js +0 -68
  45. package/src/types/YXmlText.js +0 -66
  46. package/src/utils/AbstractConnector.js +0 -25
  47. package/src/utils/AttributionManager.js +0 -619
  48. package/src/utils/Doc.js +0 -372
  49. package/src/utils/EventHandler.js +0 -87
  50. package/src/utils/ID.js +0 -89
  51. package/src/utils/IdMap.js +0 -629
  52. package/src/utils/IdSet.js +0 -823
  53. package/src/utils/RelativePosition.js +0 -352
  54. package/src/utils/Snapshot.js +0 -220
  55. package/src/utils/StructSet.js +0 -137
  56. package/src/utils/StructStore.js +0 -289
  57. package/src/utils/Transaction.js +0 -489
  58. package/src/utils/UndoManager.js +0 -391
  59. package/src/utils/UpdateDecoder.js +0 -281
  60. package/src/utils/UpdateEncoder.js +0 -320
  61. package/src/utils/YEvent.js +0 -216
  62. package/src/utils/delta-helpers.js +0 -54
  63. package/src/utils/encoding.js +0 -623
  64. package/src/utils/isParentOf.js +0 -21
  65. package/src/utils/logging.js +0 -21
  66. package/src/utils/types.js +0 -28
  67. package/src/utils/updates.js +0 -715
  68. package/tests/testHelper.js +0 -600
@@ -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()