@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.
- package/dist/ChangeSet.d.ts +1 -0
- package/dist/change-steps/diffChangeSteps.d.ts +0 -2
- package/dist/change-steps/matchInserted.d.ts +3 -0
- package/dist/index.cjs +85 -76
- package/dist/index.js +85 -76
- package/dist/types/change.d.ts +1 -1
- package/package.json +1 -1
- package/dist/index.es.js +0 -1547
- package/dist/track/applyChanges.d.ts +0 -28
- package/dist/track/deleteNode.d.ts +0 -27
- package/dist/track/findChanges.d.ts +0 -27
- package/dist/track/fixInconsistentChanges.d.ts +0 -29
- package/dist/track/mergeNode.d.ts +0 -25
- package/dist/track/node-utils.d.ts +0 -27
- package/dist/track/steps/deleteAndMergeSplitNodes.d.ts +0 -53
- package/dist/track/steps/mergeTrackedMarks.d.ts +0 -29
- package/dist/track/steps/setFragmentAsInserted.d.ts +0 -18
- package/dist/track/steps/track-utils.d.ts +0 -18
- package/dist/track/steps/trackReplaceAroundStep.d.ts +0 -4
- package/dist/track/steps/trackReplaceStep.d.ts +0 -4
- package/dist/track/trackTransaction.d.ts +0 -17
- package/dist/track/updateChangeAttrs.d.ts +0 -21
- package/dist/types/editor.d.ts +0 -23
package/dist/ChangeSet.d.ts
CHANGED
|
@@ -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[];
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
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
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
1547
|
-
newContent.push(child.cut(
|
|
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 =
|
|
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 [
|
|
1592
|
-
if (
|
|
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,
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
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
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
1539
|
-
newContent.push(child.cut(
|
|
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 =
|
|
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 [
|
|
1584
|
-
if (
|
|
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,
|
|
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
|
|
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.
|
|
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
|
}
|
package/dist/types/change.d.ts
CHANGED
|
@@ -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.
|
|
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",
|