Implemented history in preview mode
This commit is contained in:
commit
e3e3886da8
|
@ -1,337 +1,88 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var util = require('./util');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @constructor History
|
* Keep track on any history, be able
|
||||||
* Store action history, enables undo and redo
|
* @param {function} onChange
|
||||||
* @param {JSONEditor} editor
|
* @param {function} calculateItemSize
|
||||||
|
* @param {number} limit Maximum size of all items in history
|
||||||
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function History (editor) {
|
function History (onChange, calculateItemSize, limit) {
|
||||||
this.editor = editor;
|
this.onChange = onChange;
|
||||||
this.history = [];
|
this.calculateItemSize = calculateItemSize || function () {
|
||||||
this.index = -1;
|
return 1;
|
||||||
|
|
||||||
this.clear();
|
|
||||||
|
|
||||||
// helper function to find a Node from a path
|
|
||||||
function findNode(path) {
|
|
||||||
return editor.node.findNodeByInternalPath(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// map with all supported actions
|
|
||||||
this.actions = {
|
|
||||||
'editField': {
|
|
||||||
'undo': function (params) {
|
|
||||||
var parentNode = findNode(params.parentPath);
|
|
||||||
var node = parentNode.childs[params.index];
|
|
||||||
node.updateField(params.oldValue);
|
|
||||||
},
|
|
||||||
'redo': function (params) {
|
|
||||||
var parentNode = findNode(params.parentPath);
|
|
||||||
var node = parentNode.childs[params.index];
|
|
||||||
node.updateField(params.newValue);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'editValue': {
|
|
||||||
'undo': function (params) {
|
|
||||||
findNode(params.path).updateValue(params.oldValue);
|
|
||||||
},
|
|
||||||
'redo': function (params) {
|
|
||||||
findNode(params.path).updateValue(params.newValue);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'changeType': {
|
|
||||||
'undo': function (params) {
|
|
||||||
findNode(params.path).changeType(params.oldType);
|
|
||||||
},
|
|
||||||
'redo': function (params) {
|
|
||||||
findNode(params.path).changeType(params.newType);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
'appendNodes': {
|
|
||||||
'undo': function (params) {
|
|
||||||
var parentNode = findNode(params.parentPath);
|
|
||||||
params.paths.map(findNode).forEach(function (node) {
|
|
||||||
parentNode.removeChild(node);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
'redo': function (params) {
|
|
||||||
var parentNode = findNode(params.parentPath);
|
|
||||||
params.nodes.forEach(function (node) {
|
|
||||||
parentNode.appendChild(node);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'insertBeforeNodes': {
|
|
||||||
'undo': function (params) {
|
|
||||||
var parentNode = findNode(params.parentPath);
|
|
||||||
params.paths.map(findNode).forEach(function (node) {
|
|
||||||
parentNode.removeChild(node);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
'redo': function (params) {
|
|
||||||
var parentNode = findNode(params.parentPath);
|
|
||||||
var beforeNode = findNode(params.beforePath);
|
|
||||||
params.nodes.forEach(function (node) {
|
|
||||||
parentNode.insertBefore(node, beforeNode);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'insertAfterNodes': {
|
|
||||||
'undo': function (params) {
|
|
||||||
var parentNode = findNode(params.parentPath);
|
|
||||||
params.paths.map(findNode).forEach(function (node) {
|
|
||||||
parentNode.removeChild(node);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
'redo': function (params) {
|
|
||||||
var parentNode = findNode(params.parentPath);
|
|
||||||
var afterNode = findNode(params.afterPath);
|
|
||||||
params.nodes.forEach(function (node) {
|
|
||||||
parentNode.insertAfter(node, afterNode);
|
|
||||||
afterNode = node;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'removeNodes': {
|
|
||||||
'undo': function (params) {
|
|
||||||
var parentNode = findNode(params.parentPath);
|
|
||||||
var beforeNode = parentNode.childs[params.index] || parentNode.append;
|
|
||||||
params.nodes.forEach(function (node) {
|
|
||||||
parentNode.insertBefore(node, beforeNode);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
'redo': function (params) {
|
|
||||||
var parentNode = findNode(params.parentPath);
|
|
||||||
params.paths.map(findNode).forEach(function (node) {
|
|
||||||
parentNode.removeChild(node);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'duplicateNodes': {
|
|
||||||
'undo': function (params) {
|
|
||||||
var parentNode = findNode(params.parentPath);
|
|
||||||
params.clonePaths.map(findNode).forEach(function (node) {
|
|
||||||
parentNode.removeChild(node);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
'redo': function (params) {
|
|
||||||
var parentNode = findNode(params.parentPath);
|
|
||||||
var afterNode = findNode(params.afterPath);
|
|
||||||
var nodes = params.paths.map(findNode);
|
|
||||||
nodes.forEach(function (node) {
|
|
||||||
var clone = node.clone();
|
|
||||||
if (parentNode.type === 'object') {
|
|
||||||
var existingFieldNames = parentNode.getFieldNames();
|
|
||||||
clone.field = util.findUniqueName(node.field, existingFieldNames);
|
|
||||||
}
|
|
||||||
parentNode.insertAfter(clone, afterNode);
|
|
||||||
afterNode = clone;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'moveNodes': {
|
|
||||||
'undo': function (params) {
|
|
||||||
var oldParentNode = findNode(params.oldParentPath);
|
|
||||||
var newParentNode = findNode(params.newParentPath);
|
|
||||||
var oldBeforeNode = oldParentNode.childs[params.oldIndex] || oldParentNode.append;
|
|
||||||
|
|
||||||
// first copy the nodes, then move them
|
|
||||||
var nodes = newParentNode.childs.slice(params.newIndex, params.newIndex + params.count);
|
|
||||||
|
|
||||||
nodes.forEach(function (node, index) {
|
|
||||||
node.field = params.fieldNames[index];
|
|
||||||
oldParentNode.moveBefore(node, oldBeforeNode);
|
|
||||||
});
|
|
||||||
|
|
||||||
// This is a hack to work around an issue that we don't know tha original
|
|
||||||
// path of the new parent after dragging, as the node is already moved at that time.
|
|
||||||
if (params.newParentPathRedo === null) {
|
|
||||||
params.newParentPathRedo = newParentNode.getInternalPath();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'redo': function (params) {
|
|
||||||
var oldParentNode = findNode(params.oldParentPathRedo);
|
|
||||||
var newParentNode = findNode(params.newParentPathRedo);
|
|
||||||
var newBeforeNode = newParentNode.childs[params.newIndexRedo] || newParentNode.append;
|
|
||||||
|
|
||||||
// first copy the nodes, then move them
|
|
||||||
var nodes = oldParentNode.childs.slice(params.oldIndexRedo, params.oldIndexRedo + params.count);
|
|
||||||
|
|
||||||
nodes.forEach(function (node, index) {
|
|
||||||
node.field = params.fieldNames[index];
|
|
||||||
newParentNode.moveBefore(node, newBeforeNode);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
'sort': {
|
|
||||||
'undo': function (params) {
|
|
||||||
var node = findNode(params.path);
|
|
||||||
node.hideChilds();
|
|
||||||
node.childs = params.oldChilds;
|
|
||||||
node.updateDom({updateIndexes: true});
|
|
||||||
node.showChilds();
|
|
||||||
},
|
|
||||||
'redo': function (params) {
|
|
||||||
var node = findNode(params.path);
|
|
||||||
node.hideChilds();
|
|
||||||
node.childs = params.newChilds;
|
|
||||||
node.updateDom({updateIndexes: true});
|
|
||||||
node.showChilds();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
'transform': {
|
|
||||||
'undo': function (params) {
|
|
||||||
findNode(params.path).setInternalValue(params.oldValue);
|
|
||||||
|
|
||||||
// TODO: would be nice to restore the state of the node and childs
|
|
||||||
},
|
|
||||||
'redo': function (params) {
|
|
||||||
findNode(params.path).setInternalValue(params.newValue);
|
|
||||||
|
|
||||||
// TODO: would be nice to restore the state of the node and childs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: restore the original caret position and selection with each undo
|
|
||||||
// TODO: implement history for actions "expand", "collapse", "scroll", "setDocument"
|
|
||||||
};
|
};
|
||||||
|
this.limit = limit;
|
||||||
|
|
||||||
|
this.items = [];
|
||||||
|
this.index = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
History.prototype.add = function (item) {
|
||||||
* The method onChange is executed when the History is changed, and can
|
// limit number of items in history so that the total size doesn't
|
||||||
* be overloaded.
|
// always keep at least one item in memory
|
||||||
*/
|
while (this._calculateHistorySize() > this.limit && this.items.length > 1) {
|
||||||
History.prototype.onChange = function () {};
|
this.items.shift();
|
||||||
|
this.index--;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
// cleanup any redo action that are not valid anymore
|
||||||
* Add a new action to the history
|
this.items = this.items.slice(0, this.index + 1);
|
||||||
* @param {String} action The executed action. Available actions: "editField",
|
|
||||||
* "editValue", "changeType", "appendNode",
|
this.items.push(item);
|
||||||
* "removeNode", "duplicateNode", "moveNode"
|
|
||||||
* @param {Object} params Object containing parameters describing the change.
|
|
||||||
* The parameters in params depend on the action (for
|
|
||||||
* example for "editValue" the Node, old value, and new
|
|
||||||
* value are provided). params contains all information
|
|
||||||
* needed to undo or redo the action.
|
|
||||||
*/
|
|
||||||
History.prototype.add = function (action, params) {
|
|
||||||
this.index++;
|
this.index++;
|
||||||
this.history[this.index] = {
|
|
||||||
'action': action,
|
|
||||||
'params': params,
|
|
||||||
'timestamp': new Date()
|
|
||||||
};
|
|
||||||
|
|
||||||
// remove redo actions which are invalid now
|
|
||||||
if (this.index < this.history.length - 1) {
|
|
||||||
this.history.splice(this.index + 1, this.history.length - this.index - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// fire onchange event
|
|
||||||
this.onChange();
|
this.onChange();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
History.prototype._calculateHistorySize = function () {
|
||||||
* Clear history
|
var calculateItemSize = this.calculateItemSize;
|
||||||
*/
|
var totalSize = 0;
|
||||||
History.prototype.clear = function () {
|
|
||||||
this.history = [];
|
|
||||||
this.index = -1;
|
|
||||||
|
|
||||||
// fire onchange event
|
this.items.forEach(item => {
|
||||||
this.onChange();
|
totalSize += calculateItemSize(item);
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
return totalSize;
|
||||||
* Check if there is an action available for undo
|
}
|
||||||
* @return {Boolean} canUndo
|
|
||||||
*/
|
|
||||||
History.prototype.canUndo = function () {
|
|
||||||
return (this.index >= 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if there is an action available for redo
|
|
||||||
* @return {Boolean} canRedo
|
|
||||||
*/
|
|
||||||
History.prototype.canRedo = function () {
|
|
||||||
return (this.index < this.history.length - 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Undo the last action
|
|
||||||
*/
|
|
||||||
History.prototype.undo = function () {
|
History.prototype.undo = function () {
|
||||||
if (this.canUndo()) {
|
if (!this.canUndo()) {
|
||||||
var obj = this.history[this.index];
|
return;
|
||||||
if (obj) {
|
|
||||||
var action = this.actions[obj.action];
|
|
||||||
if (action && action.undo) {
|
|
||||||
action.undo(obj.params);
|
|
||||||
if (obj.params.oldSelection) {
|
|
||||||
try {
|
|
||||||
this.editor.setDomSelection(obj.params.oldSelection);
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.error(new Error('unknown action "' + obj.action + '"'));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.index--;
|
this.index--;
|
||||||
|
|
||||||
// fire onchange event
|
|
||||||
this.onChange();
|
this.onChange();
|
||||||
}
|
|
||||||
|
return this.items[this.index];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Redo the last action
|
|
||||||
*/
|
|
||||||
History.prototype.redo = function () {
|
History.prototype.redo = function () {
|
||||||
if (this.canRedo()) {
|
if (!this.canRedo()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.index++;
|
this.index++;
|
||||||
|
|
||||||
var obj = this.history[this.index];
|
|
||||||
if (obj) {
|
|
||||||
var action = this.actions[obj.action];
|
|
||||||
if (action && action.redo) {
|
|
||||||
action.redo(obj.params);
|
|
||||||
if (obj.params.newSelection) {
|
|
||||||
try {
|
|
||||||
this.editor.setDomSelection(obj.params.newSelection);
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.error(new Error('unknown action "' + obj.action + '"'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fire onchange event
|
|
||||||
this.onChange();
|
this.onChange();
|
||||||
}
|
|
||||||
|
return this.items[this.index];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
History.prototype.canUndo = function () {
|
||||||
* Destroy history
|
return this.index > 0;
|
||||||
*/
|
};
|
||||||
History.prototype.destroy = function () {
|
|
||||||
this.editor = null;
|
|
||||||
|
|
||||||
this.history = [];
|
History.prototype.canRedo = function () {
|
||||||
|
return this.index < this.items.length - 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
History.prototype.clear = function () {
|
||||||
|
this.items = [];
|
||||||
this.index = -1;
|
this.index = -1;
|
||||||
|
|
||||||
|
this.onChange();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
module.exports = History;
|
module.exports = History;
|
||||||
|
|
|
@ -0,0 +1,337 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var util = require('./util');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @constructor History
|
||||||
|
* Store action history, enables undo and redo
|
||||||
|
* @param {JSONEditor} editor
|
||||||
|
*/
|
||||||
|
function NodeHistory (editor) {
|
||||||
|
this.editor = editor;
|
||||||
|
this.history = [];
|
||||||
|
this.index = -1;
|
||||||
|
|
||||||
|
this.clear();
|
||||||
|
|
||||||
|
// helper function to find a Node from a path
|
||||||
|
function findNode(path) {
|
||||||
|
return editor.node.findNodeByInternalPath(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// map with all supported actions
|
||||||
|
this.actions = {
|
||||||
|
'editField': {
|
||||||
|
'undo': function (params) {
|
||||||
|
var parentNode = findNode(params.parentPath);
|
||||||
|
var node = parentNode.childs[params.index];
|
||||||
|
node.updateField(params.oldValue);
|
||||||
|
},
|
||||||
|
'redo': function (params) {
|
||||||
|
var parentNode = findNode(params.parentPath);
|
||||||
|
var node = parentNode.childs[params.index];
|
||||||
|
node.updateField(params.newValue);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'editValue': {
|
||||||
|
'undo': function (params) {
|
||||||
|
findNode(params.path).updateValue(params.oldValue);
|
||||||
|
},
|
||||||
|
'redo': function (params) {
|
||||||
|
findNode(params.path).updateValue(params.newValue);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'changeType': {
|
||||||
|
'undo': function (params) {
|
||||||
|
findNode(params.path).changeType(params.oldType);
|
||||||
|
},
|
||||||
|
'redo': function (params) {
|
||||||
|
findNode(params.path).changeType(params.newType);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'appendNodes': {
|
||||||
|
'undo': function (params) {
|
||||||
|
var parentNode = findNode(params.parentPath);
|
||||||
|
params.paths.map(findNode).forEach(function (node) {
|
||||||
|
parentNode.removeChild(node);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
'redo': function (params) {
|
||||||
|
var parentNode = findNode(params.parentPath);
|
||||||
|
params.nodes.forEach(function (node) {
|
||||||
|
parentNode.appendChild(node);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'insertBeforeNodes': {
|
||||||
|
'undo': function (params) {
|
||||||
|
var parentNode = findNode(params.parentPath);
|
||||||
|
params.paths.map(findNode).forEach(function (node) {
|
||||||
|
parentNode.removeChild(node);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
'redo': function (params) {
|
||||||
|
var parentNode = findNode(params.parentPath);
|
||||||
|
var beforeNode = findNode(params.beforePath);
|
||||||
|
params.nodes.forEach(function (node) {
|
||||||
|
parentNode.insertBefore(node, beforeNode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'insertAfterNodes': {
|
||||||
|
'undo': function (params) {
|
||||||
|
var parentNode = findNode(params.parentPath);
|
||||||
|
params.paths.map(findNode).forEach(function (node) {
|
||||||
|
parentNode.removeChild(node);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
'redo': function (params) {
|
||||||
|
var parentNode = findNode(params.parentPath);
|
||||||
|
var afterNode = findNode(params.afterPath);
|
||||||
|
params.nodes.forEach(function (node) {
|
||||||
|
parentNode.insertAfter(node, afterNode);
|
||||||
|
afterNode = node;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'removeNodes': {
|
||||||
|
'undo': function (params) {
|
||||||
|
var parentNode = findNode(params.parentPath);
|
||||||
|
var beforeNode = parentNode.childs[params.index] || parentNode.append;
|
||||||
|
params.nodes.forEach(function (node) {
|
||||||
|
parentNode.insertBefore(node, beforeNode);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
'redo': function (params) {
|
||||||
|
var parentNode = findNode(params.parentPath);
|
||||||
|
params.paths.map(findNode).forEach(function (node) {
|
||||||
|
parentNode.removeChild(node);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'duplicateNodes': {
|
||||||
|
'undo': function (params) {
|
||||||
|
var parentNode = findNode(params.parentPath);
|
||||||
|
params.clonePaths.map(findNode).forEach(function (node) {
|
||||||
|
parentNode.removeChild(node);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
'redo': function (params) {
|
||||||
|
var parentNode = findNode(params.parentPath);
|
||||||
|
var afterNode = findNode(params.afterPath);
|
||||||
|
var nodes = params.paths.map(findNode);
|
||||||
|
nodes.forEach(function (node) {
|
||||||
|
var clone = node.clone();
|
||||||
|
if (parentNode.type === 'object') {
|
||||||
|
var existingFieldNames = parentNode.getFieldNames();
|
||||||
|
clone.field = util.findUniqueName(node.field, existingFieldNames);
|
||||||
|
}
|
||||||
|
parentNode.insertAfter(clone, afterNode);
|
||||||
|
afterNode = clone;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'moveNodes': {
|
||||||
|
'undo': function (params) {
|
||||||
|
var oldParentNode = findNode(params.oldParentPath);
|
||||||
|
var newParentNode = findNode(params.newParentPath);
|
||||||
|
var oldBeforeNode = oldParentNode.childs[params.oldIndex] || oldParentNode.append;
|
||||||
|
|
||||||
|
// first copy the nodes, then move them
|
||||||
|
var nodes = newParentNode.childs.slice(params.newIndex, params.newIndex + params.count);
|
||||||
|
|
||||||
|
nodes.forEach(function (node, index) {
|
||||||
|
node.field = params.fieldNames[index];
|
||||||
|
oldParentNode.moveBefore(node, oldBeforeNode);
|
||||||
|
});
|
||||||
|
|
||||||
|
// This is a hack to work around an issue that we don't know tha original
|
||||||
|
// path of the new parent after dragging, as the node is already moved at that time.
|
||||||
|
if (params.newParentPathRedo === null) {
|
||||||
|
params.newParentPathRedo = newParentNode.getInternalPath();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'redo': function (params) {
|
||||||
|
var oldParentNode = findNode(params.oldParentPathRedo);
|
||||||
|
var newParentNode = findNode(params.newParentPathRedo);
|
||||||
|
var newBeforeNode = newParentNode.childs[params.newIndexRedo] || newParentNode.append;
|
||||||
|
|
||||||
|
// first copy the nodes, then move them
|
||||||
|
var nodes = oldParentNode.childs.slice(params.oldIndexRedo, params.oldIndexRedo + params.count);
|
||||||
|
|
||||||
|
nodes.forEach(function (node, index) {
|
||||||
|
node.field = params.fieldNames[index];
|
||||||
|
newParentNode.moveBefore(node, newBeforeNode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'sort': {
|
||||||
|
'undo': function (params) {
|
||||||
|
var node = findNode(params.path);
|
||||||
|
node.hideChilds();
|
||||||
|
node.childs = params.oldChilds;
|
||||||
|
node.updateDom({updateIndexes: true});
|
||||||
|
node.showChilds();
|
||||||
|
},
|
||||||
|
'redo': function (params) {
|
||||||
|
var node = findNode(params.path);
|
||||||
|
node.hideChilds();
|
||||||
|
node.childs = params.newChilds;
|
||||||
|
node.updateDom({updateIndexes: true});
|
||||||
|
node.showChilds();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'transform': {
|
||||||
|
'undo': function (params) {
|
||||||
|
findNode(params.path).setInternalValue(params.oldValue);
|
||||||
|
|
||||||
|
// TODO: would be nice to restore the state of the node and childs
|
||||||
|
},
|
||||||
|
'redo': function (params) {
|
||||||
|
findNode(params.path).setInternalValue(params.newValue);
|
||||||
|
|
||||||
|
// TODO: would be nice to restore the state of the node and childs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: restore the original caret position and selection with each undo
|
||||||
|
// TODO: implement history for actions "expand", "collapse", "scroll", "setDocument"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The method onChange is executed when the History is changed, and can
|
||||||
|
* be overloaded.
|
||||||
|
*/
|
||||||
|
NodeHistory.prototype.onChange = function () {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new action to the history
|
||||||
|
* @param {String} action The executed action. Available actions: "editField",
|
||||||
|
* "editValue", "changeType", "appendNode",
|
||||||
|
* "removeNode", "duplicateNode", "moveNode"
|
||||||
|
* @param {Object} params Object containing parameters describing the change.
|
||||||
|
* The parameters in params depend on the action (for
|
||||||
|
* example for "editValue" the Node, old value, and new
|
||||||
|
* value are provided). params contains all information
|
||||||
|
* needed to undo or redo the action.
|
||||||
|
*/
|
||||||
|
NodeHistory.prototype.add = function (action, params) {
|
||||||
|
this.index++;
|
||||||
|
this.history[this.index] = {
|
||||||
|
'action': action,
|
||||||
|
'params': params,
|
||||||
|
'timestamp': new Date()
|
||||||
|
};
|
||||||
|
|
||||||
|
// remove redo actions which are invalid now
|
||||||
|
if (this.index < this.history.length - 1) {
|
||||||
|
this.history.splice(this.index + 1, this.history.length - this.index - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fire onchange event
|
||||||
|
this.onChange();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear history
|
||||||
|
*/
|
||||||
|
NodeHistory.prototype.clear = function () {
|
||||||
|
this.history = [];
|
||||||
|
this.index = -1;
|
||||||
|
|
||||||
|
// fire onchange event
|
||||||
|
this.onChange();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if there is an action available for undo
|
||||||
|
* @return {Boolean} canUndo
|
||||||
|
*/
|
||||||
|
NodeHistory.prototype.canUndo = function () {
|
||||||
|
return (this.index >= 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if there is an action available for redo
|
||||||
|
* @return {Boolean} canRedo
|
||||||
|
*/
|
||||||
|
NodeHistory.prototype.canRedo = function () {
|
||||||
|
return (this.index < this.history.length - 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undo the last action
|
||||||
|
*/
|
||||||
|
NodeHistory.prototype.undo = function () {
|
||||||
|
if (this.canUndo()) {
|
||||||
|
var obj = this.history[this.index];
|
||||||
|
if (obj) {
|
||||||
|
var action = this.actions[obj.action];
|
||||||
|
if (action && action.undo) {
|
||||||
|
action.undo(obj.params);
|
||||||
|
if (obj.params.oldSelection) {
|
||||||
|
try {
|
||||||
|
this.editor.setDomSelection(obj.params.oldSelection);
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.error(new Error('unknown action "' + obj.action + '"'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.index--;
|
||||||
|
|
||||||
|
// fire onchange event
|
||||||
|
this.onChange();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redo the last action
|
||||||
|
*/
|
||||||
|
NodeHistory.prototype.redo = function () {
|
||||||
|
if (this.canRedo()) {
|
||||||
|
this.index++;
|
||||||
|
|
||||||
|
var obj = this.history[this.index];
|
||||||
|
if (obj) {
|
||||||
|
var action = this.actions[obj.action];
|
||||||
|
if (action && action.redo) {
|
||||||
|
action.redo(obj.params);
|
||||||
|
if (obj.params.newSelection) {
|
||||||
|
try {
|
||||||
|
this.editor.setDomSelection(obj.params.newSelection);
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.error(new Error('unknown action "' + obj.action + '"'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fire onchange event
|
||||||
|
this.onChange();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy history
|
||||||
|
*/
|
||||||
|
NodeHistory.prototype.destroy = function () {
|
||||||
|
this.editor = null;
|
||||||
|
|
||||||
|
this.history = [];
|
||||||
|
this.index = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = NodeHistory;
|
|
@ -3,3 +3,5 @@ exports.DEFAULT_MODAL_ANCHOR = document.body;
|
||||||
exports.SIZE_LARGE = 10 * 1024 * 1024; // 10 MB
|
exports.SIZE_LARGE = 10 * 1024 * 1024; // 10 MB
|
||||||
|
|
||||||
exports.MAX_PREVIEW_CHARACTERS = 20000;
|
exports.MAX_PREVIEW_CHARACTERS = 20000;
|
||||||
|
|
||||||
|
exports.PREVIEW_HISTORY_LIMIT = 2 * 1024 * 1024 * 1024; // 2 GB
|
||||||
|
|
|
@ -8,7 +8,9 @@ var showTransformModal = require('./showTransformModal');
|
||||||
var MAX_PREVIEW_CHARACTERS = require('./constants').MAX_PREVIEW_CHARACTERS;
|
var MAX_PREVIEW_CHARACTERS = require('./constants').MAX_PREVIEW_CHARACTERS;
|
||||||
var DEFAULT_MODAL_ANCHOR = require('./constants').DEFAULT_MODAL_ANCHOR;
|
var DEFAULT_MODAL_ANCHOR = require('./constants').DEFAULT_MODAL_ANCHOR;
|
||||||
var SIZE_LARGE = require('./constants').SIZE_LARGE;
|
var SIZE_LARGE = require('./constants').SIZE_LARGE;
|
||||||
|
var PREVIEW_HISTORY_LIMIT = require('./constants').PREVIEW_HISTORY_LIMIT;
|
||||||
var util = require('./util');
|
var util = require('./util');
|
||||||
|
var History = require('./History');
|
||||||
var jsonUtils = require('./jsonUtils');
|
var jsonUtils = require('./jsonUtils');
|
||||||
|
|
||||||
// create a mixin with the functions for text mode
|
// create a mixin with the functions for text mode
|
||||||
|
@ -102,7 +104,6 @@ previewmode.create = function (container, options) {
|
||||||
me.executeWithBusyMessage(function () {
|
me.executeWithBusyMessage(function () {
|
||||||
try {
|
try {
|
||||||
me.format();
|
me.format();
|
||||||
me._onChange();
|
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
me._onError(err);
|
me._onError(err);
|
||||||
|
@ -119,9 +120,7 @@ previewmode.create = function (container, options) {
|
||||||
buttonCompact.onclick = function handleCompact() {
|
buttonCompact.onclick = function handleCompact() {
|
||||||
me.executeWithBusyMessage(function () {
|
me.executeWithBusyMessage(function () {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
me.compact();
|
me.compact();
|
||||||
me._onChange();
|
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
me._onError(err);
|
me._onError(err);
|
||||||
|
@ -163,7 +162,6 @@ previewmode.create = function (container, options) {
|
||||||
me.executeWithBusyMessage(function () {
|
me.executeWithBusyMessage(function () {
|
||||||
try {
|
try {
|
||||||
me.repair();
|
me.repair();
|
||||||
me._onChange();
|
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
me._onError(err);
|
me._onError(err);
|
||||||
|
@ -171,6 +169,51 @@ previewmode.create = function (container, options) {
|
||||||
}, 'repairing...');
|
}, 'repairing...');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// create history and undo/redo buttons
|
||||||
|
if (this.options.history !== false) { // default option value is true
|
||||||
|
var onHistoryChange = function () {
|
||||||
|
me.dom.undo.disabled = !me.history.canUndo();
|
||||||
|
me.dom.redo.disabled = !me.history.canRedo();
|
||||||
|
};
|
||||||
|
|
||||||
|
var calculateItemSize = function (item) {
|
||||||
|
return item.text.length * 2; // times two to account for the json object
|
||||||
|
}
|
||||||
|
|
||||||
|
this.history = new History(onHistoryChange, calculateItemSize, PREVIEW_HISTORY_LIMIT);
|
||||||
|
|
||||||
|
// create undo button
|
||||||
|
var undo = document.createElement('button');
|
||||||
|
undo.type = 'button';
|
||||||
|
undo.className = 'jsoneditor-undo jsoneditor-separator';
|
||||||
|
undo.title = translate('undo');
|
||||||
|
undo.onclick = function () {
|
||||||
|
var action = me.history.undo();
|
||||||
|
if (action) {
|
||||||
|
me._applyHistory(action);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.menu.appendChild(undo);
|
||||||
|
this.dom.undo = undo;
|
||||||
|
|
||||||
|
// create redo button
|
||||||
|
var redo = document.createElement('button');
|
||||||
|
redo.type = 'button';
|
||||||
|
redo.className = 'jsoneditor-redo';
|
||||||
|
redo.title = translate('redo');
|
||||||
|
redo.onclick = function () {
|
||||||
|
var action = me.history.redo();
|
||||||
|
if (action) {
|
||||||
|
me._applyHistory(action);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.menu.appendChild(redo);
|
||||||
|
this.dom.redo = redo;
|
||||||
|
|
||||||
|
// force enabling/disabling the undo/redo button
|
||||||
|
this.history.onChange();
|
||||||
|
}
|
||||||
|
|
||||||
// create mode box
|
// create mode box
|
||||||
if (this.options && this.options.modes && this.options.modes.length) {
|
if (this.options && this.options.modes && this.options.modes.length) {
|
||||||
this.modeSwitcher = new ModeSwitcher(this.menu, this.options.modes, this.options.mode, function onSwitch(mode) {
|
this.modeSwitcher = new ModeSwitcher(this.menu, this.options.modes, this.options.mode, function onSwitch(mode) {
|
||||||
|
@ -203,12 +246,12 @@ previewmode.create = function (container, options) {
|
||||||
statusBar.appendChild(this.dom.arrayInfo);
|
statusBar.appendChild(this.dom.arrayInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.renderPreview();
|
this._renderPreview();
|
||||||
|
|
||||||
this.setSchema(this.options.schema, this.options.schemaRefs);
|
this.setSchema(this.options.schema, this.options.schemaRefs);
|
||||||
};
|
};
|
||||||
|
|
||||||
previewmode.renderPreview = function () {
|
previewmode._renderPreview = function () {
|
||||||
var text = this.getText();
|
var text = this.getText();
|
||||||
|
|
||||||
this.dom.previewText.nodeValue = util.limitCharacters(text, MAX_PREVIEW_CHARACTERS);
|
this.dom.previewText.nodeValue = util.limitCharacters(text, MAX_PREVIEW_CHARACTERS);
|
||||||
|
@ -232,7 +275,7 @@ previewmode.renderPreview = function () {
|
||||||
me.executeWithBusyMessage(function () {
|
me.executeWithBusyMessage(function () {
|
||||||
try {
|
try {
|
||||||
me.get();
|
me.get();
|
||||||
me.renderPreview();
|
me._renderPreview();
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
me._onError(err);
|
me._onError(err);
|
||||||
|
@ -259,10 +302,6 @@ previewmode.renderPreview = function () {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
previewmode._onChange = function () {
|
previewmode._onChange = function () {
|
||||||
if (this.onChangeDisabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate JSON schema (if configured)
|
// validate JSON schema (if configured)
|
||||||
this._debouncedValidate();
|
this._debouncedValidate();
|
||||||
|
|
||||||
|
@ -306,31 +345,29 @@ previewmode._showSortModal = function () {
|
||||||
|
|
||||||
function onSort (json, sortedBy) {
|
function onSort (json, sortedBy) {
|
||||||
if (Array.isArray(json)) {
|
if (Array.isArray(json)) {
|
||||||
var sortedJson = util.sort(json, sortedBy.path, sortedBy.direction);
|
var sortedArray = util.sort(json, sortedBy.path, sortedBy.direction);
|
||||||
|
|
||||||
me.sortedBy = sortedBy
|
me.sortedBy = sortedBy
|
||||||
me.set(sortedJson);
|
me._setAndFireOnChange(sortedArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (util.isObject(json)) {
|
if (util.isObject(json)) {
|
||||||
var sortedJson = util.sortObjectKeys(json, sortedBy.direction);
|
var sortedObject = util.sortObjectKeys(json, sortedBy.direction);
|
||||||
|
|
||||||
me.sortedBy = sortedBy;
|
me.sortedBy = sortedBy;
|
||||||
me.set(sortedJson);
|
me._setAndFireOnChange(sortedObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
me._onChange();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.executeWithBusyMessage(function () {
|
this.executeWithBusyMessage(function () {
|
||||||
var container = me.options.modalAnchor || DEFAULT_MODAL_ANCHOR;
|
var container = me.options.modalAnchor || DEFAULT_MODAL_ANCHOR;
|
||||||
var json = me.get();
|
var json = me.get();
|
||||||
me.renderPreview(); // update array count
|
me._renderPreview(); // update array count
|
||||||
|
|
||||||
showSortModal(container, json, function (sortedBy) {
|
showSortModal(container, json, function (sortedBy) {
|
||||||
me.executeWithBusyMessage(function () {
|
me.executeWithBusyMessage(function () {
|
||||||
onSort(json, sortedBy);
|
onSort(json, sortedBy);
|
||||||
}, 'stringifying...');
|
}, 'sorting...');
|
||||||
}, me.sortedBy)
|
}, me.sortedBy)
|
||||||
}, 'parsing...');
|
}, 'parsing...');
|
||||||
}
|
}
|
||||||
|
@ -345,14 +382,12 @@ previewmode._showTransformModal = function () {
|
||||||
this.executeWithBusyMessage(function () {
|
this.executeWithBusyMessage(function () {
|
||||||
var anchor = me.options.modalAnchor || DEFAULT_MODAL_ANCHOR;
|
var anchor = me.options.modalAnchor || DEFAULT_MODAL_ANCHOR;
|
||||||
var json = me.get();
|
var json = me.get();
|
||||||
me.renderPreview(); // update array count
|
me._renderPreview(); // update array count
|
||||||
|
|
||||||
showTransformModal(anchor, json, function (query) {
|
showTransformModal(anchor, json, function (query) {
|
||||||
me.executeWithBusyMessage(function () {
|
me.executeWithBusyMessage(function () {
|
||||||
var updatedJson = jmespath.search(json, query);
|
var updatedJson = jmespath.search(json, query);
|
||||||
me.set(updatedJson);
|
me._setAndFireOnChange(updatedJson);
|
||||||
|
|
||||||
me._onChange();
|
|
||||||
}, 'transforming...')
|
}, 'transforming...')
|
||||||
})
|
})
|
||||||
}, 'parsing...')
|
}, 'parsing...')
|
||||||
|
@ -362,7 +397,7 @@ previewmode._showTransformModal = function () {
|
||||||
* Destroy the editor. Clean up DOM, event listeners, and web workers.
|
* Destroy the editor. Clean up DOM, event listeners, and web workers.
|
||||||
*/
|
*/
|
||||||
previewmode.destroy = function () {
|
previewmode.destroy = function () {
|
||||||
if (this.frame && this.container && this.frame.parentNode == this.container) {
|
if (this.frame && this.container && this.frame.parentNode === this.container) {
|
||||||
this.container.removeChild(this.frame);
|
this.container.removeChild(this.frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,6 +407,9 @@ previewmode.destroy = function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
this._debouncedValidate = null;
|
this._debouncedValidate = null;
|
||||||
|
|
||||||
|
this.history.clear();
|
||||||
|
this.history = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -380,12 +418,9 @@ previewmode.destroy = function () {
|
||||||
previewmode.compact = function () {
|
previewmode.compact = function () {
|
||||||
var json = this.get();
|
var json = this.get();
|
||||||
var text = JSON.stringify(json);
|
var text = JSON.stringify(json);
|
||||||
this.setText(text);
|
|
||||||
|
|
||||||
// we know that in this case the json is still the same
|
// we know that in this case the json is still the same, so we pass json too
|
||||||
this.json = json;
|
this._setTextAndFireOnChange(text, json);
|
||||||
|
|
||||||
this.renderPreview();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -394,12 +429,9 @@ previewmode.compact = function () {
|
||||||
previewmode.format = function () {
|
previewmode.format = function () {
|
||||||
var json = this.get();
|
var json = this.get();
|
||||||
var text = JSON.stringify(json, null, this.indentation);
|
var text = JSON.stringify(json, null, this.indentation);
|
||||||
this.setText(text);
|
|
||||||
|
|
||||||
// we know that in this case the json is still the same
|
// we know that in this case the json is still the same, so we pass json too
|
||||||
this.json = json;
|
this._setTextAndFireOnChange(text, json);
|
||||||
|
|
||||||
this.renderPreview();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -408,28 +440,27 @@ previewmode.format = function () {
|
||||||
previewmode.repair = function () {
|
previewmode.repair = function () {
|
||||||
var text = this.getText();
|
var text = this.getText();
|
||||||
var sanitizedText = util.sanitize(text);
|
var sanitizedText = util.sanitize(text);
|
||||||
this.setText(sanitizedText);
|
|
||||||
|
this._setTextAndFireOnChange(sanitizedText);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set focus to the formatter
|
* Set focus to the editor
|
||||||
*/
|
*/
|
||||||
previewmode.focus = function () {
|
previewmode.focus = function () {
|
||||||
// TODO: implement method focus
|
// TODO: implement method focus
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set json data in the formatter
|
* Set json data in the editor
|
||||||
* @param {*} json
|
* @param {*} json
|
||||||
*/
|
*/
|
||||||
previewmode.set = function(json) {
|
previewmode.set = function(json) {
|
||||||
this.text = undefined;
|
if (this.history) {
|
||||||
this.json = json;
|
this.history.clear();
|
||||||
|
}
|
||||||
|
|
||||||
this.renderPreview();
|
this._set(json);
|
||||||
|
|
||||||
// validate JSON schema
|
|
||||||
this._debouncedValidate();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -437,11 +468,32 @@ previewmode.set = function(json) {
|
||||||
* @param {*} json
|
* @param {*} json
|
||||||
*/
|
*/
|
||||||
previewmode.update = function(json) {
|
previewmode.update = function(json) {
|
||||||
this.set(json);
|
this._set(json);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get json data from the formatter
|
* Set json data
|
||||||
|
* @param {*} json
|
||||||
|
*/
|
||||||
|
previewmode._set = function(json) {
|
||||||
|
this.text = undefined;
|
||||||
|
this.json = json;
|
||||||
|
|
||||||
|
this._renderPreview();
|
||||||
|
|
||||||
|
this._pushHistory();
|
||||||
|
|
||||||
|
// validate JSON schema
|
||||||
|
this._debouncedValidate();
|
||||||
|
};
|
||||||
|
|
||||||
|
previewmode._setAndFireOnChange = function (json) {
|
||||||
|
this._set(json);
|
||||||
|
this._onChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get json data
|
||||||
* @return {*} json
|
* @return {*} json
|
||||||
*/
|
*/
|
||||||
previewmode.get = function() {
|
previewmode.get = function() {
|
||||||
|
@ -490,20 +542,11 @@ previewmode.getText = function() {
|
||||||
* @param {String} jsonText
|
* @param {String} jsonText
|
||||||
*/
|
*/
|
||||||
previewmode.setText = function(jsonText) {
|
previewmode.setText = function(jsonText) {
|
||||||
if (this.options.escapeUnicode === true) {
|
if (this.history) {
|
||||||
console.time('escape') // TODO: cleanup
|
this.history.clear();
|
||||||
this.text = util.escapeUnicodeChars(jsonText);
|
|
||||||
console.timeEnd('escape') // TODO: cleanup
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
this.text = jsonText;
|
|
||||||
}
|
|
||||||
this.json = undefined;
|
|
||||||
|
|
||||||
this.renderPreview();
|
this._setText(jsonText);
|
||||||
|
|
||||||
// validate JSON schema
|
|
||||||
this._debouncedValidate();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -516,11 +559,75 @@ previewmode.updateText = function(jsonText) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.onChangeDisabled = true; // don't fire an onChange event
|
this._setText(jsonText);
|
||||||
this.setText(jsonText);
|
|
||||||
this.onChangeDisabled = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the text contents of the editor
|
||||||
|
* @param {string} jsonText
|
||||||
|
* @param {*} [json] Optional JSON instance of the text
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
previewmode._setText = function(jsonText, json) {
|
||||||
|
if (this.options.escapeUnicode === true) {
|
||||||
|
console.time('escape') // TODO: cleanup
|
||||||
|
this.text = util.escapeUnicodeChars(jsonText);
|
||||||
|
console.timeEnd('escape') // TODO: cleanup
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.text = jsonText;
|
||||||
|
}
|
||||||
|
this.json = json;
|
||||||
|
|
||||||
|
this._renderPreview();
|
||||||
|
|
||||||
|
this._pushHistory();
|
||||||
|
|
||||||
|
this._debouncedValidate();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set text and fire onChange callback
|
||||||
|
* @param {string} jsonText
|
||||||
|
* @param {*} [json] Optional JSON instance of the text
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
previewmode._setTextAndFireOnChange = function (jsonText, json) {
|
||||||
|
this._setText(jsonText, json);
|
||||||
|
this._onChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply history to the current state
|
||||||
|
* @param {{json?: JSON, text?: string}} action
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
previewmode._applyHistory = function (action) {
|
||||||
|
this.json = action.json;
|
||||||
|
this.text = action.text;
|
||||||
|
|
||||||
|
this._renderPreview();
|
||||||
|
|
||||||
|
this._debouncedValidate();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Push the current state to history
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
previewmode._pushHistory = function () {
|
||||||
|
if (!this.history) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var action = {
|
||||||
|
text: this.text,
|
||||||
|
json: this.json
|
||||||
|
};
|
||||||
|
|
||||||
|
this.history.add(action);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute a heavy, blocking action.
|
* Execute a heavy, blocking action.
|
||||||
* Before starting the action, show a message on screen like "parsing..."
|
* Before starting the action, show a message on screen like "parsing..."
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
var VanillaPicker = require('./vanilla-picker');
|
var VanillaPicker = require('./vanilla-picker');
|
||||||
var Highlighter = require('./Highlighter');
|
var Highlighter = require('./Highlighter');
|
||||||
var History = require('./History');
|
var NodeHistory = require('./NodeHistory');
|
||||||
var SearchBox = require('./SearchBox');
|
var SearchBox = require('./SearchBox');
|
||||||
var ContextMenu = require('./ContextMenu');
|
var ContextMenu = require('./ContextMenu');
|
||||||
var TreePath = require('./TreePath');
|
var TreePath = require('./TreePath');
|
||||||
|
@ -47,7 +47,7 @@ treemode.create = function (container, options) {
|
||||||
this.autocomplete = new autocomplete(options.autocomplete);
|
this.autocomplete = new autocomplete(options.autocomplete);
|
||||||
|
|
||||||
if (this.options.history && this.options.mode !== 'view') {
|
if (this.options.history && this.options.mode !== 'view') {
|
||||||
this.history = new History(this);
|
this.history = new NodeHistory(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._createFrame();
|
this._createFrame();
|
||||||
|
|
Loading…
Reference in New Issue