@textbus/collaborate 2.5.5 → 2.5.7

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