@textbus/collaborate 2.5.8 → 2.5.9

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