@manuscripts/track-changes-plugin 0.4.1 → 0.4.2

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
@@ -15,7 +15,5 @@
15
15
  */
16
16
  import { Schema } from 'prosemirror-model';
17
17
  import type { Transaction } from 'prosemirror-state';
18
- import { ExposedFragment } from '../types/pm';
19
18
  import { ChangeStep, InsertSliceStep } from '../types/step';
20
- export declare function matchInserted(matchedDeleted: number, deleted: ChangeStep[], inserted: ExposedFragment, newTr: Transaction, schema: Schema): [number, ChangeStep[]];
21
19
  export declare function diffChangeSteps(deleted: ChangeStep[], inserted: InsertSliceStep[], newTr: Transaction, schema: Schema): ChangeStep[];
@@ -0,0 +1,3 @@
1
+ import { ExposedFragment } from '../types/pm';
2
+ import { ChangeStep } from '../types/step';
3
+ export declare function matchInserted(matchedDeleted: number, deleted: ChangeStep[], inserted: ExposedFragment): [number, ChangeStep[]];
package/dist/index.cjs CHANGED
@@ -232,6 +232,9 @@ class ChangeSet {
232
232
  get nodeAttrChanges() {
233
233
  return this.changes.filter((c) => c.type === 'node-attr-change');
234
234
  }
235
+ get bothNodeChanges() {
236
+ return this.changes.filter((c) => c.type === 'node-change' || c.type === 'node-attr-change');
237
+ }
235
238
  get isEmpty() {
236
239
  return __classPrivateFieldGet(this, _ChangeSet_changes, "f").length === 0;
237
240
  }
@@ -545,11 +548,11 @@ function updateChangeAttrs(tr, change, trackedAttrs, schema) {
545
548
  // TODO add operation based on mark type if it's undefined?
546
549
  tr.addMark(change.from, change.to, oldMark.type.create({ ...oldMark.attrs, dataTracked: { ...oldTrackData, ...trackedAttrs } }));
547
550
  }
548
- else if (change.type === 'node-change' && !operation) {
551
+ else if ((change.type === 'node-change' || change.type === 'node-attr-change') && !operation) {
549
552
  // Very weird edge-case if this happens
550
553
  tr.setNodeMarkup(change.from, undefined, { ...node.attrs, dataTracked: null }, node.marks);
551
554
  }
552
- else if (change.type === 'node-change') {
555
+ else if (change.type === 'node-change' || change.type === 'node-attr-change') {
553
556
  const newDataTracked = (getBlockInlineTrackedData(node) || []).map((oldTrack) => {
554
557
  if (oldTrack.operation === operation) {
555
558
  return { ...oldTrack, ...trackedAttrs };
@@ -619,13 +622,11 @@ function applyAcceptedRejectedChanges(tr, schema, changes, deleteMap = new prose
619
622
  }
620
623
  else if (ChangeSet.isNodeAttrChange(change) &&
621
624
  change.dataTracked.status === exports.CHANGE_STATUS.accepted) {
622
- const attrs = { ...node.attrs, dataTracked: null };
623
- tr.setNodeMarkup(from, undefined, attrs, node.marks);
625
+ tr.setNodeMarkup(from, undefined, { ...node.attrs, dataTracked: null }, node.marks);
624
626
  }
625
627
  else if (ChangeSet.isNodeAttrChange(change) &&
626
628
  change.dataTracked.status === exports.CHANGE_STATUS.rejected) {
627
- const attrs = { ...change.oldAttrs, dataTracked: null };
628
- tr.setNodeMarkup(from, undefined, attrs, node.marks);
629
+ tr.setNodeMarkup(from, undefined, { ...change.oldAttrs, dataTracked: null }, node.marks);
629
630
  }
630
631
  });
631
632
  return deleteMap;
@@ -945,10 +946,12 @@ function createNewDeleteAttrs(attrs) {
945
946
  };
946
947
  }
947
948
  function createNewUpdateAttrs(attrs, oldAttrs) {
949
+ // Omit dataTracked
950
+ const { dataTracked, ...restAttrs } = oldAttrs;
948
951
  return {
949
952
  ...attrs,
950
953
  operation: exports.CHANGE_OPERATION.set_node_attributes,
951
- oldAttrs,
954
+ oldAttrs: restAttrs,
952
955
  };
953
956
  }
954
957
 
@@ -1419,22 +1422,18 @@ function processChangeSteps(changes, startPos, newTr, emptyAttrs, schema) {
1419
1422
  else if (c.type === 'update-node-attrs') {
1420
1423
  const oldDataTracked = getBlockInlineTrackedData(c.node) || [];
1421
1424
  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
- ];
1425
+ const { dataTracked, ...oldAttrs } = (oldUpdate === null || oldUpdate === void 0 ? void 0 : oldUpdate.oldAttrs) || c.node.attrs;
1426
+ const newDataTracked = [...oldDataTracked.filter((d) => !oldUpdate || d.id !== oldUpdate.id)];
1427
+ const newUpdate = oldUpdate
1428
+ ? {
1429
+ ...oldUpdate,
1430
+ updatedAt: emptyAttrs.updatedAt,
1431
+ }
1432
+ : addTrackIdIfDoesntExist(createNewUpdateAttrs(emptyAttrs, c.node.attrs));
1433
+ // Dont add update changes if there exists already an insert change for this node
1434
+ if (JSON.stringify(oldAttrs) !== JSON.stringify(c.newAttrs) &&
1435
+ !oldDataTracked.find((d) => d.operation === exports.CHANGE_OPERATION.insert)) {
1436
+ newDataTracked.push(newUpdate);
1438
1437
  }
1439
1438
  newTr.setNodeMarkup(mapping.map(c.pos), undefined, {
1440
1439
  ...c.newAttrs,
@@ -1449,22 +1448,39 @@ function processChangeSteps(changes, startPos, newTr, emptyAttrs, schema) {
1449
1448
  return [mapping, selectionPos];
1450
1449
  }
1451
1450
 
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
1458
- *
1459
- * http://www.apache.org/licenses/LICENSE-2.0
1460
- *
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.
1466
- */
1467
- function matchInserted(matchedDeleted, deleted, inserted, newTr, schema) {
1451
+ function matchText(adjDeleted, insNode, offset, matchedDeleted, deleted) {
1452
+ const { pos, from, to, node: delNode } = adjDeleted;
1453
+ let j = offset, d = from - pos, maxSteps = to - Math.max(pos, from);
1454
+ // Match text inside the inserted text node to the deleted text node
1455
+ for (; maxSteps !== j && insNode.text[j] !== undefined && insNode.text[j] === delNode.text[d]; j += 1, d += 1) {
1456
+ matchedDeleted += 1;
1457
+ }
1458
+ // this is needed incase diffing tr.doc
1459
+ // deleted.push({
1460
+ // pos: pos,
1461
+ // type: 'update-node-attrs',
1462
+ // // Should check the attrs for equality in fixInconsistentChanges? to remove dataTracked completely
1463
+ // oldAttrs: adjDeleted.node.attrs || {},
1464
+ // newAttrs: child.attrs || {},
1465
+ // })
1466
+ deleted = deleted.filter((d) => d !== adjDeleted);
1467
+ if (maxSteps !== j) {
1468
+ deleted.push({
1469
+ pos,
1470
+ from: from + j - offset,
1471
+ to,
1472
+ type: 'delete-text',
1473
+ node: delNode,
1474
+ });
1475
+ return [matchedDeleted, deleted];
1476
+ }
1477
+ const nextTextDelete = deleted.find((d) => d.type === 'delete-text' && d.pos === to);
1478
+ if (nextTextDelete) {
1479
+ return matchText(nextTextDelete, insNode, j, matchedDeleted, deleted);
1480
+ }
1481
+ return [matchedDeleted, deleted];
1482
+ }
1483
+ function matchInserted(matchedDeleted, deleted, inserted) {
1468
1484
  var _a;
1469
1485
  let matched = [matchedDeleted, deleted];
1470
1486
  for (let i = 0;; i += 1) {
@@ -1478,32 +1494,7 @@ function matchInserted(matchedDeleted, deleted, inserted, newTr, schema) {
1478
1494
  return matched;
1479
1495
  }
1480
1496
  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
- }
1497
+ matched = matchText(adjDeleted, insNode, 0, matched[0], matched[1]);
1507
1498
  continue;
1508
1499
  }
1509
1500
  else if (insNode.content.size > 0 || (adjDeleted === null || adjDeleted === void 0 ? void 0 : adjDeleted.node.content.size) > 0) {
@@ -1513,15 +1504,32 @@ function matchInserted(matchedDeleted, deleted, inserted, newTr, schema) {
1513
1504
  else {
1514
1505
  matched = [matched[0] + insNode.nodeSize, matched[1].filter((d) => d !== adjDeleted)];
1515
1506
  }
1507
+ // Omit dataTracked
1508
+ const { dataTracked, ...newAttrs } = insNode.attrs || {};
1516
1509
  matched[1].push({
1517
1510
  pos: adjDeleted.pos,
1518
1511
  type: 'update-node-attrs',
1519
1512
  node: adjDeleted.node,
1520
- // Should check the attrs for equality in fixInconsistentChanges? to remove dataTracked completely
1521
- newAttrs: insNode.attrs || {},
1513
+ newAttrs,
1522
1514
  });
1523
1515
  }
1524
- }
1516
+ }
1517
+
1518
+ /*!
1519
+ * © 2021 Atypon Systems LLC
1520
+ *
1521
+ * Licensed under the Apache License, Version 2.0 (the "License");
1522
+ * you may not use this file except in compliance with the License.
1523
+ * You may obtain a copy of the License at
1524
+ *
1525
+ * http://www.apache.org/licenses/LICENSE-2.0
1526
+ *
1527
+ * Unless required by applicable law or agreed to in writing, software
1528
+ * distributed under the License is distributed on an "AS IS" BASIS,
1529
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1530
+ * See the License for the specific language governing permissions and
1531
+ * limitations under the License.
1532
+ */
1525
1533
  /**
1526
1534
  * Cuts a fragment similar to Fragment.cut but also removes the parent node.
1527
1535
  *
@@ -1543,8 +1551,8 @@ function cutFragment(matched, deleted, content) {
1543
1551
  newContent.push(...cut[1].content);
1544
1552
  }
1545
1553
  else if (child.isText && matched + child.nodeSize > deleted) {
1546
- if (matched - deleted + 1 > 0) {
1547
- newContent.push(child.cut(0, matched - deleted + 1));
1554
+ if (deleted - matched > 0) {
1555
+ newContent.push(child.cut(deleted - matched));
1548
1556
  }
1549
1557
  else {
1550
1558
  newContent.push(child);
@@ -1579,7 +1587,7 @@ function diffChangeSteps(deleted, inserted, newTr, schema) {
1579
1587
  return;
1580
1588
  }
1581
1589
  // Start diffing from the start of the deleted range
1582
- const deleteStart = deleted.reduce((acc, cur) => {
1590
+ const deleteStart = updatedDeleted.reduce((acc, cur) => {
1583
1591
  if (cur.type === 'delete-node') {
1584
1592
  return Math.min(acc, cur.pos);
1585
1593
  }
@@ -1588,13 +1596,13 @@ function diffChangeSteps(deleted, inserted, newTr, schema) {
1588
1596
  }
1589
1597
  return acc;
1590
1598
  }, Number.MAX_SAFE_INTEGER);
1591
- const [inDeleted, updatedDel] = matchInserted(deleteStart, updatedDeleted, ins.slice.content);
1592
- if (inDeleted === deleteStart) {
1599
+ const [matchedDeleted, updatedDel] = matchInserted(deleteStart, updatedDeleted, ins.slice.content);
1600
+ if (matchedDeleted === deleteStart) {
1593
1601
  updated.push(ins);
1594
1602
  return;
1595
1603
  }
1596
1604
  updatedDeleted = updatedDel;
1597
- const newInserted = cutFragment(0, inDeleted, ins.slice.content)[1];
1605
+ const [_, newInserted] = cutFragment(0, matchedDeleted - deleteStart, ins.slice.content);
1598
1606
  if (newInserted.size > 0) {
1599
1607
  updated.push({
1600
1608
  ...ins,
@@ -1664,7 +1672,8 @@ function trackTransaction(tr, oldState, newTr, authorID) {
1664
1672
  const inserted = steps.filter((s) => s.type === 'insert-slice');
1665
1673
  steps = diffChangeSteps(deleted, inserted, newTr, oldState.schema);
1666
1674
  log.info('DIFFED STEPS: ', steps);
1667
- const [mapping, selectionPos] = processChangeSteps(steps, startPos, newTr, emptyAttrs, oldState.schema);
1675
+ const [mapping, selectionPos] = processChangeSteps(steps, startPos || tr.selection.head, // Incase startPos is it's default value 0, use the old selection head
1676
+ newTr, emptyAttrs, oldState.schema);
1668
1677
  if (!wasNodeSelection) {
1669
1678
  const sel = getSelectionStaticConstructor(tr.selection);
1670
1679
  // Use Selection.near to fix selections that point to a block node instead of inline content
@@ -1824,7 +1833,7 @@ const trackChangesPlugin = (opts = { userID: 'anonymous:Anonymous' }) => {
1824
1833
  setAction(createdTr, TrackChangesAction.updateChanges, ids);
1825
1834
  }
1826
1835
  else if (getAction(tr, TrackChangesAction.applyAndRemoveChanges)) {
1827
- const mapping = applyAcceptedRejectedChanges(createdTr, oldState.schema, changeSet.nodeChanges);
1836
+ const mapping = applyAcceptedRejectedChanges(createdTr, oldState.schema, changeSet.bothNodeChanges);
1828
1837
  applyAcceptedRejectedChanges(createdTr, oldState.schema, changeSet.textChanges, mapping);
1829
1838
  setAction(createdTr, TrackChangesAction.refreshChanges, true);
1830
1839
  }
package/dist/index.js CHANGED
@@ -224,6 +224,9 @@ class ChangeSet {
224
224
  get nodeAttrChanges() {
225
225
  return this.changes.filter((c) => c.type === 'node-attr-change');
226
226
  }
227
+ get bothNodeChanges() {
228
+ return this.changes.filter((c) => c.type === 'node-change' || c.type === 'node-attr-change');
229
+ }
227
230
  get isEmpty() {
228
231
  return __classPrivateFieldGet(this, _ChangeSet_changes, "f").length === 0;
229
232
  }
@@ -537,11 +540,11 @@ function updateChangeAttrs(tr, change, trackedAttrs, schema) {
537
540
  // TODO add operation based on mark type if it's undefined?
538
541
  tr.addMark(change.from, change.to, oldMark.type.create({ ...oldMark.attrs, dataTracked: { ...oldTrackData, ...trackedAttrs } }));
539
542
  }
540
- else if (change.type === 'node-change' && !operation) {
543
+ else if ((change.type === 'node-change' || change.type === 'node-attr-change') && !operation) {
541
544
  // Very weird edge-case if this happens
542
545
  tr.setNodeMarkup(change.from, undefined, { ...node.attrs, dataTracked: null }, node.marks);
543
546
  }
544
- else if (change.type === 'node-change') {
547
+ else if (change.type === 'node-change' || change.type === 'node-attr-change') {
545
548
  const newDataTracked = (getBlockInlineTrackedData(node) || []).map((oldTrack) => {
546
549
  if (oldTrack.operation === operation) {
547
550
  return { ...oldTrack, ...trackedAttrs };
@@ -611,13 +614,11 @@ function applyAcceptedRejectedChanges(tr, schema, changes, deleteMap = new Mappi
611
614
  }
612
615
  else if (ChangeSet.isNodeAttrChange(change) &&
613
616
  change.dataTracked.status === CHANGE_STATUS.accepted) {
614
- const attrs = { ...node.attrs, dataTracked: null };
615
- tr.setNodeMarkup(from, undefined, attrs, node.marks);
617
+ tr.setNodeMarkup(from, undefined, { ...node.attrs, dataTracked: null }, node.marks);
616
618
  }
617
619
  else if (ChangeSet.isNodeAttrChange(change) &&
618
620
  change.dataTracked.status === CHANGE_STATUS.rejected) {
619
- const attrs = { ...change.oldAttrs, dataTracked: null };
620
- tr.setNodeMarkup(from, undefined, attrs, node.marks);
621
+ tr.setNodeMarkup(from, undefined, { ...change.oldAttrs, dataTracked: null }, node.marks);
621
622
  }
622
623
  });
623
624
  return deleteMap;
@@ -937,10 +938,12 @@ function createNewDeleteAttrs(attrs) {
937
938
  };
938
939
  }
939
940
  function createNewUpdateAttrs(attrs, oldAttrs) {
941
+ // Omit dataTracked
942
+ const { dataTracked, ...restAttrs } = oldAttrs;
940
943
  return {
941
944
  ...attrs,
942
945
  operation: CHANGE_OPERATION.set_node_attributes,
943
- oldAttrs,
946
+ oldAttrs: restAttrs,
944
947
  };
945
948
  }
946
949
 
@@ -1411,22 +1414,18 @@ function processChangeSteps(changes, startPos, newTr, emptyAttrs, schema) {
1411
1414
  else if (c.type === 'update-node-attrs') {
1412
1415
  const oldDataTracked = getBlockInlineTrackedData(c.node) || [];
1413
1416
  const oldUpdate = oldDataTracked.find((d) => d.operation === CHANGE_OPERATION.set_node_attributes);
1414
- let newDataTracked = oldDataTracked;
1415
- if (oldUpdate) {
1416
- newDataTracked = [
1417
- ...oldDataTracked.filter((d) => d === oldUpdate),
1418
- {
1419
- ...oldUpdate,
1420
- updatedAt: emptyAttrs.updatedAt,
1421
- },
1422
- ];
1423
- }
1424
- else if (oldDataTracked.length === 0 ||
1425
- oldDataTracked.find((d) => d.operation === CHANGE_OPERATION.delete)) {
1426
- newDataTracked = [
1427
- ...oldDataTracked,
1428
- addTrackIdIfDoesntExist(createNewUpdateAttrs(emptyAttrs, c.node.attrs)),
1429
- ];
1417
+ const { dataTracked, ...oldAttrs } = (oldUpdate === null || oldUpdate === void 0 ? void 0 : oldUpdate.oldAttrs) || c.node.attrs;
1418
+ const newDataTracked = [...oldDataTracked.filter((d) => !oldUpdate || d.id !== oldUpdate.id)];
1419
+ const newUpdate = oldUpdate
1420
+ ? {
1421
+ ...oldUpdate,
1422
+ updatedAt: emptyAttrs.updatedAt,
1423
+ }
1424
+ : addTrackIdIfDoesntExist(createNewUpdateAttrs(emptyAttrs, c.node.attrs));
1425
+ // Dont add update changes if there exists already an insert change for this node
1426
+ if (JSON.stringify(oldAttrs) !== JSON.stringify(c.newAttrs) &&
1427
+ !oldDataTracked.find((d) => d.operation === CHANGE_OPERATION.insert)) {
1428
+ newDataTracked.push(newUpdate);
1430
1429
  }
1431
1430
  newTr.setNodeMarkup(mapping.map(c.pos), undefined, {
1432
1431
  ...c.newAttrs,
@@ -1441,22 +1440,39 @@ function processChangeSteps(changes, startPos, newTr, emptyAttrs, schema) {
1441
1440
  return [mapping, selectionPos];
1442
1441
  }
1443
1442
 
1444
- /*!
1445
- * © 2021 Atypon Systems LLC
1446
- *
1447
- * Licensed under the Apache License, Version 2.0 (the "License");
1448
- * you may not use this file except in compliance with the License.
1449
- * You may obtain a copy of the License at
1450
- *
1451
- * http://www.apache.org/licenses/LICENSE-2.0
1452
- *
1453
- * Unless required by applicable law or agreed to in writing, software
1454
- * distributed under the License is distributed on an "AS IS" BASIS,
1455
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1456
- * See the License for the specific language governing permissions and
1457
- * limitations under the License.
1458
- */
1459
- function matchInserted(matchedDeleted, deleted, inserted, newTr, schema) {
1443
+ function matchText(adjDeleted, insNode, offset, matchedDeleted, deleted) {
1444
+ const { pos, from, to, node: delNode } = adjDeleted;
1445
+ let j = offset, d = from - pos, maxSteps = to - Math.max(pos, from);
1446
+ // Match text inside the inserted text node to the deleted text node
1447
+ for (; maxSteps !== j && insNode.text[j] !== undefined && insNode.text[j] === delNode.text[d]; j += 1, d += 1) {
1448
+ matchedDeleted += 1;
1449
+ }
1450
+ // this is needed incase diffing tr.doc
1451
+ // deleted.push({
1452
+ // pos: pos,
1453
+ // type: 'update-node-attrs',
1454
+ // // Should check the attrs for equality in fixInconsistentChanges? to remove dataTracked completely
1455
+ // oldAttrs: adjDeleted.node.attrs || {},
1456
+ // newAttrs: child.attrs || {},
1457
+ // })
1458
+ deleted = deleted.filter((d) => d !== adjDeleted);
1459
+ if (maxSteps !== j) {
1460
+ deleted.push({
1461
+ pos,
1462
+ from: from + j - offset,
1463
+ to,
1464
+ type: 'delete-text',
1465
+ node: delNode,
1466
+ });
1467
+ return [matchedDeleted, deleted];
1468
+ }
1469
+ const nextTextDelete = deleted.find((d) => d.type === 'delete-text' && d.pos === to);
1470
+ if (nextTextDelete) {
1471
+ return matchText(nextTextDelete, insNode, j, matchedDeleted, deleted);
1472
+ }
1473
+ return [matchedDeleted, deleted];
1474
+ }
1475
+ function matchInserted(matchedDeleted, deleted, inserted) {
1460
1476
  var _a;
1461
1477
  let matched = [matchedDeleted, deleted];
1462
1478
  for (let i = 0;; i += 1) {
@@ -1470,32 +1486,7 @@ function matchInserted(matchedDeleted, deleted, inserted, newTr, schema) {
1470
1486
  return matched;
1471
1487
  }
1472
1488
  else if (insNode.isText && (adjDeleted === null || adjDeleted === void 0 ? void 0 : adjDeleted.node)) {
1473
- adjDeleted = adjDeleted;
1474
- const { pos, from, to, node: delNode } = adjDeleted;
1475
- let j = 0, d = from - pos, maxSteps = to - Math.max(pos, from);
1476
- // Match text inside the inserted text node to the deleted text node
1477
- for (; maxSteps !== j && insNode.text[j] !== undefined && insNode.text[j] === delNode.text[d]; j += 1, d += 1) {
1478
- matched[0] += 1;
1479
- }
1480
- // this is needed incase diffing tr.doc
1481
- // deleted.push({
1482
- // pos: pos,
1483
- // type: 'update-node-attrs',
1484
- // // Should check the attrs for equality in fixInconsistentChanges? to remove dataTracked completely
1485
- // oldAttrs: adjDeleted.node.attrs || {},
1486
- // newAttrs: child.attrs || {},
1487
- // })
1488
- matched = [matched[0], matched[1].filter((d) => d !== adjDeleted)];
1489
- if (maxSteps !== j) {
1490
- matched[1].push({
1491
- pos,
1492
- from: Math.max(pos, from) + j,
1493
- to,
1494
- type: 'delete-text',
1495
- node: delNode,
1496
- });
1497
- return matched;
1498
- }
1489
+ matched = matchText(adjDeleted, insNode, 0, matched[0], matched[1]);
1499
1490
  continue;
1500
1491
  }
1501
1492
  else if (insNode.content.size > 0 || (adjDeleted === null || adjDeleted === void 0 ? void 0 : adjDeleted.node.content.size) > 0) {
@@ -1505,15 +1496,32 @@ function matchInserted(matchedDeleted, deleted, inserted, newTr, schema) {
1505
1496
  else {
1506
1497
  matched = [matched[0] + insNode.nodeSize, matched[1].filter((d) => d !== adjDeleted)];
1507
1498
  }
1499
+ // Omit dataTracked
1500
+ const { dataTracked, ...newAttrs } = insNode.attrs || {};
1508
1501
  matched[1].push({
1509
1502
  pos: adjDeleted.pos,
1510
1503
  type: 'update-node-attrs',
1511
1504
  node: adjDeleted.node,
1512
- // Should check the attrs for equality in fixInconsistentChanges? to remove dataTracked completely
1513
- newAttrs: insNode.attrs || {},
1505
+ newAttrs,
1514
1506
  });
1515
1507
  }
1516
- }
1508
+ }
1509
+
1510
+ /*!
1511
+ * © 2021 Atypon Systems LLC
1512
+ *
1513
+ * Licensed under the Apache License, Version 2.0 (the "License");
1514
+ * you may not use this file except in compliance with the License.
1515
+ * You may obtain a copy of the License at
1516
+ *
1517
+ * http://www.apache.org/licenses/LICENSE-2.0
1518
+ *
1519
+ * Unless required by applicable law or agreed to in writing, software
1520
+ * distributed under the License is distributed on an "AS IS" BASIS,
1521
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1522
+ * See the License for the specific language governing permissions and
1523
+ * limitations under the License.
1524
+ */
1517
1525
  /**
1518
1526
  * Cuts a fragment similar to Fragment.cut but also removes the parent node.
1519
1527
  *
@@ -1535,8 +1543,8 @@ function cutFragment(matched, deleted, content) {
1535
1543
  newContent.push(...cut[1].content);
1536
1544
  }
1537
1545
  else if (child.isText && matched + child.nodeSize > deleted) {
1538
- if (matched - deleted + 1 > 0) {
1539
- newContent.push(child.cut(0, matched - deleted + 1));
1546
+ if (deleted - matched > 0) {
1547
+ newContent.push(child.cut(deleted - matched));
1540
1548
  }
1541
1549
  else {
1542
1550
  newContent.push(child);
@@ -1571,7 +1579,7 @@ function diffChangeSteps(deleted, inserted, newTr, schema) {
1571
1579
  return;
1572
1580
  }
1573
1581
  // Start diffing from the start of the deleted range
1574
- const deleteStart = deleted.reduce((acc, cur) => {
1582
+ const deleteStart = updatedDeleted.reduce((acc, cur) => {
1575
1583
  if (cur.type === 'delete-node') {
1576
1584
  return Math.min(acc, cur.pos);
1577
1585
  }
@@ -1580,13 +1588,13 @@ function diffChangeSteps(deleted, inserted, newTr, schema) {
1580
1588
  }
1581
1589
  return acc;
1582
1590
  }, Number.MAX_SAFE_INTEGER);
1583
- const [inDeleted, updatedDel] = matchInserted(deleteStart, updatedDeleted, ins.slice.content);
1584
- if (inDeleted === deleteStart) {
1591
+ const [matchedDeleted, updatedDel] = matchInserted(deleteStart, updatedDeleted, ins.slice.content);
1592
+ if (matchedDeleted === deleteStart) {
1585
1593
  updated.push(ins);
1586
1594
  return;
1587
1595
  }
1588
1596
  updatedDeleted = updatedDel;
1589
- const newInserted = cutFragment(0, inDeleted, ins.slice.content)[1];
1597
+ const [_, newInserted] = cutFragment(0, matchedDeleted - deleteStart, ins.slice.content);
1590
1598
  if (newInserted.size > 0) {
1591
1599
  updated.push({
1592
1600
  ...ins,
@@ -1656,7 +1664,8 @@ function trackTransaction(tr, oldState, newTr, authorID) {
1656
1664
  const inserted = steps.filter((s) => s.type === 'insert-slice');
1657
1665
  steps = diffChangeSteps(deleted, inserted, newTr, oldState.schema);
1658
1666
  log.info('DIFFED STEPS: ', steps);
1659
- const [mapping, selectionPos] = processChangeSteps(steps, startPos, newTr, emptyAttrs, oldState.schema);
1667
+ const [mapping, selectionPos] = processChangeSteps(steps, startPos || tr.selection.head, // Incase startPos is it's default value 0, use the old selection head
1668
+ newTr, emptyAttrs, oldState.schema);
1660
1669
  if (!wasNodeSelection) {
1661
1670
  const sel = getSelectionStaticConstructor(tr.selection);
1662
1671
  // Use Selection.near to fix selections that point to a block node instead of inline content
@@ -1816,7 +1825,7 @@ const trackChangesPlugin = (opts = { userID: 'anonymous:Anonymous' }) => {
1816
1825
  setAction(createdTr, TrackChangesAction.updateChanges, ids);
1817
1826
  }
1818
1827
  else if (getAction(tr, TrackChangesAction.applyAndRemoveChanges)) {
1819
- const mapping = applyAcceptedRejectedChanges(createdTr, oldState.schema, changeSet.nodeChanges);
1828
+ const mapping = applyAcceptedRejectedChanges(createdTr, oldState.schema, changeSet.bothNodeChanges);
1820
1829
  applyAcceptedRejectedChanges(createdTr, oldState.schema, changeSet.textChanges, mapping);
1821
1830
  setAction(createdTr, TrackChangesAction.refreshChanges, true);
1822
1831
  }
@@ -32,7 +32,7 @@ declare type InsertDeleteAttrs = {
32
32
  createdAt: number;
33
33
  updatedAt: number;
34
34
  };
35
- declare type UpdateAttrs = Omit<InsertDeleteAttrs, 'operation'> & {
35
+ export declare type UpdateAttrs = Omit<InsertDeleteAttrs, 'operation'> & {
36
36
  operation: CHANGE_OPERATION.set_node_attributes;
37
37
  oldAttrs: Record<string, any>;
38
38
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@manuscripts/track-changes-plugin",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "author": "Atypon Systems LLC",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/Atypon-OpenSource/manuscripts-quarterback/tree/main/quarterback-packages/track-changes-plugin",