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'; 'use strict';
var util = require('./util');
/** /**
* @constructor History * @constructor History
* Store action history, enables undo and redo * Store action history, enables undo and redo
@ -14,122 +12,144 @@ function History (editor) {
this.clear(); 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 // map with all supported actions
this.actions = { this.actions = {
'editField': { 'editField': {
'undo': function (params) { 'undo': function (params) {
params.node.updateField(params.oldValue); findNode(params.path).updateField(params.oldValue);
}, },
'redo': function (params) { 'redo': function (params) {
params.node.updateField(params.newValue); findNode(params.path).updateField(params.newValue);
} }
}, },
'editValue': { 'editValue': {
'undo': function (params) { 'undo': function (params) {
params.node.updateValue(params.oldValue); findNode(params.path).updateValue(params.oldValue);
}, },
'redo': function (params) { 'redo': function (params) {
params.node.updateValue(params.newValue); findNode(params.path).updateValue(params.newValue);
} }
}, },
'changeType': { 'changeType': {
'undo': function (params) { 'undo': function (params) {
params.node.changeType(params.oldType); findNode(params.path).changeType(params.oldType);
}, },
'redo': function (params) { 'redo': function (params) {
params.node.changeType(params.newType); findNode(params.path).changeType(params.newType);
} }
}, },
'appendNodes': { 'appendNodes': {
'undo': function (params) { 'undo': function (params) {
params.nodes.forEach(function (node) { var parentNode = findNode(params.parentPath);
params.parent.removeChild(node); params.paths.map(findNode).forEach(function (node) {
parentNode.removeChild(node);
}); });
}, },
'redo': function (params) { 'redo': function (params) {
params.nodes.forEach(function (node) { var parentNode = findNode(params.parentPath);
params.parent.appendChild(node); params.paths.map(findNode).forEach(function (node) {
parentNode.appendChild(node);
}); });
} }
}, },
'insertBeforeNodes': { 'insertBeforeNodes': {
'undo': function (params) { 'undo': function (params) {
params.nodes.forEach(function (node) { var parentNode = findNode(params.parentPath);
params.parent.removeChild(node); params.paths.map(findNode).forEach(function (node) {
parentNode.removeChild(node);
}); });
}, },
'redo': function (params) { 'redo': function (params) {
params.nodes.forEach(function (node) { var parentNode = findNode(params.parentPath);
params.parent.insertBefore(node, params.beforeNode); var beforeNode = findNode(params.beforePath);
params.paths.map(findNode).forEach(function (node) {
parentNode.insertBefore(node, beforeNode);
}); });
} }
}, },
'insertAfterNodes': { 'insertAfterNodes': {
'undo': function (params) { 'undo': function (params) {
params.nodes.forEach(function (node) { var parentNode = findNode(params.parentPath);
params.parent.removeChild(node); params.paths.map(findNode).forEach(function (node) {
parentNode.removeChild(node);
}); });
}, },
'redo': function (params) { 'redo': function (params) {
var afterNode = params.afterNode; var parentNode = findNode(params.parentPath);
params.nodes.forEach(function (node) { var afterNode = findNode(params.afterPath);
params.parent.insertAfter(params.node, afterNode); params.paths.map(findNode).forEach(function (node) {
parentNode.insertAfter(node, afterNode);
afterNode = node; afterNode = node;
}); });
} }
}, },
'removeNodes': { 'removeNodes': {
'undo': function (params) { 'undo': function (params) {
var parent = params.parent; var parentNode = findNode(params.parentPath);
var beforeNode = parent.childs[params.index] || parent.append; var beforeNode = parentNode.childs[params.index] || parentNode.append;
params.nodes.forEach(function (node) { params.paths.map(findNode).forEach(function (node) {
parent.insertBefore(node, beforeNode); parentNode.insertBefore(node, beforeNode);
}); });
}, },
'redo': function (params) { 'redo': function (params) {
params.nodes.forEach(function (node) { params.paths.map(findNode).forEach(function (node) {
params.parent.removeChild(node); params.parent.removeChild(node);
}); });
} }
}, },
'duplicateNodes': { 'duplicateNodes': {
'undo': function (params) { 'undo': function (params) {
params.nodes.forEach(function (node) { var parentNode = findNode(params.parentPath);
params.parent.removeChild(node); params.paths.map(findNode).forEach(function (node) {
parentNode.removeChild(node);
}); });
}, },
'redo': function (params) { 'redo': function (params) {
var afterNode = params.afterNode; var parentNode = findNode(params.parentPath);
params.nodes.forEach(function (node) { var afterNode = findNode(params.afterPath);
params.parent.insertAfter(node, afterNode); var nodes = params.paths.map(findNode).map(cloneNode);
nodes.forEach(function (node) {
parentNode.insertAfter(node, afterNode);
afterNode = node; afterNode = node;
}); });
} }
}, },
'moveNodes': { 'moveNodes': {
'undo': function (params) { 'undo': function (params) {
params.nodes.forEach(function (node) { var oldBeforeNode = findNode(params.oldBeforePath);
params.oldBeforeNode.parent.moveBefore(node, params.oldBeforeNode); params.paths.map(findNode).forEach(function (node) {
oldBeforeNode.parent.moveBefore(node, oldBeforeNode);
}); });
}, },
'redo': function (params) { 'redo': function (params) {
params.nodes.forEach(function (node) { var newBeforeNode = findNode(params.newBeforePath);
params.newBeforeNode.parent.moveBefore(node, params.newBeforeNode); params.paths.map(findNode).forEach(function (node) {
newBeforeNode.parent.moveBefore(node, newBeforeNode);
}); });
} }
}, },
'sort': { 'sort': {
'undo': function (params) { 'undo': function (params) {
var node = params.node; var node = findNode(params.path);
node.hideChilds(); node.hideChilds();
node.childs = params.oldChilds; node.childs = params.oldChilds;
node.updateDom({updateIndexes: true}); node.updateDom({updateIndexes: true});
node.showChilds(); node.showChilds();
}, },
'redo': function (params) { 'redo': function (params) {
var node = params.node; var node = findNode(params.path);
node.hideChilds(); node.hideChilds();
node.childs = params.newChilds; node.childs = params.newChilds;
node.updateDom({updateIndexes: true}); node.updateDom({updateIndexes: true});
@ -139,14 +159,12 @@ function History (editor) {
'transform': { 'transform': {
'undo': function (params) { 'undo': function (params) {
var node = params.node; findNode(params.path).setValue(params.oldValue);
node.setValue(params.oldValue);
// TODO: would be nice to restore the state of the node and childs // TODO: would be nice to restore the state of the node and childs
}, },
'redo': function (params) { 'redo': function (params) {
var node = params.node; findNode(params.path).setValue(params.newValue);
node.setValue(params.newValue);
// TODO: would be nice to restore the state of the node and childs // 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', { this.editor._onAction('editValue', {
node: this, path: this.getPath(),
oldValue: this.previousValue, oldValue: this.previousValue,
newValue: this.value, newValue: this.value,
oldSelection: oldSelection, oldSelection: oldSelection,
@ -1436,7 +1436,7 @@ Node.prototype._onChangeField = function () {
} }
this.editor._onAction('editField', { this.editor._onAction('editField', {
node: this, path: this.getPath(),
oldValue: this.previousField, oldValue: this.previousField,
newValue: this.field, newValue: this.field,
oldSelection: oldSelection, oldSelection: oldSelection,
@ -2010,11 +2010,11 @@ Node.onDragEnd = function (nodes, event) {
} }
var params = { var params = {
nodes: nodes, paths: nodes.map(getPath),
oldSelection: editor.drag.oldSelection, oldSelection: editor.drag.oldSelection,
newSelection: editor.getDomSelection(), newSelection: editor.getDomSelection(),
oldBeforeNode: editor.drag.oldBeforeNode, oldBeforePath: editor.drag.oldBeforeNode.getPath(),
newBeforeNode: beforeNode newBeforePath: beforeNode.getPath()
}; };
if (params.oldBeforeNode != params.newBeforeNode) { if (params.oldBeforeNode != params.newBeforeNode) {
@ -2811,9 +2811,9 @@ Node.prototype.onKeyDown = function (event) {
this.focus(Node.focusElement || this._getElementName(target)); this.focus(Node.focusElement || this._getElementName(target));
this.editor._onAction('moveNodes', { this.editor._onAction('moveNodes', {
nodes: selectedNodes, paths: selectedNodes.map(getPath),
oldBeforeNode: oldBeforeNode, oldBeforePath: oldBeforeNode.getPath(),
newBeforeNode: nextNode2, newBeforePath: nextNode2.getPath(),
oldSelection: oldSelection, oldSelection: oldSelection,
newSelection: this.editor.getDomSelection() newSelection: this.editor.getDomSelection()
}); });
@ -2858,9 +2858,9 @@ Node.prototype.onKeyDown = function (event) {
this.focus(Node.focusElement || this._getElementName(target)); this.focus(Node.focusElement || this._getElementName(target));
this.editor._onAction('moveNodes', { this.editor._onAction('moveNodes', {
nodes: selectedNodes, paths: selectedNodes.map(getPath),
oldBeforeNode: oldBeforeNode, oldBeforePath: oldBeforeNode.getPath(),
newBeforeNode: prevNode, newBeforePath: prevNode.getPath(),
oldSelection: oldSelection, oldSelection: oldSelection,
newSelection: this.editor.getDomSelection() newSelection: this.editor.getDomSelection()
}); });
@ -2892,9 +2892,9 @@ Node.prototype.onKeyDown = function (event) {
this.focus(Node.focusElement || this._getElementName(target)); this.focus(Node.focusElement || this._getElementName(target));
this.editor._onAction('moveNodes', { this.editor._onAction('moveNodes', {
nodes: selectedNodes, paths: selectedNodes.map(getPath),
oldBeforeNode: oldBeforeNode, oldBeforePath: oldBeforeNode.getPath(),
newBeforeNode: prevNode, newBeforePath: prevNode.getPath(),
oldSelection: oldSelection, oldSelection: oldSelection,
newSelection: this.editor.getDomSelection() newSelection: this.editor.getDomSelection()
}); });
@ -2955,9 +2955,9 @@ Node.prototype.onKeyDown = function (event) {
this.focus(Node.focusElement || this._getElementName(target)); this.focus(Node.focusElement || this._getElementName(target));
this.editor._onAction('moveNodes', { this.editor._onAction('moveNodes', {
nodes: selectedNodes, paths: selectedNodes.map(getPath),
oldBeforeNode: oldBeforeNode, oldBeforePath: oldBeforeNode.getPath(),
newBeforeNode: nextNode2, newBeforePath: nextNode2.getPath(),
oldSelection: oldSelection, oldSelection: oldSelection,
newSelection: this.editor.getDomSelection() newSelection: this.editor.getDomSelection()
}); });
@ -3028,8 +3028,8 @@ Node.onRemove = function(nodes) {
// store history action // store history action
editor._onAction('removeNodes', { editor._onAction('removeNodes', {
nodes: nodes.slice(0), // store a copy of the array! paths: nodes.map(getPath),
parent: parent, parentPath: parent.getPath(),
index: firstIndex, index: firstIndex,
oldSelection: oldSelection, oldSelection: oldSelection,
newSelection: newSelection newSelection: newSelection
@ -3075,9 +3075,9 @@ Node.onDuplicate = function(nodes) {
var newSelection = editor.getDomSelection(); var newSelection = editor.getDomSelection();
editor._onAction('duplicateNodes', { editor._onAction('duplicateNodes', {
afterNode: lastNode, afterPath: lastNode.getPath(),
nodes: clones, paths: clones.map(getPath),
parent: parent, parentPath: parent.getPath(),
oldSelection: oldSelection, oldSelection: oldSelection,
newSelection: newSelection newSelection: newSelection
}); });
@ -3106,9 +3106,9 @@ Node.prototype._onInsertBefore = function (field, value, type) {
var newSelection = this.editor.getDomSelection(); var newSelection = this.editor.getDomSelection();
this.editor._onAction('insertBeforeNodes', { this.editor._onAction('insertBeforeNodes', {
nodes: [newNode], paths: [newNode.getPath()],
beforeNode: this, beforePath: this.getPath(),
parent: this.parent, parentPath: this.parent.getPath(),
oldSelection: oldSelection, oldSelection: oldSelection,
newSelection: newSelection newSelection: newSelection
}); });
@ -3136,9 +3136,9 @@ Node.prototype._onInsertAfter = function (field, value, type) {
var newSelection = this.editor.getDomSelection(); var newSelection = this.editor.getDomSelection();
this.editor._onAction('insertAfterNodes', { this.editor._onAction('insertAfterNodes', {
nodes: [newNode], paths: [newNode.getPath()],
afterNode: this, afterPath: this.getPath(),
parent: this.parent, parentPath: this.parent.getPath(),
oldSelection: oldSelection, oldSelection: oldSelection,
newSelection: newSelection newSelection: newSelection
}); });
@ -3166,8 +3166,8 @@ Node.prototype._onAppend = function (field, value, type) {
var newSelection = this.editor.getDomSelection(); var newSelection = this.editor.getDomSelection();
this.editor._onAction('appendNodes', { this.editor._onAction('appendNodes', {
nodes: [newNode], paths: [newNode.getPath()],
parent: this.parent, parentPath: this.parent.getPath(),
oldSelection: oldSelection, oldSelection: oldSelection,
newSelection: newSelection newSelection: newSelection
}); });
@ -3186,7 +3186,7 @@ Node.prototype._onChangeType = function (newType) {
var newSelection = this.editor.getDomSelection(); var newSelection = this.editor.getDomSelection();
this.editor._onAction('changeType', { this.editor._onAction('changeType', {
node: this, path: this.getPath(),
oldType: oldType, oldType: oldType,
newType: newType, newType: newType,
oldSelection: oldSelection, oldSelection: oldSelection,
@ -3249,7 +3249,7 @@ Node.prototype.sort = function (path, direction) {
this._updateDomIndexes(); this._updateDomIndexes();
this.editor._onAction('sort', { this.editor._onAction('sort', {
node: this, path: this.getPath(),
oldChilds: oldChilds, oldChilds: oldChilds,
newChilds: this.childs newChilds: this.childs
}); });
@ -3269,7 +3269,7 @@ Node.prototype.update = function (newValue) {
this.setValue(newValue); this.setValue(newValue);
this.editor._onAction('transform', { this.editor._onAction('transform', {
node: this, path: this.getPath(),
oldType: oldType, oldType: oldType,
newType: this.type, newType: this.type,
oldValue: oldValue, oldValue: oldValue,
@ -3302,7 +3302,7 @@ Node.prototype.transform = function (query) {
this.setValue(newValue); this.setValue(newValue);
this.editor._onAction('transform', { this.editor._onAction('transform', {
node: this, path: this.getPath(),
oldType: oldType, oldType: oldType,
newType: this.type, newType: this.type,
oldValue: oldValue, oldValue: oldValue,
@ -4018,6 +4018,11 @@ Node.prototype._escapeJSON = function (text) {
return escaped; 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 // 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 // idea: introduce properties .isAppendNode and .isNode and use that instead of instanceof AppendNode checks
var AppendNode = appendNodeFactory(Node); var AppendNode = appendNodeFactory(Node);