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

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