1/*
2 * Copyright (C) 2013 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26WebInspector.CSSStyleDeclarationSection = function(style)
27{
28    WebInspector.Object.call(this);
29
30    console.assert(style);
31    this._style = style || null;
32
33    this._element = document.createElement("div");
34    this._element.className = WebInspector.CSSStyleDeclarationSection.StyleClassName;
35
36    this._headerElement = document.createElement("div");
37    this._headerElement.className = WebInspector.CSSStyleDeclarationSection.HeaderElementStyleClassName;
38
39    this._iconElement = document.createElement("img");
40    this._iconElement.className = WebInspector.CSSStyleDeclarationSection.IconElementStyleClassName;
41    this._headerElement.appendChild(this._iconElement);
42
43    this._selectorElement = document.createElement("span");
44    this._selectorElement.className = WebInspector.CSSStyleDeclarationSection.SelectorElementStyleClassName;
45    this._selectorElement.setAttribute("spellcheck", "false");
46    this._headerElement.appendChild(this._selectorElement);
47
48    this._originElement = document.createElement("span");
49    this._originElement.className = WebInspector.CSSStyleDeclarationSection.OriginElementStyleClassName;
50    this._headerElement.appendChild(this._originElement);
51
52    this._propertiesElement = document.createElement("div");
53    this._propertiesElement.className = WebInspector.CSSStyleDeclarationSection.PropertiesElementStyleClassName;
54
55    this._propertiesTextEditor = new WebInspector.CSSStyleDeclarationTextEditor(this, style);
56    this._propertiesElement.appendChild(this._propertiesTextEditor.element);
57
58    this._element.appendChild(this._headerElement);
59    this._element.appendChild(this._propertiesElement);
60
61    var iconClassName;
62    switch (style.type) {
63    case WebInspector.CSSStyleDeclaration.Type.Rule:
64        console.assert(style.ownerRule);
65
66        if (style.inherited)
67            iconClassName = WebInspector.CSSStyleDeclarationSection.InheritedStyleRuleIconStyleClassName;
68        else if (style.ownerRule.type === WebInspector.CSSRule.Type.Author)
69            iconClassName = WebInspector.CSSStyleDeclarationSection.AuthorStyleRuleIconStyleClassName;
70        else if (style.ownerRule.type === WebInspector.CSSRule.Type.User)
71            iconClassName = WebInspector.CSSStyleDeclarationSection.UserStyleRuleIconStyleClassName;
72        else if (style.ownerRule.type === WebInspector.CSSRule.Type.UserAgent)
73            iconClassName = WebInspector.CSSStyleDeclarationSection.UserAgentStyleRuleIconStyleClassName;
74        else if (style.ownerRule.type === WebInspector.CSSRule.Type.Inspector)
75            iconClassName = WebInspector.CSSStyleDeclarationSection.InspectorStyleRuleIconStyleClassName;
76        break;
77
78    case WebInspector.CSSStyleDeclaration.Type.Inline:
79    case WebInspector.CSSStyleDeclaration.Type.Attribute:
80        if (style.inherited)
81            iconClassName = WebInspector.CSSStyleDeclarationSection.InheritedElementStyleRuleIconStyleClassName;
82        else
83            iconClassName = WebInspector.DOMTreeElementPathComponent.DOMElementIconStyleClassName;
84        break;
85    }
86
87    console.assert(iconClassName);
88    this._element.classList.add(iconClassName);
89
90    if (!style.editable)
91        this._element.classList.add(WebInspector.CSSStyleDeclarationSection.LockedStyleClassName);
92    else if (style.ownerRule) {
93        this._commitSelectorKeyboardShortcut = new WebInspector.KeyboardShortcut(null, WebInspector.KeyboardShortcut.Key.Enter, this._commitSelector.bind(this), this._selectorElement);
94        this._selectorElement.addEventListener("blur", this._commitSelector.bind(this));
95    } else
96        this._element.classList.add(WebInspector.CSSStyleDeclarationSection.SelectorLockedStyleClassName);
97
98    if (!WebInspector.CSSStyleDeclarationSection._generatedLockImages) {
99        WebInspector.CSSStyleDeclarationSection._generatedLockImages = true;
100
101        var specifications = {"style-lock-normal": {fillColor: [0, 0, 0, 0.5]}};
102        generateColoredImagesForCSS("Images/Locked.svg", specifications, 8, 10);
103    }
104
105    this.refresh();
106};
107
108WebInspector.CSSStyleDeclarationSection.StyleClassName = "style-declaration-section";
109WebInspector.CSSStyleDeclarationSection.LockedStyleClassName = "locked";
110WebInspector.CSSStyleDeclarationSection.SelectorLockedStyleClassName = "selector-locked";
111WebInspector.CSSStyleDeclarationSection.LastInGroupStyleClassName = "last-in-group";
112WebInspector.CSSStyleDeclarationSection.HeaderElementStyleClassName = "header";
113WebInspector.CSSStyleDeclarationSection.IconElementStyleClassName = "icon";
114WebInspector.CSSStyleDeclarationSection.SelectorElementStyleClassName = "selector";
115WebInspector.CSSStyleDeclarationSection.OriginElementStyleClassName = "origin";
116WebInspector.CSSStyleDeclarationSection.PropertiesElementStyleClassName = "properties";
117WebInspector.CSSStyleDeclarationSection.MatchedSelectorElementStyleClassName = "matched";
118
119WebInspector.CSSStyleDeclarationSection.AuthorStyleRuleIconStyleClassName = "author-style-rule-icon";
120WebInspector.CSSStyleDeclarationSection.UserStyleRuleIconStyleClassName = "user-style-rule-icon";
121WebInspector.CSSStyleDeclarationSection.UserAgentStyleRuleIconStyleClassName = "user-agent-style-rule-icon";
122WebInspector.CSSStyleDeclarationSection.InspectorStyleRuleIconStyleClassName = "inspector-style-rule-icon";
123WebInspector.CSSStyleDeclarationSection.InheritedStyleRuleIconStyleClassName = "inherited-style-rule-icon";
124WebInspector.CSSStyleDeclarationSection.InheritedElementStyleRuleIconStyleClassName = "inherited-element-style-rule-icon";
125
126WebInspector.CSSStyleDeclarationSection.prototype = {
127    constructor: WebInspector.CSSStyleDeclarationSection,
128
129    // Public
130
131    get element()
132    {
133        return this._element;
134    },
135
136    get style()
137    {
138        return this._style;
139    },
140
141    get lastInGroup()
142    {
143        return this._element.classList.contains(WebInspector.CSSStyleDeclarationSection.LastInGroupStyleClassName);
144    },
145
146    set lastInGroup(last)
147    {
148        if (last)
149            this._element.classList.add(WebInspector.CSSStyleDeclarationSection.LastInGroupStyleClassName);
150        else
151            this._element.classList.remove(WebInspector.CSSStyleDeclarationSection.LastInGroupStyleClassName);
152    },
153
154    get focused()
155    {
156        return this._propertiesTextEditor.focused;
157    },
158
159    focus: function()
160    {
161        this._propertiesTextEditor.focus();
162    },
163
164    refresh: function()
165    {
166        this._selectorElement.removeChildren();
167        this._originElement.removeChildren();
168
169        this._originElement.appendChild(document.createTextNode(" \u2014 "));
170
171        function appendSelector(selectorText, matched)
172        {
173            var selectorElement = document.createElement("span");
174            if (matched)
175                selectorElement.className = WebInspector.CSSStyleDeclarationSection.MatchedSelectorElementStyleClassName;
176            selectorElement.textContent = selectorText;
177            this._selectorElement.appendChild(selectorElement);
178        }
179
180        switch (this._style.type) {
181        case WebInspector.CSSStyleDeclaration.Type.Rule:
182            console.assert(this._style.ownerRule);
183
184            var selectors = this._style.ownerRule.selectors;
185            var matchedSelectorIndices = this._style.ownerRule.matchedSelectorIndices;
186            if (selectors.length && matchedSelectorIndices.length) {
187                for (var i = 0; i < selectors.length; ++i) {
188                    appendSelector.call(this, selectors[i], matchedSelectorIndices.contains(i));
189                    if (i < selectors.length - 1)
190                        this._selectorElement.appendChild(document.createTextNode(", "));
191                }
192            } else
193                appendSelector.call(this, this._style.ownerRule.selectorText, true);
194
195            if (this._style.ownerRule.sourceCodeLocation) {
196                var sourceCodeLink = WebInspector.createSourceCodeLocationLink(this._style.ownerRule.sourceCodeLocation, true);
197                this._originElement.appendChild(sourceCodeLink);
198            } else {
199                var originString;
200                switch (this._style.ownerRule.type) {
201                case WebInspector.CSSRule.Type.Author:
202                    originString = WebInspector.UIString("Author Stylesheet");
203                    break;
204
205                case WebInspector.CSSRule.Type.User:
206                    originString = WebInspector.UIString("User Stylesheet");
207                    break;
208
209                case WebInspector.CSSRule.Type.UserAgent:
210                    originString = WebInspector.UIString("User Agent Stylesheet");
211                    break;
212
213                case WebInspector.CSSRule.Type.Inspector:
214                    originString = WebInspector.UIString("Web Inspector");
215                    break;
216                }
217
218                console.assert(originString);
219                if (originString)
220                    this._originElement.appendChild(document.createTextNode(originString));
221            }
222
223            break;
224
225        case WebInspector.CSSStyleDeclaration.Type.Inline:
226            appendSelector.call(this, WebInspector.displayNameForNode(this._style.node), true);
227            this._originElement.appendChild(document.createTextNode(WebInspector.UIString("Style Attribute")));
228            break;
229
230        case WebInspector.CSSStyleDeclaration.Type.Attribute:
231            appendSelector.call(this, WebInspector.displayNameForNode(this._style.node), true);
232            this._originElement.appendChild(document.createTextNode(WebInspector.UIString("HTML Attributes")));
233            break;
234        }
235    },
236
237    updateLayout: function()
238    {
239        this._propertiesTextEditor.updateLayout();
240    },
241
242    // Private
243
244    _commitSelector: function(mutations)
245    {
246        console.assert(this._style.ownerRule);
247        if (!this._style.ownerRule)
248            return;
249
250        var newSelectorText = this._selectorElement.textContent.trim();
251        if (!newSelectorText) {
252            // Revert to the current selector (by doing a refresh) since the new selector is empty.
253            this.refresh();
254            return;
255        }
256
257        this._style.ownerRule.selectorText = newSelectorText;
258    }
259};
260
261WebInspector.CSSStyleDeclarationSection.prototype.__proto__ = WebInspector.StyleDetailsPanel.prototype;
262