@textbus/collaborate 2.5.14 → 2.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bundles/index.js CHANGED
@@ -36,994 +36,993 @@ function __metadata(metadataKey, metadataValue) {
36
36
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue);
37
37
  }
38
38
 
39
- class CollaborateSelectionAwarenessDelegate {
40
- }
41
- exports.CollaborateCursor = class CollaborateCursor {
42
- constructor(injector, nativeSelection, scheduler, selection, awarenessDelegate) {
43
- this.injector = injector;
44
- this.nativeSelection = nativeSelection;
45
- this.scheduler = scheduler;
46
- this.selection = selection;
47
- this.awarenessDelegate = awarenessDelegate;
48
- this.host = browser.createElement('div', {
49
- styles: {
50
- position: 'absolute',
51
- left: 0,
52
- top: 0,
53
- width: '100%',
54
- height: '100%',
55
- pointerEvents: 'none',
56
- zIndex: 1
57
- }
58
- });
59
- this.canvasContainer = browser.createElement('div', {
60
- styles: {
61
- position: 'absolute',
62
- left: 0,
63
- top: 0,
64
- width: '100%',
65
- height: '100%',
66
- overflow: 'hidden'
67
- }
68
- });
69
- this.canvas = browser.createElement('canvas', {
70
- styles: {
71
- position: 'absolute',
72
- opacity: 0.5,
73
- left: 0,
74
- top: 0,
75
- width: '100%',
76
- height: document.documentElement.clientHeight + 'px',
77
- pointerEvents: 'none',
78
- }
79
- });
80
- this.context = this.canvas.getContext('2d');
81
- this.tooltips = browser.createElement('div', {
82
- styles: {
83
- position: 'absolute',
84
- left: 0,
85
- top: 0,
86
- width: '100%',
87
- height: '100%',
88
- pointerEvents: 'none',
89
- fontSize: '12px',
90
- zIndex: 10
91
- }
92
- });
93
- this.onRectsChange = new stream.Subject();
94
- this.subscription = new stream.Subscription();
95
- this.currentSelection = [];
96
- this.container = injector.get(browser.VIEW_CONTAINER);
97
- this.canvasContainer.append(this.canvas);
98
- this.host.append(this.canvasContainer, this.tooltips);
99
- this.container.prepend(this.host);
100
- this.subscription.add(this.onRectsChange.subscribe(rects => {
101
- for (const rect of rects) {
102
- this.context.fillStyle = rect.color;
103
- this.context.beginPath();
104
- this.context.rect(rect.left, rect.top, rect.width, rect.height);
105
- this.context.fill();
106
- this.context.closePath();
107
- }
108
- }), stream.fromEvent(window, 'resize').subscribe(() => {
109
- this.canvas.style.height = document.documentElement.clientHeight + 'px';
110
- this.refresh();
111
- }), this.scheduler.onDocChanged.subscribe(() => {
112
- this.refresh();
113
- }));
114
- }
115
- refresh() {
116
- this.draw(this.currentSelection);
117
- }
118
- destroy() {
119
- this.subscription.unsubscribe();
120
- }
121
- draw(paths) {
122
- this.currentSelection = paths;
123
- const containerRect = this.container.getBoundingClientRect();
124
- this.canvas.style.top = containerRect.top * -1 + 'px';
125
- this.canvas.width = this.canvas.offsetWidth;
126
- this.canvas.height = this.canvas.offsetHeight;
127
- this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
128
- const users = [];
129
- paths.filter(i => {
130
- return i.paths.anchor.length && i.paths.focus.length;
131
- }).forEach(item => {
132
- const anchorPaths = [...item.paths.anchor];
133
- const focusPaths = [...item.paths.focus];
134
- const anchorOffset = anchorPaths.pop();
135
- const anchorSlot = this.selection.findSlotByPaths(anchorPaths);
136
- const focusOffset = focusPaths.pop();
137
- const focusSlot = this.selection.findSlotByPaths(focusPaths);
138
- if (!anchorSlot || !focusSlot) {
139
- return;
140
- }
141
- const { focus, anchor } = this.nativeSelection.getPositionByRange({
142
- focusOffset,
143
- anchorOffset,
144
- focusSlot,
145
- anchorSlot
146
- });
147
- if (!focus || !anchor) {
148
- return;
149
- }
150
- const nativeRange = document.createRange();
151
- nativeRange.setStart(anchor.node, anchor.offset);
152
- nativeRange.setEnd(focus.node, focus.offset);
153
- if ((anchor.node !== focus.node || anchor.offset !== focus.offset) && nativeRange.collapsed) {
154
- nativeRange.setStart(focus.node, focus.offset);
155
- nativeRange.setEnd(anchor.node, anchor.offset);
156
- }
157
- let rects = false;
158
- if (this.awarenessDelegate) {
159
- rects = this.awarenessDelegate.getRects({
160
- focusOffset,
161
- anchorOffset,
162
- focusSlot,
163
- anchorSlot
164
- }, nativeRange);
165
- }
166
- if (!rects) {
167
- rects = nativeRange.getClientRects();
168
- }
169
- const selectionRects = [];
170
- for (let i = rects.length - 1; i >= 0; i--) {
171
- const rect = rects[i];
172
- selectionRects.push({
173
- id: item.id,
174
- color: item.color,
175
- username: item.username,
176
- left: rect.left - containerRect.left,
177
- top: rect.top,
178
- width: rect.width,
179
- height: rect.height,
180
- });
181
- }
182
- this.onRectsChange.next(selectionRects);
183
- const cursorRange = nativeRange.cloneRange();
184
- cursorRange.setStart(focus.node, focus.offset);
185
- cursorRange.collapse(true);
186
- const cursorRect = browser.getLayoutRectByRange(cursorRange);
187
- const rect = {
188
- id: item.id,
189
- username: item.username,
190
- color: item.color,
191
- left: cursorRect.left - containerRect.left,
192
- top: cursorRect.top - containerRect.top,
193
- width: 1,
194
- height: cursorRect.height
195
- };
196
- if (rect.left < 0 || rect.top < 0 || rect.left > containerRect.width) {
197
- return;
198
- }
199
- users.push(rect);
200
- });
201
- this.drawUserCursor(users);
202
- }
203
- drawUserCursor(rects) {
204
- for (let i = 0; i < rects.length; i++) {
205
- const rect = rects[i];
206
- const { cursor, userTip, anchor } = this.getUserCursor(i);
207
- Object.assign(cursor.style, {
208
- left: rect.left + 'px',
209
- top: rect.top + 'px',
210
- width: rect.width + 'px',
211
- height: rect.height + 'px',
212
- background: rect.color,
213
- display: 'block'
214
- });
215
- anchor.style.background = rect.color;
216
- userTip.innerText = rect.username;
217
- userTip.style.background = rect.color;
218
- }
219
- for (let i = rects.length; i < this.tooltips.children.length; i++) {
220
- this.tooltips.removeChild(this.tooltips.children[i]);
221
- }
222
- }
223
- getUserCursor(index) {
224
- let child = this.tooltips.children[index];
225
- if (child) {
226
- const anchor = child.children[0];
227
- return {
228
- cursor: child,
229
- anchor,
230
- userTip: anchor.children[0]
231
- };
232
- }
233
- const userTip = browser.createElement('span', {
234
- styles: {
235
- position: 'absolute',
236
- left: '50%',
237
- transform: 'translateX(-50%)',
238
- marginBottom: '2px',
239
- bottom: '100%',
240
- whiteSpace: 'nowrap',
241
- color: '#fff',
242
- boxShadow: '0 1px 2px rgba(0,0,0,.1)',
243
- opacity: 0.8,
244
- borderRadius: '3px',
245
- padding: '3px 5px',
246
- pointerEvents: 'none',
247
- }
248
- });
249
- const anchor = browser.createElement('span', {
250
- styles: {
251
- position: 'absolute',
252
- top: '-2px',
253
- left: '-2px',
254
- width: '5px',
255
- height: '5px',
256
- borderRadius: '50%',
257
- pointerEvents: 'auto',
258
- pointer: 'cursor',
259
- },
260
- children: [userTip]
261
- });
262
- child = browser.createElement('span', {
263
- styles: {
264
- position: 'absolute',
265
- },
266
- children: [
267
- anchor
268
- ]
269
- });
270
- this.tooltips.append(child);
271
- return {
272
- cursor: child,
273
- anchor,
274
- userTip
275
- };
276
- }
277
- };
278
- exports.CollaborateCursor = __decorate([
279
- di.Injectable(),
280
- __param(4, di.Optional()),
281
- __metadata("design:paramtypes", [di.Injector,
282
- browser.SelectionBridge,
283
- core.Scheduler,
284
- core.Selection,
285
- CollaborateSelectionAwarenessDelegate])
39
+ class CollaborateSelectionAwarenessDelegate {
40
+ }
41
+ exports.CollaborateCursor = class CollaborateCursor {
42
+ constructor(injector, nativeSelection, scheduler, selection, awarenessDelegate) {
43
+ this.injector = injector;
44
+ this.nativeSelection = nativeSelection;
45
+ this.scheduler = scheduler;
46
+ this.selection = selection;
47
+ this.awarenessDelegate = awarenessDelegate;
48
+ this.host = browser.createElement('div', {
49
+ styles: {
50
+ position: 'absolute',
51
+ left: 0,
52
+ top: 0,
53
+ width: '100%',
54
+ height: '100%',
55
+ pointerEvents: 'none',
56
+ zIndex: 1
57
+ }
58
+ });
59
+ this.canvasContainer = browser.createElement('div', {
60
+ styles: {
61
+ position: 'absolute',
62
+ left: 0,
63
+ top: 0,
64
+ width: '100%',
65
+ height: '100%',
66
+ overflow: 'hidden'
67
+ }
68
+ });
69
+ this.canvas = browser.createElement('canvas', {
70
+ styles: {
71
+ position: 'absolute',
72
+ opacity: 0.5,
73
+ left: 0,
74
+ top: 0,
75
+ width: '100%',
76
+ height: document.documentElement.clientHeight + 'px',
77
+ pointerEvents: 'none',
78
+ }
79
+ });
80
+ this.context = this.canvas.getContext('2d');
81
+ this.tooltips = browser.createElement('div', {
82
+ styles: {
83
+ position: 'absolute',
84
+ left: 0,
85
+ top: 0,
86
+ width: '100%',
87
+ height: '100%',
88
+ pointerEvents: 'none',
89
+ fontSize: '12px',
90
+ zIndex: 10
91
+ }
92
+ });
93
+ this.onRectsChange = new stream.Subject();
94
+ this.subscription = new stream.Subscription();
95
+ this.currentSelection = [];
96
+ this.container = injector.get(browser.VIEW_CONTAINER);
97
+ this.canvasContainer.append(this.canvas);
98
+ this.host.append(this.canvasContainer, this.tooltips);
99
+ this.container.prepend(this.host);
100
+ this.subscription.add(this.onRectsChange.subscribe(rects => {
101
+ for (const rect of rects) {
102
+ this.context.fillStyle = rect.color;
103
+ this.context.beginPath();
104
+ this.context.rect(rect.left, rect.top, rect.width, rect.height);
105
+ this.context.fill();
106
+ this.context.closePath();
107
+ }
108
+ }), stream.fromEvent(window, 'resize').subscribe(() => {
109
+ this.canvas.style.height = document.documentElement.clientHeight + 'px';
110
+ this.refresh();
111
+ }), this.scheduler.onDocChanged.subscribe(() => {
112
+ this.refresh();
113
+ }));
114
+ }
115
+ refresh() {
116
+ this.draw(this.currentSelection);
117
+ }
118
+ destroy() {
119
+ this.subscription.unsubscribe();
120
+ }
121
+ draw(paths) {
122
+ this.currentSelection = paths;
123
+ const containerRect = this.container.getBoundingClientRect();
124
+ this.canvas.style.top = containerRect.top * -1 + 'px';
125
+ this.canvas.width = this.canvas.offsetWidth;
126
+ this.canvas.height = this.canvas.offsetHeight;
127
+ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
128
+ const users = [];
129
+ paths.filter(i => {
130
+ return i.paths.anchor.length && i.paths.focus.length;
131
+ }).forEach(item => {
132
+ const anchorPaths = [...item.paths.anchor];
133
+ const focusPaths = [...item.paths.focus];
134
+ const anchorOffset = anchorPaths.pop();
135
+ const anchorSlot = this.selection.findSlotByPaths(anchorPaths);
136
+ const focusOffset = focusPaths.pop();
137
+ const focusSlot = this.selection.findSlotByPaths(focusPaths);
138
+ if (!anchorSlot || !focusSlot) {
139
+ return;
140
+ }
141
+ const { focus, anchor } = this.nativeSelection.getPositionByRange({
142
+ focusOffset,
143
+ anchorOffset,
144
+ focusSlot,
145
+ anchorSlot
146
+ });
147
+ if (!focus || !anchor) {
148
+ return;
149
+ }
150
+ const nativeRange = document.createRange();
151
+ nativeRange.setStart(anchor.node, anchor.offset);
152
+ nativeRange.setEnd(focus.node, focus.offset);
153
+ if ((anchor.node !== focus.node || anchor.offset !== focus.offset) && nativeRange.collapsed) {
154
+ nativeRange.setStart(focus.node, focus.offset);
155
+ nativeRange.setEnd(anchor.node, anchor.offset);
156
+ }
157
+ let rects = false;
158
+ if (this.awarenessDelegate) {
159
+ rects = this.awarenessDelegate.getRects({
160
+ focusOffset,
161
+ anchorOffset,
162
+ focusSlot,
163
+ anchorSlot
164
+ }, nativeRange);
165
+ }
166
+ if (!rects) {
167
+ rects = nativeRange.getClientRects();
168
+ }
169
+ const selectionRects = [];
170
+ for (let i = rects.length - 1; i >= 0; i--) {
171
+ const rect = rects[i];
172
+ selectionRects.push({
173
+ id: item.id,
174
+ color: item.color,
175
+ username: item.username,
176
+ left: rect.left - containerRect.left,
177
+ top: rect.top,
178
+ width: rect.width,
179
+ height: rect.height,
180
+ });
181
+ }
182
+ this.onRectsChange.next(selectionRects);
183
+ const cursorRange = nativeRange.cloneRange();
184
+ cursorRange.setStart(focus.node, focus.offset);
185
+ cursorRange.collapse(true);
186
+ const cursorRect = browser.getLayoutRectByRange(cursorRange);
187
+ const rect = {
188
+ id: item.id,
189
+ username: item.username,
190
+ color: item.color,
191
+ left: cursorRect.left - containerRect.left,
192
+ top: cursorRect.top - containerRect.top,
193
+ width: 1,
194
+ height: cursorRect.height
195
+ };
196
+ if (rect.left < 0 || rect.top < 0 || rect.left > containerRect.width) {
197
+ return;
198
+ }
199
+ users.push(rect);
200
+ });
201
+ this.drawUserCursor(users);
202
+ }
203
+ drawUserCursor(rects) {
204
+ for (let i = 0; i < rects.length; i++) {
205
+ const rect = rects[i];
206
+ const { cursor, userTip, anchor } = this.getUserCursor(i);
207
+ Object.assign(cursor.style, {
208
+ left: rect.left + 'px',
209
+ top: rect.top + 'px',
210
+ width: rect.width + 'px',
211
+ height: rect.height + 'px',
212
+ background: rect.color,
213
+ display: 'block'
214
+ });
215
+ anchor.style.background = rect.color;
216
+ userTip.innerText = rect.username;
217
+ userTip.style.background = rect.color;
218
+ }
219
+ for (let i = rects.length; i < this.tooltips.children.length; i++) {
220
+ this.tooltips.removeChild(this.tooltips.children[i]);
221
+ }
222
+ }
223
+ getUserCursor(index) {
224
+ let child = this.tooltips.children[index];
225
+ if (child) {
226
+ const anchor = child.children[0];
227
+ return {
228
+ cursor: child,
229
+ anchor,
230
+ userTip: anchor.children[0]
231
+ };
232
+ }
233
+ const userTip = browser.createElement('span', {
234
+ styles: {
235
+ position: 'absolute',
236
+ left: '50%',
237
+ transform: 'translateX(-50%)',
238
+ marginBottom: '2px',
239
+ bottom: '100%',
240
+ whiteSpace: 'nowrap',
241
+ color: '#fff',
242
+ boxShadow: '0 1px 2px rgba(0,0,0,.1)',
243
+ opacity: 0.8,
244
+ borderRadius: '3px',
245
+ padding: '3px 5px',
246
+ pointerEvents: 'none',
247
+ }
248
+ });
249
+ const anchor = browser.createElement('span', {
250
+ styles: {
251
+ position: 'absolute',
252
+ top: '-2px',
253
+ left: '-2px',
254
+ width: '5px',
255
+ height: '5px',
256
+ borderRadius: '50%',
257
+ pointerEvents: 'auto',
258
+ pointer: 'cursor',
259
+ },
260
+ children: [userTip]
261
+ });
262
+ child = browser.createElement('span', {
263
+ styles: {
264
+ position: 'absolute',
265
+ },
266
+ children: [
267
+ anchor
268
+ ]
269
+ });
270
+ this.tooltips.append(child);
271
+ return {
272
+ cursor: child,
273
+ anchor,
274
+ userTip
275
+ };
276
+ }
277
+ };
278
+ exports.CollaborateCursor = __decorate([
279
+ di.Injectable(),
280
+ __param(4, di.Optional()),
281
+ __metadata("design:paramtypes", [di.Injector,
282
+ browser.SelectionBridge,
283
+ core.Scheduler,
284
+ core.Selection,
285
+ CollaborateSelectionAwarenessDelegate])
286
286
  ], exports.CollaborateCursor);
287
287
 
288
- function createUnknownComponent(factoryName, canInsertInlineComponent) {
289
- const unknownComponent = core.defineComponent({
290
- type: canInsertInlineComponent ? core.ContentType.InlineComponent : core.ContentType.BlockComponent,
291
- name: 'UnknownComponent',
292
- setup() {
293
- console.error(`cannot find component factory \`${factoryName}\`.`);
294
- return {
295
- render() {
296
- return core.VElement.createElement('textbus-unknown-component', {
297
- style: {
298
- display: canInsertInlineComponent ? 'inline' : 'block',
299
- color: '#f00'
300
- }
301
- }, unknownComponent.name);
302
- }
303
- };
304
- }
305
- });
306
- return unknownComponent;
288
+ function createUnknownComponent(factoryName, canInsertInlineComponent) {
289
+ const unknownComponent = core.defineComponent({
290
+ type: canInsertInlineComponent ? core.ContentType.InlineComponent : core.ContentType.BlockComponent,
291
+ name: 'UnknownComponent',
292
+ setup() {
293
+ console.error(`cannot find component factory \`${factoryName}\`.`);
294
+ return {
295
+ render() {
296
+ return core.VElement.createElement('textbus-unknown-component', {
297
+ style: {
298
+ display: canInsertInlineComponent ? 'inline' : 'block',
299
+ color: '#f00'
300
+ }
301
+ }, unknownComponent.name);
302
+ }
303
+ };
304
+ }
305
+ });
306
+ return unknownComponent;
307
307
  }
308
308
 
309
- const collaborateErrorFn = core.makeError('Collaborate');
310
- class ContentMap {
311
- constructor() {
312
- this.slotAndYTextMap = new WeakMap();
313
- this.yTextAndSLotMap = new WeakMap();
314
- }
315
- set(key, value) {
316
- if (key instanceof core.Slot) {
317
- this.slotAndYTextMap.set(key, value);
318
- this.yTextAndSLotMap.set(value, key);
319
- }
320
- else {
321
- this.slotAndYTextMap.set(value, key);
322
- this.yTextAndSLotMap.set(key, value);
323
- }
324
- }
325
- get(key) {
326
- if (key instanceof core.Slot) {
327
- return this.slotAndYTextMap.get(key) || null;
328
- }
329
- return this.yTextAndSLotMap.get(key) || null;
330
- }
331
- delete(key) {
332
- if (key instanceof core.Slot) {
333
- const v = this.slotAndYTextMap.get(key);
334
- this.slotAndYTextMap.delete(key);
335
- if (v) {
336
- this.yTextAndSLotMap.delete(v);
337
- }
338
- }
339
- else {
340
- const v = this.yTextAndSLotMap.get(key);
341
- this.yTextAndSLotMap.delete(key);
342
- if (v) {
343
- this.slotAndYTextMap.delete(v);
344
- }
345
- }
346
- }
347
- }
348
- exports.Collaborate = class Collaborate {
349
- get canBack() {
350
- var _a;
351
- return ((_a = this.manager) === null || _a === void 0 ? void 0 : _a.canUndo()) || false;
352
- }
353
- get canForward() {
354
- var _a;
355
- return ((_a = this.manager) === null || _a === void 0 ? void 0 : _a.canRedo()) || false;
356
- }
357
- constructor(stackSize, rootComponentRef, collaborateCursor, controller, scheduler, translator, registry, selection, starter) {
358
- this.stackSize = stackSize;
359
- this.rootComponentRef = rootComponentRef;
360
- this.collaborateCursor = collaborateCursor;
361
- this.controller = controller;
362
- this.scheduler = scheduler;
363
- this.translator = translator;
364
- this.registry = registry;
365
- this.selection = selection;
366
- this.starter = starter;
367
- this.yDoc = new yjs.Doc();
368
- this.backEvent = new stream.Subject();
369
- this.forwardEvent = new stream.Subject();
370
- this.changeEvent = new stream.Subject();
371
- this.pushEvent = new stream.Subject();
372
- this.manager = null;
373
- this.subscriptions = [];
374
- this.updateFromRemote = false;
375
- this.contentSyncCaches = new WeakMap();
376
- this.slotStateSyncCaches = new WeakMap();
377
- this.slotsSyncCaches = new WeakMap();
378
- this.componentStateSyncCaches = new WeakMap();
379
- this.localChangesAppliedEvent = new stream.Subject();
380
- this.selectionChangeEvent = new stream.Subject();
381
- this.contentMap = new ContentMap();
382
- this.updateRemoteActions = [];
383
- this.noRecord = {};
384
- this.historyItems = [];
385
- this.index = 0;
386
- this.onSelectionChange = this.selectionChangeEvent.asObservable().pipe(stream.delay());
387
- this.onBack = this.backEvent.asObservable();
388
- this.onForward = this.forwardEvent.asObservable();
389
- this.onChange = this.changeEvent.asObservable();
390
- this.onPush = this.pushEvent.asObservable();
391
- this.onLocalChangesApplied = this.localChangesAppliedEvent.asObservable();
392
- }
393
- listen() {
394
- const root = this.yDoc.getMap('RootComponent');
395
- const rootComponent = this.rootComponentRef.component;
396
- const manager = new yjs.UndoManager(root, {
397
- trackedOrigins: new Set([this.yDoc])
398
- });
399
- this.manager = manager;
400
- manager.on('stack-item-added', event => {
401
- if (event.type === 'undo') {
402
- if (event.origin === manager) {
403
- this.index++;
404
- }
405
- else {
406
- this.historyItems.length = this.index;
407
- this.historyItems.push(this.getRelativeCursorLocation());
408
- this.index++;
409
- }
410
- }
411
- else {
412
- this.index--;
413
- }
414
- if (manager.undoStack.length > this.stackSize) {
415
- this.historyItems.shift();
416
- manager.undoStack.shift();
417
- }
418
- if (event.origin === this.yDoc) {
419
- this.pushEvent.next();
420
- }
421
- this.changeEvent.next();
422
- });
423
- manager.on('stack-item-popped', () => {
424
- const position = this.historyItems[this.index - 1];
425
- if (position) {
426
- const selection = this.getAbstractSelection(position);
427
- if (selection) {
428
- this.selection.setBaseAndExtent(selection.anchorSlot, selection.anchorOffset, selection.focusSlot, selection.focusOffset);
429
- return;
430
- }
431
- }
432
- this.selection.unSelect();
433
- });
434
- this.subscriptions.push(this.selection.onChange.subscribe(() => {
435
- const paths = this.selection.getPaths();
436
- this.selectionChangeEvent.next(paths);
437
- }), this.scheduler.onDocChanged.pipe(stream.map(item => {
438
- return item.filter(i => {
439
- return i.from !== core.ChangeOrigin.Remote;
440
- });
441
- }), stream.filter(item => {
442
- return item.length;
443
- })).subscribe(() => {
444
- const updates = [];
445
- let update = null;
446
- for (const item of this.updateRemoteActions) {
447
- if (!update) {
448
- update = {
449
- record: item.record,
450
- actions: []
451
- };
452
- updates.push(update);
453
- }
454
- if (update.record === item.record) {
455
- update.actions.push(item.action);
456
- }
457
- else {
458
- update = {
459
- record: item.record,
460
- actions: [item.action]
461
- };
462
- updates.push(update);
463
- }
464
- }
465
- this.updateRemoteActions = [];
466
- for (const item of updates) {
467
- this.yDoc.transact(() => {
468
- item.actions.forEach(fn => {
469
- fn();
470
- });
471
- }, item.record ? this.yDoc : this.noRecord);
472
- }
473
- this.localChangesAppliedEvent.next();
474
- }));
475
- this.syncRootComponent(root, rootComponent);
476
- }
477
- updateRemoteSelection(paths) {
478
- this.collaborateCursor.draw(paths);
479
- }
480
- back() {
481
- var _a;
482
- if (this.canBack) {
483
- (_a = this.manager) === null || _a === void 0 ? void 0 : _a.undo();
484
- this.backEvent.next();
485
- }
486
- }
487
- forward() {
488
- var _a;
489
- if (this.canForward) {
490
- (_a = this.manager) === null || _a === void 0 ? void 0 : _a.redo();
491
- this.forwardEvent.next();
492
- }
493
- }
494
- clear() {
495
- var _a;
496
- this.index = 0;
497
- this.historyItems = [];
498
- (_a = this.manager) === null || _a === void 0 ? void 0 : _a.clear();
499
- this.changeEvent.next();
500
- }
501
- destroy() {
502
- var _a;
503
- this.index = 0;
504
- this.historyItems = [];
505
- this.subscriptions.forEach(i => i.unsubscribe());
506
- this.collaborateCursor.destroy();
507
- (_a = this.manager) === null || _a === void 0 ? void 0 : _a.destroy();
508
- }
509
- syncRootComponent(root, rootComponent) {
510
- let slots = root.get('slots');
511
- if (!slots) {
512
- slots = new yjs.Array();
513
- rootComponent.slots.toArray().forEach(i => {
514
- const sharedSlot = this.createSharedSlotBySlot(i);
515
- slots.push([sharedSlot]);
516
- });
517
- this.yDoc.transact(() => {
518
- root.set('state', rootComponent.state);
519
- root.set('slots', slots);
520
- });
521
- }
522
- else if (slots.length === 0) {
523
- rootComponent.updateState(() => {
524
- return root.get('state');
525
- });
526
- this.yDoc.transact(() => {
527
- rootComponent.slots.toArray().forEach(i => {
528
- const sharedSlot = this.createSharedSlotBySlot(i);
529
- slots.push([sharedSlot]);
530
- });
531
- });
532
- }
533
- else {
534
- rootComponent.updateState(() => {
535
- return root.get('state');
536
- });
537
- rootComponent.slots.clean();
538
- slots.forEach(sharedSlot => {
539
- const slot = this.createSlotBySharedSlot(sharedSlot);
540
- this.syncContent(sharedSlot.get('content'), slot);
541
- this.syncSlot(sharedSlot, slot);
542
- rootComponent.slots.insert(slot);
543
- });
544
- }
545
- this.syncComponent(root, rootComponent);
546
- this.syncSlots(slots, rootComponent);
547
- }
548
- getAbstractSelection(position) {
549
- const anchorPosition = yjs.createAbsolutePositionFromRelativePosition(position.anchor, this.yDoc);
550
- const focusPosition = yjs.createAbsolutePositionFromRelativePosition(position.focus, this.yDoc);
551
- if (anchorPosition && focusPosition) {
552
- const focusSlot = this.contentMap.get(focusPosition.type);
553
- const anchorSlot = this.contentMap.get(anchorPosition.type);
554
- if (focusSlot && anchorSlot) {
555
- return {
556
- anchorSlot,
557
- anchorOffset: anchorPosition.index,
558
- focusSlot,
559
- focusOffset: focusPosition.index
560
- };
561
- }
562
- }
563
- return null;
564
- }
565
- getRelativeCursorLocation() {
566
- const { anchorSlot, anchorOffset, focusSlot, focusOffset } = this.selection;
567
- if (anchorSlot) {
568
- const anchorYText = this.contentMap.get(anchorSlot);
569
- if (anchorYText) {
570
- const anchorPosition = yjs.createRelativePositionFromTypeIndex(anchorYText, anchorOffset);
571
- if (focusSlot) {
572
- const focusYText = this.contentMap.get(focusSlot);
573
- if (focusYText) {
574
- const focusPosition = yjs.createRelativePositionFromTypeIndex(focusYText, focusOffset);
575
- return {
576
- focus: focusPosition,
577
- anchor: anchorPosition
578
- };
579
- }
580
- }
581
- }
582
- }
583
- return null;
584
- }
585
- syncContent(content, slot) {
586
- this.contentMap.set(slot, content);
587
- const syncRemote = (ev, tr) => {
588
- this.runRemoteUpdate(tr, () => {
589
- slot.retain(0);
590
- ev.delta.forEach(action => {
591
- if (Reflect.has(action, 'retain')) {
592
- if (action.attributes) {
593
- const formats = remoteFormatsToLocal(this.registry, action.attributes);
594
- if (formats.length) {
595
- slot.retain(action.retain, formats);
596
- }
597
- slot.retain(slot.index + action.retain);
598
- }
599
- else {
600
- slot.retain(action.retain);
601
- }
602
- }
603
- else if (action.insert) {
604
- const index = slot.index;
605
- let length = 1;
606
- if (typeof action.insert === 'string') {
607
- length = action.insert.length;
608
- slot.insert(action.insert, remoteFormatsToLocal(this.registry, action.attributes));
609
- }
610
- else {
611
- const sharedComponent = action.insert;
612
- const canInsertInlineComponent = slot.schema.includes(core.ContentType.InlineComponent);
613
- const component = this.createComponentBySharedComponent(sharedComponent, canInsertInlineComponent);
614
- this.syncSlots(sharedComponent.get('slots'), component);
615
- this.syncComponent(sharedComponent, component);
616
- slot.insert(component);
617
- }
618
- if (this.selection.isSelected) {
619
- if (slot === this.selection.anchorSlot && this.selection.anchorOffset > index) {
620
- this.selection.setAnchor(slot, this.selection.anchorOffset + length);
621
- }
622
- if (slot === this.selection.focusSlot && this.selection.focusOffset > index) {
623
- this.selection.setFocus(slot, this.selection.focusOffset + length);
624
- }
625
- }
626
- }
627
- else if (action.delete) {
628
- const index = slot.index;
629
- slot.retain(slot.index);
630
- slot.delete(action.delete);
631
- if (this.selection.isSelected) {
632
- if (slot === this.selection.anchorSlot && this.selection.anchorOffset >= index) {
633
- this.selection.setAnchor(slot, this.selection.startOffset - action.delete);
634
- }
635
- if (slot === this.selection.focusSlot && this.selection.focusOffset >= index) {
636
- this.selection.setFocus(slot, this.selection.focusOffset - action.delete);
637
- }
638
- }
639
- }
640
- else if (action.attributes) {
641
- slot.updateState(draft => {
642
- if (typeof draft === 'object' && draft !== null) {
643
- Object.assign(draft, action.attributes);
644
- }
645
- else {
646
- return action.attributes;
647
- }
648
- });
649
- }
650
- });
651
- });
652
- };
653
- content.observe(syncRemote);
654
- const sub = slot.onContentChange.subscribe(actions => {
655
- this.runLocalUpdate(() => {
656
- var _a;
657
- let offset = 0;
658
- let length = 0;
659
- for (const action of actions) {
660
- if (action.type === 'retain') {
661
- const formats = action.formats;
662
- if (formats) {
663
- const keys = Object.keys(formats);
664
- let length = keys.length;
665
- keys.forEach(key => {
666
- const formatter = this.registry.getFormatter(key);
667
- if (!formatter) {
668
- length--;
669
- Reflect.deleteProperty(formats, key);
670
- }
671
- });
672
- if (length) {
673
- content.format(offset, action.offset, formats);
674
- }
675
- }
676
- else {
677
- offset = action.offset;
678
- }
679
- }
680
- else if (action.type === 'insert') {
681
- const delta = content.toDelta();
682
- const isEmpty = delta.length === 1 && delta[0].insert === core.Slot.emptyPlaceholder;
683
- if (typeof action.content === 'string') {
684
- length = action.content.length;
685
- content.insert(offset, action.content, action.formats || {});
686
- }
687
- else {
688
- length = 1;
689
- const sharedComponent = this.createSharedComponentByComponent(action.ref);
690
- content.insertEmbed(offset, sharedComponent, action.formats || {});
691
- }
692
- if (isEmpty && offset === 0) {
693
- content.delete(content.length - 1, 1);
694
- }
695
- offset += length;
696
- }
697
- else if (action.type === 'delete') {
698
- const delta = content.toDelta();
699
- if (content.length) {
700
- content.delete(offset, action.count);
701
- }
702
- if (content.length === 0) {
703
- content.insert(0, '\n', (_a = delta[0]) === null || _a === void 0 ? void 0 : _a.attributes);
704
- }
705
- }
706
- }
707
- });
708
- });
709
- sub.add(slot.onChildComponentRemove.subscribe(components => {
710
- components.forEach(c => {
711
- this.cleanSubscriptionsByComponent(c);
712
- });
713
- }));
714
- this.contentSyncCaches.set(slot, () => {
715
- content.unobserve(syncRemote);
716
- sub.unsubscribe();
717
- });
718
- }
719
- syncSlot(remoteSlot, slot) {
720
- const syncRemote = (ev, tr) => {
721
- this.runRemoteUpdate(tr, () => {
722
- ev.keysChanged.forEach(key => {
723
- if (key === 'state') {
724
- const state = ev.target.get('state');
725
- slot.updateState(draft => {
726
- if (typeof draft === 'object' && draft !== null) {
727
- Object.assign(draft, state);
728
- }
729
- else {
730
- return state;
731
- }
732
- });
733
- }
734
- });
735
- });
736
- };
737
- remoteSlot.observe(syncRemote);
738
- const sub = slot.onStateChange.subscribe(change => {
739
- this.runLocalUpdate(() => {
740
- remoteSlot.set('state', change.newState);
741
- }, change.record);
742
- });
743
- this.slotStateSyncCaches.set(slot, () => {
744
- remoteSlot.unobserve(syncRemote);
745
- sub.unsubscribe();
746
- });
747
- }
748
- syncSlots(remoteSlots, component) {
749
- const slots = component.slots;
750
- const syncRemote = (ev, tr) => {
751
- this.runRemoteUpdate(tr, () => {
752
- let index = 0;
753
- slots.retain(index);
754
- ev.delta.forEach(action => {
755
- if (Reflect.has(action, 'retain')) {
756
- index += action.retain;
757
- slots.retain(index);
758
- }
759
- else if (action.insert) {
760
- action.insert.forEach(item => {
761
- const slot = this.createSlotBySharedSlot(item);
762
- slots.insert(slot);
763
- this.syncContent(item.get('content'), slot);
764
- this.syncSlot(item, slot);
765
- index++;
766
- });
767
- }
768
- else if (action.delete) {
769
- slots.retain(index);
770
- slots.delete(action.delete);
771
- }
772
- });
773
- });
774
- };
775
- remoteSlots.observe(syncRemote);
776
- const sub = slots.onChange.subscribe(operations => {
777
- this.runLocalUpdate(() => {
778
- const applyActions = operations.apply;
779
- let index;
780
- applyActions.forEach(action => {
781
- if (action.type === 'retain') {
782
- index = action.offset;
783
- }
784
- else if (action.type === 'insertSlot') {
785
- const sharedSlot = this.createSharedSlotBySlot(action.ref);
786
- remoteSlots.insert(index, [sharedSlot]);
787
- index++;
788
- }
789
- else if (action.type === 'delete') {
790
- remoteSlots.delete(index, action.count);
791
- }
792
- });
793
- });
794
- });
795
- sub.add(slots.onChildSlotRemove.subscribe(slots => {
796
- slots.forEach(slot => {
797
- this.cleanSubscriptionsBySlot(slot);
798
- });
799
- }));
800
- this.slotsSyncCaches.set(component, () => {
801
- remoteSlots.unobserve(syncRemote);
802
- sub.unsubscribe();
803
- });
804
- }
805
- syncComponent(remoteComponent, component) {
806
- const syncRemote = (ev, tr) => {
807
- this.runRemoteUpdate(tr, () => {
808
- ev.keysChanged.forEach(key => {
809
- if (key === 'state') {
810
- const state = ev.target.get('state');
811
- component.updateState(draft => {
812
- if (typeof draft === 'object' && draft !== null) {
813
- Object.assign(draft, state);
814
- }
815
- else {
816
- return state;
817
- }
818
- });
819
- }
820
- });
821
- });
822
- };
823
- remoteComponent.observe(syncRemote);
824
- const sub = component.onStateChange.subscribe(change => {
825
- this.runLocalUpdate(() => {
826
- remoteComponent.set('state', change.newState);
827
- }, change.record);
828
- });
829
- this.componentStateSyncCaches.set(component, () => {
830
- remoteComponent.unobserve(syncRemote);
831
- sub.unsubscribe();
832
- });
833
- }
834
- runLocalUpdate(fn, record = true) {
835
- if (this.updateFromRemote || this.controller.readonly) {
836
- return;
837
- }
838
- this.updateRemoteActions.push({
839
- record,
840
- action: fn
841
- });
842
- }
843
- runRemoteUpdate(tr, fn) {
844
- if (tr.origin === this.yDoc) {
845
- return;
846
- }
847
- this.updateFromRemote = true;
848
- if (tr.origin === this.manager) {
849
- this.scheduler.historyApplyTransact(fn);
850
- }
851
- else {
852
- this.scheduler.remoteUpdateTransact(fn);
853
- }
854
- this.updateFromRemote = false;
855
- }
856
- createSharedComponentByComponent(component) {
857
- const sharedComponent = new yjs.Map();
858
- sharedComponent.set('state', component.state);
859
- sharedComponent.set('name', component.name);
860
- const sharedSlots = new yjs.Array();
861
- sharedComponent.set('slots', sharedSlots);
862
- component.slots.toArray().forEach(slot => {
863
- const sharedSlot = this.createSharedSlotBySlot(slot);
864
- sharedSlots.push([sharedSlot]);
865
- });
866
- this.syncSlots(sharedSlots, component);
867
- this.syncComponent(sharedComponent, component);
868
- return sharedComponent;
869
- }
870
- createSharedSlotBySlot(slot) {
871
- const sharedSlot = new yjs.Map();
872
- sharedSlot.set('schema', slot.schema);
873
- sharedSlot.set('state', slot.state);
874
- const sharedContent = new yjs.Text();
875
- sharedSlot.set('content', sharedContent);
876
- let offset = 0;
877
- slot.toDelta().forEach(i => {
878
- let formats = {};
879
- if (i.formats) {
880
- i.formats.forEach(item => {
881
- formats[item[0].name] = item[1];
882
- });
883
- }
884
- else {
885
- formats = null;
886
- }
887
- if (typeof i.insert === 'string') {
888
- sharedContent.insert(offset, i.insert, formats);
889
- }
890
- else {
891
- const sharedComponent = this.createSharedComponentByComponent(i.insert);
892
- sharedContent.insertEmbed(offset, sharedComponent, formats);
893
- }
894
- offset += i.insert.length;
895
- });
896
- this.syncContent(sharedContent, slot);
897
- this.syncSlot(sharedSlot, slot);
898
- return sharedSlot;
899
- }
900
- createComponentBySharedComponent(yMap, canInsertInlineComponent) {
901
- const sharedSlots = yMap.get('slots');
902
- const slots = [];
903
- sharedSlots.forEach(sharedSlot => {
904
- const slot = this.createSlotBySharedSlot(sharedSlot);
905
- slots.push(slot);
906
- });
907
- const name = yMap.get('name');
908
- const state = yMap.get('state');
909
- const instance = this.translator.createComponentByData(name, {
910
- state,
911
- slots
912
- });
913
- if (instance) {
914
- instance.slots.toArray().forEach((slot, index) => {
915
- let sharedSlot = sharedSlots.get(index);
916
- if (!sharedSlot) {
917
- sharedSlot = this.createSharedSlotBySlot(slot);
918
- sharedSlots.push([sharedSlot]);
919
- }
920
- this.syncSlot(sharedSlot, slot);
921
- this.syncContent(sharedSlot.get('content'), slot);
922
- });
923
- return instance;
924
- }
925
- return createUnknownComponent(name, canInsertInlineComponent).createInstance(this.starter);
926
- }
927
- createSlotBySharedSlot(sharedSlot) {
928
- const content = sharedSlot.get('content');
929
- const delta = content.toDelta();
930
- const slot = this.translator.createSlot({
931
- schema: sharedSlot.get('schema'),
932
- state: sharedSlot.get('state'),
933
- formats: {},
934
- content: []
935
- });
936
- for (const action of delta) {
937
- if (action.insert) {
938
- if (typeof action.insert === 'string') {
939
- const blockFormats = [];
940
- const formats = remoteFormatsToLocal(this.registry, action.attributes).filter(item => {
941
- if (item[0].type === core.FormatType.Block) {
942
- blockFormats.push(item);
943
- return false;
944
- }
945
- return true;
946
- });
947
- slot.insert(action.insert, formats);
948
- const index = slot.index;
949
- blockFormats.forEach(item => {
950
- slot.setAttribute(item[0], item[1]);
951
- });
952
- slot.retain(index);
953
- }
954
- else {
955
- const sharedComponent = action.insert;
956
- const canInsertInlineComponent = slot.schema.includes(core.ContentType.InlineComponent);
957
- const component = this.createComponentBySharedComponent(sharedComponent, canInsertInlineComponent);
958
- slot.insert(component, remoteFormatsToLocal(this.registry, action.attributes));
959
- this.syncSlots(sharedComponent.get('slots'), component);
960
- this.syncComponent(sharedComponent, component);
961
- }
962
- }
963
- else {
964
- throw collaborateErrorFn('unexpected delta action.');
965
- }
966
- }
967
- return slot;
968
- }
969
- cleanSubscriptionsBySlot(slot) {
970
- this.contentMap.delete(slot);
971
- [this.contentSyncCaches.get(slot), this.slotStateSyncCaches.get(slot)].forEach(fn => {
972
- if (fn) {
973
- fn();
974
- }
975
- });
976
- slot.sliceContent().forEach(i => {
977
- if (typeof i !== 'string') {
978
- this.cleanSubscriptionsByComponent(i);
979
- }
980
- });
981
- }
982
- cleanSubscriptionsByComponent(component) {
983
- [this.slotsSyncCaches.get(component), this.componentStateSyncCaches.get(component)].forEach(fn => {
984
- if (fn) {
985
- fn();
986
- }
987
- });
988
- component.slots.toArray().forEach(slot => {
989
- this.cleanSubscriptionsBySlot(slot);
990
- });
991
- }
992
- };
993
- exports.Collaborate = __decorate([
994
- di.Injectable(),
995
- __param(0, di.Inject(core.HISTORY_STACK_SIZE)),
996
- __metadata("design:paramtypes", [Number, core.RootComponentRef,
997
- exports.CollaborateCursor,
998
- core.Controller,
999
- core.Scheduler,
1000
- core.Translator,
1001
- core.Registry,
1002
- core.Selection,
1003
- core.Starter])
1004
- ], exports.Collaborate);
1005
- function remoteFormatsToLocal(registry, attrs) {
1006
- const formats = [];
1007
- if (attrs) {
1008
- Object.keys(attrs).forEach(key => {
1009
- const formatter = registry.getFormatter(key);
1010
- if (formatter) {
1011
- formats.push([formatter, attrs[key]]);
1012
- }
1013
- });
1014
- }
1015
- return formats;
309
+ const collaborateErrorFn = core.makeError('Collaborate');
310
+ class ContentMap {
311
+ constructor() {
312
+ this.slotAndYTextMap = new WeakMap();
313
+ this.yTextAndSLotMap = new WeakMap();
314
+ }
315
+ set(key, value) {
316
+ if (key instanceof core.Slot) {
317
+ this.slotAndYTextMap.set(key, value);
318
+ this.yTextAndSLotMap.set(value, key);
319
+ }
320
+ else {
321
+ this.slotAndYTextMap.set(value, key);
322
+ this.yTextAndSLotMap.set(key, value);
323
+ }
324
+ }
325
+ get(key) {
326
+ if (key instanceof core.Slot) {
327
+ return this.slotAndYTextMap.get(key) || null;
328
+ }
329
+ return this.yTextAndSLotMap.get(key) || null;
330
+ }
331
+ delete(key) {
332
+ if (key instanceof core.Slot) {
333
+ const v = this.slotAndYTextMap.get(key);
334
+ this.slotAndYTextMap.delete(key);
335
+ if (v) {
336
+ this.yTextAndSLotMap.delete(v);
337
+ }
338
+ }
339
+ else {
340
+ const v = this.yTextAndSLotMap.get(key);
341
+ this.yTextAndSLotMap.delete(key);
342
+ if (v) {
343
+ this.slotAndYTextMap.delete(v);
344
+ }
345
+ }
346
+ }
347
+ }
348
+ exports.Collaborate = class Collaborate {
349
+ get canBack() {
350
+ var _a;
351
+ return ((_a = this.manager) === null || _a === void 0 ? void 0 : _a.canUndo()) || false;
352
+ }
353
+ get canForward() {
354
+ var _a;
355
+ return ((_a = this.manager) === null || _a === void 0 ? void 0 : _a.canRedo()) || false;
356
+ }
357
+ constructor(stackSize, rootComponentRef, collaborateCursor, controller, scheduler, translator, registry, selection, starter) {
358
+ this.stackSize = stackSize;
359
+ this.rootComponentRef = rootComponentRef;
360
+ this.collaborateCursor = collaborateCursor;
361
+ this.controller = controller;
362
+ this.scheduler = scheduler;
363
+ this.translator = translator;
364
+ this.registry = registry;
365
+ this.selection = selection;
366
+ this.starter = starter;
367
+ this.yDoc = new yjs.Doc();
368
+ this.backEvent = new stream.Subject();
369
+ this.forwardEvent = new stream.Subject();
370
+ this.changeEvent = new stream.Subject();
371
+ this.pushEvent = new stream.Subject();
372
+ this.manager = null;
373
+ this.subscriptions = [];
374
+ this.updateFromRemote = false;
375
+ this.contentSyncCaches = new WeakMap();
376
+ this.slotStateSyncCaches = new WeakMap();
377
+ this.slotsSyncCaches = new WeakMap();
378
+ this.componentStateSyncCaches = new WeakMap();
379
+ this.localChangesAppliedEvent = new stream.Subject();
380
+ this.selectionChangeEvent = new stream.Subject();
381
+ this.contentMap = new ContentMap();
382
+ this.updateRemoteActions = [];
383
+ this.noRecord = {};
384
+ this.historyItems = [];
385
+ this.index = 0;
386
+ this.onSelectionChange = this.selectionChangeEvent.asObservable().pipe(stream.delay());
387
+ this.onBack = this.backEvent.asObservable();
388
+ this.onForward = this.forwardEvent.asObservable();
389
+ this.onChange = this.changeEvent.asObservable();
390
+ this.onPush = this.pushEvent.asObservable();
391
+ this.onLocalChangesApplied = this.localChangesAppliedEvent.asObservable();
392
+ }
393
+ listen() {
394
+ const root = this.yDoc.getMap('RootComponent');
395
+ const rootComponent = this.rootComponentRef.component;
396
+ const manager = new yjs.UndoManager(root, {
397
+ trackedOrigins: new Set([this.yDoc])
398
+ });
399
+ this.manager = manager;
400
+ manager.on('stack-item-added', event => {
401
+ if (event.type === 'undo') {
402
+ if (event.origin === manager) {
403
+ this.index++;
404
+ }
405
+ else {
406
+ this.historyItems.length = this.index;
407
+ this.historyItems.push(this.getRelativeCursorLocation());
408
+ this.index++;
409
+ }
410
+ }
411
+ else {
412
+ this.index--;
413
+ }
414
+ if (manager.undoStack.length > this.stackSize) {
415
+ this.historyItems.shift();
416
+ manager.undoStack.shift();
417
+ }
418
+ if (event.origin === this.yDoc) {
419
+ this.pushEvent.next();
420
+ }
421
+ this.changeEvent.next();
422
+ });
423
+ manager.on('stack-item-popped', () => {
424
+ const position = this.historyItems[this.index - 1];
425
+ if (position) {
426
+ const selection = this.getAbstractSelection(position);
427
+ if (selection) {
428
+ this.selection.setBaseAndExtent(selection.anchorSlot, selection.anchorOffset, selection.focusSlot, selection.focusOffset);
429
+ return;
430
+ }
431
+ }
432
+ this.selection.unSelect();
433
+ });
434
+ this.subscriptions.push(this.selection.onChange.subscribe(() => {
435
+ const paths = this.selection.getPaths();
436
+ this.selectionChangeEvent.next(paths);
437
+ }), this.scheduler.onDocChanged.pipe(stream.map(item => {
438
+ return item.filter(i => {
439
+ return i.from !== core.ChangeOrigin.Remote;
440
+ });
441
+ }), stream.filter(item => {
442
+ return item.length;
443
+ })).subscribe(() => {
444
+ const updates = [];
445
+ let update = null;
446
+ for (const item of this.updateRemoteActions) {
447
+ if (!update) {
448
+ update = {
449
+ record: item.record,
450
+ actions: []
451
+ };
452
+ updates.push(update);
453
+ }
454
+ if (update.record === item.record) {
455
+ update.actions.push(item.action);
456
+ }
457
+ else {
458
+ update = {
459
+ record: item.record,
460
+ actions: [item.action]
461
+ };
462
+ updates.push(update);
463
+ }
464
+ }
465
+ this.updateRemoteActions = [];
466
+ for (const item of updates) {
467
+ this.yDoc.transact(() => {
468
+ item.actions.forEach(fn => {
469
+ fn();
470
+ });
471
+ }, item.record ? this.yDoc : this.noRecord);
472
+ }
473
+ this.localChangesAppliedEvent.next();
474
+ }));
475
+ this.syncRootComponent(root, rootComponent);
476
+ }
477
+ updateRemoteSelection(paths) {
478
+ this.collaborateCursor.draw(paths);
479
+ }
480
+ back() {
481
+ var _a;
482
+ if (this.canBack) {
483
+ (_a = this.manager) === null || _a === void 0 ? void 0 : _a.undo();
484
+ this.backEvent.next();
485
+ }
486
+ }
487
+ forward() {
488
+ var _a;
489
+ if (this.canForward) {
490
+ (_a = this.manager) === null || _a === void 0 ? void 0 : _a.redo();
491
+ this.forwardEvent.next();
492
+ }
493
+ }
494
+ clear() {
495
+ var _a;
496
+ this.index = 0;
497
+ this.historyItems = [];
498
+ (_a = this.manager) === null || _a === void 0 ? void 0 : _a.clear();
499
+ this.changeEvent.next();
500
+ }
501
+ destroy() {
502
+ var _a;
503
+ this.index = 0;
504
+ this.historyItems = [];
505
+ this.subscriptions.forEach(i => i.unsubscribe());
506
+ this.collaborateCursor.destroy();
507
+ (_a = this.manager) === null || _a === void 0 ? void 0 : _a.destroy();
508
+ }
509
+ syncRootComponent(root, rootComponent) {
510
+ let slots = root.get('slots');
511
+ if (!slots) {
512
+ slots = new yjs.Array();
513
+ rootComponent.slots.toArray().forEach(i => {
514
+ const sharedSlot = this.createSharedSlotBySlot(i);
515
+ slots.push([sharedSlot]);
516
+ });
517
+ this.yDoc.transact(() => {
518
+ root.set('state', rootComponent.state);
519
+ root.set('slots', slots);
520
+ });
521
+ }
522
+ else if (slots.length === 0) {
523
+ rootComponent.updateState(() => {
524
+ return root.get('state');
525
+ });
526
+ this.yDoc.transact(() => {
527
+ rootComponent.slots.toArray().forEach(i => {
528
+ const sharedSlot = this.createSharedSlotBySlot(i);
529
+ slots.push([sharedSlot]);
530
+ });
531
+ });
532
+ }
533
+ else {
534
+ rootComponent.updateState(() => {
535
+ return root.get('state');
536
+ });
537
+ rootComponent.slots.clean();
538
+ slots.forEach(sharedSlot => {
539
+ const slot = this.createSlotBySharedSlot(sharedSlot);
540
+ this.syncContent(sharedSlot.get('content'), slot);
541
+ this.syncSlot(sharedSlot, slot);
542
+ rootComponent.slots.insert(slot);
543
+ });
544
+ }
545
+ this.syncComponent(root, rootComponent);
546
+ this.syncSlots(slots, rootComponent);
547
+ }
548
+ getAbstractSelection(position) {
549
+ const anchorPosition = yjs.createAbsolutePositionFromRelativePosition(position.anchor, this.yDoc);
550
+ const focusPosition = yjs.createAbsolutePositionFromRelativePosition(position.focus, this.yDoc);
551
+ if (anchorPosition && focusPosition) {
552
+ const focusSlot = this.contentMap.get(focusPosition.type);
553
+ const anchorSlot = this.contentMap.get(anchorPosition.type);
554
+ if (focusSlot && anchorSlot) {
555
+ return {
556
+ anchorSlot,
557
+ anchorOffset: anchorPosition.index,
558
+ focusSlot,
559
+ focusOffset: focusPosition.index
560
+ };
561
+ }
562
+ }
563
+ return null;
564
+ }
565
+ getRelativeCursorLocation() {
566
+ const { anchorSlot, anchorOffset, focusSlot, focusOffset } = this.selection;
567
+ if (anchorSlot) {
568
+ const anchorYText = this.contentMap.get(anchorSlot);
569
+ if (anchorYText) {
570
+ const anchorPosition = yjs.createRelativePositionFromTypeIndex(anchorYText, anchorOffset);
571
+ if (focusSlot) {
572
+ const focusYText = this.contentMap.get(focusSlot);
573
+ if (focusYText) {
574
+ const focusPosition = yjs.createRelativePositionFromTypeIndex(focusYText, focusOffset);
575
+ return {
576
+ focus: focusPosition,
577
+ anchor: anchorPosition
578
+ };
579
+ }
580
+ }
581
+ }
582
+ }
583
+ return null;
584
+ }
585
+ syncContent(content, slot) {
586
+ this.contentMap.set(slot, content);
587
+ const syncRemote = (ev, tr) => {
588
+ this.runRemoteUpdate(tr, () => {
589
+ slot.retain(0);
590
+ ev.delta.forEach(action => {
591
+ if (Reflect.has(action, 'retain')) {
592
+ if (action.attributes) {
593
+ const formats = remoteFormatsToLocal(this.registry, action.attributes);
594
+ if (formats.length) {
595
+ slot.retain(action.retain, formats);
596
+ }
597
+ slot.retain(slot.index + action.retain);
598
+ }
599
+ else {
600
+ slot.retain(action.retain);
601
+ }
602
+ }
603
+ else if (action.insert) {
604
+ const index = slot.index;
605
+ let length = 1;
606
+ if (typeof action.insert === 'string') {
607
+ length = action.insert.length;
608
+ slot.insert(action.insert, remoteFormatsToLocal(this.registry, action.attributes));
609
+ }
610
+ else {
611
+ const sharedComponent = action.insert;
612
+ const canInsertInlineComponent = slot.schema.includes(core.ContentType.InlineComponent);
613
+ const component = this.createComponentBySharedComponent(sharedComponent, canInsertInlineComponent);
614
+ this.syncSlots(sharedComponent.get('slots'), component);
615
+ this.syncComponent(sharedComponent, component);
616
+ slot.insert(component);
617
+ }
618
+ if (this.selection.isSelected && tr.origin !== this.manager) {
619
+ if (slot === this.selection.anchorSlot && this.selection.anchorOffset > index) {
620
+ this.selection.setAnchor(slot, this.selection.anchorOffset + length);
621
+ }
622
+ if (slot === this.selection.focusSlot && this.selection.focusOffset > index) {
623
+ this.selection.setFocus(slot, this.selection.focusOffset + length);
624
+ }
625
+ }
626
+ }
627
+ else if (action.delete) {
628
+ const index = slot.index;
629
+ slot.delete(action.delete);
630
+ if (this.selection.isSelected && tr.origin !== this.manager) {
631
+ if (slot === this.selection.anchorSlot && this.selection.anchorOffset >= index) {
632
+ this.selection.setAnchor(slot, this.selection.startOffset - action.delete);
633
+ }
634
+ if (slot === this.selection.focusSlot && this.selection.focusOffset >= index) {
635
+ this.selection.setFocus(slot, this.selection.focusOffset - action.delete);
636
+ }
637
+ }
638
+ }
639
+ else if (action.attributes) {
640
+ slot.updateState(draft => {
641
+ if (typeof draft === 'object' && draft !== null) {
642
+ Object.assign(draft, action.attributes);
643
+ }
644
+ else {
645
+ return action.attributes;
646
+ }
647
+ });
648
+ }
649
+ });
650
+ });
651
+ };
652
+ content.observe(syncRemote);
653
+ const sub = slot.onContentChange.subscribe(actions => {
654
+ this.runLocalUpdate(() => {
655
+ var _a;
656
+ let offset = 0;
657
+ let length = 0;
658
+ for (const action of actions) {
659
+ if (action.type === 'retain') {
660
+ const formats = action.formats;
661
+ if (formats) {
662
+ const keys = Object.keys(formats);
663
+ let length = keys.length;
664
+ keys.forEach(key => {
665
+ const formatter = this.registry.getFormatter(key);
666
+ if (!formatter) {
667
+ length--;
668
+ Reflect.deleteProperty(formats, key);
669
+ }
670
+ });
671
+ if (length) {
672
+ content.format(offset, action.offset, formats);
673
+ }
674
+ }
675
+ else {
676
+ offset = action.offset;
677
+ }
678
+ }
679
+ else if (action.type === 'insert') {
680
+ const delta = content.toDelta();
681
+ const isEmpty = delta.length === 1 && delta[0].insert === core.Slot.emptyPlaceholder;
682
+ if (typeof action.content === 'string') {
683
+ length = action.content.length;
684
+ content.insert(offset, action.content, action.formats || {});
685
+ }
686
+ else {
687
+ length = 1;
688
+ const sharedComponent = this.createSharedComponentByComponent(action.ref);
689
+ content.insertEmbed(offset, sharedComponent, action.formats || {});
690
+ }
691
+ if (isEmpty && offset === 0) {
692
+ content.delete(content.length - 1, 1);
693
+ }
694
+ offset += length;
695
+ }
696
+ else if (action.type === 'delete') {
697
+ const delta = content.toDelta();
698
+ if (content.length) {
699
+ content.delete(offset, action.count);
700
+ }
701
+ if (content.length === 0) {
702
+ content.insert(0, '\n', (_a = delta[0]) === null || _a === void 0 ? void 0 : _a.attributes);
703
+ }
704
+ }
705
+ }
706
+ });
707
+ });
708
+ sub.add(slot.onChildComponentRemove.subscribe(components => {
709
+ components.forEach(c => {
710
+ this.cleanSubscriptionsByComponent(c);
711
+ });
712
+ }));
713
+ this.contentSyncCaches.set(slot, () => {
714
+ content.unobserve(syncRemote);
715
+ sub.unsubscribe();
716
+ });
717
+ }
718
+ syncSlot(remoteSlot, slot) {
719
+ const syncRemote = (ev, tr) => {
720
+ this.runRemoteUpdate(tr, () => {
721
+ ev.keysChanged.forEach(key => {
722
+ if (key === 'state') {
723
+ const state = ev.target.get('state');
724
+ slot.updateState(draft => {
725
+ if (typeof draft === 'object' && draft !== null) {
726
+ Object.assign(draft, state);
727
+ }
728
+ else {
729
+ return state;
730
+ }
731
+ });
732
+ }
733
+ });
734
+ });
735
+ };
736
+ remoteSlot.observe(syncRemote);
737
+ const sub = slot.onStateChange.subscribe(change => {
738
+ this.runLocalUpdate(() => {
739
+ remoteSlot.set('state', change.newState);
740
+ }, change.record);
741
+ });
742
+ this.slotStateSyncCaches.set(slot, () => {
743
+ remoteSlot.unobserve(syncRemote);
744
+ sub.unsubscribe();
745
+ });
746
+ }
747
+ syncSlots(remoteSlots, component) {
748
+ const slots = component.slots;
749
+ const syncRemote = (ev, tr) => {
750
+ this.runRemoteUpdate(tr, () => {
751
+ let index = 0;
752
+ slots.retain(index);
753
+ ev.delta.forEach(action => {
754
+ if (Reflect.has(action, 'retain')) {
755
+ index += action.retain;
756
+ slots.retain(index);
757
+ }
758
+ else if (action.insert) {
759
+ action.insert.forEach(item => {
760
+ const slot = this.createSlotBySharedSlot(item);
761
+ slots.insert(slot);
762
+ this.syncContent(item.get('content'), slot);
763
+ this.syncSlot(item, slot);
764
+ index++;
765
+ });
766
+ }
767
+ else if (action.delete) {
768
+ slots.retain(index);
769
+ slots.delete(action.delete);
770
+ }
771
+ });
772
+ });
773
+ };
774
+ remoteSlots.observe(syncRemote);
775
+ const sub = slots.onChange.subscribe(operations => {
776
+ this.runLocalUpdate(() => {
777
+ const applyActions = operations.apply;
778
+ let index;
779
+ applyActions.forEach(action => {
780
+ if (action.type === 'retain') {
781
+ index = action.offset;
782
+ }
783
+ else if (action.type === 'insertSlot') {
784
+ const sharedSlot = this.createSharedSlotBySlot(action.ref);
785
+ remoteSlots.insert(index, [sharedSlot]);
786
+ index++;
787
+ }
788
+ else if (action.type === 'delete') {
789
+ remoteSlots.delete(index, action.count);
790
+ }
791
+ });
792
+ });
793
+ });
794
+ sub.add(slots.onChildSlotRemove.subscribe(slots => {
795
+ slots.forEach(slot => {
796
+ this.cleanSubscriptionsBySlot(slot);
797
+ });
798
+ }));
799
+ this.slotsSyncCaches.set(component, () => {
800
+ remoteSlots.unobserve(syncRemote);
801
+ sub.unsubscribe();
802
+ });
803
+ }
804
+ syncComponent(remoteComponent, component) {
805
+ const syncRemote = (ev, tr) => {
806
+ this.runRemoteUpdate(tr, () => {
807
+ ev.keysChanged.forEach(key => {
808
+ if (key === 'state') {
809
+ const state = ev.target.get('state');
810
+ component.updateState(draft => {
811
+ if (typeof draft === 'object' && draft !== null) {
812
+ Object.assign(draft, state);
813
+ }
814
+ else {
815
+ return state;
816
+ }
817
+ });
818
+ }
819
+ });
820
+ });
821
+ };
822
+ remoteComponent.observe(syncRemote);
823
+ const sub = component.onStateChange.subscribe(change => {
824
+ this.runLocalUpdate(() => {
825
+ remoteComponent.set('state', change.newState);
826
+ }, change.record);
827
+ });
828
+ this.componentStateSyncCaches.set(component, () => {
829
+ remoteComponent.unobserve(syncRemote);
830
+ sub.unsubscribe();
831
+ });
832
+ }
833
+ runLocalUpdate(fn, record = true) {
834
+ if (this.updateFromRemote || this.controller.readonly) {
835
+ return;
836
+ }
837
+ this.updateRemoteActions.push({
838
+ record,
839
+ action: fn
840
+ });
841
+ }
842
+ runRemoteUpdate(tr, fn) {
843
+ if (tr.origin === this.yDoc) {
844
+ return;
845
+ }
846
+ this.updateFromRemote = true;
847
+ if (tr.origin === this.manager) {
848
+ this.scheduler.historyApplyTransact(fn);
849
+ }
850
+ else {
851
+ this.scheduler.remoteUpdateTransact(fn);
852
+ }
853
+ this.updateFromRemote = false;
854
+ }
855
+ createSharedComponentByComponent(component) {
856
+ const sharedComponent = new yjs.Map();
857
+ sharedComponent.set('state', component.state);
858
+ sharedComponent.set('name', component.name);
859
+ const sharedSlots = new yjs.Array();
860
+ sharedComponent.set('slots', sharedSlots);
861
+ component.slots.toArray().forEach(slot => {
862
+ const sharedSlot = this.createSharedSlotBySlot(slot);
863
+ sharedSlots.push([sharedSlot]);
864
+ });
865
+ this.syncSlots(sharedSlots, component);
866
+ this.syncComponent(sharedComponent, component);
867
+ return sharedComponent;
868
+ }
869
+ createSharedSlotBySlot(slot) {
870
+ const sharedSlot = new yjs.Map();
871
+ sharedSlot.set('schema', slot.schema);
872
+ sharedSlot.set('state', slot.state);
873
+ const sharedContent = new yjs.Text();
874
+ sharedSlot.set('content', sharedContent);
875
+ let offset = 0;
876
+ slot.toDelta().forEach(i => {
877
+ let formats = {};
878
+ if (i.formats) {
879
+ i.formats.forEach(item => {
880
+ formats[item[0].name] = item[1];
881
+ });
882
+ }
883
+ else {
884
+ formats = null;
885
+ }
886
+ if (typeof i.insert === 'string') {
887
+ sharedContent.insert(offset, i.insert, formats);
888
+ }
889
+ else {
890
+ const sharedComponent = this.createSharedComponentByComponent(i.insert);
891
+ sharedContent.insertEmbed(offset, sharedComponent, formats);
892
+ }
893
+ offset += i.insert.length;
894
+ });
895
+ this.syncContent(sharedContent, slot);
896
+ this.syncSlot(sharedSlot, slot);
897
+ return sharedSlot;
898
+ }
899
+ createComponentBySharedComponent(yMap, canInsertInlineComponent) {
900
+ const sharedSlots = yMap.get('slots');
901
+ const slots = [];
902
+ sharedSlots.forEach(sharedSlot => {
903
+ const slot = this.createSlotBySharedSlot(sharedSlot);
904
+ slots.push(slot);
905
+ });
906
+ const name = yMap.get('name');
907
+ const state = yMap.get('state');
908
+ const instance = this.translator.createComponentByData(name, {
909
+ state,
910
+ slots
911
+ });
912
+ if (instance) {
913
+ instance.slots.toArray().forEach((slot, index) => {
914
+ let sharedSlot = sharedSlots.get(index);
915
+ if (!sharedSlot) {
916
+ sharedSlot = this.createSharedSlotBySlot(slot);
917
+ sharedSlots.push([sharedSlot]);
918
+ }
919
+ this.syncSlot(sharedSlot, slot);
920
+ this.syncContent(sharedSlot.get('content'), slot);
921
+ });
922
+ return instance;
923
+ }
924
+ return createUnknownComponent(name, canInsertInlineComponent).createInstance(this.starter);
925
+ }
926
+ createSlotBySharedSlot(sharedSlot) {
927
+ const content = sharedSlot.get('content');
928
+ const delta = content.toDelta();
929
+ const slot = this.translator.createSlot({
930
+ schema: sharedSlot.get('schema'),
931
+ state: sharedSlot.get('state'),
932
+ formats: {},
933
+ content: []
934
+ });
935
+ for (const action of delta) {
936
+ if (action.insert) {
937
+ if (typeof action.insert === 'string') {
938
+ const blockFormats = [];
939
+ const formats = remoteFormatsToLocal(this.registry, action.attributes).filter(item => {
940
+ if (item[0].type === core.FormatType.Block) {
941
+ blockFormats.push(item);
942
+ return false;
943
+ }
944
+ return true;
945
+ });
946
+ slot.insert(action.insert, formats);
947
+ const index = slot.index;
948
+ blockFormats.forEach(item => {
949
+ slot.setAttribute(item[0], item[1]);
950
+ });
951
+ slot.retain(index);
952
+ }
953
+ else {
954
+ const sharedComponent = action.insert;
955
+ const canInsertInlineComponent = slot.schema.includes(core.ContentType.InlineComponent);
956
+ const component = this.createComponentBySharedComponent(sharedComponent, canInsertInlineComponent);
957
+ slot.insert(component, remoteFormatsToLocal(this.registry, action.attributes));
958
+ this.syncSlots(sharedComponent.get('slots'), component);
959
+ this.syncComponent(sharedComponent, component);
960
+ }
961
+ }
962
+ else {
963
+ throw collaborateErrorFn('unexpected delta action.');
964
+ }
965
+ }
966
+ return slot;
967
+ }
968
+ cleanSubscriptionsBySlot(slot) {
969
+ this.contentMap.delete(slot);
970
+ [this.contentSyncCaches.get(slot), this.slotStateSyncCaches.get(slot)].forEach(fn => {
971
+ if (fn) {
972
+ fn();
973
+ }
974
+ });
975
+ slot.sliceContent().forEach(i => {
976
+ if (typeof i !== 'string') {
977
+ this.cleanSubscriptionsByComponent(i);
978
+ }
979
+ });
980
+ }
981
+ cleanSubscriptionsByComponent(component) {
982
+ [this.slotsSyncCaches.get(component), this.componentStateSyncCaches.get(component)].forEach(fn => {
983
+ if (fn) {
984
+ fn();
985
+ }
986
+ });
987
+ component.slots.toArray().forEach(slot => {
988
+ this.cleanSubscriptionsBySlot(slot);
989
+ });
990
+ }
991
+ };
992
+ exports.Collaborate = __decorate([
993
+ di.Injectable(),
994
+ __param(0, di.Inject(core.HISTORY_STACK_SIZE)),
995
+ __metadata("design:paramtypes", [Number, core.RootComponentRef,
996
+ exports.CollaborateCursor,
997
+ core.Controller,
998
+ core.Scheduler,
999
+ core.Translator,
1000
+ core.Registry,
1001
+ core.Selection,
1002
+ core.Starter])
1003
+ ], exports.Collaborate);
1004
+ function remoteFormatsToLocal(registry, attrs) {
1005
+ const formats = [];
1006
+ if (attrs) {
1007
+ Object.keys(attrs).forEach(key => {
1008
+ const formatter = registry.getFormatter(key);
1009
+ if (formatter) {
1010
+ formats.push([formatter, attrs[key]]);
1011
+ }
1012
+ });
1013
+ }
1014
+ return formats;
1016
1015
  }
1017
1016
 
1018
- const collaborateModule = {
1019
- providers: [
1020
- exports.Collaborate,
1021
- exports.CollaborateCursor,
1022
- {
1023
- provide: core.History,
1024
- useExisting: exports.Collaborate
1025
- }
1026
- ]
1017
+ const collaborateModule = {
1018
+ providers: [
1019
+ exports.Collaborate,
1020
+ exports.CollaborateCursor,
1021
+ {
1022
+ provide: core.History,
1023
+ useExisting: exports.Collaborate
1024
+ }
1025
+ ]
1027
1026
  };
1028
1027
 
1029
1028
  exports.CollaborateSelectionAwarenessDelegate = CollaborateSelectionAwarenessDelegate;