@textbus/collaborate 3.0.0-alpha.22 → 3.0.0-alpha.23

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