@cocreate/crdt 1.3.14 → 1.3.18

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.
@@ -1,460 +0,0 @@
1
- import * as Y from 'yjs'
2
- import { WebsocketProvider } from 'y-websocket'
3
- import { fetchUpdates, storeState, IndexeddbPersistence } from 'y-indexeddb'
4
- import crud from '@cocreate/crud-client';
5
- import CoCreateCursors from '@cocreate/cursors'
6
- import { logger } from '@cocreate/utils'
7
-
8
- let console = logger('off');
9
-
10
- class CoCreateCrdt {
11
- constructor(org, ydoc) {
12
- this.doc = ydoc;
13
- this.orgName = org;
14
- this.docs = {};
15
- this._awarenessListener = null;
16
- this.character = '_';
17
- this.listenAwereness = function(){}
18
- }
19
-
20
- createDoc(id, element) {
21
- if (!id || id == "") {
22
- return null;
23
- }
24
-
25
- let newInfo = this.parseType(id)
26
- // console.log(newInfo);
27
-
28
- const newId = newInfo.id;
29
-
30
- if (this.docs[newId]) {
31
- if (element && !this.__checkExistElement(this.docs[newId].elements, element)) {
32
- this.docs[newId].elements.push(element);
33
- }
34
-
35
- if (!this.docs[newId].types.some((type) => type === id)) {
36
- // register event
37
- this.docs[newId].types.push(id);
38
- this.registerUpdateEvent(this.docs[newId], id)
39
-
40
- }
41
- return false;
42
- }
43
-
44
- const yDoc = this.doc
45
-
46
- const url_socket = this.__getSocketUrl();
47
- //draw cursor dinamially
48
- // new UserCursor(socketProvider);
49
-
50
-
51
- var socketProvider = new WebsocketProvider(url_socket, newId, yDoc);
52
- let indexeddbProvider = null;
53
- if (newInfo.document_id != "null") {
54
- indexeddbProvider = new IndexeddbPersistence(newId, this.doc)
55
- indexeddbProvider.whenSynced.then(() => {
56
- console.log('loaded data from indexed db')
57
- })
58
- }
59
-
60
- let awareness = socketProvider.awareness;
61
-
62
- this._cursors = new Map();
63
-
64
- this._awarenessListener = event => {
65
- const f = clientId => {
66
- // if (clientId !== this.doc.clientID) {
67
- this.updateRemoteSelection(yDoc, id, yDoc.getText(id), this._cursors, clientId, awareness)
68
- // }
69
- }
70
- event.added.forEach(f)
71
- event.removed.forEach(f)
72
- event.updated.forEach(f)
73
- }
74
-
75
- awareness.on('change', this._awarenessListener);
76
- /*
77
- awareness.getStates().forEach((aw, clientId) => {
78
- console.log("Update --")
79
- })
80
- */
81
-
82
- this.docs[newId] = {
83
- id: newId,
84
- doc: yDoc,
85
- socket: socketProvider,
86
- awareness: awareness,
87
- elements: element ? [element] : [],
88
- types: [id],
89
- indexeddb: indexeddbProvider
90
- }
91
- this.registerUpdateEvent(this.docs[newId], id)
92
-
93
- return true;
94
- }
95
-
96
- registerUpdateEvent(docObject, id) {
97
- const yDoc = docObject.doc;
98
- const shardType = yDoc.getText(id)
99
- let _this = this;
100
-
101
- shardType.observe((event) => {
102
- _this.__setTypeObserveEvent(event, docObject.elements, id);
103
- })
104
- }
105
-
106
- __checkExistElement(elements, element) {
107
- for (var i = 0; i < elements.length; i++) {
108
- if (elements[i].isSameNode(element)) {
109
- return true;
110
- }
111
- }
112
- return false;
113
- }
114
-
115
- __getSocketUrl() {
116
- console.log("get_socket url")
117
- let w_location = window.location || window.parent.location;
118
- let w_protocol = w_location.protocol;
119
- let w_host = w_location.host;
120
- if (w_location.protocol === "about:") {
121
- w_protocol = w_location.protocol;
122
- w_host = document.referrer;
123
- }
124
- let protocol = w_protocol === 'http:' ? 'ws' : 'wss';
125
-
126
- let url_socket = `${protocol}://${w_host}:8080/`;
127
- if (window.config && window.config.host) {
128
- if (window.config.host.includes("://")) {
129
- url_socket = `${window.config.host}/`;
130
- } else {
131
- url_socket = `${protocol}://${window.config.host}/`;
132
- }
133
- }
134
-
135
- console.log(url_socket)
136
- url_socket += "crdt/";
137
-
138
- return url_socket;
139
-
140
- }
141
-
142
- __setTypeObserveEvent(event, elements, id) {
143
- console.log('set crdt event .', event.delta)
144
- if (!id) return;
145
-
146
- const eventDelta = event.delta;
147
-
148
- if (eventDelta.length == 0) {
149
- return;
150
- }
151
- const info = JSON.parse(atob(id));
152
- let is_save_value = false
153
-
154
- // let is_crud = eventDelta.attributes && eventDelta.attributes.crud === false ? false : true;
155
-
156
- const wholestring = event.target.toString()
157
- const store_event = new CustomEvent('store-content-db', {
158
- detail: wholestring
159
- })
160
-
161
-
162
- const update_event = new CustomEvent('cocreate-crdt-update', {
163
- detail: eventDelta,
164
- })
165
- const update_event2 = new CustomEvent('cocreate-crdt-update', {
166
- detail: {eventDelta,...info},
167
- })
168
-
169
- window.dispatchEvent(update_event2);
170
- elements.forEach((el) => {
171
- if (crud.isReadAttr(el) && el.getAttribute('name') === info.name) {
172
- el.dispatchEvent(update_event)
173
- }
174
- })
175
-
176
-
177
- if (typeof info !== 'object') {
178
- return;
179
- }
180
-
181
- if (event.transaction.local) {
182
- if (elements.length == 0) {
183
- is_save_value = true;
184
- }
185
- elements.forEach((el) => {
186
- if (crud.isSaveAttr(el) && el.getAttribute('name') === info.name && info.document_id != "null") {
187
- is_save_value = true;
188
- el.dispatchEvent(store_event)
189
- }
190
- })
191
-
192
- // if (is_save_value && is_crud) {
193
- // crud.updateDocument({
194
- // collection: info.collection,
195
- // document_id: info.document_id,
196
- // data: {
197
- // [info.name]: wholestring
198
- // },
199
- // metadata: 'yjs-change'
200
- // })
201
- // }
202
- } else {
203
-
204
- }
205
- }
206
-
207
- deleteDoc(id) {
208
- const info = this.parseType(id)
209
- if (this.docs[info.id]) {
210
- delete this.docs[info.id];
211
- }
212
- }
213
-
214
- generateDocName(collection, document_id, name) {
215
- const info = {org: this.orgName, collection, document_id, name}
216
- return btoa(JSON.stringify(info));
217
- // return this.orgName + "_" + collection + "_" + document_id + "_" + name;
218
- }
219
-
220
- insertData(id, index, content, attribute) {
221
- const info = this.parseType(id)
222
- if (this.docs[info.id]) {
223
- if (attribute) {
224
- this.docs[info.id].doc.getText(id).insert(index, content, attribute);
225
- } else {
226
-
227
- this.docs[info.id].doc.getText(id).insert(index, content);
228
- }
229
- }
230
- }
231
-
232
- deleteData(id, index, length) {
233
- const info = this.parseType(id)
234
- if (this.docs[info.id]) {
235
- this.docs[info.id].doc.getText(id).delete(index, length);
236
- }
237
- }
238
-
239
- getWholeString(id) {
240
- const info = this.parseType(id)
241
- if (this.docs[info.id]) {
242
- // console.log("!Get data")
243
- return this.docs[info.id].doc.getText(id).toString();
244
- } else {
245
- return "--";
246
- }
247
- }
248
-
249
- updateRemoteSelection (y, cm, type, cursors, clientId, awareness) {
250
- console.log("CHANGE ---- DOCID ",this.doc.clientID,' OTHER CLIENTEID ',clientId)
251
- // ToDo: blocks character inserts
252
- if(clientId !== this.doc.clientID){
253
-
254
- //console.log("TEXT -> updateRemoteSelection ",clientId,awareness,cursor)
255
- //console.log("AW status",awareness.getLocalState())
256
- // destroy current text mark
257
- const m = cursors.get(clientId)
258
- if (m !== undefined) {
259
- m.caret.clear()
260
- if (m.sel !== null) {
261
- m.sel.clear()
262
- }
263
- cursors.delete(clientId)
264
- }
265
- // redraw caret and selection for clientId
266
- const aw = awareness.getStates().get(clientId);
267
- console.log(aw)
268
- if (aw === undefined) {
269
- console.log(" Cursor OUT ",clientId)
270
- //awareness.setLocalStateField('cursor', null);
271
- let elements = document.querySelectorAll('[id*="socket_'+clientId+'"]');
272
- elements.forEach(function (element, index, array) {
273
- element.parentNode.removeChild(element);
274
- })
275
-
276
- let sel_elements = document.querySelectorAll('[id*="sel-'+clientId+'"]');
277
- sel_elements.forEach(function (sel_element, index, array) {
278
- sel_element.parentNode.removeChild(sel_element);
279
- })
280
- /*
281
- let element = document.getElementById("socket_"+clientId)
282
- let sel_element = document.getElementById("sel-"+clientId)
283
- if(element)
284
- element.parentNode.removeChild(element);
285
- if(sel_element)
286
- sel_element.parentNode.removeChild(sel_element);
287
- */
288
- return
289
- }
290
- const user = aw.user || {}
291
- if (user.color == null) {
292
- user.color = '#ffa500'
293
- }
294
- if (user.name == null) {
295
- user.name = `User: ${clientId}`
296
- }
297
- const cursor = aw.cursor
298
- console.log("Cursor ",cursor)
299
- if (cursor == null || cursor.anchor == null || cursor.head == null) {
300
- //let element = document.getElementById("socket_"+clientId)
301
- let elements = document.querySelectorAll('[id*="socket_'+clientId+'"]');
302
- elements.forEach(function (element, index, array) {
303
- element.parentNode.removeChild(element);
304
- })
305
-
306
- //let sel_element = document.getElementById("sel-"+clientId)
307
- let sel_elements = document.querySelectorAll('[id*="sel-'+clientId+'"]');
308
- sel_elements.forEach(function (sel_element, index, array) {
309
- sel_element.parentNode.removeChild(sel_element);
310
- })
311
- return
312
- }
313
-
314
- const anchor = Y.createAbsolutePositionFromRelativePosition(Y.createRelativePositionFromJSON(cursor.anchor), y)
315
- const head = Y.createAbsolutePositionFromRelativePosition(Y.createRelativePositionFromJSON(cursor.head), y)
316
- //CoCreate.cursors.draw_cursor(1,11,12,66,{},true);
317
- //console.log("PRE Draw Cursor ")
318
- //console.log("anchor ",anchor , " head ",head,' Type ',type)
319
- //console.log("anchor Type",anchor.type === type)
320
- //console.log("anchor Type",head.type === type)
321
- //if (anchor !== null && head !== null && anchor.type === type && head.type === type) {
322
- if (anchor !== null && head !== null ) {
323
- let from, to;
324
- if (head.index < anchor.index) {
325
- from = head.index
326
- to = anchor.index
327
- } else {
328
- console.log(anchor.index)
329
- from = anchor.index
330
- to = head.index
331
- }
332
- console.log("Draw Cursor ",from,to,clientId,aw.user)
333
- let t_info = this.parseTypeName(cursor.anchor['tname']);
334
- let id_mirror = t_info.document_id + t_info.name+'--mirror-div';
335
- let json = {};
336
- let selector = '[collection="'+t_info.collection+'"][document_id="'+t_info.document_id+'"][name="'+t_info.name+'"]'
337
- selector += ':not(.codemirror):not(.quill):not(.monaco)';
338
- let elements = document.querySelectorAll(selector);
339
- let that = this;
340
- elements.forEach(function (element, index, array) {
341
- json = {
342
- element:element,
343
- startPosition:from,
344
- selector:selector,
345
- endPositon:to,
346
- clientId : clientId,
347
- user:{
348
- 'color':user.color,
349
- 'name':user.name
350
- },
351
- }
352
-
353
- console.log(json)
354
- CoCreateCursors.draw_cursor(json);
355
- //sent custom position
356
- that.listen(json);
357
- });
358
- }
359
- }
360
- }
361
-
362
- changeListenAwereness(callback){
363
- this.listenAwereness = callback;
364
- }
365
-
366
- listen(json){
367
- this.listenAwereness.apply(this,[json])
368
- }
369
-
370
- destroyObserver(id, element) {
371
- const info = this.parseType(id)
372
- this.docs[info.id].doc.getText(id).unobserve((event) => {});
373
-
374
- this.docs[info.id].socket.awareness.off('change', this._awarenessListener);
375
- }
376
-
377
- getProvider(id){
378
- const info = this.parseType(id)
379
- if (!this.docs[info.id]) {
380
- return null;
381
- }
382
- return this.docs[info.id].socket;
383
- }
384
-
385
- getType(id){
386
- const info = this.parseType(id)
387
- if (!this.docs[info.id]) {
388
- return null;
389
- }
390
- return this.docs[info.id].doc.getText(id);
391
- }
392
-
393
- setCursorNull(id){
394
- const info = this.parseType(id)
395
- if (!this.docs[info.id]) {
396
- return null;
397
- }
398
-
399
- this.docs[info.id].socket.awareness.setLocalStateField('cursor', null);
400
- }
401
-
402
- setPositionYJS(id, from, to) {
403
- const info = this.parseType(id)
404
- const type = this.getType(id);
405
- console.log("Type ",type)
406
- if (!type) {
407
- return;
408
- }
409
- var anchor = Y.createRelativePositionFromTypeIndex(type, from)
410
- var head = Y.createRelativePositionFromTypeIndex(type, to)
411
-
412
- console.log("Sending Cursor ",{
413
- anchor,
414
- head
415
- },{'to':to,'from':from,'info.id':info.id})
416
-
417
- this.docs[info.id].socket.awareness.setLocalStateField('cursor', {
418
- anchor,
419
- head
420
- })
421
- /*
422
- console.log("Cursor Send")
423
- */
424
- }
425
-
426
- //send Position Custom
427
- sendPosition(json) {
428
- let collection = json['collection'];
429
- let document_id = json['document_id'];
430
- let name = json['name'];
431
- let from = json['startPosition'];
432
- let to = json['endPositon'];
433
- let id = this.generateID(config.organization_Id, collection, document_id, name);
434
- this.setPositionYJS(id,from,to);
435
- }
436
-
437
- generateID(org, collection, document_id, name) {
438
- const info = {org, collection, document_id, name}
439
- return btoa(JSON.stringify(info));
440
- }
441
-
442
- parseTypeName(name) {
443
- const data = JSON.parse(atob(name));
444
- return data;
445
- }
446
-
447
- parseType(id) {
448
- let data = JSON.parse(atob(id));
449
-
450
- let newId = {org: data.org, collection: data.collection, document_id: data.document_id}
451
- return {
452
- id: btoa(JSON.stringify(newId)),
453
- name: data.name,
454
- document_id: data.document_id
455
- }
456
- }
457
- }
458
-
459
- export default CoCreateCrdt;
460
-