@textbus/collaborate 2.5.11 → 2.5.12

Sign up to get free protection for your applications and to get access to all the features.
package/bundles/index.js CHANGED
@@ -36,994 +36,994 @@ 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
- constructor(stackSize, rootComponentRef, collaborateCursor, controller, scheduler, translator, registry, selection, starter) {
350
- this.stackSize = stackSize;
351
- this.rootComponentRef = rootComponentRef;
352
- this.collaborateCursor = collaborateCursor;
353
- this.controller = controller;
354
- this.scheduler = scheduler;
355
- this.translator = translator;
356
- this.registry = registry;
357
- this.selection = selection;
358
- this.starter = starter;
359
- this.yDoc = new yjs.Doc();
360
- this.backEvent = new stream.Subject();
361
- this.forwardEvent = new stream.Subject();
362
- this.changeEvent = new stream.Subject();
363
- this.pushEvent = new stream.Subject();
364
- this.manager = null;
365
- this.subscriptions = [];
366
- this.updateFromRemote = false;
367
- this.contentSyncCaches = new WeakMap();
368
- this.slotStateSyncCaches = new WeakMap();
369
- this.slotsSyncCaches = new WeakMap();
370
- this.componentStateSyncCaches = new WeakMap();
371
- this.localChangesAppliedEvent = new stream.Subject();
372
- this.selectionChangeEvent = new stream.Subject();
373
- this.contentMap = new ContentMap();
374
- this.updateRemoteActions = [];
375
- this.noRecord = {};
376
- this.historyItems = [];
377
- this.index = 0;
378
- this.onSelectionChange = this.selectionChangeEvent.asObservable().pipe(stream.delay());
379
- this.onBack = this.backEvent.asObservable();
380
- this.onForward = this.forwardEvent.asObservable();
381
- this.onChange = this.changeEvent.asObservable();
382
- this.onPush = this.pushEvent.asObservable();
383
- this.onLocalChangesApplied = this.localChangesAppliedEvent.asObservable();
384
- }
385
- get canBack() {
386
- var _a;
387
- return ((_a = this.manager) === null || _a === void 0 ? void 0 : _a.canUndo()) || false;
388
- }
389
- get canForward() {
390
- var _a;
391
- return ((_a = this.manager) === null || _a === void 0 ? void 0 : _a.canRedo()) || false;
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) {
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;
1016
1016
  }
1017
1017
 
1018
- const collaborateModule = {
1019
- providers: [
1020
- exports.Collaborate,
1021
- exports.CollaborateCursor,
1022
- {
1023
- provide: core.History,
1024
- useExisting: exports.Collaborate
1025
- }
1026
- ]
1018
+ const collaborateModule = {
1019
+ providers: [
1020
+ exports.Collaborate,
1021
+ exports.CollaborateCursor,
1022
+ {
1023
+ provide: core.History,
1024
+ useExisting: exports.Collaborate
1025
+ }
1026
+ ]
1027
1027
  };
1028
1028
 
1029
1029
  exports.CollaborateSelectionAwarenessDelegate = CollaborateSelectionAwarenessDelegate;