Fix undo/redo (WIP)

This commit is contained in:
jos 2018-08-06 13:36:35 +02:00
parent ab8e6322d2
commit 338df99410
2 changed files with 99 additions and 76 deletions

View File

@ -1,7 +1,5 @@
'use strict';
var util = require('./util');
/**
* @constructor History
* Store action history, enables undo and redo
@ -14,122 +12,144 @@ function History (editor) {
this.clear();
// helper function to find a Node from a path
function findNode(path) {
return editor.node.findNodeByPath(path)
}
// helper function to clone a Node
function cloneNode(node) {
return node.clone();
}
// map with all supported actions
this.actions = {
'editField': {
'undo': function (params) {
params.node.updateField(params.oldValue);
findNode(params.path).updateField(params.oldValue);
},
'redo': function (params) {
params.node.updateField(params.newValue);
findNode(params.path).updateField(params.newValue);
}
},
'editValue': {
'undo': function (params) {
params.node.updateValue(params.oldValue);
findNode(params.path).updateValue(params.oldValue);
},
'redo': function (params) {
params.node.updateValue(params.newValue);
findNode(params.path).updateValue(params.newValue);
}
},
'changeType': {
'undo': function (params) {
params.node.changeType(params.oldType);
findNode(params.path).changeType(params.oldType);
},
'redo': function (params) {
params.node.changeType(params.newType);
findNode(params.path).changeType(params.newType);
}
},
'appendNodes': {
'undo': function (params) {
params.nodes.forEach(function (node) {
params.parent.removeChild(node);
var parentNode = findNode(params.parentPath);
params.paths.map(findNode).forEach(function (node) {
parentNode.removeChild(node);
});
},
},
'redo': function (params) {
params.nodes.forEach(function (node) {
params.parent.appendChild(node);
var parentNode = findNode(params.parentPath);
params.paths.map(findNode).forEach(function (node) {
parentNode.appendChild(node);
});
}
},
'insertBeforeNodes': {
'undo': function (params) {
params.nodes.forEach(function (node) {
params.parent.removeChild(node);
var parentNode = findNode(params.parentPath);
params.paths.map(findNode).forEach(function (node) {
parentNode.removeChild(node);
});
},
'redo': function (params) {
params.nodes.forEach(function (node) {
params.parent.insertBefore(node, params.beforeNode);
var parentNode = findNode(params.parentPath);
var beforeNode = findNode(params.beforePath);
params.paths.map(findNode).forEach(function (node) {
parentNode.insertBefore(node, beforeNode);
});
}
},
'insertAfterNodes': {
'undo': function (params) {
params.nodes.forEach(function (node) {
params.parent.removeChild(node);
var parentNode = findNode(params.parentPath);
params.paths.map(findNode).forEach(function (node) {
parentNode.removeChild(node);
});
},
'redo': function (params) {
var afterNode = params.afterNode;
params.nodes.forEach(function (node) {
params.parent.insertAfter(params.node, afterNode);
var parentNode = findNode(params.parentPath);
var afterNode = findNode(params.afterPath);
params.paths.map(findNode).forEach(function (node) {
parentNode.insertAfter(node, afterNode);
afterNode = node;
});
}
},
'removeNodes': {
'undo': function (params) {
var parent = params.parent;
var beforeNode = parent.childs[params.index] || parent.append;
params.nodes.forEach(function (node) {
parent.insertBefore(node, beforeNode);
var parentNode = findNode(params.parentPath);
var beforeNode = parentNode.childs[params.index] || parentNode.append;
params.paths.map(findNode).forEach(function (node) {
parentNode.insertBefore(node, beforeNode);
});
},
'redo': function (params) {
params.nodes.forEach(function (node) {
params.paths.map(findNode).forEach(function (node) {
params.parent.removeChild(node);
});
}
},
'duplicateNodes': {
'undo': function (params) {
params.nodes.forEach(function (node) {
params.parent.removeChild(node);
var parentNode = findNode(params.parentPath);
params.paths.map(findNode).forEach(function (node) {
parentNode.removeChild(node);
});
},
'redo': function (params) {
var afterNode = params.afterNode;
params.nodes.forEach(function (node) {
params.parent.insertAfter(node, afterNode);
var parentNode = findNode(params.parentPath);
var afterNode = findNode(params.afterPath);
var nodes = params.paths.map(findNode).map(cloneNode);
nodes.forEach(function (node) {
parentNode.insertAfter(node, afterNode);
afterNode = node;
});
}
},
'moveNodes': {
'undo': function (params) {
params.nodes.forEach(function (node) {
params.oldBeforeNode.parent.moveBefore(node, params.oldBeforeNode);
var oldBeforeNode = findNode(params.oldBeforePath);
params.paths.map(findNode).forEach(function (node) {
oldBeforeNode.parent.moveBefore(node, oldBeforeNode);
});
},
'redo': function (params) {
params.nodes.forEach(function (node) {
params.newBeforeNode.parent.moveBefore(node, params.newBeforeNode);
var newBeforeNode = findNode(params.newBeforePath);
params.paths.map(findNode).forEach(function (node) {
newBeforeNode.parent.moveBefore(node, newBeforeNode);
});
}
},
'sort': {
'undo': function (params) {
var node = params.node;
var node = findNode(params.path);
node.hideChilds();
node.childs = params.oldChilds;
node.updateDom({updateIndexes: true});
node.showChilds();
},
'redo': function (params) {
var node = params.node;
var node = findNode(params.path);
node.hideChilds();
node.childs = params.newChilds;
node.updateDom({updateIndexes: true});
@ -139,14 +159,12 @@ function History (editor) {
'transform': {
'undo': function (params) {
var node = params.node;
node.setValue(params.oldValue);
findNode(params.path).setValue(params.oldValue);
// TODO: would be nice to restore the state of the node and childs
},
'redo': function (params) {
var node = params.node;
node.setValue(params.newValue);
findNode(params.path).setValue(params.newValue);
// TODO: would be nice to restore the state of the node and childs
}

View File

@ -1404,7 +1404,7 @@ Node.prototype._onChangeValue = function () {
}
this.editor._onAction('editValue', {
node: this,
path: this.getPath(),
oldValue: this.previousValue,
newValue: this.value,
oldSelection: oldSelection,
@ -1436,7 +1436,7 @@ Node.prototype._onChangeField = function () {
}
this.editor._onAction('editField', {
node: this,
path: this.getPath(),
oldValue: this.previousField,
newValue: this.field,
oldSelection: oldSelection,
@ -2010,11 +2010,11 @@ Node.onDragEnd = function (nodes, event) {
}
var params = {
nodes: nodes,
paths: nodes.map(getPath),
oldSelection: editor.drag.oldSelection,
newSelection: editor.getDomSelection(),
oldBeforeNode: editor.drag.oldBeforeNode,
newBeforeNode: beforeNode
oldBeforePath: editor.drag.oldBeforeNode.getPath(),
newBeforePath: beforeNode.getPath()
};
if (params.oldBeforeNode != params.newBeforeNode) {
@ -2811,9 +2811,9 @@ Node.prototype.onKeyDown = function (event) {
this.focus(Node.focusElement || this._getElementName(target));
this.editor._onAction('moveNodes', {
nodes: selectedNodes,
oldBeforeNode: oldBeforeNode,
newBeforeNode: nextNode2,
paths: selectedNodes.map(getPath),
oldBeforePath: oldBeforeNode.getPath(),
newBeforePath: nextNode2.getPath(),
oldSelection: oldSelection,
newSelection: this.editor.getDomSelection()
});
@ -2858,9 +2858,9 @@ Node.prototype.onKeyDown = function (event) {
this.focus(Node.focusElement || this._getElementName(target));
this.editor._onAction('moveNodes', {
nodes: selectedNodes,
oldBeforeNode: oldBeforeNode,
newBeforeNode: prevNode,
paths: selectedNodes.map(getPath),
oldBeforePath: oldBeforeNode.getPath(),
newBeforePath: prevNode.getPath(),
oldSelection: oldSelection,
newSelection: this.editor.getDomSelection()
});
@ -2892,9 +2892,9 @@ Node.prototype.onKeyDown = function (event) {
this.focus(Node.focusElement || this._getElementName(target));
this.editor._onAction('moveNodes', {
nodes: selectedNodes,
oldBeforeNode: oldBeforeNode,
newBeforeNode: prevNode,
paths: selectedNodes.map(getPath),
oldBeforePath: oldBeforeNode.getPath(),
newBeforePath: prevNode.getPath(),
oldSelection: oldSelection,
newSelection: this.editor.getDomSelection()
});
@ -2955,9 +2955,9 @@ Node.prototype.onKeyDown = function (event) {
this.focus(Node.focusElement || this._getElementName(target));
this.editor._onAction('moveNodes', {
nodes: selectedNodes,
oldBeforeNode: oldBeforeNode,
newBeforeNode: nextNode2,
paths: selectedNodes.map(getPath),
oldBeforePath: oldBeforeNode.getPath(),
newBeforePath: nextNode2.getPath(),
oldSelection: oldSelection,
newSelection: this.editor.getDomSelection()
});
@ -3028,8 +3028,8 @@ Node.onRemove = function(nodes) {
// store history action
editor._onAction('removeNodes', {
nodes: nodes.slice(0), // store a copy of the array!
parent: parent,
paths: nodes.map(getPath),
parentPath: parent.getPath(),
index: firstIndex,
oldSelection: oldSelection,
newSelection: newSelection
@ -3075,9 +3075,9 @@ Node.onDuplicate = function(nodes) {
var newSelection = editor.getDomSelection();
editor._onAction('duplicateNodes', {
afterNode: lastNode,
nodes: clones,
parent: parent,
afterPath: lastNode.getPath(),
paths: clones.map(getPath),
parentPath: parent.getPath(),
oldSelection: oldSelection,
newSelection: newSelection
});
@ -3106,9 +3106,9 @@ Node.prototype._onInsertBefore = function (field, value, type) {
var newSelection = this.editor.getDomSelection();
this.editor._onAction('insertBeforeNodes', {
nodes: [newNode],
beforeNode: this,
parent: this.parent,
paths: [newNode.getPath()],
beforePath: this.getPath(),
parentPath: this.parent.getPath(),
oldSelection: oldSelection,
newSelection: newSelection
});
@ -3136,9 +3136,9 @@ Node.prototype._onInsertAfter = function (field, value, type) {
var newSelection = this.editor.getDomSelection();
this.editor._onAction('insertAfterNodes', {
nodes: [newNode],
afterNode: this,
parent: this.parent,
paths: [newNode.getPath()],
afterPath: this.getPath(),
parentPath: this.parent.getPath(),
oldSelection: oldSelection,
newSelection: newSelection
});
@ -3166,8 +3166,8 @@ Node.prototype._onAppend = function (field, value, type) {
var newSelection = this.editor.getDomSelection();
this.editor._onAction('appendNodes', {
nodes: [newNode],
parent: this.parent,
paths: [newNode.getPath()],
parentPath: this.parent.getPath(),
oldSelection: oldSelection,
newSelection: newSelection
});
@ -3186,7 +3186,7 @@ Node.prototype._onChangeType = function (newType) {
var newSelection = this.editor.getDomSelection();
this.editor._onAction('changeType', {
node: this,
path: this.getPath(),
oldType: oldType,
newType: newType,
oldSelection: oldSelection,
@ -3249,7 +3249,7 @@ Node.prototype.sort = function (path, direction) {
this._updateDomIndexes();
this.editor._onAction('sort', {
node: this,
path: this.getPath(),
oldChilds: oldChilds,
newChilds: this.childs
});
@ -3269,7 +3269,7 @@ Node.prototype.update = function (newValue) {
this.setValue(newValue);
this.editor._onAction('transform', {
node: this,
path: this.getPath(),
oldType: oldType,
newType: this.type,
oldValue: oldValue,
@ -3302,7 +3302,7 @@ Node.prototype.transform = function (query) {
this.setValue(newValue);
this.editor._onAction('transform', {
node: this,
path: this.getPath(),
oldType: oldType,
newType: this.type,
oldValue: oldValue,
@ -4018,6 +4018,11 @@ Node.prototype._escapeJSON = function (text) {
return escaped;
};
// helper function to the get path of a node
function getPath (node) {
return node.getPath();
}
// TODO: find a nicer solution to resolve this circular dependency between Node and AppendNode
// idea: introduce properties .isAppendNode and .isNode and use that instead of instanceof AppendNode checks
var AppendNode = appendNodeFactory(Node);