blob: 0fd114e5a56c88b2c090b1cf022d761626af728d [file] [log] [blame] [edit]
/*
* 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;