UNPKG

occaecatidicta

Version:
1,254 lines (1,067 loc) 102 kB
/* * Copyright (C) 2007 Apple Inc. All rights reserved. * Copyright (C) 2009 Joseph Pecoraro * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @constructor * @extends {WebInspector.SidebarPane} */ WebInspector.StylesSidebarPane = function(computedStylePane) { WebInspector.SidebarPane.call(this, WebInspector.UIString("Styles")); this.settingsSelectElement = document.createElement("select"); this.settingsSelectElement.className = "select-settings"; var option = document.createElement("option"); option.value = WebInspector.StylesSidebarPane.ColorFormat.Original; option.label = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "As authored" : "As Authored"); this.settingsSelectElement.appendChild(option); option = document.createElement("option"); option.value = WebInspector.StylesSidebarPane.ColorFormat.HEX; option.label = WebInspector.UIString("Hex Colors"); this.settingsSelectElement.appendChild(option); option = document.createElement("option"); option.value = WebInspector.StylesSidebarPane.ColorFormat.RGB; option.label = WebInspector.UIString("RGB Colors"); this.settingsSelectElement.appendChild(option); option = document.createElement("option"); option.value = WebInspector.StylesSidebarPane.ColorFormat.HSL; option.label = WebInspector.UIString("HSL Colors"); this.settingsSelectElement.appendChild(option); // Prevent section from collapsing. var muteEventListener = function(event) { event.consume(true); }; this.settingsSelectElement.addEventListener("click", muteEventListener, true); this.settingsSelectElement.addEventListener("change", this._changeSetting.bind(this), false); this._updateColorFormatFilter(); this.titleElement.appendChild(this.settingsSelectElement); this._elementStateButton = document.createElement("button"); this._elementStateButton.className = "pane-title-button element-state"; this._elementStateButton.title = WebInspector.UIString("Toggle Element State"); this._elementStateButton.addEventListener("click", this._toggleElementStatePane.bind(this), false); this.titleElement.appendChild(this._elementStateButton); var addButton = document.createElement("button"); addButton.className = "pane-title-button add"; addButton.id = "add-style-button-test-id"; addButton.title = WebInspector.UIString("New Style Rule"); addButton.addEventListener("click", this._createNewRule.bind(this), false); this.titleElement.appendChild(addButton); this._computedStylePane = computedStylePane; computedStylePane._stylesSidebarPane = this; this.element.addEventListener("contextmenu", this._contextMenuEventFired.bind(this), true); WebInspector.settings.colorFormat.addChangeListener(this._colorFormatSettingChanged.bind(this)); this._createElementStatePane(); this.bodyElement.appendChild(this._elementStatePane); this._sectionsContainer = document.createElement("div"); this.bodyElement.appendChild(this._sectionsContainer); this._spectrum = new WebInspector.Spectrum(); WebInspector.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetChanged, this._styleSheetOrMediaQueryResultChanged, this); WebInspector.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.MediaQueryResultChanged, this._styleSheetOrMediaQueryResultChanged, this); WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.AttrModified, this._attributesModified, this); WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.AttrRemoved, this._attributesRemoved, this); WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.StyleInvalidated, this._styleInvalidated, this); WebInspector.settings.showUserAgentStyles.addChangeListener(this._showUserAgentStylesSettingChanged.bind(this)); } WebInspector.StylesSidebarPane.ColorFormat = { Original: "original", Nickname: "nickname", HEX: "hex", ShortHEX: "shorthex", RGB: "rgb", RGBA: "rgba", HSL: "hsl", HSLA: "hsla" } WebInspector.StylesSidebarPane.StyleValueDelimiters = " \xA0\t\n\"':;,/()"; // Keep in sync with RenderStyleConstants.h PseudoId enum. Array below contains pseudo id names for corresponding enum indexes. // First item is empty due to its artificial NOPSEUDO nature in the enum. // FIXME: find a way of generating this mapping or getting it from combination of RenderStyleConstants and CSSSelector.cpp at // runtime. WebInspector.StylesSidebarPane.PseudoIdNames = [ "", "first-line", "first-letter", "before", "after", "selection", "", "-webkit-scrollbar", "-webkit-file-upload-button", "-webkit-input-placeholder", "-webkit-slider-thumb", "-webkit-search-cancel-button", "-webkit-search-decoration", "-webkit-search-results-decoration", "-webkit-search-results-button", "-webkit-media-controls-panel", "-webkit-media-controls-play-button", "-webkit-media-controls-mute-button", "-webkit-media-controls-timeline", "-webkit-media-controls-timeline-container", "-webkit-media-controls-volume-slider", "-webkit-media-controls-volume-slider-container", "-webkit-media-controls-current-time-display", "-webkit-media-controls-time-remaining-display", "-webkit-media-controls-seek-back-button", "-webkit-media-controls-seek-forward-button", "-webkit-media-controls-fullscreen-button", "-webkit-media-controls-rewind-button", "-webkit-media-controls-return-to-realtime-button", "-webkit-media-controls-toggle-closed-captions-button", "-webkit-media-controls-status-display", "-webkit-scrollbar-thumb", "-webkit-scrollbar-button", "-webkit-scrollbar-track", "-webkit-scrollbar-track-piece", "-webkit-scrollbar-corner", "-webkit-resizer", "-webkit-input-list-button", "-webkit-inner-spin-button", "-webkit-outer-spin-button" ]; WebInspector.StylesSidebarPane.CSSNumberRegex = /^(-?(?:\d+(?:\.\d+)?|\.\d+))$/; WebInspector.StylesSidebarPane.alteredFloatNumber = function(number, event) { var arrowKeyPressed = (event.keyIdentifier === "Up" || event.keyIdentifier === "Down"); // Jump by 10 when shift is down or jump by 0.1 when Alt/Option is down. // Also jump by 10 for page up and down, or by 100 if shift is held with a page key. var changeAmount = 1; if (event.shiftKey && !arrowKeyPressed) changeAmount = 100; else if (event.shiftKey || !arrowKeyPressed) changeAmount = 10; else if (event.altKey) changeAmount = 0.1; if (event.keyIdentifier === "Down" || event.keyIdentifier === "PageDown") changeAmount *= -1; // Make the new number and constrain it to a precision of 6, this matches numbers the engine returns. // Use the Number constructor to forget the fixed precision, so 1.100000 will print as 1.1. var result = Number((number + changeAmount).toFixed(6)); if (!String(result).match(WebInspector.StylesSidebarPane.CSSNumberRegex)) return null; return result; } WebInspector.StylesSidebarPane.alteredHexNumber = function(hexString, event) { var number = parseInt(hexString, 16); if (isNaN(number) || !isFinite(number)) return hexString; var maxValue = Math.pow(16, hexString.length) - 1; var arrowKeyPressed = (event.keyIdentifier === "Up" || event.keyIdentifier === "Down"); var delta; if (arrowKeyPressed) delta = (event.keyIdentifier === "Up") ? 1 : -1; else delta = (event.keyIdentifier === "PageUp") ? 16 : -16; if (event.shiftKey) delta *= 16; var result = number + delta; if (result < 0) result = 0; // Color hex values are never negative, so clamp to 0. else if (result > maxValue) return hexString; // Ensure the result length is the same as the original hex value. var resultString = result.toString(16).toUpperCase(); for (var i = 0, lengthDelta = hexString.length - resultString.length; i < lengthDelta; ++i) resultString = "0" + resultString; return resultString; } WebInspector.StylesSidebarPane.canonicalPropertyName = function(name) { if (!name || name.length < 9 || name.charAt(0) !== "-") return name; var match = name.match(/(?:-webkit-|-khtml-|-apple-)(.+)/); if (!match) return name; return match[1]; } WebInspector.StylesSidebarPane.prototype = { _contextMenuEventFired: function(event) { var contextMenu = new WebInspector.ContextMenu(); if (WebInspector.populateHrefContextMenu(contextMenu, this.node, event)) contextMenu.show(event); }, get forcedPseudoClasses() { return this._forcedPseudoClasses; }, update: function(node, forceUpdate) { if (this._spectrum.visible) this._spectrum.hide(); var refresh = false; if (forceUpdate) delete this.node; if (!forceUpdate && (node === this.node)) refresh = true; if (node && node.nodeType() === Node.TEXT_NODE && node.parentNode) node = node.parentNode; if (node && node.nodeType() !== Node.ELEMENT_NODE) node = null; if (node) this.node = node; else node = this.node; if (refresh) this._refreshUpdate(); else this._rebuildUpdate(); }, /** * @param {WebInspector.StylePropertiesSection=} editedSection * @param {boolean=} forceFetchComputedStyle * @param {function()=} userCallback */ _refreshUpdate: function(editedSection, forceFetchComputedStyle, userCallback) { if (this._refreshUpdateInProgress) { this._lastNodeForInnerRefresh = this.node; return; } var node = this._validateNode(userCallback); if (!node) return; function computedStyleCallback(computedStyle) { delete this._refreshUpdateInProgress; if (this._lastNodeForInnerRefresh) { delete this._lastNodeForInnerRefresh; this._refreshUpdate(editedSection, forceFetchComputedStyle, userCallback); return; } if (this.node === node && computedStyle) this._innerRefreshUpdate(node, computedStyle, editedSection); if (userCallback) userCallback(); } if (this._computedStylePane.expanded || forceFetchComputedStyle) { this._refreshUpdateInProgress = true; WebInspector.cssModel.getComputedStyleAsync(node.id, this._forcedPseudoClasses, computedStyleCallback.bind(this)); } else { this._innerRefreshUpdate(node, null, editedSection); if (userCallback) userCallback(); } }, /** * @param {function()=} userCallback */ _rebuildUpdate: function(userCallback) { if (this._rebuildUpdateInProgress) { this._lastNodeForInnerRebuild = this.node; return; } var node = this._validateNode(userCallback); if (!node) return; this._rebuildUpdateInProgress = true; var resultStyles = {}; function stylesCallback(matchedResult) { delete this._rebuildUpdateInProgress; if (this._lastNodeForInnerRebuild) { delete this._lastNodeForInnerRebuild; this._rebuildUpdate(userCallback); return; } if (matchedResult && this.node === node) { resultStyles.matchedCSSRules = matchedResult.matchedCSSRules; resultStyles.pseudoElements = matchedResult.pseudoElements; resultStyles.inherited = matchedResult.inherited; this._innerRebuildUpdate(node, resultStyles); } if (userCallback) userCallback(); } function inlineCallback(inlineStyle, attributesStyle) { resultStyles.inlineStyle = inlineStyle; resultStyles.attributesStyle = attributesStyle; } function computedCallback(computedStyle) { resultStyles.computedStyle = computedStyle; } if (this._computedStylePane.expanded) WebInspector.cssModel.getComputedStyleAsync(node.id, this._forcedPseudoClasses, computedCallback.bind(this)); WebInspector.cssModel.getInlineStylesAsync(node.id, inlineCallback.bind(this)); WebInspector.cssModel.getMatchedStylesAsync(node.id, this._forcedPseudoClasses, true, true, stylesCallback.bind(this)); }, _validateNode: function(userCallback) { if (!this.node) { this._sectionsContainer.removeChildren(); this._computedStylePane.bodyElement.removeChildren(); this.sections = {}; if (userCallback) userCallback(); return null; } return this.node; }, _styleSheetOrMediaQueryResultChanged: function() { if (this._userOperation || this._isEditingStyle) return; this._rebuildUpdate(); }, _attributesModified: function(event) { if (this.node !== event.data.node) return; // Changing style attribute will anyways generate _styleInvalidated message. if (event.data.name === "style") return; // "class" (or any other) attribute might have changed. Update styles unless they are being edited. if (!this._isEditingStyle && !this._userOperation) this._rebuildUpdate(); }, _attributesRemoved: function(event) { if (this.node !== event.data.node) return; // "style" attribute might have been removed. if (!this._isEditingStyle && !this._userOperation) this._rebuildUpdate(); }, _styleInvalidated: function(event) { if (this.node !== event.data) return; if (!this._isEditingStyle && !this._userOperation) this._rebuildUpdate(); }, _innerRefreshUpdate: function(node, computedStyle, editedSection) { for (var pseudoId in this.sections) { var styleRules = this._refreshStyleRules(this.sections[pseudoId], computedStyle); var usedProperties = {}; this._markUsedProperties(styleRules, usedProperties); this._refreshSectionsForStyleRules(styleRules, usedProperties, editedSection); } if (computedStyle) this.sections[0][0].rebuildComputedTrace(this.sections[0]); this._nodeStylesUpdatedForTest(node, false); }, _innerRebuildUpdate: function(node, styles) { this._sectionsContainer.removeChildren(); this._computedStylePane.bodyElement.removeChildren(); var styleRules = this._rebuildStyleRules(node, styles); var usedProperties = {}; this._markUsedProperties(styleRules, usedProperties); this.sections[0] = this._rebuildSectionsForStyleRules(styleRules, usedProperties, 0, null); var anchorElement = this.sections[0].inheritedPropertiesSeparatorElement; if (styles.computedStyle) this.sections[0][0].rebuildComputedTrace(this.sections[0]); for (var i = 0; i < styles.pseudoElements.length; ++i) { var pseudoElementCSSRules = styles.pseudoElements[i]; styleRules = []; var pseudoId = pseudoElementCSSRules.pseudoId; var entry = { isStyleSeparator: true, pseudoId: pseudoId }; styleRules.push(entry); // Add rules in reverse order to match the cascade order. for (var j = pseudoElementCSSRules.rules.length - 1; j >= 0; --j) { var rule = pseudoElementCSSRules.rules[j]; styleRules.push({ style: rule.style, selectorText: rule.selectorText, media: rule.media, sourceURL: rule.sourceURL, rule: rule, editable: !!(rule.style && rule.style.id) }); } usedProperties = {}; this._markUsedProperties(styleRules, usedProperties); this.sections[pseudoId] = this._rebuildSectionsForStyleRules(styleRules, usedProperties, pseudoId, anchorElement); } this._nodeStylesUpdatedForTest(node, true); }, _nodeStylesUpdatedForTest: function(node, rebuild) { // Tests override this method. }, _refreshStyleRules: function(sections, computedStyle) { var nodeComputedStyle = computedStyle; var styleRules = []; for (var i = 0; sections && i < sections.length; ++i) { var section = sections[i]; if (section.isBlank) continue; if (section.computedStyle) section.styleRule.style = nodeComputedStyle; var styleRule = { section: section, style: section.styleRule.style, computedStyle: section.computedStyle, rule: section.rule, editable: !!(section.styleRule.style && section.styleRule.style.id) }; styleRules.push(styleRule); } return styleRules; }, _rebuildStyleRules: function(node, styles) { var nodeComputedStyle = styles.computedStyle; this.sections = {}; var styleRules = []; function addAttributesStyle() { if (!styles.attributesStyle) return; var attrStyle = { style: styles.attributesStyle, editable: false }; attrStyle.selectorText = node.nodeNameInCorrectCase() + "[" + WebInspector.UIString("Attributes Style") + "]"; styleRules.push(attrStyle); } styleRules.push({ computedStyle: true, selectorText: "", style: nodeComputedStyle, editable: false }); // Inline style has the greatest specificity. if (styles.inlineStyle && node.nodeType() === Node.ELEMENT_NODE) { var inlineStyle = { selectorText: "element.style", style: styles.inlineStyle, isAttribute: true }; styleRules.push(inlineStyle); } // Add rules in reverse order to match the cascade order. if (styles.matchedCSSRules.length) styleRules.push({ isStyleSeparator: true, text: WebInspector.UIString("Matched CSS Rules") }); var addedAttributesStyle; for (var i = styles.matchedCSSRules.length - 1; i >= 0; --i) { var rule = styles.matchedCSSRules[i]; if (!WebInspector.settings.showUserAgentStyles.get() && (rule.isUser || rule.isUserAgent)) continue; if ((rule.isUser || rule.isUserAgent) && !addedAttributesStyle) { // Show element's Style Attributes after all author rules. addedAttributesStyle = true; addAttributesStyle(); } styleRules.push({ style: rule.style, selectorText: rule.selectorText, media: rule.media, sourceURL: rule.sourceURL, rule: rule, editable: !!(rule.style && rule.style.id) }); } if (!addedAttributesStyle) addAttributesStyle(); // Walk the node structure and identify styles with inherited properties. var parentNode = node.parentNode; function insertInheritedNodeSeparator(node) { var entry = {}; entry.isStyleSeparator = true; entry.node = node; styleRules.push(entry); } for (var parentOrdinal = 0; parentOrdinal < styles.inherited.length; ++parentOrdinal) { var parentStyles = styles.inherited[parentOrdinal]; var separatorInserted = false; if (parentStyles.inlineStyle) { if (this._containsInherited(parentStyles.inlineStyle)) { var inlineStyle = { selectorText: WebInspector.UIString("Style Attribute"), style: parentStyles.inlineStyle, isAttribute: true, isInherited: true }; if (!separatorInserted) { insertInheritedNodeSeparator(parentNode); separatorInserted = true; } styleRules.push(inlineStyle); } } for (var i = parentStyles.matchedCSSRules.length - 1; i >= 0; --i) { var rulePayload = parentStyles.matchedCSSRules[i]; if (!this._containsInherited(rulePayload.style)) continue; var rule = rulePayload; if (!WebInspector.settings.showUserAgentStyles.get() && (rule.isUser || rule.isUserAgent)) continue; if (!separatorInserted) { insertInheritedNodeSeparator(parentNode); separatorInserted = true; } styleRules.push({ style: rule.style, selectorText: rule.selectorText, media: rule.media, sourceURL: rule.sourceURL, rule: rule, isInherited: true, editable: !!(rule.style && rule.style.id) }); } parentNode = parentNode.parentNode; } return styleRules; }, _markUsedProperties: function(styleRules, usedProperties) { var priorityUsed = false; // Walk the style rules and make a list of all used and overloaded properties. for (var i = 0; i < styleRules.length; ++i) { var styleRule = styleRules[i]; if (styleRule.computedStyle || styleRule.isStyleSeparator) continue; if (styleRule.section && styleRule.section.noAffect) continue; styleRule.usedProperties = {}; var style = styleRule.style; var allProperties = style.allProperties; for (var j = 0; j < allProperties.length; ++j) { var property = allProperties[j]; if (!property.isLive || !property.parsedOk) continue; var canonicalName = WebInspector.StylesSidebarPane.canonicalPropertyName(property.name); if (!priorityUsed && property.priority.length) priorityUsed = true; // If the property name is already used by another rule then this rule's // property is overloaded, so don't add it to the rule's usedProperties. if (!(canonicalName in usedProperties)) styleRule.usedProperties[canonicalName] = true; } // Add all the properties found in this style to the used properties list. // Do this here so only future rules are affect by properties used in this rule. for (var canonicalName in styleRules[i].usedProperties) usedProperties[canonicalName] = true; } if (priorityUsed) { // Walk the properties again and account for !important. var foundPriorityProperties = {}; // Walk in direct order to detect the active/most specific rule providing a priority // (in this case all subsequent !important values get canceled.) for (var i = 0; i < styleRules.length; ++i) { if (styleRules[i].computedStyle || styleRules[i].isStyleSeparator) continue; var style = styleRules[i].style; var allProperties = style.allProperties; for (var j = 0; j < allProperties.length; ++j) { var property = allProperties[j]; if (!property.isLive) continue; var canonicalName = WebInspector.StylesSidebarPane.canonicalPropertyName(property.name); if (property.priority.length) { if (!(canonicalName in foundPriorityProperties)) styleRules[i].usedProperties[canonicalName] = true; else delete styleRules[i].usedProperties[canonicalName]; foundPriorityProperties[canonicalName] = true; } else if (canonicalName in foundPriorityProperties) delete styleRules[i].usedProperties[canonicalName]; } } } }, _refreshSectionsForStyleRules: function(styleRules, usedProperties, editedSection) { // Walk the style rules and update the sections with new overloaded and used properties. for (var i = 0; i < styleRules.length; ++i) { var styleRule = styleRules[i]; var section = styleRule.section; if (styleRule.computedStyle) { section._usedProperties = usedProperties; section.update(); } else { section._usedProperties = styleRule.usedProperties; section.update(section === editedSection); } } }, _rebuildSectionsForStyleRules: function(styleRules, usedProperties, pseudoId, anchorElement) { // Make a property section for each style rule. var sections = []; var lastWasSeparator = true; for (var i = 0; i < styleRules.length; ++i) { var styleRule = styleRules[i]; if (styleRule.isStyleSeparator) { var separatorElement = document.createElement("div"); separatorElement.className = "sidebar-separator"; if (styleRule.node) { var link = WebInspector.DOMPresentationUtils.linkifyNodeReference(styleRule.node); separatorElement.appendChild(document.createTextNode(WebInspector.UIString("Inherited from") + " ")); separatorElement.appendChild(link); if (!sections.inheritedPropertiesSeparatorElement) sections.inheritedPropertiesSeparatorElement = separatorElement; } else if ("pseudoId" in styleRule) { var pseudoName = WebInspector.StylesSidebarPane.PseudoIdNames[styleRule.pseudoId]; if (pseudoName) separatorElement.textContent = WebInspector.UIString("Pseudo ::%s element", pseudoName); else separatorElement.textContent = WebInspector.UIString("Pseudo element"); } else separatorElement.textContent = styleRule.text; this._sectionsContainer.insertBefore(separatorElement, anchorElement); lastWasSeparator = true; continue; } var computedStyle = styleRule.computedStyle; // Default editable to true if it was omitted. var editable = styleRule.editable; if (typeof editable === "undefined") editable = true; if (computedStyle) var section = new WebInspector.ComputedStylePropertiesSection(styleRule, usedProperties); else var section = new WebInspector.StylePropertiesSection(this, styleRule, editable, styleRule.isInherited, lastWasSeparator); section.pane = this; section.expanded = true; if (computedStyle) { this._computedStylePane.bodyElement.appendChild(section.element); lastWasSeparator = true; } else { this._sectionsContainer.insertBefore(section.element, anchorElement); lastWasSeparator = false; } sections.push(section); } return sections; }, _containsInherited: function(style) { var properties = style.allProperties; for (var i = 0; i < properties.length; ++i) { var property = properties[i]; // Does this style contain non-overridden inherited property? if (property.isLive && property.name in WebInspector.CSSKeywordCompletions.InheritedProperties) return true; } return false; }, _colorFormatSettingChanged: function(event) { this._updateColorFormatFilter(); for (var pseudoId in this.sections) { var sections = this.sections[pseudoId]; for (var i = 0; i < sections.length; ++i) sections[i].update(true); } }, _updateColorFormatFilter: function() { // Select the correct color format setting again, since it needs to be selected. var selectedIndex = 0; var value = WebInspector.settings.colorFormat.get(); var options = this.settingsSelectElement.options; for (var i = 0; i < options.length; ++i) { if (options[i].value === value) { selectedIndex = i; break; } } this.settingsSelectElement.selectedIndex = selectedIndex; }, _changeSetting: function(event) { var options = this.settingsSelectElement.options; var selectedOption = options[this.settingsSelectElement.selectedIndex]; WebInspector.settings.colorFormat.set(selectedOption.value); }, _createNewRule: function(event) { event.consume(); this.expanded = true; this.addBlankSection().startEditingSelector(); }, addBlankSection: function() { var blankSection = new WebInspector.BlankStylePropertiesSection(this, this.node ? this.node.appropriateSelectorFor(true) : ""); blankSection.pane = this; var elementStyleSection = this.sections[0][1]; this._sectionsContainer.insertBefore(blankSection.element, elementStyleSection.element.nextSibling); this.sections[0].splice(2, 0, blankSection); return blankSection; }, removeSection: function(section) { for (var pseudoId in this.sections) { var sections = this.sections[pseudoId]; var index = sections.indexOf(section); if (index === -1) continue; sections.splice(index, 1); if (section.element.parentNode) section.element.parentNode.removeChild(section.element); } }, registerShortcuts: function() { var section = WebInspector.shortcutsScreen.section(WebInspector.UIString("Styles Pane")); var shortcut = WebInspector.KeyboardShortcut; var keys = [ shortcut.shortcutToString(shortcut.Keys.Tab), shortcut.shortcutToString(shortcut.Keys.Tab, shortcut.Modifiers.Shift) ]; section.addRelatedKeys(keys, WebInspector.UIString("Next/previous property")); keys = [ shortcut.shortcutToString(shortcut.Keys.Up), shortcut.shortcutToString(shortcut.Keys.Down) ]; section.addRelatedKeys(keys, WebInspector.UIString("Increment/decrement value")); keys = [ shortcut.shortcutToString(shortcut.Keys.Up, shortcut.Modifiers.Shift), shortcut.shortcutToString(shortcut.Keys.Down, shortcut.Modifiers.Shift) ]; section.addRelatedKeys(keys, WebInspector.UIString("Increment/decrement by %f", 10)); keys = [ shortcut.shortcutToString(shortcut.Keys.PageUp), shortcut.shortcutToString(shortcut.Keys.PageDown) ]; section.addRelatedKeys(keys, WebInspector.UIString("Increment/decrement by %f", 10)); keys = [ shortcut.shortcutToString(shortcut.Keys.PageUp, shortcut.Modifiers.Shift), shortcut.shortcutToString(shortcut.Keys.PageDown, shortcut.Modifiers.Shift) ]; section.addRelatedKeys(keys, WebInspector.UIString("Increment/decrement by %f", 100)); keys = [ shortcut.shortcutToString(shortcut.Keys.PageUp, shortcut.Modifiers.Alt), shortcut.shortcutToString(shortcut.Keys.PageDown, shortcut.Modifiers.Alt) ]; section.addRelatedKeys(keys, WebInspector.UIString("Increment/decrement by %f", 0.1)); }, _toggleElementStatePane: function(event) { event.consume(); if (!this._elementStateButton.hasStyleClass("toggled")) { this.expand(); this._elementStateButton.addStyleClass("toggled"); this._elementStatePane.addStyleClass("expanded"); } else { this._elementStateButton.removeStyleClass("toggled"); this._elementStatePane.removeStyleClass("expanded"); // Clear flags on hide. if (this._forcedPseudoClasses) { for (var i = 0; i < this._elementStatePane.inputs.length; ++i) this._elementStatePane.inputs[i].checked = false; delete this._forcedPseudoClasses; this._rebuildUpdate(); } } }, _createElementStatePane: function() { this._elementStatePane = document.createElement("div"); this._elementStatePane.className = "styles-element-state-pane source-code"; var table = document.createElement("table"); var inputs = []; this._elementStatePane.inputs = inputs; function clickListener(event) { var pseudoClasses = []; for (var i = 0; i < inputs.length; ++i) { if (inputs[i].checked) pseudoClasses.push(inputs[i].state); } this._forcedPseudoClasses = pseudoClasses.length ? pseudoClasses : undefined; this._rebuildUpdate(); } function createCheckbox(state) { var td = document.createElement("td"); var label = document.createElement("label"); var input = document.createElement("input"); input.type = "checkbox"; input.state = state; input.addEventListener("click", clickListener.bind(this), false); inputs.push(input); label.appendChild(input); label.appendChild(document.createTextNode(":" + state)); td.appendChild(label); return td; } var tr = document.createElement("tr"); tr.appendChild(createCheckbox.call(this, "active")); tr.appendChild(createCheckbox.call(this, "hover")); table.appendChild(tr); tr = document.createElement("tr"); tr.appendChild(createCheckbox.call(this, "focus")); tr.appendChild(createCheckbox.call(this, "visited")); table.appendChild(tr); this._elementStatePane.appendChild(table); }, _showUserAgentStylesSettingChanged: function() { this._rebuildUpdate(); }, willHide: function() { if (this._spectrum.visible) this._spectrum.hide(); } } WebInspector.StylesSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype; /** * @constructor * @extends {WebInspector.SidebarPane} */ WebInspector.ComputedStyleSidebarPane = function() { WebInspector.SidebarPane.call(this, WebInspector.UIString("Computed Style")); var showInheritedCheckbox = new WebInspector.Checkbox(WebInspector.UIString("Show inherited"), "sidebar-pane-subtitle"); this.titleElement.appendChild(showInheritedCheckbox.element); if (WebInspector.settings.showInheritedComputedStyleProperties.get()) { this.bodyElement.addStyleClass("show-inherited"); showInheritedCheckbox.checked = true; } function showInheritedToggleFunction(event) { WebInspector.settings.showInheritedComputedStyleProperties.set(showInheritedCheckbox.checked); if (WebInspector.settings.showInheritedComputedStyleProperties.get()) this.bodyElement.addStyleClass("show-inherited"); else this.bodyElement.removeStyleClass("show-inherited"); } showInheritedCheckbox.addEventListener(showInheritedToggleFunction.bind(this)); } WebInspector.ComputedStyleSidebarPane.prototype = { // Overriding expand() rather than onexpand() to eliminate the visual slowness due to a possible backend trip. expand: function() { function callback() { WebInspector.SidebarPane.prototype.expand.call(this); } this._stylesSidebarPane._refreshUpdate(null, true, callback.bind(this)); } } WebInspector.ComputedStyleSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype; /** * @constructor * @extends {WebInspector.PropertiesSection} */ WebInspector.StylePropertiesSection = function(parentPane, styleRule, editable, isInherited, isFirstSection) { WebInspector.PropertiesSection.call(this, ""); this.element.className = "styles-section matched-styles monospace" + (isFirstSection ? " first-styles-section" : ""); if (styleRule.media) { for (var i = styleRule.media.length - 1; i >= 0; --i) { var media = styleRule.media[i]; var mediaDataElement = this.titleElement.createChild("div", "media"); var mediaText; switch (media.source) { case WebInspector.CSSMedia.Source.LINKED_SHEET: case WebInspector.CSSMedia.Source.INLINE_SHEET: mediaText = "media=\"" + media.text + "\""; break; case WebInspector.CSSMedia.Source.MEDIA_RULE: mediaText = "@media " + media.text; break; case WebInspector.CSSMedia.Source.IMPORT_RULE: mediaText = "@import " + media.text; break; } if (media.sourceURL) { var refElement = mediaDataElement.createChild("div", "subtitle"); var lineNumber = media.sourceLine < 0 ? undefined : media.sourceLine; var anchor = WebInspector.linkifyResourceAsNode(media.sourceURL, lineNumber, "subtitle", media.sourceURL + (isNaN(lineNumber) ? "" : (":" + (lineNumber + 1)))); anchor.style.float = "right"; refElement.appendChild(anchor); } var mediaTextElement = mediaDataElement.createChild("span"); mediaTextElement.textContent = mediaText; mediaTextElement.title = media.text; } } var selectorContainer = document.createElement("div"); this._selectorElement = document.createElement("span"); this._selectorElement.textContent = styleRule.selectorText; selectorContainer.appendChild(this._selectorElement); var openBrace = document.createElement("span"); openBrace.textContent = " {"; selectorContainer.appendChild(openBrace); selectorContainer.addEventListener("mousedown", this._handleEmptySpaceMouseDown.bind(this), false); selectorContainer.addEventListener("click", this._handleSelectorContainerClick.bind(this), false); var closeBrace = document.createElement("div"); closeBrace.textContent = "}"; this.element.appendChild(closeBrace); this._selectorElement.addEventListener("click", this._handleSelectorClick.bind(this), false); this.element.addEventListener("mousedown", this._handleEmptySpaceMouseDown.bind(this), false); this.element.addEventListener("click", this._handleEmptySpaceClick.bind(this), false); this._parentPane = parentPane; this.styleRule = styleRule; this.rule = this.styleRule.rule; this.editable = editable; this.isInherited = isInherited; if (this.rule) { // Prevent editing the user agent and user rules. if (this.rule.isUserAgent || this.rule.isUser) this.editable = false; this.titleElement.addStyleClass("styles-selector"); } this._usedProperties = styleRule.usedProperties; this._selectorRefElement = document.createElement("div"); this._selectorRefElement.className = "subtitle"; this._selectorRefElement.appendChild(this._createRuleOriginNode()); selectorContainer.insertBefore(this._selectorRefElement, selectorContainer.firstChild); this.titleElement.appendChild(selectorContainer); this._selectorContainer = selectorContainer; if (isInherited) this.element.addStyleClass("show-inherited"); // This one is related to inherited rules, not compted style. if (!this.editable) this.element.addStyleClass("read-only"); } WebInspector.StylePropertiesSection.prototype = { collapse: function(dontRememberState) { // Overriding with empty body. }, isPropertyInherited: function(propertyName) { if (this.isInherited) { // While rendering inherited stylesheet, reverse meaning of this property. // Render truly inherited properties with black, i.e. return them as non-inherited. return !(propertyName in WebInspector.CSSKeywordCompletions.InheritedProperties); } return false; }, isPropertyOverloaded: function(propertyName, shorthand) { if (!this._usedProperties || this.noAffect) return false; if (this.isInherited && !(propertyName in WebInspector.CSSKeywordCompletions.InheritedProperties)) { // In the inherited sections, only show overrides for the potentially inherited properties. return false; } var canonicalName = WebInspector.StylesSidebarPane.canonicalPropertyName(propertyName); var used = (canonicalName in this._usedProperties); if (used || !shorthand) return !used; // Find out if any of the individual longhand properties of the shorthand // are used, if none are then the shorthand is overloaded too. var longhandProperties = this.styleRule.style.getLonghandProperties(propertyName); for (var j = 0; j < longhandProperties.length; ++j) { var individualProperty = longhandProperties[j]; if (WebInspector.StylesSidebarPane.canonicalPropertyName(individualProperty.name) in this._usedProperties) return false; } return true; }, nextEditableSibling: function() { var curSection = this; do { curSection = curSection.nextSibling; } while (curSection && !curSection.editable); if (!curSection) { curSection = this.firstSibling; while (curSection && !curSection.editable) curSection = curSection.nextSibling; } return (curSection && curSection.editable) ? curSection : null; }, previousEditableSibling: function() { var curSection = this; do { curSection = curSection.previousSibling; } while (curSection && !curSection.editable); if (!curSection) { curSection = this.lastSibling; while (curSection && !curSection.editable) curSection = curSection.previousSibling; } return (curSection && curSection.editable) ? curSection : null; }, update: function(full) { if (full) { this.propertiesTreeOutline.removeChildren(); this.populated = false; } else { var child = this.propertiesTreeOutline.children[0]; while (child) { child.overloaded = this.isPropertyOverloaded(child.name, child.shorthand); child = child.traverseNextTreeElement(false, null, true); } } this.afterUpdate(); }, afterUpdate: function() { if (this._afterUpdate) { this._afterUpdate(this); delete this._afterUpdate; } }, onpopulate: function() { var style = this.styleRule.style; var handledProperties = {}; var shorthandNames = {}; this.uniqueProperties = []; var allProperties = style.allProperties; for (var i = 0; i < allProperties.length; ++i) this.uniqueProperties.push(allProperties[i]); // Collect all shorthand names. for (var i = 0; i < this.uniqueProperties.length; ++i) { var property = this.uniqueProperties[i]; if (property.disabled) continue; if (property.shorthand) shorthandNames[property.shorthand] = true; } // Create property tree elements. for (var i = 0; i < this.uniqueProperties.length; ++i) { var property = this.uniqueProperties[i]; var disabled = property.disabled; var shorthand = !disabled ? property.shorthand : null; if (shorthand && shorthand in handledProperties) continue; if (shorthand) { property = style.getLiveProperty(shorthand); if (!property) property = new WebInspector.CSSProperty(style, style.allProperties.length, shorthand, style.getShorthandValue(shorthand), style.getShorthandPriority(shorthand), "style", true, true, "", undefined); } // BUG71275: Never show purely style-based properties in editable rules. if (!shorthand && this.editable && property.styleBased) continue; var isShorthand = !!(property.isLive && (shorthand || shorthandNames[property.name])); var inherited = this.isPropertyInherited(property.name); var overloaded = this.isPropertyOverloaded(property.name, isShorthand); var item = new WebInspector.StylePropertyTreeElement(this, this._parentPane, this.styleRule, style, property, isShorthand, inherited, overloaded); this.propertiesTreeOutline.appendChild(item); handledProperties[property.name] = property; } }, findTreeElementWithName: function(name) { var treeElement = this.propertiesTreeOutline.children[0]; while (treeElement) { if (treeElement.name === name) return treeElement; treeElement = treeElement.traverseNextTreeElement(true, null, true); } return null; }, _checkWillCancelEditing: function() { var willCauseCancelEditing = this._willCauseCancelEditing; delete this._willCauseCancelEditing; return willCauseCancelEditing; }, _handleSelectorContainerClick: function(event) { if (this._checkWillCancelEditing()) return; if (event.target === this._selectorContainer) this.addNewBlankProperty(0).startEditing(); }, /** * @param {number=} index */ addNewBlankProperty: function(index) { var style = this.styleRule.style; var property = style.newBlankProperty(index); var item = new WebInspector.StylePropertyTreeElement(this, this._parentPane, this.styleRule, style, property, false, false, false); index = property.index; this.propertiesTreeOutline.insertChild(item, index); item.listItemElement.textContent = ""; item._newProperty = true; item.updateTitle(); return item; }, _createRuleOriginNode: function() { function linkifyUncopyable(url, line) { var link = WebInspector.linkifyResourceAsNode(url, line, "", url + ":" + (line + 1)); link.classList.add("webkit-html-resource-link"); link.setAttribute("data-uncopyable", link.textContent); link.textContent = ""; return link; } if (this.styleRule.sourceURL) return linkifyUncopyable(this.styleRule.sourceURL, this.rule.sourceLine); if (!this.rule) return document.createTextNode(""); var origin = ""; if (this.rule.isUserAgent) origin = WebInspector.UIString("user agent stylesheet"); else if (this.rule.isUser) origin = WebInspector.UIString("user stylesheet"); else if (this.rule.isViaInspector) origin = WebInspector.UIString("via inspector"); return document.createTextNode(origin); }, _handleEmptySpaceMouseDown: function(event) { this._willCauseCancelEditing = this._parentPane._isEditingStyle; }, _handleEmptySpaceClick: function(event) { if (!this.editable) return; if (this._checkWillCancelEditing()) return; if (event.target.hasStyleClass("header") || this.element.hasStyleClass("read-only") || event.target.enclosingNodeOrSelfWithClass("media")) { event.consume(); return; } this.expand(); this.addNewBlankProperty()