/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; const nodeConstants = require("devtools/shared/dom-node-constants"); var EventEmitter = require("devtools/shared/event-emitter"); /** * API * * new Selection(walker=null) * destroy() * node (readonly) * setNode(node, origin="unknown") * * Helpers: * * window * document * isRoot() * isNode() * isHTMLNode() * * Check the nature of the node: * * isElementNode() * isAttributeNode() * isTextNode() * isCDATANode() * isEntityRefNode() * isEntityNode() * isProcessingInstructionNode() * isCommentNode() * isDocumentNode() * isDocumentTypeNode() * isDocumentFragmentNode() * isNotationNode() * * Events: * "new-node-front" when the inner node changed * "attribute-changed" when an attribute is changed * "detached-front" when the node (or one of its parents) is removed from * the document * "reparented" when the node (or one of its parents) is moved under * a different node */ /** * A Selection object. Hold a reference to a node. * Includes some helpers, fire some helpful events. */ function Selection(walker) { EventEmitter.decorate(this); this._onMutations = this._onMutations.bind(this); this.setWalker(walker); } exports.Selection = Selection; Selection.prototype = { _walker: null, _onMutations: function (mutations) { let attributeChange = false; let pseudoChange = false; let detached = false; let parentNode = null; for (let m of mutations) { if (!attributeChange && m.type == "attributes") { attributeChange = true; } if (m.type == "childList") { if (!detached && !this.isConnected()) { if (this.isNode()) { parentNode = m.target; } detached = true; } } if (m.type == "pseudoClassLock") { pseudoChange = true; } } // Fire our events depending on what changed in the mutations array if (attributeChange) { this.emit("attribute-changed"); } if (pseudoChange) { this.emit("pseudoclass"); } if (detached) { this.emit("detached-front", parentNode); } }, destroy: function () { this.setWalker(null); }, setWalker: function (walker) { if (this._walker) { this._walker.off("mutations", this._onMutations); } this._walker = walker; if (this._walker) { this._walker.on("mutations", this._onMutations); } }, setNodeFront: function (value, reason = "unknown") { this.reason = reason; // If an inlineTextChild text node is being set, then set it's parent instead. let parentNode = value && value.parentNode(); if (value && parentNode && parentNode.inlineTextChild === value) { value = parentNode; } this._nodeFront = value; this.emit("new-node-front", value, this.reason); }, get documentFront() { return this._walker.document(this._nodeFront); }, get nodeFront() { return this._nodeFront; }, isRoot: function () { return this.isNode() && this.isConnected() && this._nodeFront.isDocumentElement; }, isNode: function () { return !!this._nodeFront; }, isConnected: function () { let node = this._nodeFront; if (!node || !node.actorID) { return false; } while (node) { if (node === this._walker.rootNode) { return true; } node = node.parentNode(); } return false; }, isHTMLNode: function () { let xhtmlNs = "http://www.w3.org/1999/xhtml"; return this.isNode() && this.nodeFront.namespaceURI == xhtmlNs; }, // Node type isElementNode: function () { return this.isNode() && this.nodeFront.nodeType == nodeConstants.ELEMENT_NODE; }, isPseudoElementNode: function () { return this.isNode() && this.nodeFront.isPseudoElement; }, isAnonymousNode: function () { return this.isNode() && this.nodeFront.isAnonymous; }, isAttributeNode: function () { return this.isNode() && this.nodeFront.nodeType == nodeConstants.ATTRIBUTE_NODE; }, isTextNode: function () { return this.isNode() && this.nodeFront.nodeType == nodeConstants.TEXT_NODE; }, isCDATANode: function () { return this.isNode() && this.nodeFront.nodeType == nodeConstants.CDATA_SECTION_NODE; }, isEntityRefNode: function () { return this.isNode() && this.nodeFront.nodeType == nodeConstants.ENTITY_REFERENCE_NODE; }, isEntityNode: function () { return this.isNode() && this.nodeFront.nodeType == nodeConstants.ENTITY_NODE; }, isProcessingInstructionNode: function () { return this.isNode() && this.nodeFront.nodeType == nodeConstants.PROCESSING_INSTRUCTION_NODE; }, isCommentNode: function () { return this.isNode() && this.nodeFront.nodeType == nodeConstants.PROCESSING_INSTRUCTION_NODE; }, isDocumentNode: function () { return this.isNode() && this.nodeFront.nodeType == nodeConstants.DOCUMENT_NODE; }, /** * @returns true if the selection is the <body> HTML element. */ isBodyNode: function () { return this.isHTMLNode() && this.isConnected() && this.nodeFront.nodeName === "BODY"; }, /** * @returns true if the selection is the <head> HTML element. */ isHeadNode: function () { return this.isHTMLNode() && this.isConnected() && this.nodeFront.nodeName === "HEAD"; }, isDocumentTypeNode: function () { return this.isNode() && this.nodeFront.nodeType == nodeConstants.DOCUMENT_TYPE_NODE; }, isDocumentFragmentNode: function () { return this.isNode() && this.nodeFront.nodeType == nodeConstants.DOCUMENT_FRAGMENT_NODE; }, isNotationNode: function () { return this.isNode() && this.nodeFront.nodeType == nodeConstants.NOTATION_NODE; }, };