UNPKG

wed

Version:

Wed is a schema-aware editor for XML documents.

204 lines 8.37 kB
/** * Listens to changes on a tree and updates the GUI tree in response to changes. * @author Louis-Dominique Dubeau * @license MPL 2.0 * @copyright Mangalam Research Center for Buddhist Languages */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; 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", "jquery", "./convert", "./dloc", "./domtypeguards", "./domutil", "./tree-updater", "./util"], function (require, exports, jquery_1, convert, dloc_1, domtypeguards_1, domutil_1, tree_updater_1, util) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); jquery_1 = __importDefault(jquery_1); convert = __importStar(convert); util = __importStar(util); /** * Updates a GUI tree so that its data nodes (those nodes that are not * decorations) mirror a data tree. */ class GUIUpdater extends tree_updater_1.TreeUpdater { /** * @param guiTree The DOM tree to update. * * @param treeUpdater A tree updater that updates the data tree. It serves as * a source of modification events which the object being created will listen * on. */ constructor(guiTree, treeUpdater) { super(guiTree); this.treeUpdater = treeUpdater; this.haveTooltips = guiTree.ownerDocument.getElementsByClassName("wed-has-tooltip"); this.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... } }); } /** * Handles "InsertNodeAt" events. * * @param ev The event. */ _insertNodeAtHandler(ev) { const guiCaret = this.fromDataLocation(ev.parent, ev.index); if (guiCaret === null) { throw new Error("cannot find gui tree position"); } const clone = convert.toHTMLTree(this.tree.ownerDocument, ev.node); if (domtypeguards_1.isElement(ev.node)) { // If ev.node is an element, then the clone is an element too. domutil_1.linkTrees(ev.node, clone); } this.insertNodeAt(guiCaret, clone); } /** * Handles "SetTextNodeValue" events. * * @param ev The event. */ _setTextNodeValueHandler(ev) { const guiCaret = this.fromDataLocation(ev.node, 0); if (guiCaret === null) { throw new Error("cannot find gui tree position"); } this.setTextNodeValue(guiCaret.node, ev.value); } /** * Handles "BeforeDeleteNode" events. * * @param ev The event. */ _beforeDeleteNodeHandler(ev) { const dataNode = ev.node; let toRemove; let element = false; switch (dataNode.nodeType) { case Node.TEXT_NODE: const guiCaret = this.fromDataLocation(dataNode, 0); if (guiCaret === null) { throw new Error("cannot find gui tree position"); } toRemove = guiCaret.node; break; case Node.ELEMENT_NODE: toRemove = jquery_1.default.data(dataNode, "wed_mirror_node"); element = true; break; default: } this.deleteNode(toRemove); // We have to do this **after** we delete the node. if (element) { domutil_1.unlinkTree(dataNode); domutil_1.unlinkTree(toRemove); } } /** * Handles "SetAttributeNS" events. * * @param ev The event. */ _setAttributeNSHandler(ev) { const guiCaret = this.fromDataLocation(ev.node, 0); if (guiCaret === null) { throw new Error("cannot find gui tree position"); } this.setAttributeNS(guiCaret.node, "", util.encodeAttrName(ev.attribute), ev.newValue); } fromDataLocation(loc, offset) { let node; if (loc instanceof dloc_1.DLoc) { node = loc.node; offset = loc.offset; } else { node = loc; if (offset === undefined) { throw new Error("must specify an offset"); } } let guiNode = this.pathToNode(this.treeUpdater.nodeToPath(node)); if (guiNode === null) { return null; } if (domtypeguards_1.isText(node)) { return dloc_1.DLoc.mustMakeDLoc(this.tree, guiNode, offset); } if (domutil_1.isAttr(node)) { // The check for the node type is to avoid getting a location inside a // placeholder. if (domtypeguards_1.isText(guiNode.firstChild)) { guiNode = guiNode.firstChild; } return dloc_1.DLoc.mustMakeDLoc(this.tree, guiNode, offset); } if (offset === 0) { return dloc_1.DLoc.mustMakeDLoc(this.tree, guiNode, 0); } if (offset >= node.childNodes.length) { return dloc_1.DLoc.mustMakeDLoc(this.tree, guiNode, guiNode.childNodes.length); } const guiChild = this.pathToNode(this.treeUpdater.nodeToPath(node.childNodes[offset])); if (guiChild === null) { // This happens if for instance node has X children but the // corresponding node in tree has X-1 children. return dloc_1.DLoc.mustMakeDLoc(this.tree, guiNode, guiNode.childNodes.length); } return dloc_1.DLoc.mustMakeDLoc(this.tree, guiChild); } /** * Check whether a tooltip should be destroyed when the element is removed * from the tree. This function checks whether the element or any descendant * has a tooltip. * * @param el An element to check. * */ removeTooltips(el) { for (const hasTooltip of Array.from(this.haveTooltips)) { if (!el.contains(hasTooltip)) { continue; } const tt = jquery_1.default.data(hasTooltip, "bs.tooltip"); if (tt != null) { tt.destroy(); } // We don't remove the wed-has-tooltip class. Generally, the elements // that have tooltips and are removed from the GUI tree won't be added // to the tree again. If they are added again, they'll most likely get // a new tooltip so removing the class does not gain us much because // it will be added again. // // If we *were* to remove the class, then the collection would change // as we go through it. } } } exports.GUIUpdater = GUIUpdater; }); // LocalWords: domutil jquery pathToNode nodeToPath jQuery deleteNode Dubeau // LocalWords: insertNodeAt MPL Mangalam gui setTextNodeValue TreeUpdater ev // LocalWords: BeforeDeleteNode SetAttributeNS //# sourceMappingURL=gui-updater.js.map