@textbus/collaborate 3.0.0-alpha.6 → 3.0.0-alpha.61

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