@textbus/collaborate 3.0.0-alpha.8 → 3.0.0-y.0

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