/* * Copyright (C) 2013 Apple Inc. All rights reserved. * * 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. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. */ WebInspector.RulesStyleDetailsPanel = function() { WebInspector.StyleDetailsPanel.call(this, WebInspector.RulesStyleDetailsPanel.StyleClassName, "rules", WebInspector.UIString("Rules")); this._sections = []; }; WebInspector.RulesStyleDetailsPanel.StyleClassName = "rules"; WebInspector.RulesStyleDetailsPanel.LabelElementStyleClassName = "label"; WebInspector.RulesStyleDetailsPanel.NewRuleElementStyleClassName = "new-rule"; WebInspector.RulesStyleDetailsPanel.prototype = { constructor: WebInspector.RulesStyleDetailsPanel, // Public refresh: function(significantChange) { // We only need to do a rebuild on significant changes. Other changes are handled // by the sections and text editors themselves. if (!significantChange) return; var newSections = []; var newDOMFragment = document.createDocumentFragment(); var previousMediaList = []; var previousSection = null; var previousFocusedSection = null; function mediaListsEqual(a, b) { a = a || []; b = b || []; if (a.length !== b.length) return false; for (var i = 0; i < a.length; ++i) { var aMedia = a[i]; var bMedia = b[i]; if (aMedia.type !== bMedia.type) return false; if (aMedia.text !== bMedia.text) return false; if (!aMedia.sourceCodeLocation && bMedia.sourceCodeLocation) return false; if (aMedia.sourceCodeLocation && !aMedia.sourceCodeLocation.isEqual(bMedia.sourceCodeLocation)) return false; } return true; } function filteredMediaList(mediaList) { if (!mediaList) return []; // Exclude the basic "screen" query since it's very common and just clutters things. return mediaList.filter(function(media) { return media.text !== "screen"; }); } function appendStyleSection(style) { var section = style.__rulesSection; if (section && section.focused && !previousFocusedSection) previousFocusedSection = section; if (!section) { section = new WebInspector.CSSStyleDeclarationSection(style); style.__rulesSection = section; } else section.refresh(); if (this._focusNextNewInspectorRule && style.ownerRule && style.ownerRule.type === WebInspector.CSSRule.Type.Inspector) { previousFocusedSection = section; delete this._focusNextNewInspectorRule; } // Reset lastInGroup in case the order/grouping changed. section.lastInGroup = false; newDOMFragment.appendChild(section.element); newSections.push(section); previousSection = section; } function addNewRuleButton() { if (previousSection) previousSection.lastInGroup = true; var newRuleButton = document.createElement("div"); newRuleButton.className = WebInspector.RulesStyleDetailsPanel.NewRuleElementStyleClassName; newRuleButton.addEventListener("click", this._newRuleClicked.bind(this)); newRuleButton.appendChild(document.createElement("img")); newRuleButton.appendChild(document.createTextNode(WebInspector.UIString("New Rule"))); newDOMFragment.appendChild(newRuleButton); addedNewRuleButton = true; } var pseudoElements = this.nodeStyles.pseudoElements; for (var pseudoIdentifier in pseudoElements) { var pseudoElement = pseudoElements[pseudoIdentifier]; for (var i = 0; i < pseudoElement.orderedStyles.length; ++i) { var style = pseudoElement.orderedStyles[i]; appendStyleSection.call(this, style); } if (previousSection) previousSection.lastInGroup = true; } var addedNewRuleButton = false; var orderedStyles = this.nodeStyles.orderedStyles; for (var i = 0; i < orderedStyles.length; ++i) { var style = orderedStyles[i]; if (style.type === WebInspector.CSSStyleDeclaration.Type.Rule && !addedNewRuleButton) addNewRuleButton.call(this); if (previousSection && previousSection.style.node !== style.node) { previousSection.lastInGroup = true; var prefixElement = document.createElement("strong"); prefixElement.textContent = WebInspector.UIString("Inherited From: "); var inheritedLabel = document.createElement("div"); inheritedLabel.className = WebInspector.RulesStyleDetailsPanel.LabelElementStyleClassName; inheritedLabel.appendChild(prefixElement); inheritedLabel.appendChild(WebInspector.linkifyNodeReference(style.node)); newDOMFragment.appendChild(inheritedLabel); } // Only include the media list if it is different from the previous media list shown. var currentMediaList = filteredMediaList(style.ownerRule && style.ownerRule.mediaList); if (!mediaListsEqual(previousMediaList, currentMediaList)) { previousMediaList = currentMediaList; // Break the section group even if the media list is empty. That way the user knows // the previous displayed media list does not apply to the next section. if (previousSection) previousSection.lastInGroup = true; for (var j = 0; j < currentMediaList.length; ++j) { var media = currentMediaList[j]; var prefixElement = document.createElement("strong"); prefixElement.textContent = WebInspector.UIString("Media: "); var mediaLabel = document.createElement("div"); mediaLabel.className = WebInspector.RulesStyleDetailsPanel.LabelElementStyleClassName; mediaLabel.appendChild(prefixElement); mediaLabel.appendChild(document.createTextNode(media.text)); if (media.sourceCodeLocation) { mediaLabel.appendChild(document.createTextNode(" \u2014 ")); mediaLabel.appendChild(WebInspector.createSourceCodeLocationLink(media.sourceCodeLocation, true)); } newDOMFragment.appendChild(mediaLabel); } } appendStyleSection.call(this, style); } if (!addedNewRuleButton) addNewRuleButton.call(this); if (previousSection) previousSection.lastInGroup = true; this.element.removeChildren(); this.element.appendChild(newDOMFragment); this._sections = newSections; for (var i = 0; i < this._sections.length; ++i) this._sections[i].updateLayout(); if (previousFocusedSection) { previousFocusedSection.focus(); function scrollToFocusedSection() { previousFocusedSection.element.scrollIntoViewIfNeeded(true); } // Do the scroll on a timeout since StyleDetailsPanel restores scroll position // after the refresh, and we might not need to scroll after the restore. setTimeout(scrollToFocusedSection, 0); } }, // Protected shown: function() { WebInspector.StyleDetailsPanel.prototype.shown.call(this); // Associate the style and section objects so they can be reused. // Also update the layout in case we changed widths while hidden. for (var i = 0; i < this._sections.length; ++i) { var section = this._sections[i]; section.style.__rulesSection = section; section.updateLayout(); } }, hidden: function() { WebInspector.StyleDetailsPanel.prototype.hidden.call(this); // Disconnect the style and section objects so they have a chance // to release their objects when this panel is not visible. for (var i = 0; i < this._sections.length; ++i) delete this._sections[i].style.__rulesSection; }, widthDidChange: function() { for (var i = 0; i < this._sections.length; ++i) this._sections[i].updateLayout(); }, // Private _newRuleClicked: function(event) { this._focusNextNewInspectorRule = true; this.nodeStyles.addRule(); } }; WebInspector.RulesStyleDetailsPanel.prototype.__proto__ = WebInspector.StyleDetailsPanel.prototype;