@manuscripts/track-changes-plugin 1.10.7 → 1.10.8-LEAN-4160.1
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/cjs/ChangeSet.js +4 -2
- package/dist/cjs/changes/applyChanges.js +45 -8
- package/dist/cjs/changes/updateChangesStatus.js +19 -1
- package/dist/cjs/compute/setFragmentAsInserted.js +9 -1
- package/dist/cjs/plugin.js +1 -1
- package/dist/cjs/steps/trackReplaceStep.js +9 -2
- package/dist/cjs/steps/trackTransaction.js +9 -4
- package/dist/cjs/types/change.js +1 -0
- package/dist/cjs/utils/track-utils.js +123 -1
- package/dist/es/ChangeSet.js +4 -2
- package/dist/es/changes/applyChanges.js +45 -8
- package/dist/es/changes/updateChangesStatus.js +19 -1
- package/dist/es/compute/setFragmentAsInserted.js +7 -0
- package/dist/es/plugin.js +1 -1
- package/dist/es/steps/trackReplaceStep.js +10 -3
- package/dist/es/steps/trackTransaction.js +9 -4
- package/dist/es/types/change.js +1 -0
- package/dist/es/utils/track-utils.js +117 -1
- package/dist/types/compute/setFragmentAsInserted.d.ts +1 -0
- package/dist/types/steps/trackReplaceStep.d.ts +1 -1
- package/dist/types/steps/trackTransaction.d.ts +2 -1
- package/dist/types/types/change.d.ts +7 -2
- package/dist/types/types/track.d.ts +1 -1
- package/dist/types/utils/track-utils.d.ts +7 -1
- package/package.json +1 -1
package/dist/cjs/ChangeSet.js
CHANGED
|
@@ -91,7 +91,8 @@ class ChangeSet {
|
|
|
91
91
|
}
|
|
92
92
|
rootNodes.push([change]);
|
|
93
93
|
});
|
|
94
|
-
return rootNodes.filter((changes) => changes.filter((c) => c.dataTracked.operation !== change_1.CHANGE_OPERATION.reference
|
|
94
|
+
return rootNodes.filter((changes) => changes.filter((c) => c.dataTracked.operation !== change_1.CHANGE_OPERATION.reference &&
|
|
95
|
+
!(c.dataTracked.moveNodeId && c.dataTracked.operation === change_1.CHANGE_OPERATION.delete)).length);
|
|
95
96
|
}
|
|
96
97
|
get pending() {
|
|
97
98
|
return this.changes.filter((c) => c.dataTracked.status === change_1.CHANGE_STATUS.pending);
|
|
@@ -167,7 +168,8 @@ class ChangeSet {
|
|
|
167
168
|
const { status, operation } = change.dataTracked;
|
|
168
169
|
return (((operation === change_1.CHANGE_OPERATION.insert ||
|
|
169
170
|
operation === change_1.CHANGE_OPERATION.node_split ||
|
|
170
|
-
operation === change_1.CHANGE_OPERATION.wrap_with_node
|
|
171
|
+
operation === change_1.CHANGE_OPERATION.wrap_with_node ||
|
|
172
|
+
operation === change_1.CHANGE_OPERATION.move) &&
|
|
171
173
|
status === change_1.CHANGE_STATUS.rejected) ||
|
|
172
174
|
(operation === change_1.CHANGE_OPERATION.delete && status === change_1.CHANGE_STATUS.accepted));
|
|
173
175
|
}
|
|
@@ -20,13 +20,8 @@ exports.getUpdatedDataTracked = getUpdatedDataTracked;
|
|
|
20
20
|
function applyAcceptedRejectedChanges(tr, schema, changes, changeSet, deleteMap = new prosemirror_transform_1.Mapping()) {
|
|
21
21
|
changes.sort((c1, c2) => c1.dataTracked.updatedAt - c2.dataTracked.updatedAt);
|
|
22
22
|
changes.forEach((change) => {
|
|
23
|
-
if (change.dataTracked.
|
|
24
|
-
|
|
25
|
-
return (0, revertChange_1.revertSplitNodeChange)(tr, change, changeSet);
|
|
26
|
-
}
|
|
27
|
-
if (change.dataTracked.operation === change_1.CHANGE_OPERATION.wrap_with_node) {
|
|
28
|
-
return (0, revertChange_1.revertWrapNodeChange)(tr, change);
|
|
29
|
-
}
|
|
23
|
+
if (change.dataTracked.operation === change_1.CHANGE_OPERATION.move) {
|
|
24
|
+
return;
|
|
30
25
|
}
|
|
31
26
|
const { pos: from, deleted } = deleteMap.mapResult(change.from);
|
|
32
27
|
const node = tr.doc.nodeAt(from);
|
|
@@ -35,9 +30,17 @@ function applyAcceptedRejectedChanges(tr, schema, changes, changeSet, deleteMap
|
|
|
35
30
|
return;
|
|
36
31
|
}
|
|
37
32
|
if (!node) {
|
|
38
|
-
!deleted && logger_1.log.warn('
|
|
33
|
+
!deleted && logger_1.log.warn('No node found to update for change', change);
|
|
39
34
|
return;
|
|
40
35
|
}
|
|
36
|
+
if (change.dataTracked.status === change_1.CHANGE_STATUS.rejected) {
|
|
37
|
+
if (change.dataTracked.operation === change_1.CHANGE_OPERATION.node_split) {
|
|
38
|
+
return (0, revertChange_1.revertSplitNodeChange)(tr, change, changeSet);
|
|
39
|
+
}
|
|
40
|
+
if (change.dataTracked.operation === change_1.CHANGE_OPERATION.wrap_with_node) {
|
|
41
|
+
return (0, revertChange_1.revertWrapNodeChange)(tr, change);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
41
44
|
if (ChangeSet_1.ChangeSet.isTextChange(change) && noChangeNeeded) {
|
|
42
45
|
tr.removeMark(from, deleteMap.map(change.to), schema.marks.tracked_insert);
|
|
43
46
|
tr.removeMark(from, deleteMap.map(change.to), schema.marks.tracked_delete);
|
|
@@ -72,6 +75,40 @@ function applyAcceptedRejectedChanges(tr, schema, changes, changeSet, deleteMap
|
|
|
72
75
|
tr.setNodeMarkup(from, undefined, Object.assign(Object.assign({}, node.attrs), { dataTracked: getUpdatedDataTracked(node.attrs.dataTracked, change.id) }), node.marks);
|
|
73
76
|
}
|
|
74
77
|
});
|
|
78
|
+
changes.forEach((change) => {
|
|
79
|
+
if (change.dataTracked.operation !== change_1.CHANGE_OPERATION.move) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const { pos: from, deleted } = deleteMap.mapResult(change.from);
|
|
83
|
+
const node = tr.doc.nodeAt(from);
|
|
84
|
+
if (deleted || !node) {
|
|
85
|
+
if (!deleted && !node) {
|
|
86
|
+
logger_1.log.warn('No node found for move change', { change });
|
|
87
|
+
}
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (change.dataTracked.status === change_1.CHANGE_STATUS.accepted) {
|
|
91
|
+
const originalChange = changeSet.changes.find((c) => c.dataTracked.moveNodeId === change.dataTracked.moveNodeId &&
|
|
92
|
+
c.dataTracked.operation === change_1.CHANGE_OPERATION.delete);
|
|
93
|
+
if (originalChange) {
|
|
94
|
+
const { pos: originalFrom } = deleteMap.mapResult(originalChange.from);
|
|
95
|
+
const originalNode = tr.doc.nodeAt(originalFrom);
|
|
96
|
+
const attrs = Object.assign(Object.assign({}, node.attrs), { dataTracked: getUpdatedDataTracked(node.attrs.dataTracked, change.id) });
|
|
97
|
+
tr.setNodeMarkup(from, undefined, attrs, node.marks);
|
|
98
|
+
if (originalNode) {
|
|
99
|
+
tr.delete(originalFrom, originalFrom + originalNode.nodeSize);
|
|
100
|
+
deleteMap.appendMap(tr.steps[tr.steps.length - 1].getMap());
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
logger_1.log.warn('No original change found for move operation', { change });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
else if (change.dataTracked.status === change_1.CHANGE_STATUS.rejected) {
|
|
108
|
+
tr.delete(from, from + node.nodeSize);
|
|
109
|
+
deleteMap.appendMap(tr.steps[tr.steps.length - 1].getMap());
|
|
110
|
+
}
|
|
111
|
+
});
|
|
75
112
|
return deleteMap;
|
|
76
113
|
}
|
|
77
114
|
exports.applyAcceptedRejectedChanges = applyAcceptedRejectedChanges;
|
|
@@ -23,6 +23,7 @@ const applyChanges_1 = require("./applyChanges");
|
|
|
23
23
|
const updateChangeAttrs_1 = require("./updateChangeAttrs");
|
|
24
24
|
function updateChangesStatus(createdTr, changeSet, ids, status, userID, oldState) {
|
|
25
25
|
const change = changeSet.get(ids[0]);
|
|
26
|
+
const changeTime = new Date().getTime();
|
|
26
27
|
if (change && status !== change_1.CHANGE_STATUS.pending) {
|
|
27
28
|
const textChanges = [];
|
|
28
29
|
const nonTextChanges = [];
|
|
@@ -40,6 +41,24 @@ function updateChangesStatus(createdTr, changeSet, ids, status, userID, oldState
|
|
|
40
41
|
nonTextChanges.push(relatedRefChange);
|
|
41
42
|
}
|
|
42
43
|
}
|
|
44
|
+
if (c.dataTracked.operation === change_1.CHANGE_OPERATION.move) {
|
|
45
|
+
const oldChange = changeSet.changeTree.find((c) => ChangeSet_1.ChangeSet.isNodeChange(c) &&
|
|
46
|
+
c.dataTracked.operation === 'delete' &&
|
|
47
|
+
c.dataTracked.moveNodeId === change.dataTracked.moveNodeId);
|
|
48
|
+
if (oldChange && ChangeSet_1.ChangeSet.isNodeChange(oldChange)) {
|
|
49
|
+
createdTr = (0, updateChangeAttrs_1.updateChangeAttrs)(createdTr, oldChange, Object.assign(Object.assign({}, oldChange.dataTracked), { status, statusUpdateAt: changeTime, reviewedByID: userID }), oldState.schema);
|
|
50
|
+
oldChange.children.forEach((child) => {
|
|
51
|
+
createdTr = (0, updateChangeAttrs_1.updateChangeAttrs)(createdTr, child, Object.assign(Object.assign({}, child.dataTracked), { status, statusUpdateAt: changeTime, reviewedByID: userID }), oldState.schema);
|
|
52
|
+
if (ChangeSet_1.ChangeSet.isTextChange(child)) {
|
|
53
|
+
textChanges.push(child);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
nonTextChanges.push(child);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
nonTextChanges.push(oldChange);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
43
62
|
}
|
|
44
63
|
}
|
|
45
64
|
});
|
|
@@ -47,7 +66,6 @@ function updateChangesStatus(createdTr, changeSet, ids, status, userID, oldState
|
|
|
47
66
|
(0, applyChanges_1.applyAcceptedRejectedChanges)(createdTr, oldState.schema, textChanges, changeSet, mapping);
|
|
48
67
|
}
|
|
49
68
|
else {
|
|
50
|
-
const changeTime = new Date().getTime();
|
|
51
69
|
ids.forEach((changeId) => {
|
|
52
70
|
const change = changeSet === null || changeSet === void 0 ? void 0 : changeSet.get(changeId);
|
|
53
71
|
if (change) {
|
|
@@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.setFragmentAsNodeSplit = exports.setFragmentAsWrapChange = exports.setFragmentAsInserted = void 0;
|
|
26
|
+
exports.setFragmentAsNodeSplit = exports.setFragmentAsMoveChange = exports.setFragmentAsWrapChange = exports.setFragmentAsInserted = void 0;
|
|
27
27
|
const prosemirror_model_1 = require("prosemirror-model");
|
|
28
28
|
const change_1 = require("../types/change");
|
|
29
29
|
const logger_1 = require("../utils/logger");
|
|
@@ -84,6 +84,14 @@ function setFragmentAsWrapChange(inserted, attrs, schema) {
|
|
|
84
84
|
return prosemirror_model_1.Fragment.from(content);
|
|
85
85
|
}
|
|
86
86
|
exports.setFragmentAsWrapChange = setFragmentAsWrapChange;
|
|
87
|
+
function setFragmentAsMoveChange(fragment, attrs) {
|
|
88
|
+
const content = [];
|
|
89
|
+
fragment.forEach((node) => {
|
|
90
|
+
content.push(node.type.create(Object.assign(Object.assign({}, node.attrs), { dataTracked: [(0, nodeHelpers_1.addTrackIdIfDoesntExist)(trackUtils.createNewMoveAttrs(attrs))] }), node.content, node.marks));
|
|
91
|
+
});
|
|
92
|
+
return prosemirror_model_1.Fragment.from(content);
|
|
93
|
+
}
|
|
94
|
+
exports.setFragmentAsMoveChange = setFragmentAsMoveChange;
|
|
87
95
|
function setFragmentAsNodeSplit($pos, newTr, inserted, attrs) {
|
|
88
96
|
const lastChild = inserted.lastChild;
|
|
89
97
|
const referenceId = (0, uuidv4_1.uuidv4)();
|
package/dist/cjs/plugin.js
CHANGED
|
@@ -100,7 +100,7 @@ const trackChangesPlugin = (opts = { userID: 'anonymous:Anonymous', initialStatu
|
|
|
100
100
|
!skipTrackUsed &&
|
|
101
101
|
!(0, track_utils_1.trFromHistory)(tr) &&
|
|
102
102
|
!(wasAppended && tr.getMeta('origin') === 'paragraphs')) {
|
|
103
|
-
createdTr = (0, trackTransaction_1.trackTransaction)(tr, oldState, createdTr, userID);
|
|
103
|
+
createdTr = (0, trackTransaction_1.trackTransaction)(tr, oldState, createdTr, userID, changeSet);
|
|
104
104
|
}
|
|
105
105
|
docChanged = docChanged || tr.docChanged;
|
|
106
106
|
if (setChangeStatuses) {
|
|
@@ -30,10 +30,15 @@ const deleteAndMergeSplitNodes_1 = require("../mutate/deleteAndMergeSplitNodes")
|
|
|
30
30
|
const logger_1 = require("../utils/logger");
|
|
31
31
|
const trackUtils = __importStar(require("../utils/track-utils"));
|
|
32
32
|
const track_utils_1 = require("../utils/track-utils");
|
|
33
|
-
function trackReplaceStep(step, oldState, newTr,
|
|
33
|
+
function trackReplaceStep(step, oldState, newTr, attrsTemplate, stepResult, currentStepDoc, tr, moveID) {
|
|
34
34
|
logger_1.log.info('###### ReplaceStep ######');
|
|
35
35
|
let selectionPos = 0;
|
|
36
36
|
const changeSteps = [];
|
|
37
|
+
const attrs = Object.assign({}, attrsTemplate);
|
|
38
|
+
if (moveID) {
|
|
39
|
+
console.log('Detected Node Moving ReplaceStep and assigning the following movenodeID: ' + moveID);
|
|
40
|
+
attrs.moveNodeId = moveID;
|
|
41
|
+
}
|
|
37
42
|
step.getMap().forEach((fromA, toA, fromB, toB) => {
|
|
38
43
|
var _a, _b;
|
|
39
44
|
logger_1.log.info(`changed ranges: ${fromA} ${toA} ${fromB} ${toB}`);
|
|
@@ -60,13 +65,15 @@ function trackReplaceStep(step, oldState, newTr, attrs, stepResult, currentStepD
|
|
|
60
65
|
changeSteps.splice(changeSteps.indexOf(backSpacedText));
|
|
61
66
|
}
|
|
62
67
|
const textWasDeleted = !!changeSteps.length && !(fromA === fromB);
|
|
63
|
-
console.log(textWasDeleted);
|
|
64
68
|
if (!backSpacedText && newSliceContent.size > 0) {
|
|
65
69
|
logger_1.log.info('newSliceContent', newSliceContent);
|
|
66
70
|
let fragment = (0, setFragmentAsInserted_1.setFragmentAsInserted)(newSliceContent, trackUtils.createNewInsertAttrs(attrs), oldState.schema);
|
|
67
71
|
if ((0, track_utils_1.isSplitStep)(step, oldState.selection, tr.getMeta('uiEvent'))) {
|
|
68
72
|
fragment = (0, setFragmentAsInserted_1.setFragmentAsNodeSplit)(newTr.doc.resolve(step.from), newTr, fragment, attrs);
|
|
69
73
|
}
|
|
74
|
+
if (moveID) {
|
|
75
|
+
fragment = (0, setFragmentAsInserted_1.setFragmentAsMoveChange)(newSliceContent, trackUtils.createNewMoveAttrs(attrs));
|
|
76
|
+
}
|
|
70
77
|
const openStart = slice.openStart !== slice.openEnd ? 0 : slice.openStart;
|
|
71
78
|
const openEnd = slice.openStart !== slice.openEnd ? 0 : slice.openEnd;
|
|
72
79
|
changeSteps.push({
|
|
@@ -13,13 +13,14 @@ const nodeHelpers_1 = require("../compute/nodeHelpers");
|
|
|
13
13
|
const change_1 = require("../types/change");
|
|
14
14
|
const logger_1 = require("../utils/logger");
|
|
15
15
|
const mapChangeStep_1 = require("../utils/mapChangeStep");
|
|
16
|
+
const track_utils_1 = require("../utils/track-utils");
|
|
16
17
|
const uuidv4_1 = require("../utils/uuidv4");
|
|
17
18
|
const trackAttrsChange_1 = __importDefault(require("./trackAttrsChange"));
|
|
18
19
|
const trackReplaceAroundStep_1 = require("./trackReplaceAroundStep");
|
|
19
20
|
const trackReplaceStep_1 = require("./trackReplaceStep");
|
|
20
21
|
const getSelectionStaticConstructor = (sel) => Object.getPrototypeOf(sel).constructor;
|
|
21
22
|
const isHighlightMarkerNode = (node) => node && node.type === node.type.schema.nodes.highlight_marker;
|
|
22
|
-
function trackTransaction(tr, oldState, newTr, authorID) {
|
|
23
|
+
function trackTransaction(tr, oldState, newTr, authorID, changeSet) {
|
|
23
24
|
var _a, _b, _c, _d, _e;
|
|
24
25
|
const emptyAttrs = {
|
|
25
26
|
authorID,
|
|
@@ -35,7 +36,10 @@ function trackTransaction(tr, oldState, newTr, authorID) {
|
|
|
35
36
|
let iters = 0;
|
|
36
37
|
logger_1.log.info('ORIGINAL transaction', tr);
|
|
37
38
|
let trContext = {};
|
|
38
|
-
|
|
39
|
+
const movingStepsAssociated = (0, track_utils_1.HasMoveOperations)(tr);
|
|
40
|
+
(0, track_utils_1.handleDirectPendingMoveDeletions)(tr, newTr, movingStepsAssociated);
|
|
41
|
+
const cleanSteps = (0, track_utils_1.filterMeaninglessMoveSteps)(tr, movingStepsAssociated);
|
|
42
|
+
for (let i = cleanSteps.length - 1; i >= 0; i--) {
|
|
39
43
|
const step = tr.steps[i];
|
|
40
44
|
logger_1.log.info('transaction step', step);
|
|
41
45
|
iters += 1;
|
|
@@ -64,7 +68,7 @@ function trackTransaction(tr, oldState, newTr, authorID) {
|
|
|
64
68
|
}
|
|
65
69
|
const newStep = new prosemirror_transform_1.ReplaceStep(thisStepMapping.map(invertedStep.from), thisStepMapping.map(invertedStep.to), invertedStep.slice);
|
|
66
70
|
const stepResult = newTr.maybeStep(newStep);
|
|
67
|
-
let [steps, startPos] = (0, trackReplaceStep_1.trackReplaceStep)(step, oldState, newTr, emptyAttrs, stepResult, tr.docs[i], tr);
|
|
71
|
+
let [steps, startPos] = (0, trackReplaceStep_1.trackReplaceStep)(step, oldState, newTr, emptyAttrs, stepResult, tr.docs[i], tr, movingStepsAssociated.get(step));
|
|
68
72
|
if (steps.length === 1) {
|
|
69
73
|
const step = steps[0];
|
|
70
74
|
if (isHighlightMarkerNode((step === null || step === void 0 ? void 0 : step.node) || ((_d = (_c = step === null || step === void 0 ? void 0 : step.slice) === null || _c === void 0 ? void 0 : _c.content) === null || _d === void 0 ? void 0 : _d.content[0]))) {
|
|
@@ -78,7 +82,8 @@ function trackTransaction(tr, oldState, newTr, authorID) {
|
|
|
78
82
|
const inserted = steps.filter((s) => s.type === 'insert-slice');
|
|
79
83
|
steps = (0, diffChangeSteps_1.diffChangeSteps)(deleted, inserted);
|
|
80
84
|
logger_1.log.info('DIFFED STEPS: ', steps);
|
|
81
|
-
const [mapping, selectionPos] = (0, processChangeSteps_1.processChangeSteps)(steps, startPos || tr.selection.head, newTr,
|
|
85
|
+
const [mapping, selectionPos] = (0, processChangeSteps_1.processChangeSteps)(steps, startPos || tr.selection.head, newTr, movingStepsAssociated.has(step)
|
|
86
|
+
? Object.assign(Object.assign({}, emptyAttrs), { moveNodeId: movingStepsAssociated.get(step) }) : emptyAttrs, oldState.schema, deletedNodeMapping);
|
|
82
87
|
if (!wasNodeSelection && !setsNewSelection) {
|
|
83
88
|
const sel = getSelectionStaticConstructor(tr.selection);
|
|
84
89
|
const near = sel.near(newTr.doc.resolve(selectionPos), -1);
|
package/dist/cjs/types/change.js
CHANGED
|
@@ -24,6 +24,7 @@ var CHANGE_OPERATION;
|
|
|
24
24
|
CHANGE_OPERATION["wrap_with_node"] = "wrap_with_node";
|
|
25
25
|
CHANGE_OPERATION["node_split"] = "node_split";
|
|
26
26
|
CHANGE_OPERATION["reference"] = "reference";
|
|
27
|
+
CHANGE_OPERATION["move"] = "move";
|
|
27
28
|
})(CHANGE_OPERATION = exports.CHANGE_OPERATION || (exports.CHANGE_OPERATION = {}));
|
|
28
29
|
var CHANGE_STATUS;
|
|
29
30
|
(function (CHANGE_STATUS) {
|
|
@@ -11,8 +11,10 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
11
11
|
return t;
|
|
12
12
|
};
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
-
exports.trFromHistory = exports.stepIsLift = exports.isLiftStep = exports.isWrapStep = exports.isSplitStep = exports.createNewUpdateAttrs = exports.createNewDeleteAttrs = exports.createNewReferenceAttrs = exports.createNewSplitAttrs = exports.createNewWrapAttrs = exports.createNewInsertAttrs = void 0;
|
|
14
|
+
exports.filterMeaninglessMoveSteps = exports.handleDirectPendingMoveDeletions = exports.isDirectPendingMoveDeletion = exports.isDeletingPendingMovedNode = exports.HasMoveOperations = exports.trFromHistory = exports.stepIsLift = exports.isLiftStep = exports.isWrapStep = exports.isSplitStep = exports.createNewUpdateAttrs = exports.createNewMoveAttrs = exports.createNewDeleteAttrs = exports.createNewReferenceAttrs = exports.createNewSplitAttrs = exports.createNewWrapAttrs = exports.createNewInsertAttrs = void 0;
|
|
15
|
+
const prosemirror_transform_1 = require("prosemirror-transform");
|
|
15
16
|
const change_1 = require("../types/change");
|
|
17
|
+
const uuidv4_1 = require("./uuidv4");
|
|
16
18
|
function createNewInsertAttrs(attrs) {
|
|
17
19
|
return Object.assign(Object.assign({}, attrs), { operation: change_1.CHANGE_OPERATION.insert });
|
|
18
20
|
}
|
|
@@ -33,6 +35,10 @@ function createNewDeleteAttrs(attrs) {
|
|
|
33
35
|
return Object.assign(Object.assign({}, attrs), { operation: change_1.CHANGE_OPERATION.delete });
|
|
34
36
|
}
|
|
35
37
|
exports.createNewDeleteAttrs = createNewDeleteAttrs;
|
|
38
|
+
function createNewMoveAttrs(attrs) {
|
|
39
|
+
return Object.assign(Object.assign({}, attrs), { operation: change_1.CHANGE_OPERATION.move });
|
|
40
|
+
}
|
|
41
|
+
exports.createNewMoveAttrs = createNewMoveAttrs;
|
|
36
42
|
function createNewUpdateAttrs(attrs, oldAttrs) {
|
|
37
43
|
const { dataTracked } = oldAttrs, restAttrs = __rest(oldAttrs, ["dataTracked"]);
|
|
38
44
|
return Object.assign(Object.assign({}, attrs), { operation: change_1.CHANGE_OPERATION.set_node_attributes, oldAttrs: JSON.parse(JSON.stringify(restAttrs)) });
|
|
@@ -84,3 +90,119 @@ function stepIsLift(gap, node, to) {
|
|
|
84
90
|
exports.stepIsLift = stepIsLift;
|
|
85
91
|
const trFromHistory = (tr) => Object.keys(tr.meta).find((s) => s.startsWith('history$'));
|
|
86
92
|
exports.trFromHistory = trFromHistory;
|
|
93
|
+
const HasMoveOperations = (tr) => {
|
|
94
|
+
const movingAssoc = new Map();
|
|
95
|
+
if (tr.steps.length < 2) {
|
|
96
|
+
return movingAssoc;
|
|
97
|
+
}
|
|
98
|
+
const matched = [];
|
|
99
|
+
for (let i = 0; i < tr.steps.length; i++) {
|
|
100
|
+
if (matched.includes(i)) {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const step = tr.steps[i];
|
|
104
|
+
const doc = tr.docs[i];
|
|
105
|
+
const stepDeletesContent = step.from !== step.to && step.slice.size === 0;
|
|
106
|
+
const stepInsertsContent = step.slice.size && step.slice.content.firstChild ? true : false;
|
|
107
|
+
console.log('stepDeletesContent', stepDeletesContent);
|
|
108
|
+
console.log('stepInsertsContent', stepInsertsContent);
|
|
109
|
+
for (let g = 0; g < tr.steps.length; g++) {
|
|
110
|
+
if (g === i || matched.includes(g)) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
const peerStep = tr.steps[g];
|
|
114
|
+
const peerStepInsertsContent = peerStep.slice.size && peerStep.slice.content.firstChild;
|
|
115
|
+
const peerStepDeletesContent = peerStep.from !== peerStep.to && peerStep.slice.size === 0;
|
|
116
|
+
if (stepDeletesContent) {
|
|
117
|
+
const deletedContent = doc.slice(step.from, step.to);
|
|
118
|
+
if (peerStepInsertsContent &&
|
|
119
|
+
deletedContent.content.firstChild &&
|
|
120
|
+
peerStep.slice.content.firstChild.toString() === deletedContent.content.firstChild.toString()) {
|
|
121
|
+
const commonID = (0, uuidv4_1.uuidv4)();
|
|
122
|
+
movingAssoc.set(peerStep, commonID);
|
|
123
|
+
movingAssoc.set(step, commonID);
|
|
124
|
+
matched.push(i, g);
|
|
125
|
+
}
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (stepInsertsContent && peerStepDeletesContent) {
|
|
129
|
+
const insertedContent = step.slice;
|
|
130
|
+
const deletedPeerContent = tr.docs[g].slice(peerStep.from, peerStep.to);
|
|
131
|
+
if (insertedContent.content.firstChild &&
|
|
132
|
+
deletedPeerContent.content.firstChild &&
|
|
133
|
+
insertedContent.content.firstChild.toString() === deletedPeerContent.content.firstChild.toString()) {
|
|
134
|
+
const commonID = (0, uuidv4_1.uuidv4)();
|
|
135
|
+
movingAssoc.set(peerStep, commonID);
|
|
136
|
+
movingAssoc.set(step, commonID);
|
|
137
|
+
}
|
|
138
|
+
matched.push(i, g);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return movingAssoc;
|
|
143
|
+
};
|
|
144
|
+
exports.HasMoveOperations = HasMoveOperations;
|
|
145
|
+
const isDeletingPendingMovedNode = (step, doc) => {
|
|
146
|
+
if (!step.slice || step.from === step.to || step.slice.content.size > 0) {
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
const node = doc.nodeAt(step.from);
|
|
150
|
+
if (!node) {
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
const trackedAttrs = node.attrs.dataTracked;
|
|
154
|
+
const found = trackedAttrs === null || trackedAttrs === void 0 ? void 0 : trackedAttrs.find((tracked) => tracked.operation === change_1.CHANGE_OPERATION.move && tracked.status === change_1.CHANGE_STATUS.pending);
|
|
155
|
+
if (found === null || found === void 0 ? void 0 : found.moveNodeId) {
|
|
156
|
+
return found.moveNodeId;
|
|
157
|
+
}
|
|
158
|
+
return undefined;
|
|
159
|
+
};
|
|
160
|
+
exports.isDeletingPendingMovedNode = isDeletingPendingMovedNode;
|
|
161
|
+
const isDirectPendingMoveDeletion = (step, doc, movingSteps) => {
|
|
162
|
+
if (step.from === step.to || step.slice.content.size > 0) {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
if (movingSteps.has(step)) {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
const node = doc.nodeAt(step.from);
|
|
169
|
+
if (!node) {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
const trackedAttrs = node.attrs.dataTracked;
|
|
173
|
+
return !!(trackedAttrs === null || trackedAttrs === void 0 ? void 0 : trackedAttrs.some((t) => t.operation === change_1.CHANGE_OPERATION.move && t.status === change_1.CHANGE_STATUS.pending));
|
|
174
|
+
};
|
|
175
|
+
exports.isDirectPendingMoveDeletion = isDirectPendingMoveDeletion;
|
|
176
|
+
const handleDirectPendingMoveDeletions = (tr, newTr, movingSteps) => {
|
|
177
|
+
tr.steps.forEach((step) => {
|
|
178
|
+
if (step instanceof prosemirror_transform_1.ReplaceStep) {
|
|
179
|
+
const doc = tr.docs[tr.steps.indexOf(step)];
|
|
180
|
+
if ((0, exports.isDirectPendingMoveDeletion)(step, doc, movingSteps)) {
|
|
181
|
+
const node = doc.nodeAt(step.from);
|
|
182
|
+
if (node === null || node === void 0 ? void 0 : node.attrs.dataTracked) {
|
|
183
|
+
newTr.setNodeMarkup(step.from, undefined, Object.assign(Object.assign({}, node.attrs), { dataTracked: node.attrs.dataTracked.filter((t) => !(t.operation === change_1.CHANGE_OPERATION.move && t.status === change_1.CHANGE_STATUS.pending)) }));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
};
|
|
189
|
+
exports.handleDirectPendingMoveDeletions = handleDirectPendingMoveDeletions;
|
|
190
|
+
const filterMeaninglessMoveSteps = (tr, movingSteps) => {
|
|
191
|
+
const cleanSteps = [];
|
|
192
|
+
for (let i = 0; i < tr.steps.length; i++) {
|
|
193
|
+
const step = tr.steps[i];
|
|
194
|
+
const moveID = movingSteps.get(step);
|
|
195
|
+
const prevMoveID = (0, exports.isDeletingPendingMovedNode)(step, tr.docs[i]);
|
|
196
|
+
if (moveID && prevMoveID) {
|
|
197
|
+
movingSteps.forEach((replaceStepMoveID, replaceStep) => {
|
|
198
|
+
if (replaceStep !== step && moveID === replaceStepMoveID) {
|
|
199
|
+
movingSteps.set(replaceStep, prevMoveID);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
cleanSteps.push(step);
|
|
205
|
+
}
|
|
206
|
+
return cleanSteps;
|
|
207
|
+
};
|
|
208
|
+
exports.filterMeaninglessMoveSteps = filterMeaninglessMoveSteps;
|
package/dist/es/ChangeSet.js
CHANGED
|
@@ -88,7 +88,8 @@ export class ChangeSet {
|
|
|
88
88
|
}
|
|
89
89
|
rootNodes.push([change]);
|
|
90
90
|
});
|
|
91
|
-
return rootNodes.filter((changes) => changes.filter((c) => c.dataTracked.operation !== CHANGE_OPERATION.reference
|
|
91
|
+
return rootNodes.filter((changes) => changes.filter((c) => c.dataTracked.operation !== CHANGE_OPERATION.reference &&
|
|
92
|
+
!(c.dataTracked.moveNodeId && c.dataTracked.operation === CHANGE_OPERATION.delete)).length);
|
|
92
93
|
}
|
|
93
94
|
get pending() {
|
|
94
95
|
return this.changes.filter((c) => c.dataTracked.status === CHANGE_STATUS.pending);
|
|
@@ -164,7 +165,8 @@ export class ChangeSet {
|
|
|
164
165
|
const { status, operation } = change.dataTracked;
|
|
165
166
|
return (((operation === CHANGE_OPERATION.insert ||
|
|
166
167
|
operation === CHANGE_OPERATION.node_split ||
|
|
167
|
-
operation === CHANGE_OPERATION.wrap_with_node
|
|
168
|
+
operation === CHANGE_OPERATION.wrap_with_node ||
|
|
169
|
+
operation === CHANGE_OPERATION.move) &&
|
|
168
170
|
status === CHANGE_STATUS.rejected) ||
|
|
169
171
|
(operation === CHANGE_OPERATION.delete && status === CHANGE_STATUS.accepted));
|
|
170
172
|
}
|
|
@@ -16,13 +16,8 @@ export function getUpdatedDataTracked(dataTracked, changeId) {
|
|
|
16
16
|
export function applyAcceptedRejectedChanges(tr, schema, changes, changeSet, deleteMap = new Mapping()) {
|
|
17
17
|
changes.sort((c1, c2) => c1.dataTracked.updatedAt - c2.dataTracked.updatedAt);
|
|
18
18
|
changes.forEach((change) => {
|
|
19
|
-
if (change.dataTracked.
|
|
20
|
-
|
|
21
|
-
return revertSplitNodeChange(tr, change, changeSet);
|
|
22
|
-
}
|
|
23
|
-
if (change.dataTracked.operation === CHANGE_OPERATION.wrap_with_node) {
|
|
24
|
-
return revertWrapNodeChange(tr, change);
|
|
25
|
-
}
|
|
19
|
+
if (change.dataTracked.operation === CHANGE_OPERATION.move) {
|
|
20
|
+
return;
|
|
26
21
|
}
|
|
27
22
|
const { pos: from, deleted } = deleteMap.mapResult(change.from);
|
|
28
23
|
const node = tr.doc.nodeAt(from);
|
|
@@ -31,9 +26,17 @@ export function applyAcceptedRejectedChanges(tr, schema, changes, changeSet, del
|
|
|
31
26
|
return;
|
|
32
27
|
}
|
|
33
28
|
if (!node) {
|
|
34
|
-
!deleted && log.warn('
|
|
29
|
+
!deleted && log.warn('No node found to update for change', change);
|
|
35
30
|
return;
|
|
36
31
|
}
|
|
32
|
+
if (change.dataTracked.status === CHANGE_STATUS.rejected) {
|
|
33
|
+
if (change.dataTracked.operation === CHANGE_OPERATION.node_split) {
|
|
34
|
+
return revertSplitNodeChange(tr, change, changeSet);
|
|
35
|
+
}
|
|
36
|
+
if (change.dataTracked.operation === CHANGE_OPERATION.wrap_with_node) {
|
|
37
|
+
return revertWrapNodeChange(tr, change);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
37
40
|
if (ChangeSet.isTextChange(change) && noChangeNeeded) {
|
|
38
41
|
tr.removeMark(from, deleteMap.map(change.to), schema.marks.tracked_insert);
|
|
39
42
|
tr.removeMark(from, deleteMap.map(change.to), schema.marks.tracked_delete);
|
|
@@ -68,5 +71,39 @@ export function applyAcceptedRejectedChanges(tr, schema, changes, changeSet, del
|
|
|
68
71
|
tr.setNodeMarkup(from, undefined, Object.assign(Object.assign({}, node.attrs), { dataTracked: getUpdatedDataTracked(node.attrs.dataTracked, change.id) }), node.marks);
|
|
69
72
|
}
|
|
70
73
|
});
|
|
74
|
+
changes.forEach((change) => {
|
|
75
|
+
if (change.dataTracked.operation !== CHANGE_OPERATION.move) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const { pos: from, deleted } = deleteMap.mapResult(change.from);
|
|
79
|
+
const node = tr.doc.nodeAt(from);
|
|
80
|
+
if (deleted || !node) {
|
|
81
|
+
if (!deleted && !node) {
|
|
82
|
+
log.warn('No node found for move change', { change });
|
|
83
|
+
}
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (change.dataTracked.status === CHANGE_STATUS.accepted) {
|
|
87
|
+
const originalChange = changeSet.changes.find((c) => c.dataTracked.moveNodeId === change.dataTracked.moveNodeId &&
|
|
88
|
+
c.dataTracked.operation === CHANGE_OPERATION.delete);
|
|
89
|
+
if (originalChange) {
|
|
90
|
+
const { pos: originalFrom } = deleteMap.mapResult(originalChange.from);
|
|
91
|
+
const originalNode = tr.doc.nodeAt(originalFrom);
|
|
92
|
+
const attrs = Object.assign(Object.assign({}, node.attrs), { dataTracked: getUpdatedDataTracked(node.attrs.dataTracked, change.id) });
|
|
93
|
+
tr.setNodeMarkup(from, undefined, attrs, node.marks);
|
|
94
|
+
if (originalNode) {
|
|
95
|
+
tr.delete(originalFrom, originalFrom + originalNode.nodeSize);
|
|
96
|
+
deleteMap.appendMap(tr.steps[tr.steps.length - 1].getMap());
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
log.warn('No original change found for move operation', { change });
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else if (change.dataTracked.status === CHANGE_STATUS.rejected) {
|
|
104
|
+
tr.delete(from, from + node.nodeSize);
|
|
105
|
+
deleteMap.appendMap(tr.steps[tr.steps.length - 1].getMap());
|
|
106
|
+
}
|
|
107
|
+
});
|
|
71
108
|
return deleteMap;
|
|
72
109
|
}
|
|
@@ -20,6 +20,7 @@ import { applyAcceptedRejectedChanges } from './applyChanges';
|
|
|
20
20
|
import { updateChangeAttrs } from './updateChangeAttrs';
|
|
21
21
|
export function updateChangesStatus(createdTr, changeSet, ids, status, userID, oldState) {
|
|
22
22
|
const change = changeSet.get(ids[0]);
|
|
23
|
+
const changeTime = new Date().getTime();
|
|
23
24
|
if (change && status !== CHANGE_STATUS.pending) {
|
|
24
25
|
const textChanges = [];
|
|
25
26
|
const nonTextChanges = [];
|
|
@@ -37,6 +38,24 @@ export function updateChangesStatus(createdTr, changeSet, ids, status, userID, o
|
|
|
37
38
|
nonTextChanges.push(relatedRefChange);
|
|
38
39
|
}
|
|
39
40
|
}
|
|
41
|
+
if (c.dataTracked.operation === CHANGE_OPERATION.move) {
|
|
42
|
+
const oldChange = changeSet.changeTree.find((c) => ChangeSet.isNodeChange(c) &&
|
|
43
|
+
c.dataTracked.operation === 'delete' &&
|
|
44
|
+
c.dataTracked.moveNodeId === change.dataTracked.moveNodeId);
|
|
45
|
+
if (oldChange && ChangeSet.isNodeChange(oldChange)) {
|
|
46
|
+
createdTr = updateChangeAttrs(createdTr, oldChange, Object.assign(Object.assign({}, oldChange.dataTracked), { status, statusUpdateAt: changeTime, reviewedByID: userID }), oldState.schema);
|
|
47
|
+
oldChange.children.forEach((child) => {
|
|
48
|
+
createdTr = updateChangeAttrs(createdTr, child, Object.assign(Object.assign({}, child.dataTracked), { status, statusUpdateAt: changeTime, reviewedByID: userID }), oldState.schema);
|
|
49
|
+
if (ChangeSet.isTextChange(child)) {
|
|
50
|
+
textChanges.push(child);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
nonTextChanges.push(child);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
nonTextChanges.push(oldChange);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
40
59
|
}
|
|
41
60
|
}
|
|
42
61
|
});
|
|
@@ -44,7 +63,6 @@ export function updateChangesStatus(createdTr, changeSet, ids, status, userID, o
|
|
|
44
63
|
applyAcceptedRejectedChanges(createdTr, oldState.schema, textChanges, changeSet, mapping);
|
|
45
64
|
}
|
|
46
65
|
else {
|
|
47
|
-
const changeTime = new Date().getTime();
|
|
48
66
|
ids.forEach((changeId) => {
|
|
49
67
|
const change = changeSet === null || changeSet === void 0 ? void 0 : changeSet.get(changeId);
|
|
50
68
|
if (change) {
|
|
@@ -56,6 +56,13 @@ export function setFragmentAsWrapChange(inserted, attrs, schema) {
|
|
|
56
56
|
});
|
|
57
57
|
return Fragment.from(content);
|
|
58
58
|
}
|
|
59
|
+
export function setFragmentAsMoveChange(fragment, attrs) {
|
|
60
|
+
const content = [];
|
|
61
|
+
fragment.forEach((node) => {
|
|
62
|
+
content.push(node.type.create(Object.assign(Object.assign({}, node.attrs), { dataTracked: [addTrackIdIfDoesntExist(trackUtils.createNewMoveAttrs(attrs))] }), node.content, node.marks));
|
|
63
|
+
});
|
|
64
|
+
return Fragment.from(content);
|
|
65
|
+
}
|
|
59
66
|
export function setFragmentAsNodeSplit($pos, newTr, inserted, attrs) {
|
|
60
67
|
const lastChild = inserted.lastChild;
|
|
61
68
|
const referenceId = uuidv4();
|
package/dist/es/plugin.js
CHANGED
|
@@ -97,7 +97,7 @@ export const trackChangesPlugin = (opts = { userID: 'anonymous:Anonymous', initi
|
|
|
97
97
|
!skipTrackUsed &&
|
|
98
98
|
!trFromHistory(tr) &&
|
|
99
99
|
!(wasAppended && tr.getMeta('origin') === 'paragraphs')) {
|
|
100
|
-
createdTr = trackTransaction(tr, oldState, createdTr, userID);
|
|
100
|
+
createdTr = trackTransaction(tr, oldState, createdTr, userID, changeSet);
|
|
101
101
|
}
|
|
102
102
|
docChanged = docChanged || tr.docChanged;
|
|
103
103
|
if (setChangeStatuses) {
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { Slice } from 'prosemirror-model';
|
|
2
|
-
import { setFragmentAsInserted, setFragmentAsNodeSplit } from '../compute/setFragmentAsInserted';
|
|
2
|
+
import { setFragmentAsInserted, setFragmentAsMoveChange, setFragmentAsNodeSplit, } from '../compute/setFragmentAsInserted';
|
|
3
3
|
import { deleteAndMergeSplitNodes } from '../mutate/deleteAndMergeSplitNodes';
|
|
4
4
|
import { log } from '../utils/logger';
|
|
5
5
|
import * as trackUtils from '../utils/track-utils';
|
|
6
6
|
import { isSplitStep } from '../utils/track-utils';
|
|
7
|
-
export function trackReplaceStep(step, oldState, newTr,
|
|
7
|
+
export function trackReplaceStep(step, oldState, newTr, attrsTemplate, stepResult, currentStepDoc, tr, moveID) {
|
|
8
8
|
log.info('###### ReplaceStep ######');
|
|
9
9
|
let selectionPos = 0;
|
|
10
10
|
const changeSteps = [];
|
|
11
|
+
const attrs = Object.assign({}, attrsTemplate);
|
|
12
|
+
if (moveID) {
|
|
13
|
+
console.log('Detected Node Moving ReplaceStep and assigning the following movenodeID: ' + moveID);
|
|
14
|
+
attrs.moveNodeId = moveID;
|
|
15
|
+
}
|
|
11
16
|
step.getMap().forEach((fromA, toA, fromB, toB) => {
|
|
12
17
|
var _a, _b;
|
|
13
18
|
log.info(`changed ranges: ${fromA} ${toA} ${fromB} ${toB}`);
|
|
@@ -34,13 +39,15 @@ export function trackReplaceStep(step, oldState, newTr, attrs, stepResult, curre
|
|
|
34
39
|
changeSteps.splice(changeSteps.indexOf(backSpacedText));
|
|
35
40
|
}
|
|
36
41
|
const textWasDeleted = !!changeSteps.length && !(fromA === fromB);
|
|
37
|
-
console.log(textWasDeleted);
|
|
38
42
|
if (!backSpacedText && newSliceContent.size > 0) {
|
|
39
43
|
log.info('newSliceContent', newSliceContent);
|
|
40
44
|
let fragment = setFragmentAsInserted(newSliceContent, trackUtils.createNewInsertAttrs(attrs), oldState.schema);
|
|
41
45
|
if (isSplitStep(step, oldState.selection, tr.getMeta('uiEvent'))) {
|
|
42
46
|
fragment = setFragmentAsNodeSplit(newTr.doc.resolve(step.from), newTr, fragment, attrs);
|
|
43
47
|
}
|
|
48
|
+
if (moveID) {
|
|
49
|
+
fragment = setFragmentAsMoveChange(newSliceContent, trackUtils.createNewMoveAttrs(attrs));
|
|
50
|
+
}
|
|
44
51
|
const openStart = slice.openStart !== slice.openEnd ? 0 : slice.openStart;
|
|
45
52
|
const openEnd = slice.openStart !== slice.openEnd ? 0 : slice.openEnd;
|
|
46
53
|
changeSteps.push({
|
|
@@ -7,13 +7,14 @@ import { getNodeTrackedData } from '../compute/nodeHelpers';
|
|
|
7
7
|
import { CHANGE_STATUS } from '../types/change';
|
|
8
8
|
import { log } from '../utils/logger';
|
|
9
9
|
import { mapChangeSteps } from '../utils/mapChangeStep';
|
|
10
|
+
import { filterMeaninglessMoveSteps, handleDirectPendingMoveDeletions, HasMoveOperations, } from '../utils/track-utils';
|
|
10
11
|
import { uuidv4 } from '../utils/uuidv4';
|
|
11
12
|
import trackAttrsChange from './trackAttrsChange';
|
|
12
13
|
import { trackReplaceAroundStep } from './trackReplaceAroundStep';
|
|
13
14
|
import { trackReplaceStep } from './trackReplaceStep';
|
|
14
15
|
const getSelectionStaticConstructor = (sel) => Object.getPrototypeOf(sel).constructor;
|
|
15
16
|
const isHighlightMarkerNode = (node) => node && node.type === node.type.schema.nodes.highlight_marker;
|
|
16
|
-
export function trackTransaction(tr, oldState, newTr, authorID) {
|
|
17
|
+
export function trackTransaction(tr, oldState, newTr, authorID, changeSet) {
|
|
17
18
|
var _a, _b, _c, _d, _e;
|
|
18
19
|
const emptyAttrs = {
|
|
19
20
|
authorID,
|
|
@@ -29,7 +30,10 @@ export function trackTransaction(tr, oldState, newTr, authorID) {
|
|
|
29
30
|
let iters = 0;
|
|
30
31
|
log.info('ORIGINAL transaction', tr);
|
|
31
32
|
let trContext = {};
|
|
32
|
-
|
|
33
|
+
const movingStepsAssociated = HasMoveOperations(tr);
|
|
34
|
+
handleDirectPendingMoveDeletions(tr, newTr, movingStepsAssociated);
|
|
35
|
+
const cleanSteps = filterMeaninglessMoveSteps(tr, movingStepsAssociated);
|
|
36
|
+
for (let i = cleanSteps.length - 1; i >= 0; i--) {
|
|
33
37
|
const step = tr.steps[i];
|
|
34
38
|
log.info('transaction step', step);
|
|
35
39
|
iters += 1;
|
|
@@ -58,7 +62,7 @@ export function trackTransaction(tr, oldState, newTr, authorID) {
|
|
|
58
62
|
}
|
|
59
63
|
const newStep = new ReplaceStep(thisStepMapping.map(invertedStep.from), thisStepMapping.map(invertedStep.to), invertedStep.slice);
|
|
60
64
|
const stepResult = newTr.maybeStep(newStep);
|
|
61
|
-
let [steps, startPos] = trackReplaceStep(step, oldState, newTr, emptyAttrs, stepResult, tr.docs[i], tr);
|
|
65
|
+
let [steps, startPos] = trackReplaceStep(step, oldState, newTr, emptyAttrs, stepResult, tr.docs[i], tr, movingStepsAssociated.get(step));
|
|
62
66
|
if (steps.length === 1) {
|
|
63
67
|
const step = steps[0];
|
|
64
68
|
if (isHighlightMarkerNode((step === null || step === void 0 ? void 0 : step.node) || ((_d = (_c = step === null || step === void 0 ? void 0 : step.slice) === null || _c === void 0 ? void 0 : _c.content) === null || _d === void 0 ? void 0 : _d.content[0]))) {
|
|
@@ -72,7 +76,8 @@ export function trackTransaction(tr, oldState, newTr, authorID) {
|
|
|
72
76
|
const inserted = steps.filter((s) => s.type === 'insert-slice');
|
|
73
77
|
steps = diffChangeSteps(deleted, inserted);
|
|
74
78
|
log.info('DIFFED STEPS: ', steps);
|
|
75
|
-
const [mapping, selectionPos] = processChangeSteps(steps, startPos || tr.selection.head, newTr,
|
|
79
|
+
const [mapping, selectionPos] = processChangeSteps(steps, startPos || tr.selection.head, newTr, movingStepsAssociated.has(step)
|
|
80
|
+
? Object.assign(Object.assign({}, emptyAttrs), { moveNodeId: movingStepsAssociated.get(step) }) : emptyAttrs, oldState.schema, deletedNodeMapping);
|
|
76
81
|
if (!wasNodeSelection && !setsNewSelection) {
|
|
77
82
|
const sel = getSelectionStaticConstructor(tr.selection);
|
|
78
83
|
const near = sel.near(newTr.doc.resolve(selectionPos), -1);
|
package/dist/es/types/change.js
CHANGED
|
@@ -21,6 +21,7 @@ export var CHANGE_OPERATION;
|
|
|
21
21
|
CHANGE_OPERATION["wrap_with_node"] = "wrap_with_node";
|
|
22
22
|
CHANGE_OPERATION["node_split"] = "node_split";
|
|
23
23
|
CHANGE_OPERATION["reference"] = "reference";
|
|
24
|
+
CHANGE_OPERATION["move"] = "move";
|
|
24
25
|
})(CHANGE_OPERATION || (CHANGE_OPERATION = {}));
|
|
25
26
|
export var CHANGE_STATUS;
|
|
26
27
|
(function (CHANGE_STATUS) {
|
|
@@ -9,7 +9,9 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
9
9
|
}
|
|
10
10
|
return t;
|
|
11
11
|
};
|
|
12
|
-
import {
|
|
12
|
+
import { ReplaceStep } from 'prosemirror-transform';
|
|
13
|
+
import { CHANGE_OPERATION, CHANGE_STATUS } from '../types/change';
|
|
14
|
+
import { uuidv4 } from './uuidv4';
|
|
13
15
|
export function createNewInsertAttrs(attrs) {
|
|
14
16
|
return Object.assign(Object.assign({}, attrs), { operation: CHANGE_OPERATION.insert });
|
|
15
17
|
}
|
|
@@ -25,6 +27,9 @@ export function createNewReferenceAttrs(attrs, id) {
|
|
|
25
27
|
export function createNewDeleteAttrs(attrs) {
|
|
26
28
|
return Object.assign(Object.assign({}, attrs), { operation: CHANGE_OPERATION.delete });
|
|
27
29
|
}
|
|
30
|
+
export function createNewMoveAttrs(attrs) {
|
|
31
|
+
return Object.assign(Object.assign({}, attrs), { operation: CHANGE_OPERATION.move });
|
|
32
|
+
}
|
|
28
33
|
export function createNewUpdateAttrs(attrs, oldAttrs) {
|
|
29
34
|
const { dataTracked } = oldAttrs, restAttrs = __rest(oldAttrs, ["dataTracked"]);
|
|
30
35
|
return Object.assign(Object.assign({}, attrs), { operation: CHANGE_OPERATION.set_node_attributes, oldAttrs: JSON.parse(JSON.stringify(restAttrs)) });
|
|
@@ -70,3 +75,114 @@ export function stepIsLift(gap, node, to) {
|
|
|
70
75
|
return gap.start < gap.end && gap.insert === 0 && gap.end === to && !node.isText;
|
|
71
76
|
}
|
|
72
77
|
export const trFromHistory = (tr) => Object.keys(tr.meta).find((s) => s.startsWith('history$'));
|
|
78
|
+
export const HasMoveOperations = (tr) => {
|
|
79
|
+
const movingAssoc = new Map();
|
|
80
|
+
if (tr.steps.length < 2) {
|
|
81
|
+
return movingAssoc;
|
|
82
|
+
}
|
|
83
|
+
const matched = [];
|
|
84
|
+
for (let i = 0; i < tr.steps.length; i++) {
|
|
85
|
+
if (matched.includes(i)) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const step = tr.steps[i];
|
|
89
|
+
const doc = tr.docs[i];
|
|
90
|
+
const stepDeletesContent = step.from !== step.to && step.slice.size === 0;
|
|
91
|
+
const stepInsertsContent = step.slice.size && step.slice.content.firstChild ? true : false;
|
|
92
|
+
console.log('stepDeletesContent', stepDeletesContent);
|
|
93
|
+
console.log('stepInsertsContent', stepInsertsContent);
|
|
94
|
+
for (let g = 0; g < tr.steps.length; g++) {
|
|
95
|
+
if (g === i || matched.includes(g)) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const peerStep = tr.steps[g];
|
|
99
|
+
const peerStepInsertsContent = peerStep.slice.size && peerStep.slice.content.firstChild;
|
|
100
|
+
const peerStepDeletesContent = peerStep.from !== peerStep.to && peerStep.slice.size === 0;
|
|
101
|
+
if (stepDeletesContent) {
|
|
102
|
+
const deletedContent = doc.slice(step.from, step.to);
|
|
103
|
+
if (peerStepInsertsContent &&
|
|
104
|
+
deletedContent.content.firstChild &&
|
|
105
|
+
peerStep.slice.content.firstChild.toString() === deletedContent.content.firstChild.toString()) {
|
|
106
|
+
const commonID = uuidv4();
|
|
107
|
+
movingAssoc.set(peerStep, commonID);
|
|
108
|
+
movingAssoc.set(step, commonID);
|
|
109
|
+
matched.push(i, g);
|
|
110
|
+
}
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (stepInsertsContent && peerStepDeletesContent) {
|
|
114
|
+
const insertedContent = step.slice;
|
|
115
|
+
const deletedPeerContent = tr.docs[g].slice(peerStep.from, peerStep.to);
|
|
116
|
+
if (insertedContent.content.firstChild &&
|
|
117
|
+
deletedPeerContent.content.firstChild &&
|
|
118
|
+
insertedContent.content.firstChild.toString() === deletedPeerContent.content.firstChild.toString()) {
|
|
119
|
+
const commonID = uuidv4();
|
|
120
|
+
movingAssoc.set(peerStep, commonID);
|
|
121
|
+
movingAssoc.set(step, commonID);
|
|
122
|
+
}
|
|
123
|
+
matched.push(i, g);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return movingAssoc;
|
|
128
|
+
};
|
|
129
|
+
export const isDeletingPendingMovedNode = (step, doc) => {
|
|
130
|
+
if (!step.slice || step.from === step.to || step.slice.content.size > 0) {
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
const node = doc.nodeAt(step.from);
|
|
134
|
+
if (!node) {
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
const trackedAttrs = node.attrs.dataTracked;
|
|
138
|
+
const found = trackedAttrs === null || trackedAttrs === void 0 ? void 0 : trackedAttrs.find((tracked) => tracked.operation === CHANGE_OPERATION.move && tracked.status === CHANGE_STATUS.pending);
|
|
139
|
+
if (found === null || found === void 0 ? void 0 : found.moveNodeId) {
|
|
140
|
+
return found.moveNodeId;
|
|
141
|
+
}
|
|
142
|
+
return undefined;
|
|
143
|
+
};
|
|
144
|
+
export const isDirectPendingMoveDeletion = (step, doc, movingSteps) => {
|
|
145
|
+
if (step.from === step.to || step.slice.content.size > 0) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
if (movingSteps.has(step)) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
const node = doc.nodeAt(step.from);
|
|
152
|
+
if (!node) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
const trackedAttrs = node.attrs.dataTracked;
|
|
156
|
+
return !!(trackedAttrs === null || trackedAttrs === void 0 ? void 0 : trackedAttrs.some((t) => t.operation === CHANGE_OPERATION.move && t.status === CHANGE_STATUS.pending));
|
|
157
|
+
};
|
|
158
|
+
export const handleDirectPendingMoveDeletions = (tr, newTr, movingSteps) => {
|
|
159
|
+
tr.steps.forEach((step) => {
|
|
160
|
+
if (step instanceof ReplaceStep) {
|
|
161
|
+
const doc = tr.docs[tr.steps.indexOf(step)];
|
|
162
|
+
if (isDirectPendingMoveDeletion(step, doc, movingSteps)) {
|
|
163
|
+
const node = doc.nodeAt(step.from);
|
|
164
|
+
if (node === null || node === void 0 ? void 0 : node.attrs.dataTracked) {
|
|
165
|
+
newTr.setNodeMarkup(step.from, undefined, Object.assign(Object.assign({}, node.attrs), { dataTracked: node.attrs.dataTracked.filter((t) => !(t.operation === CHANGE_OPERATION.move && t.status === CHANGE_STATUS.pending)) }));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
};
|
|
171
|
+
export const filterMeaninglessMoveSteps = (tr, movingSteps) => {
|
|
172
|
+
const cleanSteps = [];
|
|
173
|
+
for (let i = 0; i < tr.steps.length; i++) {
|
|
174
|
+
const step = tr.steps[i];
|
|
175
|
+
const moveID = movingSteps.get(step);
|
|
176
|
+
const prevMoveID = isDeletingPendingMovedNode(step, tr.docs[i]);
|
|
177
|
+
if (moveID && prevMoveID) {
|
|
178
|
+
movingSteps.forEach((replaceStepMoveID, replaceStep) => {
|
|
179
|
+
if (replaceStep !== step && moveID === replaceStepMoveID) {
|
|
180
|
+
movingSteps.set(replaceStep, prevMoveID);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
cleanSteps.push(step);
|
|
186
|
+
}
|
|
187
|
+
return cleanSteps;
|
|
188
|
+
};
|
|
@@ -3,4 +3,5 @@ import { Transaction } from 'prosemirror-state';
|
|
|
3
3
|
import { NewEmptyAttrs, NewInsertAttrs } from '../types/track';
|
|
4
4
|
export declare function setFragmentAsInserted(inserted: Fragment, insertAttrs: NewInsertAttrs, schema: Schema): Fragment;
|
|
5
5
|
export declare function setFragmentAsWrapChange(inserted: Fragment, attrs: NewEmptyAttrs, schema: Schema): Fragment;
|
|
6
|
+
export declare function setFragmentAsMoveChange(fragment: Fragment, attrs: NewEmptyAttrs): Fragment;
|
|
6
7
|
export declare function setFragmentAsNodeSplit($pos: ResolvedPos, newTr: Transaction, inserted: Fragment, attrs: NewEmptyAttrs): Fragment;
|
|
@@ -3,4 +3,4 @@ import type { EditorState, Transaction } from 'prosemirror-state';
|
|
|
3
3
|
import { ReplaceStep, StepResult } from 'prosemirror-transform';
|
|
4
4
|
import { ChangeStep } from '../types/step';
|
|
5
5
|
import { NewEmptyAttrs } from '../types/track';
|
|
6
|
-
export declare function trackReplaceStep(step: ReplaceStep, oldState: EditorState, newTr: Transaction,
|
|
6
|
+
export declare function trackReplaceStep(step: ReplaceStep, oldState: EditorState, newTr: Transaction, attrsTemplate: NewEmptyAttrs, stepResult: StepResult, currentStepDoc: PMNode, tr: Transaction, moveID?: string): [ChangeStep[], number];
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
import { EditorState, Transaction } from 'prosemirror-state';
|
|
2
|
-
|
|
2
|
+
import { ChangeSet } from '../ChangeSet';
|
|
3
|
+
export declare function trackTransaction(tr: Transaction, oldState: EditorState, newTr: Transaction, authorID: string, changeSet: ChangeSet): Transaction;
|
|
@@ -20,7 +20,8 @@ export declare enum CHANGE_OPERATION {
|
|
|
20
20
|
set_node_attributes = "set_attrs",
|
|
21
21
|
wrap_with_node = "wrap_with_node",
|
|
22
22
|
node_split = "node_split",
|
|
23
|
-
reference = "reference"
|
|
23
|
+
reference = "reference",
|
|
24
|
+
move = "move"
|
|
24
25
|
}
|
|
25
26
|
export declare enum CHANGE_STATUS {
|
|
26
27
|
accepted = "accepted",
|
|
@@ -36,6 +37,7 @@ type InsertDeleteAttrs = {
|
|
|
36
37
|
statusUpdateAt: number;
|
|
37
38
|
createdAt: number;
|
|
38
39
|
updatedAt: number;
|
|
40
|
+
moveNodeId?: string;
|
|
39
41
|
};
|
|
40
42
|
export type UpdateAttrs = Omit<InsertDeleteAttrs, 'operation'> & {
|
|
41
43
|
operation: CHANGE_OPERATION.set_node_attributes;
|
|
@@ -51,7 +53,10 @@ export type ReferenceAttrs = Omit<InsertDeleteAttrs, 'operation'> & {
|
|
|
51
53
|
operation: CHANGE_OPERATION.reference;
|
|
52
54
|
referenceId: string;
|
|
53
55
|
};
|
|
54
|
-
export type
|
|
56
|
+
export type NodeMoveAttrs = Omit<InsertDeleteAttrs, 'operation'> & {
|
|
57
|
+
operation: CHANGE_OPERATION.move;
|
|
58
|
+
};
|
|
59
|
+
export type TrackedAttrs = InsertDeleteAttrs | UpdateAttrs | WrapAttrs | NodeSplitAttrs | ReferenceAttrs | NodeMoveAttrs;
|
|
55
60
|
type Change = {
|
|
56
61
|
id: string;
|
|
57
62
|
from: number;
|
|
@@ -16,7 +16,7 @@ export interface TrackChangesState {
|
|
|
16
16
|
}
|
|
17
17
|
export type NewEmptyAttrs = Omit<TrackedAttrs, 'id' | 'operation'>;
|
|
18
18
|
export type NewInsertAttrs = Omit<TrackedAttrs, 'id' | 'operation'> & {
|
|
19
|
-
operation: CHANGE_OPERATION.insert | CHANGE_OPERATION.wrap_with_node;
|
|
19
|
+
operation: CHANGE_OPERATION.insert | CHANGE_OPERATION.wrap_with_node | CHANGE_OPERATION.move;
|
|
20
20
|
};
|
|
21
21
|
export type NewDeleteAttrs = Omit<TrackedAttrs, 'id' | 'operation'> & {
|
|
22
22
|
operation: CHANGE_OPERATION.delete;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { Node as PMNode, Slice } from 'prosemirror-model';
|
|
2
2
|
import { Selection, Transaction } from 'prosemirror-state';
|
|
3
|
-
import { ReplaceAroundStep, ReplaceStep } from 'prosemirror-transform';
|
|
3
|
+
import { ReplaceAroundStep, ReplaceStep, Step } from 'prosemirror-transform';
|
|
4
4
|
import { NewDeleteAttrs, NewEmptyAttrs, NewInsertAttrs, NewReferenceAttrs, NewSplitNodeAttrs, NewUpdateAttrs } from '../types/track';
|
|
5
5
|
export declare function createNewInsertAttrs(attrs: NewEmptyAttrs): NewInsertAttrs;
|
|
6
6
|
export declare function createNewWrapAttrs(attrs: NewEmptyAttrs): NewInsertAttrs;
|
|
7
7
|
export declare function createNewSplitAttrs(attrs: NewEmptyAttrs): NewSplitNodeAttrs;
|
|
8
8
|
export declare function createNewReferenceAttrs(attrs: NewEmptyAttrs, id: string): NewReferenceAttrs;
|
|
9
9
|
export declare function createNewDeleteAttrs(attrs: NewEmptyAttrs): NewDeleteAttrs;
|
|
10
|
+
export declare function createNewMoveAttrs(attrs: NewEmptyAttrs): NewInsertAttrs;
|
|
10
11
|
export declare function createNewUpdateAttrs(attrs: NewEmptyAttrs, oldAttrs: Record<string, any>): NewUpdateAttrs;
|
|
11
12
|
export declare const isSplitStep: (step: ReplaceStep, selection: Selection, uiEvent: string) => boolean;
|
|
12
13
|
export declare const isWrapStep: (step: ReplaceAroundStep) => boolean;
|
|
@@ -18,3 +19,8 @@ export declare function stepIsLift(gap: {
|
|
|
18
19
|
insert: number;
|
|
19
20
|
}, node: PMNode, to: number): boolean;
|
|
20
21
|
export declare const trFromHistory: (tr: Transaction) => string | undefined;
|
|
22
|
+
export declare const HasMoveOperations: (tr: Transaction) => Map<ReplaceStep, string>;
|
|
23
|
+
export declare const isDeletingPendingMovedNode: (step: ReplaceStep, doc: PMNode) => string | undefined;
|
|
24
|
+
export declare const isDirectPendingMoveDeletion: (step: ReplaceStep, doc: PMNode, movingSteps: Map<ReplaceStep, string>) => boolean;
|
|
25
|
+
export declare const handleDirectPendingMoveDeletions: (tr: Transaction, newTr: Transaction, movingSteps: Map<ReplaceStep, string>) => void;
|
|
26
|
+
export declare const filterMeaninglessMoveSteps: (tr: Transaction, movingSteps: Map<ReplaceStep, string>) => Step[];
|
package/package.json
CHANGED