home *** CD-ROM | disk | FTP | other *** search
/ Computer Active Guide 2009 July / CAG7.ISO / Internetas / SafariSetup.exe / AppleApplicationSupport.msi / WebKit.resources_inspector_StylesSidebarPane.js < prev    next >
Encoding:
Text File  |  2010-06-03  |  59.9 KB  |  1,532 lines

  1. /*
  2.  * Copyright (C) 2007 Apple Inc.  All rights reserved.
  3.  * Copyright (C) 2009 Joseph Pecoraro
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  *
  9.  * 1.  Redistributions of source code must retain the above copyright
  10.  *     notice, this list of conditions and the following disclaimer.
  11.  * 2.  Redistributions in binary form must reproduce the above copyright
  12.  *     notice, this list of conditions and the following disclaimer in the
  13.  *     documentation and/or other materials provided with the distribution.
  14.  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
  15.  *     its contributors may be used to endorse or promote products derived
  16.  *     from this software without specific prior written permission.
  17.  *
  18.  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
  19.  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21.  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
  22.  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  23.  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  24.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  25.  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  27.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28.  */
  29.  
  30. WebInspector.StylesSidebarPane = function()
  31. {
  32.     WebInspector.SidebarPane.call(this, WebInspector.UIString("Styles"));
  33.  
  34.     this.settingsSelectElement = document.createElement("select");
  35.  
  36.     var option = document.createElement("option");
  37.     option.value = "hex";
  38.     option.action = this._changeColorFormat.bind(this);
  39.     option.label = WebInspector.UIString("Hex Colors");
  40.     this.settingsSelectElement.appendChild(option);
  41.  
  42.     option = document.createElement("option");
  43.     option.value = "rgb";
  44.     option.action = this._changeColorFormat.bind(this);
  45.     option.label = WebInspector.UIString("RGB Colors");
  46.     this.settingsSelectElement.appendChild(option);
  47.  
  48.     option = document.createElement("option");
  49.     option.value = "hsl";
  50.     option.action = this._changeColorFormat.bind(this);
  51.     option.label = WebInspector.UIString("HSL Colors");
  52.     this.settingsSelectElement.appendChild(option);
  53.  
  54.     this.settingsSelectElement.appendChild(document.createElement("hr"));
  55.  
  56.     option = document.createElement("option");
  57.     option.action = this._createNewRule.bind(this);
  58.     option.label = WebInspector.UIString("New Style Rule");
  59.     this.settingsSelectElement.appendChild(option);
  60.  
  61.     this.settingsSelectElement.addEventListener("click", function(event) { event.stopPropagation() }, false);
  62.     this.settingsSelectElement.addEventListener("change", this._changeSetting.bind(this), false);
  63.     WebInspector.settings.addEventListener("loaded", this._settingsLoaded, this);
  64.  
  65.     this.titleElement.appendChild(this.settingsSelectElement);
  66. }
  67.  
  68. // Taken from http://www.w3.org/TR/CSS21/propidx.html.
  69. WebInspector.StylesSidebarPane.InheritedProperties = [
  70.     "azimuth", "border-collapse", "border-spacing", "caption-side", "color", "cursor", "direction", "elevation",
  71.     "empty-cells", "font-family", "font-size", "font-style", "font-variant", "font-weight", "font", "letter-spacing",
  72.     "line-height", "list-style-image", "list-style-position", "list-style-type", "list-style", "orphans", "pitch-range",
  73.     "pitch", "quotes", "richness", "speak-header", "speak-numeral", "speak-punctuation", "speak", "speech-rate", "stress",
  74.     "text-align", "text-indent", "text-transform", "visibility", "voice-family", "volume", "white-space", "widows", "word-spacing"
  75. ].keySet();
  76.  
  77. // Keep in sync with RenderStyleConstants.h PseudoId enum. Array below contains pseudo id names for corresponding enum indexes.
  78. // First item is empty due to its artificial NOPSEUDO nature in the enum.
  79. // FIXME: find a way of generating this mapping or getting it from combination of RenderStyleConstants and CSSSelector.cpp at
  80. // runtime.
  81. WebInspector.StylesSidebarPane.PseudoIdNames = [
  82.     "", "first-line", "first-letter", "before", "after", "selection", "", "-webkit-scrollbar", "-webkit-file-upload-button",
  83.     "-webkit-input-placeholder", "-webkit-slider-thumb", "-webkit-search-cancel-button", "-webkit-search-decoration",
  84.     "-webkit-search-results-decoration", "-webkit-search-results-button", "-webkit-media-controls-panel",
  85.     "-webkit-media-controls-play-button", "-webkit-media-controls-mute-button", "-webkit-media-controls-timeline",
  86.     "-webkit-media-controls-timeline-container", "-webkit-media-controls-volume-slider",
  87.     "-webkit-media-controls-volume-slider-container", "-webkit-media-controls-current-time-display",
  88.     "-webkit-media-controls-time-remaining-display", "-webkit-media-controls-seek-back-button", "-webkit-media-controls-seek-forward-button",
  89.     "-webkit-media-controls-fullscreen-button", "-webkit-media-controls-rewind-button", "-webkit-media-controls-return-to-realtime-button",
  90.     "-webkit-media-controls-toggle-closed-captions-button", "-webkit-media-controls-status-display", "-webkit-scrollbar-thumb",
  91.     "-webkit-scrollbar-button", "-webkit-scrollbar-track", "-webkit-scrollbar-track-piece", "-webkit-scrollbar-corner",
  92.     "-webkit-resizer", "-webkit-input-list-button", "-webkit-inner-spin-button", "-webkit-outer-spin-button"
  93. ];
  94.  
  95. WebInspector.StylesSidebarPane.prototype = {
  96.     _settingsLoaded: function()
  97.     {
  98.         var format = WebInspector.settings.colorFormat;
  99.         if (format === "hex")
  100.             this.settingsSelectElement[0].selected = true;
  101.         if (format === "rgb")
  102.             this.settingsSelectElement[1].selected = true;
  103.         if (format === "hsl")
  104.             this.settingsSelectElement[2].selected = true;
  105.     },
  106.  
  107.     update: function(node, editedSection, forceUpdate)
  108.     {
  109.         var refresh = false;
  110.  
  111.         if (forceUpdate)
  112.             delete this.node;
  113.  
  114.         if (!forceUpdate && (!node || node === this.node))
  115.             refresh = true;
  116.  
  117.         if (node && node.nodeType === Node.TEXT_NODE && node.parentNode)
  118.             node = node.parentNode;
  119.  
  120.         if (node && node.nodeType !== Node.ELEMENT_NODE)
  121.             node = null;
  122.  
  123.         if (node)
  124.             this.node = node;
  125.         else
  126.             node = this.node;
  127.  
  128.         var body = this.bodyElement;
  129.  
  130.         if (!node) {
  131.             body.removeChildren();
  132.             this.sections = {};
  133.             return;
  134.         }
  135.  
  136.         function getStylesCallback(styles)
  137.         {
  138.             if (styles)
  139.                 this._rebuildUpdate(node, styles);
  140.         }
  141.  
  142.         function getComputedStyleCallback(computedStyle)
  143.         {
  144.             if (computedStyle)
  145.                 this._refreshUpdate(node, computedStyle, editedSection);
  146.         };
  147.  
  148.         if (refresh)
  149.             InspectorBackend.getComputedStyle(WebInspector.Callback.wrap(getComputedStyleCallback.bind(this)), node.id);
  150.         else
  151.             InspectorBackend.getStyles(WebInspector.Callback.wrap(getStylesCallback.bind(this)), node.id, !WebInspector.settings.showUserAgentStyles);
  152.     },
  153.  
  154.     _refreshUpdate: function(node, computedStyle, editedSection)
  155.     {
  156.         for (var pseudoId in this.sections) {
  157.             var styleRules = this._refreshStyleRules(this.sections[pseudoId], computedStyle);
  158.             var usedProperties = {};
  159.             var disabledComputedProperties = {};
  160.             this._markUsedProperties(styleRules, usedProperties, disabledComputedProperties);
  161.             this._refreshSectionsForStyleRules(styleRules, usedProperties, disabledComputedProperties, editedSection);
  162.         }
  163.     },
  164.  
  165.     _rebuildUpdate: function(node, styles)
  166.     {
  167.         this.bodyElement.removeChildren();
  168.         var styleRules = this._rebuildStyleRules(node, styles);
  169.         var usedProperties = {};
  170.         var disabledComputedProperties = {};
  171.         this._markUsedProperties(styleRules, usedProperties, disabledComputedProperties);
  172.         this.sections[0] = this._rebuildSectionsForStyleRules(styleRules, usedProperties, disabledComputedProperties, 0);
  173.  
  174.         for (var i = 0; i < styles.pseudoElements.length; ++i) {
  175.             var pseudoElementCSSRules = styles.pseudoElements[i];
  176.  
  177.             styleRules = [];
  178.             var pseudoId = pseudoElementCSSRules.pseudoId;
  179.  
  180.             var entry = { isStyleSeparator: true, pseudoId: pseudoId };
  181.             styleRules.push(entry);
  182.  
  183.             // Add rules in reverse order to match the cascade order.
  184.             for (var j = pseudoElementCSSRules.rules.length - 1; j >= 0; --j) {
  185.                 var rule = WebInspector.CSSStyleDeclaration.parseRule(pseudoElementCSSRules.rules[j]);
  186.                 styleRules.push({ style: rule.style, selectorText: rule.selectorText, parentStyleSheet: rule.parentStyleSheet, rule: rule });
  187.             }
  188.             usedProperties = {};
  189.             disabledComputedProperties = {};
  190.             this._markUsedProperties(styleRules, usedProperties, disabledComputedProperties);
  191.             this.sections[pseudoId] = this._rebuildSectionsForStyleRules(styleRules, usedProperties, disabledComputedProperties, pseudoId);
  192.         }
  193.     },
  194.  
  195.     _refreshStyleRules: function(sections, computedStyle)
  196.     {
  197.         var nodeComputedStyle = new WebInspector.CSSStyleDeclaration(computedStyle);
  198.         var styleRules = [];
  199.         for (var i = 0; sections && i < sections.length; ++i) {
  200.             var section = sections[i];
  201.             if (section instanceof WebInspector.BlankStylePropertiesSection)
  202.                 continue;
  203.             if (section.computedStyle)
  204.                 section.styleRule.style = nodeComputedStyle;
  205.             var styleRule = { section: section, style: section.styleRule.style, computedStyle: section.computedStyle, rule: section.rule };
  206.             styleRules.push(styleRule);
  207.         }
  208.         return styleRules;
  209.     },
  210.  
  211.     _rebuildStyleRules: function(node, styles)
  212.     {
  213.         var nodeComputedStyle = new WebInspector.CSSStyleDeclaration(styles.computedStyle);
  214.         this.sections = {};
  215.  
  216.         var styleRules = [];
  217.  
  218.         styleRules.push({ computedStyle: true, selectorText: WebInspector.UIString("Computed Style"), style: nodeComputedStyle, editable: false });
  219.  
  220.         var styleAttributes = {};
  221.         for (var name in styles.styleAttributes) {
  222.             var attrStyle = { style: new WebInspector.CSSStyleDeclaration(styles.styleAttributes[name]), editable: false };
  223.             attrStyle.subtitle = WebInspector.UIString("elementΓÇÖs ΓÇ£%sΓÇ¥ attribute", name);
  224.             attrStyle.selectorText = WebInspector.panels.elements.treeOutline.nodeNameToCorrectCase(node.nodeName) + "[" + name;
  225.             if (node.getAttribute(name))
  226.                 attrStyle.selectorText += "=" + node.getAttribute(name);
  227.             attrStyle.selectorText += "]";
  228.             styleRules.push(attrStyle);
  229.         }
  230.  
  231.         // Show element's Style Attributes
  232.         if (styles.inlineStyle && node.nodeType === Node.ELEMENT_NODE) {
  233.             var inlineStyle = { selectorText: WebInspector.UIString("Style Attribute"), style: new WebInspector.CSSStyleDeclaration(styles.inlineStyle), isAttribute: true };
  234.             inlineStyle.subtitle = WebInspector.UIString("elementΓÇÖs ΓÇ£%sΓÇ¥ attribute", "style");
  235.             styleRules.push(inlineStyle);
  236.         }
  237.  
  238.         // Add rules in reverse order to match the cascade order.
  239.         for (var i = styles.matchedCSSRules.length - 1; i >= 0; --i) {
  240.             var rule = WebInspector.CSSStyleDeclaration.parseRule(styles.matchedCSSRules[i]);
  241.             styleRules.push({ style: rule.style, selectorText: rule.selectorText, parentStyleSheet: rule.parentStyleSheet, rule: rule });
  242.         }
  243.  
  244.         // Collect used properties first in order to identify inherited ones.
  245.         var userPropertyNames = {};
  246.         for (var i = 0; i < styleRules.length; ++i) {
  247.             var styleRule = styleRules[i];
  248.             if (styleRule.computedStyle)
  249.                 continue;
  250.             var style = styleRule.style;
  251.             for (var j = 0; j < style.length; ++j)
  252.                 userPropertyNames[style[j]] = true;
  253.         }
  254.  
  255.         // Walk the node structure and identify styles with inherited properties.
  256.         var parentStyles = styles.parent;
  257.         var parentNode = node.parentNode;
  258.         function insertInheritedNodeSeparator(node)
  259.         {
  260.             var entry = {};
  261.             entry.isStyleSeparator = true;
  262.             entry.node = node;
  263.             styleRules.push(entry);
  264.         }
  265.  
  266.         while (parentStyles) {
  267.             var separatorInserted = false;
  268.             if (parentStyles.inlineStyle) {
  269.                 if (this._containsInherited(parentStyles.inlineStyle)) {
  270.                     var inlineStyle = { selectorText: WebInspector.UIString("Style Attribute"), style: new WebInspector.CSSStyleDeclaration(parentStyles.inlineStyle), isAttribute: true, isInherited: true };
  271.                     inlineStyle.subtitle = WebInspector.UIString("elementΓÇÖs ΓÇ£%sΓÇ¥ attribute", "style");
  272.                     if (!separatorInserted) {
  273.                         insertInheritedNodeSeparator(parentNode);
  274.                         separatorInserted = true;
  275.                     }
  276.                     styleRules.push(inlineStyle);
  277.                 }
  278.             }
  279.  
  280.             for (var i = parentStyles.matchedCSSRules.length - 1; i >= 0; --i) {
  281.                 var rulePayload = parentStyles.matchedCSSRules[i];
  282.                 if (!this._containsInherited(rulePayload.style))
  283.                     continue;
  284.                 var rule = WebInspector.CSSStyleDeclaration.parseRule(rulePayload);
  285.                 if (!separatorInserted) {
  286.                     insertInheritedNodeSeparator(parentNode);
  287.                     separatorInserted = true;
  288.                 }
  289.                 styleRules.push({ style: rule.style, selectorText: rule.selectorText, parentStyleSheet: rule.parentStyleSheet, rule: rule, isInherited: true });
  290.             }
  291.             parentStyles = parentStyles.parent;
  292.             parentNode = parentNode.parentNode;
  293.         }
  294.         return styleRules;
  295.     },
  296.  
  297.     _markUsedProperties: function(styleRules, usedProperties, disabledComputedProperties)
  298.     {
  299.         function deleteDisabledProperty(style, name)
  300.         {
  301.             if (!style || !name)
  302.                 return;
  303.             if (style.__disabledPropertyValues)
  304.                 delete style.__disabledPropertyValues[name];
  305.             if (style.__disabledPropertyPriorities)
  306.                 delete style.__disabledPropertyPriorities[name];
  307.             if (style.__disabledProperties)
  308.                 delete style.__disabledProperties[name];
  309.         }
  310.  
  311.         var priorityUsed = false;
  312.  
  313.         // Walk the style rules and make a list of all used and overloaded properties.
  314.         for (var i = 0; i < styleRules.length; ++i) {
  315.             var styleRule = styleRules[i];
  316.             if (styleRule.computedStyle || styleRule.isStyleSeparator)
  317.                 continue;
  318.             if (styleRule.section && styleRule.section.noAffect)
  319.                 continue;
  320.  
  321.             styleRule.usedProperties = {};
  322.  
  323.             var style = styleRule.style;
  324.             for (var j = 0; j < style.length; ++j) {
  325.                 var name = style[j];
  326.  
  327.                 if (!priorityUsed && style.getPropertyPriority(name).length)
  328.                     priorityUsed = true;
  329.  
  330.                 // If the property name is already used by another rule then this rule's
  331.                 // property is overloaded, so don't add it to the rule's usedProperties.
  332.                 if (!(name in usedProperties))
  333.                     styleRule.usedProperties[name] = true;
  334.  
  335.                 if (name === "font") {
  336.                     // The font property is not reported as a shorthand. Report finding the individual
  337.                     // properties so they are visible in computed style.
  338.                     // FIXME: remove this when http://bugs.webkit.org/show_bug.cgi?id=15598 is fixed.
  339.                     styleRule.usedProperties["font-family"] = true;
  340.                     styleRule.usedProperties["font-size"] = true;
  341.                     styleRule.usedProperties["font-style"] = true;
  342.                     styleRule.usedProperties["font-variant"] = true;
  343.                     styleRule.usedProperties["font-weight"] = true;
  344.                     styleRule.usedProperties["line-height"] = true;
  345.                 }
  346.  
  347.                 // Delete any disabled properties, since the property does exist.
  348.                 // This prevents it from showing twice.
  349.                 deleteDisabledProperty(style, name);
  350.                 deleteDisabledProperty(style, style.getPropertyShorthand(name));
  351.             }
  352.  
  353.             // Add all the properties found in this style to the used properties list.
  354.             // Do this here so only future rules are affect by properties used in this rule.
  355.             for (var name in styleRules[i].usedProperties)
  356.                 usedProperties[name] = true;
  357.  
  358.             // Remember all disabled properties so they show up in computed style.
  359.             if (style.__disabledProperties)
  360.                 for (var name in style.__disabledProperties)
  361.                     disabledComputedProperties[name] = true;
  362.         }
  363.  
  364.         if (priorityUsed) {
  365.             // Walk the properties again and account for !important.
  366.             var foundPriorityProperties = [];
  367.  
  368.             // Walk in reverse to match the order !important overrides.
  369.             for (var i = (styleRules.length - 1); i >= 0; --i) {
  370.                 if (styleRules[i].computedStyle || styleRules[i].isStyleSeparator)
  371.                     continue;
  372.  
  373.                 var style = styleRules[i].style;
  374.                 for (var j = 0; j < style.length; ++j) {
  375.                     var name = style[j];
  376.                     if (style.getPropertyPriority(name).length) {
  377.                         if (!(name in foundPriorityProperties))
  378.                             styleRules[i].usedProperties[name] = true;
  379.                         else
  380.                             delete styleRules[i].usedProperties[name];
  381.                         foundPriorityProperties[name] = true;
  382.                     } else if (name in foundPriorityProperties)
  383.                         delete styleRules[i].usedProperties[name];
  384.                 }
  385.             }
  386.         }
  387.     },
  388.  
  389.     _refreshSectionsForStyleRules: function(styleRules, usedProperties, disabledComputedProperties, editedSection)
  390.     {
  391.         // Walk the style rules and update the sections with new overloaded and used properties.
  392.         for (var i = 0; i < styleRules.length; ++i) {
  393.             var styleRule = styleRules[i];
  394.             var section = styleRule.section;
  395.             if (styleRule.computedStyle)
  396.                 section.disabledComputedProperties = disabledComputedProperties;
  397.             section._usedProperties = (styleRule.usedProperties || usedProperties);
  398.             section.update((section === editedSection) || styleRule.computedStyle);
  399.         }
  400.     },
  401.  
  402.     _rebuildSectionsForStyleRules: function(styleRules, usedProperties, disabledComputedProperties, pseudoId)
  403.     {
  404.         // Make a property section for each style rule.
  405.         var sections = [];
  406.         for (var i = 0; i < styleRules.length; ++i) {
  407.             var styleRule = styleRules[i];
  408.             if (styleRule.isStyleSeparator) {
  409.                 var separatorElement = document.createElement("div");
  410.                 separatorElement.className = "styles-sidebar-separator";
  411.                 if (styleRule.node) {
  412.                     var link = WebInspector.panels.elements.linkifyNodeReference(styleRule.node);
  413.                     separatorElement.appendChild(document.createTextNode(WebInspector.UIString("Inherited from") + " "));
  414.                     separatorElement.appendChild(link);
  415.                 } else if ("pseudoId" in styleRule) {
  416.                     var pseudoName = WebInspector.StylesSidebarPane.PseudoIdNames[styleRule.pseudoId];
  417.                     if (pseudoName)
  418.                         separatorElement.textContent = WebInspector.UIString("Pseudo ::%s element", pseudoName);
  419.                     else
  420.                         separatorElement.textContent = WebInspector.UIString("Pseudo element");
  421.                 }
  422.                 this.bodyElement.appendChild(separatorElement);
  423.                 continue;
  424.             }
  425.             var computedStyle = styleRule.computedStyle;
  426.  
  427.             // Default editable to true if it was omitted.
  428.             var editable = styleRule.editable;
  429.             if (typeof editable === "undefined")
  430.                 editable = true;
  431.  
  432.             var section = new WebInspector.StylePropertiesSection(styleRule, styleRule.subtitle, styleRule.computedStyle, (styleRule.usedProperties || usedProperties), editable, styleRule.isInherited);
  433.             if (computedStyle)
  434.                 section.disabledComputedProperties = disabledComputedProperties;
  435.             section.pane = this;
  436.  
  437.             if (Preferences.styleRulesExpandedState && section.identifier in Preferences.styleRulesExpandedState)
  438.                 section.expanded = Preferences.styleRulesExpandedState[section.identifier];
  439.             else if (computedStyle)
  440.                 section.collapse(true);
  441.             else if (styleRule.isAttribute && styleRule.style.length === 0)
  442.                 section.collapse(true);
  443.             else if (styleRule.isInherited)
  444.                 section.collapse(true);
  445.             else
  446.                 section.expand(true);
  447.  
  448.             this.bodyElement.appendChild(section.element);
  449.             sections.push(section);
  450.         }
  451.         return sections;
  452.     },
  453.  
  454.     _containsInherited: function(payload)
  455.     {
  456.         if (this._arrayContainsInheritedProperty(payload.properties))
  457.             return true;
  458.         if (payload.disabled && this._arrayContainsInheritedProperty(payload.disabled))
  459.             return true;
  460.         return false;
  461.     },
  462.  
  463.     _arrayContainsInheritedProperty: function(properties)
  464.     {
  465.         for (var i = 0; i < properties.length; ++i) {
  466.             var property = properties[i];
  467.             // Does this style contain non-overriden inherited property?
  468.             if (property.name in WebInspector.StylesSidebarPane.InheritedProperties)
  469.                 return true;
  470.         }
  471.         return false;
  472.     },
  473.  
  474.     _changeSetting: function(event)
  475.     {
  476.         var options = this.settingsSelectElement.options;
  477.         var selectedOption = options[this.settingsSelectElement.selectedIndex];
  478.         selectedOption.action(event);
  479.  
  480.         // Select the correct color format setting again, since it needs to be selected.
  481.         var selectedIndex = 0;
  482.         for (var i = 0; i < options.length; ++i) {
  483.             if (options[i].value === WebInspector.settings.colorFormat) {
  484.                 selectedIndex = i;
  485.                 break;
  486.             }
  487.         }
  488.  
  489.         this.settingsSelectElement.selectedIndex = selectedIndex;
  490.     },
  491.  
  492.     _changeColorFormat: function(event)
  493.     {
  494.         var selectedOption = this.settingsSelectElement[this.settingsSelectElement.selectedIndex];
  495.         WebInspector.settings.colorFormat = selectedOption.value;
  496.  
  497.         for (var pseudoId in this.sections) {
  498.             var sections = this.sections[pseudoId];
  499.             for (var i = 0; i < sections.length; ++i)
  500.                 sections[i].update(true);
  501.         }
  502.     },
  503.  
  504.     _createNewRule: function(event)
  505.     {
  506.         this.addBlankSection().startEditingSelector();
  507.     },
  508.  
  509.     addBlankSection: function()
  510.     {
  511.         var blankSection = new WebInspector.BlankStylePropertiesSection(appropriateSelectorForNode(this.node, true));
  512.         blankSection.pane = this;
  513.  
  514.         var elementStyleSection = this.sections[0][1];
  515.         this.bodyElement.insertBefore(blankSection.element, elementStyleSection.element.nextSibling);
  516.  
  517.         this.sections[0].splice(2, 0, blankSection);
  518.  
  519.         return blankSection;
  520.     },
  521.  
  522.     removeSection: function(section)
  523.     {
  524.         for (var pseudoId in this.sections) {
  525.             var sections = this.sections[pseudoId];
  526.             var index = sections.indexOf(section);
  527.             if (index === -1)
  528.                 continue;
  529.             sections.splice(index, 1);
  530.             if (section.element.parentNode)
  531.                 section.element.parentNode.removeChild(section.element);
  532.         }
  533.     }
  534. }
  535.  
  536. WebInspector.StylesSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype;
  537.  
  538. WebInspector.StylePropertiesSection = function(styleRule, subtitle, computedStyle, usedProperties, editable, isInherited)
  539. {
  540.     WebInspector.PropertiesSection.call(this, styleRule.selectorText);
  541.  
  542.     this.titleElement.addEventListener("dblclick", this._handleSelectorDoubleClick.bind(this), false);
  543.     this.titleElement.addEventListener("click", this._handleSelectorClick.bind(this), false);
  544.     this.element.addEventListener("dblclick", this._handleEmptySpaceDoubleClick.bind(this), false);
  545.  
  546.     this.styleRule = styleRule;
  547.     this.rule = this.styleRule.rule;
  548.     this.computedStyle = computedStyle;
  549.     this.editable = (editable && !computedStyle);
  550.     this.isInherited = isInherited;
  551.  
  552.     // Prevent editing the user agent and user rules.
  553.     var isUserAgent = this.rule && this.rule.isUserAgent;
  554.     var isUser = this.rule && this.rule.isUser;
  555.     var isViaInspector = this.rule && this.rule.isViaInspector;
  556.  
  557.     if (isUserAgent || isUser)
  558.         this.editable = false;
  559.  
  560.     this._usedProperties = usedProperties;
  561.  
  562.     if (this.rule)
  563.         this.titleElement.addStyleClass("styles-selector");
  564.  
  565.     if (computedStyle) {
  566.         this.element.addStyleClass("computed-style");
  567.  
  568.         if (WebInspector.settings.showInheritedComputedStyleProperties)
  569.             this.element.addStyleClass("show-inherited");
  570.  
  571.         var computedStyleSection = this;
  572.         var showInheritedToggleFunction = function(event) {
  573.             WebInspector.settings.showInheritedComputedStyleProperties = computedStyleSection._showInheritedCheckbox.checked();
  574.             if (WebInspector.settings.showInheritedComputedStyleProperties)
  575.                 computedStyleSection.element.addStyleClass("show-inherited");
  576.             else
  577.                 computedStyleSection.element.removeStyleClass("show-inherited");
  578.         };
  579.  
  580.         this._showInheritedCheckbox = new WebInspector.Checkbox(WebInspector.UIString("Show inherited"),
  581.                                                                 showInheritedToggleFunction,
  582.                                                                 WebInspector.settings.showInheritedComputedStyleProperties);
  583.  
  584.         this.subtitleElement.appendChild(this._showInheritedCheckbox.element);
  585.     } else {
  586.         if (!subtitle) {
  587.             if (this.styleRule.parentStyleSheet && this.styleRule.parentStyleSheet.href) {
  588.                 var url = this.styleRule.parentStyleSheet.href;
  589.                 this.subtitleElement.appendChild(WebInspector.linkifyResourceAsNode(url, "resources", this.rule.sourceLine + 1));
  590.             } else if (isUserAgent)
  591.                 subtitle = WebInspector.UIString("user agent stylesheet");
  592.             else if (isUser)
  593.                 subtitle = WebInspector.UIString("user stylesheet");
  594.             else if (isViaInspector)
  595.                 subtitle = WebInspector.UIString("via inspector");
  596.             else
  597.                 subtitle = WebInspector.UIString("inline stylesheet");
  598.         }
  599.         if (isInherited)
  600.             this.element.addStyleClass("show-inherited");
  601.         if (subtitle)
  602.             this.subtitle = subtitle;
  603.     }
  604.  
  605.     this.identifier = styleRule.selectorText;
  606.     if (this.subtitle)
  607.         this.identifier += ":" + this.subtitleElement.textContent;
  608. }
  609.  
  610. WebInspector.StylePropertiesSection.prototype = {
  611.     get usedProperties()
  612.     {
  613.         return this._usedProperties || {};
  614.     },
  615.  
  616.     set usedProperties(x)
  617.     {
  618.         this._usedProperties = x;
  619.         this.update();
  620.     },
  621.  
  622.     expand: function(dontRememberState)
  623.     {
  624.         WebInspector.PropertiesSection.prototype.expand.call(this);
  625.         if (dontRememberState)
  626.             return;
  627.  
  628.         if (!Preferences.styleRulesExpandedState)
  629.             Preferences.styleRulesExpandedState = {};
  630.         Preferences.styleRulesExpandedState[this.identifier] = true;
  631.     },
  632.  
  633.     collapse: function(dontRememberState)
  634.     {
  635.         WebInspector.PropertiesSection.prototype.collapse.call(this);
  636.         if (dontRememberState)
  637.             return;
  638.  
  639.         if (!Preferences.styleRulesExpandedState)
  640.             Preferences.styleRulesExpandedState = {};
  641.         Preferences.styleRulesExpandedState[this.identifier] = false;
  642.     },
  643.  
  644.     isPropertyInherited: function(property)
  645.     {
  646.         if (this.isInherited) {
  647.             // While rendering inherited stylesheet, reverse meaning of this property.
  648.             // Render truly inherited properties with black, i.e. return them as non-inherited.
  649.             return !(property in WebInspector.StylesSidebarPane.InheritedProperties);
  650.         }
  651.  
  652.         if (!this.computedStyle || !this._usedProperties || this.noAffect)
  653.             return false;
  654.         // These properties should always show for Computed Style.
  655.         var alwaysShowComputedProperties = { "display": true, "height": true, "width": true };
  656.         return !(property in this.usedProperties) && !(property in alwaysShowComputedProperties) && !(property in this.disabledComputedProperties);
  657.     },
  658.  
  659.     isPropertyOverloaded: function(property, shorthand)
  660.     {
  661.         if (this.computedStyle || !this._usedProperties || this.noAffect)
  662.             return false;
  663.  
  664.         if (this.isInherited && !(property in WebInspector.StylesSidebarPane.InheritedProperties)) {
  665.             // In the inherited sections, only show overrides for the potentially inherited properties.
  666.             return false;
  667.         }
  668.  
  669.         var used = (property in this.usedProperties);
  670.         if (used || !shorthand)
  671.             return !used;
  672.  
  673.         // Find out if any of the individual longhand properties of the shorthand
  674.         // are used, if none are then the shorthand is overloaded too.
  675.         var longhandProperties = this.styleRule.style.getLonghandProperties(property);
  676.         for (var j = 0; j < longhandProperties.length; ++j) {
  677.             var individualProperty = longhandProperties[j];
  678.             if (individualProperty in this.usedProperties)
  679.                 return false;
  680.         }
  681.  
  682.         return true;
  683.     },
  684.  
  685.     update: function(full)
  686.     {
  687.         if (full || this.computedStyle) {
  688.             this.propertiesTreeOutline.removeChildren();
  689.             this.populated = false;
  690.         } else {
  691.             var child = this.propertiesTreeOutline.children[0];
  692.             while (child) {
  693.                 child.overloaded = this.isPropertyOverloaded(child.name, child.shorthand);
  694.                 child = child.traverseNextTreeElement(false, null, true);
  695.             }
  696.         }
  697.  
  698.         this.afterUpdate();
  699.     },
  700.  
  701.     afterUpdate: function()
  702.     {
  703.         if (this._afterUpdate) {
  704.             this._afterUpdate(this);
  705.             delete this._afterUpdate;
  706.         }
  707.     },
  708.  
  709.     onpopulate: function()
  710.     {
  711.         var style = this.styleRule.style;
  712.  
  713.         var foundShorthands = {};
  714.         var disabledProperties = style.__disabledPropertyValues || {};
  715.  
  716.         var uniqueProperties = [];
  717.         for (var i = 0; i < style.length; ++i)
  718.             uniqueProperties.push(style[i]);
  719.  
  720.         for (var name in disabledProperties)
  721.             uniqueProperties.push(name);
  722.  
  723.         uniqueProperties.sort();
  724.  
  725.         for (var i = 0; i < uniqueProperties.length; ++i) {
  726.             var name = uniqueProperties[i];
  727.             var disabled = name in disabledProperties;
  728.             if (!disabled && this.disabledComputedProperties && !(name in this.usedProperties) && name in this.disabledComputedProperties)
  729.                 disabled = true;
  730.  
  731.             var shorthand = !disabled ? style.getPropertyShorthand(name) : null;
  732.  
  733.             if (shorthand && shorthand in foundShorthands)
  734.                 continue;
  735.  
  736.             if (shorthand) {
  737.                 foundShorthands[shorthand] = true;
  738.                 name = shorthand;
  739.             }
  740.  
  741.             var isShorthand = (shorthand ? true : false);
  742.             var inherited = this.isPropertyInherited(name);
  743.             var overloaded = this.isPropertyOverloaded(name, isShorthand);
  744.  
  745.             var item = new WebInspector.StylePropertyTreeElement(this.styleRule, style, name, isShorthand, inherited, overloaded, disabled);
  746.             this.propertiesTreeOutline.appendChild(item);
  747.         }
  748.     },
  749.  
  750.     findTreeElementWithName: function(name)
  751.     {
  752.         var treeElement = this.propertiesTreeOutline.children[0];
  753.         while (treeElement) {
  754.             if (treeElement.name === name)
  755.                 return treeElement;
  756.             treeElement = treeElement.traverseNextTreeElement(true, null, true);
  757.         }
  758.         return null;
  759.     },
  760.  
  761.     addNewBlankProperty: function()
  762.     {
  763.         var item = new WebInspector.StylePropertyTreeElement(this.styleRule, this.styleRule.style, "", false, false, false, false);
  764.         this.propertiesTreeOutline.appendChild(item);
  765.         item.listItemElement.textContent = "";
  766.         item._newProperty = true;
  767.         return item;
  768.     },
  769.  
  770.     _handleEmptySpaceDoubleClick: function(event)
  771.     {
  772.         if (event.target.hasStyleClass("header")) {
  773.             event.stopPropagation();
  774.             return;
  775.         }
  776.         this.expand();
  777.         this.addNewBlankProperty().startEditing();
  778.     },
  779.  
  780.     _handleSelectorClick: function(event)
  781.     {
  782.         event.stopPropagation();
  783.     },
  784.  
  785.     _handleSelectorDoubleClick: function(event)
  786.     {
  787.         this._startEditingOnMouseEvent();
  788.         event.stopPropagation();
  789.     },
  790.  
  791.     _startEditingOnMouseEvent: function()
  792.     {
  793.         if (!this.editable)
  794.             return;
  795.  
  796.         if (!this.rule && this.propertiesTreeOutline.children.length === 0) {
  797.             this.expand();
  798.             this.addNewBlankProperty().startEditing();
  799.             return;
  800.         }
  801.  
  802.         if (!this.rule)
  803.             return;
  804.  
  805.         this.startEditingSelector();
  806.     },
  807.  
  808.     startEditingSelector: function()
  809.     {
  810.         var element = this.titleElement;
  811.         if (WebInspector.isBeingEdited(element))
  812.             return;
  813.  
  814.         WebInspector.startEditing(this.titleElement, this.editingSelectorCommitted.bind(this), this.editingSelectorCancelled.bind(this), null);
  815.         window.getSelection().setBaseAndExtent(element, 0, element, 1);
  816.     },
  817.  
  818.     editingSelectorCommitted: function(element, newContent, oldContent, context, moveDirection)
  819.     {
  820.         function moveToNextIfNeeded() {
  821.             if (!moveDirection || moveDirection !== "forward")
  822.                 return;
  823.  
  824.             this.expand();
  825.             if (this.propertiesTreeOutline.children.length === 0)
  826.                 this.addNewBlankProperty().startEditing();
  827.             else {
  828.                 var item = this.propertiesTreeOutline.children[0]
  829.                 item.startEditing(item.valueElement);
  830.             }
  831.         }
  832.  
  833.         if (newContent === oldContent)
  834.             return moveToNextIfNeeded.call(this);
  835.  
  836.         var self = this;
  837.         function callback(newRulePayload, doesAffectSelectedNode)
  838.         {
  839.             if (!newRulePayload) {
  840.                 // Invalid Syntax for a Selector
  841.                 moveToNextIfNeeded.call(self);
  842.                 return;
  843.             }
  844.  
  845.             if (!doesAffectSelectedNode) {
  846.                 self.noAffect = true;
  847.                 self.element.addStyleClass("no-affect");
  848.             } else {
  849.                 delete self.noAffect;
  850.                 self.element.removeStyleClass("no-affect");
  851.             }
  852.  
  853.             var newRule = WebInspector.CSSStyleDeclaration.parseRule(newRulePayload);
  854.             self.rule = newRule;
  855.             self.styleRule = { section: self, style: newRule.style, selectorText: newRule.selectorText, parentStyleSheet: newRule.parentStyleSheet, rule: newRule };
  856.  
  857.             var oldIdentifier = this.identifier;
  858.             self.identifier = newRule.selectorText + ":" + self.subtitleElement.textContent;
  859.  
  860.             self.pane.update();
  861.  
  862.             WebInspector.panels.elements.renameSelector(oldIdentifier, this.identifier, oldContent, newContent);
  863.  
  864.             moveToNextIfNeeded.call(self);
  865.         }
  866.  
  867.         InspectorBackend.setRuleSelector(WebInspector.Callback.wrap(callback), this.rule.id, newContent, this.pane.node.id);
  868.     },
  869.  
  870.     editingSelectorCancelled: function()
  871.     {
  872.         // Do nothing, this is overridden by BlankStylePropertiesSection.
  873.     }
  874. }
  875.  
  876. WebInspector.StylePropertiesSection.prototype.__proto__ = WebInspector.PropertiesSection.prototype;
  877.  
  878. WebInspector.BlankStylePropertiesSection = function(defaultSelectorText)
  879. {
  880.     WebInspector.StylePropertiesSection.call(this, {selectorText: defaultSelectorText, rule: {isViaInspector: true}}, "", false, {}, false);
  881.  
  882.     this.element.addStyleClass("blank-section");
  883. }
  884.  
  885. WebInspector.BlankStylePropertiesSection.prototype = {
  886.     expand: function()
  887.     {
  888.         // Do nothing, blank sections are not expandable.
  889.     },
  890.  
  891.     editingSelectorCommitted: function(element, newContent, oldContent, context)
  892.     {
  893.         var self = this;
  894.         function callback(rule, doesSelectorAffectSelectedNode)
  895.         {
  896.             if (!rule) {
  897.                 // Invalid Syntax for a Selector
  898.                 self.editingSelectorCancelled();
  899.                 return;
  900.             }
  901.  
  902.             var styleRule = WebInspector.CSSStyleDeclaration.parseRule(rule);
  903.             styleRule.rule = rule;
  904.  
  905.             self.makeNormal(styleRule);
  906.  
  907.             if (!doesSelectorAffectSelectedNode) {
  908.                 self.noAffect = true;
  909.                 self.element.addStyleClass("no-affect");
  910.             }
  911.  
  912.             self.subtitleElement.textContent = WebInspector.UIString("via inspector");
  913.             self.expand();
  914.  
  915.             self.addNewBlankProperty().startEditing();
  916.         }
  917.  
  918.         InspectorBackend.addRule(WebInspector.Callback.wrap(callback), newContent, this.pane.node.id);
  919.     },
  920.  
  921.     editingSelectorCancelled: function()
  922.     {
  923.         this.pane.removeSection(this);
  924.     },
  925.  
  926.     makeNormal: function(styleRule)
  927.     {
  928.         this.element.removeStyleClass("blank-section");
  929.  
  930.         this.styleRule = styleRule;
  931.         this.rule = styleRule.rule;
  932.         this.computedStyle = false;
  933.         this.editable = true;
  934.         this.identifier = styleRule.selectorText + ":via inspector";
  935.  
  936.         this.__proto__ = WebInspector.StylePropertiesSection.prototype;
  937.     }
  938. }
  939.  
  940. WebInspector.BlankStylePropertiesSection.prototype.__proto__ = WebInspector.StylePropertiesSection.prototype;
  941.  
  942. WebInspector.StylePropertyTreeElement = function(styleRule, style, name, shorthand, inherited, overloaded, disabled)
  943. {
  944.     this._styleRule = styleRule;
  945.     this.style = style;
  946.     this.name = name;
  947.     this.shorthand = shorthand;
  948.     this._inherited = inherited;
  949.     this._overloaded = overloaded;
  950.     this._disabled = disabled;
  951.  
  952.     // Pass an empty title, the title gets made later in onattach.
  953.     TreeElement.call(this, "", null, shorthand);
  954. }
  955.  
  956. WebInspector.StylePropertyTreeElement.prototype = {
  957.     get inherited()
  958.     {
  959.         return this._inherited;
  960.     },
  961.  
  962.     set inherited(x)
  963.     {
  964.         if (x === this._inherited)
  965.             return;
  966.         this._inherited = x;
  967.         this.updateState();
  968.     },
  969.  
  970.     get overloaded()
  971.     {
  972.         return this._overloaded;
  973.     },
  974.  
  975.     set overloaded(x)
  976.     {
  977.         if (x === this._overloaded)
  978.             return;
  979.         this._overloaded = x;
  980.         this.updateState();
  981.     },
  982.  
  983.     get disabled()
  984.     {
  985.         return this._disabled;
  986.     },
  987.  
  988.     set disabled(x)
  989.     {
  990.         if (x === this._disabled)
  991.             return;
  992.         this._disabled = x;
  993.         this.updateState();
  994.     },
  995.  
  996.     get priority()
  997.     {
  998.         if (this.disabled && this.style.__disabledPropertyPriorities && this.name in this.style.__disabledPropertyPriorities)
  999.             return this.style.__disabledPropertyPriorities[this.name];
  1000.         return (this.shorthand ? this.style.getShorthandPriority(this.name) : this.style.getPropertyPriority(this.name));
  1001.     },
  1002.  
  1003.     get value()
  1004.     {
  1005.         if (this.disabled && this.style.__disabledPropertyValues && this.name in this.style.__disabledPropertyValues)
  1006.             return this.style.__disabledPropertyValues[this.name];
  1007.         return (this.shorthand ? this.style.getShorthandValue(this.name) : this.style.getPropertyValue(this.name));
  1008.     },
  1009.  
  1010.     onattach: function()
  1011.     {
  1012.         this.updateTitle();
  1013.     },
  1014.  
  1015.     updateTitle: function()
  1016.     {
  1017.         var priority = this.priority;
  1018.         var value = this.value;
  1019.  
  1020.         if (priority && !priority.length)
  1021.             delete priority;
  1022.         if (priority)
  1023.             priority = "!" + priority;
  1024.  
  1025.         this.updateState();
  1026.  
  1027.         var enabledCheckboxElement = document.createElement("input");
  1028.         enabledCheckboxElement.className = "enabled-button";
  1029.         enabledCheckboxElement.type = "checkbox";
  1030.         enabledCheckboxElement.checked = !this.disabled;
  1031.         enabledCheckboxElement.addEventListener("change", this.toggleEnabled.bind(this), false);
  1032.  
  1033.         var nameElement = document.createElement("span");
  1034.         nameElement.className = "name";
  1035.         nameElement.textContent = this.name;
  1036.         this.nameElement = nameElement;
  1037.  
  1038.         var valueElement = document.createElement("span");
  1039.         valueElement.className = "value";
  1040.         this.valueElement = valueElement;
  1041.  
  1042.         if (value) {
  1043.             function processValue(regex, processor, nextProcessor, valueText)
  1044.             {
  1045.                 var container = document.createDocumentFragment();
  1046.  
  1047.                 var items = valueText.replace(regex, "\0$1\0").split("\0");
  1048.                 for (var i = 0; i < items.length; ++i) {
  1049.                     if ((i % 2) === 0) {
  1050.                         if (nextProcessor)
  1051.                             container.appendChild(nextProcessor(items[i]));
  1052.                         else
  1053.                             container.appendChild(document.createTextNode(items[i]));
  1054.                     } else {
  1055.                         var processedNode = processor(items[i]);
  1056.                         if (processedNode)
  1057.                             container.appendChild(processedNode);
  1058.                     }
  1059.                 }
  1060.  
  1061.                 return container;
  1062.             }
  1063.  
  1064.             function linkifyURL(url)
  1065.             {
  1066.                 var container = document.createDocumentFragment();
  1067.                 container.appendChild(document.createTextNode("url("));
  1068.                 container.appendChild(WebInspector.linkifyURLAsNode(url, url, null, (url in WebInspector.resourceURLMap)));
  1069.                 container.appendChild(document.createTextNode(")"));
  1070.                 return container;
  1071.             }
  1072.  
  1073.             function processColor(text)
  1074.             {
  1075.                 try {
  1076.                     var color = new WebInspector.Color(text);
  1077.                 } catch (e) {
  1078.                     return document.createTextNode(text);
  1079.                 }
  1080.  
  1081.                 var swatchElement = document.createElement("span");
  1082.                 swatchElement.title = WebInspector.UIString("Click to change color format");
  1083.                 swatchElement.className = "swatch";
  1084.                 swatchElement.style.setProperty("background-color", text);
  1085.  
  1086.                 swatchElement.addEventListener("click", changeColorDisplay, false);
  1087.                 swatchElement.addEventListener("dblclick", function(event) { event.stopPropagation() }, false);
  1088.  
  1089.                 var format;
  1090.                 if (Preferences.showColorNicknames && color.nickname)
  1091.                     format = "nickname";
  1092.                 else if (WebInspector.settings.colorFormat === "rgb")
  1093.                     format = (color.simple ? "rgb" : "rgba");
  1094.                 else if (WebInspector.settings.colorFormat === "hsl")
  1095.                     format = (color.simple ? "hsl" : "hsla");
  1096.                 else if (color.simple)
  1097.                     format = (color.hasShortHex() ? "shorthex" : "hex");
  1098.                 else
  1099.                     format = "rgba";
  1100.  
  1101.                 var colorValueElement = document.createElement("span");
  1102.                 colorValueElement.textContent = color.toString(format);
  1103.  
  1104.                 function changeColorDisplay(event)
  1105.                 {
  1106.                     switch (format) {
  1107.                         case "rgb":
  1108.                             format = "hsl";
  1109.                             break;
  1110.  
  1111.                         case "shorthex":
  1112.                             format = "hex";
  1113.                             break;
  1114.  
  1115.                         case "hex":
  1116.                             format = "rgb";
  1117.                             break;
  1118.  
  1119.                         case "nickname":
  1120.                             if (color.simple) {
  1121.                                 if (color.hasShortHex())
  1122.                                     format = "shorthex";
  1123.                                 else
  1124.                                     format = "hex";
  1125.                                 break;
  1126.                             }
  1127.  
  1128.                             format = "rgba";
  1129.                             break;
  1130.  
  1131.                         case "hsl":
  1132.                             if (color.nickname)
  1133.                                 format = "nickname";
  1134.                             else if (color.hasShortHex())
  1135.                                 format = "shorthex";
  1136.                             else
  1137.                                 format = "hex";
  1138.                             break;
  1139.  
  1140.                         case "rgba":
  1141.                             format = "hsla";
  1142.                             break;
  1143.  
  1144.                         case "hsla":
  1145.                             if (color.nickname)
  1146.                                 format = "nickname";
  1147.                             else
  1148.                                 format = "rgba";
  1149.                             break;
  1150.                     }
  1151.  
  1152.                     colorValueElement.textContent = color.toString(format);
  1153.                 }
  1154.  
  1155.                 var container = document.createDocumentFragment();
  1156.                 container.appendChild(swatchElement);
  1157.                 container.appendChild(colorValueElement);
  1158.                 return container;
  1159.             }
  1160.  
  1161.             var colorRegex = /((?:rgb|hsl)a?\([^)]+\)|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3}|\b\w+\b(?!-))/g;
  1162.             var colorProcessor = processValue.bind(window, colorRegex, processColor, null);
  1163.  
  1164.             valueElement.appendChild(processValue(/url\(([^)]+)\)/g, linkifyURL, colorProcessor, value));
  1165.         }
  1166.  
  1167.         if (priority) {
  1168.             var priorityElement = document.createElement("span");
  1169.             priorityElement.className = "priority";
  1170.             priorityElement.textContent = priority;
  1171.         }
  1172.  
  1173.         this.listItemElement.removeChildren();
  1174.  
  1175.         // Append the checkbox for root elements of an editable section.
  1176.         if (this.treeOutline.section && this.treeOutline.section.editable && this.parent.root)
  1177.             this.listItemElement.appendChild(enabledCheckboxElement);
  1178.         this.listItemElement.appendChild(nameElement);
  1179.         this.listItemElement.appendChild(document.createTextNode(": "));
  1180.         this.listItemElement.appendChild(valueElement);
  1181.  
  1182.         if (priorityElement) {
  1183.             this.listItemElement.appendChild(document.createTextNode(" "));
  1184.             this.listItemElement.appendChild(priorityElement);
  1185.         }
  1186.  
  1187.         this.listItemElement.appendChild(document.createTextNode(";"));
  1188.  
  1189.         this.tooltip = this.name + ": " + valueElement.textContent + (priority ? " " + priority : "");
  1190.     },
  1191.  
  1192.     updateAll: function(updateAllRules)
  1193.     {
  1194.         if (updateAllRules && this.treeOutline.section && this.treeOutline.section.pane)
  1195.             this.treeOutline.section.pane.update(null, this.treeOutline.section);
  1196.         else if (this.treeOutline.section)
  1197.             this.treeOutline.section.update(true);
  1198.         else
  1199.             this.updateTitle(); // FIXME: this will not show new properties. But we don't hit his case yet.
  1200.     },
  1201.  
  1202.     toggleEnabled: function(event)
  1203.     {
  1204.         var disabled = !event.target.checked;
  1205.  
  1206.         var self = this;
  1207.         function callback(newPayload)
  1208.         {
  1209.             if (!newPayload)
  1210.                 return;
  1211.  
  1212.             self.style = WebInspector.CSSStyleDeclaration.parseStyle(newPayload);
  1213.             self._styleRule.style = self.style;
  1214.  
  1215.             // Set the disabled property here, since the code above replies on it not changing
  1216.             // until after the value and priority are retrieved.
  1217.             self.disabled = disabled;
  1218.  
  1219.             if (self.treeOutline.section && self.treeOutline.section.pane)
  1220.                 self.treeOutline.section.pane.dispatchEventToListeners("style property toggled");
  1221.  
  1222.             self.updateAll(true);
  1223.         }
  1224.  
  1225.         InspectorBackend.toggleStyleEnabled(WebInspector.Callback.wrap(callback), this.style.id, this.name, disabled);
  1226.     },
  1227.  
  1228.     updateState: function()
  1229.     {
  1230.         if (!this.listItemElement)
  1231.             return;
  1232.  
  1233.         if (this.style.isPropertyImplicit(this.name) || this.value === "initial")
  1234.             this.listItemElement.addStyleClass("implicit");
  1235.         else
  1236.             this.listItemElement.removeStyleClass("implicit");
  1237.  
  1238.         if (this.inherited)
  1239.             this.listItemElement.addStyleClass("inherited");
  1240.         else
  1241.             this.listItemElement.removeStyleClass("inherited");
  1242.  
  1243.         if (this.overloaded)
  1244.             this.listItemElement.addStyleClass("overloaded");
  1245.         else
  1246.             this.listItemElement.removeStyleClass("overloaded");
  1247.  
  1248.         if (this.disabled)
  1249.             this.listItemElement.addStyleClass("disabled");
  1250.         else
  1251.             this.listItemElement.removeStyleClass("disabled");
  1252.     },
  1253.  
  1254.     onpopulate: function()
  1255.     {
  1256.         // Only populate once and if this property is a shorthand.
  1257.         if (this.children.length || !this.shorthand)
  1258.             return;
  1259.  
  1260.         var longhandProperties = this.style.getLonghandProperties(this.name);
  1261.         for (var i = 0; i < longhandProperties.length; ++i) {
  1262.             var name = longhandProperties[i];
  1263.  
  1264.             if (this.treeOutline.section) {
  1265.                 var inherited = this.treeOutline.section.isPropertyInherited(name);
  1266.                 var overloaded = this.treeOutline.section.isPropertyOverloaded(name);
  1267.             }
  1268.  
  1269.             var item = new WebInspector.StylePropertyTreeElement(this._styleRule, this.style, name, false, inherited, overloaded);
  1270.             this.appendChild(item);
  1271.         }
  1272.     },
  1273.  
  1274.     ondblclick: function(event)
  1275.     {
  1276.         this.startEditing(event.target);
  1277.         event.stopPropagation();
  1278.     },
  1279.  
  1280.     startEditing: function(selectElement)
  1281.     {
  1282.         // FIXME: we don't allow editing of longhand properties under a shorthand right now.
  1283.         if (this.parent.shorthand)
  1284.             return;
  1285.  
  1286.         if (WebInspector.isBeingEdited(this.listItemElement) || (this.treeOutline.section && !this.treeOutline.section.editable))
  1287.             return;
  1288.  
  1289.         var context = { expanded: this.expanded, hasChildren: this.hasChildren, keyDownListener: this.editingKeyDown.bind(this) };
  1290.  
  1291.         // Lie about our children to prevent expanding on double click and to collapse shorthands.
  1292.         this.hasChildren = false;
  1293.  
  1294.         if (!selectElement)
  1295.             selectElement = this.listItemElement;
  1296.  
  1297.         this.listItemElement.addEventListener("keydown", context.keyDownListener, false);
  1298.  
  1299.         WebInspector.startEditing(this.listItemElement, this.editingCommitted.bind(this), this.editingCancelled.bind(this), context);
  1300.         window.getSelection().setBaseAndExtent(selectElement, 0, selectElement, 1);
  1301.     },
  1302.  
  1303.     editingKeyDown: function(event)
  1304.     {
  1305.         var arrowKeyPressed = (event.keyIdentifier === "Up" || event.keyIdentifier === "Down");
  1306.         var pageKeyPressed = (event.keyIdentifier === "PageUp" || event.keyIdentifier === "PageDown");
  1307.         if (!arrowKeyPressed && !pageKeyPressed)
  1308.             return;
  1309.  
  1310.         var selection = window.getSelection();
  1311.         if (!selection.rangeCount)
  1312.             return;
  1313.  
  1314.         var selectionRange = selection.getRangeAt(0);
  1315.         if (selectionRange.commonAncestorContainer !== this.listItemElement && !selectionRange.commonAncestorContainer.isDescendant(this.listItemElement))
  1316.             return;
  1317.  
  1318.         const styleValueDelimeters = " \t\n\"':;,/()";
  1319.         var wordRange = selectionRange.startContainer.rangeOfWord(selectionRange.startOffset, styleValueDelimeters, this.listItemElement);
  1320.         var wordString = wordRange.toString();
  1321.         var replacementString = wordString;
  1322.  
  1323.         var matches = /(.*?)(-?\d+(?:\.\d+)?)(.*)/.exec(wordString);
  1324.         if (matches && matches.length) {
  1325.             var prefix = matches[1];
  1326.             var number = parseFloat(matches[2]);
  1327.             var suffix = matches[3];
  1328.  
  1329.             // If the number is near zero or the number is one and the direction will take it near zero.
  1330.             var numberNearZero = (number < 1 && number > -1);
  1331.             if (number === 1 && event.keyIdentifier === "Down")
  1332.                 numberNearZero = true;
  1333.             else if (number === -1 && event.keyIdentifier === "Up")
  1334.                 numberNearZero = true;
  1335.  
  1336.             if (numberNearZero && event.altKey && arrowKeyPressed) {
  1337.                 if (event.keyIdentifier === "Down")
  1338.                     number = Math.ceil(number - 1);
  1339.                 else
  1340.                     number = Math.floor(number + 1);
  1341.             } else {
  1342.                 // Jump by 10 when shift is down or jump by 0.1 when near zero or Alt/Option is down.
  1343.                 // Also jump by 10 for page up and down, or by 100 if shift is held with a page key.
  1344.                 var changeAmount = 1;
  1345.                 if (event.shiftKey && pageKeyPressed)
  1346.                     changeAmount = 100;
  1347.                 else if (event.shiftKey || pageKeyPressed)
  1348.                     changeAmount = 10;
  1349.                 else if (event.altKey || numberNearZero)
  1350.                     changeAmount = 0.1;
  1351.  
  1352.                 if (event.keyIdentifier === "Down" || event.keyIdentifier === "PageDown")
  1353.                     changeAmount *= -1;
  1354.  
  1355.                 // Make the new number and constrain it to a precision of 6, this matches numbers the engine returns.
  1356.                 // Use the Number constructor to forget the fixed precision, so 1.100000 will print as 1.1.
  1357.                 number = Number((number + changeAmount).toFixed(6));
  1358.             }
  1359.  
  1360.             replacementString = prefix + number + suffix;
  1361.         } else {
  1362.             // FIXME: this should cycle through known keywords for the current property name.
  1363.             return;
  1364.         }
  1365.  
  1366.         var replacementTextNode = document.createTextNode(replacementString);
  1367.  
  1368.         wordRange.deleteContents();
  1369.         wordRange.insertNode(replacementTextNode);
  1370.  
  1371.         var finalSelectionRange = document.createRange();
  1372.         finalSelectionRange.setStart(replacementTextNode, 0);
  1373.         finalSelectionRange.setEnd(replacementTextNode, replacementString.length);
  1374.  
  1375.         selection.removeAllRanges();
  1376.         selection.addRange(finalSelectionRange);
  1377.  
  1378.         event.preventDefault();
  1379.  
  1380.         if (!this.originalCSSText) {
  1381.             // Remember the rule's original CSS text, so it can be restored
  1382.             // if the editing is canceled and before each apply.
  1383.             this.originalCSSText = this.style.styleTextWithShorthands();
  1384.         } else {
  1385.             // Restore the original CSS text before applying user changes. This is needed to prevent
  1386.             // new properties from sticking around if the user adds one, then removes it.
  1387.             InspectorBackend.setStyleText(WebInspector.Callback.wrap(null), this.style.id, this.originalCSSText);
  1388.         }
  1389.  
  1390.         this.applyStyleText(this.listItemElement.textContent);
  1391.     },
  1392.  
  1393.     editingEnded: function(context)
  1394.     {
  1395.         this.hasChildren = context.hasChildren;
  1396.         if (context.expanded)
  1397.             this.expand();
  1398.         this.listItemElement.removeEventListener("keydown", context.keyDownListener, false);
  1399.         delete this.originalCSSText;
  1400.     },
  1401.  
  1402.     editingCancelled: function(element, context)
  1403.     {
  1404.         if (this._newProperty)
  1405.             this.treeOutline.removeChild(this);
  1406.         else if (this.originalCSSText) {
  1407.             InspectorBackend.setStyleText(WebInspector.Callback.wrap(null), this.style.id, this.originalCSSText);
  1408.  
  1409.             if (this.treeOutline.section && this.treeOutline.section.pane)
  1410.                 this.treeOutline.section.pane.dispatchEventToListeners("style edited");
  1411.  
  1412.             this.updateAll();
  1413.         } else
  1414.             this.updateTitle();
  1415.  
  1416.         this.editingEnded(context);
  1417.     },
  1418.  
  1419.     editingCommitted: function(element, userInput, previousContent, context, moveDirection)
  1420.     {
  1421.         this.editingEnded(context);
  1422.  
  1423.         // Determine where to move to before making changes
  1424.         var newProperty, moveToPropertyName, moveToSelector;
  1425.         var moveTo = (moveDirection === "forward" ? this.nextSibling : this.previousSibling);
  1426.         if (moveTo)
  1427.             moveToPropertyName = moveTo.name;
  1428.         else if (moveDirection === "forward")
  1429.             newProperty = true;
  1430.         else if (moveDirection === "backward" && this.treeOutline.section.rule)
  1431.             moveToSelector = true;
  1432.  
  1433.         // Make the Changes and trigger the moveToNextCallback after updating
  1434.         var blankInput = /^\s*$/.test(userInput);
  1435.         if (userInput !== previousContent || (this._newProperty && blankInput)) { // only if something changed, or adding a new style and it was blank
  1436.             this.treeOutline.section._afterUpdate = moveToNextCallback.bind(this, this._newProperty, !blankInput);
  1437.             this.applyStyleText(userInput, true);
  1438.         } else
  1439.             moveToNextCallback(this._newProperty, false, this.treeOutline.section, false);
  1440.  
  1441.         // The Callback to start editing the next property
  1442.         function moveToNextCallback(alreadyNew, valueChanged, section)
  1443.         {
  1444.             if (!moveDirection)
  1445.                 return;
  1446.  
  1447.             // User just tabbed through without changes
  1448.             if (moveTo && moveTo.parent) {
  1449.                 moveTo.startEditing(moveTo.valueElement);
  1450.                 return;
  1451.             }
  1452.  
  1453.             // User has made a change then tabbed, wiping all the original treeElements,
  1454.             // recalculate the new treeElement for the same property we were going to edit next
  1455.             if (moveTo && !moveTo.parent) {
  1456.                 var treeElement = section.findTreeElementWithName(moveToPropertyName);
  1457.                 if (treeElement)
  1458.                     treeElement.startEditing(treeElement.valueElement);
  1459.                 return;
  1460.             }
  1461.  
  1462.             // Create a new attribute in this section
  1463.             if (newProperty) {
  1464.                 if (alreadyNew && !valueChanged)
  1465.                     return;
  1466.  
  1467.                 section.addNewBlankProperty().startEditing();
  1468.                 return;
  1469.             }
  1470.  
  1471.             if (moveToSelector)
  1472.                 section.startEditingSelector();
  1473.         }
  1474.     },
  1475.  
  1476.     applyStyleText: function(styleText, updateInterface)
  1477.     {
  1478.         var section = this.treeOutline.section;
  1479.         var elementsPanel = WebInspector.panels.elements;
  1480.         styleText = styleText.replace(/\s/g, " ").trim(); // replace   with whitespace.
  1481.         var styleTextLength = styleText.length;
  1482.         if (!styleTextLength && updateInterface) {
  1483.             if (this._newProperty) {
  1484.                 // The user deleted everything, so remove the tree element and update.
  1485.                 this.parent.removeChild(this);
  1486.                 section.afterUpdate();
  1487.                 return;
  1488.             } else {
  1489.                 delete section._afterUpdate;
  1490.             }
  1491.         }
  1492.  
  1493.         var self = this;
  1494.         function callback(success, newPayload, changedProperties)
  1495.         {
  1496.             if (!success) {
  1497.                 // The user typed something, but it didn't parse. Just abort and restore
  1498.                 // the original title for this property.  If this was a new attribute and
  1499.                 // we couldn't parse, then just remove it.
  1500.                 if (self._newProperty) {
  1501.                     self.parent.removeChild(self);
  1502.                     return;
  1503.                 }
  1504.                 if (updateInterface)
  1505.                     self.updateTitle();
  1506.                 return;
  1507.             }
  1508.  
  1509.             elementsPanel.removeStyleChange(section.identifier, self.style, self.name);
  1510.  
  1511.             if (!styleTextLength) {
  1512.                 // Do remove ourselves from UI when the property removal is confirmed.
  1513.                 self.parent.removeChild(self);
  1514.             } else {
  1515.                 self.style = WebInspector.CSSStyleDeclaration.parseStyle(newPayload);
  1516.                 for (var i = 0; i < changedProperties.length; ++i)
  1517.                     elementsPanel.addStyleChange(section.identifier, self.style, changedProperties[i]);
  1518.                 self._styleRule.style = self.style;
  1519.             }
  1520.  
  1521.             if (section && section.pane)
  1522.                 section.pane.dispatchEventToListeners("style edited");
  1523.  
  1524.             if (updateInterface)
  1525.                 self.updateAll(true);
  1526.         }
  1527.         InspectorBackend.applyStyleText(WebInspector.Callback.wrap(callback), this.style.id, styleText, this.name);
  1528.     }
  1529. }
  1530.  
  1531. WebInspector.StylePropertyTreeElement.prototype.__proto__ = TreeElement.prototype;
  1532.