Home | History | Annotate | Download | only in front-end
      1 /*
      2  * Copyright (C) 2007 Apple 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
      6  * are met:
      7  *
      8  * 1.  Redistributions of source code must retain the above copyright
      9  *     notice, this list of conditions and the following disclaimer.
     10  * 2.  Redistributions in binary form must reproduce the above copyright
     11  *     notice, this list of conditions and the following disclaimer in the
     12  *     documentation and/or other materials provided with the distribution.
     13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
     14  *     its contributors may be used to endorse or promote products derived
     15  *     from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 WebInspector.MetricsSidebarPane = function()
     30 {
     31     WebInspector.SidebarPane.call(this, WebInspector.UIString("Metrics"));
     32     this._inlineStyleId = null;
     33 }
     34 
     35 WebInspector.MetricsSidebarPane.prototype = {
     36     update: function(node)
     37     {
     38         if (node)
     39             this.node = node;
     40         else
     41             node = this.node;
     42 
     43         if (!node || node.nodeType() !== Node.ELEMENT_NODE) {
     44             this.bodyElement.removeChildren();
     45             return;
     46         }
     47 
     48         var self = this;
     49         var callback = function(style) {
     50             if (!style)
     51                 return;
     52             self._update(style);
     53         };
     54         WebInspector.cssModel.getComputedStyleAsync(node.id, callback);
     55 
     56         var inlineStyleCallback = function(style) {
     57             if (!style)
     58                 return;
     59             self.inlineStyle = style;
     60         };
     61         WebInspector.cssModel.getInlineStyleAsync(node.id, inlineStyleCallback);
     62     },
     63 
     64     _getPropertyValueAsPx: function(style, propertyName)
     65     {
     66         return Number(style.getPropertyValue(propertyName).replace(/px$/, "") || 0);
     67     },
     68 
     69     _getBox: function(computedStyle, componentName)
     70     {
     71         var suffix = componentName === "border" ? "-width" : "";
     72         var left = this._getPropertyValueAsPx(computedStyle, componentName + "-left" + suffix);
     73         var top = this._getPropertyValueAsPx(computedStyle, componentName + "-top" + suffix);
     74         var right = this._getPropertyValueAsPx(computedStyle, componentName + "-right" + suffix);
     75         var bottom = this._getPropertyValueAsPx(computedStyle, componentName + "-bottom" + suffix);
     76         return { left: left, top: top, right: right, bottom: bottom };
     77     },
     78 
     79     _update: function(style)
     80     {
     81         // Updating with computed style.
     82         var metricsElement = document.createElement("div");
     83         metricsElement.className = "metrics";
     84         var self = this;
     85 
     86         function createBoxPartElement(style, name, side, suffix)
     87         {
     88             var propertyName = (name !== "position" ? name + "-" : "") + side + suffix;
     89             var value = style.getPropertyValue(propertyName);
     90             if (value === "" || (name !== "position" && value === "0px"))
     91                 value = "\u2012";
     92             else if (name === "position" && value === "auto")
     93                 value = "\u2012";
     94             value = value.replace(/px$/, "");
     95 
     96             var element = document.createElement("div");
     97             element.className = side;
     98             element.textContent = value;
     99             element.addEventListener("dblclick", this.startEditing.bind(this, element, name, propertyName), false);
    100             return element;
    101         }
    102 
    103         function getContentAreaWidthPx(style)
    104         {
    105             var width = style.getPropertyValue("width").replace(/px$/, "");
    106             if (style.getPropertyValue("box-sizing") === "border-box") {
    107                 var borderBox = self._getBox(style, "border");
    108                 var paddingBox = self._getBox(style, "padding");
    109 
    110                 width = width - borderBox.left - borderBox.right - paddingBox.left - paddingBox.right;
    111             }
    112 
    113             return width;
    114         }
    115 
    116         function getContentAreaHeightPx(style)
    117         {
    118             var height = style.getPropertyValue("height").replace(/px$/, "");
    119             if (style.getPropertyValue("box-sizing") === "border-box") {
    120                 var borderBox = self._getBox(style, "border");
    121                 var paddingBox = self._getBox(style, "padding");
    122 
    123                 height = height - borderBox.top - borderBox.bottom - paddingBox.top - paddingBox.bottom;
    124             }
    125 
    126             return height;
    127         }
    128 
    129         // Display types for which margin is ignored.
    130         var noMarginDisplayType = {
    131             "table-cell": true,
    132             "table-column": true,
    133             "table-column-group": true,
    134             "table-footer-group": true,
    135             "table-header-group": true,
    136             "table-row": true,
    137             "table-row-group": true
    138         };
    139 
    140         // Display types for which padding is ignored.
    141         var noPaddingDisplayType = {
    142             "table-column": true,
    143             "table-column-group": true,
    144             "table-footer-group": true,
    145             "table-header-group": true,
    146             "table-row": true,
    147             "table-row-group": true
    148         };
    149 
    150         // Position types for which top, left, bottom and right are ignored.
    151         var noPositionType = {
    152             "static": true
    153         };
    154 
    155         var boxes = ["content", "padding", "border", "margin", "position"];
    156         var boxLabels = [WebInspector.UIString("content"), WebInspector.UIString("padding"), WebInspector.UIString("border"), WebInspector.UIString("margin"), WebInspector.UIString("position")];
    157         var previousBox;
    158         for (var i = 0; i < boxes.length; ++i) {
    159             var name = boxes[i];
    160 
    161             if (name === "margin" && noMarginDisplayType[style.getPropertyValue("display")])
    162                 continue;
    163             if (name === "padding" && noPaddingDisplayType[style.getPropertyValue("display")])
    164                 continue;
    165             if (name === "position" && noPositionType[style.getPropertyValue("position")])
    166                 continue;
    167 
    168             var boxElement = document.createElement("div");
    169             boxElement.className = name;
    170 
    171             if (name === "content") {
    172                 var widthElement = document.createElement("span");
    173                 widthElement.textContent = getContentAreaWidthPx(style);
    174                 widthElement.addEventListener("dblclick", this.startEditing.bind(this, widthElement, "width", "width", style), false);
    175 
    176                 var heightElement = document.createElement("span");
    177                 heightElement.textContent = getContentAreaHeightPx(style);
    178                 heightElement.addEventListener("dblclick", this.startEditing.bind(this, heightElement, "height", "height", style), false);
    179 
    180                 boxElement.appendChild(widthElement);
    181                 boxElement.appendChild(document.createTextNode(" \u00D7 "));
    182                 boxElement.appendChild(heightElement);
    183             } else {
    184                 var suffix = (name === "border" ? "-width" : "");
    185 
    186                 var labelElement = document.createElement("div");
    187                 labelElement.className = "label";
    188                 labelElement.textContent = boxLabels[i];
    189                 boxElement.appendChild(labelElement);
    190 
    191                 boxElement.appendChild(createBoxPartElement.call(this, style, name, "top", suffix));
    192                 boxElement.appendChild(document.createElement("br"));
    193                 boxElement.appendChild(createBoxPartElement.call(this, style, name, "left", suffix));
    194 
    195                 if (previousBox)
    196                     boxElement.appendChild(previousBox);
    197 
    198                 boxElement.appendChild(createBoxPartElement.call(this, style, name, "right", suffix));
    199                 boxElement.appendChild(document.createElement("br"));
    200                 boxElement.appendChild(createBoxPartElement.call(this, style, name, "bottom", suffix));
    201             }
    202 
    203             previousBox = boxElement;
    204         }
    205 
    206         metricsElement.appendChild(previousBox);
    207         this.bodyElement.removeChildren();
    208         this.bodyElement.appendChild(metricsElement);
    209     },
    210 
    211     startEditing: function(targetElement, box, styleProperty, computedStyle)
    212     {
    213         if (WebInspector.isBeingEdited(targetElement))
    214             return;
    215 
    216         var context = { box: box, styleProperty: styleProperty, computedStyle: computedStyle };
    217 
    218         WebInspector.startEditing(targetElement, {
    219             context: context,
    220             commitHandler: this.editingCommitted.bind(this),
    221             cancelHandler: this.editingCancelled.bind(this)
    222         });
    223     },
    224 
    225     editingCancelled: function(element, context)
    226     {
    227         this.update();
    228     },
    229 
    230     editingCommitted: function(element, userInput, previousContent, context)
    231     {
    232         if (!this.inlineStyle) {
    233             // Element has no renderer.
    234             return this.editingCancelled(element, context); // nothing changed, so cancel
    235         }
    236 
    237         if (userInput === previousContent)
    238             return this.editingCancelled(element, context); // nothing changed, so cancel
    239 
    240         if (context.box !== "position" && (!userInput || userInput === "\u2012"))
    241             userInput = "0px";
    242         else if (context.box === "position" && (!userInput || userInput === "\u2012"))
    243             userInput = "auto";
    244 
    245         userInput = userInput.toLowerCase();
    246         // Append a "px" unit if the user input was just a number.
    247         if (/^\d+$/.test(userInput))
    248             userInput += "px";
    249 
    250         var styleProperty = context.styleProperty;
    251         var computedStyle = context.computedStyle;
    252 
    253         if (computedStyle.getPropertyValue("box-sizing") === "border-box" && (styleProperty === "width" || styleProperty === "height")) {
    254             if (!userInput.match(/px$/)) {
    255                 WebInspector.log("For elements with box-sizing: border-box, only absolute content area dimensions can be applied", WebInspector.ConsoleMessage.MessageLevel.Error);
    256                 WebInspector.showConsole();
    257                 return;
    258             }
    259 
    260             var borderBox = this._getBox(computedStyle, "border");
    261             var paddingBox = this._getBox(computedStyle, "padding");
    262             var userValuePx = Number(userInput.replace(/px$/, ""));
    263             if (isNaN(userValuePx))
    264                 return;
    265             if (styleProperty === "width")
    266                 userValuePx += borderBox.left + borderBox.right + paddingBox.left + paddingBox.right;
    267             else
    268                 userValuePx += borderBox.top + borderBox.bottom + paddingBox.top + paddingBox.bottom;
    269 
    270             userInput = userValuePx + "px";
    271         }
    272 
    273         var self = this;
    274         var callback = function(style) {
    275             if (!style)
    276                 return;
    277             self.inlineStyle = style;
    278             self.dispatchEventToListeners("metrics edited");
    279             self.update();
    280         };
    281 
    282         function setEnabledValueCallback(context, style)
    283         {
    284             var property = style.getLiveProperty(styleProperty);
    285             if (!property)
    286                 style.appendProperty(context.styleProperty, userInput, callback);
    287              else
    288                 property.setValue(userInput, callback);
    289         }
    290 
    291         var allProperties = this.inlineStyle.allProperties;
    292         for (var i = 0; i < allProperties.length; ++i) {
    293             var property = allProperties[i];
    294             if (property.name !== context.styleProperty || property.inactive)
    295                 continue;
    296             if (property.disabled)
    297                 property.setDisabled(false, setEnabledValueCallback.bind(null, context));
    298             else
    299                 property.setValue(userInput, callback);
    300             return;
    301         }
    302 
    303         this.inlineStyle.appendProperty(context.styleProperty, userInput, callback);
    304     }
    305 }
    306 
    307 WebInspector.MetricsSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype;
    308