@textbus/collaborate 3.0.0-alpha.9 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,778 @@
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
+ 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;
765
+ }
766
+
767
+ const collaborateModule = {
768
+ providers: [
769
+ exports.Collaborate,
770
+ {
771
+ provide: core.History,
772
+ useExisting: exports.Collaborate
773
+ }
774
+ ]
775
+ };
776
+
777
+ exports.CustomUndoManagerConfig = CustomUndoManagerConfig;
778
+ exports.collaborateModule = collaborateModule;