/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 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/. */ const Cc = Components.classes; const Cu = Components.utils; const Ci = Components.interfaces; const PSEUDO_CLASSES = [":hover", ":active", ":focus"]; this.EXPORTED_SYMBOLS = ["HTMLBreadcrumbs"]; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource:///modules/devtools/DOMHelpers.jsm"); Cu.import("resource:///modules/devtools/LayoutHelpers.jsm"); const LOW_PRIORITY_ELEMENTS = { "HEAD": true, "BASE": true, "BASEFONT": true, "ISINDEX": true, "LINK": true, "META": true, "SCRIPT": true, "STYLE": true, "TITLE": true, }; /////////////////////////////////////////////////////////////////////////// //// HTML Breadcrumbs /** * Display the ancestors of the current node and its children. * Only one "branch" of children are displayed (only one line). * * Mechanism: * . If no nodes displayed yet: * then display the ancestor of the selected node and the selected node; * else select the node; * . If the selected node is the last node displayed, append its first (if any). */ this.HTMLBreadcrumbs = function HTMLBreadcrumbs(aInspector) { this.inspector = aInspector; this.selection = this.inspector.selection; this.chromeWin = this.inspector.panelWin; this.chromeDoc = this.inspector.panelDoc; this.DOMHelpers = new DOMHelpers(this.chromeWin); this._init(); } HTMLBreadcrumbs.prototype = { _init: function BC__init() { this.container = this.chromeDoc.getElementById("inspector-breadcrumbs"); this.container.addEventListener("mousedown", this, true); this.container.addEventListener("keypress", this, true); // We will save a list of already displayed nodes in this array. this.nodeHierarchy = []; // Last selected node in nodeHierarchy. this.currentIndex = -1; // By default, hide the arrows. We let the show them // in case of overflow. this.container.removeAttribute("overflows"); this.container._scrollButtonUp.collapsed = true; this.container._scrollButtonDown.collapsed = true; this.onscrollboxreflow = function() { if (this.container._scrollButtonDown.collapsed) this.container.removeAttribute("overflows"); else this.container.setAttribute("overflows", true); }.bind(this); this.container.addEventListener("underflow", this.onscrollboxreflow, false); this.container.addEventListener("overflow", this.onscrollboxreflow, false); this.update = this.update.bind(this); this.updateSelectors = this.updateSelectors.bind(this); this.selection.on("new-node", this.update); this.selection.on("pseudoclass", this.updateSelectors); this.selection.on("attribute-changed", this.updateSelectors); this.update(); }, /** * Build a string that represents the node: tagName#id.class1.class2. * * @param aNode The node to pretty-print * @returns a string */ prettyPrintNodeAsText: function BC_prettyPrintNodeText(aNode) { let text = aNode.tagName.toLowerCase(); if (aNode.id) { text += "#" + aNode.id; } for (let i = 0; i < aNode.classList.length; i++) { text += "." + aNode.classList[i]; } for (let i = 0; i < PSEUDO_CLASSES.length; i++) { let pseudo = PSEUDO_CLASSES[i]; if (DOMUtils.hasPseudoClassLock(aNode, pseudo)) { text += pseudo; } } return text; }, /** * Build