@textbus/collaborate 2.5.5 → 2.5.7

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