Home | History | Annotate | Download | only in front_end
      1 /*
      2  * Copyright (C) 2009 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 /**
     32  * @constructor
     33  * @extends {WebInspector.Object}
     34  * @param {!Element} element
     35  */
     36 WebInspector.StatusBarItem = function(element)
     37 {
     38     this.element = element;
     39     this._enabled = true;
     40 }
     41 
     42 WebInspector.StatusBarItem.prototype = {
     43     /**
     44      * @param {boolean} value
     45      */
     46     setEnabled: function(value)
     47     {
     48         if (this._enabled === value)
     49             return;
     50         this._enabled = value;
     51         this._applyEnabledState();
     52     },
     53 
     54     /**
     55      * @protected
     56      */
     57     _applyEnabledState: function()
     58     {
     59         this.element.disabled = !this._enabled;
     60     },
     61 
     62     __proto__: WebInspector.Object.prototype
     63 }
     64 
     65 /**
     66  * @constructor
     67  * @extends {WebInspector.StatusBarItem}
     68  * @param {string} text
     69  * @param {string=} className
     70  */
     71 WebInspector.StatusBarText = function(text, className)
     72 {
     73     WebInspector.StatusBarItem.call(this, document.createElement("span"));
     74     this.element.className = "status-bar-item status-bar-text";
     75     if (className)
     76         this.element.classList.add(className);
     77     this.element.textContent = text;
     78 }
     79 
     80 WebInspector.StatusBarText.prototype = {
     81     /**
     82      * @param {string} text
     83      */
     84     setText: function(text)
     85     {
     86         this.element.textContent = text;
     87     },
     88 
     89     __proto__: WebInspector.StatusBarItem.prototype
     90 }
     91 
     92 
     93 /**
     94  * @constructor
     95  * @extends {WebInspector.StatusBarItem}
     96  * @param {string} title
     97  * @param {string} className
     98  * @param {number=} states
     99  */
    100 WebInspector.StatusBarButton = function(title, className, states)
    101 {
    102     WebInspector.StatusBarItem.call(this, document.createElement("button"));
    103     this.element.className = className + " status-bar-item";
    104     this.element.addEventListener("click", this._clicked.bind(this), false);
    105 
    106     this.glyph = document.createElement("div");
    107     this.glyph.className = "glyph";
    108     this.element.appendChild(this.glyph);
    109 
    110     this.glyphShadow = document.createElement("div");
    111     this.glyphShadow.className = "glyph shadow";
    112     this.element.appendChild(this.glyphShadow);
    113 
    114     this.states = states;
    115     if (!states)
    116         this.states = 2;
    117 
    118     if (states == 2)
    119         this._state = false;
    120     else
    121         this._state = 0;
    122 
    123     this.title = title;
    124     this.className = className;
    125     this._visible = true;
    126 }
    127 
    128 WebInspector.StatusBarButton.prototype = {
    129     _clicked: function()
    130     {
    131         this.dispatchEventToListeners("click");
    132         if (this._longClickInterval) {
    133             clearInterval(this._longClickInterval);
    134             delete this._longClickInterval;
    135         }
    136     },
    137 
    138     /**
    139      * @override
    140      */
    141     _applyEnabledState: function()
    142     {
    143         this.element.disabled = !this._enabled;
    144         if (this._longClickInterval) {
    145             clearInterval(this._longClickInterval);
    146             delete this._longClickInterval;
    147         }
    148     },
    149 
    150     /**
    151      * @return {boolean}
    152      */
    153     enabled: function()
    154     {
    155         return this._enabled;
    156     },
    157 
    158     get title()
    159     {
    160         return this._title;
    161     },
    162 
    163     set title(x)
    164     {
    165         if (this._title === x)
    166             return;
    167         this._title = x;
    168         this.element.title = x;
    169     },
    170 
    171     get state()
    172     {
    173         return this._state;
    174     },
    175 
    176     set state(x)
    177     {
    178         if (this._state === x)
    179             return;
    180 
    181         if (this.states === 2)
    182             this.element.enableStyleClass("toggled-on", x);
    183         else {
    184             this.element.classList.remove("toggled-" + this._state);
    185             if (x !== 0)
    186                 this.element.classList.add("toggled-" + x);
    187         }
    188         this._state = x;
    189     },
    190 
    191     get toggled()
    192     {
    193         if (this.states !== 2)
    194             throw("Only used toggled when there are 2 states, otherwise, use state");
    195         return this.state;
    196     },
    197 
    198     set toggled(x)
    199     {
    200         if (this.states !== 2)
    201             throw("Only used toggled when there are 2 states, otherwise, use state");
    202         this.state = x;
    203     },
    204 
    205     get visible()
    206     {
    207         return this._visible;
    208     },
    209 
    210     set visible(x)
    211     {
    212         if (this._visible === x)
    213             return;
    214 
    215         this.element.enableStyleClass("hidden", !x);
    216         this._visible = x;
    217     },
    218 
    219     makeLongClickEnabled: function()
    220     {
    221         var boundMouseDown = mouseDown.bind(this);
    222         var boundMouseUp = mouseUp.bind(this);
    223 
    224         this.element.addEventListener("mousedown", boundMouseDown, false);
    225         this.element.addEventListener("mouseout", boundMouseUp, false);
    226         this.element.addEventListener("mouseup", boundMouseUp, false);
    227 
    228         var longClicks = 0;
    229 
    230         this._longClickData = { mouseUp: boundMouseUp, mouseDown: boundMouseDown };
    231 
    232         /**
    233          * @param {?Event} e
    234          * @this {WebInspector.StatusBarButton}
    235          */
    236         function mouseDown(e)
    237         {
    238             if (e.which !== 1)
    239                 return;
    240             longClicks = 0;
    241             this._longClickInterval = setInterval(longClicked.bind(this), 200);
    242         }
    243 
    244         /**
    245          * @param {?Event} e
    246          * @this {WebInspector.StatusBarButton}
    247          */
    248         function mouseUp(e)
    249         {
    250             if (e.which !== 1)
    251                 return;
    252             if (this._longClickInterval) {
    253                 clearInterval(this._longClickInterval);
    254                 delete this._longClickInterval;
    255             }
    256         }
    257 
    258         /**
    259          * @this {WebInspector.StatusBarButton}
    260          */
    261         function longClicked()
    262         {
    263             ++longClicks;
    264             this.dispatchEventToListeners(longClicks === 1 ? "longClickDown" : "longClickPress");
    265         }
    266     },
    267 
    268     unmakeLongClickEnabled: function()
    269     {
    270         if (!this._longClickData)
    271             return;
    272         this.element.removeEventListener("mousedown", this._longClickData.mouseDown, false);
    273         this.element.removeEventListener("mouseout", this._longClickData.mouseUp, false);
    274         this.element.removeEventListener("mouseup", this._longClickData.mouseUp, false);
    275         delete this._longClickData;
    276     },
    277 
    278     /**
    279      * @param {?function():!Array.<!WebInspector.StatusBarButton>} buttonsProvider
    280      */
    281     setLongClickOptionsEnabled: function(buttonsProvider)
    282     {
    283         if (buttonsProvider) {
    284             if (!this._longClickOptionsData) {
    285                 this.makeLongClickEnabled();
    286 
    287                 this.longClickGlyph = document.createElement("div");
    288                 this.longClickGlyph.className = "fill long-click-glyph";
    289                 this.element.appendChild(this.longClickGlyph);
    290 
    291                 this.longClickGlyphShadow = document.createElement("div");
    292                 this.longClickGlyphShadow.className = "fill long-click-glyph shadow";
    293                 this.element.appendChild(this.longClickGlyphShadow);
    294 
    295                 var longClickDownListener = this._showOptions.bind(this);
    296                 this.addEventListener("longClickDown", longClickDownListener, this);
    297 
    298                 this._longClickOptionsData = {
    299                     glyphElement: this.longClickGlyph,
    300                     glyphShadowElement: this.longClickGlyphShadow,
    301                     longClickDownListener: longClickDownListener
    302                 };
    303             }
    304             this._longClickOptionsData.buttonsProvider = buttonsProvider;
    305         } else {
    306             if (!this._longClickOptionsData)
    307                 return;
    308             this.element.removeChild(this._longClickOptionsData.glyphElement);
    309             this.element.removeChild(this._longClickOptionsData.glyphShadowElement);
    310 
    311             this.removeEventListener("longClickDown", this._longClickOptionsData.longClickDownListener, this);
    312             delete this._longClickOptionsData;
    313 
    314             this.unmakeLongClickEnabled();
    315         }
    316     },
    317 
    318     _showOptions: function()
    319     {
    320         var buttons = this._longClickOptionsData.buttonsProvider();
    321         var mainButtonClone = new WebInspector.StatusBarButton(this.title, this.className, this.states);
    322         mainButtonClone.addEventListener("click", this._clicked, this);
    323         mainButtonClone.state = this.state;
    324         buttons.push(mainButtonClone);
    325 
    326         var mouseUpListener = mouseUp.bind(this);
    327         document.documentElement.addEventListener("mouseup", mouseUpListener, false);
    328 
    329         var optionsGlassPane = new WebInspector.GlassPane();
    330         var optionsBarElement = optionsGlassPane.element.createChild("div", "alternate-status-bar-buttons-bar");
    331         const buttonHeight = 23;
    332 
    333         var hostButtonPosition = this.element.totalOffset();
    334 
    335         var topNotBottom = hostButtonPosition.top + buttonHeight * buttons.length < document.documentElement.offsetHeight;
    336 
    337         if (topNotBottom)
    338             buttons = buttons.reverse();
    339 
    340         optionsBarElement.style.height = (buttonHeight * buttons.length) + "px";
    341         if (topNotBottom)
    342             optionsBarElement.style.top = (hostButtonPosition.top + 1) + "px";
    343         else
    344             optionsBarElement.style.top = (hostButtonPosition.top - (buttonHeight * (buttons.length - 1))) + "px";
    345         optionsBarElement.style.left = (hostButtonPosition.left + 1) + "px";
    346 
    347         var boundMouseOver = mouseOver.bind(this);
    348         var boundMouseOut = mouseOut.bind(this);
    349         for (var i = 0; i < buttons.length; ++i) {
    350             buttons[i].element.addEventListener("mousemove", boundMouseOver, false);
    351             buttons[i].element.addEventListener("mouseout", boundMouseOut, false);
    352             optionsBarElement.appendChild(buttons[i].element);
    353         }
    354         var hostButtonIndex = topNotBottom ? 0 : buttons.length - 1;
    355         buttons[hostButtonIndex].element.classList.add("emulate-active");
    356 
    357         function mouseOver(e)
    358         {
    359             if (e.which !== 1)
    360                 return;
    361             var buttonElement = e.target.enclosingNodeOrSelfWithClass("status-bar-item");
    362             buttonElement.classList.add("emulate-active");
    363         }
    364 
    365         function mouseOut(e)
    366         {
    367             if (e.which !== 1)
    368                 return;
    369             var buttonElement = e.target.enclosingNodeOrSelfWithClass("status-bar-item");
    370             buttonElement.classList.remove("emulate-active");
    371         }
    372 
    373         function mouseUp(e)
    374         {
    375             if (e.which !== 1)
    376                 return;
    377             optionsGlassPane.dispose();
    378             document.documentElement.removeEventListener("mouseup", mouseUpListener, false);
    379 
    380             for (var i = 0; i < buttons.length; ++i) {
    381                 if (buttons[i].element.classList.contains("emulate-active")) {
    382                     buttons[i].element.classList.remove("emulate-active");
    383                     buttons[i]._clicked();
    384                     break;
    385                 }
    386             }
    387         }
    388     },
    389 
    390     __proto__: WebInspector.StatusBarItem.prototype
    391 }
    392 
    393 /**
    394  * @constructor
    395  * @extends {WebInspector.StatusBarItem}
    396  * @param {?function(!Event)} changeHandler
    397  * @param {string=} className
    398  */
    399 WebInspector.StatusBarComboBox = function(changeHandler, className)
    400 {
    401     WebInspector.StatusBarItem.call(this, document.createElement("span"));
    402     this.element.className = "status-bar-select-container";
    403 
    404     this._selectElement = this.element.createChild("select", "status-bar-item");
    405     this.element.createChild("div", "status-bar-select-arrow");
    406     if (changeHandler)
    407         this._selectElement.addEventListener("change", changeHandler, false);
    408     if (className)
    409         this._selectElement.classList.add(className);
    410 }
    411 
    412 WebInspector.StatusBarComboBox.prototype = {
    413     /**
    414      * @return {!Element}
    415      */
    416     selectElement: function()
    417     {
    418         return this._selectElement;
    419     },
    420 
    421     /**
    422      * @return {number}
    423      */
    424     size: function()
    425     {
    426         return this._selectElement.childElementCount;
    427     },
    428 
    429     /**
    430      * @param {!Element} option
    431      */
    432     addOption: function(option)
    433     {
    434         this._selectElement.appendChild(option);
    435     },
    436 
    437     /**
    438      * @param {string} label
    439      * @param {string=} title
    440      * @param {string=} value
    441      * @return {!Element}
    442      */
    443     createOption: function(label, title, value)
    444     {
    445         var option = this._selectElement.createChild("option");
    446         option.text = label;
    447         if (title)
    448             option.title = title;
    449         if (typeof value !== "undefined")
    450             option.value = value;
    451         return option;
    452     },
    453 
    454     /**
    455      * @override
    456      */
    457     _applyEnabledState: function()
    458     {
    459         this._selectElement.disabled = !this._enabled;
    460     },
    461 
    462     /**
    463      * @param {!Element} option
    464      */
    465     removeOption: function(option)
    466     {
    467         this._selectElement.removeChild(option);
    468     },
    469 
    470     removeOptions: function()
    471     {
    472         this._selectElement.removeChildren();
    473     },
    474 
    475     /**
    476      * @return {?Element}
    477      */
    478     selectedOption: function()
    479     {
    480         if (this._selectElement.selectedIndex >= 0)
    481             return this._selectElement[this._selectElement.selectedIndex];
    482         return null;
    483     },
    484 
    485     /**
    486      * @param {!Element} option
    487      */
    488     select: function(option)
    489     {
    490         this._selectElement.selectedIndex = Array.prototype.indexOf.call(this._selectElement, option);
    491     },
    492 
    493     /**
    494      * @param {number} index
    495      */
    496     setSelectedIndex: function(index)
    497     {
    498         this._selectElement.selectedIndex = index;
    499     },
    500 
    501     /**
    502      * @return {number}
    503      */
    504     selectedIndex: function()
    505     {
    506         return this._selectElement.selectedIndex;
    507     },
    508 
    509     __proto__: WebInspector.StatusBarItem.prototype
    510 }
    511 
    512 /**
    513  * @constructor
    514  * @extends {WebInspector.StatusBarItem}
    515  * @param {string} title
    516  */
    517 WebInspector.StatusBarCheckbox = function(title)
    518 {
    519     WebInspector.StatusBarItem.call(this, document.createElement("label"));
    520     this.element.classList.add("status-bar-item", "checkbox");
    521     this._checkbox = this.element.createChild("input");
    522     this._checkbox.type = "checkbox";
    523     this.element.createTextChild(title);
    524 }
    525 
    526 WebInspector.StatusBarCheckbox.prototype = {
    527     /**
    528      * @return {boolean}
    529      */
    530     checked: function()
    531     {
    532         return this._checkbox.checked;
    533     },
    534 
    535     __proto__: WebInspector.StatusBarItem.prototype
    536 }
    537