@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.
@@ -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).length);
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.status == change_1.CHANGE_STATUS.rejected) {
24
- if (change.dataTracked.operation === change_1.CHANGE_OPERATION.node_split) {
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('no node found to update for change', change);
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)();
@@ -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, attrs, stepResult, currentStepDoc, tr) {
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
- for (let i = tr.steps.length - 1; i >= 0; i--) {
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, emptyAttrs, oldState.schema, deletedNodeMapping);
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);
@@ -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;
@@ -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).length);
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.status == CHANGE_STATUS.rejected) {
20
- if (change.dataTracked.operation === CHANGE_OPERATION.node_split) {
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('no node found to update for change', change);
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, attrs, stepResult, currentStepDoc, tr) {
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
- for (let i = tr.steps.length - 1; i >= 0; i--) {
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, emptyAttrs, oldState.schema, deletedNodeMapping);
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);
@@ -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 { CHANGE_OPERATION } from '../types/change';
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, attrs: NewEmptyAttrs, stepResult: StepResult, currentStepDoc: PMNode, tr: Transaction): [ChangeStep[], number];
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
- export declare function trackTransaction(tr: Transaction, oldState: EditorState, newTr: Transaction, authorID: string): Transaction;
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 TrackedAttrs = InsertDeleteAttrs | UpdateAttrs | WrapAttrs | NodeSplitAttrs | ReferenceAttrs;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@manuscripts/track-changes-plugin",
3
- "version": "1.10.7",
3
+ "version": "1.10.8-LEAN-4160.1",
4
4
  "author": "Atypon Systems LLC",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/Atypon-OpenSource/manuscripts-track-changes-plugin",