@textbus/collaborate 3.1.7 → 3.1.14

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