@cocreate/crdt 1.18.28 → 1.19.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/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
- getText(data).then(value => {
14
- data.value = value;
15
- data.start = 0;
16
- data['clientId'] = clientId;
17
- localChange(data);
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
- try {
23
- if (['_id', 'organization_id'].includes(data.name))
24
- return
25
- let docName = getDocName(data);
26
- let doc = docs.get(docName);
27
-
28
- if (!doc) {
29
- docs.set(docName, new Map());
30
- doc = docs.get(docName);
31
- }
32
-
33
- if (!doc.has('changeLog')) {
34
- let changeLog = [];
35
- doc.set('undoLog', new Map())
36
- doc.set('redoLog', new Map())
37
-
38
- if (data.read != 'false') {
39
- if (!data.newDocument) {
40
- let response = await crud.readDocument({
41
- collection: "crdt-transactions",
42
- filter: {
43
- query: [{
44
- name: 'docName',
45
- operator: "$eq",
46
- value: docName
47
- }]
48
- }
49
- });
50
- if (response.document && response.document[0] && response.document[0].changeLog) {
51
- changeLog = response.document[0].changeLog;
52
- }
53
- }
54
- doc.set('changeLog', changeLog);
55
- await generateText(data, true);
56
- }
57
- }
58
- else if (!doc.has('text')){
59
- await generateText(data, false);
60
- }
61
- return doc;
62
- }
63
- catch (e) {
64
- console.log('Invalid param', e);
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
- try {
74
- let doc = docs.get(getDocName(data))
75
-
76
- let string = '';
77
- let changeLog = doc.get('changeLog');
78
- for (let change of changeLog) {
79
- if (change || change !== null ) {
80
- string = string.customSplice(change.start, change.length, change.value);
81
- }
82
- }
83
- if (string === '' && data.read !== 'false') {
84
- string = await checkDb(data, flag);
85
- }
86
- doc.set('text', string);
87
- return;
88
- }
89
- catch (e) {
90
- console.error(e);
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
- let { collection, document_id, name } = data;
96
- if (checkedDb.get(`${collection}${document_id}${name}`)) return;
97
- checkedDb.set(`${collection}${document_id}${name}`, true);
98
-
99
- let string = ''
100
- if (data.newDocument)
101
- string = data.newDocument
102
- else {
103
- let response = await crud.readDocument({ collection, document: {_id: document_id, name}});
104
- string = crud.getValueFromObject(response.document[0], name);
105
- }
106
- if (string && typeof string !== 'string')
107
- string = ""
108
- if (string && typeof string === 'string' && flag != false) {
109
- data.value = string;
110
- data.start = 0;
111
- data.clientId = clientId;
112
- insertChange(data);
113
- }
114
- return string || '';
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
- let docName = getDocName(data);
119
- let doc = docs.get(docName);
120
- let changeLog = doc.get('changeLog');
121
- if (!changeLog)
122
- return
123
-
124
- let type = 'insert';
125
- if (data.start == undefined) return;
126
- if (!data.value)
127
- type = 'delete';
128
-
129
- let change = {
130
- datetime: data.datetime || new Date().toISOString(),
131
- value: data.value || '',
132
- start: data.start,
133
- end: data.end,
134
- length: data.length || 0,
135
- clientId: data.clientId || clientId,
136
- user_id: data.user_id || localStorage.getItem("user_id"),
137
- type
138
- };
139
-
140
- let lastChange = changeLog[changeLog.length - 1];
141
- if (lastChange && change.datetime && lastChange.datetime){
142
- if (change.datetime < lastChange.datetime){
143
- // TODO: insert change at index position and update start and end postions for every element after insert
144
- console.log('requires changeLog rebuild');
145
- }
146
- if (flag != 'replace') {
147
- if (lastChange && change.start == lastChange.start) {
148
- let date1 = new Date(lastChange.datetime);
149
- let date2 = new Date(change.datetime);
150
- let diff = date2.getTime() - date1.getTime();
151
- if (diff < 500) {
152
- if (change.value.length == 1) {
153
- change.start = lastChange.start + lastChange.value.length;
154
- data.start = change.start;
155
- }
156
- if (change.length == 1) {
157
- change.start = lastChange.start - lastChange.length;
158
- data.start = change.start;
159
- }
160
- }
161
- }
162
- }
163
- }
164
- if (!change || change == null ) {
165
- console.log('null change')
166
- return
167
- }
168
- let string = doc.get('text') || '';
169
- if (change.length > 0)
170
- change.removedValue = string.substring(change.start, change.length);
171
-
172
- if (flag == 'replace') {
173
- // TODO get current string and create new changeLog array then push new change
174
- changeLog = [change];
175
- doc.set('changeLog', changeLog)
176
- } else {
177
- changeLog.push(change);
178
- }
179
-
180
- doc.set('text', string.customSplice(change.start, change.length, change.value));
181
- string = doc.get('text');
182
-
183
- if (!data.clientId){
184
- data['datetime'] = change.datetime;
185
- data['clientId'] = change.clientId;
186
-
187
- broadcastChange(data);
188
- localChange(data, string);
189
- }
190
- else
191
- localChange(data, string);
192
-
193
- if (data.clientId == clientId && data.save != "false")
194
- persistChange(data);
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
- message.send({
199
- room: "",
200
- broadcastSender: 'false',
201
- broadcastBrowser: 'once',
202
- message: "crdt",
203
- data
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
- const localChange = new CustomEvent('cocreate-crdt-update', {
209
- detail: { ...data, string },
210
- });
211
- window.dispatchEvent(localChange);
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
- let docName = getDocName(data);
216
- let doc = docs.get(docName);
217
- let changeLog = doc.get('changeLog');
218
- let text = doc.get('text');
219
- let Data = {
220
- collection: 'crdt-transactions',
221
- document: {
222
- _id: data.document_id,
223
- docName,
224
- changeLog,
225
- text,
226
- crud: {
227
- collection: data.collection,
228
- document_id: data.document_id,
229
- name: data.name
230
- }
231
- },
232
- upsert: true,
233
- namespace: data.namespace,
234
- room: data.room,
235
- broadcast: data.broadcast,
236
- broadcastSender: data.broadcastSender,
237
- metadata: 'crdt-change'
238
- }
239
-
240
- crud.updateDocument(Data);
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
- let data = response.data
245
- let docName = getDocName(data);
246
- let doc = docs.get(docName);
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
- if (doc){
249
- if (data.clientId !== clientId){
250
- insertChange(data);
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
- if (data.collection.includes('crdt-transactions')) {
257
- if (data.document && data.document[0]) {
258
- let Data = data.document[0];
259
- let docName = Data.docName;
260
- let doc = docs.get(docName);
261
- if (doc && Data.crud) {
262
- Data.crud.value = Data.text
263
- Data.crud.start = 0
264
- Data.crud.length = doc.get('text').length
265
-
266
- doc.set('changeLog', Data.changeLog)
267
- doc.set('text', Data.text)
268
- // TODO: compare modified dates to check if arrays need to merged and orderd by date or if we just use server
269
- localChange(Data.crud, Data.text)
270
- console.log('crdtSync')
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
- collection: 'modules',
280
- document_id: '5e4802ce3ed96d38e71fc7e5',
281
- name: 'name'
303
+ array: 'modules',
304
+ object: '5e4802ce3ed96d38e71fc7e5',
305
+ key: 'name'
282
306
  })
283
307
  */
284
308
  async function getText(data) {
285
- try {
286
- let doc = await getDoc(data);
287
- if (doc) {
288
- let value = doc.get('text')
289
- return value;
290
- }
291
- else {
292
- console.log('undefined')
293
- }
294
- }
295
- catch (e) {
296
- console.error(e);
297
- return "";
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
- collection: "module",
305
- document_id: "",
306
- name: "",
307
- value: "",
308
- crud: true | false,
309
- element: dom_object,
310
- metadata: "xxxx"
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
- try {
315
- let doc = await getDoc(data);
316
- if (doc) {
317
- let oldValue = await getText(data);
318
- if (oldValue)
319
- data.length = oldValue.length;
320
- else
321
- data.length = 0;
322
-
323
- data.start = 0;
324
- updateText(data, 'replace');
325
- }
326
- }
327
- catch (e) {
328
- console.error(e);
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
- collection: 'module_activities',
335
- document_id: '5e4802ce3ed96d38e71fc7e5',
336
- name: 'name',
337
- value: 'T',
338
- start: '8',
339
- attributes: {bold: true}
340
- length: 2, // length is used to define charcters that will be deleted
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
- let doc = await getDoc(data);
345
- if (doc) {
346
-
347
- insertChange(data, flag);
348
-
349
- if (data.crud != 'false' && data.save != 'false') {
350
- let wholestring = await getText(data);
351
- crud.updateDocument({
352
- collection: data.collection,
353
- document: {
354
- _id: data.document_id,
355
- [data.name]: wholestring
356
- },
357
- upsert: data.upsert,
358
- namespace: data.namespace,
359
- room: data.room,
360
- broadcast: data.broadcast,
361
- broadcastSender: data.broadcastSender,
362
- metadata: 'crdt-updateDocument'
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
- if (change.value && change.length == 0) {
370
- change.length = change.value.length
371
- change.removedValue = change.value
372
- change.value = '';
373
- change.type = 'delete';
374
- } else {
375
- change.value = change.removedValue
376
- change.length = 0;
377
- change.type = 'insert';
378
- }
379
- data = {...data, ...change};
380
- delete data.clientId
381
- delete data.datetime
382
- delete change.clientId
383
- delete change.datetime
384
- return {data, change}
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
- let docName = getDocName(data);
389
- let doc = docs.get(docName);
390
- let changeLog = doc.get('changeLog');
391
- let undoLog = doc.get('undoLog')
392
-
393
- for (let index = changeLog.length - 1; index >= 0; index--) {
394
- let change = Object.assign({}, changeLog[index]);
395
- if (change && change.clientId == clientId){
396
- let log = undoLog.get(index)
397
- if (!log) {
398
- if (log != 'undo') {
399
- undoLog.set(index, 'undo')
400
- change.index = changeLog.length += 1;
401
- let updated = createChange(data, change);
402
- undoLog.set(updated.change.index, updated.change)
403
- updateText(updated.data)
404
- return
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
- let docName = getDocName(data);
413
- let doc = docs.get(docName);
414
- let redoLog = doc.get('redoLog')
415
- let undoLog = Array.from(doc.get('undoLog').values());
416
-
417
- for (let index = undoLog.length - 1; index >= 0; index--) {
418
- let change = Object.assign({}, undoLog[index]);
419
- if (change && change != 'undo') {
420
- let log = redoLog.get(change.index)
421
-
422
- if (!log) {
423
- if (log != 'redo') {
424
- let updated = createChange(data, change);
425
- redoLog.set(updated.change.index, updated.change)
426
- updateText(updated.data)
427
- return
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
- try {
436
- let docName = getDocName(data);
437
- let doc = docs.get(docName);
438
- let changeLog = doc.get('changeLog');
439
- let string = '';
440
-
441
- let log = changeLog.slice(0, data.version)
442
- for (let change of log) {
443
- if (change || change !== null ) {
444
- string = string.customSplice(change.start, change.length, change.value);
445
- }
446
- }
447
- return { ...data, ...string }
448
- }
449
- catch (e) {
450
- console.error(e);
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
- return `${data.collection}${data.document_id}${data.name}`;
493
+ return `${data.array}${data.object}${data.key}`;
469
494
  }
470
495
 
471
496
  export default { init, getText, updateText, replaceText, undoText, redoText, viewVersion };