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