Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Issue 5931: Integrate SelectBug extension into Firebug

  • Loading branch information...
commit 34c8d8a18050537cbc1b322e267d868621fb695a 1 parent 068aef5
Jan Odvarko authored September 19, 2012
59  extension/content/firebug/css/selectorEditor.js
... ...
@@ -0,0 +1,59 @@
  1
+/* See license.txt for terms of usage */
  2
+
  3
+define([
  4
+    "firebug/firebug",
  5
+    "firebug/lib/domplate",
  6
+    "firebug/lib/locale",
  7
+],
  8
+function(Firebug, Domplate, Locale) {
  9
+with (Domplate) {
  10
+
  11
+// ********************************************************************************************* //
  12
+// Constants
  13
+
  14
+const Cc = Components.classes;
  15
+const Ci = Components.interfaces;
  16
+
  17
+// ********************************************************************************************* //
  18
+// CSS Selector Editor
  19
+
  20
+function SelectorEditor(panel)
  21
+{
  22
+    var doc = panel.document;
  23
+
  24
+    this.panel = panel;
  25
+    this.box = this.tag.replace({}, doc, this);
  26
+    this.input = this.box;
  27
+
  28
+    this.tabNavigation = false;
  29
+    this.tabCompletion = true;
  30
+    this.completeAsYouType = false;
  31
+    this.fixedWidth = true;
  32
+}
  33
+
  34
+SelectorEditor.prototype = domplate(Firebug.InlineEditor.prototype,
  35
+{
  36
+    tag:
  37
+        INPUT({"class": "fixedWidthEditor a11yFocusNoTab",
  38
+            type: "text",
  39
+            title: Locale.$STR("Selector"),
  40
+            oninput: "$onInput",
  41
+            onkeypress: "$onKeyPress"}
  42
+        ),
  43
+
  44
+    endEditing: function(target, value, cancel)
  45
+    {
  46
+        if (cancel || value == "")
  47
+            return;
  48
+
  49
+        this.panel.setTrialSelector(target, value);
  50
+    },
  51
+});
  52
+
  53
+// ********************************************************************************************* //
  54
+// Registration
  55
+
  56
+return SelectorEditor;
  57
+
  58
+// ********************************************************************************************* //
  59
+}});
392  extension/content/firebug/css/selectorPanel.js
... ...
@@ -0,0 +1,392 @@
  1
+/* See license.txt for terms of usage */
  2
+
  3
+define([
  4
+    "firebug/firebug",
  5
+    "firebug/lib/object",
  6
+    "firebug/lib/locale",
  7
+    "firebug/lib/events",
  8
+    "firebug/lib/dom",
  9
+    "firebug/lib/domplate",
  10
+    "firebug/css/selectorEditor",
  11
+],
  12
+function(Firebug, Obj, Locale, Events, Dom, Domplate, SelectorEditor) {
  13
+with (Domplate) {
  14
+
  15
+// ********************************************************************************************* //
  16
+// Constants
  17
+
  18
+const Cc = Components.classes;
  19
+const Ci = Components.interfaces;
  20
+
  21
+const prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch2);
  22
+
  23
+// ********************************************************************************************* //
  24
+// CSS Selector Panel
  25
+
  26
+/**
  27
+ * @panel Selector side panel displaying HTML elements for the current selector,
  28
+ * either from the CSS main panel or user entry
  29
+ */
  30
+function SelectorPanel() {}
  31
+SelectorPanel.prototype = Obj.extend(Firebug.Panel,
  32
+/** @lends SelectorPanel */
  33
+{
  34
+    name: "selector",
  35
+    parentPanel: "stylesheet",
  36
+    title: Locale.$STR("css.selector.Selection"),
  37
+    editable: true,
  38
+
  39
+    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
  40
+    // Initialization
  41
+
  42
+    initialize: function(context, doc)
  43
+    {
  44
+        Firebug.Panel.initialize.apply(this, arguments);
  45
+    },
  46
+
  47
+    initializeNode: function(oldPanelNode)
  48
+    {
  49
+        Firebug.Panel.initializeNode.apply(this, arguments);
  50
+
  51
+        this.setSelection = Obj.bind(this.setSelection, this);
  52
+        this.clearSelection = Obj.bind(this.clearSelection, this);
  53
+        this.lockSelection = Obj.bind(this.lockSelection, this);
  54
+
  55
+        var panelNode = this.mainPanel.panelNode;
  56
+        Events.addEventListener(panelNode, "mouseover", this.setSelection, false);
  57
+        Events.addEventListener(panelNode, "mouseout", this.clearSelection, false);
  58
+        Events.addEventListener(panelNode, "mousedown", this.lockSelection, false);
  59
+    },
  60
+
  61
+    destroyNode: function()
  62
+    {
  63
+        var panelNode = this.mainPanel.panelNode;
  64
+        Events.removeEventListener(panelNode, "mouseover", this.setSelection, false);
  65
+        Events.removeEventListener(panelNode, "mouseout", this.clearSelection, false);
  66
+        Events.removeEventListener(panelNode, "mousedown", this.lockSelection, false);
  67
+
  68
+        Firebug.Panel.destroyNode.apply(this, arguments);
  69
+    },
  70
+
  71
+    show: function(state)
  72
+    {
  73
+        Firebug.Panel.show.apply(this, arguments);
  74
+
  75
+        this.refresh();
  76
+    },
  77
+
  78
+    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
  79
+
  80
+    getCSSStyleRule: function(event)
  81
+    {
  82
+        var object = Firebug.getRepObject(event.target);
  83
+
  84
+        if (object && (object instanceof window.CSSStyleRule))
  85
+            return object;
  86
+    },
  87
+
  88
+    getCSSRuleElement: function(element)
  89
+    {
  90
+        while (element && !element.classList.contains("cssRule"))
  91
+            element = element.parentNode;
  92
+
  93
+        return element;
  94
+    },
  95
+
  96
+    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
  97
+    // Selection
  98
+
  99
+    setSelection: function(event)
  100
+    {
  101
+        var rule = this.getCSSStyleRule(event);
  102
+
  103
+        if (rule)
  104
+        {
  105
+            // then we have entered a rule element
  106
+            var ruleElement = this.getCSSRuleElement(event.target);
  107
+            if (ruleElement && ruleElement !== this.lockedElement)
  108
+                ruleElement.classList.add("selectedSelectorRule");
  109
+
  110
+            this.selection = rule;
  111
+            this.rebuild();
  112
+        }
  113
+    },
  114
+
  115
+    clearSelection: function(event)
  116
+    {
  117
+        if (this.selection !== this.lockedSelection)
  118
+        {
  119
+            this.selection = this.lockedSelection;
  120
+            this.rebuild();
  121
+        }
  122
+
  123
+        var rule = this.getCSSStyleRule(event);
  124
+        if (rule)
  125
+        {
  126
+            // then we are leaving a rule element that we may have highlighted.
  127
+            var ruleElement = this.getCSSRuleElement(event.target);
  128
+            if (ruleElement)
  129
+                ruleElement.classList.remove("selectedSelectorRule");
  130
+        }
  131
+    },
  132
+
  133
+    lockSelection: function(event)
  134
+    {
  135
+        var rule = this.getCSSStyleRule(event);
  136
+        if (rule)
  137
+        {
  138
+            if (this.lockedElement)
  139
+                this.lockedElement.classList.remove("lockedSelectorRule");
  140
+
  141
+            this.lockedElement = this.getCSSRuleElement(event.target);
  142
+
  143
+            if (this.lockedElement)
  144
+            {
  145
+                this.lockedElement.classList.add("lockedSelectorRule");
  146
+                this.lockedElement.classList.remove("selectedSelectorRule");
  147
+            }
  148
+
  149
+            this.lockedSelection = rule;
  150
+        }
  151
+    },
  152
+
  153
+    hide: function()
  154
+    {
  155
+        Firebug.Panel.hide.apply(this, arguments);
  156
+    },
  157
+
  158
+    refresh: function()
  159
+    {
  160
+        var root = this.context.window.document.documentElement;
  161
+        this.selection = this.mainPanel.selection;
  162
+        this.rebuild(true);
  163
+    },
  164
+
  165
+    /**
  166
+     * returns an array of Elements matched from selector
  167
+     */
  168
+    getSelectedElements: function(selectorText)
  169
+    {
  170
+        var selections = Firebug.currentContext.window.document.querySelectorAll(selectorText);
  171
+
  172
+        // For some reason the return value of querySelectorAll()
  173
+        // is not recognized as a NodeList anymore since Firefox 10.0.
  174
+        // See issue 5442.
  175
+        if (selections)
  176
+        {
  177
+            var elements = [];
  178
+            for (var i=0; i<selections.length; i++)
  179
+                elements.push(selections[i]);
  180
+
  181
+            return elements;
  182
+        }
  183
+        else
  184
+        {
  185
+            throw new Error("Selection Failed: " + selections);
  186
+        }
  187
+    },
  188
+
  189
+    /**
  190
+     * Build content of the panel. The basic layout of the panel is generated by
  191
+     * {@link SelectorTemplate} template.
  192
+     */
  193
+    rebuild: function()
  194
+    {
  195
+        if (this.selection)
  196
+        {
  197
+            try
  198
+            {
  199
+                var selectorText;
  200
+
  201
+                if (this.selection instanceof window.CSSStyleRule)
  202
+                    selectorText = this.selection.selectorText;
  203
+                else
  204
+                    selectorText = this.selection;
  205
+
  206
+                var elements = this.getSelectedElements(selectorText);
  207
+                if (elements && elements.length != 0)
  208
+                {
  209
+                    SelectorTemplate.tag.replace({object: elements}, this.panelNode);
  210
+                    this.showTrialSelector(this.trialSelector);
  211
+                    return;
  212
+                }
  213
+            }
  214
+            catch (e)
  215
+            {
  216
+                var table = SelectorTemplate.tag.replace({object: []}, this.panelNode);
  217
+                var tbody = table.lastChild;
  218
+
  219
+                WarningTemplate.selectErrorTag.insertRows({object: e}, tbody.lastChild);
  220
+                WarningTemplate.selectErrorTextTag.insertRows({object: e}, tbody.lastChild);
  221
+
  222
+                this.showTrialSelector(this.trialSelector);
  223
+                return;
  224
+            }
  225
+        }
  226
+
  227
+        var table = SelectorTemplate.tag.replace({object: []}, this.panelNode);
  228
+        var tbody = table.lastChild;
  229
+
  230
+        if (this.trialSelector)
  231
+        {
  232
+            WarningTemplate.noSelectionResultsTag.insertRows(
  233
+                {object: this.selection}, tbody.lastChild)
  234
+        }
  235
+        else
  236
+        {
  237
+            WarningTemplate.noSelectionTag.insertRows(
  238
+                {object: this.selection}, tbody.lastChild);
  239
+        }
  240
+
  241
+        this.showTrialSelector(this.trialSelector);
  242
+    },
  243
+
  244
+    getObjectPath: function(object)
  245
+    {
  246
+        if (FBTrace.DBG_SELECTOR)
  247
+            FBTrace.sysout("css.selector.getObjectPath NOOP", object);
  248
+    },
  249
+
  250
+    supportsObject: function(object)
  251
+    {
  252
+        return 0;
  253
+    },
  254
+
  255
+    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
  256
+
  257
+    tryASelector:function(element)
  258
+    {
  259
+        if (!this.trialSelector)
  260
+            this.trialSelector = this.selection ? this.selection.selectorText : "";
  261
+
  262
+        this.editProperty(element, this.trialSelector);
  263
+    },
  264
+
  265
+    editProperty: function(row, editValue)
  266
+    {
  267
+        Firebug.Editor.startEditing(row, editValue);
  268
+    },
  269
+
  270
+    getEditor: function(target, value)
  271
+    {
  272
+        if (!this.editor)
  273
+            this.editor = new SelectorEditor(this);
  274
+
  275
+        return this.editor;
  276
+    },
  277
+
  278
+    setTrialSelector: function(target, value)
  279
+    {
  280
+        if (this.lockedElement)
  281
+            this.lockedElement.classList.remove("lockedSelectorRule");
  282
+
  283
+        this.trialSelector = value;
  284
+        this.selection = this.trialSelector;
  285
+        this.lockedElement = target;
  286
+        this.lockedSelection = this.selection;
  287
+        this.rebuild();
  288
+    },
  289
+
  290
+    showTrialSelector: function(trialSelector)
  291
+    {
  292
+        var show = trialSelector ? true : false;
  293
+        Dom.collapse(this.document.getElementById("trialHint"), show);
  294
+
  295
+        var trialSelectorDiv = this.document.getElementById("trialSelector");
  296
+        trialSelectorDiv.textContent = trialSelector;
  297
+        Dom.collapse(trialSelectorDiv, !show);
  298
+    },
  299
+});
  300
+
  301
+// ********************************************************************************************* //
  302
+
  303
+var BaseRep = domplate(Firebug.Rep,
  304
+{
  305
+    // xxxHonza: shouldn't this be in Firebug.Rep?
  306
+    getNaturalTag: function(value)
  307
+    {
  308
+        var rep = Firebug.getRep(value);
  309
+        var tag = rep.shortTag ? rep.shortTag : rep.tag;
  310
+        return tag;
  311
+    }
  312
+});
  313
+
  314
+// ********************************************************************************************* //
  315
+
  316
+var TrialRow =
  317
+    TR({"class": "watchNewRow", level: 0, onclick: "$onClickEditor"},
  318
+        TD({"class": "watchEditCell", colspan: 3},
  319
+            DIV({"class": "watchEditBox a11yFocusNoTab", "id": "trialHint",
  320
+                role: "button", "tabindex" : "0",
  321
+                "aria-label": Locale.$STR("a11y.labels.press enter to add new selector")},
  322
+                Locale.$STR("css.selector.TryASelector")
  323
+            ),
  324
+            DIV({"class": "trialSelector", "id": "trialSelector"}, "")
  325
+        )
  326
+    );
  327
+
  328
+// ********************************************************************************************* //
  329
+
  330
+/**
  331
+ * @domplate: Template for basic layout of the {@link SelectorPanel} panel.
  332
+ */
  333
+var SelectorTemplate = domplate(BaseRep,
  334
+{
  335
+    // object will be array of elements CSSStyleRule
  336
+    tag:
  337
+        TABLE({"class": "cssSelectionTable", cellpadding: 0, cellspacing: 0},
  338
+            TBODY({"class": "cssSelectionTBody"},
  339
+                TrialRow,
  340
+                FOR("element", "$object",
  341
+                    TR({"class": "selectionElementRow", _repObject: "$element"},
  342
+                        TD({"class": "selectionElement"},
  343
+                            TAG( "$element|getNaturalTag", {object: "$element"})
  344
+                        )
  345
+                    )
  346
+                )
  347
+            )
  348
+        ),
  349
+
  350
+    onClickEditor: function(event)
  351
+    {
  352
+        var tr = event.currentTarget;
  353
+        var panel = Firebug.getElementPanel(tr);
  354
+        panel.tryASelector(tr);
  355
+    },
  356
+});
  357
+
  358
+// ********************************************************************************************* //
  359
+
  360
+var WarningTemplate = domplate(Firebug.Rep,
  361
+{
  362
+    noSelectionTag:
  363
+        TR({"class": "selectbugWarning "},
  364
+            TD({"class": "selectionElement"}, Locale.$STR("css.selector.noSelection"))
  365
+        ),
  366
+
  367
+    noSelectionResultsTag:
  368
+        TR({"class": "selectbugWarning "},
  369
+            TD({"class": "selectionElement"}, Locale.$STR("css.selector.noSelectionResults"))
  370
+        ),
  371
+
  372
+    selectErrorTag:
  373
+        TR({"class": "selectbugWarning"},
  374
+            TD({"class": "selectionElement"}, Locale.$STR("css.selector.selectorError"))
  375
+        ),
  376
+
  377
+    selectErrorTextTag:
  378
+        TR({"class": "selectbugWarning"},
  379
+            TD({"class": "selectionErrorText selectionElement"}, SPAN("$object"))
  380
+        ),
  381
+});
  382
+
  383
+// ********************************************************************************************* //
  384
+// Registration
  385
+
  386
+Firebug.registerStylesheet("chrome://firebug/skin/selector.css");
  387
+Firebug.registerPanel(SelectorPanel);
  388
+
  389
+return SelectorPanel;
  390
+
  391
+// ********************************************************************************************* //
  392
+}});
1  extension/content/firebug/moduleConfig.js
@@ -59,6 +59,7 @@ Firebug.getModuleLoaderConfig = function(baseConfig)
59 59
         "firebug/css/computedPanel",
60 60
         "firebug/cookies/cookieModule",
61 61
         "firebug/cookies/cookiePanel",
  62
+        "firebug/css/selectorPanel",
62 63
     ];
63 64
 
64 65
     return config;
1  extension/defaults/preferences/tracingConsole.js
@@ -68,3 +68,4 @@ pref("extensions.firebug.DBG_OBSERVERS", false);        // track/untrack support
68 68
 pref("extensions.firebug.DBG_EVENTLISTENERS", false);   // track/untrack for registered event listeners, restart needed
69 69
 pref("extensions.firebug.DBG_COMMANDEDITOR", false);    // Multiline console based on SourceEditor (Orion)
70 70
 pref("extensions.firebug.DBG_MENU", false);             // Menus and context menus in Firebug
  71
+pref("extensions.firebug.DBG_SELECTOR", false);         // Selector side panel (in the CSS panel)
7  extension/locale/en-US/firebug.properties
@@ -1665,3 +1665,10 @@ console.cmd.help.help=Displays help for all available commands.
1665 1665
 # if registered command doesn't have help URL associated and the user clicks on the command name.
1666 1666
 # List of command names is displayed if you type "help" (without quotes) into the command line.
1667 1667
 console.cmd.helpUrlNotAvailable=Help page for this command is not available.
  1668
+
  1669
+css.selector.noSelection=Mouseover CSS Rules to preview, Click to select one
  1670
+css.selector.noSelectionResults=No matching elements
  1671
+css.selector.selectorError=Selection failed:
  1672
+css.selector.Selection=Selection
  1673
+css.selector.TryASelector=Try a selector...
  1674
+
41  extension/skin/classic/selector.css
... ...
@@ -0,0 +1,41 @@
  1
+/* Rules for selectbug's Selector panel elements */
  2
+.cssSelectionTable {
  3
+    width: 100%;
  4
+}
  5
+.selectionElement {
  6
+    padding: 0 0 0 1ex;
  7
+}
  8
+
  9
+.selectbugWarning {
  10
+    font-size: 11px;
  11
+    color: gray;
  12
+    padding: 2px 0 0 1ex;
  13
+}
  14
+
  15
+.selectionErrorText  {
  16
+    color: black;
  17
+    padding: 1em;
  18
+    background-color: #FFEBEB;
  19
+}
  20
+
  21
+/* Rules for selectbug's effect on CSS stylesheet panel */
  22
+.selectedSelectorRule {
  23
+    border-color: Highlight !important;
  24
+    background-color: #EBF5FF !important;
  25
+    color: #000000 !important;
  26
+    cursor: pointer;
  27
+}
  28
+
  29
+.cssSelector,
  30
+.cssPropName,
  31
+.cssPropValue {
  32
+    cursor: text;
  33
+}
  34
+
  35
+.lockedSelectorRule {
  36
+    background-color: Highlight;
  37
+}
  38
+
  39
+.lockedSelectorRule * {
  40
+    color: HighlightText !important;
  41
+}

0 notes on commit 34c8d8a

Please sign in to comment.
Something went wrong with that request. Please try again.