@fluidframework/sequence 0.59.4001 → 1.1.0-75972
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/.eslintrc.js +1 -1
- package/README.md +18 -6
- package/dist/defaultMap.d.ts +2 -6
- package/dist/defaultMap.d.ts.map +1 -1
- package/dist/defaultMap.js +27 -37
- package/dist/defaultMap.js.map +1 -1
- package/dist/defaultMapInterfaces.d.ts +24 -3
- package/dist/defaultMapInterfaces.d.ts.map +1 -1
- package/dist/defaultMapInterfaces.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/intervalCollection.d.ts +93 -8
- package/dist/intervalCollection.d.ts.map +1 -1
- package/dist/intervalCollection.js +434 -178
- package/dist/intervalCollection.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/sequence.d.ts +6 -7
- package/dist/sequence.d.ts.map +1 -1
- package/dist/sequence.js +17 -21
- package/dist/sequence.js.map +1 -1
- package/dist/sharedIntervalCollection.d.ts.map +1 -1
- package/dist/sharedIntervalCollection.js +2 -2
- package/dist/sharedIntervalCollection.js.map +1 -1
- package/dist/sharedSequence.js.map +1 -1
- package/dist/sparsematrix.js +2 -2
- package/dist/sparsematrix.js.map +1 -1
- package/lib/defaultMap.d.ts +2 -6
- package/lib/defaultMap.d.ts.map +1 -1
- package/lib/defaultMap.js +27 -37
- package/lib/defaultMap.js.map +1 -1
- package/lib/defaultMapInterfaces.d.ts +24 -3
- package/lib/defaultMapInterfaces.d.ts.map +1 -1
- package/lib/defaultMapInterfaces.js.map +1 -1
- package/lib/index.d.ts +2 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/intervalCollection.d.ts +93 -8
- package/lib/intervalCollection.d.ts.map +1 -1
- package/lib/intervalCollection.js +435 -179
- package/lib/intervalCollection.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/sequence.d.ts +6 -7
- package/lib/sequence.d.ts.map +1 -1
- package/lib/sequence.js +18 -22
- package/lib/sequence.js.map +1 -1
- package/lib/sharedIntervalCollection.d.ts.map +1 -1
- package/lib/sharedIntervalCollection.js +2 -2
- package/lib/sharedIntervalCollection.js.map +1 -1
- package/lib/sharedSequence.js.map +1 -1
- package/lib/sparsematrix.js +2 -2
- package/lib/sparsematrix.js.map +1 -1
- package/package.json +23 -45
- package/src/defaultMap.ts +39 -41
- package/src/defaultMapInterfaces.ts +28 -3
- package/src/index.ts +3 -0
- package/src/intervalCollection.ts +575 -211
- package/src/packageVersion.ts +1 -1
- package/src/sequence.ts +36 -38
- package/src/sharedIntervalCollection.ts +4 -3
- package/src/sharedSequence.ts +1 -1
- package/src/sparsematrix.ts +2 -2
|
@@ -15,20 +15,61 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
15
15
|
};
|
|
16
16
|
/* eslint-disable no-bitwise */
|
|
17
17
|
import { assert, TypedEventEmitter } from "@fluidframework/common-utils";
|
|
18
|
-
import {
|
|
18
|
+
import { UsageError } from "@fluidframework/container-utils";
|
|
19
|
+
import { addProperties, createMap, IntervalTree, LocalReference, MergeTreeDeltaType, PropertiesManager, RedBlackTree, ReferenceType, refTypeIncludesFlag, reservedRangeLabelsKey, UnassignedSequenceNumber, } from "@fluidframework/merge-tree";
|
|
20
|
+
import { LoggingError } from "@fluidframework/telemetry-utils";
|
|
19
21
|
import { v4 as uuid } from "uuid";
|
|
20
22
|
const reservedIntervalIdKey = "intervalId";
|
|
21
23
|
export var IntervalType;
|
|
22
24
|
(function (IntervalType) {
|
|
23
25
|
IntervalType[IntervalType["Simple"] = 0] = "Simple";
|
|
24
26
|
IntervalType[IntervalType["Nest"] = 1] = "Nest";
|
|
27
|
+
/**
|
|
28
|
+
* SlideOnRemove indicates that the ends of the interval will slide if the segment
|
|
29
|
+
* they reference is removed and acked.
|
|
30
|
+
* See `packages\dds\merge-tree\REFERENCEPOSITIONS.md` for details
|
|
31
|
+
* SlideOnRemove is the default interval behavior and does not need to be specified.
|
|
32
|
+
*/
|
|
25
33
|
IntervalType[IntervalType["SlideOnRemove"] = 2] = "SlideOnRemove";
|
|
34
|
+
/**
|
|
35
|
+
* @internal
|
|
36
|
+
* A temporary interval, used internally
|
|
37
|
+
*/
|
|
26
38
|
IntervalType[IntervalType["Transient"] = 4] = "Transient";
|
|
27
39
|
})(IntervalType || (IntervalType = {}));
|
|
40
|
+
/**
|
|
41
|
+
* Decompress an interval after loading a summary from JSON. The exact format
|
|
42
|
+
* of this compression is unspecified and subject to change
|
|
43
|
+
*/
|
|
44
|
+
function decompressInterval(interval, label) {
|
|
45
|
+
return {
|
|
46
|
+
start: interval[0],
|
|
47
|
+
end: interval[1],
|
|
48
|
+
sequenceNumber: interval[2],
|
|
49
|
+
intervalType: interval[3],
|
|
50
|
+
properties: Object.assign(Object.assign({}, interval[4]), { [reservedRangeLabelsKey]: label }),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Compress an interval prior to serialization as JSON. The exact format of this
|
|
55
|
+
* compression is unspecified and subject to change
|
|
56
|
+
*/
|
|
57
|
+
function compressInterval(interval) {
|
|
58
|
+
const { start, end, sequenceNumber, intervalType, properties } = interval;
|
|
59
|
+
return [
|
|
60
|
+
start,
|
|
61
|
+
end,
|
|
62
|
+
sequenceNumber,
|
|
63
|
+
intervalType,
|
|
64
|
+
Object.assign(Object.assign({}, properties), { [reservedRangeLabelsKey]: undefined }),
|
|
65
|
+
];
|
|
66
|
+
}
|
|
28
67
|
export class Interval {
|
|
29
68
|
constructor(start, end, props) {
|
|
30
69
|
this.start = start;
|
|
31
70
|
this.end = end;
|
|
71
|
+
this.propertyManager = new PropertiesManager();
|
|
72
|
+
this.properties = {};
|
|
32
73
|
if (props) {
|
|
33
74
|
this.addProperties(props);
|
|
34
75
|
}
|
|
@@ -111,12 +152,7 @@ export class Interval {
|
|
|
111
152
|
}
|
|
112
153
|
addProperties(newProps, collaborating = false, seq, op) {
|
|
113
154
|
if (newProps) {
|
|
114
|
-
|
|
115
|
-
this.propertyManager = new PropertiesManager();
|
|
116
|
-
}
|
|
117
|
-
if (!this.properties) {
|
|
118
|
-
this.properties = createMap();
|
|
119
|
-
}
|
|
155
|
+
this.initializeProperties();
|
|
120
156
|
return this.propertyManager.addProperties(this.properties, newProps, op, seq, collaborating);
|
|
121
157
|
}
|
|
122
158
|
}
|
|
@@ -127,7 +163,20 @@ export class Interval {
|
|
|
127
163
|
// Return undefined to indicate that no change is necessary.
|
|
128
164
|
return;
|
|
129
165
|
}
|
|
130
|
-
|
|
166
|
+
const newInterval = new Interval(startPos, endPos);
|
|
167
|
+
if (this.properties) {
|
|
168
|
+
newInterval.initializeProperties();
|
|
169
|
+
this.propertyManager.copyTo(this.properties, newInterval.properties, newInterval.propertyManager);
|
|
170
|
+
}
|
|
171
|
+
return newInterval;
|
|
172
|
+
}
|
|
173
|
+
initializeProperties() {
|
|
174
|
+
if (!this.propertyManager) {
|
|
175
|
+
this.propertyManager = new PropertiesManager();
|
|
176
|
+
}
|
|
177
|
+
if (!this.properties) {
|
|
178
|
+
this.properties = createMap();
|
|
179
|
+
}
|
|
131
180
|
}
|
|
132
181
|
}
|
|
133
182
|
export class SequenceInterval {
|
|
@@ -135,10 +184,41 @@ export class SequenceInterval {
|
|
|
135
184
|
this.start = start;
|
|
136
185
|
this.end = end;
|
|
137
186
|
this.intervalType = intervalType;
|
|
187
|
+
this.propertyManager = new PropertiesManager();
|
|
188
|
+
this.properties = {};
|
|
138
189
|
if (props) {
|
|
139
190
|
this.addProperties(props);
|
|
140
191
|
}
|
|
141
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* @internal
|
|
195
|
+
* Subscribes to position change events on this interval if there are no current listeners.
|
|
196
|
+
*/
|
|
197
|
+
addPositionChangeListeners(beforePositionChange, afterPositionChange) {
|
|
198
|
+
var _a, _b;
|
|
199
|
+
var _c, _d;
|
|
200
|
+
if (this.callbacks === undefined) {
|
|
201
|
+
this.callbacks = {
|
|
202
|
+
beforePositionChange,
|
|
203
|
+
afterPositionChange,
|
|
204
|
+
};
|
|
205
|
+
const startCbs = (_a = (_c = this.start).callbacks) !== null && _a !== void 0 ? _a : (_c.callbacks = {});
|
|
206
|
+
const endCbs = (_b = (_d = this.end).callbacks) !== null && _b !== void 0 ? _b : (_d.callbacks = {});
|
|
207
|
+
startCbs.beforeSlide = endCbs.beforeSlide = beforePositionChange;
|
|
208
|
+
startCbs.afterSlide = endCbs.afterSlide = afterPositionChange;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* @internal
|
|
213
|
+
* Removes the currently subscribed position change listeners.
|
|
214
|
+
*/
|
|
215
|
+
removePositionChangeListeners() {
|
|
216
|
+
if (this.callbacks) {
|
|
217
|
+
this.callbacks = undefined;
|
|
218
|
+
this.start.callbacks = undefined;
|
|
219
|
+
this.end.callbacks = undefined;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
142
222
|
serialize(client) {
|
|
143
223
|
const startPosition = this.start.toPosition();
|
|
144
224
|
const endPosition = this.end.toPosition();
|
|
@@ -202,12 +282,7 @@ export class SequenceInterval {
|
|
|
202
282
|
return new SequenceInterval(this.start.min(b.start), this.end.max(b.end), this.intervalType);
|
|
203
283
|
}
|
|
204
284
|
addProperties(newProps, collab = false, seq, op) {
|
|
205
|
-
|
|
206
|
-
this.propertyManager = new PropertiesManager();
|
|
207
|
-
}
|
|
208
|
-
if (!this.properties) {
|
|
209
|
-
this.properties = createMap();
|
|
210
|
-
}
|
|
285
|
+
this.initializeProperties();
|
|
211
286
|
return this.propertyManager.addProperties(this.properties, newProps, op, seq, collab);
|
|
212
287
|
}
|
|
213
288
|
overlapsPos(bstart, bend) {
|
|
@@ -216,59 +291,102 @@ export class SequenceInterval {
|
|
|
216
291
|
return (endPos > bstart) && (startPos < bend);
|
|
217
292
|
}
|
|
218
293
|
modify(label, start, end, op) {
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
294
|
+
const getRefType = (baseType) => {
|
|
295
|
+
let refType = baseType;
|
|
296
|
+
if (op === undefined) {
|
|
297
|
+
refType &= ~ReferenceType.SlideOnRemove;
|
|
298
|
+
refType |= ReferenceType.StayOnRemove;
|
|
299
|
+
}
|
|
300
|
+
return refType;
|
|
301
|
+
};
|
|
302
|
+
let startRef = this.start;
|
|
303
|
+
if (start !== undefined) {
|
|
304
|
+
startRef = createPositionReference(this.start.getClient(), start, getRefType(this.start.refType), op);
|
|
305
|
+
startRef.addProperties(this.start.properties);
|
|
306
|
+
}
|
|
307
|
+
let endRef = this.end;
|
|
308
|
+
if (end !== undefined) {
|
|
309
|
+
endRef = createPositionReference(this.end.getClient(), end, getRefType(this.end.refType), op);
|
|
310
|
+
endRef.addProperties(this.end.properties);
|
|
311
|
+
}
|
|
312
|
+
startRef.pairedRef = endRef;
|
|
313
|
+
endRef.pairedRef = startRef;
|
|
314
|
+
const newInterval = new SequenceInterval(startRef, endRef, this.intervalType);
|
|
226
315
|
if (this.properties) {
|
|
227
|
-
newInterval.
|
|
316
|
+
newInterval.initializeProperties();
|
|
317
|
+
this.propertyManager.copyTo(this.properties, newInterval.properties, newInterval.propertyManager);
|
|
228
318
|
}
|
|
229
319
|
return newInterval;
|
|
230
320
|
}
|
|
321
|
+
initializeProperties() {
|
|
322
|
+
if (!this.propertyManager) {
|
|
323
|
+
this.propertyManager = new PropertiesManager();
|
|
324
|
+
}
|
|
325
|
+
if (!this.properties) {
|
|
326
|
+
this.properties = createMap();
|
|
327
|
+
}
|
|
328
|
+
}
|
|
231
329
|
}
|
|
232
|
-
function
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
330
|
+
function createPositionReferenceFromSegoff(client, segoff, refType, op) {
|
|
331
|
+
if (segoff.segment) {
|
|
332
|
+
const ref = client.createLocalReferencePosition(segoff.segment, segoff.offset, refType, undefined);
|
|
333
|
+
return ref;
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
if (!op && !refTypeIncludesFlag(refType, ReferenceType.Transient)) {
|
|
337
|
+
throw new UsageError("Non-transient references need segment");
|
|
238
338
|
}
|
|
239
|
-
return
|
|
339
|
+
return new LocalReference(client, undefined, 0, refType);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
function createPositionReference(client, pos, refType, op) {
|
|
343
|
+
let segoff;
|
|
344
|
+
if (op) {
|
|
345
|
+
assert((refType & ReferenceType.SlideOnRemove) !== 0, 0x2f5 /* op create references must be SlideOnRemove */);
|
|
346
|
+
segoff = client.getContainingSegment(pos, op);
|
|
347
|
+
segoff = client.getSlideToSegment(segoff);
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
assert((refType & ReferenceType.SlideOnRemove) === 0, 0x2f6 /* SlideOnRemove references must be op created */);
|
|
351
|
+
segoff = client.getContainingSegment(pos);
|
|
240
352
|
}
|
|
241
|
-
return
|
|
353
|
+
return createPositionReferenceFromSegoff(client, segoff, refType, op);
|
|
242
354
|
}
|
|
243
355
|
function createSequenceInterval(label, start, end, client, intervalType, op) {
|
|
244
356
|
let beginRefType = ReferenceType.RangeBegin;
|
|
245
357
|
let endRefType = ReferenceType.RangeEnd;
|
|
246
|
-
if (intervalType === IntervalType.
|
|
247
|
-
beginRefType = ReferenceType.NestBegin;
|
|
248
|
-
endRefType = ReferenceType.NestEnd;
|
|
249
|
-
}
|
|
250
|
-
else if (intervalType === IntervalType.Transient) {
|
|
358
|
+
if (intervalType === IntervalType.Transient) {
|
|
251
359
|
beginRefType = ReferenceType.Transient;
|
|
252
360
|
endRefType = ReferenceType.Transient;
|
|
253
361
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
362
|
+
else {
|
|
363
|
+
if (intervalType === IntervalType.Nest) {
|
|
364
|
+
beginRefType = ReferenceType.NestBegin;
|
|
365
|
+
endRefType = ReferenceType.NestEnd;
|
|
366
|
+
}
|
|
367
|
+
// All non-transient interval references must eventually be SlideOnRemove
|
|
368
|
+
// To ensure eventual consistency, they must start as StayOnRemove when
|
|
369
|
+
// pending (created locally and creation op is not acked)
|
|
370
|
+
if (op) {
|
|
371
|
+
beginRefType |= ReferenceType.SlideOnRemove;
|
|
372
|
+
endRefType |= ReferenceType.SlideOnRemove;
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
beginRefType |= ReferenceType.StayOnRemove;
|
|
376
|
+
endRefType |= ReferenceType.StayOnRemove;
|
|
377
|
+
}
|
|
258
378
|
}
|
|
259
379
|
const startLref = createPositionReference(client, start, beginRefType, op);
|
|
260
380
|
const endLref = createPositionReference(client, end, endRefType, op);
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
return ival;
|
|
271
|
-
}
|
|
381
|
+
startLref.pairedRef = endLref;
|
|
382
|
+
endLref.pairedRef = startLref;
|
|
383
|
+
const rangeProp = {
|
|
384
|
+
[reservedRangeLabelsKey]: [label],
|
|
385
|
+
};
|
|
386
|
+
startLref.addProperties(rangeProp);
|
|
387
|
+
endLref.addProperties(rangeProp);
|
|
388
|
+
const ival = new SequenceInterval(startLref, endLref, intervalType, rangeProp);
|
|
389
|
+
return ival;
|
|
272
390
|
}
|
|
273
391
|
export function defaultIntervalConflictResolver(a, b) {
|
|
274
392
|
a.addPropertySet(b.properties);
|
|
@@ -289,11 +407,15 @@ export function createIntervalIndex(conflict) {
|
|
|
289
407
|
return lc;
|
|
290
408
|
}
|
|
291
409
|
export class LocalIntervalCollection {
|
|
292
|
-
constructor(client, label, helpers
|
|
410
|
+
constructor(client, label, helpers,
|
|
411
|
+
/** Callback invoked each time one of the endpoints of an interval slides. */
|
|
412
|
+
onPositionChange) {
|
|
293
413
|
this.client = client;
|
|
294
414
|
this.label = label;
|
|
295
415
|
this.helpers = helpers;
|
|
416
|
+
this.onPositionChange = onPositionChange;
|
|
296
417
|
this.intervalTree = new IntervalTree();
|
|
418
|
+
this.intervalIdMap = new Map();
|
|
297
419
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
298
420
|
this.endIntervalTree = new RedBlackTree(helpers.compareEnds);
|
|
299
421
|
}
|
|
@@ -301,7 +423,7 @@ export class LocalIntervalCollection {
|
|
|
301
423
|
this.conflictResolver = conflictResolver;
|
|
302
424
|
this.endConflictResolver =
|
|
303
425
|
(key, currentKey) => {
|
|
304
|
-
const ival =
|
|
426
|
+
const ival = conflictResolver(key, currentKey);
|
|
305
427
|
return {
|
|
306
428
|
data: ival,
|
|
307
429
|
key: ival,
|
|
@@ -316,13 +438,22 @@ export class LocalIntervalCollection {
|
|
|
316
438
|
// without ID's.
|
|
317
439
|
return `${LocalIntervalCollection.legacyIdPrefix}${start}-${end}`;
|
|
318
440
|
}
|
|
441
|
+
/**
|
|
442
|
+
* Validates that a serialized interval has the ID property. Creates an ID
|
|
443
|
+
* if one does not already exist
|
|
444
|
+
*
|
|
445
|
+
* @param serializedInterval - The interval to be checked
|
|
446
|
+
* @returns The interval's existing or newly created id
|
|
447
|
+
*/
|
|
319
448
|
ensureSerializedId(serializedInterval) {
|
|
320
449
|
var _a;
|
|
321
|
-
|
|
450
|
+
let id = (_a = serializedInterval.properties) === null || _a === void 0 ? void 0 : _a[reservedIntervalIdKey];
|
|
451
|
+
if (id === undefined) {
|
|
322
452
|
// An interval came over the wire without an ID, so create a non-unique one based on start/end.
|
|
323
453
|
// This will allow all clients to refer to this interval consistently.
|
|
454
|
+
id = this.createLegacyId(serializedInterval.start, serializedInterval.end);
|
|
324
455
|
const newProps = {
|
|
325
|
-
[reservedIntervalIdKey]:
|
|
456
|
+
[reservedIntervalIdKey]: id,
|
|
326
457
|
};
|
|
327
458
|
serializedInterval.properties = addProperties(serializedInterval.properties, newProps);
|
|
328
459
|
}
|
|
@@ -332,6 +463,7 @@ export class LocalIntervalCollection {
|
|
|
332
463
|
enumerable: true,
|
|
333
464
|
writable: false,
|
|
334
465
|
});
|
|
466
|
+
return id;
|
|
335
467
|
}
|
|
336
468
|
mapUntil(fn) {
|
|
337
469
|
this.intervalTree.mapUntil(fn);
|
|
@@ -398,14 +530,12 @@ export class LocalIntervalCollection {
|
|
|
398
530
|
}
|
|
399
531
|
}
|
|
400
532
|
findOverlappingIntervals(startPosition, endPosition) {
|
|
401
|
-
if (
|
|
402
|
-
const transientInterval = this.helpers.create("transient", startPosition, endPosition, this.client, IntervalType.Transient);
|
|
403
|
-
const overlappingIntervalNodes = this.intervalTree.match(transientInterval);
|
|
404
|
-
return overlappingIntervalNodes.map((node) => node.key);
|
|
405
|
-
}
|
|
406
|
-
else {
|
|
533
|
+
if (endPosition < startPosition || this.intervalTree.intervals.isEmpty()) {
|
|
407
534
|
return [];
|
|
408
535
|
}
|
|
536
|
+
const transientInterval = this.helpers.create("transient", startPosition, endPosition, this.client, IntervalType.Transient);
|
|
537
|
+
const overlappingIntervalNodes = this.intervalTree.match(transientInterval);
|
|
538
|
+
return overlappingIntervalNodes.map((node) => node.key);
|
|
409
539
|
}
|
|
410
540
|
previousInterval(pos) {
|
|
411
541
|
const transientInterval = this.helpers.create("transient", pos, pos, this.client, IntervalType.Transient);
|
|
@@ -427,9 +557,16 @@ export class LocalIntervalCollection {
|
|
|
427
557
|
this.endIntervalTree.remove(transientInterval);
|
|
428
558
|
return transientInterval;
|
|
429
559
|
}
|
|
430
|
-
|
|
560
|
+
removeIntervalFromIndex(interval) {
|
|
431
561
|
this.intervalTree.removeExisting(interval);
|
|
432
562
|
this.endIntervalTree.remove(interval);
|
|
563
|
+
const id = interval.getIntervalId();
|
|
564
|
+
assert(id !== undefined, 0x311 /* expected id to exist on interval */);
|
|
565
|
+
this.intervalIdMap.delete(id);
|
|
566
|
+
}
|
|
567
|
+
removeExistingInterval(interval) {
|
|
568
|
+
this.removeIntervalFromIndex(interval);
|
|
569
|
+
this.removeIntervalListeners(interval);
|
|
433
570
|
}
|
|
434
571
|
createInterval(start, end, intervalType, op) {
|
|
435
572
|
return this.helpers.create(this.label, start, end, this.client, intervalType, op);
|
|
@@ -451,8 +588,9 @@ export class LocalIntervalCollection {
|
|
|
451
588
|
}
|
|
452
589
|
return interval;
|
|
453
590
|
}
|
|
454
|
-
|
|
455
|
-
|
|
591
|
+
addIntervalToIndex(interval) {
|
|
592
|
+
const id = interval.getIntervalId();
|
|
593
|
+
assert(id !== undefined, 0x2c0 /* "ID must be created before adding interval to collection" */);
|
|
456
594
|
// Make the ID immutable.
|
|
457
595
|
Object.defineProperty(interval.properties, reservedIntervalIdKey, {
|
|
458
596
|
configurable: false,
|
|
@@ -461,17 +599,14 @@ export class LocalIntervalCollection {
|
|
|
461
599
|
});
|
|
462
600
|
this.intervalTree.put(interval, this.conflictResolver);
|
|
463
601
|
this.endIntervalTree.put(interval, interval, this.endConflictResolver);
|
|
602
|
+
this.intervalIdMap.set(id, interval);
|
|
603
|
+
}
|
|
604
|
+
add(interval) {
|
|
605
|
+
this.addIntervalToIndex(interval);
|
|
606
|
+
this.addIntervalListeners(interval);
|
|
464
607
|
}
|
|
465
608
|
getIntervalById(id) {
|
|
466
|
-
|
|
467
|
-
this.mapUntil((interval) => {
|
|
468
|
-
if (interval.getIntervalId() === id) {
|
|
469
|
-
result = interval;
|
|
470
|
-
return false;
|
|
471
|
-
}
|
|
472
|
-
return true;
|
|
473
|
-
});
|
|
474
|
-
return result;
|
|
609
|
+
return this.intervalIdMap.get(id);
|
|
475
610
|
}
|
|
476
611
|
changeInterval(interval, start, end, op) {
|
|
477
612
|
const newInterval = interval.modify(this.label, start, end, op);
|
|
@@ -484,7 +619,25 @@ export class LocalIntervalCollection {
|
|
|
484
619
|
serialize() {
|
|
485
620
|
const client = this.client;
|
|
486
621
|
const intervals = this.intervalTree.intervals.keys();
|
|
487
|
-
return
|
|
622
|
+
return {
|
|
623
|
+
label: this.label,
|
|
624
|
+
intervals: intervals.map((interval) => compressInterval(interval.serialize(client))),
|
|
625
|
+
version: 2,
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
addIntervalListeners(interval) {
|
|
629
|
+
if (interval instanceof SequenceInterval) {
|
|
630
|
+
interval.addPositionChangeListeners(() => this.removeIntervalFromIndex(interval), () => {
|
|
631
|
+
var _a;
|
|
632
|
+
this.addIntervalToIndex(interval);
|
|
633
|
+
(_a = this.onPositionChange) === null || _a === void 0 ? void 0 : _a.call(this, interval);
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
removeIntervalListeners(interval) {
|
|
638
|
+
if (interval instanceof SequenceInterval) {
|
|
639
|
+
interval.removePositionChangeListeners();
|
|
640
|
+
}
|
|
488
641
|
}
|
|
489
642
|
}
|
|
490
643
|
LocalIntervalCollection.legacyIdPrefix = "legacy";
|
|
@@ -514,37 +667,12 @@ export class SequenceIntervalCollectionValueType {
|
|
|
514
667
|
}
|
|
515
668
|
SequenceIntervalCollectionValueType.Name = "sharedStringIntervalCollection";
|
|
516
669
|
SequenceIntervalCollectionValueType._factory = new SequenceIntervalCollectionFactory();
|
|
517
|
-
SequenceIntervalCollectionValueType._ops =
|
|
518
|
-
"add",
|
|
519
|
-
{
|
|
520
|
-
process: (value, params, local, op) => {
|
|
521
|
-
value.ackAdd(params, local, op);
|
|
522
|
-
},
|
|
523
|
-
},
|
|
524
|
-
],
|
|
525
|
-
[
|
|
526
|
-
"delete",
|
|
527
|
-
{
|
|
528
|
-
process: (value, params, local, op) => {
|
|
529
|
-
value.ackDelete(params, local, op);
|
|
530
|
-
},
|
|
531
|
-
},
|
|
532
|
-
],
|
|
533
|
-
[
|
|
534
|
-
"change",
|
|
535
|
-
{
|
|
536
|
-
process: (value, params, local, op) => {
|
|
537
|
-
value.ackChange(params, local, op);
|
|
538
|
-
},
|
|
539
|
-
},
|
|
540
|
-
]]);
|
|
670
|
+
SequenceIntervalCollectionValueType._ops = makeOpsMap();
|
|
541
671
|
const compareIntervalEnds = (a, b) => a.end - b.end;
|
|
542
672
|
function createInterval(label, start, end, client) {
|
|
543
|
-
|
|
544
|
-
if (label &&
|
|
545
|
-
rangeProp =
|
|
546
|
-
[reservedRangeLabelsKey]: [label],
|
|
547
|
-
};
|
|
673
|
+
const rangeProp = {};
|
|
674
|
+
if (label && label.length > 0) {
|
|
675
|
+
rangeProp[reservedRangeLabelsKey] = [label];
|
|
548
676
|
}
|
|
549
677
|
return new Interval(start, end, rangeProp);
|
|
550
678
|
}
|
|
@@ -575,30 +703,45 @@ export class IntervalCollectionValueType {
|
|
|
575
703
|
}
|
|
576
704
|
IntervalCollectionValueType.Name = "sharedIntervalCollection";
|
|
577
705
|
IntervalCollectionValueType._factory = new IntervalCollectionFactory();
|
|
578
|
-
IntervalCollectionValueType._ops =
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
706
|
+
IntervalCollectionValueType._ops = makeOpsMap();
|
|
707
|
+
function makeOpsMap() {
|
|
708
|
+
const rebase = (collection, op, localOpMetadata) => {
|
|
709
|
+
const { localSeq } = localOpMetadata;
|
|
710
|
+
const rebasedValue = collection.rebaseLocalInterval(op.opName, op.value, localSeq);
|
|
711
|
+
const rebasedOp = Object.assign(Object.assign({}, op), { value: rebasedValue });
|
|
712
|
+
return { rebasedOp, rebasedLocalOpMetadata: localOpMetadata };
|
|
713
|
+
};
|
|
714
|
+
return new Map([[
|
|
715
|
+
"add",
|
|
716
|
+
{
|
|
717
|
+
process: (collection, params, local, op) => {
|
|
718
|
+
collection.ackAdd(params, local, op);
|
|
719
|
+
},
|
|
720
|
+
rebase,
|
|
583
721
|
},
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
722
|
+
],
|
|
723
|
+
[
|
|
724
|
+
"delete",
|
|
725
|
+
{
|
|
726
|
+
process: (collection, params, local, op) => {
|
|
727
|
+
collection.ackDelete(params, local, op);
|
|
728
|
+
},
|
|
729
|
+
rebase: (collection, op, localOpMetadata) => {
|
|
730
|
+
// Deletion of intervals is based on id, so requires no rebasing.
|
|
731
|
+
return { rebasedOp: op, rebasedLocalOpMetadata: localOpMetadata };
|
|
732
|
+
},
|
|
591
733
|
},
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
734
|
+
],
|
|
735
|
+
[
|
|
736
|
+
"change",
|
|
737
|
+
{
|
|
738
|
+
process: (collection, params, local, op) => {
|
|
739
|
+
collection.ackChange(params, local, op);
|
|
740
|
+
},
|
|
741
|
+
rebase,
|
|
599
742
|
},
|
|
600
|
-
|
|
601
|
-
|
|
743
|
+
]]);
|
|
744
|
+
}
|
|
602
745
|
export class IntervalCollectionIterator {
|
|
603
746
|
constructor(collection, iteratesForward = true, start, end) {
|
|
604
747
|
this.results = [];
|
|
@@ -619,26 +762,35 @@ export class IntervalCollectionIterator {
|
|
|
619
762
|
}
|
|
620
763
|
}
|
|
621
764
|
export class IntervalCollection extends TypedEventEmitter {
|
|
765
|
+
/** @internal */
|
|
622
766
|
constructor(helpers, requiresClient, emitter, serializedIntervals) {
|
|
623
767
|
super();
|
|
624
768
|
this.helpers = helpers;
|
|
625
769
|
this.requiresClient = requiresClient;
|
|
626
770
|
this.emitter = emitter;
|
|
627
|
-
this.
|
|
771
|
+
this.pendingChangesStart = new Map();
|
|
772
|
+
this.pendingChangesEnd = new Map();
|
|
773
|
+
if (Array.isArray(serializedIntervals)) {
|
|
774
|
+
this.savedSerializedIntervals = serializedIntervals;
|
|
775
|
+
}
|
|
776
|
+
else {
|
|
777
|
+
this.savedSerializedIntervals =
|
|
778
|
+
serializedIntervals.intervals.map((i) => decompressInterval(i, serializedIntervals.label));
|
|
779
|
+
}
|
|
628
780
|
}
|
|
629
781
|
get attached() {
|
|
630
782
|
return !!this.localCollection;
|
|
631
783
|
}
|
|
632
784
|
attachGraph(client, label) {
|
|
633
785
|
if (this.attached) {
|
|
634
|
-
throw new
|
|
786
|
+
throw new LoggingError("Only supports one Sequence attach");
|
|
635
787
|
}
|
|
636
788
|
if ((client === undefined) && (this.requiresClient)) {
|
|
637
|
-
throw new
|
|
789
|
+
throw new LoggingError("Client required for this collection");
|
|
638
790
|
}
|
|
639
791
|
// Instantiate the local interval collection based on the saved intervals
|
|
640
792
|
this.client = client;
|
|
641
|
-
this.localCollection = new LocalIntervalCollection(client, label, this.helpers);
|
|
793
|
+
this.localCollection = new LocalIntervalCollection(client, label, this.helpers, (interval) => this.emit("changeInterval", interval, true, undefined));
|
|
642
794
|
if (this.savedSerializedIntervals) {
|
|
643
795
|
for (const serializedInterval of this.savedSerializedIntervals) {
|
|
644
796
|
this.localCollection.ensureSerializedId(serializedInterval);
|
|
@@ -647,16 +799,33 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
647
799
|
}
|
|
648
800
|
this.savedSerializedIntervals = undefined;
|
|
649
801
|
}
|
|
802
|
+
/**
|
|
803
|
+
* Gets the next local sequence number, modifying this client's collab window in doing so.
|
|
804
|
+
*/
|
|
805
|
+
getNextLocalSeq() {
|
|
806
|
+
return ++this.client.getCollabWindow().localSeq;
|
|
807
|
+
}
|
|
650
808
|
getIntervalById(id) {
|
|
651
809
|
if (!this.attached) {
|
|
652
|
-
throw new
|
|
810
|
+
throw new LoggingError("attach must be called before accessing intervals");
|
|
653
811
|
}
|
|
654
812
|
return this.localCollection.getIntervalById(id);
|
|
655
813
|
}
|
|
814
|
+
/**
|
|
815
|
+
* Create a new interval and add it to the collection
|
|
816
|
+
* @param start - interval start position
|
|
817
|
+
* @param end - interval end position
|
|
818
|
+
* @param intervalType - type of the interval. All intervals are SlideOnRemove. Intervals may not be Transient.
|
|
819
|
+
* @param props - properties of the interval
|
|
820
|
+
* @returns - the created interval
|
|
821
|
+
*/
|
|
656
822
|
add(start, end, intervalType, props) {
|
|
657
823
|
var _a, _b;
|
|
658
824
|
if (!this.attached) {
|
|
659
|
-
throw new
|
|
825
|
+
throw new LoggingError("attach must be called prior to adding intervals");
|
|
826
|
+
}
|
|
827
|
+
if (intervalType & IntervalType.Transient) {
|
|
828
|
+
throw new LoggingError("Can not add transient intervals");
|
|
660
829
|
}
|
|
661
830
|
const interval = this.localCollection.addInterval(start, end, intervalType, props);
|
|
662
831
|
if (interval) {
|
|
@@ -668,7 +837,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
668
837
|
start,
|
|
669
838
|
};
|
|
670
839
|
// Local ops get submitted to the server. Remote ops have the deserializer run.
|
|
671
|
-
this.emitter.emit("add", undefined, serializedInterval);
|
|
840
|
+
this.emitter.emit("add", undefined, serializedInterval, { localSeq: this.getNextLocalSeq() });
|
|
672
841
|
}
|
|
673
842
|
this.emit("addInterval", interval, true, undefined);
|
|
674
843
|
return interval;
|
|
@@ -679,7 +848,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
679
848
|
if (interval) {
|
|
680
849
|
// Local ops get submitted to the server. Remote ops have the deserializer run.
|
|
681
850
|
if (local) {
|
|
682
|
-
this.emitter.emit("delete", undefined, interval.serialize(this.client));
|
|
851
|
+
this.emitter.emit("delete", undefined, interval.serialize(this.client), { localSeq: this.getNextLocalSeq() });
|
|
683
852
|
}
|
|
684
853
|
else {
|
|
685
854
|
if (this.onDeserialize) {
|
|
@@ -698,40 +867,41 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
698
867
|
}
|
|
699
868
|
changeProperties(id, props) {
|
|
700
869
|
if (!this.attached) {
|
|
701
|
-
throw new
|
|
870
|
+
throw new LoggingError("Attach must be called before accessing intervals");
|
|
702
871
|
}
|
|
703
872
|
if (typeof (id) !== "string") {
|
|
704
|
-
throw new
|
|
873
|
+
throw new LoggingError("Change API requires an ID that is a string");
|
|
705
874
|
}
|
|
706
875
|
if (!props) {
|
|
707
|
-
throw new
|
|
876
|
+
throw new LoggingError("changeProperties should be called with a property set");
|
|
708
877
|
}
|
|
709
878
|
const interval = this.getIntervalById(id);
|
|
710
879
|
if (interval) {
|
|
711
880
|
// Pass Unassigned as the sequence number to indicate that this is a local op that is waiting for an ack.
|
|
712
881
|
const deltaProps = interval.addProperties(props, true, UnassignedSequenceNumber);
|
|
713
882
|
const serializedInterval = interval.serialize(this.client);
|
|
714
|
-
// Emit a change op that will only change properties. Add the ID to
|
|
883
|
+
// Emit a change op that will only change properties. Add the ID to
|
|
884
|
+
// the property bag provided by the caller.
|
|
715
885
|
serializedInterval.start = undefined;
|
|
716
886
|
serializedInterval.end = undefined;
|
|
717
887
|
serializedInterval.properties = props;
|
|
718
888
|
serializedInterval.properties[reservedIntervalIdKey] = interval.getIntervalId();
|
|
719
|
-
this.emitter.emit("change", undefined, serializedInterval);
|
|
889
|
+
this.emitter.emit("change", undefined, serializedInterval, { localSeq: this.getNextLocalSeq() });
|
|
720
890
|
this.emit("propertyChanged", interval, deltaProps);
|
|
721
891
|
}
|
|
722
892
|
this.emit("changeInterval", interval, true, undefined);
|
|
723
893
|
}
|
|
724
894
|
change(id, start, end) {
|
|
725
895
|
if (!this.attached) {
|
|
726
|
-
throw new
|
|
896
|
+
throw new LoggingError("Attach must be called before accessing intervals");
|
|
727
897
|
}
|
|
898
|
+
// Force id to be a string.
|
|
728
899
|
if (typeof (id) !== "string") {
|
|
729
|
-
throw new
|
|
900
|
+
throw new LoggingError("Change API requires an ID that is a string");
|
|
730
901
|
}
|
|
731
|
-
// Force id to be a string.
|
|
732
902
|
const interval = this.getIntervalById(id);
|
|
733
903
|
if (interval) {
|
|
734
|
-
this.localCollection.changeInterval(interval, start, end);
|
|
904
|
+
const newInterval = this.localCollection.changeInterval(interval, start, end);
|
|
735
905
|
const serializedInterval = interval.serialize(this.client);
|
|
736
906
|
serializedInterval.start = start;
|
|
737
907
|
serializedInterval.end = end;
|
|
@@ -740,23 +910,19 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
740
910
|
{
|
|
741
911
|
[reservedIntervalIdKey]: interval.getIntervalId(),
|
|
742
912
|
};
|
|
743
|
-
this.emitter.emit("change", undefined, serializedInterval);
|
|
913
|
+
this.emitter.emit("change", undefined, serializedInterval, { localSeq: this.getNextLocalSeq() });
|
|
744
914
|
this.addPendingChange(id, serializedInterval);
|
|
915
|
+
this.emit("changeInterval", newInterval, true, undefined);
|
|
916
|
+
return newInterval;
|
|
745
917
|
}
|
|
746
|
-
|
|
747
|
-
return
|
|
918
|
+
// No interval to change
|
|
919
|
+
return undefined;
|
|
748
920
|
}
|
|
749
921
|
addPendingChange(id, serializedInterval) {
|
|
750
922
|
if (serializedInterval.start !== undefined) {
|
|
751
|
-
if (!this.pendingChangesStart) {
|
|
752
|
-
this.pendingChangesStart = new Map();
|
|
753
|
-
}
|
|
754
923
|
this.addPendingChangeHelper(id, this.pendingChangesStart, serializedInterval);
|
|
755
924
|
}
|
|
756
925
|
if (serializedInterval.end !== undefined) {
|
|
757
|
-
if (!this.pendingChangesEnd) {
|
|
758
|
-
this.pendingChangesEnd = new Map();
|
|
759
|
-
}
|
|
760
926
|
this.addPendingChangeHelper(id, this.pendingChangesEnd, serializedInterval);
|
|
761
927
|
}
|
|
762
928
|
}
|
|
@@ -769,8 +935,9 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
769
935
|
entries.push(serializedInterval);
|
|
770
936
|
}
|
|
771
937
|
removePendingChange(serializedInterval) {
|
|
938
|
+
var _a;
|
|
772
939
|
// Change ops always have an ID.
|
|
773
|
-
const id = serializedInterval.properties[reservedIntervalIdKey];
|
|
940
|
+
const id = (_a = serializedInterval.properties) === null || _a === void 0 ? void 0 : _a[reservedIntervalIdKey];
|
|
774
941
|
if (serializedInterval.start !== undefined) {
|
|
775
942
|
this.removePendingChangeHelper(id, this.pendingChangesStart, serializedInterval);
|
|
776
943
|
}
|
|
@@ -779,26 +946,24 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
779
946
|
}
|
|
780
947
|
}
|
|
781
948
|
removePendingChangeHelper(id, pendingChanges, serializedInterval) {
|
|
782
|
-
const entries = pendingChanges
|
|
949
|
+
const entries = pendingChanges.get(id);
|
|
783
950
|
if (entries) {
|
|
784
951
|
const pendingChange = entries.shift();
|
|
785
952
|
if (entries.length === 0) {
|
|
786
953
|
pendingChanges.delete(id);
|
|
787
954
|
}
|
|
788
|
-
if (pendingChange.start !== serializedInterval.start ||
|
|
789
|
-
pendingChange.end !== serializedInterval.end) {
|
|
790
|
-
throw new
|
|
955
|
+
if ((pendingChange === null || pendingChange === void 0 ? void 0 : pendingChange.start) !== serializedInterval.start ||
|
|
956
|
+
(pendingChange === null || pendingChange === void 0 ? void 0 : pendingChange.end) !== serializedInterval.end) {
|
|
957
|
+
throw new LoggingError("Mismatch in pending changes");
|
|
791
958
|
}
|
|
792
959
|
}
|
|
793
960
|
}
|
|
794
961
|
hasPendingChangeStart(id) {
|
|
795
|
-
|
|
796
|
-
const entries = (_a = this.pendingChangesStart) === null || _a === void 0 ? void 0 : _a.get(id);
|
|
962
|
+
const entries = this.pendingChangesStart.get(id);
|
|
797
963
|
return entries && entries.length !== 0;
|
|
798
964
|
}
|
|
799
965
|
hasPendingChangeEnd(id) {
|
|
800
|
-
|
|
801
|
-
const entries = (_a = this.pendingChangesEnd) === null || _a === void 0 ? void 0 : _a.get(id);
|
|
966
|
+
const entries = this.pendingChangesEnd.get(id);
|
|
802
967
|
return entries && entries.length !== 0;
|
|
803
968
|
}
|
|
804
969
|
/** @deprecated - use ackChange */
|
|
@@ -807,22 +972,23 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
807
972
|
}
|
|
808
973
|
/** @internal */
|
|
809
974
|
ackChange(serializedInterval, local, op) {
|
|
810
|
-
var _a, _b;
|
|
975
|
+
var _a, _b, _c, _d;
|
|
811
976
|
if (!this.attached) {
|
|
812
|
-
throw new
|
|
977
|
+
throw new LoggingError("Attach must be called before accessing intervals");
|
|
813
978
|
}
|
|
814
979
|
let interval;
|
|
815
980
|
if (local) {
|
|
816
981
|
// This is an ack from the server. Remove the pending change.
|
|
817
982
|
this.removePendingChange(serializedInterval);
|
|
818
|
-
const id = serializedInterval.properties[reservedIntervalIdKey];
|
|
983
|
+
const id = (_a = serializedInterval.properties) === null || _a === void 0 ? void 0 : _a[reservedIntervalIdKey];
|
|
819
984
|
interval = this.getIntervalById(id);
|
|
820
985
|
if (interval) {
|
|
821
986
|
// Let the propertyManager prune its pending change-properties set.
|
|
822
|
-
(
|
|
823
|
-
type:
|
|
824
|
-
props: serializedInterval.properties,
|
|
987
|
+
(_b = interval.propertyManager) === null || _b === void 0 ? void 0 : _b.ackPendingProperties({
|
|
988
|
+
type: MergeTreeDeltaType.ANNOTATE,
|
|
989
|
+
props: (_c = serializedInterval.properties) !== null && _c !== void 0 ? _c : {},
|
|
825
990
|
});
|
|
991
|
+
this.ackInterval(interval, op);
|
|
826
992
|
}
|
|
827
993
|
}
|
|
828
994
|
else {
|
|
@@ -831,7 +997,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
831
997
|
// Note that the ID is in the property bag only to allow us to find the interval.
|
|
832
998
|
// This API cannot change the ID, and writing to the ID property will result in an exception. So we
|
|
833
999
|
// strip it out of the properties here.
|
|
834
|
-
const
|
|
1000
|
+
const _e = serializedInterval.properties, _f = reservedIntervalIdKey, id = _e[_f], newProps = __rest(_e, [typeof _f === "symbol" ? _f : _f + ""]);
|
|
835
1001
|
interval = this.getIntervalById(id);
|
|
836
1002
|
if (interval) {
|
|
837
1003
|
let start;
|
|
@@ -846,7 +1012,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
846
1012
|
if (start !== undefined || end !== undefined) {
|
|
847
1013
|
// If changeInterval gives us a new interval, work with that one. Otherwise keep working with
|
|
848
1014
|
// the one we originally found in the tree.
|
|
849
|
-
interval = (
|
|
1015
|
+
interval = (_d = this.localCollection.changeInterval(interval, start, end, op)) !== null && _d !== void 0 ? _d : interval;
|
|
850
1016
|
}
|
|
851
1017
|
const deltaProps = interval.addProperties(newProps, true, op.sequenceNumber);
|
|
852
1018
|
if (this.onDeserialize) {
|
|
@@ -861,7 +1027,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
861
1027
|
}
|
|
862
1028
|
addConflictResolver(conflictResolver) {
|
|
863
1029
|
if (!this.attached) {
|
|
864
|
-
throw new
|
|
1030
|
+
throw new LoggingError("attachSequence must be called");
|
|
865
1031
|
}
|
|
866
1032
|
this.localCollection.addConflictResolver(conflictResolver);
|
|
867
1033
|
}
|
|
@@ -874,22 +1040,109 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
874
1040
|
this.onDeserialize = onDeserialize;
|
|
875
1041
|
// Trigger the async prepare work across all values in the collection
|
|
876
1042
|
this.localCollection.map((interval) => {
|
|
877
|
-
|
|
1043
|
+
onDeserialize(interval);
|
|
878
1044
|
});
|
|
879
1045
|
}
|
|
1046
|
+
/** @internal */
|
|
1047
|
+
rebaseLocalInterval(opName, serializedInterval, localSeq) {
|
|
1048
|
+
var _a, _b;
|
|
1049
|
+
if (!this.attached) {
|
|
1050
|
+
throw new LoggingError("attachSequence must be called");
|
|
1051
|
+
}
|
|
1052
|
+
const { start, end, intervalType, properties, sequenceNumber } = serializedInterval;
|
|
1053
|
+
const startRebased = start === undefined ? undefined :
|
|
1054
|
+
this.client.rebasePosition(start, sequenceNumber, localSeq);
|
|
1055
|
+
const endRebased = end === undefined ? undefined :
|
|
1056
|
+
this.client.rebasePosition(end, sequenceNumber, localSeq);
|
|
1057
|
+
const intervalId = properties === null || properties === void 0 ? void 0 : properties[reservedIntervalIdKey];
|
|
1058
|
+
const rebased = {
|
|
1059
|
+
start: startRebased,
|
|
1060
|
+
end: endRebased,
|
|
1061
|
+
intervalType,
|
|
1062
|
+
sequenceNumber: (_b = (_a = this.client) === null || _a === void 0 ? void 0 : _a.getCurrentSeq()) !== null && _b !== void 0 ? _b : 0,
|
|
1063
|
+
properties,
|
|
1064
|
+
};
|
|
1065
|
+
if (opName === "change" && (this.hasPendingChangeStart(intervalId) || this.hasPendingChangeEnd(intervalId))) {
|
|
1066
|
+
this.removePendingChange(serializedInterval);
|
|
1067
|
+
this.addPendingChange(intervalId, rebased);
|
|
1068
|
+
}
|
|
1069
|
+
return rebased;
|
|
1070
|
+
}
|
|
1071
|
+
getSlideToSegment(lref) {
|
|
1072
|
+
const segoff = { segment: lref.segment, offset: lref.offset };
|
|
1073
|
+
const newSegoff = this.client.getSlideToSegment(segoff);
|
|
1074
|
+
const value = (segoff.segment === newSegoff.segment && segoff.offset === newSegoff.offset) ? undefined : newSegoff;
|
|
1075
|
+
return value;
|
|
1076
|
+
}
|
|
1077
|
+
setSlideOnRemove(lref) {
|
|
1078
|
+
let refType = lref.refType;
|
|
1079
|
+
refType = refType & ~ReferenceType.StayOnRemove;
|
|
1080
|
+
refType = refType | ReferenceType.SlideOnRemove;
|
|
1081
|
+
lref.refType = refType;
|
|
1082
|
+
}
|
|
1083
|
+
ackInterval(interval, op) {
|
|
1084
|
+
// in current usage, interval is always a SequenceInterval
|
|
1085
|
+
if (!(interval instanceof SequenceInterval)) {
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
1088
|
+
if (!refTypeIncludesFlag(interval.start, ReferenceType.StayOnRemove) &&
|
|
1089
|
+
!refTypeIncludesFlag(interval.end, ReferenceType.StayOnRemove)) {
|
|
1090
|
+
return;
|
|
1091
|
+
}
|
|
1092
|
+
const newStart = this.getSlideToSegment(interval.start);
|
|
1093
|
+
const newEnd = this.getSlideToSegment(interval.end);
|
|
1094
|
+
const id = interval.properties[reservedIntervalIdKey];
|
|
1095
|
+
const hasPendingStartChange = this.hasPendingChangeStart(id);
|
|
1096
|
+
const hasPendingEndChange = this.hasPendingChangeEnd(id);
|
|
1097
|
+
if (!hasPendingStartChange) {
|
|
1098
|
+
this.setSlideOnRemove(interval.start);
|
|
1099
|
+
}
|
|
1100
|
+
if (!hasPendingEndChange) {
|
|
1101
|
+
this.setSlideOnRemove(interval.end);
|
|
1102
|
+
}
|
|
1103
|
+
const needsStartUpdate = newStart !== undefined && !hasPendingStartChange;
|
|
1104
|
+
const needsEndUpdate = newEnd !== undefined && !hasPendingEndChange;
|
|
1105
|
+
if (needsStartUpdate || needsEndUpdate) {
|
|
1106
|
+
// In this case, where we change the start or end of an interval,
|
|
1107
|
+
// it is necessary to remove and re-add the interval listeners.
|
|
1108
|
+
// This ensures that the correct listeners are added to the ReferencePosition.
|
|
1109
|
+
this.localCollection.removeExistingInterval(interval);
|
|
1110
|
+
if (needsStartUpdate) {
|
|
1111
|
+
const props = interval.start.properties;
|
|
1112
|
+
this.client.removeLocalReferencePosition(interval.start);
|
|
1113
|
+
interval.start = createPositionReferenceFromSegoff(this.client, newStart, interval.start.refType, op);
|
|
1114
|
+
if (props) {
|
|
1115
|
+
interval.start.addProperties(props);
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
if (needsEndUpdate) {
|
|
1119
|
+
const props = interval.end.properties;
|
|
1120
|
+
this.client.removeLocalReferencePosition(interval.end);
|
|
1121
|
+
interval.end = createPositionReferenceFromSegoff(this.client, newEnd, interval.end.refType, op);
|
|
1122
|
+
if (props) {
|
|
1123
|
+
interval.end.addProperties(props);
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
this.localCollection.add(interval);
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
880
1129
|
/** @deprecated - use ackAdd */
|
|
881
1130
|
addInternal(serializedInterval, local, op) {
|
|
882
1131
|
return this.ackAdd(serializedInterval, local, op);
|
|
883
1132
|
}
|
|
884
1133
|
/** @internal */
|
|
885
1134
|
ackAdd(serializedInterval, local, op) {
|
|
1135
|
+
var _a;
|
|
886
1136
|
if (local) {
|
|
887
|
-
|
|
888
|
-
|
|
1137
|
+
const id = (_a = serializedInterval.properties) === null || _a === void 0 ? void 0 : _a[reservedIntervalIdKey];
|
|
1138
|
+
const localInterval = this.getIntervalById(id);
|
|
1139
|
+
if (localInterval) {
|
|
1140
|
+
this.ackInterval(localInterval, op);
|
|
1141
|
+
}
|
|
889
1142
|
return;
|
|
890
1143
|
}
|
|
891
1144
|
if (!this.attached) {
|
|
892
|
-
throw new
|
|
1145
|
+
throw new LoggingError("attachSequence must be called");
|
|
893
1146
|
}
|
|
894
1147
|
this.localCollection.ensureSerializedId(serializedInterval);
|
|
895
1148
|
const interval = this.localCollection.addInterval(serializedInterval.start, serializedInterval.end, serializedInterval.intervalType, serializedInterval.properties, op);
|
|
@@ -914,17 +1167,20 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
914
1167
|
return;
|
|
915
1168
|
}
|
|
916
1169
|
if (!this.attached) {
|
|
917
|
-
throw new
|
|
1170
|
+
throw new LoggingError("attach must be called prior to deleting intervals");
|
|
918
1171
|
}
|
|
919
|
-
this.localCollection.ensureSerializedId(serializedInterval);
|
|
920
|
-
const interval = this.localCollection.getIntervalById(
|
|
1172
|
+
const id = this.localCollection.ensureSerializedId(serializedInterval);
|
|
1173
|
+
const interval = this.localCollection.getIntervalById(id);
|
|
921
1174
|
if (interval) {
|
|
922
1175
|
this.deleteExistingInterval(interval, local, op);
|
|
923
1176
|
}
|
|
924
1177
|
}
|
|
1178
|
+
/**
|
|
1179
|
+
* @internal
|
|
1180
|
+
*/
|
|
925
1181
|
serializeInternal() {
|
|
926
1182
|
if (!this.attached) {
|
|
927
|
-
throw new
|
|
1183
|
+
throw new LoggingError("attachSequence must be called");
|
|
928
1184
|
}
|
|
929
1185
|
return this.localCollection.serialize();
|
|
930
1186
|
}
|
|
@@ -956,25 +1212,25 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
956
1212
|
}
|
|
957
1213
|
findOverlappingIntervals(startPosition, endPosition) {
|
|
958
1214
|
if (!this.attached) {
|
|
959
|
-
throw new
|
|
1215
|
+
throw new LoggingError("attachSequence must be called");
|
|
960
1216
|
}
|
|
961
1217
|
return this.localCollection.findOverlappingIntervals(startPosition, endPosition);
|
|
962
1218
|
}
|
|
963
1219
|
map(fn) {
|
|
964
1220
|
if (!this.attached) {
|
|
965
|
-
throw new
|
|
1221
|
+
throw new LoggingError("attachSequence must be called");
|
|
966
1222
|
}
|
|
967
1223
|
this.localCollection.map(fn);
|
|
968
1224
|
}
|
|
969
1225
|
previousInterval(pos) {
|
|
970
1226
|
if (!this.attached) {
|
|
971
|
-
throw new
|
|
1227
|
+
throw new LoggingError("attachSequence must be called");
|
|
972
1228
|
}
|
|
973
1229
|
return this.localCollection.previousInterval(pos);
|
|
974
1230
|
}
|
|
975
1231
|
nextInterval(pos) {
|
|
976
1232
|
if (!this.attached) {
|
|
977
|
-
throw new
|
|
1233
|
+
throw new LoggingError("attachSequence must be called");
|
|
978
1234
|
}
|
|
979
1235
|
return this.localCollection.nextInterval(pos);
|
|
980
1236
|
}
|