@textbus/collaborate 4.0.0-alpha.8 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundles/collaborate-module.d.ts +12 -1
- package/bundles/collaborate.d.ts +55 -64
- package/bundles/connectors/_api.d.ts +2 -0
- package/bundles/connectors/hocuspocus-connector.d.ts +13 -0
- package/bundles/connectors/y-websocket-connector.d.ts +13 -0
- package/bundles/index.esm.js +397 -317
- package/bundles/index.js +397 -314
- package/bundles/public-api.d.ts +3 -0
- package/bundles/sync-connector.d.ts +10 -0
- package/bundles/user-activity.d.ts +23 -0
- package/package.json +8 -8
package/bundles/index.js
CHANGED
|
@@ -1,10 +1,73 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var core$1 = require('@viewfly/core');
|
|
4
3
|
var stream = require('@tanbo/stream');
|
|
4
|
+
var provider = require('@hocuspocus/provider');
|
|
5
|
+
var yWebsocket = require('y-websocket');
|
|
6
|
+
var core$1 = require('@viewfly/core');
|
|
5
7
|
var core = require('@textbus/core');
|
|
6
8
|
var yjs = require('yjs');
|
|
7
9
|
|
|
10
|
+
/**
|
|
11
|
+
* 协作通信通用接口
|
|
12
|
+
*/
|
|
13
|
+
class SyncConnector {
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
class HocuspocusConnector extends SyncConnector {
|
|
17
|
+
constructor(config) {
|
|
18
|
+
super();
|
|
19
|
+
this.loadEvent = new stream.Subject();
|
|
20
|
+
this.stateChangeEvent = new stream.Subject();
|
|
21
|
+
this.onLoad = this.loadEvent.asObservable();
|
|
22
|
+
this.onStateChange = this.stateChangeEvent.asObservable();
|
|
23
|
+
this.provide = new provider.HocuspocusProvider(Object.assign(Object.assign({}, config), { onSynced: (data) => {
|
|
24
|
+
var _a;
|
|
25
|
+
(_a = config.onSynced) === null || _a === void 0 ? void 0 : _a.call(config, data);
|
|
26
|
+
this.loadEvent.next();
|
|
27
|
+
}, onAwarenessUpdate: (data) => {
|
|
28
|
+
var _a;
|
|
29
|
+
(_a = config.onAwarenessUpdate) === null || _a === void 0 ? void 0 : _a.call(config, data);
|
|
30
|
+
data.states.forEach(state => {
|
|
31
|
+
this.stateChangeEvent.next(state);
|
|
32
|
+
});
|
|
33
|
+
} }));
|
|
34
|
+
}
|
|
35
|
+
setLocalStateField(key, data) {
|
|
36
|
+
this.provide.setAwarenessField(key, data);
|
|
37
|
+
}
|
|
38
|
+
onDestroy() {
|
|
39
|
+
this.provide.disconnect();
|
|
40
|
+
this.provide.destroy();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
class YWebsocketConnector extends SyncConnector {
|
|
45
|
+
constructor(url, roomName, yDoc) {
|
|
46
|
+
super();
|
|
47
|
+
this.loadEvent = new stream.Subject();
|
|
48
|
+
this.stateChangeEvent = new stream.Subject();
|
|
49
|
+
this.onLoad = this.loadEvent.asObservable();
|
|
50
|
+
this.onStateChange = this.stateChangeEvent.asObservable();
|
|
51
|
+
this.provide = new yWebsocket.WebsocketProvider(url, roomName, yDoc);
|
|
52
|
+
this.provide.on('sync', (is) => {
|
|
53
|
+
if (is) {
|
|
54
|
+
this.loadEvent.next();
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
this.provide.awareness.on('update', () => {
|
|
58
|
+
this.provide.awareness.getStates().forEach(state => {
|
|
59
|
+
this.stateChangeEvent.next(state);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
setLocalStateField(key, data) {
|
|
64
|
+
this.provide.awareness.setLocalStateField(key, data);
|
|
65
|
+
}
|
|
66
|
+
onDestroy() {
|
|
67
|
+
this.provide.disconnect();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
8
71
|
/******************************************************************************
|
|
9
72
|
Copyright (c) Microsoft Corporation.
|
|
10
73
|
|
|
@@ -43,38 +106,38 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
43
106
|
};
|
|
44
107
|
|
|
45
108
|
const collaborateErrorFn = core.makeError('Collaborate');
|
|
46
|
-
class
|
|
109
|
+
class SlotMap {
|
|
47
110
|
constructor() {
|
|
48
111
|
this.slotAndYTextMap = new WeakMap();
|
|
49
|
-
this.
|
|
112
|
+
this.yTextAndSlotMap = new WeakMap();
|
|
50
113
|
}
|
|
51
114
|
set(key, value) {
|
|
52
115
|
if (key instanceof core.Slot) {
|
|
53
116
|
this.slotAndYTextMap.set(key, value);
|
|
54
|
-
this.
|
|
117
|
+
this.yTextAndSlotMap.set(value, key);
|
|
55
118
|
}
|
|
56
119
|
else {
|
|
57
120
|
this.slotAndYTextMap.set(value, key);
|
|
58
|
-
this.
|
|
121
|
+
this.yTextAndSlotMap.set(key, value);
|
|
59
122
|
}
|
|
60
123
|
}
|
|
61
124
|
get(key) {
|
|
62
125
|
if (key instanceof core.Slot) {
|
|
63
126
|
return this.slotAndYTextMap.get(key) || null;
|
|
64
127
|
}
|
|
65
|
-
return this.
|
|
128
|
+
return this.yTextAndSlotMap.get(key) || null;
|
|
66
129
|
}
|
|
67
130
|
delete(key) {
|
|
68
131
|
if (key instanceof core.Slot) {
|
|
69
132
|
const v = this.slotAndYTextMap.get(key);
|
|
70
133
|
this.slotAndYTextMap.delete(key);
|
|
71
134
|
if (v) {
|
|
72
|
-
this.
|
|
135
|
+
this.yTextAndSlotMap.delete(v);
|
|
73
136
|
}
|
|
74
137
|
}
|
|
75
138
|
else {
|
|
76
|
-
const v = this.
|
|
77
|
-
this.
|
|
139
|
+
const v = this.yTextAndSlotMap.get(key);
|
|
140
|
+
this.yTextAndSlotMap.delete(key);
|
|
78
141
|
if (v) {
|
|
79
142
|
this.slotAndYTextMap.delete(v);
|
|
80
143
|
}
|
|
@@ -107,13 +170,9 @@ exports.Collaborate = class Collaborate {
|
|
|
107
170
|
this.manager = null;
|
|
108
171
|
this.subscriptions = [];
|
|
109
172
|
this.updateFromRemote = false;
|
|
110
|
-
this.contentSyncCaches = new WeakMap();
|
|
111
|
-
this.slotStateSyncCaches = new WeakMap();
|
|
112
|
-
this.slotsSyncCaches = new WeakMap();
|
|
113
|
-
this.componentStateSyncCaches = new WeakMap();
|
|
114
173
|
this.localChangesAppliedEvent = new stream.Subject();
|
|
115
174
|
this.selectionChangeEvent = new stream.Subject();
|
|
116
|
-
this.
|
|
175
|
+
this.slotMap = new SlotMap();
|
|
117
176
|
this.updateRemoteActions = [];
|
|
118
177
|
this.noRecord = {};
|
|
119
178
|
this.historyItems = [];
|
|
@@ -148,7 +207,7 @@ exports.Collaborate = class Collaborate {
|
|
|
148
207
|
this.subscriptions.push(this.scheduler.onLocalChangeBefore.subscribe(() => {
|
|
149
208
|
beforePosition = this.getRelativeCursorLocation();
|
|
150
209
|
}));
|
|
151
|
-
manager.on('stack-item-added', event => {
|
|
210
|
+
manager.on('stack-item-added', (event) => {
|
|
152
211
|
if (event.type === 'undo') {
|
|
153
212
|
if (event.origin === manager) {
|
|
154
213
|
this.index++;
|
|
@@ -260,50 +319,145 @@ exports.Collaborate = class Collaborate {
|
|
|
260
319
|
(_a = this.manager) === null || _a === void 0 ? void 0 : _a.destroy();
|
|
261
320
|
}
|
|
262
321
|
syncRootComponent(root, rootComponent) {
|
|
263
|
-
let
|
|
264
|
-
if (!
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
const sharedSlot = this.createSharedSlotBySlot(i);
|
|
268
|
-
slots.push([sharedSlot]);
|
|
269
|
-
});
|
|
322
|
+
let state = root.get('state');
|
|
323
|
+
if (!state) {
|
|
324
|
+
state = new yjs.Map();
|
|
325
|
+
this.syncLocalMapToSharedMap(rootComponent.state, state);
|
|
270
326
|
this.yDoc.transact(() => {
|
|
271
|
-
root.set('state',
|
|
272
|
-
root.set('slots', slots);
|
|
327
|
+
root.set('state', state);
|
|
273
328
|
});
|
|
274
329
|
}
|
|
275
|
-
else
|
|
276
|
-
rootComponent.
|
|
277
|
-
|
|
330
|
+
else {
|
|
331
|
+
Object.keys(rootComponent.state).forEach(key => {
|
|
332
|
+
Reflect.deleteProperty(rootComponent.state, key);
|
|
278
333
|
});
|
|
279
|
-
this.
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
334
|
+
this.syncSharedMapToLocalMap(state, rootComponent.state);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
syncSharedMapToLocalMap(sharedMap, localMap) {
|
|
338
|
+
sharedMap.forEach((value, key) => {
|
|
339
|
+
localMap[key] = this.createLocalModelBySharedByModel(value);
|
|
340
|
+
});
|
|
341
|
+
this.syncObject(sharedMap, localMap);
|
|
342
|
+
}
|
|
343
|
+
createLocalMapBySharedMap(sharedMap) {
|
|
344
|
+
const localMap = core.createObjectProxy({});
|
|
345
|
+
this.syncSharedMapToLocalMap(sharedMap, localMap);
|
|
346
|
+
return localMap;
|
|
347
|
+
}
|
|
348
|
+
createLocalArrayBySharedArray(sharedArray) {
|
|
349
|
+
const localArray = core.createArrayProxy([]);
|
|
350
|
+
localArray.push(...sharedArray.map(item => this.createLocalModelBySharedByModel(item)));
|
|
351
|
+
this.syncArray(sharedArray, localArray);
|
|
352
|
+
return localArray;
|
|
353
|
+
}
|
|
354
|
+
syncLocalMapToSharedMap(localMap, sharedMap) {
|
|
355
|
+
Object.entries(localMap).forEach(([key, value]) => {
|
|
356
|
+
sharedMap.set(key, this.createSharedModelByLocalModel(value));
|
|
357
|
+
});
|
|
358
|
+
this.syncObject(sharedMap, localMap);
|
|
359
|
+
}
|
|
360
|
+
createSharedMapByLocalMap(localMap) {
|
|
361
|
+
const sharedMap = new yjs.Map();
|
|
362
|
+
this.syncLocalMapToSharedMap(localMap, sharedMap);
|
|
363
|
+
return sharedMap;
|
|
364
|
+
}
|
|
365
|
+
createSharedArrayByLocalArray(localArray) {
|
|
366
|
+
const sharedArray = new yjs.Array();
|
|
367
|
+
localArray.forEach(value => {
|
|
368
|
+
sharedArray.push([this.createSharedModelByLocalModel(value)]);
|
|
369
|
+
});
|
|
370
|
+
this.syncArray(sharedArray, localArray);
|
|
371
|
+
return sharedArray;
|
|
372
|
+
}
|
|
373
|
+
createSharedSlotByLocalSlot(localSlot) {
|
|
374
|
+
const sharedSlot = new yjs.Text();
|
|
375
|
+
sharedSlot.setAttribute('__schema__', [...localSlot.schema]);
|
|
376
|
+
let offset = 0;
|
|
377
|
+
localSlot.toDelta().forEach(i => {
|
|
378
|
+
let formats = {};
|
|
379
|
+
if (i.formats) {
|
|
380
|
+
i.formats.forEach(item => {
|
|
381
|
+
formats[item[0].name] = item[1];
|
|
283
382
|
});
|
|
284
|
-
}
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
formats = null;
|
|
386
|
+
}
|
|
387
|
+
if (typeof i.insert === 'string') {
|
|
388
|
+
sharedSlot.insert(offset, i.insert, formats);
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
const sharedComponent = this.createSharedComponentByLocalComponent(i.insert);
|
|
392
|
+
sharedSlot.insertEmbed(offset, sharedComponent, formats);
|
|
393
|
+
}
|
|
394
|
+
offset += i.insert.length;
|
|
395
|
+
});
|
|
396
|
+
localSlot.getAttributes().forEach(item => {
|
|
397
|
+
sharedSlot.setAttribute(item[0].name, item[1]);
|
|
398
|
+
});
|
|
399
|
+
this.syncSlot(sharedSlot, localSlot);
|
|
400
|
+
return sharedSlot;
|
|
401
|
+
}
|
|
402
|
+
createLocalSlotBySharedSlot(sharedSlot) {
|
|
403
|
+
const delta = sharedSlot.toDelta();
|
|
404
|
+
const localSlot = new core.Slot(sharedSlot.getAttribute('__schema__') || []); // TODO 这里有潜在的问题
|
|
405
|
+
const attrs = sharedSlot.getAttributes();
|
|
406
|
+
Object.keys(attrs).forEach(key => {
|
|
407
|
+
const attribute = this.registry.getAttribute(key);
|
|
408
|
+
if (attribute) {
|
|
409
|
+
localSlot.setAttribute(attribute, attrs[key]);
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
for (const action of delta) {
|
|
413
|
+
if (action.insert) {
|
|
414
|
+
if (typeof action.insert === 'string') {
|
|
415
|
+
const formats = remoteFormatsToLocal(this.registry, action.attributes);
|
|
416
|
+
localSlot.insert(action.insert, formats);
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
const sharedComponent = action.insert;
|
|
420
|
+
const component = this.createLocalComponentBySharedComponent(sharedComponent);
|
|
421
|
+
localSlot.insert(component, remoteFormatsToLocal(this.registry, action.attributes));
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
else {
|
|
425
|
+
throw collaborateErrorFn('unexpected delta action.');
|
|
426
|
+
}
|
|
285
427
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
rootComponent.slots.insert(slot);
|
|
296
|
-
});
|
|
428
|
+
this.syncSlot(sharedSlot, localSlot);
|
|
429
|
+
return localSlot;
|
|
430
|
+
}
|
|
431
|
+
createSharedModelByLocalModel(localModel) {
|
|
432
|
+
if (localModel instanceof core.Slot) {
|
|
433
|
+
return this.createSharedSlotByLocalSlot(localModel);
|
|
434
|
+
}
|
|
435
|
+
if (Array.isArray(localModel)) {
|
|
436
|
+
return this.createSharedArrayByLocalArray(localModel);
|
|
297
437
|
}
|
|
298
|
-
|
|
299
|
-
|
|
438
|
+
if (typeof localModel === 'object' && localModel !== null) {
|
|
439
|
+
return this.createSharedMapByLocalMap(localModel);
|
|
440
|
+
}
|
|
441
|
+
return localModel;
|
|
442
|
+
}
|
|
443
|
+
createLocalModelBySharedByModel(sharedModel) {
|
|
444
|
+
if (sharedModel instanceof yjs.Map) {
|
|
445
|
+
return this.createLocalMapBySharedMap(sharedModel);
|
|
446
|
+
}
|
|
447
|
+
if (sharedModel instanceof yjs.Array) {
|
|
448
|
+
return this.createLocalArrayBySharedArray(sharedModel);
|
|
449
|
+
}
|
|
450
|
+
if (sharedModel instanceof yjs.Text) {
|
|
451
|
+
return this.createLocalSlotBySharedSlot(sharedModel);
|
|
452
|
+
}
|
|
453
|
+
return sharedModel;
|
|
300
454
|
}
|
|
301
455
|
getAbstractSelection(position) {
|
|
302
456
|
const anchorPosition = yjs.createAbsolutePositionFromRelativePosition(position.anchor, this.yDoc);
|
|
303
457
|
const focusPosition = yjs.createAbsolutePositionFromRelativePosition(position.focus, this.yDoc);
|
|
304
458
|
if (anchorPosition && focusPosition) {
|
|
305
|
-
const focusSlot = this.
|
|
306
|
-
const anchorSlot = this.
|
|
459
|
+
const focusSlot = this.slotMap.get(focusPosition.type);
|
|
460
|
+
const anchorSlot = this.slotMap.get(anchorPosition.type);
|
|
307
461
|
if (focusSlot && anchorSlot) {
|
|
308
462
|
return {
|
|
309
463
|
anchorSlot,
|
|
@@ -318,11 +472,11 @@ exports.Collaborate = class Collaborate {
|
|
|
318
472
|
getRelativeCursorLocation() {
|
|
319
473
|
const { anchorSlot, anchorOffset, focusSlot, focusOffset } = this.selection;
|
|
320
474
|
if (anchorSlot) {
|
|
321
|
-
const anchorYText = this.
|
|
475
|
+
const anchorYText = this.slotMap.get(anchorSlot);
|
|
322
476
|
if (anchorYText) {
|
|
323
477
|
const anchorPosition = yjs.createRelativePositionFromTypeIndex(anchorYText, anchorOffset);
|
|
324
478
|
if (focusSlot) {
|
|
325
|
-
const focusYText = this.
|
|
479
|
+
const focusYText = this.slotMap.get(focusSlot);
|
|
326
480
|
if (focusYText) {
|
|
327
481
|
const focusPosition = yjs.createRelativePositionFromTypeIndex(focusYText, focusOffset);
|
|
328
482
|
return {
|
|
@@ -335,11 +489,10 @@ exports.Collaborate = class Collaborate {
|
|
|
335
489
|
}
|
|
336
490
|
return null;
|
|
337
491
|
}
|
|
338
|
-
|
|
339
|
-
this.contentMap.set(slot, content);
|
|
492
|
+
syncSlot(sharedSlot, localSlot) {
|
|
340
493
|
const syncRemote = (ev, tr) => {
|
|
341
494
|
this.runRemoteUpdate(tr, () => {
|
|
342
|
-
|
|
495
|
+
localSlot.retain(0);
|
|
343
496
|
ev.keysChanged.forEach(key => {
|
|
344
497
|
const change = ev.keys.get(key);
|
|
345
498
|
if (!change) {
|
|
@@ -349,13 +502,13 @@ exports.Collaborate = class Collaborate {
|
|
|
349
502
|
if (updateType === 'update' || updateType === 'add') {
|
|
350
503
|
const attribute = this.registry.getAttribute(key);
|
|
351
504
|
if (attribute) {
|
|
352
|
-
|
|
505
|
+
localSlot.setAttribute(attribute, sharedSlot.getAttribute(key));
|
|
353
506
|
}
|
|
354
507
|
}
|
|
355
508
|
else if (updateType === 'delete') {
|
|
356
509
|
const attribute = this.registry.getAttribute(key);
|
|
357
510
|
if (attribute) {
|
|
358
|
-
|
|
511
|
+
localSlot.removeAttribute(attribute);
|
|
359
512
|
}
|
|
360
513
|
}
|
|
361
514
|
});
|
|
@@ -364,54 +517,52 @@ exports.Collaborate = class Collaborate {
|
|
|
364
517
|
if (action.attributes) {
|
|
365
518
|
const formats = remoteFormatsToLocal(this.registry, action.attributes);
|
|
366
519
|
if (formats.length) {
|
|
367
|
-
|
|
520
|
+
localSlot.retain(action.retain, formats);
|
|
368
521
|
}
|
|
369
|
-
|
|
522
|
+
localSlot.retain(localSlot.index + action.retain);
|
|
370
523
|
}
|
|
371
524
|
else {
|
|
372
|
-
|
|
525
|
+
localSlot.retain(action.retain);
|
|
373
526
|
}
|
|
374
527
|
}
|
|
375
528
|
else if (action.insert) {
|
|
376
|
-
const index =
|
|
529
|
+
const index = localSlot.index;
|
|
377
530
|
let length = 1;
|
|
378
531
|
if (typeof action.insert === 'string') {
|
|
379
532
|
length = action.insert.length;
|
|
380
|
-
|
|
533
|
+
localSlot.insert(action.insert, remoteFormatsToLocal(this.registry, action.attributes));
|
|
381
534
|
}
|
|
382
535
|
else {
|
|
383
536
|
const sharedComponent = action.insert;
|
|
384
|
-
const component = this.
|
|
385
|
-
|
|
386
|
-
this.syncComponentState(sharedComponent, component);
|
|
387
|
-
slot.insert(component);
|
|
537
|
+
const component = this.createLocalComponentBySharedComponent(sharedComponent);
|
|
538
|
+
localSlot.insert(component);
|
|
388
539
|
}
|
|
389
540
|
if (this.selection.isSelected && tr.origin !== this.manager) {
|
|
390
|
-
if (
|
|
391
|
-
this.selection.setAnchor(
|
|
541
|
+
if (localSlot === this.selection.anchorSlot && this.selection.anchorOffset > index) {
|
|
542
|
+
this.selection.setAnchor(localSlot, this.selection.anchorOffset + length);
|
|
392
543
|
}
|
|
393
|
-
if (
|
|
394
|
-
this.selection.setFocus(
|
|
544
|
+
if (localSlot === this.selection.focusSlot && this.selection.focusOffset > index) {
|
|
545
|
+
this.selection.setFocus(localSlot, this.selection.focusOffset + length);
|
|
395
546
|
}
|
|
396
547
|
}
|
|
397
548
|
}
|
|
398
549
|
else if (action.delete) {
|
|
399
|
-
const index =
|
|
400
|
-
|
|
550
|
+
const index = localSlot.index;
|
|
551
|
+
localSlot.delete(action.delete);
|
|
401
552
|
if (this.selection.isSelected && tr.origin !== this.manager) {
|
|
402
|
-
if (
|
|
403
|
-
this.selection.setAnchor(
|
|
553
|
+
if (localSlot === this.selection.anchorSlot && this.selection.anchorOffset >= index) {
|
|
554
|
+
this.selection.setAnchor(localSlot, this.selection.startOffset - action.delete);
|
|
404
555
|
}
|
|
405
|
-
if (
|
|
406
|
-
this.selection.setFocus(
|
|
556
|
+
if (localSlot === this.selection.focusSlot && this.selection.focusOffset >= index) {
|
|
557
|
+
this.selection.setFocus(localSlot, this.selection.focusOffset - action.delete);
|
|
407
558
|
}
|
|
408
559
|
}
|
|
409
560
|
}
|
|
410
561
|
});
|
|
411
562
|
});
|
|
412
563
|
};
|
|
413
|
-
|
|
414
|
-
const sub =
|
|
564
|
+
sharedSlot.observe(syncRemote);
|
|
565
|
+
const sub = localSlot.onContentChange.subscribe(actions => {
|
|
415
566
|
this.runLocalUpdate(() => {
|
|
416
567
|
var _a;
|
|
417
568
|
let offset = 0;
|
|
@@ -430,170 +581,176 @@ exports.Collaborate = class Collaborate {
|
|
|
430
581
|
}
|
|
431
582
|
});
|
|
432
583
|
if (length) {
|
|
433
|
-
|
|
584
|
+
sharedSlot.format(offset, action.offset, formats);
|
|
434
585
|
}
|
|
435
586
|
}
|
|
436
587
|
else {
|
|
437
588
|
offset = action.offset;
|
|
438
589
|
}
|
|
439
590
|
}
|
|
440
|
-
else if (action.type === '
|
|
441
|
-
const delta =
|
|
591
|
+
else if (action.type === 'contentInsert') {
|
|
592
|
+
const delta = sharedSlot.toDelta();
|
|
442
593
|
const isEmpty = delta.length === 1 && delta[0].insert === core.Slot.emptyPlaceholder;
|
|
443
594
|
if (typeof action.content === 'string') {
|
|
444
595
|
length = action.content.length;
|
|
445
|
-
|
|
596
|
+
sharedSlot.insert(offset, action.content, action.formats || {});
|
|
446
597
|
}
|
|
447
598
|
else {
|
|
448
599
|
length = 1;
|
|
449
|
-
const sharedComponent = this.
|
|
450
|
-
|
|
600
|
+
const sharedComponent = this.createSharedComponentByLocalComponent(action.ref);
|
|
601
|
+
sharedSlot.insertEmbed(offset, sharedComponent, action.formats || {});
|
|
451
602
|
}
|
|
452
603
|
if (isEmpty && offset === 0) {
|
|
453
|
-
|
|
604
|
+
sharedSlot.delete(sharedSlot.length - 1, 1);
|
|
454
605
|
}
|
|
455
606
|
offset += length;
|
|
456
607
|
}
|
|
457
608
|
else if (action.type === 'delete') {
|
|
458
|
-
const delta =
|
|
459
|
-
if (
|
|
460
|
-
|
|
609
|
+
const delta = sharedSlot.toDelta();
|
|
610
|
+
if (sharedSlot.length) {
|
|
611
|
+
sharedSlot.delete(offset, action.count);
|
|
461
612
|
}
|
|
462
|
-
if (
|
|
463
|
-
|
|
613
|
+
if (sharedSlot.length === 0) {
|
|
614
|
+
sharedSlot.insert(0, '\n', (_a = delta[0]) === null || _a === void 0 ? void 0 : _a.attributes);
|
|
464
615
|
}
|
|
465
616
|
}
|
|
466
617
|
else if (action.type === 'attrSet') {
|
|
467
|
-
|
|
618
|
+
sharedSlot.setAttribute(action.name, action.value);
|
|
468
619
|
}
|
|
469
|
-
else if (action.type === '
|
|
470
|
-
|
|
620
|
+
else if (action.type === 'attrDelete') {
|
|
621
|
+
sharedSlot.removeAttribute(action.name);
|
|
471
622
|
}
|
|
472
623
|
}
|
|
473
624
|
});
|
|
474
625
|
});
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
}));
|
|
480
|
-
this.contentSyncCaches.set(slot, () => {
|
|
481
|
-
content.unobserve(syncRemote);
|
|
626
|
+
this.slotMap.set(localSlot, sharedSlot);
|
|
627
|
+
localSlot.__changeMarker__.destroyCallbacks.push(() => {
|
|
628
|
+
this.slotMap.delete(localSlot);
|
|
629
|
+
sharedSlot.unobserve(syncRemote);
|
|
482
630
|
sub.unsubscribe();
|
|
483
631
|
});
|
|
484
632
|
}
|
|
485
|
-
|
|
486
|
-
const
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
633
|
+
createSharedComponentByLocalComponent(component) {
|
|
634
|
+
const sharedComponent = new yjs.Map();
|
|
635
|
+
const sharedState = this.createSharedMapByLocalMap(component.state);
|
|
636
|
+
sharedComponent.set('name', component.name);
|
|
637
|
+
sharedComponent.set('state', sharedState);
|
|
638
|
+
return sharedComponent;
|
|
639
|
+
}
|
|
640
|
+
createLocalComponentBySharedComponent(yMap) {
|
|
641
|
+
const componentName = yMap.get('name');
|
|
642
|
+
const sharedState = yMap.get('state');
|
|
643
|
+
const state = this.createLocalMapBySharedMap(sharedState);
|
|
644
|
+
const instance = this.registry.createComponentByData(componentName, state);
|
|
645
|
+
if (instance) {
|
|
646
|
+
return instance;
|
|
647
|
+
}
|
|
648
|
+
throw collaborateErrorFn(`cannot find component factory \`${componentName}\`.`);
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* 双向同步数组
|
|
652
|
+
* @param sharedArray
|
|
653
|
+
* @param localArray
|
|
654
|
+
* @private
|
|
655
|
+
*/
|
|
656
|
+
syncArray(sharedArray, localArray) {
|
|
657
|
+
const sub = localArray.__changeMarker__.onSelfChange.subscribe((actions) => {
|
|
658
|
+
this.runLocalUpdate(() => {
|
|
659
|
+
let index = 0;
|
|
660
|
+
for (const action of actions) {
|
|
661
|
+
switch (action.type) {
|
|
662
|
+
case 'retain':
|
|
663
|
+
index = action.offset;
|
|
664
|
+
break;
|
|
665
|
+
case 'insert':
|
|
666
|
+
{
|
|
667
|
+
const ref = action.ref;
|
|
668
|
+
if (!Array.isArray(ref)) {
|
|
669
|
+
throw collaborateErrorFn('The insertion action must have a reference value.');
|
|
670
|
+
}
|
|
671
|
+
const data = ref.map(item => {
|
|
672
|
+
return this.createSharedModelByLocalModel(item);
|
|
673
|
+
});
|
|
674
|
+
sharedArray.insert(index, data);
|
|
494
675
|
}
|
|
495
|
-
|
|
496
|
-
|
|
676
|
+
break;
|
|
677
|
+
case 'delete':
|
|
678
|
+
if (action.count <= 0) {
|
|
679
|
+
break;
|
|
497
680
|
}
|
|
498
|
-
|
|
681
|
+
sharedArray.delete(index, action.count);
|
|
682
|
+
break;
|
|
683
|
+
case 'setIndex':
|
|
684
|
+
sharedArray.delete(action.index, 1);
|
|
685
|
+
sharedArray.insert(action.index, [this.createSharedModelByLocalModel(action.ref)]);
|
|
686
|
+
break;
|
|
499
687
|
}
|
|
500
|
-
}
|
|
688
|
+
}
|
|
501
689
|
});
|
|
502
|
-
};
|
|
503
|
-
remoteSlot.observe(syncRemote);
|
|
504
|
-
const sub = slot.onStateChange.subscribe(change => {
|
|
505
|
-
this.runLocalUpdate(() => {
|
|
506
|
-
remoteSlot.set('state', change.newState);
|
|
507
|
-
}, change.record);
|
|
508
|
-
});
|
|
509
|
-
this.slotStateSyncCaches.set(slot, () => {
|
|
510
|
-
remoteSlot.unobserve(syncRemote);
|
|
511
|
-
sub.unsubscribe();
|
|
512
690
|
});
|
|
513
|
-
}
|
|
514
|
-
syncComponentSlots(remoteSlots, component) {
|
|
515
|
-
const slots = component.slots;
|
|
516
691
|
const syncRemote = (ev, tr) => {
|
|
517
692
|
this.runRemoteUpdate(tr, () => {
|
|
518
693
|
let index = 0;
|
|
519
|
-
|
|
520
|
-
ev.delta.forEach(action => {
|
|
694
|
+
ev.delta.forEach((action) => {
|
|
521
695
|
if (Reflect.has(action, 'retain')) {
|
|
522
696
|
index += action.retain;
|
|
523
|
-
slots.retain(index);
|
|
524
697
|
}
|
|
525
698
|
else if (action.insert) {
|
|
526
|
-
action.insert.
|
|
527
|
-
|
|
528
|
-
slots.insert(slot);
|
|
529
|
-
this.syncSlotContent(item.get('content'), slot);
|
|
530
|
-
this.syncSlotState(item, slot);
|
|
531
|
-
index++;
|
|
699
|
+
const data = action.insert.map((item) => {
|
|
700
|
+
return this.createLocalModelBySharedByModel(item);
|
|
532
701
|
});
|
|
702
|
+
localArray.splice(index, 0, ...data);
|
|
703
|
+
index += data.length;
|
|
533
704
|
}
|
|
534
705
|
else if (action.delete) {
|
|
535
|
-
|
|
536
|
-
slots.delete(action.delete);
|
|
706
|
+
localArray.splice(index, action.delete);
|
|
537
707
|
}
|
|
538
708
|
});
|
|
539
709
|
});
|
|
540
710
|
};
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
this.runLocalUpdate(() => {
|
|
544
|
-
const applyActions = operations.apply;
|
|
545
|
-
let index;
|
|
546
|
-
applyActions.forEach(action => {
|
|
547
|
-
if (action.type === 'retain') {
|
|
548
|
-
index = action.offset;
|
|
549
|
-
}
|
|
550
|
-
else if (action.type === 'insertSlot') {
|
|
551
|
-
const sharedSlot = this.createSharedSlotBySlot(action.ref);
|
|
552
|
-
remoteSlots.insert(index, [sharedSlot]);
|
|
553
|
-
index++;
|
|
554
|
-
}
|
|
555
|
-
else if (action.type === 'delete') {
|
|
556
|
-
remoteSlots.delete(index, action.count);
|
|
557
|
-
}
|
|
558
|
-
});
|
|
559
|
-
});
|
|
560
|
-
});
|
|
561
|
-
sub.add(slots.onChildSlotRemove.subscribe(slots => {
|
|
562
|
-
slots.forEach(slot => {
|
|
563
|
-
this.cleanSubscriptionsBySlot(slot);
|
|
564
|
-
});
|
|
565
|
-
}));
|
|
566
|
-
this.slotsSyncCaches.set(component, () => {
|
|
567
|
-
remoteSlots.unobserve(syncRemote);
|
|
711
|
+
sharedArray.observe(syncRemote);
|
|
712
|
+
localArray.__changeMarker__.destroyCallbacks.push(() => {
|
|
568
713
|
sub.unsubscribe();
|
|
714
|
+
sharedArray.unobserve(syncRemote);
|
|
569
715
|
});
|
|
570
716
|
}
|
|
571
|
-
|
|
717
|
+
/**
|
|
718
|
+
* 双向同步对象
|
|
719
|
+
* @param sharedObject
|
|
720
|
+
* @param localObject
|
|
721
|
+
* @private
|
|
722
|
+
*/
|
|
723
|
+
syncObject(sharedObject, localObject) {
|
|
572
724
|
const syncRemote = (ev, tr) => {
|
|
573
725
|
this.runRemoteUpdate(tr, () => {
|
|
574
|
-
ev.
|
|
575
|
-
if (
|
|
576
|
-
const
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
else {
|
|
582
|
-
return state;
|
|
583
|
-
}
|
|
584
|
-
});
|
|
726
|
+
ev.changes.keys.forEach((item, key) => {
|
|
727
|
+
if (item.action === 'add' || item.action === 'update') {
|
|
728
|
+
const value = sharedObject.get(key);
|
|
729
|
+
localObject[key] = this.createLocalModelBySharedByModel(value);
|
|
730
|
+
}
|
|
731
|
+
else {
|
|
732
|
+
Reflect.deleteProperty(localObject, key);
|
|
585
733
|
}
|
|
586
734
|
});
|
|
587
735
|
});
|
|
588
736
|
};
|
|
589
|
-
|
|
590
|
-
const sub =
|
|
737
|
+
sharedObject.observe(syncRemote);
|
|
738
|
+
const sub = localObject.__changeMarker__.onSelfChange.subscribe((actions) => {
|
|
591
739
|
this.runLocalUpdate(() => {
|
|
592
|
-
|
|
593
|
-
|
|
740
|
+
for (const action of actions) {
|
|
741
|
+
switch (action.type) {
|
|
742
|
+
case 'propSet':
|
|
743
|
+
sharedObject.set(action.key, this.createSharedModelByLocalModel(action.ref));
|
|
744
|
+
break;
|
|
745
|
+
case 'propDelete':
|
|
746
|
+
sharedObject.delete(action.key);
|
|
747
|
+
break;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
});
|
|
594
751
|
});
|
|
595
|
-
|
|
596
|
-
|
|
752
|
+
localObject.__changeMarker__.destroyCallbacks.push(function () {
|
|
753
|
+
sharedObject.unobserve(syncRemote);
|
|
597
754
|
sub.unsubscribe();
|
|
598
755
|
});
|
|
599
756
|
}
|
|
@@ -619,140 +776,6 @@ exports.Collaborate = class Collaborate {
|
|
|
619
776
|
}
|
|
620
777
|
this.updateFromRemote = false;
|
|
621
778
|
}
|
|
622
|
-
createSharedComponentByComponent(component) {
|
|
623
|
-
const sharedComponent = new yjs.Map();
|
|
624
|
-
sharedComponent.set('state', component.state);
|
|
625
|
-
sharedComponent.set('name', component.name);
|
|
626
|
-
const sharedSlots = new yjs.Array();
|
|
627
|
-
sharedComponent.set('slots', sharedSlots);
|
|
628
|
-
component.slots.toArray().forEach(slot => {
|
|
629
|
-
const sharedSlot = this.createSharedSlotBySlot(slot);
|
|
630
|
-
sharedSlots.push([sharedSlot]);
|
|
631
|
-
});
|
|
632
|
-
this.syncComponentSlots(sharedSlots, component);
|
|
633
|
-
this.syncComponentState(sharedComponent, component);
|
|
634
|
-
return sharedComponent;
|
|
635
|
-
}
|
|
636
|
-
createSharedSlotBySlot(slot) {
|
|
637
|
-
const sharedSlot = new yjs.Map();
|
|
638
|
-
sharedSlot.set('schema', slot.schema);
|
|
639
|
-
sharedSlot.set('state', slot.state);
|
|
640
|
-
const sharedContent = new yjs.Text();
|
|
641
|
-
sharedSlot.set('content', sharedContent);
|
|
642
|
-
let offset = 0;
|
|
643
|
-
slot.toDelta().forEach(i => {
|
|
644
|
-
let formats = {};
|
|
645
|
-
if (i.formats) {
|
|
646
|
-
i.formats.forEach(item => {
|
|
647
|
-
formats[item[0].name] = item[1];
|
|
648
|
-
});
|
|
649
|
-
}
|
|
650
|
-
else {
|
|
651
|
-
formats = null;
|
|
652
|
-
}
|
|
653
|
-
if (typeof i.insert === 'string') {
|
|
654
|
-
sharedContent.insert(offset, i.insert, formats);
|
|
655
|
-
}
|
|
656
|
-
else {
|
|
657
|
-
const sharedComponent = this.createSharedComponentByComponent(i.insert);
|
|
658
|
-
sharedContent.insertEmbed(offset, sharedComponent, formats);
|
|
659
|
-
}
|
|
660
|
-
offset += i.insert.length;
|
|
661
|
-
});
|
|
662
|
-
slot.getAttributes().forEach(item => {
|
|
663
|
-
sharedContent.setAttribute(item[0].name, item[1]);
|
|
664
|
-
});
|
|
665
|
-
this.syncSlotContent(sharedContent, slot);
|
|
666
|
-
this.syncSlotState(sharedSlot, slot);
|
|
667
|
-
return sharedSlot;
|
|
668
|
-
}
|
|
669
|
-
createComponentBySharedComponent(yMap) {
|
|
670
|
-
const sharedSlots = yMap.get('slots');
|
|
671
|
-
const slots = [];
|
|
672
|
-
sharedSlots.forEach(sharedSlot => {
|
|
673
|
-
const slot = this.createSlotBySharedSlot(sharedSlot);
|
|
674
|
-
slots.push(slot);
|
|
675
|
-
});
|
|
676
|
-
const name = yMap.get('name');
|
|
677
|
-
const state = yMap.get('state');
|
|
678
|
-
const instance = this.registry.createComponentByData(name, {
|
|
679
|
-
state,
|
|
680
|
-
slots
|
|
681
|
-
});
|
|
682
|
-
if (instance) {
|
|
683
|
-
instance.slots.toArray().forEach((slot, index) => {
|
|
684
|
-
let sharedSlot = sharedSlots.get(index);
|
|
685
|
-
if (!sharedSlot) {
|
|
686
|
-
sharedSlot = this.createSharedSlotBySlot(slot);
|
|
687
|
-
sharedSlots.push([sharedSlot]);
|
|
688
|
-
}
|
|
689
|
-
this.syncSlotState(sharedSlot, slot);
|
|
690
|
-
this.syncSlotContent(sharedSlot.get('content'), slot);
|
|
691
|
-
});
|
|
692
|
-
return instance;
|
|
693
|
-
}
|
|
694
|
-
throw collaborateErrorFn(`cannot find component factory \`${name}\`.`);
|
|
695
|
-
}
|
|
696
|
-
createSlotBySharedSlot(sharedSlot) {
|
|
697
|
-
const content = sharedSlot.get('content');
|
|
698
|
-
const delta = content.toDelta();
|
|
699
|
-
const slot = this.registry.createSlot({
|
|
700
|
-
schema: sharedSlot.get('schema'),
|
|
701
|
-
state: sharedSlot.get('state'),
|
|
702
|
-
attributes: {},
|
|
703
|
-
formats: {},
|
|
704
|
-
content: []
|
|
705
|
-
});
|
|
706
|
-
const attrs = content.getAttributes();
|
|
707
|
-
Object.keys(attrs).forEach(key => {
|
|
708
|
-
const attribute = this.registry.getAttribute(key);
|
|
709
|
-
if (attribute) {
|
|
710
|
-
slot.setAttribute(attribute, attrs[key]);
|
|
711
|
-
}
|
|
712
|
-
});
|
|
713
|
-
for (const action of delta) {
|
|
714
|
-
if (action.insert) {
|
|
715
|
-
if (typeof action.insert === 'string') {
|
|
716
|
-
const formats = remoteFormatsToLocal(this.registry, action.attributes);
|
|
717
|
-
slot.insert(action.insert, formats);
|
|
718
|
-
}
|
|
719
|
-
else {
|
|
720
|
-
const sharedComponent = action.insert;
|
|
721
|
-
const component = this.createComponentBySharedComponent(sharedComponent);
|
|
722
|
-
slot.insert(component, remoteFormatsToLocal(this.registry, action.attributes));
|
|
723
|
-
this.syncComponentSlots(sharedComponent.get('slots'), component);
|
|
724
|
-
this.syncComponentState(sharedComponent, component);
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
else {
|
|
728
|
-
throw collaborateErrorFn('unexpected delta action.');
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
return slot;
|
|
732
|
-
}
|
|
733
|
-
cleanSubscriptionsBySlot(slot) {
|
|
734
|
-
this.contentMap.delete(slot);
|
|
735
|
-
[this.contentSyncCaches.get(slot), this.slotStateSyncCaches.get(slot)].forEach(fn => {
|
|
736
|
-
if (fn) {
|
|
737
|
-
fn();
|
|
738
|
-
}
|
|
739
|
-
});
|
|
740
|
-
slot.sliceContent().forEach(i => {
|
|
741
|
-
if (typeof i !== 'string') {
|
|
742
|
-
this.cleanSubscriptionsByComponent(i);
|
|
743
|
-
}
|
|
744
|
-
});
|
|
745
|
-
}
|
|
746
|
-
cleanSubscriptionsByComponent(component) {
|
|
747
|
-
[this.slotsSyncCaches.get(component), this.componentStateSyncCaches.get(component)].forEach(fn => {
|
|
748
|
-
if (fn) {
|
|
749
|
-
fn();
|
|
750
|
-
}
|
|
751
|
-
});
|
|
752
|
-
component.slots.toArray().forEach(slot => {
|
|
753
|
-
this.cleanSubscriptionsBySlot(slot);
|
|
754
|
-
});
|
|
755
|
-
}
|
|
756
779
|
};
|
|
757
780
|
exports.Collaborate = __decorate([
|
|
758
781
|
core$1.Injectable(),
|
|
@@ -777,17 +800,77 @@ function remoteFormatsToLocal(registry, attrs) {
|
|
|
777
800
|
return formats;
|
|
778
801
|
}
|
|
779
802
|
|
|
803
|
+
exports.UserActivity = class UserActivity {
|
|
804
|
+
constructor(syncConnector, selection) {
|
|
805
|
+
this.syncConnector = syncConnector;
|
|
806
|
+
this.selection = selection;
|
|
807
|
+
this.stateChangeEvent = new stream.Subject();
|
|
808
|
+
this.userChangeEvent = new stream.Subject();
|
|
809
|
+
this.subscription = new stream.Subscription();
|
|
810
|
+
this.onStateChange = this.stateChangeEvent.asObservable();
|
|
811
|
+
this.onUserChange = this.userChangeEvent.asObservable();
|
|
812
|
+
}
|
|
813
|
+
init(userinfo) {
|
|
814
|
+
this.syncConnector.setLocalStateField('user', userinfo);
|
|
815
|
+
this.subscription.add(this.selection.onChange.subscribe(() => {
|
|
816
|
+
const selection = this.selection.getPaths();
|
|
817
|
+
this.syncConnector.setLocalStateField('selection', Object.assign(Object.assign({}, userinfo), { selection }));
|
|
818
|
+
}), this.syncConnector.onStateChange.subscribe((state) => {
|
|
819
|
+
const users = [];
|
|
820
|
+
const remoteSelections = [];
|
|
821
|
+
if (state.user) {
|
|
822
|
+
users.push(state.user);
|
|
823
|
+
}
|
|
824
|
+
if (state.selection) {
|
|
825
|
+
remoteSelections.push(state.selection);
|
|
826
|
+
}
|
|
827
|
+
const selections = remoteSelections.filter(i => i.id !== userinfo.id);
|
|
828
|
+
this.userChangeEvent.next(users);
|
|
829
|
+
this.stateChangeEvent.next(selections);
|
|
830
|
+
}));
|
|
831
|
+
}
|
|
832
|
+
destroy() {
|
|
833
|
+
this.subscription.unsubscribe();
|
|
834
|
+
}
|
|
835
|
+
};
|
|
836
|
+
exports.UserActivity = __decorate([
|
|
837
|
+
core$1.Injectable(),
|
|
838
|
+
__metadata("design:paramtypes", [SyncConnector,
|
|
839
|
+
core.Selection])
|
|
840
|
+
], exports.UserActivity);
|
|
841
|
+
|
|
780
842
|
class CollaborateModule {
|
|
781
|
-
constructor() {
|
|
843
|
+
constructor(config) {
|
|
844
|
+
this.config = config;
|
|
782
845
|
this.providers = [
|
|
783
846
|
exports.Collaborate,
|
|
847
|
+
exports.UserActivity,
|
|
784
848
|
{
|
|
785
849
|
provide: core.History,
|
|
786
850
|
useExisting: exports.Collaborate
|
|
851
|
+
}, {
|
|
852
|
+
provide: SyncConnector,
|
|
853
|
+
useFactory: (collab) => {
|
|
854
|
+
return this.config.createConnector(collab.yDoc);
|
|
855
|
+
},
|
|
856
|
+
deps: [exports.Collaborate]
|
|
787
857
|
}
|
|
788
858
|
];
|
|
789
859
|
}
|
|
860
|
+
setup(textbus) {
|
|
861
|
+
const connector = textbus.get(SyncConnector);
|
|
862
|
+
const userActivity = textbus.get(exports.UserActivity);
|
|
863
|
+
userActivity.init(this.config.userinfo);
|
|
864
|
+
return connector.onLoad.toPromise();
|
|
865
|
+
}
|
|
866
|
+
onDestroy(textbus) {
|
|
867
|
+
textbus.get(exports.UserActivity).destroy();
|
|
868
|
+
textbus.get(SyncConnector).onDestroy();
|
|
869
|
+
}
|
|
790
870
|
}
|
|
791
871
|
|
|
792
872
|
exports.CollaborateModule = CollaborateModule;
|
|
793
873
|
exports.CustomUndoManagerConfig = CustomUndoManagerConfig;
|
|
874
|
+
exports.HocuspocusConnector = HocuspocusConnector;
|
|
875
|
+
exports.SyncConnector = SyncConnector;
|
|
876
|
+
exports.YWebsocketConnector = YWebsocketConnector;
|