@textbus/collaborate 3.0.0-alpha.33 → 3.0.0-alpha.34

Sign up to get free protection for your applications and to get access to all the features.
package/bundles/index.js CHANGED
@@ -35,726 +35,726 @@ function __metadata(metadataKey, metadataValue) {
35
35
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue);
36
36
  }
37
37
 
38
- const collaborateErrorFn = core.makeError('Collaborate');
39
- class ContentMap {
40
- constructor() {
41
- this.slotAndYTextMap = new WeakMap();
42
- this.yTextAndSLotMap = new WeakMap();
43
- }
44
- set(key, value) {
45
- if (key instanceof core.Slot) {
46
- this.slotAndYTextMap.set(key, value);
47
- this.yTextAndSLotMap.set(value, key);
48
- }
49
- else {
50
- this.slotAndYTextMap.set(value, key);
51
- this.yTextAndSLotMap.set(key, value);
52
- }
53
- }
54
- get(key) {
55
- if (key instanceof core.Slot) {
56
- return this.slotAndYTextMap.get(key) || null;
57
- }
58
- return this.yTextAndSLotMap.get(key) || null;
59
- }
60
- delete(key) {
61
- if (key instanceof core.Slot) {
62
- const v = this.slotAndYTextMap.get(key);
63
- this.slotAndYTextMap.delete(key);
64
- if (v) {
65
- this.yTextAndSLotMap.delete(v);
66
- }
67
- }
68
- else {
69
- const v = this.yTextAndSLotMap.get(key);
70
- this.yTextAndSLotMap.delete(key);
71
- if (v) {
72
- this.slotAndYTextMap.delete(v);
73
- }
74
- }
75
- }
76
- }
77
- exports.Collaborate = class Collaborate {
78
- constructor(stackSize, rootComponentRef, controller, scheduler, registry, selection, starter) {
79
- this.stackSize = stackSize;
80
- this.rootComponentRef = rootComponentRef;
81
- this.controller = controller;
82
- this.scheduler = scheduler;
83
- this.registry = registry;
84
- this.selection = selection;
85
- this.starter = starter;
86
- this.yDoc = new yjs.Doc();
87
- this.backEvent = new stream.Subject();
88
- this.forwardEvent = new stream.Subject();
89
- this.changeEvent = new stream.Subject();
90
- this.pushEvent = new stream.Subject();
91
- this.manager = null;
92
- this.subscriptions = [];
93
- this.updateFromRemote = false;
94
- this.contentSyncCaches = new WeakMap();
95
- this.slotStateSyncCaches = new WeakMap();
96
- this.slotsSyncCaches = new WeakMap();
97
- this.componentStateSyncCaches = new WeakMap();
98
- this.localChangesAppliedEvent = new stream.Subject();
99
- this.selectionChangeEvent = new stream.Subject();
100
- this.contentMap = new ContentMap();
101
- this.updateRemoteActions = [];
102
- this.noRecord = {};
103
- this.historyItems = [];
104
- this.index = 0;
105
- this.onBack = this.backEvent.asObservable();
106
- this.onForward = this.forwardEvent.asObservable();
107
- this.onChange = this.changeEvent.asObservable();
108
- this.onPush = this.pushEvent.asObservable();
109
- this.onLocalChangesApplied = this.localChangesAppliedEvent.asObservable();
110
- }
111
- get canBack() {
112
- var _a;
113
- return ((_a = this.manager) === null || _a === void 0 ? void 0 : _a.canUndo()) || false;
114
- }
115
- get canForward() {
116
- var _a;
117
- return ((_a = this.manager) === null || _a === void 0 ? void 0 : _a.canRedo()) || false;
118
- }
119
- listen() {
120
- const root = this.yDoc.getMap('RootComponent');
121
- const rootComponent = this.rootComponentRef.component;
122
- const manager = new yjs.UndoManager(root, {
123
- trackedOrigins: new Set([this.yDoc])
124
- });
125
- this.manager = manager;
126
- manager.on('stack-item-added', event => {
127
- if (event.type === 'undo') {
128
- if (event.origin === manager) {
129
- this.index++;
130
- }
131
- else {
132
- this.historyItems.length = this.index;
133
- this.historyItems.push(this.getRelativeCursorLocation());
134
- this.index++;
135
- }
136
- }
137
- else {
138
- this.index--;
139
- }
140
- if (manager.undoStack.length > this.stackSize) {
141
- this.historyItems.shift();
142
- manager.undoStack.shift();
143
- }
144
- if (event.origin === this.yDoc) {
145
- this.pushEvent.next();
146
- }
147
- this.changeEvent.next();
148
- });
149
- manager.on('stack-item-popped', () => {
150
- const position = this.historyItems[this.index - 1];
151
- if (position) {
152
- const selection = this.getAbstractSelection(position);
153
- if (selection) {
154
- this.selection.setBaseAndExtent(selection.anchorSlot, selection.anchorOffset, selection.focusSlot, selection.focusOffset);
155
- return;
156
- }
157
- }
158
- this.selection.unSelect();
159
- });
160
- this.subscriptions.push(this.selection.onChange.subscribe(() => {
161
- const paths = this.selection.getPaths();
162
- this.selectionChangeEvent.next(paths);
163
- }), this.scheduler.onDocChanged.pipe(stream.map(item => {
164
- return item.filter(i => {
165
- return i.from !== core.ChangeOrigin.Remote;
166
- });
167
- }), stream.filter(item => {
168
- return item.length;
169
- })).subscribe(() => {
170
- const updates = [];
171
- let update = null;
172
- for (const item of this.updateRemoteActions) {
173
- if (!update) {
174
- update = {
175
- record: item.record,
176
- actions: []
177
- };
178
- updates.push(update);
179
- }
180
- if (update.record === item.record) {
181
- update.actions.push(item.action);
182
- }
183
- else {
184
- update = {
185
- record: item.record,
186
- actions: [item.action]
187
- };
188
- updates.push(update);
189
- }
190
- }
191
- this.updateRemoteActions = [];
192
- for (const item of updates) {
193
- this.yDoc.transact(() => {
194
- item.actions.forEach(fn => {
195
- fn();
196
- });
197
- }, item.record ? this.yDoc : this.noRecord);
198
- }
199
- this.localChangesAppliedEvent.next();
200
- }));
201
- this.syncRootComponent(root, rootComponent);
202
- }
203
- back() {
204
- var _a;
205
- if (this.canBack) {
206
- (_a = this.manager) === null || _a === void 0 ? void 0 : _a.undo();
207
- this.backEvent.next();
208
- }
209
- }
210
- forward() {
211
- var _a;
212
- if (this.canForward) {
213
- (_a = this.manager) === null || _a === void 0 ? void 0 : _a.redo();
214
- this.forwardEvent.next();
215
- }
216
- }
217
- clear() {
218
- var _a;
219
- this.index = 0;
220
- this.historyItems = [];
221
- (_a = this.manager) === null || _a === void 0 ? void 0 : _a.clear();
222
- this.changeEvent.next();
223
- }
224
- destroy() {
225
- var _a;
226
- this.index = 0;
227
- this.historyItems = [];
228
- this.subscriptions.forEach(i => i.unsubscribe());
229
- (_a = this.manager) === null || _a === void 0 ? void 0 : _a.destroy();
230
- }
231
- syncRootComponent(root, rootComponent) {
232
- let slots = root.get('slots');
233
- if (!slots) {
234
- slots = new yjs.Array();
235
- rootComponent.slots.toArray().forEach(i => {
236
- const sharedSlot = this.createSharedSlotBySlot(i);
237
- slots.push([sharedSlot]);
238
- });
239
- this.yDoc.transact(() => {
240
- root.set('state', rootComponent.state);
241
- root.set('slots', slots);
242
- });
243
- }
244
- else if (slots.length === 0) {
245
- rootComponent.updateState(() => {
246
- return root.get('state');
247
- });
248
- this.yDoc.transact(() => {
249
- rootComponent.slots.toArray().forEach(i => {
250
- const sharedSlot = this.createSharedSlotBySlot(i);
251
- slots.push([sharedSlot]);
252
- });
253
- });
254
- }
255
- else {
256
- rootComponent.updateState(() => {
257
- return root.get('state');
258
- });
259
- rootComponent.slots.clean();
260
- slots.forEach(sharedSlot => {
261
- const slot = this.createSlotBySharedSlot(sharedSlot);
262
- this.syncSlotContent(sharedSlot.get('content'), slot);
263
- this.syncSlotState(sharedSlot, slot);
264
- rootComponent.slots.insert(slot);
265
- });
266
- }
267
- this.syncComponentState(root, rootComponent);
268
- this.syncComponentSlots(slots, rootComponent);
269
- }
270
- getAbstractSelection(position) {
271
- const anchorPosition = yjs.createAbsolutePositionFromRelativePosition(position.anchor, this.yDoc);
272
- const focusPosition = yjs.createAbsolutePositionFromRelativePosition(position.focus, this.yDoc);
273
- if (anchorPosition && focusPosition) {
274
- const focusSlot = this.contentMap.get(focusPosition.type);
275
- const anchorSlot = this.contentMap.get(anchorPosition.type);
276
- if (focusSlot && anchorSlot) {
277
- return {
278
- anchorSlot,
279
- anchorOffset: anchorPosition.index,
280
- focusSlot,
281
- focusOffset: focusPosition.index
282
- };
283
- }
284
- }
285
- return null;
286
- }
287
- getRelativeCursorLocation() {
288
- const { anchorSlot, anchorOffset, focusSlot, focusOffset } = this.selection;
289
- if (anchorSlot) {
290
- const anchorYText = this.contentMap.get(anchorSlot);
291
- if (anchorYText) {
292
- const anchorPosition = yjs.createRelativePositionFromTypeIndex(anchorYText, anchorOffset);
293
- if (focusSlot) {
294
- const focusYText = this.contentMap.get(focusSlot);
295
- if (focusYText) {
296
- const focusPosition = yjs.createRelativePositionFromTypeIndex(focusYText, focusOffset);
297
- return {
298
- focus: focusPosition,
299
- anchor: anchorPosition
300
- };
301
- }
302
- }
303
- }
304
- }
305
- return null;
306
- }
307
- syncSlotContent(content, slot) {
308
- this.contentMap.set(slot, content);
309
- const syncRemote = (ev, tr) => {
310
- this.runRemoteUpdate(tr, () => {
311
- slot.retain(0);
312
- ev.keysChanged.forEach(key => {
313
- const change = ev.keys.get(key);
314
- if (!change) {
315
- return;
316
- }
317
- const updateType = change.action;
318
- if (updateType === 'update' || updateType === 'add') {
319
- const attribute = this.registry.getAttribute(key);
320
- if (attribute) {
321
- slot.setAttribute(attribute, content.getAttribute(key));
322
- }
323
- }
324
- else if (updateType === 'delete') {
325
- const attribute = this.registry.getAttribute(key);
326
- if (attribute) {
327
- slot.removeAttribute(attribute);
328
- }
329
- }
330
- });
331
- ev.delta.forEach(action => {
332
- if (Reflect.has(action, 'retain')) {
333
- if (action.attributes) {
334
- const formats = remoteFormatsToLocal(this.registry, action.attributes);
335
- if (formats.length) {
336
- slot.retain(action.retain, formats);
337
- }
338
- slot.retain(slot.index + action.retain);
339
- }
340
- else {
341
- slot.retain(action.retain);
342
- }
343
- }
344
- else if (action.insert) {
345
- const index = slot.index;
346
- let length = 1;
347
- if (typeof action.insert === 'string') {
348
- length = action.insert.length;
349
- slot.insert(action.insert, remoteFormatsToLocal(this.registry, action.attributes));
350
- }
351
- else {
352
- const sharedComponent = action.insert;
353
- const component = this.createComponentBySharedComponent(sharedComponent);
354
- this.syncComponentSlots(sharedComponent.get('slots'), component);
355
- this.syncComponentState(sharedComponent, component);
356
- slot.insert(component);
357
- }
358
- if (this.selection.isSelected) {
359
- if (slot === this.selection.anchorSlot && this.selection.anchorOffset > index) {
360
- this.selection.setAnchor(slot, this.selection.anchorOffset + length);
361
- }
362
- if (slot === this.selection.focusSlot && this.selection.focusOffset > index) {
363
- this.selection.setFocus(slot, this.selection.focusOffset + length);
364
- }
365
- }
366
- }
367
- else if (action.delete) {
368
- const index = slot.index;
369
- slot.retain(slot.index);
370
- slot.delete(action.delete);
371
- if (this.selection.isSelected) {
372
- if (slot === this.selection.anchorSlot && this.selection.anchorOffset >= index) {
373
- this.selection.setAnchor(slot, this.selection.startOffset - action.delete);
374
- }
375
- if (slot === this.selection.focusSlot && this.selection.focusOffset >= index) {
376
- this.selection.setFocus(slot, this.selection.focusOffset - action.delete);
377
- }
378
- }
379
- }
380
- });
381
- });
382
- };
383
- content.observe(syncRemote);
384
- const sub = slot.onContentChange.subscribe(actions => {
385
- this.runLocalUpdate(() => {
386
- var _a;
387
- let offset = 0;
388
- let length = 0;
389
- for (const action of actions) {
390
- if (action.type === 'retain') {
391
- const formats = action.formats;
392
- if (formats) {
393
- const keys = Object.keys(formats);
394
- let length = keys.length;
395
- keys.forEach(key => {
396
- const formatter = this.registry.getFormatter(key);
397
- if (!formatter) {
398
- length--;
399
- Reflect.deleteProperty(formats, key);
400
- }
401
- });
402
- if (length) {
403
- content.format(offset, action.offset, formats);
404
- }
405
- }
406
- else {
407
- offset = action.offset;
408
- }
409
- }
410
- else if (action.type === 'insert') {
411
- const delta = content.toDelta();
412
- const isEmpty = delta.length === 1 && delta[0].insert === core.Slot.emptyPlaceholder;
413
- if (typeof action.content === 'string') {
414
- length = action.content.length;
415
- content.insert(offset, action.content, action.formats || {});
416
- }
417
- else {
418
- length = 1;
419
- const sharedComponent = this.createSharedComponentByComponent(action.ref);
420
- content.insertEmbed(offset, sharedComponent, action.formats || {});
421
- }
422
- if (isEmpty && offset === 0) {
423
- content.delete(content.length - 1, 1);
424
- }
425
- offset += length;
426
- }
427
- else if (action.type === 'delete') {
428
- const delta = content.toDelta();
429
- if (content.length) {
430
- content.delete(offset, action.count);
431
- }
432
- if (content.length === 0) {
433
- content.insert(0, '\n', (_a = delta[0]) === null || _a === void 0 ? void 0 : _a.attributes);
434
- }
435
- }
436
- else if (action.type === 'attrSet') {
437
- content.setAttribute(action.name, action.value);
438
- }
439
- else if (action.type === 'attrRemove') {
440
- content.removeAttribute(action.name);
441
- }
442
- }
443
- });
444
- });
445
- sub.add(slot.onChildComponentRemove.subscribe(components => {
446
- components.forEach(c => {
447
- this.cleanSubscriptionsByComponent(c);
448
- });
449
- }));
450
- this.contentSyncCaches.set(slot, () => {
451
- content.unobserve(syncRemote);
452
- sub.unsubscribe();
453
- });
454
- }
455
- syncSlotState(remoteSlot, slot) {
456
- const syncRemote = (ev, tr) => {
457
- this.runRemoteUpdate(tr, () => {
458
- ev.keysChanged.forEach(key => {
459
- if (key === 'state') {
460
- const state = ev.target.get('state');
461
- slot.updateState(draft => {
462
- if (typeof draft === 'object' && draft !== null) {
463
- Object.assign(draft, state);
464
- }
465
- else {
466
- return state;
467
- }
468
- });
469
- }
470
- });
471
- });
472
- };
473
- remoteSlot.observe(syncRemote);
474
- const sub = slot.onStateChange.subscribe(change => {
475
- this.runLocalUpdate(() => {
476
- remoteSlot.set('state', change.newState);
477
- }, change.record);
478
- });
479
- this.slotStateSyncCaches.set(slot, () => {
480
- remoteSlot.unobserve(syncRemote);
481
- sub.unsubscribe();
482
- });
483
- }
484
- syncComponentSlots(remoteSlots, component) {
485
- const slots = component.slots;
486
- const syncRemote = (ev, tr) => {
487
- this.runRemoteUpdate(tr, () => {
488
- let index = 0;
489
- slots.retain(index);
490
- ev.delta.forEach(action => {
491
- if (Reflect.has(action, 'retain')) {
492
- index += action.retain;
493
- slots.retain(index);
494
- }
495
- else if (action.insert) {
496
- action.insert.forEach(item => {
497
- const slot = this.createSlotBySharedSlot(item);
498
- slots.insert(slot);
499
- this.syncSlotContent(item.get('content'), slot);
500
- this.syncSlotState(item, slot);
501
- index++;
502
- });
503
- }
504
- else if (action.delete) {
505
- slots.retain(index);
506
- slots.delete(action.delete);
507
- }
508
- });
509
- });
510
- };
511
- remoteSlots.observe(syncRemote);
512
- const sub = slots.onChange.subscribe(operations => {
513
- this.runLocalUpdate(() => {
514
- const applyActions = operations.apply;
515
- let index;
516
- applyActions.forEach(action => {
517
- if (action.type === 'retain') {
518
- index = action.offset;
519
- }
520
- else if (action.type === 'insertSlot') {
521
- const sharedSlot = this.createSharedSlotBySlot(action.ref);
522
- remoteSlots.insert(index, [sharedSlot]);
523
- index++;
524
- }
525
- else if (action.type === 'delete') {
526
- remoteSlots.delete(index, action.count);
527
- }
528
- });
529
- });
530
- });
531
- sub.add(slots.onChildSlotRemove.subscribe(slots => {
532
- slots.forEach(slot => {
533
- this.cleanSubscriptionsBySlot(slot);
534
- });
535
- }));
536
- this.slotsSyncCaches.set(component, () => {
537
- remoteSlots.unobserve(syncRemote);
538
- sub.unsubscribe();
539
- });
540
- }
541
- syncComponentState(remoteComponent, component) {
542
- const syncRemote = (ev, tr) => {
543
- this.runRemoteUpdate(tr, () => {
544
- ev.keysChanged.forEach(key => {
545
- if (key === 'state') {
546
- const state = ev.target.get('state');
547
- component.updateState(draft => {
548
- if (typeof draft === 'object' && draft !== null) {
549
- Object.assign(draft, state);
550
- }
551
- else {
552
- return state;
553
- }
554
- });
555
- }
556
- });
557
- });
558
- };
559
- remoteComponent.observe(syncRemote);
560
- const sub = component.onStateChange.subscribe(change => {
561
- this.runLocalUpdate(() => {
562
- remoteComponent.set('state', change.newState);
563
- }, change.record);
564
- });
565
- this.componentStateSyncCaches.set(component, () => {
566
- remoteComponent.unobserve(syncRemote);
567
- sub.unsubscribe();
568
- });
569
- }
570
- runLocalUpdate(fn, record = true) {
571
- if (this.updateFromRemote || this.controller.readonly) {
572
- return;
573
- }
574
- this.updateRemoteActions.push({
575
- record,
576
- action: fn
577
- });
578
- }
579
- runRemoteUpdate(tr, fn) {
580
- if (tr.origin === this.yDoc) {
581
- return;
582
- }
583
- this.updateFromRemote = true;
584
- if (tr.origin === this.manager) {
585
- this.scheduler.historyApplyTransact(fn);
586
- }
587
- else {
588
- this.scheduler.remoteUpdateTransact(fn);
589
- }
590
- this.updateFromRemote = false;
591
- }
592
- createSharedComponentByComponent(component) {
593
- const sharedComponent = new yjs.Map();
594
- sharedComponent.set('state', component.state);
595
- sharedComponent.set('name', component.name);
596
- const sharedSlots = new yjs.Array();
597
- sharedComponent.set('slots', sharedSlots);
598
- component.slots.toArray().forEach(slot => {
599
- const sharedSlot = this.createSharedSlotBySlot(slot);
600
- sharedSlots.push([sharedSlot]);
601
- });
602
- this.syncComponentSlots(sharedSlots, component);
603
- this.syncComponentState(sharedComponent, component);
604
- return sharedComponent;
605
- }
606
- createSharedSlotBySlot(slot) {
607
- const sharedSlot = new yjs.Map();
608
- sharedSlot.set('schema', slot.schema);
609
- sharedSlot.set('state', slot.state);
610
- const sharedContent = new yjs.Text();
611
- sharedSlot.set('content', sharedContent);
612
- let offset = 0;
613
- slot.toDelta().forEach(i => {
614
- let formats = {};
615
- if (i.formats) {
616
- i.formats.forEach(item => {
617
- formats[item[0].name] = item[1];
618
- });
619
- }
620
- else {
621
- formats = null;
622
- }
623
- if (typeof i.insert === 'string') {
624
- sharedContent.insert(offset, i.insert, formats);
625
- }
626
- else {
627
- const sharedComponent = this.createSharedComponentByComponent(i.insert);
628
- sharedContent.insertEmbed(offset, sharedComponent, formats);
629
- }
630
- offset += i.insert.length;
631
- });
632
- slot.getAttributes().forEach(item => {
633
- sharedContent.setAttribute(item[0].name, item[1]);
634
- });
635
- this.syncSlotContent(sharedContent, slot);
636
- this.syncSlotState(sharedSlot, slot);
637
- return sharedSlot;
638
- }
639
- createComponentBySharedComponent(yMap) {
640
- const sharedSlots = yMap.get('slots');
641
- const slots = [];
642
- sharedSlots.forEach(sharedSlot => {
643
- const slot = this.createSlotBySharedSlot(sharedSlot);
644
- slots.push(slot);
645
- });
646
- const name = yMap.get('name');
647
- const state = yMap.get('state');
648
- const instance = this.registry.createComponentByData(name, {
649
- state,
650
- slots
651
- });
652
- if (instance) {
653
- instance.slots.toArray().forEach((slot, index) => {
654
- let sharedSlot = sharedSlots.get(index);
655
- if (!sharedSlot) {
656
- sharedSlot = this.createSharedSlotBySlot(slot);
657
- sharedSlots.push([sharedSlot]);
658
- }
659
- this.syncSlotState(sharedSlot, slot);
660
- this.syncSlotContent(sharedSlot.get('content'), slot);
661
- });
662
- return instance;
663
- }
664
- throw collaborateErrorFn(`cannot find component factory \`${name}\`.`);
665
- }
666
- createSlotBySharedSlot(sharedSlot) {
667
- const content = sharedSlot.get('content');
668
- const delta = content.toDelta();
669
- const slot = this.registry.createSlot({
670
- schema: sharedSlot.get('schema'),
671
- state: sharedSlot.get('state'),
672
- attributes: {},
673
- formats: {},
674
- content: []
675
- });
676
- const attrs = content.getAttributes();
677
- Object.keys(attrs).forEach(key => {
678
- const attribute = this.registry.getAttribute(key);
679
- if (attribute) {
680
- slot.setAttribute(attribute, attrs[key]);
681
- }
682
- });
683
- for (const action of delta) {
684
- if (action.insert) {
685
- if (typeof action.insert === 'string') {
686
- const formats = remoteFormatsToLocal(this.registry, action.attributes);
687
- slot.insert(action.insert, formats);
688
- }
689
- else {
690
- const sharedComponent = action.insert;
691
- const component = this.createComponentBySharedComponent(sharedComponent);
692
- slot.insert(component, remoteFormatsToLocal(this.registry, action.attributes));
693
- this.syncComponentSlots(sharedComponent.get('slots'), component);
694
- this.syncComponentState(sharedComponent, component);
695
- }
696
- }
697
- else {
698
- throw collaborateErrorFn('unexpected delta action.');
699
- }
700
- }
701
- return slot;
702
- }
703
- cleanSubscriptionsBySlot(slot) {
704
- this.contentMap.delete(slot);
705
- [this.contentSyncCaches.get(slot), this.slotStateSyncCaches.get(slot)].forEach(fn => {
706
- if (fn) {
707
- fn();
708
- }
709
- });
710
- slot.sliceContent().forEach(i => {
711
- if (typeof i !== 'string') {
712
- this.cleanSubscriptionsByComponent(i);
713
- }
714
- });
715
- }
716
- cleanSubscriptionsByComponent(component) {
717
- [this.slotsSyncCaches.get(component), this.componentStateSyncCaches.get(component)].forEach(fn => {
718
- if (fn) {
719
- fn();
720
- }
721
- });
722
- component.slots.toArray().forEach(slot => {
723
- this.cleanSubscriptionsBySlot(slot);
724
- });
725
- }
726
- };
727
- exports.Collaborate = __decorate([
728
- di.Injectable(),
729
- __param(0, di.Inject(core.HISTORY_STACK_SIZE)),
730
- __metadata("design:paramtypes", [Number, core.RootComponentRef,
731
- core.Controller,
732
- core.Scheduler,
733
- core.Registry,
734
- core.Selection,
735
- core.Starter])
736
- ], exports.Collaborate);
737
- function remoteFormatsToLocal(registry, attrs) {
738
- const formats = [];
739
- if (attrs) {
740
- Object.keys(attrs).forEach(key => {
741
- const formatter = registry.getFormatter(key);
742
- if (formatter) {
743
- formats.push([formatter, attrs[key]]);
744
- }
745
- });
746
- }
747
- return formats;
38
+ const collaborateErrorFn = core.makeError('Collaborate');
39
+ class ContentMap {
40
+ constructor() {
41
+ this.slotAndYTextMap = new WeakMap();
42
+ this.yTextAndSLotMap = new WeakMap();
43
+ }
44
+ set(key, value) {
45
+ if (key instanceof core.Slot) {
46
+ this.slotAndYTextMap.set(key, value);
47
+ this.yTextAndSLotMap.set(value, key);
48
+ }
49
+ else {
50
+ this.slotAndYTextMap.set(value, key);
51
+ this.yTextAndSLotMap.set(key, value);
52
+ }
53
+ }
54
+ get(key) {
55
+ if (key instanceof core.Slot) {
56
+ return this.slotAndYTextMap.get(key) || null;
57
+ }
58
+ return this.yTextAndSLotMap.get(key) || null;
59
+ }
60
+ delete(key) {
61
+ if (key instanceof core.Slot) {
62
+ const v = this.slotAndYTextMap.get(key);
63
+ this.slotAndYTextMap.delete(key);
64
+ if (v) {
65
+ this.yTextAndSLotMap.delete(v);
66
+ }
67
+ }
68
+ else {
69
+ const v = this.yTextAndSLotMap.get(key);
70
+ this.yTextAndSLotMap.delete(key);
71
+ if (v) {
72
+ this.slotAndYTextMap.delete(v);
73
+ }
74
+ }
75
+ }
76
+ }
77
+ exports.Collaborate = class Collaborate {
78
+ constructor(stackSize, rootComponentRef, controller, scheduler, registry, selection, starter) {
79
+ this.stackSize = stackSize;
80
+ this.rootComponentRef = rootComponentRef;
81
+ this.controller = controller;
82
+ this.scheduler = scheduler;
83
+ this.registry = registry;
84
+ this.selection = selection;
85
+ this.starter = starter;
86
+ this.yDoc = new yjs.Doc();
87
+ this.backEvent = new stream.Subject();
88
+ this.forwardEvent = new stream.Subject();
89
+ this.changeEvent = new stream.Subject();
90
+ this.pushEvent = new stream.Subject();
91
+ this.manager = null;
92
+ this.subscriptions = [];
93
+ this.updateFromRemote = false;
94
+ this.contentSyncCaches = new WeakMap();
95
+ this.slotStateSyncCaches = new WeakMap();
96
+ this.slotsSyncCaches = new WeakMap();
97
+ this.componentStateSyncCaches = new WeakMap();
98
+ this.localChangesAppliedEvent = new stream.Subject();
99
+ this.selectionChangeEvent = new stream.Subject();
100
+ this.contentMap = new ContentMap();
101
+ this.updateRemoteActions = [];
102
+ this.noRecord = {};
103
+ this.historyItems = [];
104
+ this.index = 0;
105
+ this.onBack = this.backEvent.asObservable();
106
+ this.onForward = this.forwardEvent.asObservable();
107
+ this.onChange = this.changeEvent.asObservable();
108
+ this.onPush = this.pushEvent.asObservable();
109
+ this.onLocalChangesApplied = this.localChangesAppliedEvent.asObservable();
110
+ }
111
+ get canBack() {
112
+ var _a;
113
+ return ((_a = this.manager) === null || _a === void 0 ? void 0 : _a.canUndo()) || false;
114
+ }
115
+ get canForward() {
116
+ var _a;
117
+ return ((_a = this.manager) === null || _a === void 0 ? void 0 : _a.canRedo()) || false;
118
+ }
119
+ listen() {
120
+ const root = this.yDoc.getMap('RootComponent');
121
+ const rootComponent = this.rootComponentRef.component;
122
+ const manager = new yjs.UndoManager(root, {
123
+ trackedOrigins: new Set([this.yDoc])
124
+ });
125
+ this.manager = manager;
126
+ manager.on('stack-item-added', event => {
127
+ if (event.type === 'undo') {
128
+ if (event.origin === manager) {
129
+ this.index++;
130
+ }
131
+ else {
132
+ this.historyItems.length = this.index;
133
+ this.historyItems.push(this.getRelativeCursorLocation());
134
+ this.index++;
135
+ }
136
+ }
137
+ else {
138
+ this.index--;
139
+ }
140
+ if (manager.undoStack.length > this.stackSize) {
141
+ this.historyItems.shift();
142
+ manager.undoStack.shift();
143
+ }
144
+ if (event.origin === this.yDoc) {
145
+ this.pushEvent.next();
146
+ }
147
+ this.changeEvent.next();
148
+ });
149
+ manager.on('stack-item-popped', () => {
150
+ const position = this.historyItems[this.index - 1];
151
+ if (position) {
152
+ const selection = this.getAbstractSelection(position);
153
+ if (selection) {
154
+ this.selection.setBaseAndExtent(selection.anchorSlot, selection.anchorOffset, selection.focusSlot, selection.focusOffset);
155
+ return;
156
+ }
157
+ }
158
+ this.selection.unSelect();
159
+ });
160
+ this.subscriptions.push(this.selection.onChange.subscribe(() => {
161
+ const paths = this.selection.getPaths();
162
+ this.selectionChangeEvent.next(paths);
163
+ }), this.scheduler.onDocChanged.pipe(stream.map(item => {
164
+ return item.filter(i => {
165
+ return i.from !== core.ChangeOrigin.Remote;
166
+ });
167
+ }), stream.filter(item => {
168
+ return item.length;
169
+ })).subscribe(() => {
170
+ const updates = [];
171
+ let update = null;
172
+ for (const item of this.updateRemoteActions) {
173
+ if (!update) {
174
+ update = {
175
+ record: item.record,
176
+ actions: []
177
+ };
178
+ updates.push(update);
179
+ }
180
+ if (update.record === item.record) {
181
+ update.actions.push(item.action);
182
+ }
183
+ else {
184
+ update = {
185
+ record: item.record,
186
+ actions: [item.action]
187
+ };
188
+ updates.push(update);
189
+ }
190
+ }
191
+ this.updateRemoteActions = [];
192
+ for (const item of updates) {
193
+ this.yDoc.transact(() => {
194
+ item.actions.forEach(fn => {
195
+ fn();
196
+ });
197
+ }, item.record ? this.yDoc : this.noRecord);
198
+ }
199
+ this.localChangesAppliedEvent.next();
200
+ }));
201
+ this.syncRootComponent(root, rootComponent);
202
+ }
203
+ back() {
204
+ var _a;
205
+ if (this.canBack) {
206
+ (_a = this.manager) === null || _a === void 0 ? void 0 : _a.undo();
207
+ this.backEvent.next();
208
+ }
209
+ }
210
+ forward() {
211
+ var _a;
212
+ if (this.canForward) {
213
+ (_a = this.manager) === null || _a === void 0 ? void 0 : _a.redo();
214
+ this.forwardEvent.next();
215
+ }
216
+ }
217
+ clear() {
218
+ var _a;
219
+ this.index = 0;
220
+ this.historyItems = [];
221
+ (_a = this.manager) === null || _a === void 0 ? void 0 : _a.clear();
222
+ this.changeEvent.next();
223
+ }
224
+ destroy() {
225
+ var _a;
226
+ this.index = 0;
227
+ this.historyItems = [];
228
+ this.subscriptions.forEach(i => i.unsubscribe());
229
+ (_a = this.manager) === null || _a === void 0 ? void 0 : _a.destroy();
230
+ }
231
+ syncRootComponent(root, rootComponent) {
232
+ let slots = root.get('slots');
233
+ if (!slots) {
234
+ slots = new yjs.Array();
235
+ rootComponent.slots.toArray().forEach(i => {
236
+ const sharedSlot = this.createSharedSlotBySlot(i);
237
+ slots.push([sharedSlot]);
238
+ });
239
+ this.yDoc.transact(() => {
240
+ root.set('state', rootComponent.state);
241
+ root.set('slots', slots);
242
+ });
243
+ }
244
+ else if (slots.length === 0) {
245
+ rootComponent.updateState(() => {
246
+ return root.get('state');
247
+ });
248
+ this.yDoc.transact(() => {
249
+ rootComponent.slots.toArray().forEach(i => {
250
+ const sharedSlot = this.createSharedSlotBySlot(i);
251
+ slots.push([sharedSlot]);
252
+ });
253
+ });
254
+ }
255
+ else {
256
+ rootComponent.updateState(() => {
257
+ return root.get('state');
258
+ });
259
+ rootComponent.slots.clean();
260
+ slots.forEach(sharedSlot => {
261
+ const slot = this.createSlotBySharedSlot(sharedSlot);
262
+ this.syncSlotContent(sharedSlot.get('content'), slot);
263
+ this.syncSlotState(sharedSlot, slot);
264
+ rootComponent.slots.insert(slot);
265
+ });
266
+ }
267
+ this.syncComponentState(root, rootComponent);
268
+ this.syncComponentSlots(slots, rootComponent);
269
+ }
270
+ getAbstractSelection(position) {
271
+ const anchorPosition = yjs.createAbsolutePositionFromRelativePosition(position.anchor, this.yDoc);
272
+ const focusPosition = yjs.createAbsolutePositionFromRelativePosition(position.focus, this.yDoc);
273
+ if (anchorPosition && focusPosition) {
274
+ const focusSlot = this.contentMap.get(focusPosition.type);
275
+ const anchorSlot = this.contentMap.get(anchorPosition.type);
276
+ if (focusSlot && anchorSlot) {
277
+ return {
278
+ anchorSlot,
279
+ anchorOffset: anchorPosition.index,
280
+ focusSlot,
281
+ focusOffset: focusPosition.index
282
+ };
283
+ }
284
+ }
285
+ return null;
286
+ }
287
+ getRelativeCursorLocation() {
288
+ const { anchorSlot, anchorOffset, focusSlot, focusOffset } = this.selection;
289
+ if (anchorSlot) {
290
+ const anchorYText = this.contentMap.get(anchorSlot);
291
+ if (anchorYText) {
292
+ const anchorPosition = yjs.createRelativePositionFromTypeIndex(anchorYText, anchorOffset);
293
+ if (focusSlot) {
294
+ const focusYText = this.contentMap.get(focusSlot);
295
+ if (focusYText) {
296
+ const focusPosition = yjs.createRelativePositionFromTypeIndex(focusYText, focusOffset);
297
+ return {
298
+ focus: focusPosition,
299
+ anchor: anchorPosition
300
+ };
301
+ }
302
+ }
303
+ }
304
+ }
305
+ return null;
306
+ }
307
+ syncSlotContent(content, slot) {
308
+ this.contentMap.set(slot, content);
309
+ const syncRemote = (ev, tr) => {
310
+ this.runRemoteUpdate(tr, () => {
311
+ slot.retain(0);
312
+ ev.keysChanged.forEach(key => {
313
+ const change = ev.keys.get(key);
314
+ if (!change) {
315
+ return;
316
+ }
317
+ const updateType = change.action;
318
+ if (updateType === 'update' || updateType === 'add') {
319
+ const attribute = this.registry.getAttribute(key);
320
+ if (attribute) {
321
+ slot.setAttribute(attribute, content.getAttribute(key));
322
+ }
323
+ }
324
+ else if (updateType === 'delete') {
325
+ const attribute = this.registry.getAttribute(key);
326
+ if (attribute) {
327
+ slot.removeAttribute(attribute);
328
+ }
329
+ }
330
+ });
331
+ ev.delta.forEach(action => {
332
+ if (Reflect.has(action, 'retain')) {
333
+ if (action.attributes) {
334
+ const formats = remoteFormatsToLocal(this.registry, action.attributes);
335
+ if (formats.length) {
336
+ slot.retain(action.retain, formats);
337
+ }
338
+ slot.retain(slot.index + action.retain);
339
+ }
340
+ else {
341
+ slot.retain(action.retain);
342
+ }
343
+ }
344
+ else if (action.insert) {
345
+ const index = slot.index;
346
+ let length = 1;
347
+ if (typeof action.insert === 'string') {
348
+ length = action.insert.length;
349
+ slot.insert(action.insert, remoteFormatsToLocal(this.registry, action.attributes));
350
+ }
351
+ else {
352
+ const sharedComponent = action.insert;
353
+ const component = this.createComponentBySharedComponent(sharedComponent);
354
+ this.syncComponentSlots(sharedComponent.get('slots'), component);
355
+ this.syncComponentState(sharedComponent, component);
356
+ slot.insert(component);
357
+ }
358
+ if (this.selection.isSelected) {
359
+ if (slot === this.selection.anchorSlot && this.selection.anchorOffset > index) {
360
+ this.selection.setAnchor(slot, this.selection.anchorOffset + length);
361
+ }
362
+ if (slot === this.selection.focusSlot && this.selection.focusOffset > index) {
363
+ this.selection.setFocus(slot, this.selection.focusOffset + length);
364
+ }
365
+ }
366
+ }
367
+ else if (action.delete) {
368
+ const index = slot.index;
369
+ slot.retain(slot.index);
370
+ slot.delete(action.delete);
371
+ if (this.selection.isSelected) {
372
+ if (slot === this.selection.anchorSlot && this.selection.anchorOffset >= index) {
373
+ this.selection.setAnchor(slot, this.selection.startOffset - action.delete);
374
+ }
375
+ if (slot === this.selection.focusSlot && this.selection.focusOffset >= index) {
376
+ this.selection.setFocus(slot, this.selection.focusOffset - action.delete);
377
+ }
378
+ }
379
+ }
380
+ });
381
+ });
382
+ };
383
+ content.observe(syncRemote);
384
+ const sub = slot.onContentChange.subscribe(actions => {
385
+ this.runLocalUpdate(() => {
386
+ var _a;
387
+ let offset = 0;
388
+ let length = 0;
389
+ for (const action of actions) {
390
+ if (action.type === 'retain') {
391
+ const formats = action.formats;
392
+ if (formats) {
393
+ const keys = Object.keys(formats);
394
+ let length = keys.length;
395
+ keys.forEach(key => {
396
+ const formatter = this.registry.getFormatter(key);
397
+ if (!formatter) {
398
+ length--;
399
+ Reflect.deleteProperty(formats, key);
400
+ }
401
+ });
402
+ if (length) {
403
+ content.format(offset, action.offset, formats);
404
+ }
405
+ }
406
+ else {
407
+ offset = action.offset;
408
+ }
409
+ }
410
+ else if (action.type === 'insert') {
411
+ const delta = content.toDelta();
412
+ const isEmpty = delta.length === 1 && delta[0].insert === core.Slot.emptyPlaceholder;
413
+ if (typeof action.content === 'string') {
414
+ length = action.content.length;
415
+ content.insert(offset, action.content, action.formats || {});
416
+ }
417
+ else {
418
+ length = 1;
419
+ const sharedComponent = this.createSharedComponentByComponent(action.ref);
420
+ content.insertEmbed(offset, sharedComponent, action.formats || {});
421
+ }
422
+ if (isEmpty && offset === 0) {
423
+ content.delete(content.length - 1, 1);
424
+ }
425
+ offset += length;
426
+ }
427
+ else if (action.type === 'delete') {
428
+ const delta = content.toDelta();
429
+ if (content.length) {
430
+ content.delete(offset, action.count);
431
+ }
432
+ if (content.length === 0) {
433
+ content.insert(0, '\n', (_a = delta[0]) === null || _a === void 0 ? void 0 : _a.attributes);
434
+ }
435
+ }
436
+ else if (action.type === 'attrSet') {
437
+ content.setAttribute(action.name, action.value);
438
+ }
439
+ else if (action.type === 'attrRemove') {
440
+ content.removeAttribute(action.name);
441
+ }
442
+ }
443
+ });
444
+ });
445
+ sub.add(slot.onChildComponentRemove.subscribe(components => {
446
+ components.forEach(c => {
447
+ this.cleanSubscriptionsByComponent(c);
448
+ });
449
+ }));
450
+ this.contentSyncCaches.set(slot, () => {
451
+ content.unobserve(syncRemote);
452
+ sub.unsubscribe();
453
+ });
454
+ }
455
+ syncSlotState(remoteSlot, slot) {
456
+ const syncRemote = (ev, tr) => {
457
+ this.runRemoteUpdate(tr, () => {
458
+ ev.keysChanged.forEach(key => {
459
+ if (key === 'state') {
460
+ const state = ev.target.get('state');
461
+ slot.updateState(draft => {
462
+ if (typeof draft === 'object' && draft !== null) {
463
+ Object.assign(draft, state);
464
+ }
465
+ else {
466
+ return state;
467
+ }
468
+ });
469
+ }
470
+ });
471
+ });
472
+ };
473
+ remoteSlot.observe(syncRemote);
474
+ const sub = slot.onStateChange.subscribe(change => {
475
+ this.runLocalUpdate(() => {
476
+ remoteSlot.set('state', change.newState);
477
+ }, change.record);
478
+ });
479
+ this.slotStateSyncCaches.set(slot, () => {
480
+ remoteSlot.unobserve(syncRemote);
481
+ sub.unsubscribe();
482
+ });
483
+ }
484
+ syncComponentSlots(remoteSlots, component) {
485
+ const slots = component.slots;
486
+ const syncRemote = (ev, tr) => {
487
+ this.runRemoteUpdate(tr, () => {
488
+ let index = 0;
489
+ slots.retain(index);
490
+ ev.delta.forEach(action => {
491
+ if (Reflect.has(action, 'retain')) {
492
+ index += action.retain;
493
+ slots.retain(index);
494
+ }
495
+ else if (action.insert) {
496
+ action.insert.forEach(item => {
497
+ const slot = this.createSlotBySharedSlot(item);
498
+ slots.insert(slot);
499
+ this.syncSlotContent(item.get('content'), slot);
500
+ this.syncSlotState(item, slot);
501
+ index++;
502
+ });
503
+ }
504
+ else if (action.delete) {
505
+ slots.retain(index);
506
+ slots.delete(action.delete);
507
+ }
508
+ });
509
+ });
510
+ };
511
+ remoteSlots.observe(syncRemote);
512
+ const sub = slots.onChange.subscribe(operations => {
513
+ this.runLocalUpdate(() => {
514
+ const applyActions = operations.apply;
515
+ let index;
516
+ applyActions.forEach(action => {
517
+ if (action.type === 'retain') {
518
+ index = action.offset;
519
+ }
520
+ else if (action.type === 'insertSlot') {
521
+ const sharedSlot = this.createSharedSlotBySlot(action.ref);
522
+ remoteSlots.insert(index, [sharedSlot]);
523
+ index++;
524
+ }
525
+ else if (action.type === 'delete') {
526
+ remoteSlots.delete(index, action.count);
527
+ }
528
+ });
529
+ });
530
+ });
531
+ sub.add(slots.onChildSlotRemove.subscribe(slots => {
532
+ slots.forEach(slot => {
533
+ this.cleanSubscriptionsBySlot(slot);
534
+ });
535
+ }));
536
+ this.slotsSyncCaches.set(component, () => {
537
+ remoteSlots.unobserve(syncRemote);
538
+ sub.unsubscribe();
539
+ });
540
+ }
541
+ syncComponentState(remoteComponent, component) {
542
+ const syncRemote = (ev, tr) => {
543
+ this.runRemoteUpdate(tr, () => {
544
+ ev.keysChanged.forEach(key => {
545
+ if (key === 'state') {
546
+ const state = ev.target.get('state');
547
+ component.updateState(draft => {
548
+ if (typeof draft === 'object' && draft !== null) {
549
+ Object.assign(draft, state);
550
+ }
551
+ else {
552
+ return state;
553
+ }
554
+ });
555
+ }
556
+ });
557
+ });
558
+ };
559
+ remoteComponent.observe(syncRemote);
560
+ const sub = component.onStateChange.subscribe(change => {
561
+ this.runLocalUpdate(() => {
562
+ remoteComponent.set('state', change.newState);
563
+ }, change.record);
564
+ });
565
+ this.componentStateSyncCaches.set(component, () => {
566
+ remoteComponent.unobserve(syncRemote);
567
+ sub.unsubscribe();
568
+ });
569
+ }
570
+ runLocalUpdate(fn, record = true) {
571
+ if (this.updateFromRemote || this.controller.readonly) {
572
+ return;
573
+ }
574
+ this.updateRemoteActions.push({
575
+ record,
576
+ action: fn
577
+ });
578
+ }
579
+ runRemoteUpdate(tr, fn) {
580
+ if (tr.origin === this.yDoc) {
581
+ return;
582
+ }
583
+ this.updateFromRemote = true;
584
+ if (tr.origin === this.manager) {
585
+ this.scheduler.historyApplyTransact(fn);
586
+ }
587
+ else {
588
+ this.scheduler.remoteUpdateTransact(fn);
589
+ }
590
+ this.updateFromRemote = false;
591
+ }
592
+ createSharedComponentByComponent(component) {
593
+ const sharedComponent = new yjs.Map();
594
+ sharedComponent.set('state', component.state);
595
+ sharedComponent.set('name', component.name);
596
+ const sharedSlots = new yjs.Array();
597
+ sharedComponent.set('slots', sharedSlots);
598
+ component.slots.toArray().forEach(slot => {
599
+ const sharedSlot = this.createSharedSlotBySlot(slot);
600
+ sharedSlots.push([sharedSlot]);
601
+ });
602
+ this.syncComponentSlots(sharedSlots, component);
603
+ this.syncComponentState(sharedComponent, component);
604
+ return sharedComponent;
605
+ }
606
+ createSharedSlotBySlot(slot) {
607
+ const sharedSlot = new yjs.Map();
608
+ sharedSlot.set('schema', slot.schema);
609
+ sharedSlot.set('state', slot.state);
610
+ const sharedContent = new yjs.Text();
611
+ sharedSlot.set('content', sharedContent);
612
+ let offset = 0;
613
+ slot.toDelta().forEach(i => {
614
+ let formats = {};
615
+ if (i.formats) {
616
+ i.formats.forEach(item => {
617
+ formats[item[0].name] = item[1];
618
+ });
619
+ }
620
+ else {
621
+ formats = null;
622
+ }
623
+ if (typeof i.insert === 'string') {
624
+ sharedContent.insert(offset, i.insert, formats);
625
+ }
626
+ else {
627
+ const sharedComponent = this.createSharedComponentByComponent(i.insert);
628
+ sharedContent.insertEmbed(offset, sharedComponent, formats);
629
+ }
630
+ offset += i.insert.length;
631
+ });
632
+ slot.getAttributes().forEach(item => {
633
+ sharedContent.setAttribute(item[0].name, item[1]);
634
+ });
635
+ this.syncSlotContent(sharedContent, slot);
636
+ this.syncSlotState(sharedSlot, slot);
637
+ return sharedSlot;
638
+ }
639
+ createComponentBySharedComponent(yMap) {
640
+ const sharedSlots = yMap.get('slots');
641
+ const slots = [];
642
+ sharedSlots.forEach(sharedSlot => {
643
+ const slot = this.createSlotBySharedSlot(sharedSlot);
644
+ slots.push(slot);
645
+ });
646
+ const name = yMap.get('name');
647
+ const state = yMap.get('state');
648
+ const instance = this.registry.createComponentByData(name, {
649
+ state,
650
+ slots
651
+ });
652
+ if (instance) {
653
+ instance.slots.toArray().forEach((slot, index) => {
654
+ let sharedSlot = sharedSlots.get(index);
655
+ if (!sharedSlot) {
656
+ sharedSlot = this.createSharedSlotBySlot(slot);
657
+ sharedSlots.push([sharedSlot]);
658
+ }
659
+ this.syncSlotState(sharedSlot, slot);
660
+ this.syncSlotContent(sharedSlot.get('content'), slot);
661
+ });
662
+ return instance;
663
+ }
664
+ throw collaborateErrorFn(`cannot find component factory \`${name}\`.`);
665
+ }
666
+ createSlotBySharedSlot(sharedSlot) {
667
+ const content = sharedSlot.get('content');
668
+ const delta = content.toDelta();
669
+ const slot = this.registry.createSlot({
670
+ schema: sharedSlot.get('schema'),
671
+ state: sharedSlot.get('state'),
672
+ attributes: {},
673
+ formats: {},
674
+ content: []
675
+ });
676
+ const attrs = content.getAttributes();
677
+ Object.keys(attrs).forEach(key => {
678
+ const attribute = this.registry.getAttribute(key);
679
+ if (attribute) {
680
+ slot.setAttribute(attribute, attrs[key]);
681
+ }
682
+ });
683
+ for (const action of delta) {
684
+ if (action.insert) {
685
+ if (typeof action.insert === 'string') {
686
+ const formats = remoteFormatsToLocal(this.registry, action.attributes);
687
+ slot.insert(action.insert, formats);
688
+ }
689
+ else {
690
+ const sharedComponent = action.insert;
691
+ const component = this.createComponentBySharedComponent(sharedComponent);
692
+ slot.insert(component, remoteFormatsToLocal(this.registry, action.attributes));
693
+ this.syncComponentSlots(sharedComponent.get('slots'), component);
694
+ this.syncComponentState(sharedComponent, component);
695
+ }
696
+ }
697
+ else {
698
+ throw collaborateErrorFn('unexpected delta action.');
699
+ }
700
+ }
701
+ return slot;
702
+ }
703
+ cleanSubscriptionsBySlot(slot) {
704
+ this.contentMap.delete(slot);
705
+ [this.contentSyncCaches.get(slot), this.slotStateSyncCaches.get(slot)].forEach(fn => {
706
+ if (fn) {
707
+ fn();
708
+ }
709
+ });
710
+ slot.sliceContent().forEach(i => {
711
+ if (typeof i !== 'string') {
712
+ this.cleanSubscriptionsByComponent(i);
713
+ }
714
+ });
715
+ }
716
+ cleanSubscriptionsByComponent(component) {
717
+ [this.slotsSyncCaches.get(component), this.componentStateSyncCaches.get(component)].forEach(fn => {
718
+ if (fn) {
719
+ fn();
720
+ }
721
+ });
722
+ component.slots.toArray().forEach(slot => {
723
+ this.cleanSubscriptionsBySlot(slot);
724
+ });
725
+ }
726
+ };
727
+ exports.Collaborate = __decorate([
728
+ di.Injectable(),
729
+ __param(0, di.Inject(core.HISTORY_STACK_SIZE)),
730
+ __metadata("design:paramtypes", [Number, core.RootComponentRef,
731
+ core.Controller,
732
+ core.Scheduler,
733
+ core.Registry,
734
+ core.Selection,
735
+ core.Starter])
736
+ ], exports.Collaborate);
737
+ function remoteFormatsToLocal(registry, attrs) {
738
+ const formats = [];
739
+ if (attrs) {
740
+ Object.keys(attrs).forEach(key => {
741
+ const formatter = registry.getFormatter(key);
742
+ if (formatter) {
743
+ formats.push([formatter, attrs[key]]);
744
+ }
745
+ });
746
+ }
747
+ return formats;
748
748
  }
749
749
 
750
- const collaborateModule = {
751
- providers: [
752
- exports.Collaborate,
753
- {
754
- provide: core.History,
755
- useExisting: exports.Collaborate
756
- }
757
- ]
750
+ const collaborateModule = {
751
+ providers: [
752
+ exports.Collaborate,
753
+ {
754
+ provide: core.History,
755
+ useExisting: exports.Collaborate
756
+ }
757
+ ]
758
758
  };
759
759
 
760
760
  exports.collaborateModule = collaborateModule;