@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.
- package/dist/ChangeSet.d.ts +1 -0
- package/dist/actions.d.ts +0 -2
- package/dist/change-steps/diffChangeSteps.d.ts +1 -20
- package/dist/change-steps/matchInserted.d.ts +13 -0
- package/dist/changes/findChanges.d.ts +3 -3
- package/dist/commands.d.ts +1 -5
- package/dist/index.cjs +160 -154
- package/dist/index.js +160 -154
- package/dist/mutate/deleteText.d.ts +1 -0
- package/dist/types/change.d.ts +1 -1
- package/package.json +12 -12
- 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
|
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
|
|
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
|
-
*
|
|
23
|
-
*
|
|
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
|
*/
|
package/dist/commands.d.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
639
|
-
*
|
|
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
|
|
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
|
-
|
|
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;
|
|
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
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
1462
|
-
*
|
|
1463
|
-
*
|
|
1464
|
-
*
|
|
1465
|
-
*
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
1547
|
-
newContent.push(child.cut(
|
|
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
|
|
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 =
|
|
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 [
|
|
1592
|
-
if (
|
|
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,
|
|
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
|
|
1689
|
+
steps = diffChangeSteps(deleted, inserted);
|
|
1666
1690
|
log.info('DIFFED STEPS: ', steps);
|
|
1667
|
-
const [mapping, selectionPos] = processChangeSteps(steps, startPos
|
|
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
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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;
|