@textbus/collaborate 3.0.0-alpha.3 → 3.0.0-alpha.30

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,730 @@
1
+ import { makeError, HISTORY_STACK_SIZE, ChangeOrigin, Slot, RootComponentRef, Controller, Scheduler, Factory, Selection, Starter, History } from '@textbus/core';
2
+ import { Injectable, Inject } from '@tanbo/di';
3
+ import { Subject, map, filter } from '@tanbo/stream';
4
+ import { Doc, UndoManager, Array, createAbsolutePositionFromRelativePosition, createRelativePositionFromTypeIndex, Map, Text } from 'yjs';
5
+
6
+ /******************************************************************************
7
+ Copyright (c) Microsoft Corporation.
8
+
9
+ Permission to use, copy, modify, and/or distribute this software for any
10
+ purpose with or without fee is hereby granted.
11
+
12
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
13
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
14
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
15
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
16
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
17
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
18
+ PERFORMANCE OF THIS SOFTWARE.
19
+ ***************************************************************************** */
20
+
21
+ function __decorate(decorators, target, key, desc) {
22
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
23
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
24
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
25
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
26
+ }
27
+
28
+ function __param(paramIndex, decorator) {
29
+ return function (target, key) { decorator(target, key, paramIndex); }
30
+ }
31
+
32
+ function __metadata(metadataKey, metadataValue) {
33
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue);
34
+ }
35
+
36
+ const collaborateErrorFn = makeError('Collaborate');
37
+ class ContentMap {
38
+ constructor() {
39
+ this.slotAndYTextMap = new WeakMap();
40
+ this.yTextAndSLotMap = new WeakMap();
41
+ }
42
+ set(key, value) {
43
+ if (key instanceof Slot) {
44
+ this.slotAndYTextMap.set(key, value);
45
+ this.yTextAndSLotMap.set(value, key);
46
+ }
47
+ else {
48
+ this.slotAndYTextMap.set(value, key);
49
+ this.yTextAndSLotMap.set(key, value);
50
+ }
51
+ }
52
+ get(key) {
53
+ if (key instanceof Slot) {
54
+ return this.slotAndYTextMap.get(key) || null;
55
+ }
56
+ return this.yTextAndSLotMap.get(key) || null;
57
+ }
58
+ delete(key) {
59
+ if (key instanceof Slot) {
60
+ const v = this.slotAndYTextMap.get(key);
61
+ this.slotAndYTextMap.delete(key);
62
+ if (v) {
63
+ this.yTextAndSLotMap.delete(v);
64
+ }
65
+ }
66
+ else {
67
+ const v = this.yTextAndSLotMap.get(key);
68
+ this.yTextAndSLotMap.delete(key);
69
+ if (v) {
70
+ this.slotAndYTextMap.delete(v);
71
+ }
72
+ }
73
+ }
74
+ }
75
+ let Collaborate = class Collaborate {
76
+ constructor(stackSize, rootComponentRef, controller, scheduler, factory, selection, starter) {
77
+ this.stackSize = stackSize;
78
+ this.rootComponentRef = rootComponentRef;
79
+ this.controller = controller;
80
+ this.scheduler = scheduler;
81
+ this.factory = factory;
82
+ this.selection = selection;
83
+ this.starter = starter;
84
+ this.yDoc = new Doc();
85
+ this.backEvent = new Subject();
86
+ this.forwardEvent = new Subject();
87
+ this.changeEvent = new Subject();
88
+ this.pushEvent = new Subject();
89
+ this.manager = null;
90
+ this.subscriptions = [];
91
+ this.updateFromRemote = false;
92
+ this.contentSyncCaches = new WeakMap();
93
+ this.slotStateSyncCaches = new WeakMap();
94
+ this.slotsSyncCaches = new WeakMap();
95
+ this.componentStateSyncCaches = new WeakMap();
96
+ this.localChangesAppliedEvent = new Subject();
97
+ this.selectionChangeEvent = new Subject();
98
+ this.contentMap = new ContentMap();
99
+ this.updateRemoteActions = [];
100
+ this.noRecord = {};
101
+ this.onBack = this.backEvent.asObservable();
102
+ this.onForward = this.forwardEvent.asObservable();
103
+ this.onChange = this.changeEvent.asObservable();
104
+ this.onPush = this.pushEvent.asObservable();
105
+ this.onLocalChangesApplied = this.localChangesAppliedEvent.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 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(map(item => {
142
+ return item.filter(i => {
143
+ return i.from !== ChangeOrigin.Remote;
144
+ });
145
+ }), 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
+ this.localChangesAppliedEvent.next();
178
+ }));
179
+ this.syncRootComponent(root, rootComponent);
180
+ }
181
+ back() {
182
+ var _a;
183
+ if (this.canBack) {
184
+ (_a = this.manager) === null || _a === void 0 ? void 0 : _a.undo();
185
+ this.backEvent.next();
186
+ }
187
+ }
188
+ forward() {
189
+ var _a;
190
+ if (this.canForward) {
191
+ (_a = this.manager) === null || _a === void 0 ? void 0 : _a.redo();
192
+ this.forwardEvent.next();
193
+ }
194
+ }
195
+ clear() {
196
+ var _a;
197
+ (_a = this.manager) === null || _a === void 0 ? void 0 : _a.clear();
198
+ this.changeEvent.next();
199
+ }
200
+ destroy() {
201
+ var _a;
202
+ this.subscriptions.forEach(i => i.unsubscribe());
203
+ (_a = this.manager) === null || _a === void 0 ? void 0 : _a.destroy();
204
+ }
205
+ syncRootComponent(root, rootComponent) {
206
+ let slots = root.get('slots');
207
+ if (!slots) {
208
+ slots = new Array();
209
+ rootComponent.slots.toArray().forEach(i => {
210
+ const sharedSlot = this.createSharedSlotBySlot(i);
211
+ slots.push([sharedSlot]);
212
+ });
213
+ this.yDoc.transact(() => {
214
+ root.set('state', rootComponent.state);
215
+ root.set('slots', slots);
216
+ });
217
+ }
218
+ else if (slots.length === 0) {
219
+ rootComponent.updateState(() => {
220
+ return root.get('state');
221
+ });
222
+ this.yDoc.transact(() => {
223
+ rootComponent.slots.toArray().forEach(i => {
224
+ const sharedSlot = this.createSharedSlotBySlot(i);
225
+ slots.push([sharedSlot]);
226
+ });
227
+ });
228
+ }
229
+ else {
230
+ rootComponent.updateState(() => {
231
+ return root.get('state');
232
+ });
233
+ rootComponent.slots.clean();
234
+ slots.forEach(sharedSlot => {
235
+ const slot = this.createSlotBySharedSlot(sharedSlot);
236
+ this.syncSlotContent(sharedSlot.get('content'), slot);
237
+ this.syncSlotState(sharedSlot, slot);
238
+ rootComponent.slots.insert(slot);
239
+ });
240
+ }
241
+ this.syncComponentState(root, rootComponent);
242
+ this.syncComponentSlots(slots, rootComponent);
243
+ }
244
+ restoreCursorLocation(position) {
245
+ const anchorPosition = createAbsolutePositionFromRelativePosition(position.anchor, this.yDoc);
246
+ const focusPosition = createAbsolutePositionFromRelativePosition(position.focus, this.yDoc);
247
+ if (anchorPosition && focusPosition) {
248
+ const focusSlot = this.contentMap.get(focusPosition.type);
249
+ const anchorSlot = this.contentMap.get(anchorPosition.type);
250
+ if (focusSlot && anchorSlot) {
251
+ this.selection.setBaseAndExtent(anchorSlot, anchorPosition.index, focusSlot, focusPosition.index);
252
+ return;
253
+ }
254
+ }
255
+ this.selection.unSelect();
256
+ }
257
+ getRelativeCursorLocation() {
258
+ const { anchorSlot, anchorOffset, focusSlot, focusOffset } = this.selection;
259
+ if (anchorSlot) {
260
+ const anchorYText = this.contentMap.get(anchorSlot);
261
+ if (anchorYText) {
262
+ const anchorPosition = createRelativePositionFromTypeIndex(anchorYText, anchorOffset);
263
+ if (focusSlot) {
264
+ const focusYText = this.contentMap.get(focusSlot);
265
+ if (focusYText) {
266
+ const focusPosition = createRelativePositionFromTypeIndex(focusYText, focusOffset);
267
+ return {
268
+ focus: focusPosition,
269
+ anchor: anchorPosition
270
+ };
271
+ }
272
+ }
273
+ }
274
+ }
275
+ return null;
276
+ }
277
+ syncSlotContent(content, slot) {
278
+ this.contentMap.set(slot, content);
279
+ const syncRemote = (ev, tr) => {
280
+ this.runRemoteUpdate(tr, () => {
281
+ slot.retain(0);
282
+ ev.keysChanged.forEach(key => {
283
+ const change = ev.keys.get(key);
284
+ if (!change) {
285
+ return;
286
+ }
287
+ const updateType = change.action;
288
+ if (updateType === 'update' || updateType === 'add') {
289
+ const attribute = this.factory.getAttribute(key);
290
+ if (attribute) {
291
+ slot.setAttribute(attribute, content.getAttribute(key));
292
+ }
293
+ }
294
+ else if (updateType === 'delete') {
295
+ const attribute = this.factory.getAttribute(key);
296
+ if (attribute) {
297
+ slot.removeAttribute(attribute);
298
+ }
299
+ }
300
+ });
301
+ ev.delta.forEach(action => {
302
+ if (Reflect.has(action, 'retain')) {
303
+ if (action.attributes) {
304
+ const formats = remoteFormatsToLocal(this.factory, action.attributes);
305
+ if (formats.length) {
306
+ slot.retain(action.retain, formats);
307
+ }
308
+ slot.retain(slot.index + action.retain);
309
+ }
310
+ else {
311
+ slot.retain(action.retain);
312
+ }
313
+ }
314
+ else if (action.insert) {
315
+ const index = slot.index;
316
+ let length = 1;
317
+ if (typeof action.insert === 'string') {
318
+ length = action.insert.length;
319
+ slot.insert(action.insert, remoteFormatsToLocal(this.factory, action.attributes));
320
+ }
321
+ else {
322
+ const sharedComponent = action.insert;
323
+ const component = this.createComponentBySharedComponent(sharedComponent);
324
+ this.syncComponentSlots(sharedComponent.get('slots'), component);
325
+ this.syncComponentState(sharedComponent, component);
326
+ slot.insert(component);
327
+ }
328
+ if (this.selection.isSelected) {
329
+ if (slot === this.selection.anchorSlot && this.selection.anchorOffset > index) {
330
+ this.selection.setAnchor(slot, this.selection.anchorOffset + length);
331
+ }
332
+ if (slot === this.selection.focusSlot && this.selection.focusOffset > index) {
333
+ this.selection.setFocus(slot, this.selection.focusOffset + length);
334
+ }
335
+ }
336
+ }
337
+ else if (action.delete) {
338
+ const index = slot.index;
339
+ slot.retain(slot.index);
340
+ slot.delete(action.delete);
341
+ if (this.selection.isSelected) {
342
+ if (slot === this.selection.anchorSlot && this.selection.anchorOffset >= index) {
343
+ this.selection.setAnchor(slot, this.selection.startOffset - action.delete);
344
+ }
345
+ if (slot === this.selection.focusSlot && this.selection.focusOffset >= index) {
346
+ this.selection.setFocus(slot, this.selection.focusOffset - action.delete);
347
+ }
348
+ }
349
+ }
350
+ });
351
+ });
352
+ };
353
+ content.observe(syncRemote);
354
+ const sub = slot.onContentChange.subscribe(actions => {
355
+ this.runLocalUpdate(() => {
356
+ var _a;
357
+ let offset = 0;
358
+ let length = 0;
359
+ for (const action of actions) {
360
+ if (action.type === 'retain') {
361
+ const formats = action.formats;
362
+ if (formats) {
363
+ const keys = Object.keys(formats);
364
+ let length = keys.length;
365
+ keys.forEach(key => {
366
+ const formatter = this.factory.getFormatter(key);
367
+ if (!formatter) {
368
+ length--;
369
+ Reflect.deleteProperty(formats, key);
370
+ }
371
+ });
372
+ if (length) {
373
+ content.format(offset, action.offset, formats);
374
+ }
375
+ }
376
+ else {
377
+ offset = action.offset;
378
+ }
379
+ }
380
+ else if (action.type === 'insert') {
381
+ const delta = content.toDelta();
382
+ const isEmpty = delta.length === 1 && delta[0].insert === Slot.emptyPlaceholder;
383
+ if (typeof action.content === 'string') {
384
+ length = action.content.length;
385
+ content.insert(offset, action.content, action.formats || {});
386
+ }
387
+ else {
388
+ length = 1;
389
+ const sharedComponent = this.createSharedComponentByComponent(action.ref);
390
+ content.insertEmbed(offset, sharedComponent, action.formats || {});
391
+ }
392
+ if (isEmpty && offset === 0) {
393
+ content.delete(content.length - 1, 1);
394
+ }
395
+ offset += length;
396
+ }
397
+ else if (action.type === 'delete') {
398
+ const delta = content.toDelta();
399
+ if (content.length) {
400
+ content.delete(offset, action.count);
401
+ }
402
+ if (content.length === 0) {
403
+ content.insert(0, '\n', (_a = delta[0]) === null || _a === void 0 ? void 0 : _a.attributes);
404
+ }
405
+ }
406
+ else if (action.type === 'attrSet') {
407
+ content.setAttribute(action.name, action.value);
408
+ }
409
+ else if (action.type === 'attrRemove') {
410
+ content.removeAttribute(action.name);
411
+ }
412
+ }
413
+ });
414
+ });
415
+ sub.add(slot.onChildComponentRemove.subscribe(components => {
416
+ components.forEach(c => {
417
+ this.cleanSubscriptionsByComponent(c);
418
+ });
419
+ }));
420
+ this.contentSyncCaches.set(slot, () => {
421
+ content.unobserve(syncRemote);
422
+ sub.unsubscribe();
423
+ });
424
+ }
425
+ syncSlotState(remoteSlot, slot) {
426
+ const syncRemote = (ev, tr) => {
427
+ this.runRemoteUpdate(tr, () => {
428
+ ev.keysChanged.forEach(key => {
429
+ if (key === 'state') {
430
+ const state = ev.target.get('state');
431
+ slot.updateState(draft => {
432
+ if (typeof draft === 'object' && draft !== null) {
433
+ Object.assign(draft, state);
434
+ }
435
+ else {
436
+ return state;
437
+ }
438
+ });
439
+ }
440
+ });
441
+ });
442
+ };
443
+ remoteSlot.observe(syncRemote);
444
+ const sub = slot.onStateChange.subscribe(change => {
445
+ this.runLocalUpdate(() => {
446
+ remoteSlot.set('state', change.newState);
447
+ }, change.record);
448
+ });
449
+ this.slotStateSyncCaches.set(slot, () => {
450
+ remoteSlot.unobserve(syncRemote);
451
+ sub.unsubscribe();
452
+ });
453
+ }
454
+ syncComponentSlots(remoteSlots, component) {
455
+ const slots = component.slots;
456
+ const syncRemote = (ev, tr) => {
457
+ this.runRemoteUpdate(tr, () => {
458
+ let index = 0;
459
+ slots.retain(index);
460
+ ev.delta.forEach(action => {
461
+ if (Reflect.has(action, 'retain')) {
462
+ index += action.retain;
463
+ slots.retain(index);
464
+ }
465
+ else if (action.insert) {
466
+ action.insert.forEach(item => {
467
+ const slot = this.createSlotBySharedSlot(item);
468
+ slots.insert(slot);
469
+ this.syncSlotContent(item.get('content'), slot);
470
+ this.syncSlotState(item, slot);
471
+ index++;
472
+ });
473
+ }
474
+ else if (action.delete) {
475
+ slots.retain(index);
476
+ slots.delete(action.delete);
477
+ }
478
+ });
479
+ });
480
+ };
481
+ remoteSlots.observe(syncRemote);
482
+ const sub = slots.onChange.subscribe(operations => {
483
+ this.runLocalUpdate(() => {
484
+ const applyActions = operations.apply;
485
+ let index;
486
+ applyActions.forEach(action => {
487
+ if (action.type === 'retain') {
488
+ index = action.offset;
489
+ }
490
+ else if (action.type === 'insertSlot') {
491
+ const sharedSlot = this.createSharedSlotBySlot(action.ref);
492
+ remoteSlots.insert(index, [sharedSlot]);
493
+ index++;
494
+ }
495
+ else if (action.type === 'delete') {
496
+ remoteSlots.delete(index, action.count);
497
+ }
498
+ });
499
+ });
500
+ });
501
+ sub.add(slots.onChildSlotRemove.subscribe(slots => {
502
+ slots.forEach(slot => {
503
+ this.cleanSubscriptionsBySlot(slot);
504
+ });
505
+ }));
506
+ this.slotsSyncCaches.set(component, () => {
507
+ remoteSlots.unobserve(syncRemote);
508
+ sub.unsubscribe();
509
+ });
510
+ }
511
+ syncComponentState(remoteComponent, component) {
512
+ const syncRemote = (ev, tr) => {
513
+ this.runRemoteUpdate(tr, () => {
514
+ ev.keysChanged.forEach(key => {
515
+ if (key === 'state') {
516
+ const state = ev.target.get('state');
517
+ component.updateState(draft => {
518
+ if (typeof draft === 'object' && draft !== null) {
519
+ Object.assign(draft, state);
520
+ }
521
+ else {
522
+ return state;
523
+ }
524
+ });
525
+ }
526
+ });
527
+ });
528
+ };
529
+ remoteComponent.observe(syncRemote);
530
+ const sub = component.onStateChange.subscribe(change => {
531
+ this.runLocalUpdate(() => {
532
+ remoteComponent.set('state', change.newState);
533
+ }, change.record);
534
+ });
535
+ this.componentStateSyncCaches.set(component, () => {
536
+ remoteComponent.unobserve(syncRemote);
537
+ sub.unsubscribe();
538
+ });
539
+ }
540
+ runLocalUpdate(fn, record = true) {
541
+ if (this.updateFromRemote || this.controller.readonly) {
542
+ return;
543
+ }
544
+ this.updateRemoteActions.push({
545
+ record,
546
+ action: fn
547
+ });
548
+ }
549
+ runRemoteUpdate(tr, fn) {
550
+ if (tr.origin === this.yDoc) {
551
+ return;
552
+ }
553
+ this.updateFromRemote = true;
554
+ if (tr.origin === this.manager) {
555
+ this.scheduler.historyApplyTransact(fn);
556
+ }
557
+ else {
558
+ this.scheduler.remoteUpdateTransact(fn);
559
+ }
560
+ this.updateFromRemote = false;
561
+ }
562
+ createSharedComponentByComponent(component) {
563
+ const sharedComponent = new Map();
564
+ sharedComponent.set('state', component.state);
565
+ sharedComponent.set('name', component.name);
566
+ const sharedSlots = new Array();
567
+ sharedComponent.set('slots', sharedSlots);
568
+ component.slots.toArray().forEach(slot => {
569
+ const sharedSlot = this.createSharedSlotBySlot(slot);
570
+ sharedSlots.push([sharedSlot]);
571
+ });
572
+ this.syncComponentSlots(sharedSlots, component);
573
+ this.syncComponentState(sharedComponent, component);
574
+ return sharedComponent;
575
+ }
576
+ createSharedSlotBySlot(slot) {
577
+ const sharedSlot = new Map();
578
+ sharedSlot.set('schema', slot.schema);
579
+ sharedSlot.set('state', slot.state);
580
+ const sharedContent = new Text();
581
+ sharedSlot.set('content', sharedContent);
582
+ let offset = 0;
583
+ slot.toDelta().forEach(i => {
584
+ let formats = {};
585
+ if (i.formats) {
586
+ i.formats.forEach(item => {
587
+ formats[item[0].name] = item[1];
588
+ });
589
+ }
590
+ else {
591
+ formats = null;
592
+ }
593
+ if (typeof i.insert === 'string') {
594
+ sharedContent.insert(offset, i.insert, formats);
595
+ }
596
+ else {
597
+ const sharedComponent = this.createSharedComponentByComponent(i.insert);
598
+ sharedContent.insertEmbed(offset, sharedComponent, formats);
599
+ }
600
+ offset += i.insert.length;
601
+ });
602
+ slot.getAttributes().forEach(item => {
603
+ sharedContent.setAttribute(item[0].name, item[1]);
604
+ });
605
+ this.syncSlotContent(sharedContent, slot);
606
+ this.syncSlotState(sharedSlot, slot);
607
+ return sharedSlot;
608
+ }
609
+ createComponentBySharedComponent(yMap) {
610
+ const sharedSlots = yMap.get('slots');
611
+ const slots = [];
612
+ sharedSlots.forEach(sharedSlot => {
613
+ const slot = this.createSlotBySharedSlot(sharedSlot);
614
+ slots.push(slot);
615
+ });
616
+ const name = yMap.get('name');
617
+ const state = yMap.get('state');
618
+ const instance = this.factory.createComponentByData(name, {
619
+ state,
620
+ slots
621
+ });
622
+ if (instance) {
623
+ instance.slots.toArray().forEach((slot, index) => {
624
+ let sharedSlot = sharedSlots.get(index);
625
+ if (!sharedSlot) {
626
+ sharedSlot = this.createSharedSlotBySlot(slot);
627
+ sharedSlots.push([sharedSlot]);
628
+ }
629
+ this.syncSlotState(sharedSlot, slot);
630
+ this.syncSlotContent(sharedSlot.get('content'), slot);
631
+ });
632
+ return instance;
633
+ }
634
+ throw collaborateErrorFn(`cannot find component factory \`${name}\`.`);
635
+ }
636
+ createSlotBySharedSlot(sharedSlot) {
637
+ const content = sharedSlot.get('content');
638
+ const delta = content.toDelta();
639
+ const slot = this.factory.createSlot({
640
+ schema: sharedSlot.get('schema'),
641
+ state: sharedSlot.get('state'),
642
+ attributes: {},
643
+ formats: {},
644
+ content: []
645
+ });
646
+ const attrs = content.getAttributes();
647
+ Object.keys(attrs).forEach(key => {
648
+ const attribute = this.factory.getAttribute(key);
649
+ if (attribute) {
650
+ slot.setAttribute(attribute, attrs[key]);
651
+ }
652
+ });
653
+ for (const action of delta) {
654
+ if (action.insert) {
655
+ if (typeof action.insert === 'string') {
656
+ const formats = remoteFormatsToLocal(this.factory, action.attributes);
657
+ slot.insert(action.insert, formats);
658
+ }
659
+ else {
660
+ const sharedComponent = action.insert;
661
+ const component = this.createComponentBySharedComponent(sharedComponent);
662
+ slot.insert(component, remoteFormatsToLocal(this.factory, action.attributes));
663
+ this.syncComponentSlots(sharedComponent.get('slots'), component);
664
+ this.syncComponentState(sharedComponent, component);
665
+ }
666
+ }
667
+ else {
668
+ throw collaborateErrorFn('unexpected delta action.');
669
+ }
670
+ }
671
+ return slot;
672
+ }
673
+ cleanSubscriptionsBySlot(slot) {
674
+ this.contentMap.delete(slot);
675
+ [this.contentSyncCaches.get(slot), this.slotStateSyncCaches.get(slot)].forEach(fn => {
676
+ if (fn) {
677
+ fn();
678
+ }
679
+ });
680
+ slot.sliceContent().forEach(i => {
681
+ if (typeof i !== 'string') {
682
+ this.cleanSubscriptionsByComponent(i);
683
+ }
684
+ });
685
+ }
686
+ cleanSubscriptionsByComponent(component) {
687
+ [this.slotsSyncCaches.get(component), this.componentStateSyncCaches.get(component)].forEach(fn => {
688
+ if (fn) {
689
+ fn();
690
+ }
691
+ });
692
+ component.slots.toArray().forEach(slot => {
693
+ this.cleanSubscriptionsBySlot(slot);
694
+ });
695
+ }
696
+ };
697
+ Collaborate = __decorate([
698
+ Injectable(),
699
+ __param(0, Inject(HISTORY_STACK_SIZE)),
700
+ __metadata("design:paramtypes", [Number, RootComponentRef,
701
+ Controller,
702
+ Scheduler,
703
+ Factory,
704
+ Selection,
705
+ Starter])
706
+ ], Collaborate);
707
+ function remoteFormatsToLocal(factory, attrs) {
708
+ const formats = [];
709
+ if (attrs) {
710
+ Object.keys(attrs).forEach(key => {
711
+ const formatter = factory.getFormatter(key);
712
+ if (formatter) {
713
+ formats.push([formatter, attrs[key]]);
714
+ }
715
+ });
716
+ }
717
+ return formats;
718
+ }
719
+
720
+ const collaborateModule = {
721
+ providers: [
722
+ Collaborate,
723
+ {
724
+ provide: History,
725
+ useExisting: Collaborate
726
+ }
727
+ ]
728
+ };
729
+
730
+ export { Collaborate, collaborateModule };