@manuscripts/track-changes-plugin 0.4.1 → 0.4.3

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.
@@ -38,6 +38,7 @@ export declare class ChangeSet {
38
38
  get textChanges(): TrackedChange[];
39
39
  get nodeChanges(): TrackedChange[];
40
40
  get nodeAttrChanges(): TrackedChange[];
41
+ get bothNodeChanges(): TrackedChange[];
41
42
  get isEmpty(): boolean;
42
43
  /**
43
44
  * Used to determine whether `fixInconsistentChanges` has to be executed to replace eg duplicate ids or
package/dist/actions.d.ts CHANGED
@@ -21,7 +21,6 @@ export declare enum TrackChangesAction {
21
21
  setUserID = "track-changes-set-user-id",
22
22
  setPluginStatus = "track-changes-set-track-status",
23
23
  setChangeStatuses = "track-changes-set-change-statuses",
24
- updateChanges = "track-changes-update-changes",
25
24
  refreshChanges = "track-changes-refresh-changes",
26
25
  applyAndRemoveChanges = "track-changes-apply-remove-changes"
27
26
  }
@@ -33,7 +32,6 @@ export declare type TrackChangesActionParams = {
33
32
  status: CHANGE_STATUS;
34
33
  ids: string[];
35
34
  };
36
- [TrackChangesAction.updateChanges]: string[];
37
35
  [TrackChangesAction.refreshChanges]: boolean;
38
36
  [TrackChangesAction.applyAndRemoveChanges]: boolean;
39
37
  };
@@ -1,21 +1,2 @@
1
- /*!
2
- * © 2021 Atypon Systems LLC
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- * http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- */
16
- import { Schema } from 'prosemirror-model';
17
- import type { Transaction } from 'prosemirror-state';
18
- import { ExposedFragment } from '../types/pm';
19
1
  import { ChangeStep, InsertSliceStep } from '../types/step';
20
- export declare function matchInserted(matchedDeleted: number, deleted: ChangeStep[], inserted: ExposedFragment, newTr: Transaction, schema: Schema): [number, ChangeStep[]];
21
- export declare function diffChangeSteps(deleted: ChangeStep[], inserted: InsertSliceStep[], newTr: Transaction, schema: Schema): ChangeStep[];
2
+ export declare function diffChangeSteps(deleted: ChangeStep[], inserted: InsertSliceStep[]): ChangeStep[];
@@ -0,0 +1,13 @@
1
+ import { ExposedFragment } from '../types/pm';
2
+ import { ChangeStep } from '../types/step';
3
+ /**
4
+ * Matches deleted to inserted content and returns the first pos they differ and the updated
5
+ * ChangeStep list.
6
+ *
7
+ * Based on https://github.com/ProseMirror/prosemirror-model/blob/master/src/diff.ts
8
+ * @param matchedDeleted
9
+ * @param deleted
10
+ * @param inserted
11
+ * @returns
12
+ */
13
+ export declare function matchInserted(matchedDeleted: number, deleted: ChangeStep[], inserted: ExposedFragment): [number, ChangeStep[]];
@@ -18,9 +18,9 @@ import { ChangeSet } from '../ChangeSet';
18
18
  /**
19
19
  * Finds all changes (basically text marks or node attributes) from document
20
20
  *
21
- * This could be possibly made more efficient by only iterating the sections of doc
22
- * where changes have been applied. This could attempted with eg findDiffStart
23
- * but it might be less robust than just using doc.descendants
21
+ * This could be possibly made more efficient by only iterating the sections of doc where changes have
22
+ * been applied. This could attempted with eg findDiffStart but it might be less robust than just using
23
+ * doc.descendants
24
24
  * @param state
25
25
  * @returns
26
26
  */
@@ -25,7 +25,7 @@ import { TrackChangesStatus } from './types/track';
25
25
  * to the document.
26
26
  * @param status
27
27
  */
28
- export declare const setTrackingStatus: (status?: TrackChangesStatus | undefined) => Command;
28
+ export declare const setTrackingStatus: (status?: TrackChangesStatus) => Command;
29
29
  /**
30
30
  * Appends a transaction to set change attributes/marks' statuses to any of: 'pending' 'accepted' 'rejected'.
31
31
  * @param status
@@ -45,7 +45,3 @@ export declare const applyAndRemoveChanges: () => Command;
45
45
  * Runs `findChanges` to iterate over the document to collect changes into a new ChangeSet.
46
46
  */
47
47
  export declare const refreshChanges: () => Command;
48
- /**
49
- * Adds track attributes for a block node. For testing puroses
50
- */
51
- export declare const setParagraphTestAttribute: (val?: string) => Command;
package/dist/index.cjs CHANGED
@@ -17,7 +17,6 @@ var TrackChangesAction;
17
17
  TrackChangesAction["setUserID"] = "track-changes-set-user-id";
18
18
  TrackChangesAction["setPluginStatus"] = "track-changes-set-track-status";
19
19
  TrackChangesAction["setChangeStatuses"] = "track-changes-set-change-statuses";
20
- TrackChangesAction["updateChanges"] = "track-changes-update-changes";
21
20
  TrackChangesAction["refreshChanges"] = "track-changes-refresh-changes";
22
21
  TrackChangesAction["applyAndRemoveChanges"] = "track-changes-apply-remove-changes";
23
22
  })(TrackChangesAction || (TrackChangesAction = {}));
@@ -232,6 +231,9 @@ class ChangeSet {
232
231
  get nodeAttrChanges() {
233
232
  return this.changes.filter((c) => c.type === 'node-attr-change');
234
233
  }
234
+ get bothNodeChanges() {
235
+ return this.changes.filter((c) => c.type === 'node-change' || c.type === 'node-attr-change');
236
+ }
235
237
  get isEmpty() {
236
238
  return __classPrivateFieldGet(this, _ChangeSet_changes, "f").length === 0;
237
239
  }
@@ -370,7 +372,7 @@ function getTextNodeTrackedMarkData(node, schema) {
370
372
  return marksTrackedData[0] || undefined;
371
373
  }
372
374
  function getBlockInlineTrackedData(node) {
373
- let { dataTracked } = node.attrs;
375
+ const { dataTracked } = node.attrs;
374
376
  if (dataTracked && !Array.isArray(dataTracked)) {
375
377
  return [dataTracked];
376
378
  }
@@ -545,11 +547,11 @@ function updateChangeAttrs(tr, change, trackedAttrs, schema) {
545
547
  // TODO add operation based on mark type if it's undefined?
546
548
  tr.addMark(change.from, change.to, oldMark.type.create({ ...oldMark.attrs, dataTracked: { ...oldTrackData, ...trackedAttrs } }));
547
549
  }
548
- else if (change.type === 'node-change' && !operation) {
550
+ else if ((change.type === 'node-change' || change.type === 'node-attr-change') && !operation) {
549
551
  // Very weird edge-case if this happens
550
552
  tr.setNodeMarkup(change.from, undefined, { ...node.attrs, dataTracked: null }, node.marks);
551
553
  }
552
- else if (change.type === 'node-change') {
554
+ else if (change.type === 'node-change' || change.type === 'node-attr-change') {
553
555
  const newDataTracked = (getBlockInlineTrackedData(node) || []).map((oldTrack) => {
554
556
  if (oldTrack.operation === operation) {
555
557
  return { ...oldTrack, ...trackedAttrs };
@@ -619,13 +621,11 @@ function applyAcceptedRejectedChanges(tr, schema, changes, deleteMap = new prose
619
621
  }
620
622
  else if (ChangeSet.isNodeAttrChange(change) &&
621
623
  change.dataTracked.status === exports.CHANGE_STATUS.accepted) {
622
- const attrs = { ...node.attrs, dataTracked: null };
623
- tr.setNodeMarkup(from, undefined, attrs, node.marks);
624
+ tr.setNodeMarkup(from, undefined, { ...node.attrs, dataTracked: null }, node.marks);
624
625
  }
625
626
  else if (ChangeSet.isNodeAttrChange(change) &&
626
627
  change.dataTracked.status === exports.CHANGE_STATUS.rejected) {
627
- const attrs = { ...change.oldAttrs, dataTracked: null };
628
- tr.setNodeMarkup(from, undefined, attrs, node.marks);
628
+ tr.setNodeMarkup(from, undefined, { ...change.oldAttrs, dataTracked: null }, node.marks);
629
629
  }
630
630
  });
631
631
  return deleteMap;
@@ -634,9 +634,9 @@ function applyAcceptedRejectedChanges(tr, schema, changes, deleteMap = new prose
634
634
  /**
635
635
  * Finds all changes (basically text marks or node attributes) from document
636
636
  *
637
- * This could be possibly made more efficient by only iterating the sections of doc
638
- * where changes have been applied. This could attempted with eg findDiffStart
639
- * but it might be less robust than just using doc.descendants
637
+ * This could be possibly made more efficient by only iterating the sections of doc where changes have
638
+ * been applied. This could attempted with eg findDiffStart but it might be less robust than just using
639
+ * doc.descendants
640
640
  * @param state
641
641
  * @returns
642
642
  */
@@ -945,10 +945,12 @@ function createNewDeleteAttrs(attrs) {
945
945
  };
946
946
  }
947
947
  function createNewUpdateAttrs(attrs, oldAttrs) {
948
+ // Omit dataTracked
949
+ const { dataTracked, ...restAttrs } = oldAttrs;
948
950
  return {
949
951
  ...attrs,
950
952
  operation: exports.CHANGE_OPERATION.set_node_attributes,
951
- oldAttrs,
953
+ oldAttrs: restAttrs,
952
954
  };
953
955
  }
954
956
 
@@ -1018,7 +1020,7 @@ function deleteAndMergeSplitNodes(from, to, gap, startDoc, newTr, schema, trackA
1018
1020
  // @TODO ATM 20.7.2022 there doesn't seem to be tests that capture this.
1019
1021
  const wasWithinGap = gap &&
1020
1022
  ((!node.isText && pos >= gap.start) ||
1021
- (node.isText && pos <= gap.start && nodeEnd >= gap.start));
1023
+ (node.isText && pos >= gap.start && nodeEnd <= gap.end));
1022
1024
  // nodeEnd > offsetFrom -> delete touches this node
1023
1025
  // eg (del 6 10) <p 5>|<t 6>cdf</t 9></p 10>| -> <p> nodeEnd 10 > from 6
1024
1026
  if (nodeEnd > from && !wasWithinGap) {
@@ -1119,41 +1121,6 @@ function deleteAndMergeSplitNodes(from, to, gap, startDoc, newTr, schema, trackA
1119
1121
  };
1120
1122
  }
1121
1123
 
1122
- /**
1123
- * Merges tracked marks between text nodes at a position
1124
- *
1125
- * Will work for any nodes that use tracked_insert or tracked_delete marks which may not be preferrable
1126
- * if used for block nodes (since we possibly want to show the individual changed nodes).
1127
- * Merging is done based on the userID, operation type and status.
1128
- * @param pos
1129
- * @param doc
1130
- * @param newTr
1131
- * @param schema
1132
- */
1133
- function mergeTrackedMarks(pos, doc, newTr, schema) {
1134
- const resolved = doc.resolve(pos);
1135
- const { nodeAfter, nodeBefore } = resolved;
1136
- const leftMark = nodeBefore === null || nodeBefore === void 0 ? void 0 : nodeBefore.marks.filter((m) => m.type === schema.marks.tracked_insert || m.type === schema.marks.tracked_delete)[0];
1137
- const rightMark = nodeAfter === null || nodeAfter === void 0 ? void 0 : nodeAfter.marks.filter((m) => m.type === schema.marks.tracked_insert || m.type === schema.marks.tracked_delete)[0];
1138
- if (!nodeAfter || !nodeBefore || !leftMark || !rightMark || leftMark.type !== rightMark.type) {
1139
- return;
1140
- }
1141
- const leftDataTracked = leftMark.attrs.dataTracked;
1142
- const rightDataTracked = rightMark.attrs.dataTracked;
1143
- if (!shouldMergeTrackedAttributes(leftDataTracked, rightDataTracked)) {
1144
- return;
1145
- }
1146
- const isLeftOlder = (leftDataTracked.createdAt || 0) < (rightDataTracked.createdAt || 0);
1147
- const ancestorAttrs = isLeftOlder ? leftDataTracked : rightDataTracked;
1148
- const dataTracked = {
1149
- ...ancestorAttrs,
1150
- updatedAt: Date.now(),
1151
- };
1152
- const fromStartOfMark = pos - nodeBefore.nodeSize;
1153
- const toEndOfMark = pos + nodeAfter.nodeSize;
1154
- newTr.addMark(fromStartOfMark, toEndOfMark, leftMark.type.create({ ...leftMark.attrs, dataTracked }));
1155
- }
1156
-
1157
1124
  /*!
1158
1125
  * © 2021 Atypon Systems LLC
1159
1126
  *
@@ -1206,7 +1173,7 @@ function trackReplaceAroundStep(step, oldState, newTr, attrs) {
1206
1173
  // First apply the deleted range and update the insert slice to not include content that was deleted,
1207
1174
  // eg partial nodes in an open-ended slice
1208
1175
  const { sliceWasSplit, newSliceContent, steps: deleteSteps, } = deleteAndMergeSplitNodes(from, to, { start: gapFrom, end: gapTo }, newTr.doc, newTr, oldState.schema, attrs, slice);
1209
- let steps = deleteSteps;
1176
+ const steps = deleteSteps;
1210
1177
  log.info('TR: new steps after applying delete', [...newTr.steps]);
1211
1178
  log.info('DELETE STEPS: ', deleteSteps);
1212
1179
  // We only want to insert when there something inside the gap (actually would this be always true?)
@@ -1231,11 +1198,6 @@ function trackReplaceAroundStep(step, oldState, newTr, attrs) {
1231
1198
  sliceWasSplit,
1232
1199
  });
1233
1200
  }
1234
- else {
1235
- // Incase only deletion was applied, check whether tracked marks around deleted content can be merged
1236
- mergeTrackedMarks(gapFrom, newTr.doc, newTr, oldState.schema);
1237
- mergeTrackedMarks(gapTo, newTr.doc, newTr, oldState.schema);
1238
- }
1239
1201
  return steps;
1240
1202
  }
1241
1203
 
@@ -1274,7 +1236,7 @@ function trackReplaceStep(step, oldState, newTr, attrs) {
1274
1236
  changeSteps.push(...deleteSteps);
1275
1237
  log.info('TR: steps after applying delete', [...newTr.steps]);
1276
1238
  log.info('DELETE STEPS: ', changeSteps);
1277
- const adjustedInsertPos = toA; // deleteMap.map(toA)
1239
+ const adjustedInsertPos = toA;
1278
1240
  if (newSliceContent.size > 0) {
1279
1241
  log.info('newSliceContent', newSliceContent);
1280
1242
  // Since deleteAndMergeSplitBlockNodes modified the slice to not to contain any merged nodes,
@@ -1291,7 +1253,7 @@ function trackReplaceStep(step, oldState, newTr, attrs) {
1291
1253
  }
1292
1254
  else {
1293
1255
  // Incase only deletion was applied, check whether tracked marks around deleted content can be merged
1294
- mergeTrackedMarks(adjustedInsertPos, newTr.doc, newTr, oldState.schema);
1256
+ // mergeTrackedMarks(adjustedInsertPos, newTr.doc, newTr, oldState.schema)
1295
1257
  selectionPos = fromA;
1296
1258
  }
1297
1259
  });
@@ -1325,6 +1287,7 @@ function trackReplaceStep(step, oldState, newTr, attrs) {
1325
1287
  * @param deleteAttrs
1326
1288
  * @param from
1327
1289
  * @param to
1290
+ * @returns position at the end of the possibly deleted text
1328
1291
  */
1329
1292
  function deleteTextIfInserted(node, pos, newTr, schema, deleteAttrs, from, to) {
1330
1293
  const start = from ? Math.max(pos, from) : pos;
@@ -1358,6 +1321,41 @@ function deleteTextIfInserted(node, pos, newTr, schema, deleteAttrs, from, to) {
1358
1321
  }
1359
1322
  }
1360
1323
 
1324
+ /**
1325
+ * Merges tracked marks between text nodes at a position
1326
+ *
1327
+ * Will work for any nodes that use tracked_insert or tracked_delete marks which may not be preferrable
1328
+ * if used for block nodes (since we possibly want to show the individual changed nodes).
1329
+ * Merging is done based on the userID, operation type and status.
1330
+ * @param pos
1331
+ * @param doc
1332
+ * @param newTr
1333
+ * @param schema
1334
+ */
1335
+ function mergeTrackedMarks(pos, doc, newTr, schema) {
1336
+ const resolved = doc.resolve(pos);
1337
+ const { nodeAfter, nodeBefore } = resolved;
1338
+ const leftMark = nodeBefore === null || nodeBefore === void 0 ? void 0 : nodeBefore.marks.filter((m) => m.type === schema.marks.tracked_insert || m.type === schema.marks.tracked_delete)[0];
1339
+ const rightMark = nodeAfter === null || nodeAfter === void 0 ? void 0 : nodeAfter.marks.filter((m) => m.type === schema.marks.tracked_insert || m.type === schema.marks.tracked_delete)[0];
1340
+ if (!nodeAfter || !nodeBefore || !leftMark || !rightMark || leftMark.type !== rightMark.type) {
1341
+ return;
1342
+ }
1343
+ const leftDataTracked = leftMark.attrs.dataTracked;
1344
+ const rightDataTracked = rightMark.attrs.dataTracked;
1345
+ if (!shouldMergeTrackedAttributes(leftDataTracked, rightDataTracked)) {
1346
+ return;
1347
+ }
1348
+ const isLeftOlder = (leftDataTracked.createdAt || 0) < (rightDataTracked.createdAt || 0);
1349
+ const ancestorAttrs = isLeftOlder ? leftDataTracked : rightDataTracked;
1350
+ const dataTracked = {
1351
+ ...ancestorAttrs,
1352
+ updatedAt: Date.now(),
1353
+ };
1354
+ const fromStartOfMark = pos - nodeBefore.nodeSize;
1355
+ const toEndOfMark = pos + nodeAfter.nodeSize;
1356
+ newTr.addMark(fromStartOfMark, toEndOfMark, leftMark.type.create({ ...leftMark.attrs, dataTracked }));
1357
+ }
1358
+
1361
1359
  function processChangeSteps(changes, startPos, newTr, emptyAttrs, schema) {
1362
1360
  const mapping = new prosemirrorTransform.Mapping();
1363
1361
  const deleteAttrs = createNewDeleteAttrs(emptyAttrs);
@@ -1419,22 +1417,18 @@ function processChangeSteps(changes, startPos, newTr, emptyAttrs, schema) {
1419
1417
  else if (c.type === 'update-node-attrs') {
1420
1418
  const oldDataTracked = getBlockInlineTrackedData(c.node) || [];
1421
1419
  const oldUpdate = oldDataTracked.find((d) => d.operation === exports.CHANGE_OPERATION.set_node_attributes);
1422
- let newDataTracked = oldDataTracked;
1423
- if (oldUpdate) {
1424
- newDataTracked = [
1425
- ...oldDataTracked.filter((d) => d === oldUpdate),
1426
- {
1427
- ...oldUpdate,
1428
- updatedAt: emptyAttrs.updatedAt,
1429
- },
1430
- ];
1431
- }
1432
- else if (oldDataTracked.length === 0 ||
1433
- oldDataTracked.find((d) => d.operation === exports.CHANGE_OPERATION.delete)) {
1434
- newDataTracked = [
1435
- ...oldDataTracked,
1436
- addTrackIdIfDoesntExist(createNewUpdateAttrs(emptyAttrs, c.node.attrs)),
1437
- ];
1420
+ const { dataTracked, ...oldAttrs } = (oldUpdate === null || oldUpdate === void 0 ? void 0 : oldUpdate.oldAttrs) || c.node.attrs;
1421
+ const newDataTracked = [...oldDataTracked.filter((d) => !oldUpdate || d.id !== oldUpdate.id)];
1422
+ const newUpdate = oldUpdate
1423
+ ? {
1424
+ ...oldUpdate,
1425
+ updatedAt: emptyAttrs.updatedAt,
1426
+ }
1427
+ : addTrackIdIfDoesntExist(createNewUpdateAttrs(emptyAttrs, c.node.attrs));
1428
+ // Dont add update changes if there exists already an insert change for this node
1429
+ if (JSON.stringify(oldAttrs) !== JSON.stringify(c.newAttrs) &&
1430
+ !oldDataTracked.find((d) => d.operation === exports.CHANGE_OPERATION.insert)) {
1431
+ newDataTracked.push(newUpdate);
1438
1432
  }
1439
1433
  newTr.setNodeMarkup(mapping.map(c.pos), undefined, {
1440
1434
  ...c.newAttrs,
@@ -1449,61 +1443,77 @@ function processChangeSteps(changes, startPos, newTr, emptyAttrs, schema) {
1449
1443
  return [mapping, selectionPos];
1450
1444
  }
1451
1445
 
1452
- /*!
1453
- * © 2021 Atypon Systems LLC
1454
- *
1455
- * Licensed under the Apache License, Version 2.0 (the "License");
1456
- * you may not use this file except in compliance with the License.
1457
- * You may obtain a copy of the License at
1446
+ /**
1447
+ * Matches deleted-text recursively to inserted text
1458
1448
  *
1459
- * http://www.apache.org/licenses/LICENSE-2.0
1449
+ * This is needed as text containing various marks is split into multiple parts even though it's
1450
+ * continously deleted. Therefore, we need to find the next part if there is any and keep going until
1451
+ * we've reached the end of the deleted text or inserted content.
1452
+ * @param adjDeleted
1453
+ * @param insNode
1454
+ * @param offset
1455
+ * @param matchedDeleted
1456
+ * @param deleted
1457
+ * @returns
1458
+ */
1459
+ function matchText(adjDeleted, insNode, offset, matchedDeleted, deleted) {
1460
+ const { pos, from, to, node: delNode } = adjDeleted;
1461
+ let j = offset, d = from - pos, maxSteps = to - Math.max(pos, from);
1462
+ // Match text inside the inserted text node to the deleted text node
1463
+ for (; maxSteps !== j && insNode.text[j] !== undefined && insNode.text[j] === delNode.text[d]; j += 1, d += 1) {
1464
+ matchedDeleted += 1;
1465
+ }
1466
+ // this is needed incase diffing tr.doc
1467
+ // deleted.push({
1468
+ // pos: pos,
1469
+ // type: 'update-node-attrs',
1470
+ // // Should check the attrs for equality in fixInconsistentChanges? to remove dataTracked completely
1471
+ // oldAttrs: adjDeleted.node.attrs || {},
1472
+ // newAttrs: child.attrs || {},
1473
+ // })
1474
+ deleted = deleted.filter((d) => d !== adjDeleted);
1475
+ if (maxSteps !== j) {
1476
+ deleted.push({
1477
+ pos,
1478
+ from: from + j - offset,
1479
+ to,
1480
+ type: 'delete-text',
1481
+ node: delNode,
1482
+ });
1483
+ return [matchedDeleted, deleted];
1484
+ }
1485
+ const nextTextDelete = deleted.find((d) => d.type === 'delete-text' && d.pos === to);
1486
+ if (nextTextDelete) {
1487
+ return matchText(nextTextDelete, insNode, j, matchedDeleted, deleted);
1488
+ }
1489
+ return [matchedDeleted, deleted];
1490
+ }
1491
+ /**
1492
+ * Matches deleted to inserted content and returns the first pos they differ and the updated
1493
+ * ChangeStep list.
1460
1494
  *
1461
- * Unless required by applicable law or agreed to in writing, software
1462
- * distributed under the License is distributed on an "AS IS" BASIS,
1463
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1464
- * See the License for the specific language governing permissions and
1465
- * limitations under the License.
1495
+ * Based on https://github.com/ProseMirror/prosemirror-model/blob/master/src/diff.ts
1496
+ * @param matchedDeleted
1497
+ * @param deleted
1498
+ * @param inserted
1499
+ * @returns
1466
1500
  */
1467
- function matchInserted(matchedDeleted, deleted, inserted, newTr, schema) {
1501
+ function matchInserted(matchedDeleted, deleted, inserted) {
1468
1502
  var _a;
1469
1503
  let matched = [matchedDeleted, deleted];
1470
1504
  for (let i = 0;; i += 1) {
1471
- if (inserted.childCount === i)
1505
+ if (inserted.childCount === i) {
1472
1506
  return matched;
1507
+ }
1473
1508
  const insNode = inserted.child(i);
1474
1509
  // @ts-ignore
1475
- let adjDeleted = matched[1].find((d) => (d.type === 'delete-text' && Math.max(d.pos, d.from) === matched[0]) ||
1510
+ const adjDeleted = matched[1].find((d) => (d.type === 'delete-text' && Math.max(d.pos, d.from) === matched[0]) ||
1476
1511
  (d.type === 'delete-node' && d.pos === matched[0]));
1477
1512
  if (insNode.type !== ((_a = adjDeleted === null || adjDeleted === void 0 ? void 0 : adjDeleted.node) === null || _a === void 0 ? void 0 : _a.type)) {
1478
1513
  return matched;
1479
1514
  }
1480
1515
  else if (insNode.isText && (adjDeleted === null || adjDeleted === void 0 ? void 0 : adjDeleted.node)) {
1481
- adjDeleted = adjDeleted;
1482
- const { pos, from, to, node: delNode } = adjDeleted;
1483
- let j = 0, d = from - pos, maxSteps = to - Math.max(pos, from);
1484
- // Match text inside the inserted text node to the deleted text node
1485
- for (; maxSteps !== j && insNode.text[j] !== undefined && insNode.text[j] === delNode.text[d]; j += 1, d += 1) {
1486
- matched[0] += 1;
1487
- }
1488
- // this is needed incase diffing tr.doc
1489
- // deleted.push({
1490
- // pos: pos,
1491
- // type: 'update-node-attrs',
1492
- // // Should check the attrs for equality in fixInconsistentChanges? to remove dataTracked completely
1493
- // oldAttrs: adjDeleted.node.attrs || {},
1494
- // newAttrs: child.attrs || {},
1495
- // })
1496
- matched = [matched[0], matched[1].filter((d) => d !== adjDeleted)];
1497
- if (maxSteps !== j) {
1498
- matched[1].push({
1499
- pos,
1500
- from: Math.max(pos, from) + j,
1501
- to,
1502
- type: 'delete-text',
1503
- node: delNode,
1504
- });
1505
- return matched;
1506
- }
1516
+ matched = matchText(adjDeleted, insNode, 0, matched[0], matched[1]);
1507
1517
  continue;
1508
1518
  }
1509
1519
  else if (insNode.content.size > 0 || (adjDeleted === null || adjDeleted === void 0 ? void 0 : adjDeleted.node.content.size) > 0) {
@@ -1513,28 +1523,42 @@ function matchInserted(matchedDeleted, deleted, inserted, newTr, schema) {
1513
1523
  else {
1514
1524
  matched = [matched[0] + insNode.nodeSize, matched[1].filter((d) => d !== adjDeleted)];
1515
1525
  }
1526
+ // Omit dataTracked
1527
+ const { dataTracked, ...newAttrs } = insNode.attrs || {};
1516
1528
  matched[1].push({
1517
1529
  pos: adjDeleted.pos,
1518
1530
  type: 'update-node-attrs',
1519
1531
  node: adjDeleted.node,
1520
- // Should check the attrs for equality in fixInconsistentChanges? to remove dataTracked completely
1521
- newAttrs: insNode.attrs || {},
1532
+ newAttrs,
1522
1533
  });
1523
1534
  }
1524
- }
1535
+ }
1536
+
1537
+ /*!
1538
+ * © 2021 Atypon Systems LLC
1539
+ *
1540
+ * Licensed under the Apache License, Version 2.0 (the "License");
1541
+ * you may not use this file except in compliance with the License.
1542
+ * You may obtain a copy of the License at
1543
+ *
1544
+ * http://www.apache.org/licenses/LICENSE-2.0
1545
+ *
1546
+ * Unless required by applicable law or agreed to in writing, software
1547
+ * distributed under the License is distributed on an "AS IS" BASIS,
1548
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1549
+ * See the License for the specific language governing permissions and
1550
+ * limitations under the License.
1551
+ */
1525
1552
  /**
1526
1553
  * Cuts a fragment similar to Fragment.cut but also removes the parent node.
1527
1554
  *
1528
- * @TODO there is however, some silly calculation mistake so that I need to use matched - deleted + 1 > 0
1529
- * inside it to check whether to actually cut a text node. The offset might be cascading, therefore it should
1530
- * be fixed at some point.
1531
1555
  * @param matched
1532
1556
  * @param deleted
1533
1557
  * @param content
1534
1558
  * @returns
1535
1559
  */
1536
1560
  function cutFragment(matched, deleted, content) {
1537
- let newContent = [];
1561
+ const newContent = [];
1538
1562
  for (let i = 0; matched <= deleted && i < content.childCount; i += 1) {
1539
1563
  const child = content.child(i);
1540
1564
  if (!child.isText && child.content.size > 0) {
@@ -1543,8 +1567,8 @@ function cutFragment(matched, deleted, content) {
1543
1567
  newContent.push(...cut[1].content);
1544
1568
  }
1545
1569
  else if (child.isText && matched + child.nodeSize > deleted) {
1546
- if (matched - deleted + 1 > 0) {
1547
- newContent.push(child.cut(0, matched - deleted + 1));
1570
+ if (deleted - matched > 0) {
1571
+ newContent.push(child.cut(deleted - matched));
1548
1572
  }
1549
1573
  else {
1550
1574
  newContent.push(child);
@@ -1557,7 +1581,7 @@ function cutFragment(matched, deleted, content) {
1557
1581
  }
1558
1582
  return [matched, prosemirrorModel.Fragment.fromArray(newContent)];
1559
1583
  }
1560
- function diffChangeSteps(deleted, inserted, newTr, schema) {
1584
+ function diffChangeSteps(deleted, inserted) {
1561
1585
  const updated = [];
1562
1586
  let updatedDeleted = [...deleted];
1563
1587
  inserted.forEach((ins) => {
@@ -1579,7 +1603,7 @@ function diffChangeSteps(deleted, inserted, newTr, schema) {
1579
1603
  return;
1580
1604
  }
1581
1605
  // Start diffing from the start of the deleted range
1582
- const deleteStart = deleted.reduce((acc, cur) => {
1606
+ const deleteStart = updatedDeleted.reduce((acc, cur) => {
1583
1607
  if (cur.type === 'delete-node') {
1584
1608
  return Math.min(acc, cur.pos);
1585
1609
  }
@@ -1588,13 +1612,13 @@ function diffChangeSteps(deleted, inserted, newTr, schema) {
1588
1612
  }
1589
1613
  return acc;
1590
1614
  }, Number.MAX_SAFE_INTEGER);
1591
- const [inDeleted, updatedDel] = matchInserted(deleteStart, updatedDeleted, ins.slice.content);
1592
- if (inDeleted === deleteStart) {
1615
+ const [matchedDeleted, updatedDel] = matchInserted(deleteStart, updatedDeleted, ins.slice.content);
1616
+ if (matchedDeleted === deleteStart) {
1593
1617
  updated.push(ins);
1594
1618
  return;
1595
1619
  }
1596
1620
  updatedDeleted = updatedDel;
1597
- const newInserted = cutFragment(0, inDeleted, ins.slice.content)[1];
1621
+ const [_, newInserted] = cutFragment(0, matchedDeleted - deleteStart, ins.slice.content);
1598
1622
  if (newInserted.size > 0) {
1599
1623
  updated.push({
1600
1624
  ...ins,
@@ -1662,9 +1686,10 @@ function trackTransaction(tr, oldState, newTr, authorID) {
1662
1686
  // deleted and merged really...
1663
1687
  const deleted = steps.filter((s) => s.type !== 'insert-slice');
1664
1688
  const inserted = steps.filter((s) => s.type === 'insert-slice');
1665
- steps = diffChangeSteps(deleted, inserted, newTr, oldState.schema);
1689
+ steps = diffChangeSteps(deleted, inserted);
1666
1690
  log.info('DIFFED STEPS: ', steps);
1667
- const [mapping, selectionPos] = processChangeSteps(steps, startPos, newTr, emptyAttrs, oldState.schema);
1691
+ const [mapping, selectionPos] = processChangeSteps(steps, startPos || tr.selection.head, // Incase startPos is it's default value 0, use the old selection head
1692
+ newTr, emptyAttrs, oldState.schema);
1668
1693
  if (!wasNodeSelection) {
1669
1694
  const sel = getSelectionStaticConstructor(tr.selection);
1670
1695
  // Use Selection.near to fix selections that point to a block node instead of inline content
@@ -1679,7 +1704,7 @@ function trackTransaction(tr, oldState, newTr, authorID) {
1679
1704
  const deleted = steps.filter((s) => s.type !== 'insert-slice');
1680
1705
  const inserted = steps.filter((s) => s.type === 'insert-slice');
1681
1706
  log.info('INSERT STEPS: ', inserted);
1682
- steps = diffChangeSteps(deleted, inserted, newTr, oldState.schema);
1707
+ steps = diffChangeSteps(deleted, inserted);
1683
1708
  log.info('DIFFED STEPS: ', steps);
1684
1709
  processChangeSteps(steps, tr.selection.from, newTr, emptyAttrs, oldState.schema);
1685
1710
  }
@@ -1769,15 +1794,14 @@ const trackChangesPlugin = (opts = { userID: 'anonymous:Anonymous' }) => {
1769
1794
  return {
1770
1795
  ...pluginState,
1771
1796
  status: setStatus,
1772
- changeSet: findChanges(newState),
1797
+ changeSet: setStatus === exports.TrackChangesStatus.disabled ? new ChangeSet() : findChanges(newState),
1773
1798
  };
1774
1799
  }
1775
1800
  else if (pluginState.status === exports.TrackChangesStatus.disabled) {
1776
1801
  return { ...pluginState, changeSet: new ChangeSet() };
1777
1802
  }
1778
1803
  let { changeSet, ...rest } = pluginState;
1779
- const updatedChangeIds = getAction(tr, TrackChangesAction.updateChanges);
1780
- if (updatedChangeIds || getAction(tr, TrackChangesAction.refreshChanges)) {
1804
+ if (getAction(tr, TrackChangesAction.refreshChanges)) {
1781
1805
  changeSet = findChanges(newState);
1782
1806
  }
1783
1807
  return {
@@ -1821,10 +1845,9 @@ const trackChangesPlugin = (opts = { userID: 'anonymous:Anonymous' }) => {
1821
1845
  createdTr = updateChangeAttrs(createdTr, change, { ...change.dataTracked, status, reviewedByID: userID }, oldState.schema);
1822
1846
  }
1823
1847
  });
1824
- setAction(createdTr, TrackChangesAction.updateChanges, ids);
1825
1848
  }
1826
1849
  else if (getAction(tr, TrackChangesAction.applyAndRemoveChanges)) {
1827
- const mapping = applyAcceptedRejectedChanges(createdTr, oldState.schema, changeSet.nodeChanges);
1850
+ const mapping = applyAcceptedRejectedChanges(createdTr, oldState.schema, changeSet.bothNodeChanges);
1828
1851
  applyAcceptedRejectedChanges(createdTr, oldState.schema, changeSet.textChanges, mapping);
1829
1852
  setAction(createdTr, TrackChangesAction.refreshChanges, true);
1830
1853
  }
@@ -1900,24 +1923,8 @@ const applyAndRemoveChanges = () => (state, dispatch) => {
1900
1923
  * Runs `findChanges` to iterate over the document to collect changes into a new ChangeSet.
1901
1924
  */
1902
1925
  const refreshChanges = () => (state, dispatch) => {
1903
- dispatch && dispatch(setAction(state.tr, TrackChangesAction.updateChanges, []));
1926
+ dispatch && dispatch(setAction(state.tr, TrackChangesAction.refreshChanges, true));
1904
1927
  return true;
1905
- };
1906
- /**
1907
- * Adds track attributes for a block node. For testing puroses
1908
- */
1909
- const setParagraphTestAttribute = (val = 'changed') => (state, dispatch) => {
1910
- var _a;
1911
- const cursor = state.selection.head;
1912
- const blockNodePos = state.doc.resolve(cursor).start(1) - 1;
1913
- if (((_a = state.doc.resolve(blockNodePos).nodeAfter) === null || _a === void 0 ? void 0 : _a.type) === state.schema.nodes.paragraph &&
1914
- dispatch) {
1915
- dispatch(state.tr.setNodeMarkup(blockNodePos, undefined, {
1916
- testAttribute: val,
1917
- }));
1918
- return true;
1919
- }
1920
- return false;
1921
1928
  };
1922
1929
 
1923
1930
  var commands = /*#__PURE__*/Object.freeze({
@@ -1926,8 +1933,7 @@ var commands = /*#__PURE__*/Object.freeze({
1926
1933
  setChangeStatuses: setChangeStatuses,
1927
1934
  setUserID: setUserID,
1928
1935
  applyAndRemoveChanges: applyAndRemoveChanges,
1929
- refreshChanges: refreshChanges,
1930
- setParagraphTestAttribute: setParagraphTestAttribute
1936
+ refreshChanges: refreshChanges
1931
1937
  });
1932
1938
 
1933
1939
  exports.ChangeSet = ChangeSet;