@textbus/collaborate 2.5.7 → 2.5.8

Sign up to get free protection for your applications and to get access to all the features.
package/bundles/index.js CHANGED
@@ -36,965 +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 = null) {
43
- this.injector = injector;
44
- this.nativeSelection = nativeSelection;
45
- this.scheduler = scheduler;
46
- this.selection = selection;
47
- this.awarenessDelegate = awarenessDelegate;
48
- this.host = browser.createElement('div', {
49
- styles: {
50
- position: 'absolute',
51
- left: 0,
52
- top: 0,
53
- width: '100%',
54
- height: '100%',
55
- pointerEvents: 'none',
56
- zIndex: 1
57
- }
58
- });
59
- this.canvasContainer = browser.createElement('div', {
60
- styles: {
61
- position: 'absolute',
62
- left: 0,
63
- top: 0,
64
- width: '100%',
65
- height: '100%',
66
- overflow: 'hidden'
67
- }
68
- });
69
- this.canvas = browser.createElement('canvas', {
70
- styles: {
71
- position: 'absolute',
72
- opacity: 0.5,
73
- left: 0,
74
- top: 0,
75
- width: '100%',
76
- height: document.documentElement.clientHeight + 'px',
77
- pointerEvents: 'none',
78
- }
79
- });
80
- this.context = this.canvas.getContext('2d');
81
- this.tooltips = browser.createElement('div', {
82
- styles: {
83
- position: 'absolute',
84
- left: 0,
85
- top: 0,
86
- width: '100%',
87
- height: '100%',
88
- pointerEvents: 'none',
89
- fontSize: '12px',
90
- zIndex: 10
91
- }
92
- });
93
- this.onRectsChange = new stream.Subject();
94
- this.subscription = new stream.Subscription();
95
- this.currentSelection = [];
96
- this.container = injector.get(browser.VIEW_CONTAINER);
97
- this.canvasContainer.append(this.canvas);
98
- this.host.append(this.canvasContainer, this.tooltips);
99
- this.container.prepend(this.host);
100
- this.subscription.add(this.onRectsChange.subscribe(rects => {
101
- for (const rect of rects) {
102
- this.context.fillStyle = rect.color;
103
- this.context.beginPath();
104
- this.context.rect(rect.left, rect.top, rect.width, rect.height);
105
- this.context.fill();
106
- this.context.closePath();
107
- }
108
- }), stream.fromEvent(window, 'resize').subscribe(() => {
109
- this.canvas.style.height = document.documentElement.clientHeight + 'px';
110
- this.refresh();
111
- }), this.scheduler.onDocChanged.subscribe(() => {
112
- this.refresh();
113
- }));
114
- }
115
- refresh() {
116
- this.draw(this.currentSelection);
117
- }
118
- destroy() {
119
- this.subscription.unsubscribe();
120
- }
121
- draw(paths) {
122
- this.currentSelection = paths;
123
- const containerRect = this.container.getBoundingClientRect();
124
- this.canvas.style.top = containerRect.top * -1 + 'px';
125
- this.canvas.width = this.canvas.offsetWidth;
126
- this.canvas.height = this.canvas.offsetHeight;
127
- this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
128
- const users = [];
129
- paths.filter(i => {
130
- return i.paths.anchor.length && i.paths.focus.length;
131
- }).forEach(item => {
132
- const anchorPaths = [...item.paths.anchor];
133
- const focusPaths = [...item.paths.focus];
134
- const anchorOffset = anchorPaths.pop();
135
- const anchorSlot = this.selection.findSlotByPaths(anchorPaths);
136
- const focusOffset = focusPaths.pop();
137
- const focusSlot = this.selection.findSlotByPaths(focusPaths);
138
- if (!anchorSlot || !focusSlot) {
139
- return;
140
- }
141
- const { focus, anchor } = this.nativeSelection.getPositionByRange({
142
- focusOffset,
143
- anchorOffset,
144
- focusSlot,
145
- anchorSlot
146
- });
147
- if (!focus || !anchor) {
148
- return;
149
- }
150
- const nativeRange = document.createRange();
151
- nativeRange.setStart(anchor.node, anchor.offset);
152
- nativeRange.setEnd(focus.node, focus.offset);
153
- if ((anchor.node !== focus.node || anchor.offset !== focus.offset) && nativeRange.collapsed) {
154
- nativeRange.setStart(focus.node, focus.offset);
155
- nativeRange.setEnd(anchor.node, anchor.offset);
156
- }
157
- let rects = false;
158
- if (this.awarenessDelegate) {
159
- rects = this.awarenessDelegate.getRects({
160
- focusOffset,
161
- anchorOffset,
162
- focusSlot,
163
- anchorSlot
164
- }, nativeRange);
165
- }
166
- if (!rects) {
167
- rects = nativeRange.getClientRects();
168
- }
169
- const selectionRects = [];
170
- for (let i = rects.length - 1; i >= 0; i--) {
171
- const rect = rects[i];
172
- selectionRects.push({
173
- id: item.id,
174
- color: item.color,
175
- username: item.username,
176
- left: rect.left - containerRect.left,
177
- top: rect.top,
178
- width: rect.width,
179
- height: rect.height,
180
- });
181
- }
182
- this.onRectsChange.next(selectionRects);
183
- const cursorRange = nativeRange.cloneRange();
184
- cursorRange.setStart(focus.node, focus.offset);
185
- cursorRange.collapse(true);
186
- const cursorRect = browser.getLayoutRectByRange(cursorRange);
187
- const rect = {
188
- id: item.id,
189
- username: item.username,
190
- color: item.color,
191
- left: cursorRect.left - containerRect.left,
192
- top: cursorRect.top - containerRect.top,
193
- width: 1,
194
- height: cursorRect.height
195
- };
196
- if (rect.left < 0 || rect.top < 0 || rect.left > containerRect.width) {
197
- return;
198
- }
199
- users.push(rect);
200
- });
201
- this.drawUserCursor(users);
202
- }
203
- drawUserCursor(rects) {
204
- for (let i = 0; i < rects.length; i++) {
205
- const rect = rects[i];
206
- const { cursor, userTip, anchor } = this.getUserCursor(i);
207
- Object.assign(cursor.style, {
208
- left: rect.left + 'px',
209
- top: rect.top + 'px',
210
- width: rect.width + 'px',
211
- height: rect.height + 'px',
212
- background: rect.color,
213
- display: 'block'
214
- });
215
- anchor.style.background = rect.color;
216
- userTip.innerText = rect.username;
217
- userTip.style.background = rect.color;
218
- }
219
- for (let i = rects.length; i < this.tooltips.children.length; i++) {
220
- this.tooltips.removeChild(this.tooltips.children[i]);
221
- }
222
- }
223
- getUserCursor(index) {
224
- let child = this.tooltips.children[index];
225
- if (child) {
226
- const anchor = child.children[0];
227
- return {
228
- cursor: child,
229
- anchor,
230
- userTip: anchor.children[0]
231
- };
232
- }
233
- const userTip = browser.createElement('span', {
234
- styles: {
235
- position: 'absolute',
236
- left: '50%',
237
- transform: 'translateX(-50%)',
238
- marginBottom: '2px',
239
- bottom: '100%',
240
- whiteSpace: 'nowrap',
241
- color: '#fff',
242
- boxShadow: '0 1px 2px rgba(0,0,0,.1)',
243
- opacity: 0.8,
244
- borderRadius: '3px',
245
- padding: '3px 5px',
246
- pointerEvents: 'none',
247
- }
248
- });
249
- const anchor = browser.createElement('span', {
250
- styles: {
251
- position: 'absolute',
252
- top: '-2px',
253
- left: '-2px',
254
- width: '5px',
255
- height: '5px',
256
- borderRadius: '50%',
257
- pointerEvents: 'auto',
258
- pointer: 'cursor',
259
- },
260
- children: [userTip]
261
- });
262
- child = browser.createElement('span', {
263
- styles: {
264
- position: 'absolute',
265
- },
266
- children: [
267
- anchor
268
- ]
269
- });
270
- this.tooltips.append(child);
271
- return {
272
- cursor: child,
273
- anchor,
274
- userTip
275
- };
276
- }
277
- };
278
- exports.CollaborateCursor = __decorate([
279
- di.Injectable(),
280
- __param(4, di.Optional()),
281
- __metadata("design:paramtypes", [di.Injector,
282
- browser.SelectionBridge,
283
- core.Scheduler,
284
- core.Selection, Object])
39
+ class CollaborateSelectionAwarenessDelegate {
40
+ }
41
+ exports.CollaborateCursor = class CollaborateCursor {
42
+ constructor(injector, nativeSelection, scheduler, selection, awarenessDelegate) {
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])
285
286
  ], exports.CollaborateCursor);
286
287
 
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;
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;
306
307
  }
307
308
 
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;
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;
987
988
  }
988
989
 
989
- const collaborateModule = {
990
- providers: [
991
- exports.Collaborate,
992
- exports.CollaborateCursor,
993
- {
994
- provide: core.History,
995
- useClass: exports.Collaborate
996
- }
997
- ]
990
+ const collaborateModule = {
991
+ providers: [
992
+ exports.Collaborate,
993
+ exports.CollaborateCursor,
994
+ {
995
+ provide: core.History,
996
+ useClass: exports.Collaborate
997
+ }
998
+ ]
998
999
  };
999
1000
 
1000
1001
  exports.CollaborateSelectionAwarenessDelegate = CollaborateSelectionAwarenessDelegate;