wed
Version:
Wed is a schema-aware editor for XML documents.
254 lines • 9.87 kB
JavaScript
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
define(["require", "exports", "./domutil", "./undo"], function (require, exports, domutil_1, undo) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
undo = __importStar(undo);
function getOuterHTML(node) {
return (node == null) ? "undefined" : node.outerHTML;
}
/**
* Undo operation for [["wed/tree-updater".InsertNodeAtEvent]].
*
* The parameters after ``tree_updater`` are the same as the properties on the
* event corresponding to this class.
*
* @private
*/
class InsertNodeAtUndo extends undo.Undo {
/**
* @param treeUpdater The tree updater to use to perform undo or redo
* operations.
*
* @param parent
* @param index
*/
constructor(treeUpdater, parent, index) {
super("InsertNodeAtUndo");
this.treeUpdater = treeUpdater;
this.index = index;
this.parentPath = treeUpdater.nodeToPath(parent);
// We do not take a node parameter and save it here because further
// manipulations could take the node out of the tree. So we cannot rely in a
// reference to a node. What we do instead is keep a path to the parent and
// the index. The ``node`` property will be filled as needed when
// undoing/redoing.
}
performUndo() {
if (this.node !== undefined) {
throw new Error("undo called twice in a row");
}
const parent = this.treeUpdater.pathToNode(this.parentPath);
this.node = parent.childNodes[this.index].cloneNode(true);
this.treeUpdater.deleteNode(parent.childNodes[this.index]);
}
performRedo() {
if (this.node === undefined) {
throw new Error("redo called twice in a row");
}
const parent = this.treeUpdater.pathToNode(this.parentPath);
this.treeUpdater.insertNodeAt(parent, this.index, this.node);
this.node = undefined;
}
toString() {
return [this.desc, "\n",
" Parent path: ", this.parentPath, "\n",
" Index: ", this.index, "\n",
" Node: ", getOuterHTML(this.node), "\n"].join("");
}
}
/**
* Undo operation for [["wed/tree-updater".SetTextNodeValueEvent]].
*
* @private
*/
class SetTextNodeValueUndo extends undo.Undo {
/**
* @param treeUpdater The tree updater to use to perform undo or redo
* operations.
*/
constructor(treeUpdater, node, value, oldValue) {
super("SetTextNodeValueUndo");
this.treeUpdater = treeUpdater;
this.value = value;
this.oldValue = oldValue;
this.nodePath = treeUpdater.nodeToPath(node);
}
performUndo() {
// The node is necessarily a text node.
const node = this.treeUpdater.pathToNode(this.nodePath);
this.treeUpdater.setTextNodeValue(node, this.oldValue);
}
performRedo() {
// The node is necessarily a text node.
const node = this.treeUpdater.pathToNode(this.nodePath);
this.treeUpdater.setTextNodeValue(node, this.value);
}
toString() {
return [this.desc, "\n",
" Node path: ", this.nodePath, "\n",
" Value: ", this.value, "\n",
" Old value: ", this.oldValue, "\n"].join("");
}
}
/**
* Undo operation for [["wed/tree-updater".BeforeDeleteNodeEvent]].
*
* @private
*/
class DeleteNodeUndo extends undo.Undo {
/**
* @param treeUpdater The tree updater to use to perform undo or redo
* operations.
*/
constructor(treeUpdater, node) {
super("DeleteNodeUndo");
this.treeUpdater = treeUpdater;
const parent = node.parentNode;
this.parentPath = treeUpdater.nodeToPath(parent);
this.index = domutil_1.indexOf(parent.childNodes, node);
this.node = node.cloneNode(true);
}
performUndo() {
if (this.node === undefined) {
throw new Error("undo called twice in a row");
}
const parent = this.treeUpdater.pathToNode(this.parentPath);
this.treeUpdater.insertNodeAt(parent, this.index, this.node);
this.node = undefined;
}
performRedo() {
if (this.node !== undefined) {
throw new Error("redo called twice in a row");
}
const parent = this.treeUpdater.pathToNode(this.parentPath);
this.node = parent.childNodes[this.index].cloneNode(true);
this.treeUpdater.deleteNode(parent.childNodes[this.index]);
}
toString() {
return [this.desc, "\n",
" Parent path: ", this.parentPath, "\n",
" Index: ", this.index, "\n",
" Node: ", getOuterHTML(this.node), "\n"].join("");
}
}
/**
* Undo operation for [["wed/tree-updater".SetAttributeNSEvent]].
*
* @private
*/
class SetAttributeNSUndo extends undo.Undo {
/**
* @param treeUpdater The tree updater to use to perform undo or redo
* operations.
*/
constructor(treeUpdater, node, ns, attribute, oldValue, newValue) {
super("SetAttributeNSUndo");
this.treeUpdater = treeUpdater;
this.ns = ns;
this.attribute = attribute;
this.oldValue = oldValue;
this.newValue = newValue;
this.nodePath = treeUpdater.nodeToPath(node);
}
performUndo() {
const node = this.treeUpdater.pathToNode(this.nodePath);
this.treeUpdater.setAttributeNS(node, this.ns, this.attribute, this.oldValue);
}
performRedo() {
const node = this.treeUpdater.pathToNode(this.nodePath);
this.treeUpdater.setAttributeNS(node, this.ns, this.attribute, this.newValue);
}
toString() {
return [this.desc, "\n",
" Node path: ", this.nodePath, "\n",
" Namespace: ", this.ns, "\n",
" Attribute Name: ", this.attribute, "\n",
" New value: ", this.newValue, "\n",
" Old value: ", this.oldValue, "\n"].join("");
}
}
/**
* Records undo operations.
*/
class UndoRecorder {
/**
* @param editor The editor for which this recorder is created.
*
* @param treeUpdater The tree updater on which to listen for modifications.
*/
constructor(editor, treeUpdater) {
this.editor = editor;
this.treeUpdater = treeUpdater;
this.suppress = false;
treeUpdater.events.subscribe((ev) => {
switch (ev.name) {
case "InsertNodeAt":
this.insertNodeAtHandler(ev);
break;
case "SetTextNodeValue":
this.setTextNodeValueHandler(ev);
break;
case "BeforeDeleteNode":
this.beforeDeleteNodeHandler(ev);
break;
case "SetAttributeNS":
this.setAttributeNSHandler(ev);
break;
default:
// Do nothing...
}
});
}
/**
* Sets the suppression state. When suppression is on, the recorder does not
* record anything. When off, the recorder records. The recorder's suppression
* state is initially off.
*
* @param suppress Whether to suppress or not.
*
* @throws {Error} If the call does not change the suppression state.
*/
suppressRecording(suppress) {
if (suppress === this.suppress) {
throw new Error("spurious call to suppressRecording");
}
this.suppress = suppress;
}
insertNodeAtHandler(ev) {
if (this.suppress) {
return;
}
this.editor.recordUndo(new InsertNodeAtUndo(this.treeUpdater, ev.parent, ev.index));
}
setTextNodeValueHandler(ev) {
if (this.suppress) {
return;
}
this.editor.recordUndo(new SetTextNodeValueUndo(this.treeUpdater, ev.node, ev.value, ev.oldValue));
}
beforeDeleteNodeHandler(ev) {
if (this.suppress) {
return;
}
this.editor.recordUndo(new DeleteNodeUndo(this.treeUpdater, ev.node));
}
setAttributeNSHandler(ev) {
if (this.suppress) {
return;
}
this.editor.recordUndo(new SetAttributeNSUndo(this.treeUpdater, ev.node, ev.ns, ev.attribute, ev.oldValue, ev.newValue));
}
}
exports.UndoRecorder = UndoRecorder;
});
// LocalWords: domutil insertNodeAt setTextNodeValue deleteNode ev param MPL
// LocalWords: InsertNodeAtUndo SetTextNodeValueUndo DeleteNodeUndo Dubeau
// LocalWords: pathToNode nodeToPath Mangalam SetAttributeNSUndo
// LocalWords: BeforeDeleteNode SetAttributeNS suppressRecording
//# sourceMappingURL=undo-recorder.js.map