@cocreate/crdt 1.18.28 → 1.20.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/CHANGELOG.md +21 -0
- package/CoCreate.config.js +2 -2
- package/LICENSE +177 -177
- package/demo/index.html +10 -13
- package/docs/index.html +15 -15
- package/package.json +7 -7
- package/src/index.js +406 -381
- package/webpack.config.js +79 -73
package/src/index.js
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (C) 2023 CoCreate and Contributors.
|
|
3
|
+
*
|
|
4
|
+
* This program is free software: you can redistribute it and/or modify
|
|
5
|
+
* it under the terms of the GNU Affero General Public License as published
|
|
6
|
+
* by the Free Software Foundation, either version 3 of the License, or
|
|
7
|
+
* (at your option) any later version.
|
|
8
|
+
*
|
|
9
|
+
* This program is distributed in the hope that it will be useful,
|
|
10
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
* GNU Affero General Public License for more details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
15
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
********************************************************************************/
|
|
17
|
+
|
|
18
|
+
// Commercial Licensing Information:
|
|
19
|
+
// For commercial use of this software without the copyleft provisions of the AGPLv3,
|
|
20
|
+
// you must obtain a commercial license from CoCreate LLC.
|
|
21
|
+
// For details, visit <https://cocreate.app/licenses/> or contact us at sales@cocreate.app.
|
|
22
|
+
|
|
1
23
|
/*globals config, atob, btoa, localStorage, CustomEvent*/
|
|
2
24
|
import crud from '@cocreate/crud-client';
|
|
3
25
|
import message from '@cocreate/message-client';
|
|
@@ -9,446 +31,449 @@ const clientId = crud.socket.clientId || uuid.generate(12);
|
|
|
9
31
|
const checkedDb = new Map();
|
|
10
32
|
const isInit = new Map();
|
|
11
33
|
|
|
12
|
-
function init(data){
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
34
|
+
function init(data) {
|
|
35
|
+
getText(data).then(value => {
|
|
36
|
+
data.value = value;
|
|
37
|
+
data.start = 0;
|
|
38
|
+
data['clientId'] = clientId;
|
|
39
|
+
localChange(data);
|
|
40
|
+
});
|
|
19
41
|
}
|
|
20
42
|
|
|
21
43
|
async function getDoc(data) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
44
|
+
try {
|
|
45
|
+
if (['_id', 'organization_id'].includes(data.key))
|
|
46
|
+
return
|
|
47
|
+
let docName = getDocName(data);
|
|
48
|
+
let doc = docs.get(docName);
|
|
49
|
+
|
|
50
|
+
if (!doc) {
|
|
51
|
+
docs.set(docName, new Map());
|
|
52
|
+
doc = docs.get(docName);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!doc.has('changeLog')) {
|
|
56
|
+
let changeLog = [];
|
|
57
|
+
doc.set('undoLog', new Map())
|
|
58
|
+
doc.set('redoLog', new Map())
|
|
59
|
+
|
|
60
|
+
if (data.read != 'false') {
|
|
61
|
+
if (!data.newObject) {
|
|
62
|
+
let response = await crud.send({
|
|
63
|
+
method: "read.object",
|
|
64
|
+
array: "crdt-transactions",
|
|
65
|
+
filter: {
|
|
66
|
+
query: [{
|
|
67
|
+
key: 'docName',
|
|
68
|
+
operator: "$eq",
|
|
69
|
+
value: docName
|
|
70
|
+
}]
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
if (response.object && response.object[0] && response.object[0].changeLog) {
|
|
74
|
+
changeLog = response.object[0].changeLog;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
doc.set('changeLog', changeLog);
|
|
78
|
+
await generateText(data, true);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else if (!doc.has('text')) {
|
|
82
|
+
await generateText(data, false);
|
|
83
|
+
}
|
|
84
|
+
return doc;
|
|
85
|
+
}
|
|
86
|
+
catch (e) {
|
|
87
|
+
console.log('Invalid param', e);
|
|
88
|
+
}
|
|
66
89
|
}
|
|
67
90
|
|
|
68
91
|
String.prototype.customSplice = function (index, absIndex, string) {
|
|
69
|
-
return this.slice(0, index) + string+ this.slice(index + Math.abs(absIndex));
|
|
92
|
+
return this.slice(0, index) + string + this.slice(index + Math.abs(absIndex));
|
|
70
93
|
};
|
|
71
94
|
|
|
72
95
|
async function generateText(data, flag) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
96
|
+
try {
|
|
97
|
+
let doc = docs.get(getDocName(data))
|
|
98
|
+
|
|
99
|
+
let string = '';
|
|
100
|
+
let changeLog = doc.get('changeLog');
|
|
101
|
+
for (let change of changeLog) {
|
|
102
|
+
if (change || change !== null) {
|
|
103
|
+
string = string.customSplice(change.start, change.length, change.value);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (string === '' && data.read !== 'false') {
|
|
107
|
+
string = await checkDb(data, flag);
|
|
108
|
+
}
|
|
109
|
+
doc.set('text', string);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
catch (e) {
|
|
113
|
+
console.error(e);
|
|
114
|
+
}
|
|
92
115
|
}
|
|
93
116
|
|
|
94
117
|
async function checkDb(data, flag) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
118
|
+
let { array, object, key } = data;
|
|
119
|
+
if (checkedDb.get(`${array}${object}${key}`)) return;
|
|
120
|
+
checkedDb.set(`${array}${object}${key}`, true);
|
|
121
|
+
|
|
122
|
+
let string = ''
|
|
123
|
+
if (data.newObject)
|
|
124
|
+
string = data.newObject
|
|
125
|
+
else {
|
|
126
|
+
let response = await crud.send({ method: 'read.object', array, object: { _id: object, key } });
|
|
127
|
+
string = crud.getValueFromObject(response.object[0], key);
|
|
128
|
+
}
|
|
129
|
+
if (string && typeof string !== 'string')
|
|
130
|
+
string = ""
|
|
131
|
+
if (string && typeof string === 'string' && flag != false) {
|
|
132
|
+
data.value = string;
|
|
133
|
+
data.start = 0;
|
|
134
|
+
data.clientId = clientId;
|
|
135
|
+
insertChange(data);
|
|
136
|
+
}
|
|
137
|
+
return string || '';
|
|
115
138
|
}
|
|
116
139
|
|
|
117
140
|
function insertChange(data, flag) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
141
|
+
let docName = getDocName(data);
|
|
142
|
+
let doc = docs.get(docName);
|
|
143
|
+
let changeLog = doc.get('changeLog');
|
|
144
|
+
if (!changeLog)
|
|
145
|
+
return
|
|
146
|
+
|
|
147
|
+
let type = 'insert';
|
|
148
|
+
if (data.start == undefined) return;
|
|
149
|
+
if (!data.value)
|
|
150
|
+
type = 'delete';
|
|
151
|
+
|
|
152
|
+
let change = {
|
|
153
|
+
datetime: data.datetime || new Date().toISOString(),
|
|
154
|
+
value: data.value || '',
|
|
155
|
+
start: data.start,
|
|
156
|
+
end: data.end,
|
|
157
|
+
length: data.length || 0,
|
|
158
|
+
clientId: data.clientId || clientId,
|
|
159
|
+
user_id: data.user_id || localStorage.getItem("user_id"),
|
|
160
|
+
type
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
let lastChange = changeLog[changeLog.length - 1];
|
|
164
|
+
if (lastChange && change.datetime && lastChange.datetime) {
|
|
165
|
+
if (change.datetime < lastChange.datetime) {
|
|
166
|
+
// TODO: insert change at index position and update start and end postions for every element after insert
|
|
167
|
+
console.log('requires changeLog rebuild');
|
|
168
|
+
}
|
|
169
|
+
if (flag != 'replace') {
|
|
170
|
+
if (lastChange && change.start == lastChange.start) {
|
|
171
|
+
let date1 = new Date(lastChange.datetime);
|
|
172
|
+
let date2 = new Date(change.datetime);
|
|
173
|
+
let diff = date2.getTime() - date1.getTime();
|
|
174
|
+
if (diff < 500) {
|
|
175
|
+
if (change.value.length == 1) {
|
|
176
|
+
change.start = lastChange.start + lastChange.value.length;
|
|
177
|
+
data.start = change.start;
|
|
178
|
+
}
|
|
179
|
+
if (change.length == 1) {
|
|
180
|
+
change.start = lastChange.start - lastChange.length;
|
|
181
|
+
data.start = change.start;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (!change || change == null) {
|
|
188
|
+
console.log('null change')
|
|
189
|
+
return
|
|
190
|
+
}
|
|
191
|
+
let string = doc.get('text') || '';
|
|
192
|
+
if (change.length > 0)
|
|
193
|
+
change.removedValue = string.substring(change.start, change.length);
|
|
194
|
+
|
|
195
|
+
if (flag == 'replace') {
|
|
196
|
+
// TODO get current string and create new changeLog array then push new change
|
|
197
|
+
changeLog = [change];
|
|
198
|
+
doc.set('changeLog', changeLog)
|
|
199
|
+
} else {
|
|
200
|
+
changeLog.push(change);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
doc.set('text', string.customSplice(change.start, change.length, change.value));
|
|
204
|
+
string = doc.get('text');
|
|
205
|
+
|
|
206
|
+
if (!data.clientId) {
|
|
207
|
+
data['datetime'] = change.datetime;
|
|
208
|
+
data['clientId'] = change.clientId;
|
|
209
|
+
|
|
210
|
+
broadcastChange(data);
|
|
211
|
+
localChange(data, string);
|
|
212
|
+
}
|
|
213
|
+
else
|
|
214
|
+
localChange(data, string);
|
|
215
|
+
|
|
216
|
+
if (data.clientId == clientId && data.save != "false")
|
|
217
|
+
persistChange(data);
|
|
195
218
|
}
|
|
196
219
|
|
|
197
|
-
function broadcastChange(data){
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
220
|
+
function broadcastChange(data) {
|
|
221
|
+
message.send({
|
|
222
|
+
room: "",
|
|
223
|
+
broadcastSender: 'false',
|
|
224
|
+
broadcastBrowser: 'once',
|
|
225
|
+
message: "crdt",
|
|
226
|
+
data
|
|
227
|
+
});
|
|
205
228
|
}
|
|
206
229
|
|
|
207
230
|
function localChange(data, string) {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
231
|
+
const localChange = new CustomEvent('cocreate-crdt-update', {
|
|
232
|
+
detail: { ...data, string },
|
|
233
|
+
});
|
|
234
|
+
window.dispatchEvent(localChange);
|
|
212
235
|
}
|
|
213
236
|
|
|
214
237
|
function persistChange(data) {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
238
|
+
let docName = getDocName(data);
|
|
239
|
+
let doc = docs.get(docName);
|
|
240
|
+
let changeLog = doc.get('changeLog');
|
|
241
|
+
let text = doc.get('text');
|
|
242
|
+
let Data = {
|
|
243
|
+
method: 'update.object',
|
|
244
|
+
array: 'crdt-transactions',
|
|
245
|
+
object: {
|
|
246
|
+
_id: data.object,
|
|
247
|
+
docName,
|
|
248
|
+
changeLog,
|
|
249
|
+
text,
|
|
250
|
+
crud: {
|
|
251
|
+
array: data.array,
|
|
252
|
+
object: data.object,
|
|
253
|
+
key: data.key
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
upsert: true,
|
|
257
|
+
namespace: data.namespace,
|
|
258
|
+
room: data.room,
|
|
259
|
+
broadcast: data.broadcast,
|
|
260
|
+
broadcastSender: data.broadcastSender,
|
|
261
|
+
metadata: 'crdt-change'
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
crud.send(Data);
|
|
241
265
|
}
|
|
242
266
|
|
|
243
|
-
message.listen('crdt', function(response) {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
267
|
+
message.listen('crdt', function (response) {
|
|
268
|
+
let data = response.data
|
|
269
|
+
let docName = getDocName(data);
|
|
270
|
+
let doc = docs.get(docName);
|
|
247
271
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
272
|
+
if (doc) {
|
|
273
|
+
if (data.clientId !== clientId) {
|
|
274
|
+
insertChange(data);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
253
277
|
});
|
|
254
278
|
|
|
255
|
-
crud.listen('sync', function(data) {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
279
|
+
crud.listen('sync', function (data) {
|
|
280
|
+
if (data.array.includes('crdt-transactions')) {
|
|
281
|
+
if (data.object && data.object[0]) {
|
|
282
|
+
let Data = data.object[0];
|
|
283
|
+
let docName = Data.docName;
|
|
284
|
+
let doc = docs.get(docName);
|
|
285
|
+
if (doc && Data.crud) {
|
|
286
|
+
Data.crud.value = Data.text
|
|
287
|
+
Data.crud.start = 0
|
|
288
|
+
Data.crud.length = doc.get('text').length
|
|
289
|
+
|
|
290
|
+
doc.set('changeLog', Data.changeLog)
|
|
291
|
+
doc.set('text', Data.text)
|
|
292
|
+
// TODO: compare modified dates to check if arrays need to merged and orderd by date or if we just use server
|
|
293
|
+
localChange(Data.crud, Data.text)
|
|
294
|
+
console.log('crdtSync')
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
274
298
|
});
|
|
275
299
|
|
|
276
300
|
|
|
277
301
|
/*
|
|
278
302
|
crdt.getText({
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
303
|
+
array: 'modules',
|
|
304
|
+
object: '5e4802ce3ed96d38e71fc7e5',
|
|
305
|
+
key: 'name'
|
|
282
306
|
})
|
|
283
307
|
*/
|
|
284
308
|
async function getText(data) {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
309
|
+
try {
|
|
310
|
+
let doc = await getDoc(data);
|
|
311
|
+
if (doc) {
|
|
312
|
+
let value = doc.get('text')
|
|
313
|
+
return value;
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
console.log('undefined')
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
catch (e) {
|
|
320
|
+
console.error(e);
|
|
321
|
+
return "";
|
|
322
|
+
}
|
|
299
323
|
}
|
|
300
324
|
|
|
301
325
|
|
|
302
326
|
/*
|
|
303
327
|
crdt.replaceText({
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
328
|
+
array: "module",
|
|
329
|
+
object: "",
|
|
330
|
+
key: "",
|
|
331
|
+
value: "",
|
|
332
|
+
crud: true | false,
|
|
333
|
+
element: dom_object,
|
|
334
|
+
metadata: "xxxx"
|
|
311
335
|
})
|
|
312
336
|
*/
|
|
313
337
|
async function replaceText(data) {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
338
|
+
try {
|
|
339
|
+
let doc = await getDoc(data);
|
|
340
|
+
if (doc) {
|
|
341
|
+
let oldValue = await getText(data);
|
|
342
|
+
if (oldValue)
|
|
343
|
+
data.length = oldValue.length;
|
|
344
|
+
else
|
|
345
|
+
data.length = 0;
|
|
346
|
+
|
|
347
|
+
data.start = 0;
|
|
348
|
+
updateText(data, 'replace');
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
catch (e) {
|
|
352
|
+
console.error(e);
|
|
353
|
+
}
|
|
330
354
|
}
|
|
331
355
|
|
|
332
356
|
/*
|
|
333
357
|
crdt.updateText({
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
358
|
+
array: 'module_activities',
|
|
359
|
+
object: '5e4802ce3ed96d38e71fc7e5',
|
|
360
|
+
key: 'name',
|
|
361
|
+
value: 'T',
|
|
362
|
+
start: '8',
|
|
363
|
+
attributes: {bold: true}
|
|
364
|
+
length: 2, // length is used to define charcters that will be deleted
|
|
341
365
|
})
|
|
342
366
|
*/
|
|
343
367
|
async function updateText(data, flag) {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
368
|
+
let doc = await getDoc(data);
|
|
369
|
+
if (doc) {
|
|
370
|
+
|
|
371
|
+
insertChange(data, flag);
|
|
372
|
+
|
|
373
|
+
if (data.crud != 'false' && data.save != 'false') {
|
|
374
|
+
let wholestring = await getText(data);
|
|
375
|
+
crud.send({
|
|
376
|
+
method: 'update.object',
|
|
377
|
+
array: data.array,
|
|
378
|
+
object: {
|
|
379
|
+
_id: data.object,
|
|
380
|
+
[data.key]: wholestring
|
|
381
|
+
},
|
|
382
|
+
upsert: data.upsert,
|
|
383
|
+
namespace: data.namespace,
|
|
384
|
+
room: data.room,
|
|
385
|
+
broadcast: data.broadcast,
|
|
386
|
+
broadcastSender: data.broadcastSender,
|
|
387
|
+
metadata: 'crdt-updateobject'
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
}
|
|
366
391
|
}
|
|
367
392
|
|
|
368
|
-
function createChange(data, change){
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
393
|
+
function createChange(data, change) {
|
|
394
|
+
if (change.value && change.length == 0) {
|
|
395
|
+
change.length = change.value.length
|
|
396
|
+
change.removedValue = change.value
|
|
397
|
+
change.value = '';
|
|
398
|
+
change.type = 'delete';
|
|
399
|
+
} else {
|
|
400
|
+
change.value = change.removedValue
|
|
401
|
+
change.length = 0;
|
|
402
|
+
change.type = 'insert';
|
|
403
|
+
}
|
|
404
|
+
data = { ...data, ...change };
|
|
405
|
+
delete data.clientId
|
|
406
|
+
delete data.datetime
|
|
407
|
+
delete change.clientId
|
|
408
|
+
delete change.datetime
|
|
409
|
+
return { data, change }
|
|
385
410
|
}
|
|
386
411
|
|
|
387
|
-
function undoText(data){
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
412
|
+
function undoText(data) {
|
|
413
|
+
let docName = getDocName(data);
|
|
414
|
+
let doc = docs.get(docName);
|
|
415
|
+
let changeLog = doc.get('changeLog');
|
|
416
|
+
let undoLog = doc.get('undoLog')
|
|
417
|
+
|
|
418
|
+
for (let index = changeLog.length - 1; index >= 0; index--) {
|
|
419
|
+
let change = Object.assign({}, changeLog[index]);
|
|
420
|
+
if (change && change.clientId == clientId) {
|
|
421
|
+
let log = undoLog.get(index)
|
|
422
|
+
if (!log) {
|
|
423
|
+
if (log != 'undo') {
|
|
424
|
+
undoLog.set(index, 'undo')
|
|
425
|
+
change.index = changeLog.length += 1;
|
|
426
|
+
let updated = createChange(data, change);
|
|
427
|
+
undoLog.set(updated.change.index, updated.change)
|
|
428
|
+
updateText(updated.data)
|
|
429
|
+
return
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
409
434
|
}
|
|
410
435
|
|
|
411
|
-
function redoText(data){
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
436
|
+
function redoText(data) {
|
|
437
|
+
let docName = getDocName(data);
|
|
438
|
+
let doc = docs.get(docName);
|
|
439
|
+
let redoLog = doc.get('redoLog')
|
|
440
|
+
let undoLog = Array.from(doc.get('undoLog').values());
|
|
441
|
+
|
|
442
|
+
for (let index = undoLog.length - 1; index >= 0; index--) {
|
|
443
|
+
let change = Object.assign({}, undoLog[index]);
|
|
444
|
+
if (change && change != 'undo') {
|
|
445
|
+
let log = redoLog.get(change.index)
|
|
446
|
+
|
|
447
|
+
if (!log) {
|
|
448
|
+
if (log != 'redo') {
|
|
449
|
+
let updated = createChange(data, change);
|
|
450
|
+
redoLog.set(updated.change.index, updated.change)
|
|
451
|
+
updateText(updated.data)
|
|
452
|
+
return
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
432
457
|
}
|
|
433
458
|
|
|
434
459
|
async function viewVersion(data) {
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
460
|
+
try {
|
|
461
|
+
let docName = getDocName(data);
|
|
462
|
+
let doc = docs.get(docName);
|
|
463
|
+
let changeLog = doc.get('changeLog');
|
|
464
|
+
let string = '';
|
|
465
|
+
|
|
466
|
+
let log = changeLog.slice(0, data.version)
|
|
467
|
+
for (let change of log) {
|
|
468
|
+
if (change || change !== null) {
|
|
469
|
+
string = string.customSplice(change.start, change.length, change.value);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
return { ...data, ...string }
|
|
473
|
+
}
|
|
474
|
+
catch (e) {
|
|
475
|
+
console.error(e);
|
|
476
|
+
}
|
|
452
477
|
}
|
|
453
478
|
|
|
454
479
|
|
|
@@ -465,7 +490,7 @@ async function viewVersion(data) {
|
|
|
465
490
|
// }
|
|
466
491
|
|
|
467
492
|
function getDocName(data) {
|
|
468
|
-
|
|
493
|
+
return `${data.array}${data.object}${data.key}`;
|
|
469
494
|
}
|
|
470
495
|
|
|
471
496
|
export default { init, getText, updateText, replaceText, undoText, redoText, viewVersion };
|