@textbus/collaborate 2.5.12 → 2.5.13

Sign up to get free protection for your applications and to get access to all the features.
@@ -34,994 +34,994 @@ 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) {
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,
283
- CollaborateSelectionAwarenessDelegate])
37
+ class CollaborateSelectionAwarenessDelegate {
38
+ }
39
+ let CollaborateCursor = class CollaborateCursor {
40
+ constructor(injector, nativeSelection, scheduler, selection, awarenessDelegate) {
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,
283
+ CollaborateSelectionAwarenessDelegate])
284
284
  ], CollaborateCursor);
285
285
 
286
- function createUnknownComponent(factoryName, canInsertInlineComponent) {
287
- const unknownComponent = defineComponent({
288
- type: canInsertInlineComponent ? ContentType.InlineComponent : ContentType.BlockComponent,
289
- name: 'UnknownComponent',
290
- setup() {
291
- console.error(`cannot find component factory \`${factoryName}\`.`);
292
- return {
293
- render() {
294
- return VElement.createElement('textbus-unknown-component', {
295
- style: {
296
- display: canInsertInlineComponent ? 'inline' : 'block',
297
- color: '#f00'
298
- }
299
- }, unknownComponent.name);
300
- }
301
- };
302
- }
303
- });
304
- return unknownComponent;
286
+ function createUnknownComponent(factoryName, canInsertInlineComponent) {
287
+ const unknownComponent = defineComponent({
288
+ type: canInsertInlineComponent ? ContentType.InlineComponent : ContentType.BlockComponent,
289
+ name: 'UnknownComponent',
290
+ setup() {
291
+ console.error(`cannot find component factory \`${factoryName}\`.`);
292
+ return {
293
+ render() {
294
+ return VElement.createElement('textbus-unknown-component', {
295
+ style: {
296
+ display: canInsertInlineComponent ? 'inline' : 'block',
297
+ color: '#f00'
298
+ }
299
+ }, unknownComponent.name);
300
+ }
301
+ };
302
+ }
303
+ });
304
+ return unknownComponent;
305
305
  }
306
306
 
307
- const collaborateErrorFn = makeError('Collaborate');
308
- class ContentMap {
309
- constructor() {
310
- this.slotAndYTextMap = new WeakMap();
311
- this.yTextAndSLotMap = new WeakMap();
312
- }
313
- set(key, value) {
314
- if (key instanceof Slot) {
315
- this.slotAndYTextMap.set(key, value);
316
- this.yTextAndSLotMap.set(value, key);
317
- }
318
- else {
319
- this.slotAndYTextMap.set(value, key);
320
- this.yTextAndSLotMap.set(key, value);
321
- }
322
- }
323
- get(key) {
324
- if (key instanceof Slot) {
325
- return this.slotAndYTextMap.get(key) || null;
326
- }
327
- return this.yTextAndSLotMap.get(key) || null;
328
- }
329
- delete(key) {
330
- if (key instanceof Slot) {
331
- const v = this.slotAndYTextMap.get(key);
332
- this.slotAndYTextMap.delete(key);
333
- if (v) {
334
- this.yTextAndSLotMap.delete(v);
335
- }
336
- }
337
- else {
338
- const v = this.yTextAndSLotMap.get(key);
339
- this.yTextAndSLotMap.delete(key);
340
- if (v) {
341
- this.slotAndYTextMap.delete(v);
342
- }
343
- }
344
- }
345
- }
346
- let Collaborate = class Collaborate {
347
- get canBack() {
348
- var _a;
349
- return ((_a = this.manager) === null || _a === void 0 ? void 0 : _a.canUndo()) || false;
350
- }
351
- get canForward() {
352
- var _a;
353
- return ((_a = this.manager) === null || _a === void 0 ? void 0 : _a.canRedo()) || false;
354
- }
355
- constructor(stackSize, rootComponentRef, collaborateCursor, controller, scheduler, translator, registry, selection, starter) {
356
- this.stackSize = stackSize;
357
- this.rootComponentRef = rootComponentRef;
358
- this.collaborateCursor = collaborateCursor;
359
- this.controller = controller;
360
- this.scheduler = scheduler;
361
- this.translator = translator;
362
- this.registry = registry;
363
- this.selection = selection;
364
- this.starter = starter;
365
- this.yDoc = new Doc();
366
- this.backEvent = new Subject();
367
- this.forwardEvent = new Subject();
368
- this.changeEvent = new Subject();
369
- this.pushEvent = new Subject();
370
- this.manager = null;
371
- this.subscriptions = [];
372
- this.updateFromRemote = false;
373
- this.contentSyncCaches = new WeakMap();
374
- this.slotStateSyncCaches = new WeakMap();
375
- this.slotsSyncCaches = new WeakMap();
376
- this.componentStateSyncCaches = new WeakMap();
377
- this.localChangesAppliedEvent = new Subject();
378
- this.selectionChangeEvent = new Subject();
379
- this.contentMap = new ContentMap();
380
- this.updateRemoteActions = [];
381
- this.noRecord = {};
382
- this.historyItems = [];
383
- this.index = 0;
384
- this.onSelectionChange = this.selectionChangeEvent.asObservable().pipe(delay());
385
- this.onBack = this.backEvent.asObservable();
386
- this.onForward = this.forwardEvent.asObservable();
387
- this.onChange = this.changeEvent.asObservable();
388
- this.onPush = this.pushEvent.asObservable();
389
- this.onLocalChangesApplied = this.localChangesAppliedEvent.asObservable();
390
- }
391
- listen() {
392
- const root = this.yDoc.getMap('RootComponent');
393
- const rootComponent = this.rootComponentRef.component;
394
- const manager = new UndoManager(root, {
395
- trackedOrigins: new Set([this.yDoc])
396
- });
397
- this.manager = manager;
398
- manager.on('stack-item-added', event => {
399
- if (event.type === 'undo') {
400
- if (event.origin === manager) {
401
- this.index++;
402
- }
403
- else {
404
- this.historyItems.length = this.index;
405
- this.historyItems.push(this.getRelativeCursorLocation());
406
- this.index++;
407
- }
408
- }
409
- else {
410
- this.index--;
411
- }
412
- if (manager.undoStack.length > this.stackSize) {
413
- this.historyItems.shift();
414
- manager.undoStack.shift();
415
- }
416
- if (event.origin === this.yDoc) {
417
- this.pushEvent.next();
418
- }
419
- this.changeEvent.next();
420
- });
421
- manager.on('stack-item-popped', () => {
422
- const position = this.historyItems[this.index - 1];
423
- if (position) {
424
- const selection = this.getAbstractSelection(position);
425
- if (selection) {
426
- this.selection.setBaseAndExtent(selection.anchorSlot, selection.anchorOffset, selection.focusSlot, selection.focusOffset);
427
- return;
428
- }
429
- }
430
- this.selection.unSelect();
431
- });
432
- this.subscriptions.push(this.selection.onChange.subscribe(() => {
433
- const paths = this.selection.getPaths();
434
- this.selectionChangeEvent.next(paths);
435
- }), this.scheduler.onDocChanged.pipe(map(item => {
436
- return item.filter(i => {
437
- return i.from !== ChangeOrigin.Remote;
438
- });
439
- }), filter(item => {
440
- return item.length;
441
- })).subscribe(() => {
442
- const updates = [];
443
- let update = null;
444
- for (const item of this.updateRemoteActions) {
445
- if (!update) {
446
- update = {
447
- record: item.record,
448
- actions: []
449
- };
450
- updates.push(update);
451
- }
452
- if (update.record === item.record) {
453
- update.actions.push(item.action);
454
- }
455
- else {
456
- update = {
457
- record: item.record,
458
- actions: [item.action]
459
- };
460
- updates.push(update);
461
- }
462
- }
463
- this.updateRemoteActions = [];
464
- for (const item of updates) {
465
- this.yDoc.transact(() => {
466
- item.actions.forEach(fn => {
467
- fn();
468
- });
469
- }, item.record ? this.yDoc : this.noRecord);
470
- }
471
- this.localChangesAppliedEvent.next();
472
- }));
473
- this.syncRootComponent(root, rootComponent);
474
- }
475
- updateRemoteSelection(paths) {
476
- this.collaborateCursor.draw(paths);
477
- }
478
- back() {
479
- var _a;
480
- if (this.canBack) {
481
- (_a = this.manager) === null || _a === void 0 ? void 0 : _a.undo();
482
- this.backEvent.next();
483
- }
484
- }
485
- forward() {
486
- var _a;
487
- if (this.canForward) {
488
- (_a = this.manager) === null || _a === void 0 ? void 0 : _a.redo();
489
- this.forwardEvent.next();
490
- }
491
- }
492
- clear() {
493
- var _a;
494
- this.index = 0;
495
- this.historyItems = [];
496
- (_a = this.manager) === null || _a === void 0 ? void 0 : _a.clear();
497
- this.changeEvent.next();
498
- }
499
- destroy() {
500
- var _a;
501
- this.index = 0;
502
- this.historyItems = [];
503
- this.subscriptions.forEach(i => i.unsubscribe());
504
- this.collaborateCursor.destroy();
505
- (_a = this.manager) === null || _a === void 0 ? void 0 : _a.destroy();
506
- }
507
- syncRootComponent(root, rootComponent) {
508
- let slots = root.get('slots');
509
- if (!slots) {
510
- slots = new Array();
511
- rootComponent.slots.toArray().forEach(i => {
512
- const sharedSlot = this.createSharedSlotBySlot(i);
513
- slots.push([sharedSlot]);
514
- });
515
- this.yDoc.transact(() => {
516
- root.set('state', rootComponent.state);
517
- root.set('slots', slots);
518
- });
519
- }
520
- else if (slots.length === 0) {
521
- rootComponent.updateState(() => {
522
- return root.get('state');
523
- });
524
- this.yDoc.transact(() => {
525
- rootComponent.slots.toArray().forEach(i => {
526
- const sharedSlot = this.createSharedSlotBySlot(i);
527
- slots.push([sharedSlot]);
528
- });
529
- });
530
- }
531
- else {
532
- rootComponent.updateState(() => {
533
- return root.get('state');
534
- });
535
- rootComponent.slots.clean();
536
- slots.forEach(sharedSlot => {
537
- const slot = this.createSlotBySharedSlot(sharedSlot);
538
- this.syncContent(sharedSlot.get('content'), slot);
539
- this.syncSlot(sharedSlot, slot);
540
- rootComponent.slots.insert(slot);
541
- });
542
- }
543
- this.syncComponent(root, rootComponent);
544
- this.syncSlots(slots, rootComponent);
545
- }
546
- getAbstractSelection(position) {
547
- const anchorPosition = createAbsolutePositionFromRelativePosition(position.anchor, this.yDoc);
548
- const focusPosition = createAbsolutePositionFromRelativePosition(position.focus, this.yDoc);
549
- if (anchorPosition && focusPosition) {
550
- const focusSlot = this.contentMap.get(focusPosition.type);
551
- const anchorSlot = this.contentMap.get(anchorPosition.type);
552
- if (focusSlot && anchorSlot) {
553
- return {
554
- anchorSlot,
555
- anchorOffset: anchorPosition.index,
556
- focusSlot,
557
- focusOffset: focusPosition.index
558
- };
559
- }
560
- }
561
- return null;
562
- }
563
- getRelativeCursorLocation() {
564
- const { anchorSlot, anchorOffset, focusSlot, focusOffset } = this.selection;
565
- if (anchorSlot) {
566
- const anchorYText = this.contentMap.get(anchorSlot);
567
- if (anchorYText) {
568
- const anchorPosition = createRelativePositionFromTypeIndex(anchorYText, anchorOffset);
569
- if (focusSlot) {
570
- const focusYText = this.contentMap.get(focusSlot);
571
- if (focusYText) {
572
- const focusPosition = createRelativePositionFromTypeIndex(focusYText, focusOffset);
573
- return {
574
- focus: focusPosition,
575
- anchor: anchorPosition
576
- };
577
- }
578
- }
579
- }
580
- }
581
- return null;
582
- }
583
- syncContent(content, slot) {
584
- this.contentMap.set(slot, content);
585
- const syncRemote = (ev, tr) => {
586
- this.runRemoteUpdate(tr, () => {
587
- slot.retain(0);
588
- ev.delta.forEach(action => {
589
- if (Reflect.has(action, 'retain')) {
590
- if (action.attributes) {
591
- const formats = remoteFormatsToLocal(this.registry, action.attributes);
592
- if (formats.length) {
593
- slot.retain(action.retain, formats);
594
- }
595
- slot.retain(slot.index + action.retain);
596
- }
597
- else {
598
- slot.retain(action.retain);
599
- }
600
- }
601
- else if (action.insert) {
602
- const index = slot.index;
603
- let length = 1;
604
- if (typeof action.insert === 'string') {
605
- length = action.insert.length;
606
- slot.insert(action.insert, remoteFormatsToLocal(this.registry, action.attributes));
607
- }
608
- else {
609
- const sharedComponent = action.insert;
610
- const canInsertInlineComponent = slot.schema.includes(ContentType.InlineComponent);
611
- const component = this.createComponentBySharedComponent(sharedComponent, canInsertInlineComponent);
612
- this.syncSlots(sharedComponent.get('slots'), component);
613
- this.syncComponent(sharedComponent, component);
614
- slot.insert(component);
615
- }
616
- if (this.selection.isSelected) {
617
- if (slot === this.selection.anchorSlot && this.selection.anchorOffset > index) {
618
- this.selection.setAnchor(slot, this.selection.anchorOffset + length);
619
- }
620
- if (slot === this.selection.focusSlot && this.selection.focusOffset > index) {
621
- this.selection.setFocus(slot, this.selection.focusOffset + length);
622
- }
623
- }
624
- }
625
- else if (action.delete) {
626
- const index = slot.index;
627
- slot.retain(slot.index);
628
- slot.delete(action.delete);
629
- if (this.selection.isSelected) {
630
- if (slot === this.selection.anchorSlot && this.selection.anchorOffset >= index) {
631
- this.selection.setAnchor(slot, this.selection.startOffset - action.delete);
632
- }
633
- if (slot === this.selection.focusSlot && this.selection.focusOffset >= index) {
634
- this.selection.setFocus(slot, this.selection.focusOffset - action.delete);
635
- }
636
- }
637
- }
638
- else if (action.attributes) {
639
- slot.updateState(draft => {
640
- if (typeof draft === 'object' && draft !== null) {
641
- Object.assign(draft, action.attributes);
642
- }
643
- else {
644
- return action.attributes;
645
- }
646
- });
647
- }
648
- });
649
- });
650
- };
651
- content.observe(syncRemote);
652
- const sub = slot.onContentChange.subscribe(actions => {
653
- this.runLocalUpdate(() => {
654
- var _a;
655
- let offset = 0;
656
- let length = 0;
657
- for (const action of actions) {
658
- if (action.type === 'retain') {
659
- const formats = action.formats;
660
- if (formats) {
661
- const keys = Object.keys(formats);
662
- let length = keys.length;
663
- keys.forEach(key => {
664
- const formatter = this.registry.getFormatter(key);
665
- if (!formatter) {
666
- length--;
667
- Reflect.deleteProperty(formats, key);
668
- }
669
- });
670
- if (length) {
671
- content.format(offset, action.offset, formats);
672
- }
673
- }
674
- else {
675
- offset = action.offset;
676
- }
677
- }
678
- else if (action.type === 'insert') {
679
- const delta = content.toDelta();
680
- const isEmpty = delta.length === 1 && delta[0].insert === Slot.emptyPlaceholder;
681
- if (typeof action.content === 'string') {
682
- length = action.content.length;
683
- content.insert(offset, action.content, action.formats || {});
684
- }
685
- else {
686
- length = 1;
687
- const sharedComponent = this.createSharedComponentByComponent(action.ref);
688
- content.insertEmbed(offset, sharedComponent, action.formats || {});
689
- }
690
- if (isEmpty && offset === 0) {
691
- content.delete(content.length - 1, 1);
692
- }
693
- offset += length;
694
- }
695
- else if (action.type === 'delete') {
696
- const delta = content.toDelta();
697
- if (content.length) {
698
- content.delete(offset, action.count);
699
- }
700
- if (content.length === 0) {
701
- content.insert(0, '\n', (_a = delta[0]) === null || _a === void 0 ? void 0 : _a.attributes);
702
- }
703
- }
704
- }
705
- });
706
- });
707
- sub.add(slot.onChildComponentRemove.subscribe(components => {
708
- components.forEach(c => {
709
- this.cleanSubscriptionsByComponent(c);
710
- });
711
- }));
712
- this.contentSyncCaches.set(slot, () => {
713
- content.unobserve(syncRemote);
714
- sub.unsubscribe();
715
- });
716
- }
717
- syncSlot(remoteSlot, slot) {
718
- const syncRemote = (ev, tr) => {
719
- this.runRemoteUpdate(tr, () => {
720
- ev.keysChanged.forEach(key => {
721
- if (key === 'state') {
722
- const state = ev.target.get('state');
723
- slot.updateState(draft => {
724
- if (typeof draft === 'object' && draft !== null) {
725
- Object.assign(draft, state);
726
- }
727
- else {
728
- return state;
729
- }
730
- });
731
- }
732
- });
733
- });
734
- };
735
- remoteSlot.observe(syncRemote);
736
- const sub = slot.onStateChange.subscribe(change => {
737
- this.runLocalUpdate(() => {
738
- remoteSlot.set('state', change.newState);
739
- }, change.record);
740
- });
741
- this.slotStateSyncCaches.set(slot, () => {
742
- remoteSlot.unobserve(syncRemote);
743
- sub.unsubscribe();
744
- });
745
- }
746
- syncSlots(remoteSlots, component) {
747
- const slots = component.slots;
748
- const syncRemote = (ev, tr) => {
749
- this.runRemoteUpdate(tr, () => {
750
- let index = 0;
751
- slots.retain(index);
752
- ev.delta.forEach(action => {
753
- if (Reflect.has(action, 'retain')) {
754
- index += action.retain;
755
- slots.retain(index);
756
- }
757
- else if (action.insert) {
758
- action.insert.forEach(item => {
759
- const slot = this.createSlotBySharedSlot(item);
760
- slots.insert(slot);
761
- this.syncContent(item.get('content'), slot);
762
- this.syncSlot(item, slot);
763
- index++;
764
- });
765
- }
766
- else if (action.delete) {
767
- slots.retain(index);
768
- slots.delete(action.delete);
769
- }
770
- });
771
- });
772
- };
773
- remoteSlots.observe(syncRemote);
774
- const sub = slots.onChange.subscribe(operations => {
775
- this.runLocalUpdate(() => {
776
- const applyActions = operations.apply;
777
- let index;
778
- applyActions.forEach(action => {
779
- if (action.type === 'retain') {
780
- index = action.offset;
781
- }
782
- else if (action.type === 'insertSlot') {
783
- const sharedSlot = this.createSharedSlotBySlot(action.ref);
784
- remoteSlots.insert(index, [sharedSlot]);
785
- index++;
786
- }
787
- else if (action.type === 'delete') {
788
- remoteSlots.delete(index, action.count);
789
- }
790
- });
791
- });
792
- });
793
- sub.add(slots.onChildSlotRemove.subscribe(slots => {
794
- slots.forEach(slot => {
795
- this.cleanSubscriptionsBySlot(slot);
796
- });
797
- }));
798
- this.slotsSyncCaches.set(component, () => {
799
- remoteSlots.unobserve(syncRemote);
800
- sub.unsubscribe();
801
- });
802
- }
803
- syncComponent(remoteComponent, component) {
804
- const syncRemote = (ev, tr) => {
805
- this.runRemoteUpdate(tr, () => {
806
- ev.keysChanged.forEach(key => {
807
- if (key === 'state') {
808
- const state = ev.target.get('state');
809
- component.updateState(draft => {
810
- if (typeof draft === 'object' && draft !== null) {
811
- Object.assign(draft, state);
812
- }
813
- else {
814
- return state;
815
- }
816
- });
817
- }
818
- });
819
- });
820
- };
821
- remoteComponent.observe(syncRemote);
822
- const sub = component.onStateChange.subscribe(change => {
823
- this.runLocalUpdate(() => {
824
- remoteComponent.set('state', change.newState);
825
- }, change.record);
826
- });
827
- this.componentStateSyncCaches.set(component, () => {
828
- remoteComponent.unobserve(syncRemote);
829
- sub.unsubscribe();
830
- });
831
- }
832
- runLocalUpdate(fn, record = true) {
833
- if (this.updateFromRemote || this.controller.readonly) {
834
- return;
835
- }
836
- this.updateRemoteActions.push({
837
- record,
838
- action: fn
839
- });
840
- }
841
- runRemoteUpdate(tr, fn) {
842
- if (tr.origin === this.yDoc) {
843
- return;
844
- }
845
- this.updateFromRemote = true;
846
- if (tr.origin === this.manager) {
847
- this.scheduler.historyApplyTransact(fn);
848
- }
849
- else {
850
- this.scheduler.remoteUpdateTransact(fn);
851
- }
852
- this.updateFromRemote = false;
853
- }
854
- createSharedComponentByComponent(component) {
855
- const sharedComponent = new Map();
856
- sharedComponent.set('state', component.state);
857
- sharedComponent.set('name', component.name);
858
- const sharedSlots = new Array();
859
- sharedComponent.set('slots', sharedSlots);
860
- component.slots.toArray().forEach(slot => {
861
- const sharedSlot = this.createSharedSlotBySlot(slot);
862
- sharedSlots.push([sharedSlot]);
863
- });
864
- this.syncSlots(sharedSlots, component);
865
- this.syncComponent(sharedComponent, component);
866
- return sharedComponent;
867
- }
868
- createSharedSlotBySlot(slot) {
869
- const sharedSlot = new Map();
870
- sharedSlot.set('schema', slot.schema);
871
- sharedSlot.set('state', slot.state);
872
- const sharedContent = new Text();
873
- sharedSlot.set('content', sharedContent);
874
- let offset = 0;
875
- slot.toDelta().forEach(i => {
876
- let formats = {};
877
- if (i.formats) {
878
- i.formats.forEach(item => {
879
- formats[item[0].name] = item[1];
880
- });
881
- }
882
- else {
883
- formats = null;
884
- }
885
- if (typeof i.insert === 'string') {
886
- sharedContent.insert(offset, i.insert, formats);
887
- }
888
- else {
889
- const sharedComponent = this.createSharedComponentByComponent(i.insert);
890
- sharedContent.insertEmbed(offset, sharedComponent, formats);
891
- }
892
- offset += i.insert.length;
893
- });
894
- this.syncContent(sharedContent, slot);
895
- this.syncSlot(sharedSlot, slot);
896
- return sharedSlot;
897
- }
898
- createComponentBySharedComponent(yMap, canInsertInlineComponent) {
899
- const sharedSlots = yMap.get('slots');
900
- const slots = [];
901
- sharedSlots.forEach(sharedSlot => {
902
- const slot = this.createSlotBySharedSlot(sharedSlot);
903
- slots.push(slot);
904
- });
905
- const name = yMap.get('name');
906
- const state = yMap.get('state');
907
- const instance = this.translator.createComponentByData(name, {
908
- state,
909
- slots
910
- });
911
- if (instance) {
912
- instance.slots.toArray().forEach((slot, index) => {
913
- let sharedSlot = sharedSlots.get(index);
914
- if (!sharedSlot) {
915
- sharedSlot = this.createSharedSlotBySlot(slot);
916
- sharedSlots.push([sharedSlot]);
917
- }
918
- this.syncSlot(sharedSlot, slot);
919
- this.syncContent(sharedSlot.get('content'), slot);
920
- });
921
- return instance;
922
- }
923
- return createUnknownComponent(name, canInsertInlineComponent).createInstance(this.starter);
924
- }
925
- createSlotBySharedSlot(sharedSlot) {
926
- const content = sharedSlot.get('content');
927
- const delta = content.toDelta();
928
- const slot = this.translator.createSlot({
929
- schema: sharedSlot.get('schema'),
930
- state: sharedSlot.get('state'),
931
- formats: {},
932
- content: []
933
- });
934
- for (const action of delta) {
935
- if (action.insert) {
936
- if (typeof action.insert === 'string') {
937
- const blockFormats = [];
938
- const formats = remoteFormatsToLocal(this.registry, action.attributes).filter(item => {
939
- if (item[0].type === FormatType.Block) {
940
- blockFormats.push(item);
941
- return false;
942
- }
943
- return true;
944
- });
945
- slot.insert(action.insert, formats);
946
- const index = slot.index;
947
- blockFormats.forEach(item => {
948
- slot.setAttribute(item[0], item[1]);
949
- });
950
- slot.retain(index);
951
- }
952
- else {
953
- const sharedComponent = action.insert;
954
- const canInsertInlineComponent = slot.schema.includes(ContentType.InlineComponent);
955
- const component = this.createComponentBySharedComponent(sharedComponent, canInsertInlineComponent);
956
- slot.insert(component, remoteFormatsToLocal(this.registry, action.attributes));
957
- this.syncSlots(sharedComponent.get('slots'), component);
958
- this.syncComponent(sharedComponent, component);
959
- }
960
- }
961
- else {
962
- throw collaborateErrorFn('unexpected delta action.');
963
- }
964
- }
965
- return slot;
966
- }
967
- cleanSubscriptionsBySlot(slot) {
968
- this.contentMap.delete(slot);
969
- [this.contentSyncCaches.get(slot), this.slotStateSyncCaches.get(slot)].forEach(fn => {
970
- if (fn) {
971
- fn();
972
- }
973
- });
974
- slot.sliceContent().forEach(i => {
975
- if (typeof i !== 'string') {
976
- this.cleanSubscriptionsByComponent(i);
977
- }
978
- });
979
- }
980
- cleanSubscriptionsByComponent(component) {
981
- [this.slotsSyncCaches.get(component), this.componentStateSyncCaches.get(component)].forEach(fn => {
982
- if (fn) {
983
- fn();
984
- }
985
- });
986
- component.slots.toArray().forEach(slot => {
987
- this.cleanSubscriptionsBySlot(slot);
988
- });
989
- }
990
- };
991
- Collaborate = __decorate([
992
- Injectable(),
993
- __param(0, Inject(HISTORY_STACK_SIZE)),
994
- __metadata("design:paramtypes", [Number, RootComponentRef,
995
- CollaborateCursor,
996
- Controller,
997
- Scheduler,
998
- Translator,
999
- Registry,
1000
- Selection,
1001
- Starter])
1002
- ], Collaborate);
1003
- function remoteFormatsToLocal(registry, attrs) {
1004
- const formats = [];
1005
- if (attrs) {
1006
- Object.keys(attrs).forEach(key => {
1007
- const formatter = registry.getFormatter(key);
1008
- if (formatter) {
1009
- formats.push([formatter, attrs[key]]);
1010
- }
1011
- });
1012
- }
1013
- return formats;
307
+ const collaborateErrorFn = makeError('Collaborate');
308
+ class ContentMap {
309
+ constructor() {
310
+ this.slotAndYTextMap = new WeakMap();
311
+ this.yTextAndSLotMap = new WeakMap();
312
+ }
313
+ set(key, value) {
314
+ if (key instanceof Slot) {
315
+ this.slotAndYTextMap.set(key, value);
316
+ this.yTextAndSLotMap.set(value, key);
317
+ }
318
+ else {
319
+ this.slotAndYTextMap.set(value, key);
320
+ this.yTextAndSLotMap.set(key, value);
321
+ }
322
+ }
323
+ get(key) {
324
+ if (key instanceof Slot) {
325
+ return this.slotAndYTextMap.get(key) || null;
326
+ }
327
+ return this.yTextAndSLotMap.get(key) || null;
328
+ }
329
+ delete(key) {
330
+ if (key instanceof Slot) {
331
+ const v = this.slotAndYTextMap.get(key);
332
+ this.slotAndYTextMap.delete(key);
333
+ if (v) {
334
+ this.yTextAndSLotMap.delete(v);
335
+ }
336
+ }
337
+ else {
338
+ const v = this.yTextAndSLotMap.get(key);
339
+ this.yTextAndSLotMap.delete(key);
340
+ if (v) {
341
+ this.slotAndYTextMap.delete(v);
342
+ }
343
+ }
344
+ }
345
+ }
346
+ let Collaborate = class Collaborate {
347
+ get canBack() {
348
+ var _a;
349
+ return ((_a = this.manager) === null || _a === void 0 ? void 0 : _a.canUndo()) || false;
350
+ }
351
+ get canForward() {
352
+ var _a;
353
+ return ((_a = this.manager) === null || _a === void 0 ? void 0 : _a.canRedo()) || false;
354
+ }
355
+ constructor(stackSize, rootComponentRef, collaborateCursor, controller, scheduler, translator, registry, selection, starter) {
356
+ this.stackSize = stackSize;
357
+ this.rootComponentRef = rootComponentRef;
358
+ this.collaborateCursor = collaborateCursor;
359
+ this.controller = controller;
360
+ this.scheduler = scheduler;
361
+ this.translator = translator;
362
+ this.registry = registry;
363
+ this.selection = selection;
364
+ this.starter = starter;
365
+ this.yDoc = new Doc();
366
+ this.backEvent = new Subject();
367
+ this.forwardEvent = new Subject();
368
+ this.changeEvent = new Subject();
369
+ this.pushEvent = new Subject();
370
+ this.manager = null;
371
+ this.subscriptions = [];
372
+ this.updateFromRemote = false;
373
+ this.contentSyncCaches = new WeakMap();
374
+ this.slotStateSyncCaches = new WeakMap();
375
+ this.slotsSyncCaches = new WeakMap();
376
+ this.componentStateSyncCaches = new WeakMap();
377
+ this.localChangesAppliedEvent = new Subject();
378
+ this.selectionChangeEvent = new Subject();
379
+ this.contentMap = new ContentMap();
380
+ this.updateRemoteActions = [];
381
+ this.noRecord = {};
382
+ this.historyItems = [];
383
+ this.index = 0;
384
+ this.onSelectionChange = this.selectionChangeEvent.asObservable().pipe(delay());
385
+ this.onBack = this.backEvent.asObservable();
386
+ this.onForward = this.forwardEvent.asObservable();
387
+ this.onChange = this.changeEvent.asObservable();
388
+ this.onPush = this.pushEvent.asObservable();
389
+ this.onLocalChangesApplied = this.localChangesAppliedEvent.asObservable();
390
+ }
391
+ listen() {
392
+ const root = this.yDoc.getMap('RootComponent');
393
+ const rootComponent = this.rootComponentRef.component;
394
+ const manager = new UndoManager(root, {
395
+ trackedOrigins: new Set([this.yDoc])
396
+ });
397
+ this.manager = manager;
398
+ manager.on('stack-item-added', event => {
399
+ if (event.type === 'undo') {
400
+ if (event.origin === manager) {
401
+ this.index++;
402
+ }
403
+ else {
404
+ this.historyItems.length = this.index;
405
+ this.historyItems.push(this.getRelativeCursorLocation());
406
+ this.index++;
407
+ }
408
+ }
409
+ else {
410
+ this.index--;
411
+ }
412
+ if (manager.undoStack.length > this.stackSize) {
413
+ this.historyItems.shift();
414
+ manager.undoStack.shift();
415
+ }
416
+ if (event.origin === this.yDoc) {
417
+ this.pushEvent.next();
418
+ }
419
+ this.changeEvent.next();
420
+ });
421
+ manager.on('stack-item-popped', () => {
422
+ const position = this.historyItems[this.index - 1];
423
+ if (position) {
424
+ const selection = this.getAbstractSelection(position);
425
+ if (selection) {
426
+ this.selection.setBaseAndExtent(selection.anchorSlot, selection.anchorOffset, selection.focusSlot, selection.focusOffset);
427
+ return;
428
+ }
429
+ }
430
+ this.selection.unSelect();
431
+ });
432
+ this.subscriptions.push(this.selection.onChange.subscribe(() => {
433
+ const paths = this.selection.getPaths();
434
+ this.selectionChangeEvent.next(paths);
435
+ }), this.scheduler.onDocChanged.pipe(map(item => {
436
+ return item.filter(i => {
437
+ return i.from !== ChangeOrigin.Remote;
438
+ });
439
+ }), filter(item => {
440
+ return item.length;
441
+ })).subscribe(() => {
442
+ const updates = [];
443
+ let update = null;
444
+ for (const item of this.updateRemoteActions) {
445
+ if (!update) {
446
+ update = {
447
+ record: item.record,
448
+ actions: []
449
+ };
450
+ updates.push(update);
451
+ }
452
+ if (update.record === item.record) {
453
+ update.actions.push(item.action);
454
+ }
455
+ else {
456
+ update = {
457
+ record: item.record,
458
+ actions: [item.action]
459
+ };
460
+ updates.push(update);
461
+ }
462
+ }
463
+ this.updateRemoteActions = [];
464
+ for (const item of updates) {
465
+ this.yDoc.transact(() => {
466
+ item.actions.forEach(fn => {
467
+ fn();
468
+ });
469
+ }, item.record ? this.yDoc : this.noRecord);
470
+ }
471
+ this.localChangesAppliedEvent.next();
472
+ }));
473
+ this.syncRootComponent(root, rootComponent);
474
+ }
475
+ updateRemoteSelection(paths) {
476
+ this.collaborateCursor.draw(paths);
477
+ }
478
+ back() {
479
+ var _a;
480
+ if (this.canBack) {
481
+ (_a = this.manager) === null || _a === void 0 ? void 0 : _a.undo();
482
+ this.backEvent.next();
483
+ }
484
+ }
485
+ forward() {
486
+ var _a;
487
+ if (this.canForward) {
488
+ (_a = this.manager) === null || _a === void 0 ? void 0 : _a.redo();
489
+ this.forwardEvent.next();
490
+ }
491
+ }
492
+ clear() {
493
+ var _a;
494
+ this.index = 0;
495
+ this.historyItems = [];
496
+ (_a = this.manager) === null || _a === void 0 ? void 0 : _a.clear();
497
+ this.changeEvent.next();
498
+ }
499
+ destroy() {
500
+ var _a;
501
+ this.index = 0;
502
+ this.historyItems = [];
503
+ this.subscriptions.forEach(i => i.unsubscribe());
504
+ this.collaborateCursor.destroy();
505
+ (_a = this.manager) === null || _a === void 0 ? void 0 : _a.destroy();
506
+ }
507
+ syncRootComponent(root, rootComponent) {
508
+ let slots = root.get('slots');
509
+ if (!slots) {
510
+ slots = new Array();
511
+ rootComponent.slots.toArray().forEach(i => {
512
+ const sharedSlot = this.createSharedSlotBySlot(i);
513
+ slots.push([sharedSlot]);
514
+ });
515
+ this.yDoc.transact(() => {
516
+ root.set('state', rootComponent.state);
517
+ root.set('slots', slots);
518
+ });
519
+ }
520
+ else if (slots.length === 0) {
521
+ rootComponent.updateState(() => {
522
+ return root.get('state');
523
+ });
524
+ this.yDoc.transact(() => {
525
+ rootComponent.slots.toArray().forEach(i => {
526
+ const sharedSlot = this.createSharedSlotBySlot(i);
527
+ slots.push([sharedSlot]);
528
+ });
529
+ });
530
+ }
531
+ else {
532
+ rootComponent.updateState(() => {
533
+ return root.get('state');
534
+ });
535
+ rootComponent.slots.clean();
536
+ slots.forEach(sharedSlot => {
537
+ const slot = this.createSlotBySharedSlot(sharedSlot);
538
+ this.syncContent(sharedSlot.get('content'), slot);
539
+ this.syncSlot(sharedSlot, slot);
540
+ rootComponent.slots.insert(slot);
541
+ });
542
+ }
543
+ this.syncComponent(root, rootComponent);
544
+ this.syncSlots(slots, rootComponent);
545
+ }
546
+ getAbstractSelection(position) {
547
+ const anchorPosition = createAbsolutePositionFromRelativePosition(position.anchor, this.yDoc);
548
+ const focusPosition = createAbsolutePositionFromRelativePosition(position.focus, this.yDoc);
549
+ if (anchorPosition && focusPosition) {
550
+ const focusSlot = this.contentMap.get(focusPosition.type);
551
+ const anchorSlot = this.contentMap.get(anchorPosition.type);
552
+ if (focusSlot && anchorSlot) {
553
+ return {
554
+ anchorSlot,
555
+ anchorOffset: anchorPosition.index,
556
+ focusSlot,
557
+ focusOffset: focusPosition.index
558
+ };
559
+ }
560
+ }
561
+ return null;
562
+ }
563
+ getRelativeCursorLocation() {
564
+ const { anchorSlot, anchorOffset, focusSlot, focusOffset } = this.selection;
565
+ if (anchorSlot) {
566
+ const anchorYText = this.contentMap.get(anchorSlot);
567
+ if (anchorYText) {
568
+ const anchorPosition = createRelativePositionFromTypeIndex(anchorYText, anchorOffset);
569
+ if (focusSlot) {
570
+ const focusYText = this.contentMap.get(focusSlot);
571
+ if (focusYText) {
572
+ const focusPosition = createRelativePositionFromTypeIndex(focusYText, focusOffset);
573
+ return {
574
+ focus: focusPosition,
575
+ anchor: anchorPosition
576
+ };
577
+ }
578
+ }
579
+ }
580
+ }
581
+ return null;
582
+ }
583
+ syncContent(content, slot) {
584
+ this.contentMap.set(slot, content);
585
+ const syncRemote = (ev, tr) => {
586
+ this.runRemoteUpdate(tr, () => {
587
+ slot.retain(0);
588
+ ev.delta.forEach(action => {
589
+ if (Reflect.has(action, 'retain')) {
590
+ if (action.attributes) {
591
+ const formats = remoteFormatsToLocal(this.registry, action.attributes);
592
+ if (formats.length) {
593
+ slot.retain(action.retain, formats);
594
+ }
595
+ slot.retain(slot.index + action.retain);
596
+ }
597
+ else {
598
+ slot.retain(action.retain);
599
+ }
600
+ }
601
+ else if (action.insert) {
602
+ const index = slot.index;
603
+ let length = 1;
604
+ if (typeof action.insert === 'string') {
605
+ length = action.insert.length;
606
+ slot.insert(action.insert, remoteFormatsToLocal(this.registry, action.attributes));
607
+ }
608
+ else {
609
+ const sharedComponent = action.insert;
610
+ const canInsertInlineComponent = slot.schema.includes(ContentType.InlineComponent);
611
+ const component = this.createComponentBySharedComponent(sharedComponent, canInsertInlineComponent);
612
+ this.syncSlots(sharedComponent.get('slots'), component);
613
+ this.syncComponent(sharedComponent, component);
614
+ slot.insert(component);
615
+ }
616
+ if (this.selection.isSelected) {
617
+ if (slot === this.selection.anchorSlot && this.selection.anchorOffset > index) {
618
+ this.selection.setAnchor(slot, this.selection.anchorOffset + length);
619
+ }
620
+ if (slot === this.selection.focusSlot && this.selection.focusOffset > index) {
621
+ this.selection.setFocus(slot, this.selection.focusOffset + length);
622
+ }
623
+ }
624
+ }
625
+ else if (action.delete) {
626
+ const index = slot.index;
627
+ slot.retain(slot.index);
628
+ slot.delete(action.delete);
629
+ if (this.selection.isSelected) {
630
+ if (slot === this.selection.anchorSlot && this.selection.anchorOffset >= index) {
631
+ this.selection.setAnchor(slot, this.selection.startOffset - action.delete);
632
+ }
633
+ if (slot === this.selection.focusSlot && this.selection.focusOffset >= index) {
634
+ this.selection.setFocus(slot, this.selection.focusOffset - action.delete);
635
+ }
636
+ }
637
+ }
638
+ else if (action.attributes) {
639
+ slot.updateState(draft => {
640
+ if (typeof draft === 'object' && draft !== null) {
641
+ Object.assign(draft, action.attributes);
642
+ }
643
+ else {
644
+ return action.attributes;
645
+ }
646
+ });
647
+ }
648
+ });
649
+ });
650
+ };
651
+ content.observe(syncRemote);
652
+ const sub = slot.onContentChange.subscribe(actions => {
653
+ this.runLocalUpdate(() => {
654
+ var _a;
655
+ let offset = 0;
656
+ let length = 0;
657
+ for (const action of actions) {
658
+ if (action.type === 'retain') {
659
+ const formats = action.formats;
660
+ if (formats) {
661
+ const keys = Object.keys(formats);
662
+ let length = keys.length;
663
+ keys.forEach(key => {
664
+ const formatter = this.registry.getFormatter(key);
665
+ if (!formatter) {
666
+ length--;
667
+ Reflect.deleteProperty(formats, key);
668
+ }
669
+ });
670
+ if (length) {
671
+ content.format(offset, action.offset, formats);
672
+ }
673
+ }
674
+ else {
675
+ offset = action.offset;
676
+ }
677
+ }
678
+ else if (action.type === 'insert') {
679
+ const delta = content.toDelta();
680
+ const isEmpty = delta.length === 1 && delta[0].insert === Slot.emptyPlaceholder;
681
+ if (typeof action.content === 'string') {
682
+ length = action.content.length;
683
+ content.insert(offset, action.content, action.formats || {});
684
+ }
685
+ else {
686
+ length = 1;
687
+ const sharedComponent = this.createSharedComponentByComponent(action.ref);
688
+ content.insertEmbed(offset, sharedComponent, action.formats || {});
689
+ }
690
+ if (isEmpty && offset === 0) {
691
+ content.delete(content.length - 1, 1);
692
+ }
693
+ offset += length;
694
+ }
695
+ else if (action.type === 'delete') {
696
+ const delta = content.toDelta();
697
+ if (content.length) {
698
+ content.delete(offset, action.count);
699
+ }
700
+ if (content.length === 0) {
701
+ content.insert(0, '\n', (_a = delta[0]) === null || _a === void 0 ? void 0 : _a.attributes);
702
+ }
703
+ }
704
+ }
705
+ });
706
+ });
707
+ sub.add(slot.onChildComponentRemove.subscribe(components => {
708
+ components.forEach(c => {
709
+ this.cleanSubscriptionsByComponent(c);
710
+ });
711
+ }));
712
+ this.contentSyncCaches.set(slot, () => {
713
+ content.unobserve(syncRemote);
714
+ sub.unsubscribe();
715
+ });
716
+ }
717
+ syncSlot(remoteSlot, slot) {
718
+ const syncRemote = (ev, tr) => {
719
+ this.runRemoteUpdate(tr, () => {
720
+ ev.keysChanged.forEach(key => {
721
+ if (key === 'state') {
722
+ const state = ev.target.get('state');
723
+ slot.updateState(draft => {
724
+ if (typeof draft === 'object' && draft !== null) {
725
+ Object.assign(draft, state);
726
+ }
727
+ else {
728
+ return state;
729
+ }
730
+ });
731
+ }
732
+ });
733
+ });
734
+ };
735
+ remoteSlot.observe(syncRemote);
736
+ const sub = slot.onStateChange.subscribe(change => {
737
+ this.runLocalUpdate(() => {
738
+ remoteSlot.set('state', change.newState);
739
+ }, change.record);
740
+ });
741
+ this.slotStateSyncCaches.set(slot, () => {
742
+ remoteSlot.unobserve(syncRemote);
743
+ sub.unsubscribe();
744
+ });
745
+ }
746
+ syncSlots(remoteSlots, component) {
747
+ const slots = component.slots;
748
+ const syncRemote = (ev, tr) => {
749
+ this.runRemoteUpdate(tr, () => {
750
+ let index = 0;
751
+ slots.retain(index);
752
+ ev.delta.forEach(action => {
753
+ if (Reflect.has(action, 'retain')) {
754
+ index += action.retain;
755
+ slots.retain(index);
756
+ }
757
+ else if (action.insert) {
758
+ action.insert.forEach(item => {
759
+ const slot = this.createSlotBySharedSlot(item);
760
+ slots.insert(slot);
761
+ this.syncContent(item.get('content'), slot);
762
+ this.syncSlot(item, slot);
763
+ index++;
764
+ });
765
+ }
766
+ else if (action.delete) {
767
+ slots.retain(index);
768
+ slots.delete(action.delete);
769
+ }
770
+ });
771
+ });
772
+ };
773
+ remoteSlots.observe(syncRemote);
774
+ const sub = slots.onChange.subscribe(operations => {
775
+ this.runLocalUpdate(() => {
776
+ const applyActions = operations.apply;
777
+ let index;
778
+ applyActions.forEach(action => {
779
+ if (action.type === 'retain') {
780
+ index = action.offset;
781
+ }
782
+ else if (action.type === 'insertSlot') {
783
+ const sharedSlot = this.createSharedSlotBySlot(action.ref);
784
+ remoteSlots.insert(index, [sharedSlot]);
785
+ index++;
786
+ }
787
+ else if (action.type === 'delete') {
788
+ remoteSlots.delete(index, action.count);
789
+ }
790
+ });
791
+ });
792
+ });
793
+ sub.add(slots.onChildSlotRemove.subscribe(slots => {
794
+ slots.forEach(slot => {
795
+ this.cleanSubscriptionsBySlot(slot);
796
+ });
797
+ }));
798
+ this.slotsSyncCaches.set(component, () => {
799
+ remoteSlots.unobserve(syncRemote);
800
+ sub.unsubscribe();
801
+ });
802
+ }
803
+ syncComponent(remoteComponent, component) {
804
+ const syncRemote = (ev, tr) => {
805
+ this.runRemoteUpdate(tr, () => {
806
+ ev.keysChanged.forEach(key => {
807
+ if (key === 'state') {
808
+ const state = ev.target.get('state');
809
+ component.updateState(draft => {
810
+ if (typeof draft === 'object' && draft !== null) {
811
+ Object.assign(draft, state);
812
+ }
813
+ else {
814
+ return state;
815
+ }
816
+ });
817
+ }
818
+ });
819
+ });
820
+ };
821
+ remoteComponent.observe(syncRemote);
822
+ const sub = component.onStateChange.subscribe(change => {
823
+ this.runLocalUpdate(() => {
824
+ remoteComponent.set('state', change.newState);
825
+ }, change.record);
826
+ });
827
+ this.componentStateSyncCaches.set(component, () => {
828
+ remoteComponent.unobserve(syncRemote);
829
+ sub.unsubscribe();
830
+ });
831
+ }
832
+ runLocalUpdate(fn, record = true) {
833
+ if (this.updateFromRemote || this.controller.readonly) {
834
+ return;
835
+ }
836
+ this.updateRemoteActions.push({
837
+ record,
838
+ action: fn
839
+ });
840
+ }
841
+ runRemoteUpdate(tr, fn) {
842
+ if (tr.origin === this.yDoc) {
843
+ return;
844
+ }
845
+ this.updateFromRemote = true;
846
+ if (tr.origin === this.manager) {
847
+ this.scheduler.historyApplyTransact(fn);
848
+ }
849
+ else {
850
+ this.scheduler.remoteUpdateTransact(fn);
851
+ }
852
+ this.updateFromRemote = false;
853
+ }
854
+ createSharedComponentByComponent(component) {
855
+ const sharedComponent = new Map();
856
+ sharedComponent.set('state', component.state);
857
+ sharedComponent.set('name', component.name);
858
+ const sharedSlots = new Array();
859
+ sharedComponent.set('slots', sharedSlots);
860
+ component.slots.toArray().forEach(slot => {
861
+ const sharedSlot = this.createSharedSlotBySlot(slot);
862
+ sharedSlots.push([sharedSlot]);
863
+ });
864
+ this.syncSlots(sharedSlots, component);
865
+ this.syncComponent(sharedComponent, component);
866
+ return sharedComponent;
867
+ }
868
+ createSharedSlotBySlot(slot) {
869
+ const sharedSlot = new Map();
870
+ sharedSlot.set('schema', slot.schema);
871
+ sharedSlot.set('state', slot.state);
872
+ const sharedContent = new Text();
873
+ sharedSlot.set('content', sharedContent);
874
+ let offset = 0;
875
+ slot.toDelta().forEach(i => {
876
+ let formats = {};
877
+ if (i.formats) {
878
+ i.formats.forEach(item => {
879
+ formats[item[0].name] = item[1];
880
+ });
881
+ }
882
+ else {
883
+ formats = null;
884
+ }
885
+ if (typeof i.insert === 'string') {
886
+ sharedContent.insert(offset, i.insert, formats);
887
+ }
888
+ else {
889
+ const sharedComponent = this.createSharedComponentByComponent(i.insert);
890
+ sharedContent.insertEmbed(offset, sharedComponent, formats);
891
+ }
892
+ offset += i.insert.length;
893
+ });
894
+ this.syncContent(sharedContent, slot);
895
+ this.syncSlot(sharedSlot, slot);
896
+ return sharedSlot;
897
+ }
898
+ createComponentBySharedComponent(yMap, canInsertInlineComponent) {
899
+ const sharedSlots = yMap.get('slots');
900
+ const slots = [];
901
+ sharedSlots.forEach(sharedSlot => {
902
+ const slot = this.createSlotBySharedSlot(sharedSlot);
903
+ slots.push(slot);
904
+ });
905
+ const name = yMap.get('name');
906
+ const state = yMap.get('state');
907
+ const instance = this.translator.createComponentByData(name, {
908
+ state,
909
+ slots
910
+ });
911
+ if (instance) {
912
+ instance.slots.toArray().forEach((slot, index) => {
913
+ let sharedSlot = sharedSlots.get(index);
914
+ if (!sharedSlot) {
915
+ sharedSlot = this.createSharedSlotBySlot(slot);
916
+ sharedSlots.push([sharedSlot]);
917
+ }
918
+ this.syncSlot(sharedSlot, slot);
919
+ this.syncContent(sharedSlot.get('content'), slot);
920
+ });
921
+ return instance;
922
+ }
923
+ return createUnknownComponent(name, canInsertInlineComponent).createInstance(this.starter);
924
+ }
925
+ createSlotBySharedSlot(sharedSlot) {
926
+ const content = sharedSlot.get('content');
927
+ const delta = content.toDelta();
928
+ const slot = this.translator.createSlot({
929
+ schema: sharedSlot.get('schema'),
930
+ state: sharedSlot.get('state'),
931
+ formats: {},
932
+ content: []
933
+ });
934
+ for (const action of delta) {
935
+ if (action.insert) {
936
+ if (typeof action.insert === 'string') {
937
+ const blockFormats = [];
938
+ const formats = remoteFormatsToLocal(this.registry, action.attributes).filter(item => {
939
+ if (item[0].type === FormatType.Block) {
940
+ blockFormats.push(item);
941
+ return false;
942
+ }
943
+ return true;
944
+ });
945
+ slot.insert(action.insert, formats);
946
+ const index = slot.index;
947
+ blockFormats.forEach(item => {
948
+ slot.setAttribute(item[0], item[1]);
949
+ });
950
+ slot.retain(index);
951
+ }
952
+ else {
953
+ const sharedComponent = action.insert;
954
+ const canInsertInlineComponent = slot.schema.includes(ContentType.InlineComponent);
955
+ const component = this.createComponentBySharedComponent(sharedComponent, canInsertInlineComponent);
956
+ slot.insert(component, remoteFormatsToLocal(this.registry, action.attributes));
957
+ this.syncSlots(sharedComponent.get('slots'), component);
958
+ this.syncComponent(sharedComponent, component);
959
+ }
960
+ }
961
+ else {
962
+ throw collaborateErrorFn('unexpected delta action.');
963
+ }
964
+ }
965
+ return slot;
966
+ }
967
+ cleanSubscriptionsBySlot(slot) {
968
+ this.contentMap.delete(slot);
969
+ [this.contentSyncCaches.get(slot), this.slotStateSyncCaches.get(slot)].forEach(fn => {
970
+ if (fn) {
971
+ fn();
972
+ }
973
+ });
974
+ slot.sliceContent().forEach(i => {
975
+ if (typeof i !== 'string') {
976
+ this.cleanSubscriptionsByComponent(i);
977
+ }
978
+ });
979
+ }
980
+ cleanSubscriptionsByComponent(component) {
981
+ [this.slotsSyncCaches.get(component), this.componentStateSyncCaches.get(component)].forEach(fn => {
982
+ if (fn) {
983
+ fn();
984
+ }
985
+ });
986
+ component.slots.toArray().forEach(slot => {
987
+ this.cleanSubscriptionsBySlot(slot);
988
+ });
989
+ }
990
+ };
991
+ Collaborate = __decorate([
992
+ Injectable(),
993
+ __param(0, Inject(HISTORY_STACK_SIZE)),
994
+ __metadata("design:paramtypes", [Number, RootComponentRef,
995
+ CollaborateCursor,
996
+ Controller,
997
+ Scheduler,
998
+ Translator,
999
+ Registry,
1000
+ Selection,
1001
+ Starter])
1002
+ ], Collaborate);
1003
+ function remoteFormatsToLocal(registry, attrs) {
1004
+ const formats = [];
1005
+ if (attrs) {
1006
+ Object.keys(attrs).forEach(key => {
1007
+ const formatter = registry.getFormatter(key);
1008
+ if (formatter) {
1009
+ formats.push([formatter, attrs[key]]);
1010
+ }
1011
+ });
1012
+ }
1013
+ return formats;
1014
1014
  }
1015
1015
 
1016
- const collaborateModule = {
1017
- providers: [
1018
- Collaborate,
1019
- CollaborateCursor,
1020
- {
1021
- provide: History,
1022
- useExisting: Collaborate
1023
- }
1024
- ]
1016
+ const collaborateModule = {
1017
+ providers: [
1018
+ Collaborate,
1019
+ CollaborateCursor,
1020
+ {
1021
+ provide: History,
1022
+ useExisting: Collaborate
1023
+ }
1024
+ ]
1025
1025
  };
1026
1026
 
1027
1027
  export { Collaborate, CollaborateCursor, CollaborateSelectionAwarenessDelegate, collaborateModule };