@textbus/collaborate 2.5.6 → 2.5.7

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