2 * Copyright (C) 2007 Apple Inc. All rights reserved.
3 * Copyright (C) 2009 Joseph Pecoraro
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
32 * @extends {WebInspector.ElementsSidebarPane}
34 WebInspector
.StylesSidebarPane = function()
36 WebInspector
.ElementsSidebarPane
.call(this, WebInspector
.UIString("Styles"));
37 this.setMinimumSize(96, 26);
39 WebInspector
.moduleSetting("colorFormat").addChangeListener(this.update
.bind(this));
40 WebInspector
.moduleSetting("textEditorIndent").addChangeListener(this.update
.bind(this));
42 var hbox
= this.element
.createChild("div", "hbox styles-sidebar-pane-toolbar");
43 var filterContainerElement
= hbox
.createChild("div", "styles-sidebar-pane-filter-box");
44 this._filterInput
= WebInspector
.StylesSidebarPane
.createPropertyFilterElement(WebInspector
.UIString("Filter"), hbox
, this._onFilterChanged
.bind(this));
45 filterContainerElement
.appendChild(this._filterInput
);
47 var toolbar
= new WebInspector
.ExtensibleToolbar("styles-sidebarpane-toolbar", hbox
);
48 if (Runtime
.experiments
.isEnabled("layoutEditor") && !Runtime
.queryParam("remoteFrontend")) {
49 this._layoutEditorButton
= new WebInspector
.ToolbarButton(WebInspector
.UIString("Toggle Layout Editor"), "layout-editor-toolbar-item");
50 toolbar
.appendToolbarItem(this._layoutEditorButton
);
51 this._layoutEditorButton
.addEventListener("click", this._toggleLayoutEditor
, this);
52 toolbar
.appendSeparator();
55 toolbar
.element
.classList
.add("styles-pane-toolbar", "toolbar-gray-toggled");
56 this._currentToolbarPane
= null;
58 var toolbarPaneContainer
= this.element
.createChild("div", "styles-sidebar-toolbar-pane-container");
59 this._toolbarPaneElement
= toolbarPaneContainer
.createChild("div", "styles-sidebar-toolbar-pane");
60 this._sectionsContainer
= this.element
.createChild("div");
62 this._stylesPopoverHelper
= new WebInspector
.StylesPopoverHelper();
64 this._linkifier
= new WebInspector
.Linkifier(new WebInspector
.Linkifier
.DefaultCSSFormatter());
66 this.element
.classList
.add("styles-pane");
67 this.element
.addEventListener("mousemove", this._mouseMovedOverElement
.bind(this), false);
68 this._keyDownBound
= this._keyDown
.bind(this);
69 this._keyUpBound
= this._keyUp
.bind(this);
70 new WebInspector
.PropertyChangeHighlighter(this);
73 // Keep in sync with ComputedStyleConstants.h PseudoId enum. Array below contains pseudo id names for corresponding enum indexes.
74 // First item is empty due to its artificial NOPSEUDO nature in the enum.
75 // FIXME: find a way of generating this mapping or getting it from combination of ComputedStyleConstants and CSSSelector.cpp at
77 WebInspector
.StylesSidebarPane
.PseudoIdNames
= [
78 "", "first-line", "first-letter", "before", "after", "backdrop", "selection", "", "-webkit-scrollbar",
79 "-webkit-scrollbar-thumb", "-webkit-scrollbar-button", "-webkit-scrollbar-track", "-webkit-scrollbar-track-piece",
80 "-webkit-scrollbar-corner", "-webkit-resizer"
86 WebInspector
.StylesSidebarPane
.Events
= {
87 SelectorEditingStarted
: "SelectorEditingStarted",
88 SelectorEditingEnded
: "SelectorEditingEnded"
92 * @param {!WebInspector.CSSProperty} property
95 WebInspector
.StylesSidebarPane
.createExclamationMark = function(property
)
97 var exclamationElement
= createElement("label", "dt-icon-label");
98 exclamationElement
.className
= "exclamation-mark";
99 if (!WebInspector
.StylesSidebarPane
.ignoreErrorsForProperty(property
))
100 exclamationElement
.type
= "warning-icon";
101 exclamationElement
.title
= WebInspector
.CSSMetadata
.cssPropertiesMetainfo
.keySet()[property
.name
.toLowerCase()] ? WebInspector
.UIString("Invalid property value") : WebInspector
.UIString("Unknown property name");
102 return exclamationElement
;
106 * @param {!WebInspector.CSSProperty} property
109 WebInspector
.StylesSidebarPane
.ignoreErrorsForProperty = function(property
) {
111 * @param {string} string
113 function hasUnknownVendorPrefix(string
)
115 return !string
.startsWith("-webkit-") && /^[-_][\w\d]+-\w/.test(string
);
118 var name
= property
.name
.toLowerCase();
121 if (name
.charAt(0) === "_")
124 // IE has a different format for this.
125 if (name
=== "filter")
128 // Common IE-specific property prefix.
129 if (name
.startsWith("scrollbar-"))
131 if (hasUnknownVendorPrefix(name
))
134 var value
= property
.value
.toLowerCase();
137 if (value
.endsWith("\9"))
139 if (hasUnknownVendorPrefix(value
))
145 WebInspector
.StylesSidebarPane
.prototype = {
146 _toggleLayoutEditor: function()
148 this._showLayoutEditor
= !this._showLayoutEditor
;
149 this._layoutEditorButton
.setToggled(this._showLayoutEditor
);
150 var targets
= WebInspector
.targetManager
.targets();
152 if (this._showLayoutEditor
)
153 WebInspector
.inspectElementModeController
.disable();
155 WebInspector
.inspectElementModeController
.enable();
157 var mode
= this._showLayoutEditor
? DOMAgent
.InspectMode
.ShowLayoutEditor
: DOMAgent
.InspectMode
.None
;
158 for (var domModel
of WebInspector
.DOMModel
.instances())
159 domModel
.setInspectMode(mode
);
162 onUndoOrRedoHappened: function()
164 this.setNode(this.node());
168 * @param {!WebInspector.Event} event
170 _onAddButtonLongClick: function(event
)
172 var cssModel
= this.cssModel();
175 var headers
= cssModel
.styleSheetHeaders().filter(styleSheetResourceHeader
);
177 /** @type {!Array.<{text: string, handler: function()}>} */
178 var contextMenuDescriptors
= [];
179 for (var i
= 0; i
< headers
.length
; ++i
) {
180 var header
= headers
[i
];
181 var handler
= this._createNewRuleInStyleSheet
.bind(this, header
);
182 contextMenuDescriptors
.push({
183 text
: WebInspector
.displayNameForURL(header
.resourceURL()),
188 contextMenuDescriptors
.sort(compareDescriptors
);
190 var contextMenu
= new WebInspector
.ContextMenu(/** @type {!Event} */(event
.data
));
191 for (var i
= 0; i
< contextMenuDescriptors
.length
; ++i
) {
192 var descriptor
= contextMenuDescriptors
[i
];
193 contextMenu
.appendItem(descriptor
.text
, descriptor
.handler
);
195 if (!contextMenu
.isEmpty())
196 contextMenu
.appendSeparator();
197 contextMenu
.appendItem("inspector-stylesheet", this._createNewRuleInViaInspectorStyleSheet
.bind(this));
201 * @param {!{text: string, handler: function()}} descriptor1
202 * @param {!{text: string, handler: function()}} descriptor2
205 function compareDescriptors(descriptor1
, descriptor2
)
207 return String
.naturalOrderComparator(descriptor1
.text
, descriptor2
.text
);
211 * @param {!WebInspector.CSSStyleSheetHeader} header
214 function styleSheetResourceHeader(header
)
216 return !header
.isViaInspector() && !header
.isInline
&& !!header
.resourceURL();
221 * @param {!WebInspector.DOMNode} node
223 updateEditingSelectorForNode: function(node
)
225 var selectorText
= WebInspector
.DOMPresentationUtils
.simpleSelector(node
);
228 this._editingSelectorSection
.setSelectorText(selectorText
);
234 isEditingSelector: function()
236 return !!this._editingSelectorSection
;
240 * @param {!WebInspector.StylePropertiesSection} section
242 _startEditingSelector: function(section
)
244 this._editingSelectorSection
= section
;
245 this.dispatchEventToListeners(WebInspector
.StylesSidebarPane
.Events
.SelectorEditingStarted
);
248 _finishEditingSelector: function()
250 delete this._editingSelectorSection
;
251 this.dispatchEventToListeners(WebInspector
.StylesSidebarPane
.Events
.SelectorEditingEnded
);
255 * @param {!WebInspector.CSSRule} editedRule
256 * @param {!WebInspector.TextRange} oldRange
257 * @param {!WebInspector.TextRange} newRange
259 _styleSheetRuleEdited: function(editedRule
, oldRange
, newRange
)
261 if (!editedRule
.styleSheetId
)
263 for (var block
of this._sectionBlocks
) {
264 for (var section
of block
.sections
)
265 section
._styleSheetRuleEdited(editedRule
, oldRange
, newRange
);
270 * @param {!WebInspector.CSSMedia} oldMedia
271 * @param {!WebInspector.CSSMedia} newMedia
273 _styleSheetMediaEdited: function(oldMedia
, newMedia
)
275 if (!oldMedia
.parentStyleSheetId
)
277 for (var block
of this._sectionBlocks
) {
278 for (var section
of block
.sections
)
279 section
._styleSheetMediaEdited(oldMedia
, newMedia
);
284 * @param {?RegExp} regex
286 _onFilterChanged: function(regex
)
288 this._filterRegex
= regex
;
289 this._updateFilter();
294 * @param {?WebInspector.DOMNode} node
296 setNode: function(node
)
298 this._stylesPopoverHelper
.hide();
299 node
= WebInspector
.SharedSidebarModel
.elementNode(node
);
302 WebInspector
.ElementsSidebarPane
.prototype.setNode
.call(this, node
);
306 * @param {!WebInspector.StylePropertiesSection=} editedSection
308 _refreshUpdate: function(editedSection
)
310 var node
= this.node();
314 for (var block
of this._sectionBlocks
) {
315 for (var section
of block
.sections
) {
318 section
.update(section
=== editedSection
);
322 if (this._filterRegex
)
323 this._updateFilter();
324 this._nodeStylesUpdatedForTest(node
, false);
329 * @return {!Promise.<?>}
333 this._discardElementUnderMouse();
335 return this.fetchMatchedCascade()
336 .then(this._innerRebuildUpdate
.bind(this));
339 _resetCache: function()
341 delete this._matchedCascadePromise
;
345 * @return {!Promise.<?{matched: !WebInspector.SectionCascade, pseudo: !Map.<number, !WebInspector.SectionCascade>}>}
347 fetchMatchedCascade: function()
349 var node
= this.node();
351 return Promise
.resolve(/** @type {?{matched: !WebInspector.SectionCascade, pseudo: !Map.<number, !WebInspector.SectionCascade>}} */(null));
352 if (!this._matchedCascadePromise
)
353 this._matchedCascadePromise
= this._matchedStylesForNode(node
).then(buildMatchedCascades
.bind(this, node
));
354 return this._matchedCascadePromise
;
357 * @param {!WebInspector.DOMNode} node
358 * @param {?WebInspector.CSSStyleModel.MatchedStyleResult} matchedStyles
359 * @return {?{matched: !WebInspector.SectionCascade, pseudo: !Map.<number, !WebInspector.SectionCascade>}}
360 * @this {WebInspector.StylesSidebarPane}
362 function buildMatchedCascades(node
, matchedStyles
)
364 if (!matchedStyles
|| node
!== this.node())
366 if (!matchedStyles
.matchedCSSRules
|| !matchedStyles
.pseudoElements
|| !matchedStyles
.inherited
)
370 matched
: this._buildMatchedRulesSectionCascade(node
, matchedStyles
),
371 pseudo
: this._buildPseudoCascades(node
, matchedStyles
)
377 * @param {!WebInspector.DOMNode} node
378 * @return {!Promise.<?WebInspector.CSSStyleModel.MatchedStyleResult>}
380 _matchedStylesForNode: function(node
)
382 var cssModel
= this.cssModel();
384 return Promise
.resolve(/** @type {?WebInspector.CSSStyleModel.MatchedStyleResult} */(null));
385 return cssModel
.matchedStylesPromise(node
.id
)
389 * @param {boolean} editing
391 setEditingStyle: function(editing
)
393 if (this._isEditingStyle
=== editing
)
395 this._isEditingStyle
= editing
;
401 onCSSModelChanged: function()
403 if (this._userOperation
|| this._isEditingStyle
)
413 onFrameResizedThrottled: function()
415 this.onCSSModelChanged();
421 onDOMModelChanged: function()
423 // Any attribute removal or modification can affect the styles of "related" nodes.
424 // Do not touch the styles if they are being edited.
425 if (this._isEditingStyle
|| this._userOperation
)
433 * @param {?{matched: !WebInspector.SectionCascade, pseudo: !Map.<number, !WebInspector.SectionCascade>}} cascades
435 _innerRebuildUpdate: function(cascades
)
437 this._linkifier
.reset();
438 this._sectionsContainer
.removeChildren();
439 this._sectionBlocks
= [];
441 var node
= this.node();
442 if (!cascades
|| !node
)
445 this._sectionBlocks
= this._rebuildSectionsForMatchedStyleRules(cascades
.matched
);
446 var pseudoIds
= cascades
.pseudo
.keysArray().sort();
447 for (var pseudoId
of pseudoIds
) {
448 var block
= WebInspector
.SectionBlock
.createPseudoIdBlock(pseudoId
);
449 var cascade
= cascades
.pseudo
.get(pseudoId
);
450 for (var sectionModel
of cascade
.sectionModels()) {
451 var section
= new WebInspector
.StylePropertiesSection(this, sectionModel
);
452 block
.sections
.push(section
);
454 this._sectionBlocks
.push(block
);
457 for (var block
of this._sectionBlocks
) {
458 var titleElement
= block
.titleElement();
460 this._sectionsContainer
.appendChild(titleElement
);
461 for (var section
of block
.sections
)
462 this._sectionsContainer
.appendChild(section
.element
);
465 if (this._filterRegex
)
466 this._updateFilter();
468 this._nodeStylesUpdatedForTest(node
, true);
472 * @param {!WebInspector.DOMNode} node
473 * @param {!WebInspector.CSSStyleModel.MatchedStyleResult} styles
474 * @return {!Map<number, !WebInspector.SectionCascade>}
476 _buildPseudoCascades: function(node
, styles
)
478 var pseudoCascades
= new Map();
479 for (var i
= 0; i
< styles
.pseudoElements
.length
; ++i
) {
480 var pseudoElementCSSRules
= styles
.pseudoElements
[i
];
481 var pseudoId
= pseudoElementCSSRules
.pseudoId
;
483 // Add rules in reverse order to match the cascade order.
484 var pseudoElementCascade
= new WebInspector
.SectionCascade();
485 for (var j
= pseudoElementCSSRules
.rules
.length
- 1; j
>= 0; --j
) {
486 var rule
= pseudoElementCSSRules
.rules
[j
];
487 pseudoElementCascade
.appendModelFromRule(rule
);
489 pseudoCascades
.set(pseudoId
, pseudoElementCascade
);
491 return pseudoCascades
;
495 * @param {!WebInspector.DOMNode} node
496 * @param {boolean} rebuild
498 _nodeStylesUpdatedForTest: function(node
, rebuild
)
500 // For sniffing in tests.
504 * @param {!WebInspector.DOMNode} node
505 * @param {!WebInspector.CSSStyleModel.MatchedStyleResult} styles
506 * @return {!WebInspector.SectionCascade}
508 _buildMatchedRulesSectionCascade: function(node
, styles
)
510 var cascade
= new WebInspector
.SectionCascade();
512 function addAttributesStyle()
514 if (!styles
.attributesStyle
)
516 var selectorText
= node
.nodeNameInCorrectCase() + "[" + WebInspector
.UIString("Attributes Style") + "]";
517 cascade
.appendModelFromStyle(styles
.attributesStyle
, selectorText
);
520 // Inline style has the greatest specificity.
521 if (styles
.inlineStyle
&& node
.nodeType() === Node
.ELEMENT_NODE
)
522 cascade
.appendModelFromStyle(styles
.inlineStyle
, "element.style");
524 // Add rules in reverse order to match the cascade order.
525 var addedAttributesStyle
;
526 for (var i
= styles
.matchedCSSRules
.length
- 1; i
>= 0; --i
) {
527 var rule
= styles
.matchedCSSRules
[i
];
528 if ((rule
.isInjected() || rule
.isUserAgent()) && !addedAttributesStyle
) {
529 // Show element's Style Attributes after all author rules.
530 addedAttributesStyle
= true;
531 addAttributesStyle();
533 cascade
.appendModelFromRule(rule
);
536 if (!addedAttributesStyle
)
537 addAttributesStyle();
539 // Walk the node structure and identify styles with inherited properties.
540 var parentNode
= node
.parentNode
;
542 for (var parentOrdinal
= 0; parentOrdinal
< styles
.inherited
.length
; ++parentOrdinal
) {
543 var parentStyles
= styles
.inherited
[parentOrdinal
];
544 if (parentStyles
.inlineStyle
&& this._containsInherited(parentStyles
.inlineStyle
))
545 cascade
.appendModelFromStyle(parentStyles
.inlineStyle
, WebInspector
.UIString("Style Attribute"), parentNode
);
547 for (var i
= parentStyles
.matchedCSSRules
.length
- 1; i
>= 0; --i
) {
548 var rulePayload
= parentStyles
.matchedCSSRules
[i
];
549 if (!this._containsInherited(rulePayload
.style
))
551 cascade
.appendModelFromRule(rulePayload
, parentNode
);
553 parentNode
= parentNode
.parentNode
;
559 * @param {!WebInspector.SectionCascade} matchedCascade
560 * @return {!Array.<!WebInspector.SectionBlock>}
562 _rebuildSectionsForMatchedStyleRules: function(matchedCascade
)
564 var blocks
= [new WebInspector
.SectionBlock(null)];
565 var lastParentNode
= null;
566 for (var sectionModel
of matchedCascade
.sectionModels()) {
567 var parentNode
= sectionModel
.parentNode();
568 if (parentNode
&& parentNode
!== lastParentNode
) {
569 lastParentNode
= parentNode
;
570 var block
= WebInspector
.SectionBlock
.createInheritedNodeBlock(lastParentNode
);
574 var section
= new WebInspector
.StylePropertiesSection(this, sectionModel
);
575 blocks
.peekLast().sections
.push(section
);
581 * @param {!WebInspector.CSSStyleDeclaration} style
584 _containsInherited: function(style
)
586 var properties
= style
.allProperties
;
587 for (var i
= 0; i
< properties
.length
; ++i
) {
588 var property
= properties
[i
];
589 // Does this style contain non-overridden inherited property?
590 if (property
.activeInStyle() && WebInspector
.CSSMetadata
.isPropertyInherited(property
.name
))
596 _createNewRuleInViaInspectorStyleSheet: function()
598 var cssModel
= this.cssModel();
599 var node
= this.node();
600 if (!cssModel
|| !node
)
602 this._userOperation
= true;
603 cssModel
.requestViaInspectorStylesheet(node
, onViaInspectorStyleSheet
.bind(this));
606 * @param {?WebInspector.CSSStyleSheetHeader} styleSheetHeader
607 * @this {WebInspector.StylesSidebarPane}
609 function onViaInspectorStyleSheet(styleSheetHeader
)
611 delete this._userOperation
;
612 this._createNewRuleInStyleSheet(styleSheetHeader
);
617 * @param {?WebInspector.CSSStyleSheetHeader} styleSheetHeader
619 _createNewRuleInStyleSheet: function(styleSheetHeader
)
621 if (!styleSheetHeader
)
623 styleSheetHeader
.requestContent(onStyleSheetContent
.bind(this, styleSheetHeader
.id
));
626 * @param {string} styleSheetId
627 * @param {string} text
628 * @this {WebInspector.StylesSidebarPane}
630 function onStyleSheetContent(styleSheetId
, text
)
632 var lines
= text
.split("\n");
633 var range
= WebInspector
.TextRange
.createFromLocation(lines
.length
- 1, lines
[lines
.length
- 1].length
);
634 this._addBlankSection(this._sectionBlocks
[0].sections
[0], styleSheetId
, range
);
639 * @param {!WebInspector.StylePropertiesSection} insertAfterSection
640 * @param {string} styleSheetId
641 * @param {!WebInspector.TextRange} ruleLocation
643 _addBlankSection: function(insertAfterSection
, styleSheetId
, ruleLocation
)
646 var node
= this.node();
647 var blankSection
= new WebInspector
.BlankStylePropertiesSection(this, node
? WebInspector
.DOMPresentationUtils
.simpleSelector(node
) : "", styleSheetId
, ruleLocation
, insertAfterSection
.styleRule
);
649 this._sectionsContainer
.insertBefore(blankSection
.element
, insertAfterSection
.element
.nextSibling
);
651 for (var block
of this._sectionBlocks
) {
652 var index
= block
.sections
.indexOf(insertAfterSection
);
655 block
.sections
.splice(index
+ 1, 0, blankSection
);
656 blankSection
.startEditingSelector();
661 * @param {!WebInspector.StylePropertiesSection} section
663 removeSection: function(section
)
665 for (var block
of this._sectionBlocks
) {
666 var index
= block
.sections
.indexOf(section
);
669 block
.sections
.splice(index
, 1);
670 section
.element
.remove();
677 filterRegex: function()
679 return this._filterRegex
;
682 _updateFilter: function()
684 for (var block
of this._sectionBlocks
)
685 block
.updateFilter();
693 WebInspector
.ElementsSidebarPane
.prototype.wasShown
.call(this);
694 this.element
.ownerDocument
.body
.addEventListener("keydown", this._keyDownBound
, false);
695 this.element
.ownerDocument
.body
.addEventListener("keyup", this._keyUpBound
, false);
703 this.element
.ownerDocument
.body
.removeEventListener("keydown", this._keyDownBound
, false);
704 this.element
.ownerDocument
.body
.removeEventListener("keyup", this._keyUpBound
, false);
705 this._stylesPopoverHelper
.hide();
706 this._discardElementUnderMouse();
707 WebInspector
.ElementsSidebarPane
.prototype.willHide
.call(this);
710 _discardElementUnderMouse: function()
712 if (this._elementUnderMouse
)
713 this._elementUnderMouse
.classList
.remove("styles-panel-hovered");
714 delete this._elementUnderMouse
;
718 * @param {!Event} event
720 _mouseMovedOverElement: function(event
)
722 if (this._elementUnderMouse
&& event
.target
!== this._elementUnderMouse
)
723 this._discardElementUnderMouse();
724 this._elementUnderMouse
= event
.target
;
725 if (WebInspector
.KeyboardShortcut
.eventHasCtrlOrMeta(/** @type {!MouseEvent} */(event
)))
726 this._elementUnderMouse
.classList
.add("styles-panel-hovered");
730 * @param {!Event} event
732 _keyDown: function(event
)
734 if ((!WebInspector
.isMac() && event
.keyCode
=== WebInspector
.KeyboardShortcut
.Keys
.Ctrl
.code
) ||
735 (WebInspector
.isMac() && event
.keyCode
=== WebInspector
.KeyboardShortcut
.Keys
.Meta
.code
)) {
736 if (this._elementUnderMouse
)
737 this._elementUnderMouse
.classList
.add("styles-panel-hovered");
742 * @param {!Event} event
744 _keyUp: function(event
)
746 if ((!WebInspector
.isMac() && event
.keyCode
=== WebInspector
.KeyboardShortcut
.Keys
.Ctrl
.code
) ||
747 (WebInspector
.isMac() && event
.keyCode
=== WebInspector
.KeyboardShortcut
.Keys
.Meta
.code
)) {
748 this._discardElementUnderMouse();
753 * @param {?WebInspector.Widget} widget
755 showToolbarPane: function(widget
)
757 if (this._animatedToolbarPane
!== undefined)
758 this._pendingWidget
= widget
;
760 this._startToolbarPaneAnimation(widget
);
764 * @param {?WebInspector.Widget} widget
766 _startToolbarPaneAnimation: function(widget
)
768 if (widget
=== this._currentToolbarPane
)
771 if (widget
&& this._currentToolbarPane
) {
772 this._currentToolbarPane
.detach();
773 widget
.show(this._toolbarPaneElement
);
774 this._currentToolbarPane
= widget
;
778 this._animatedToolbarPane
= widget
;
780 if (this._currentToolbarPane
)
781 this._toolbarPaneElement
.style
.animationName
= 'styles-element-state-pane-slideout';
783 this._toolbarPaneElement
.style
.animationName
= 'styles-element-state-pane-slidein';
786 widget
.show(this._toolbarPaneElement
);
788 var listener
= onAnimationEnd
.bind(this);
789 this._toolbarPaneElement
.addEventListener("animationend", listener
, false);
792 * @this {WebInspector.StylesSidebarPane}
794 function onAnimationEnd()
796 this._toolbarPaneElement
.style
.removeProperty('animation-name');
797 this._toolbarPaneElement
.removeEventListener("animationend", listener
, false);
799 if (this._currentToolbarPane
)
800 this._currentToolbarPane
.detach();
802 this._currentToolbarPane
= this._animatedToolbarPane
;
803 delete this._animatedToolbarPane
;
805 if (this._pendingWidget
!== undefined) {
806 this._startToolbarPaneAnimation(this._pendingWidget
);
807 delete this._pendingWidget
;
813 * @return {!Array<!WebInspector.SectionBlock>}
815 sectionBlocks: function()
817 return this._sectionBlocks
|| [];
820 __proto__
: WebInspector
.ElementsSidebarPane
.prototype
824 * @param {string} placeholder
825 * @param {!Element} container
826 * @param {function(?RegExp)} filterCallback
829 WebInspector
.StylesSidebarPane
.createPropertyFilterElement = function(placeholder
, container
, filterCallback
)
831 var input
= createElement("input");
832 input
.placeholder
= placeholder
;
834 function searchHandler()
836 var regex
= input
.value
? new RegExp(input
.value
.escapeForRegExp(), "i") : null;
837 filterCallback(regex
);
838 container
.classList
.toggle("styles-filter-engaged", !!input
.value
);
840 input
.addEventListener("input", searchHandler
, false);
843 * @param {!Event} event
845 function keydownHandler(event
)
848 if (event
.keyIdentifier
!== Esc
|| !input
.value
)
854 input
.addEventListener("keydown", keydownHandler
, false);
856 input
.setFilterValue
= setFilterValue
;
859 * @param {string} value
861 function setFilterValue(value
)
873 * @param {?Element} titleElement
875 WebInspector
.SectionBlock = function(titleElement
)
877 this._titleElement
= titleElement
;
882 * @param {number} pseudoId
883 * @return {!WebInspector.SectionBlock}
885 WebInspector
.SectionBlock
.createPseudoIdBlock = function(pseudoId
)
887 var separatorElement
= createElement("div");
888 separatorElement
.className
= "sidebar-separator";
889 var pseudoName
= WebInspector
.StylesSidebarPane
.PseudoIdNames
[pseudoId
];
891 separatorElement
.textContent
= WebInspector
.UIString("Pseudo ::%s element", pseudoName
);
893 separatorElement
.textContent
= WebInspector
.UIString("Pseudo element");
894 return new WebInspector
.SectionBlock(separatorElement
);
898 * @param {!WebInspector.DOMNode} node
899 * @return {!WebInspector.SectionBlock}
901 WebInspector
.SectionBlock
.createInheritedNodeBlock = function(node
)
903 var separatorElement
= createElement("div");
904 separatorElement
.className
= "sidebar-separator";
905 var link
= WebInspector
.DOMPresentationUtils
.linkifyNodeReference(node
);
906 separatorElement
.createTextChild(WebInspector
.UIString("Inherited from") + " ");
907 separatorElement
.appendChild(link
);
908 return new WebInspector
.SectionBlock(separatorElement
);
911 WebInspector
.SectionBlock
.prototype = {
912 updateFilter: function()
914 var hasAnyVisibleSection
= false;
915 for (var section
of this.sections
)
916 hasAnyVisibleSection
|= section
._updateFilter();
917 if (this._titleElement
)
918 this._titleElement
.classList
.toggle("hidden", !hasAnyVisibleSection
);
924 titleElement: function()
926 return this._titleElement
;
932 * @param {!WebInspector.StylesSidebarPane} parentPane
933 * @param {!WebInspector.StylesSectionModel} styleRule
935 WebInspector
.StylePropertiesSection = function(parentPane
, styleRule
)
937 this._parentPane
= parentPane
;
938 this.styleRule
= styleRule
;
939 this.editable
= styleRule
.editable();
941 var rule
= styleRule
.rule();
942 this.element
= createElementWithClass("div", "styles-section matched-styles monospace");
943 this.element
._section
= this;
945 this._titleElement
= this.element
.createChild("div", "styles-section-title " + (rule
? "styles-selector" : ""));
947 this.propertiesTreeOutline
= new TreeOutline();
948 this.propertiesTreeOutline
.element
.classList
.add("style-properties", "monospace");
949 this.propertiesTreeOutline
.section
= this;
950 this.element
.appendChild(this.propertiesTreeOutline
.element
);
952 var selectorContainer
= createElement("div");
953 this._selectorElement
= createElementWithClass("span", "selector");
954 this._selectorElement
.textContent
= styleRule
.selectorText();
955 selectorContainer
.appendChild(this._selectorElement
);
957 var openBrace
= createElement("span");
958 openBrace
.textContent
= " {";
959 selectorContainer
.appendChild(openBrace
);
960 selectorContainer
.addEventListener("mousedown", this._handleEmptySpaceMouseDown
.bind(this), false);
961 selectorContainer
.addEventListener("click", this._handleSelectorContainerClick
.bind(this), false);
963 var closeBrace
= createElement("div");
964 closeBrace
.textContent
= "}";
965 this.element
.appendChild(closeBrace
);
967 if (this.editable
&& rule
) {
968 var newRuleButton
= closeBrace
.createChild("div", "sidebar-pane-button-new-rule");
969 newRuleButton
.title
= WebInspector
.UIString("Insert Style Rule");
970 newRuleButton
.addEventListener("click", this._onNewRuleClick
.bind(this), false);
973 this._selectorElement
.addEventListener("click", this._handleSelectorClick
.bind(this), false);
974 this.element
.addEventListener("mousedown", this._handleEmptySpaceMouseDown
.bind(this), false);
975 this.element
.addEventListener("click", this._handleEmptySpaceClick
.bind(this), false);
978 // Prevent editing the user agent and user rules.
979 if (rule
.isUserAgent() || rule
.isInjected()) {
980 this.editable
= false;
982 // Check this is a real CSSRule, not a bogus object coming from WebInspector.BlankStylePropertiesSection.
983 if (rule
.styleSheetId
)
984 this.navigable
= !!rule
.resourceURL();
988 this._selectorRefElement
= createElementWithClass("div", "styles-section-subtitle");
989 this._mediaListElement
= this._titleElement
.createChild("div", "media-list media-matches");
990 this._updateMediaList();
991 this._updateRuleOrigin();
992 selectorContainer
.insertBefore(this._selectorRefElement
, selectorContainer
.firstChild
);
993 this._titleElement
.appendChild(selectorContainer
);
994 this._selectorContainer
= selectorContainer
;
997 this.element
.classList
.add("navigable");
1000 this.element
.classList
.add("read-only");
1002 this._markSelectorMatches();
1006 WebInspector
.StylePropertiesSection
.prototype = {
1008 * @return {?WebInspector.StylePropertiesSection}
1010 firstSibling: function()
1012 var parent
= this.element
.parentElement
;
1016 var childElement
= parent
.firstChild
;
1017 while (childElement
) {
1018 if (childElement
._section
)
1019 return childElement
._section
;
1020 childElement
= childElement
.nextSibling
;
1027 * @return {?WebInspector.StylePropertiesSection}
1029 lastSibling: function()
1031 var parent
= this.element
.parentElement
;
1035 var childElement
= parent
.lastChild
;
1036 while (childElement
) {
1037 if (childElement
._section
)
1038 return childElement
._section
;
1039 childElement
= childElement
.previousSibling
;
1046 * @return {?WebInspector.StylePropertiesSection}
1048 nextSibling: function()
1050 var curElement
= this.element
;
1052 curElement
= curElement
.nextSibling
;
1053 } while (curElement
&& !curElement
._section
);
1055 return curElement
? curElement
._section
: null;
1059 * @return {?WebInspector.StylePropertiesSection}
1061 previousSibling: function()
1063 var curElement
= this.element
;
1065 curElement
= curElement
.previousSibling
;
1066 } while (curElement
&& !curElement
._section
);
1068 return curElement
? curElement
._section
: null;
1072 * @return {?WebInspector.CSSRule}
1076 return this.styleRule
.rule();
1080 * @param {?Event} event
1082 _onNewRuleClick: function(event
)
1085 var rule
= this.rule();
1086 var range
= WebInspector
.TextRange
.createFromLocation(rule
.style
.range
.endLine
, rule
.style
.range
.endColumn
+ 1);
1087 this._parentPane
._addBlankSection(this, /** @type {string} */(rule
.styleSheetId
), range
);
1091 * @param {!WebInspector.CSSRule} editedRule
1092 * @param {!WebInspector.TextRange} oldRange
1093 * @param {!WebInspector.TextRange} newRange
1095 _styleSheetRuleEdited: function(editedRule
, oldRange
, newRange
)
1097 var rule
= this.rule();
1098 if (!rule
|| !rule
.styleSheetId
)
1100 if (rule
!== editedRule
)
1101 rule
.sourceStyleSheetEdited(/** @type {string} */(editedRule
.styleSheetId
), oldRange
, newRange
);
1102 this._updateMediaList();
1103 this._updateRuleOrigin();
1107 * @param {!WebInspector.CSSMedia} oldMedia
1108 * @param {!WebInspector.CSSMedia} newMedia
1110 _styleSheetMediaEdited: function(oldMedia
, newMedia
)
1112 var rule
= this.rule();
1113 if (!rule
|| !rule
.styleSheetId
)
1115 rule
.mediaEdited(oldMedia
, newMedia
);
1116 this._updateMediaList();
1120 * @param {?Array.<!WebInspector.CSSMedia>} mediaRules
1122 _createMediaList: function(mediaRules
)
1126 for (var i
= mediaRules
.length
- 1; i
>= 0; --i
) {
1127 var media
= mediaRules
[i
];
1128 var mediaDataElement
= this._mediaListElement
.createChild("div", "media");
1129 if (media
.sourceURL
) {
1130 var anchor
= this._parentPane
._linkifier
.linkifyMedia(media
);
1131 anchor
.classList
.add("subtitle");
1132 mediaDataElement
.appendChild(anchor
);
1135 var mediaContainerElement
= mediaDataElement
.createChild("span");
1136 var mediaTextElement
= mediaContainerElement
.createChild("span", "media-text");
1137 mediaTextElement
.title
= media
.text
;
1138 switch (media
.source
) {
1139 case WebInspector
.CSSMedia
.Source
.LINKED_SHEET
:
1140 case WebInspector
.CSSMedia
.Source
.INLINE_SHEET
:
1141 mediaTextElement
.textContent
= "media=\"" + media
.text
+ "\"";
1143 case WebInspector
.CSSMedia
.Source
.MEDIA_RULE
:
1144 var decoration
= mediaContainerElement
.createChild("span");
1145 mediaContainerElement
.insertBefore(decoration
, mediaTextElement
);
1146 decoration
.textContent
= "@media ";
1147 decoration
.title
= media
.text
;
1148 mediaTextElement
.textContent
= media
.text
;
1149 if (media
.parentStyleSheetId
) {
1150 mediaDataElement
.classList
.add("editable-media");
1151 mediaTextElement
.addEventListener("click", this._handleMediaRuleClick
.bind(this, media
, mediaTextElement
), false);
1154 case WebInspector
.CSSMedia
.Source
.IMPORT_RULE
:
1155 mediaTextElement
.textContent
= "@import " + media
.text
;
1161 _updateMediaList: function()
1163 this._mediaListElement
.removeChildren();
1164 this._createMediaList(this.styleRule
.media());
1168 * @param {string} propertyName
1171 isPropertyInherited: function(propertyName
)
1173 if (this.styleRule
.inherited()) {
1174 // While rendering inherited stylesheet, reverse meaning of this property.
1175 // Render truly inherited properties with black, i.e. return them as non-inherited.
1176 return !WebInspector
.CSSMetadata
.isPropertyInherited(propertyName
);
1182 * @return {?WebInspector.StylePropertiesSection}
1184 nextEditableSibling: function()
1186 var curSection
= this;
1188 curSection
= curSection
.nextSibling();
1189 } while (curSection
&& !curSection
.editable
);
1192 curSection
= this.firstSibling();
1193 while (curSection
&& !curSection
.editable
)
1194 curSection
= curSection
.nextSibling();
1197 return (curSection
&& curSection
.editable
) ? curSection
: null;
1201 * @return {?WebInspector.StylePropertiesSection}
1203 previousEditableSibling: function()
1205 var curSection
= this;
1207 curSection
= curSection
.previousSibling();
1208 } while (curSection
&& !curSection
.editable
);
1211 curSection
= this.lastSibling();
1212 while (curSection
&& !curSection
.editable
)
1213 curSection
= curSection
.previousSibling();
1216 return (curSection
&& curSection
.editable
) ? curSection
: null;
1220 * @param {boolean} full
1222 update: function(full
)
1224 if (this.styleRule
.selectorText())
1225 this._selectorElement
.textContent
= this.styleRule
.selectorText();
1226 this._markSelectorMatches();
1228 this.propertiesTreeOutline
.removeChildren();
1231 var child
= this.propertiesTreeOutline
.firstChild();
1233 child
.setOverloaded(this.styleRule
.isPropertyOverloaded(child
.name
));
1234 child
= child
.traverseNextTreeElement(false, null, true);
1240 afterUpdate: function()
1242 if (this._afterUpdate
) {
1243 this._afterUpdate(this);
1244 delete this._afterUpdate
;
1245 this._afterUpdateFinishedForTest();
1249 _afterUpdateFinishedForTest: function()
1253 onpopulate: function()
1255 var style
= this.styleRule
.style();
1256 for (var property
of style
.leadingProperties()) {
1257 var isShorthand
= !!WebInspector
.CSSMetadata
.cssPropertiesMetainfo
.longhands(property
.name
);
1258 var inherited
= this.isPropertyInherited(property
.name
);
1259 var overloaded
= this.styleRule
.isPropertyOverloaded(property
.name
);
1260 var item
= new WebInspector
.StylePropertyTreeElement(this._parentPane
, this.styleRule
, property
, isShorthand
, inherited
, overloaded
);
1261 this.propertiesTreeOutline
.appendChild(item
);
1268 _updateFilter: function()
1270 var hasMatchingChild
= false;
1271 for (var child
of this.propertiesTreeOutline
.rootElement().children())
1272 hasMatchingChild
|= child
._updateFilter();
1274 var regex
= this._parentPane
.filterRegex();
1275 var hideRule
= !hasMatchingChild
&& regex
&& !regex
.test(this.element
.textContent
);
1276 this.element
.classList
.toggle("hidden", hideRule
);
1277 if (!hideRule
&& this.styleRule
.rule())
1278 this._markSelectorHighlights();
1282 _markSelectorMatches: function()
1284 var rule
= this.styleRule
.rule();
1288 this._mediaListElement
.classList
.toggle("media-matches", this.styleRule
.mediaMatches());
1290 if (!this.styleRule
.hasMatchingSelectors())
1293 var selectors
= rule
.selectors
;
1294 var fragment
= createDocumentFragment();
1295 var currentMatch
= 0;
1296 var matchingSelectors
= rule
.matchingSelectors
;
1297 for (var i
= 0; i
< selectors
.length
; ++i
) {
1299 fragment
.createTextChild(", ");
1300 var isSelectorMatching
= matchingSelectors
[currentMatch
] === i
;
1301 if (isSelectorMatching
)
1303 var matchingSelectorClass
= isSelectorMatching
? " selector-matches" : "";
1304 var selectorElement
= createElement("span");
1305 selectorElement
.className
= "simple-selector" + matchingSelectorClass
;
1306 if (rule
.styleSheetId
)
1307 selectorElement
._selectorIndex
= i
;
1308 selectorElement
.textContent
= selectors
[i
].value
;
1310 fragment
.appendChild(selectorElement
);
1313 this._selectorElement
.removeChildren();
1314 this._selectorElement
.appendChild(fragment
);
1315 this._markSelectorHighlights();
1318 _markSelectorHighlights: function()
1320 var selectors
= this._selectorElement
.getElementsByClassName("simple-selector");
1321 var regex
= this._parentPane
.filterRegex();
1322 for (var i
= 0; i
< selectors
.length
; ++i
) {
1323 var selectorMatchesFilter
= regex
&& regex
.test(selectors
[i
].textContent
);
1324 selectors
[i
].classList
.toggle("filter-match", selectorMatchesFilter
);
1331 _checkWillCancelEditing: function()
1333 var willCauseCancelEditing
= this._willCauseCancelEditing
;
1334 delete this._willCauseCancelEditing
;
1335 return willCauseCancelEditing
;
1339 * @param {!Event} event
1341 _handleSelectorContainerClick: function(event
)
1343 if (this._checkWillCancelEditing() || !this.editable
)
1345 if (event
.target
=== this._selectorContainer
) {
1346 this.addNewBlankProperty(0).startEditing();
1347 event
.consume(true);
1352 * @param {number=} index
1353 * @return {!WebInspector.StylePropertyTreeElement}
1355 addNewBlankProperty: function(index
)
1357 var property
= this.styleRule
.style().newBlankProperty(index
);
1358 var item
= new WebInspector
.StylePropertyTreeElement(this._parentPane
, this.styleRule
, property
, false, false, false);
1359 index
= property
.index
;
1360 this.propertiesTreeOutline
.insertChild(item
, index
);
1361 item
.listItemElement
.textContent
= "";
1362 item
._newProperty
= true;
1367 _handleEmptySpaceMouseDown: function()
1369 this._willCauseCancelEditing
= this._parentPane
._isEditingStyle
;
1373 * @param {!Event} event
1375 _handleEmptySpaceClick: function(event
)
1380 if (!event
.target
.isComponentSelectionCollapsed())
1383 if (this._checkWillCancelEditing())
1386 if (event
.target
.enclosingNodeOrSelfWithNodeName("a"))
1389 if (event
.target
.classList
.contains("header") || this.element
.classList
.contains("read-only") || event
.target
.enclosingNodeOrSelfWithClass("media")) {
1393 this.addNewBlankProperty().startEditing();
1394 event
.consume(true);
1398 * @param {!WebInspector.CSSMedia} media
1399 * @param {!Element} element
1400 * @param {!Event} event
1402 _handleMediaRuleClick: function(media
, element
, event
)
1404 if (WebInspector
.isBeingEdited(element
))
1407 var config
= new WebInspector
.InplaceEditor
.Config(this._editingMediaCommitted
.bind(this, media
), this._editingMediaCancelled
.bind(this, element
), undefined, this._editingMediaBlurHandler
.bind(this));
1408 WebInspector
.InplaceEditor
.startEditing(element
, config
);
1410 element
.getComponentSelection().setBaseAndExtent(element
, 0, element
, 1);
1411 this._parentPane
.setEditingStyle(true);
1412 var parentMediaElement
= element
.enclosingNodeOrSelfWithClass("media");
1413 parentMediaElement
.classList
.add("editing-media");
1415 event
.consume(true);
1419 * @param {!Element} element
1421 _editingMediaFinished: function(element
)
1423 this._parentPane
.setEditingStyle(false);
1424 var parentMediaElement
= element
.enclosingNodeOrSelfWithClass("media");
1425 parentMediaElement
.classList
.remove("editing-media");
1429 * @param {!Element} element
1431 _editingMediaCancelled: function(element
)
1433 this._editingMediaFinished(element
);
1434 // Mark the selectors in group if necessary.
1435 // This is overridden by BlankStylePropertiesSection.
1436 this._markSelectorMatches();
1437 element
.getComponentSelection().collapse(element
, 0);
1441 * @param {!Element} editor
1442 * @param {!Event} blurEvent
1445 _editingMediaBlurHandler: function(editor
, blurEvent
)
1451 * @param {!WebInspector.CSSMedia} media
1452 * @param {!Element} element
1453 * @param {string} newContent
1454 * @param {string} oldContent
1455 * @param {(!WebInspector.StylePropertyTreeElement.Context|undefined)} context
1456 * @param {string} moveDirection
1458 _editingMediaCommitted: function(media
, element
, newContent
, oldContent
, context
, moveDirection
)
1460 this._parentPane
.setEditingStyle(false);
1461 this._editingMediaFinished(element
);
1464 newContent
= newContent
.trim();
1467 * @param {?WebInspector.CSSMedia} newMedia
1468 * @this {WebInspector.StylePropertiesSection}
1470 function userCallback(newMedia
)
1473 this._parentPane
._styleSheetMediaEdited(media
, newMedia
);
1474 this._parentPane
._refreshUpdate(this);
1476 delete this._parentPane
._userOperation
;
1477 this._editingMediaTextCommittedForTest();
1480 // This gets deleted in finishOperation(), which is called both on success and failure.
1481 this._parentPane
._userOperation
= true;
1482 this._parentPane
._cssModel
.setMediaText(media
, newContent
, userCallback
.bind(this));
1485 _editingMediaTextCommittedForTest: function() { },
1488 * @param {!Event} event
1490 _handleSelectorClick: function(event
)
1492 if (WebInspector
.KeyboardShortcut
.eventHasCtrlOrMeta(/** @type {!MouseEvent} */(event
)) && this.navigable
&& event
.target
.classList
.contains("simple-selector")) {
1493 var index
= event
.target
._selectorIndex
;
1494 var cssModel
= this._parentPane
._cssModel
;
1495 var rule
= this.rule();
1496 var rawLocation
= new WebInspector
.CSSLocation(cssModel
, /** @type {string} */(rule
.styleSheetId
), rule
.sourceURL
, rule
.lineNumberInSource(index
), rule
.columnNumberInSource(index
));
1497 var uiLocation
= WebInspector
.cssWorkspaceBinding
.rawLocationToUILocation(rawLocation
);
1499 WebInspector
.Revealer
.reveal(uiLocation
);
1500 event
.consume(true);
1503 this._startEditingOnMouseEvent();
1504 event
.consume(true);
1507 _startEditingOnMouseEvent: function()
1512 if (!this.rule() && !this.propertiesTreeOutline
.rootElement().childCount()) {
1513 this.addNewBlankProperty().startEditing();
1520 this.startEditingSelector();
1523 startEditingSelector: function()
1525 var element
= this._selectorElement
;
1526 if (WebInspector
.isBeingEdited(element
))
1529 element
.scrollIntoViewIfNeeded(false);
1530 element
.textContent
= element
.textContent
; // Reset selector marks in group.
1532 var config
= new WebInspector
.InplaceEditor
.Config(this.editingSelectorCommitted
.bind(this), this.editingSelectorCancelled
.bind(this), undefined, this._editingSelectorBlurHandler
.bind(this));
1533 WebInspector
.InplaceEditor
.startEditing(this._selectorElement
, config
);
1535 element
.getComponentSelection().setBaseAndExtent(element
, 0, element
, 1);
1536 this._parentPane
.setEditingStyle(true);
1537 this._parentPane
._startEditingSelector(this);
1541 * @param {string} text
1543 setSelectorText: function(text
)
1545 this._selectorElement
.textContent
= text
;
1546 this._selectorElement
.getComponentSelection().setBaseAndExtent(this._selectorElement
, 0, this._selectorElement
, 1);
1550 * @param {!Element} editor
1551 * @param {!Event} blurEvent
1554 _editingSelectorBlurHandler: function(editor
, blurEvent
)
1556 if (!blurEvent
.relatedTarget
)
1558 var elementTreeOutline
= blurEvent
.relatedTarget
.enclosingNodeOrSelfWithClass("elements-tree-outline");
1559 if (!elementTreeOutline
)
1566 * @param {string} moveDirection
1568 _moveEditorFromSelector: function(moveDirection
)
1570 this._markSelectorMatches();
1575 if (moveDirection
=== "forward") {
1576 var firstChild
= this.propertiesTreeOutline
.firstChild();
1577 while (firstChild
&& firstChild
.inherited())
1578 firstChild
= firstChild
.nextSibling
;
1580 this.addNewBlankProperty().startEditing();
1582 firstChild
.startEditing(firstChild
.nameElement
);
1584 var previousSection
= this.previousEditableSibling();
1585 if (!previousSection
)
1588 previousSection
.addNewBlankProperty().startEditing();
1593 * @param {!Element} element
1594 * @param {string} newContent
1595 * @param {string} oldContent
1596 * @param {(!WebInspector.StylePropertyTreeElement.Context|undefined)} context
1597 * @param {string} moveDirection
1599 editingSelectorCommitted: function(element
, newContent
, oldContent
, context
, moveDirection
)
1601 this._editingSelectorEnded();
1603 newContent
= newContent
.trim();
1604 if (newContent
=== oldContent
) {
1605 // Revert to a trimmed version of the selector if need be.
1606 this._selectorElement
.textContent
= newContent
;
1607 this._moveEditorFromSelector(moveDirection
);
1612 * @param {?WebInspector.CSSRule} newRule
1613 * @this {WebInspector.StylePropertiesSection}
1615 function finishCallback(newRule
)
1618 var doesAffectSelectedNode
= newRule
.matchingSelectors
.length
> 0;
1619 this.element
.classList
.toggle("no-affect", !doesAffectSelectedNode
);
1621 var oldSelectorRange
= /** @type {!WebInspector.TextRange} */(this.rule().selectorRange());
1622 var newSelectorRange
= /** @type {!WebInspector.TextRange} */(newRule
.selectorRange());
1623 this.styleRule
.updateRule(newRule
);
1625 this._parentPane
._refreshUpdate(this);
1626 this._parentPane
._styleSheetRuleEdited(newRule
, oldSelectorRange
, newSelectorRange
);
1629 delete this._parentPane
._userOperation
;
1630 this._moveEditorFromSelector(moveDirection
);
1631 this._editingSelectorCommittedForTest();
1634 // This gets deleted in finishOperationAndMoveEditor(), which is called both on success and failure.
1635 this._parentPane
._userOperation
= true;
1636 var selectedNode
= this._parentPane
.node();
1637 this._parentPane
._cssModel
.setRuleSelector(this.rule(), selectedNode
? selectedNode
.id
: 0, newContent
, finishCallback
.bind(this));
1640 _editingSelectorCommittedForTest: function() { },
1642 _updateRuleOrigin: function()
1644 this._selectorRefElement
.removeChildren();
1645 this._selectorRefElement
.appendChild(WebInspector
.StylePropertiesSection
.createRuleOriginNode(this._parentPane
._cssModel
, this._parentPane
._linkifier
, this.rule()));
1648 _editingSelectorEnded: function()
1650 this._parentPane
.setEditingStyle(false);
1651 this._parentPane
._finishEditingSelector();
1654 editingSelectorCancelled: function()
1656 this._editingSelectorEnded();
1658 // Mark the selectors in group if necessary.
1659 // This is overridden by BlankStylePropertiesSection.
1660 this._markSelectorMatches();
1665 * @param {!WebInspector.CSSStyleModel} cssModel
1666 * @param {!WebInspector.Linkifier} linkifier
1667 * @param {?WebInspector.CSSRule} rule
1670 WebInspector
.StylePropertiesSection
.createRuleOriginNode = function(cssModel
, linkifier
, rule
)
1673 return createTextNode("");
1675 var firstMatchingIndex
= rule
.matchingSelectors
&& rule
.matchingSelectors
.length
? rule
.matchingSelectors
[0] : 0;
1676 var ruleLocation
= rule
.selectors
[firstMatchingIndex
].range
;
1678 var header
= rule
.styleSheetId
? cssModel
.styleSheetHeaderForId(rule
.styleSheetId
) : null;
1679 if (ruleLocation
&& rule
.styleSheetId
&& header
&& header
.resourceURL())
1680 return WebInspector
.StylePropertiesSection
._linkifyRuleLocation(cssModel
, linkifier
, rule
.styleSheetId
, ruleLocation
);
1682 if (rule
.isUserAgent())
1683 return createTextNode(WebInspector
.UIString("user agent stylesheet"));
1684 if (rule
.isInjected())
1685 return createTextNode(WebInspector
.UIString("injected stylesheet"));
1686 if (rule
.isViaInspector())
1687 return createTextNode(WebInspector
.UIString("via inspector"));
1689 if (header
&& header
.ownerNode
) {
1690 var link
= WebInspector
.DOMPresentationUtils
.linkifyDeferredNodeReference(header
.ownerNode
);
1691 link
.textContent
= "<style>…</style>";
1695 return createTextNode("");
1699 * @param {!WebInspector.CSSStyleModel} cssModel
1700 * @param {!WebInspector.Linkifier} linkifier
1701 * @param {string} styleSheetId
1702 * @param {!WebInspector.TextRange} ruleLocation
1705 WebInspector
.StylePropertiesSection
._linkifyRuleLocation = function(cssModel
, linkifier
, styleSheetId
, ruleLocation
)
1707 var styleSheetHeader
= cssModel
.styleSheetHeaderForId(styleSheetId
);
1708 var sourceURL
= styleSheetHeader
.resourceURL();
1709 var lineNumber
= styleSheetHeader
.lineNumberInSource(ruleLocation
.startLine
);
1710 var columnNumber
= styleSheetHeader
.columnNumberInSource(ruleLocation
.startLine
, ruleLocation
.startColumn
);
1711 var matchingSelectorLocation
= new WebInspector
.CSSLocation(cssModel
, styleSheetId
, sourceURL
, lineNumber
, columnNumber
);
1712 return linkifier
.linkifyCSSLocation(matchingSelectorLocation
);
1717 * @extends {WebInspector.StylePropertiesSection}
1718 * @param {!WebInspector.StylesSidebarPane} stylesPane
1719 * @param {string} defaultSelectorText
1720 * @param {string} styleSheetId
1721 * @param {!WebInspector.TextRange} ruleLocation
1722 * @param {!WebInspector.StylesSectionModel} insertAfterStyleRule
1724 WebInspector
.BlankStylePropertiesSection = function(stylesPane
, defaultSelectorText
, styleSheetId
, ruleLocation
, insertAfterStyleRule
)
1726 var dummyCascade
= new WebInspector
.SectionCascade();
1727 var blankSectionModel
= dummyCascade
.appendModelFromStyle(WebInspector
.CSSStyleDeclaration
.createDummyStyle(stylesPane
._cssModel
), defaultSelectorText
);
1728 blankSectionModel
.setEditable(true);
1729 WebInspector
.StylePropertiesSection
.call(this, stylesPane
, blankSectionModel
);
1730 this._ruleLocation
= ruleLocation
;
1731 this._styleSheetId
= styleSheetId
;
1732 this._selectorRefElement
.removeChildren();
1733 this._selectorRefElement
.appendChild(WebInspector
.StylePropertiesSection
._linkifyRuleLocation(this._parentPane
._cssModel
, this._parentPane
._linkifier
, styleSheetId
, this._actualRuleLocation()));
1734 if (insertAfterStyleRule
)
1735 this._createMediaList(insertAfterStyleRule
.media());
1736 this._insertAfterStyleRule
= insertAfterStyleRule
;
1737 this.element
.classList
.add("blank-section");
1740 WebInspector
.BlankStylePropertiesSection
.prototype = {
1742 * @return {!WebInspector.TextRange}
1744 _actualRuleLocation: function()
1746 var prefix
= this._rulePrefix();
1747 var lines
= prefix
.split("\n");
1748 var editRange
= new WebInspector
.TextRange(0, 0, lines
.length
- 1, lines
.peekLast().length
);
1749 return this._ruleLocation
.rebaseAfterTextEdit(WebInspector
.TextRange
.createFromLocation(0, 0), editRange
);
1755 _rulePrefix: function()
1757 return this._ruleLocation
.startLine
=== 0 && this._ruleLocation
.startColumn
=== 0 ? "" : "\n\n";
1765 return !this._normal
;
1770 * @param {!Element} element
1771 * @param {string} newContent
1772 * @param {string} oldContent
1773 * @param {!WebInspector.StylePropertyTreeElement.Context|undefined} context
1774 * @param {string} moveDirection
1776 editingSelectorCommitted: function(element
, newContent
, oldContent
, context
, moveDirection
)
1778 if (!this.isBlank
) {
1779 WebInspector
.StylePropertiesSection
.prototype.editingSelectorCommitted
.call(this, element
, newContent
, oldContent
, context
, moveDirection
);
1784 * @param {?WebInspector.CSSRule} newRule
1785 * @this {WebInspector.StylePropertiesSection}
1787 function userCallback(newRule
)
1790 this.editingSelectorCancelled();
1791 this._editingSelectorCommittedForTest();
1794 var doesSelectorAffectSelectedNode
= newRule
.matchingSelectors
.length
> 0;
1795 this._makeNormal(newRule
);
1797 if (!doesSelectorAffectSelectedNode
)
1798 this.element
.classList
.add("no-affect");
1800 var ruleTextLines
= ruleText
.split("\n");
1801 var startLine
= this._ruleLocation
.startLine
;
1802 var startColumn
= this._ruleLocation
.startColumn
;
1803 var newRange
= new WebInspector
.TextRange(startLine
, startColumn
, startLine
+ ruleTextLines
.length
- 1, startColumn
+ ruleTextLines
[ruleTextLines
.length
- 1].length
);
1804 this._parentPane
._styleSheetRuleEdited(newRule
, this._ruleLocation
, newRange
);
1806 this._updateRuleOrigin();
1807 if (this.element
.parentElement
) // Might have been detached already.
1808 this._moveEditorFromSelector(moveDirection
);
1810 delete this._parentPane
._userOperation
;
1811 this._editingSelectorEnded();
1812 this._markSelectorMatches();
1814 this._editingSelectorCommittedForTest();
1818 newContent
= newContent
.trim();
1819 this._parentPane
._userOperation
= true;
1821 var cssModel
= this._parentPane
._cssModel
;
1822 var ruleText
= this._rulePrefix() + newContent
+ " {}";
1823 cssModel
.addRule(this._styleSheetId
, this._parentPane
.node(), ruleText
, this._ruleLocation
, userCallback
.bind(this));
1826 editingSelectorCancelled: function()
1828 delete this._parentPane
._userOperation
;
1829 if (!this.isBlank
) {
1830 WebInspector
.StylePropertiesSection
.prototype.editingSelectorCancelled
.call(this);
1834 this._editingSelectorEnded();
1835 this._parentPane
.removeSection(this);
1839 * @param {!WebInspector.CSSRule} newRule
1841 _makeNormal: function(newRule
)
1843 this.element
.classList
.remove("blank-section");
1844 var model
= this._insertAfterStyleRule
.cascade().insertModelFromRule(newRule
, this._insertAfterStyleRule
);
1845 this.styleRule
= model
;
1847 // FIXME: replace this instance by a normal WebInspector.StylePropertiesSection.
1848 this._normal
= true;
1851 __proto__
: WebInspector
.StylePropertiesSection
.prototype
1856 * @extends {TreeElement}
1857 * @param {!WebInspector.StylesSidebarPane} stylesPane
1858 * @param {!WebInspector.StylesSectionModel} styleRule
1859 * @param {!WebInspector.CSSProperty} property
1860 * @param {boolean} isShorthand
1861 * @param {boolean} inherited
1862 * @param {boolean} overloaded
1864 WebInspector
.StylePropertyTreeElement = function(stylesPane
, styleRule
, property
, isShorthand
, inherited
, overloaded
)
1866 // Pass an empty title, the title gets made later in onattach.
1867 TreeElement
.call(this, "", isShorthand
);
1868 this._styleRule
= styleRule
;
1869 this.property
= property
;
1870 this._inherited
= inherited
;
1871 this._overloaded
= overloaded
;
1872 this.selectable
= false;
1873 this._parentPane
= stylesPane
;
1874 this.isShorthand
= isShorthand
;
1875 this._applyStyleThrottler
= new WebInspector
.Throttler(0);
1878 /** @typedef {{expanded: boolean, hasChildren: boolean, isEditingName: boolean, previousContent: string}} */
1879 WebInspector
.StylePropertyTreeElement
.Context
;
1881 WebInspector
.StylePropertyTreeElement
.prototype = {
1883 * @return {!WebInspector.CSSStyleDeclaration}
1887 return this._styleRule
.style();
1893 inherited: function()
1895 return this._inherited
;
1901 overloaded: function()
1903 return this._overloaded
;
1907 * @param {boolean} x
1909 setOverloaded: function(x
)
1911 if (x
=== this._overloaded
)
1913 this._overloaded
= x
;
1914 this._updateState();
1919 return this.property
.name
;
1924 return this.property
.value
;
1930 _updateFilter: function()
1932 var regex
= this._parentPane
.filterRegex();
1933 var matches
= !!regex
&& (regex
.test(this.property
.name
) || regex
.test(this.property
.value
));
1934 this.listItemElement
.classList
.toggle("filter-match", matches
);
1937 var hasMatchingChildren
= false;
1938 for (var i
= 0; i
< this.childCount(); ++i
)
1939 hasMatchingChildren
|= this.childAt(i
)._updateFilter();
1942 if (this._expandedDueToFilter
)
1944 this._expandedDueToFilter
= false;
1945 } else if (hasMatchingChildren
&& !this.expanded
) {
1947 this._expandedDueToFilter
= true;
1948 } else if (!hasMatchingChildren
&& this.expanded
&& this._expandedDueToFilter
) {
1950 this._expandedDueToFilter
= false;
1956 * @param {string} text
1959 _processColor: function(text
)
1961 // We can be called with valid non-color values of |text| (like 'none' from border style)
1962 var color
= WebInspector
.Color
.parse(text
);
1964 return createTextNode(text
);
1966 if (!this._styleRule
.editable()) {
1967 var swatch
= WebInspector
.ColorSwatch
.create();
1968 swatch
.setColorText(text
);
1972 var stylesPopoverHelper
= this._parentPane
._stylesPopoverHelper
;
1973 var swatchIcon
= new WebInspector
.ColorSwatchPopoverIcon(this, stylesPopoverHelper
, text
);
1976 * @param {?Map.<string, string>} styles
1978 function computedCallback(styles
)
1982 var bgColorText
= styles
.get("background-color") || "";
1983 var bgColor
= WebInspector
.Color
.parse(bgColorText
);
1984 // TODO(aboxhall): for background color with alpha, compute the actual
1985 // visible background color (blended with content underneath).
1986 if (bgColor
&& !bgColor
.hasAlpha())
1987 swatchIcon
.setContrastColor(bgColor
);
1990 if (this.property
.name
=== "color" && this._parentPane
.cssModel() && this.node()) {
1991 var cssModel
= this._parentPane
.cssModel();
1992 cssModel
.computedStylePromise(this.node().id
).then(computedCallback
);
1995 return swatchIcon
.element();
2001 renderedPropertyText: function()
2003 return this.nameElement
.textContent
+ ": " + this.valueElement
.textContent
;
2007 * @param {string} text
2010 _processBezier: function(text
)
2012 var geometry
= WebInspector
.Geometry
.CubicBezier
.parse(text
);
2013 if (!geometry
|| !this._styleRule
.editable())
2014 return createTextNode(text
);
2015 var stylesPopoverHelper
= this._parentPane
._stylesPopoverHelper
;
2016 return new WebInspector
.BezierPopoverIcon(this, stylesPopoverHelper
, text
).element();
2019 _updateState: function()
2021 if (!this.listItemElement
)
2024 if (this.style().isPropertyImplicit(this.name
))
2025 this.listItemElement
.classList
.add("implicit");
2027 this.listItemElement
.classList
.remove("implicit");
2029 var hasIgnorableError
= !this.property
.parsedOk
&& WebInspector
.StylesSidebarPane
.ignoreErrorsForProperty(this.property
);
2030 if (hasIgnorableError
)
2031 this.listItemElement
.classList
.add("has-ignorable-error");
2033 this.listItemElement
.classList
.remove("has-ignorable-error");
2035 if (this.inherited())
2036 this.listItemElement
.classList
.add("inherited");
2038 this.listItemElement
.classList
.remove("inherited");
2040 if (this.overloaded())
2041 this.listItemElement
.classList
.add("overloaded");
2043 this.listItemElement
.classList
.remove("overloaded");
2045 if (this.property
.disabled
)
2046 this.listItemElement
.classList
.add("disabled");
2048 this.listItemElement
.classList
.remove("disabled");
2052 * @return {?WebInspector.DOMNode}
2056 return this._parentPane
.node();
2060 * @return {!WebInspector.StylesSidebarPane}
2062 parentPane: function()
2064 return this._parentPane
;
2068 * @return {?WebInspector.StylePropertiesSection}
2072 return this.treeOutline
&& this.treeOutline
.section
;
2075 _updatePane: function()
2077 var section
= this.section();
2078 if (section
&& section
._parentPane
)
2079 section
._parentPane
._refreshUpdate(section
);
2083 * @param {!WebInspector.TextRange} oldStyleRange
2085 _styleTextEdited: function(oldStyleRange
)
2087 var newStyleRange
= /** @type {!WebInspector.TextRange} */ (this.style().range
);
2088 this._styleRule
.resetCachedData();
2089 if (this._styleRule
.rule())
2090 this._parentPane
._styleSheetRuleEdited(/** @type {!WebInspector.CSSRule} */(this._styleRule
.rule()), oldStyleRange
, newStyleRange
);
2094 * @param {!Event} event
2096 _toggleEnabled: function(event
)
2098 var disabled
= !event
.target
.checked
;
2099 var oldStyleRange
= this.style().range
;
2104 * @param {boolean} success
2105 * @this {WebInspector.StylePropertyTreeElement}
2107 function callback(success
)
2109 delete this._parentPane
._userOperation
;
2113 this._styleTextEdited(oldStyleRange
);
2115 this.styleTextAppliedForTest();
2119 this._parentPane
._userOperation
= true;
2120 this.property
.setDisabled(disabled
)
2121 .then(callback
.bind(this));
2127 onpopulate: function()
2129 // Only populate once and if this property is a shorthand.
2130 if (this.childCount() || !this.isShorthand
)
2133 var longhandProperties
= this.style().longhandProperties(this.name
);
2134 for (var i
= 0; i
< longhandProperties
.length
; ++i
) {
2135 var name
= longhandProperties
[i
].name
;
2136 var inherited
= false;
2137 var overloaded
= false;
2139 var section
= this.section();
2141 inherited
= section
.isPropertyInherited(name
);
2142 overloaded
= section
.styleRule
.isPropertyOverloaded(name
);
2145 var item
= new WebInspector
.StylePropertyTreeElement(this._parentPane
, this._styleRule
, longhandProperties
[i
], false, inherited
, overloaded
);
2146 this.appendChild(item
);
2153 onattach: function()
2157 this.listItemElement
.addEventListener("mousedown", this._mouseDown
.bind(this));
2158 this.listItemElement
.addEventListener("mouseup", this._resetMouseDownElement
.bind(this));
2159 this.listItemElement
.addEventListener("click", this._mouseClick
.bind(this));
2163 * @param {!Event} event
2165 _mouseDown: function(event
)
2167 if (this._parentPane
) {
2168 this._parentPane
._mouseDownTreeElement
= this;
2169 this._parentPane
._mouseDownTreeElementIsName
= this.nameElement
&& this.nameElement
.isSelfOrAncestor(event
.target
);
2170 this._parentPane
._mouseDownTreeElementIsValue
= this.valueElement
&& this.valueElement
.isSelfOrAncestor(event
.target
);
2174 _resetMouseDownElement: function()
2176 if (this._parentPane
) {
2177 delete this._parentPane
._mouseDownTreeElement
;
2178 delete this._parentPane
._mouseDownTreeElementIsName
;
2179 delete this._parentPane
._mouseDownTreeElementIsValue
;
2183 updateTitle: function()
2185 this._updateState();
2186 this._expandElement
= createElement("span");
2187 this._expandElement
.className
= "expand-element";
2189 var propertyRenderer
= new WebInspector
.StylesSidebarPropertyRenderer(this._styleRule
.rule(), this.node(), this.name
, this.value
);
2190 if (this.property
.parsedOk
) {
2191 propertyRenderer
.setColorHandler(this._processColor
.bind(this));
2192 propertyRenderer
.setBezierHandler(this._processBezier
.bind(this));
2195 this.listItemElement
.removeChildren();
2196 this.nameElement
= propertyRenderer
.renderName();
2197 this.valueElement
= propertyRenderer
.renderValue();
2198 if (!this.treeOutline
)
2201 var indent
= WebInspector
.moduleSetting("textEditorIndent").get();
2202 this.listItemElement
.createChild("span", "styles-clipboard-only").createTextChild(indent
+ (this.property
.disabled
? "/* " : ""));
2203 this.listItemElement
.appendChild(this.nameElement
);
2204 this.listItemElement
.createTextChild(": ");
2205 this.listItemElement
.appendChild(this._expandElement
);
2206 this.listItemElement
.appendChild(this.valueElement
);
2207 this.listItemElement
.createTextChild(";");
2208 if (this.property
.disabled
)
2209 this.listItemElement
.createChild("span", "styles-clipboard-only").createTextChild(" */");
2211 if (!this.property
.parsedOk
) {
2212 // Avoid having longhands under an invalid shorthand.
2213 this.listItemElement
.classList
.add("not-parsed-ok");
2215 // Add a separate exclamation mark IMG element with a tooltip.
2216 this.listItemElement
.insertBefore(WebInspector
.StylesSidebarPane
.createExclamationMark(this.property
), this.listItemElement
.firstChild
);
2218 if (!this.property
.activeInStyle())
2219 this.listItemElement
.classList
.add("inactive");
2220 this._updateFilter();
2222 if (this.property
.parsedOk
&& this.section() && this.parent
.root
) {
2223 var enabledCheckboxElement
= createElement("input");
2224 enabledCheckboxElement
.className
= "enabled-button";
2225 enabledCheckboxElement
.type
= "checkbox";
2226 enabledCheckboxElement
.checked
= !this.property
.disabled
;
2227 enabledCheckboxElement
.addEventListener("click", this._toggleEnabled
.bind(this), false);
2228 this.listItemElement
.insertBefore(enabledCheckboxElement
, this.listItemElement
.firstChild
);
2233 * @param {!Event} event
2235 _mouseClick: function(event
)
2237 if (!event
.target
.isComponentSelectionCollapsed())
2240 event
.consume(true);
2242 if (event
.target
=== this.listItemElement
) {
2243 var section
= this.section();
2244 if (!section
|| !section
.editable
)
2247 if (section
._checkWillCancelEditing())
2249 section
.addNewBlankProperty(this.property
.index
+ 1).startEditing();
2253 if (WebInspector
.KeyboardShortcut
.eventHasCtrlOrMeta(/** @type {!MouseEvent} */(event
)) && this.section().navigable
) {
2254 this._navigateToSource(/** @type {!Element} */(event
.target
));
2258 this.startEditing(/** @type {!Element} */(event
.target
));
2262 * @param {!Element} element
2264 _navigateToSource: function(element
)
2266 console
.assert(this.section().navigable
);
2267 var propertyNameClicked
= element
=== this.nameElement
;
2268 var uiLocation
= WebInspector
.cssWorkspaceBinding
.propertyUILocation(this.property
, propertyNameClicked
);
2270 WebInspector
.Revealer
.reveal(uiLocation
);
2274 * @param {?Element=} selectElement
2276 startEditing: function(selectElement
)
2278 // FIXME: we don't allow editing of longhand properties under a shorthand right now.
2279 if (this.parent
.isShorthand
)
2282 if (selectElement
=== this._expandElement
)
2285 var section
= this.section();
2286 if (section
&& !section
.editable
)
2290 selectElement
= this.nameElement
; // No arguments passed in - edit the name element by default.
2292 selectElement
= selectElement
.enclosingNodeOrSelfWithClass("webkit-css-property") || selectElement
.enclosingNodeOrSelfWithClass("value");
2294 if (WebInspector
.isBeingEdited(selectElement
))
2297 var isEditingName
= selectElement
=== this.nameElement
;
2299 this.valueElement
.textContent
= restoreURLs(this.valueElement
.textContent
, this.value
);
2302 * @param {string} fieldValue
2303 * @param {string} modelValue
2306 function restoreURLs(fieldValue
, modelValue
)
2308 const urlRegex
= /\b(url\([^)]*\))/g;
2309 var splitFieldValue
= fieldValue
.split(urlRegex
);
2310 if (splitFieldValue
.length
=== 1)
2312 var modelUrlRegex
= new RegExp(urlRegex
);
2313 for (var i
= 1; i
< splitFieldValue
.length
; i
+= 2) {
2314 var match
= modelUrlRegex
.exec(modelValue
);
2316 splitFieldValue
[i
] = match
[0];
2318 return splitFieldValue
.join("");
2321 /** @type {!WebInspector.StylePropertyTreeElement.Context} */
2323 expanded
: this.expanded
,
2324 hasChildren
: this.isExpandable(),
2325 isEditingName
: isEditingName
,
2326 previousContent
: selectElement
.textContent
2329 // Lie about our children to prevent expanding on double click and to collapse shorthands.
2330 this.setExpandable(false);
2332 if (selectElement
.parentElement
)
2333 selectElement
.parentElement
.classList
.add("child-editing");
2334 selectElement
.textContent
= selectElement
.textContent
; // remove color swatch and the like
2337 * @param {!WebInspector.StylePropertyTreeElement.Context} context
2338 * @param {!Event} event
2339 * @this {WebInspector.StylePropertyTreeElement}
2341 function pasteHandler(context
, event
)
2343 var data
= event
.clipboardData
.getData("Text");
2346 var colonIdx
= data
.indexOf(":");
2349 var name
= data
.substring(0, colonIdx
).trim();
2350 var value
= data
.substring(colonIdx
+ 1).trim();
2352 event
.preventDefault();
2354 if (!("originalName" in context
)) {
2355 context
.originalName
= this.nameElement
.textContent
;
2356 context
.originalValue
= this.valueElement
.textContent
;
2358 this.property
.name
= name
;
2359 this.property
.value
= value
;
2360 this.nameElement
.textContent
= name
;
2361 this.valueElement
.textContent
= value
;
2362 this.nameElement
.normalize();
2363 this.valueElement
.normalize();
2365 this.editingCommitted(event
.target
.textContent
, context
, "forward");
2369 * @param {!WebInspector.StylePropertyTreeElement.Context} context
2370 * @param {!Event} event
2371 * @this {WebInspector.StylePropertyTreeElement}
2373 function blurListener(context
, event
)
2375 var treeElement
= this._parentPane
._mouseDownTreeElement
;
2376 var moveDirection
= "";
2377 if (treeElement
=== this) {
2378 if (isEditingName
&& this._parentPane
._mouseDownTreeElementIsValue
)
2379 moveDirection
= "forward";
2380 if (!isEditingName
&& this._parentPane
._mouseDownTreeElementIsName
)
2381 moveDirection
= "backward";
2383 this.editingCommitted(event
.target
.textContent
, context
, moveDirection
);
2386 this._originalPropertyText
= this.property
.propertyText
;
2388 this._parentPane
.setEditingStyle(true);
2389 if (selectElement
.parentElement
)
2390 selectElement
.parentElement
.scrollIntoViewIfNeeded(false);
2392 var applyItemCallback
= !isEditingName
? this._applyFreeFlowStyleTextEdit
.bind(this) : undefined;
2393 this._prompt
= new WebInspector
.StylesSidebarPane
.CSSPropertyPrompt(isEditingName
? WebInspector
.CSSMetadata
.cssPropertiesMetainfo
: WebInspector
.CSSMetadata
.keywordsForProperty(this.nameElement
.textContent
), this, isEditingName
);
2394 this._prompt
.setAutocompletionTimeout(0);
2395 if (applyItemCallback
) {
2396 this._prompt
.addEventListener(WebInspector
.TextPrompt
.Events
.ItemApplied
, applyItemCallback
, this);
2397 this._prompt
.addEventListener(WebInspector
.TextPrompt
.Events
.ItemAccepted
, applyItemCallback
, this);
2399 var proxyElement
= this._prompt
.attachAndStartEditing(selectElement
, blurListener
.bind(this, context
));
2401 proxyElement
.addEventListener("keydown", this._editingNameValueKeyDown
.bind(this, context
), false);
2402 proxyElement
.addEventListener("keypress", this._editingNameValueKeyPress
.bind(this, context
), false);
2403 proxyElement
.addEventListener("input", this._editingNameValueInput
.bind(this, context
), false);
2405 proxyElement
.addEventListener("paste", pasteHandler
.bind(this, context
), false);
2407 selectElement
.getComponentSelection().setBaseAndExtent(selectElement
, 0, selectElement
, 1);
2411 * @param {!WebInspector.StylePropertyTreeElement.Context} context
2412 * @param {!Event} event
2414 _editingNameValueKeyDown: function(context
, event
)
2421 if (isEnterKey(event
)) {
2422 event
.preventDefault();
2424 } else if (event
.keyCode
=== WebInspector
.KeyboardShortcut
.Keys
.Esc
.code
|| event
.keyIdentifier
=== "U+001B")
2426 else if (!context
.isEditingName
&& this._newProperty
&& event
.keyCode
=== WebInspector
.KeyboardShortcut
.Keys
.Backspace
.code
) {
2427 // For a new property, when Backspace is pressed at the beginning of new property value, move back to the property name.
2428 var selection
= event
.target
.getComponentSelection();
2429 if (selection
.isCollapsed
&& !selection
.focusOffset
) {
2430 event
.preventDefault();
2431 result
= "backward";
2433 } else if (event
.keyIdentifier
=== "U+0009") { // Tab key.
2434 result
= event
.shiftKey
? "backward" : "forward";
2435 event
.preventDefault();
2441 this.editingCancelled(null, context
);
2445 this.editingCommitted(event
.target
.textContent
, context
, result
);
2455 * @param {!WebInspector.StylePropertyTreeElement.Context} context
2456 * @param {!Event} event
2458 _editingNameValueKeyPress: function(context
, event
)
2461 * @param {string} text
2462 * @param {number} cursorPosition
2465 function shouldCommitValueSemicolon(text
, cursorPosition
)
2467 // FIXME: should this account for semicolons inside comments?
2469 for (var i
= 0; i
< cursorPosition
; ++i
) {
2471 if (ch
=== "\\" && openQuote
!== "")
2472 ++i
; // skip next character inside string
2473 else if (!openQuote
&& (ch
=== "\"" || ch
=== "'"))
2475 else if (openQuote
=== ch
)
2481 var keyChar
= String
.fromCharCode(event
.charCode
);
2482 var isFieldInputTerminated
= (context
.isEditingName
? keyChar
=== ":" : keyChar
=== ";" && shouldCommitValueSemicolon(event
.target
.textContent
, event
.target
.selectionLeftOffset()));
2483 if (isFieldInputTerminated
) {
2484 // Enter or colon (for name)/semicolon outside of string (for value).
2485 event
.consume(true);
2486 this.editingCommitted(event
.target
.textContent
, context
, "forward");
2492 * @param {!WebInspector.StylePropertyTreeElement.Context} context
2493 * @param {!Event} event
2495 _editingNameValueInput: function(context
, event
)
2497 // Do not live-edit "content" property of pseudo elements. crbug.com/433889
2498 if (!context
.isEditingName
&& (!this._parentPane
.node().pseudoType() || this.name
!== "content"))
2499 this._applyFreeFlowStyleTextEdit();
2502 _applyFreeFlowStyleTextEdit: function()
2504 var valueText
= this.valueElement
.textContent
;
2505 if (valueText
.indexOf(";") === -1)
2506 this.applyStyleText(this.nameElement
.textContent
+ ": " + valueText
, false);
2509 kickFreeFlowStyleEditForTest: function()
2511 this._applyFreeFlowStyleTextEdit();
2515 * @param {!WebInspector.StylePropertyTreeElement.Context} context
2517 editingEnded: function(context
)
2519 this._resetMouseDownElement();
2521 this.setExpandable(context
.hasChildren
);
2522 if (context
.expanded
)
2524 var editedElement
= context
.isEditingName
? this.nameElement
: this.valueElement
;
2525 // The proxyElement has been deleted, no need to remove listener.
2526 if (editedElement
.parentElement
)
2527 editedElement
.parentElement
.classList
.remove("child-editing");
2529 this._parentPane
.setEditingStyle(false);
2533 * @param {?Element} element
2534 * @param {!WebInspector.StylePropertyTreeElement.Context} context
2536 editingCancelled: function(element
, context
)
2538 this._removePrompt();
2539 this._revertStyleUponEditingCanceled();
2540 // This should happen last, as it clears the info necessary to restore the property value after [Page]Up/Down changes.
2541 this.editingEnded(context
);
2544 _revertStyleUponEditingCanceled: function()
2546 if (this._propertyHasBeenEditedIncrementally
) {
2547 this.applyStyleText(this._originalPropertyText
, false);
2548 delete this._originalPropertyText
;
2549 } else if (this._newProperty
) {
2550 this.treeOutline
.removeChild(this);
2557 * @param {string} moveDirection
2558 * @return {?WebInspector.StylePropertyTreeElement}
2560 _findSibling: function(moveDirection
)
2564 target
= (moveDirection
=== "forward" ? target
.nextSibling
: target
.previousSibling
);
2565 } while(target
&& target
.inherited());
2571 * @param {string} userInput
2572 * @param {!WebInspector.StylePropertyTreeElement.Context} context
2573 * @param {string} moveDirection
2575 editingCommitted: function(userInput
, context
, moveDirection
)
2577 this._removePrompt();
2578 this.editingEnded(context
);
2579 var isEditingName
= context
.isEditingName
;
2581 // Determine where to move to before making changes
2582 var createNewProperty
, moveToPropertyName
, moveToSelector
;
2583 var isDataPasted
= "originalName" in context
;
2584 var isDirtyViaPaste
= isDataPasted
&& (this.nameElement
.textContent
!== context
.originalName
|| this.valueElement
.textContent
!== context
.originalValue
);
2585 var isPropertySplitPaste
= isDataPasted
&& isEditingName
&& this.valueElement
.textContent
!== context
.originalValue
;
2587 var moveToOther
= (isEditingName
^ (moveDirection
=== "forward"));
2588 var abandonNewProperty
= this._newProperty
&& !userInput
&& (moveToOther
|| isEditingName
);
2589 if (moveDirection
=== "forward" && (!isEditingName
|| isPropertySplitPaste
) || moveDirection
=== "backward" && isEditingName
) {
2590 moveTo
= moveTo
._findSibling(moveDirection
);
2592 moveToPropertyName
= moveTo
.name
;
2593 else if (moveDirection
=== "forward" && (!this._newProperty
|| userInput
))
2594 createNewProperty
= true;
2595 else if (moveDirection
=== "backward")
2596 moveToSelector
= true;
2599 // Make the Changes and trigger the moveToNextCallback after updating.
2600 var moveToIndex
= moveTo
&& this.treeOutline
? this.treeOutline
.rootElement().indexOfChild(moveTo
) : -1;
2601 var blankInput
= /^\s*$/.test(userInput
);
2602 var shouldCommitNewProperty
= this._newProperty
&& (isPropertySplitPaste
|| moveToOther
|| (!moveDirection
&& !isEditingName
) || (isEditingName
&& blankInput
));
2603 var section
= /** @type {!WebInspector.StylePropertiesSection} */(this.section());
2604 if (((userInput
!== context
.previousContent
|| isDirtyViaPaste
) && !this._newProperty
) || shouldCommitNewProperty
) {
2605 section
._afterUpdate
= moveToNextCallback
.bind(this, this._newProperty
, !blankInput
, section
);
2607 if (blankInput
|| (this._newProperty
&& /^\s*$/.test(this.valueElement
.textContent
)))
2611 propertyText
= userInput
+ ": " + this.property
.value
;
2613 propertyText
= this.property
.name
+ ": " + userInput
;
2615 this.applyStyleText(propertyText
, true);
2618 this.property
.name
= userInput
;
2620 this.property
.value
= userInput
;
2621 if (!isDataPasted
&& !this._newProperty
)
2623 moveToNextCallback
.call(this, this._newProperty
, false, section
);
2627 * The Callback to start editing the next/previous property/selector.
2628 * @param {boolean} alreadyNew
2629 * @param {boolean} valueChanged
2630 * @param {!WebInspector.StylePropertiesSection} section
2631 * @this {WebInspector.StylePropertyTreeElement}
2633 function moveToNextCallback(alreadyNew
, valueChanged
, section
)
2638 // User just tabbed through without changes.
2639 if (moveTo
&& moveTo
.parent
) {
2640 moveTo
.startEditing(!isEditingName
? moveTo
.nameElement
: moveTo
.valueElement
);
2644 // User has made a change then tabbed, wiping all the original treeElements.
2645 // Recalculate the new treeElement for the same property we were going to edit next.
2646 if (moveTo
&& !moveTo
.parent
) {
2647 var rootElement
= section
.propertiesTreeOutline
.rootElement();
2648 if (moveDirection
=== "forward" && blankInput
&& !isEditingName
)
2650 if (moveToIndex
>= rootElement
.childCount() && !this._newProperty
)
2651 createNewProperty
= true;
2653 var treeElement
= moveToIndex
>= 0 ? rootElement
.childAt(moveToIndex
) : null;
2655 var elementToEdit
= !isEditingName
|| isPropertySplitPaste
? treeElement
.nameElement
: treeElement
.valueElement
;
2656 if (alreadyNew
&& blankInput
)
2657 elementToEdit
= moveDirection
=== "forward" ? treeElement
.nameElement
: treeElement
.valueElement
;
2658 treeElement
.startEditing(elementToEdit
);
2660 } else if (!alreadyNew
)
2661 moveToSelector
= true;
2665 // Create a new attribute in this section (or move to next editable selector if possible).
2666 if (createNewProperty
) {
2667 if (alreadyNew
&& !valueChanged
&& (isEditingName
^ (moveDirection
=== "backward")))
2670 section
.addNewBlankProperty().startEditing();
2674 if (abandonNewProperty
) {
2675 moveTo
= this._findSibling(moveDirection
);
2676 var sectionToEdit
= (moveTo
|| moveDirection
=== "backward") ? section
: section
.nextEditableSibling();
2677 if (sectionToEdit
) {
2678 if (sectionToEdit
.rule())
2679 sectionToEdit
.startEditingSelector();
2681 sectionToEdit
._moveEditorFromSelector(moveDirection
);
2686 if (moveToSelector
) {
2688 section
.startEditingSelector();
2690 section
._moveEditorFromSelector(moveDirection
);
2695 _removePrompt: function()
2697 // BUG 53242. This cannot go into editingEnded(), as it should always happen first for any editing outcome.
2699 this._prompt
.detach();
2700 delete this._prompt
;
2704 styleTextAppliedForTest: function() { },
2707 * @param {string} styleText
2708 * @param {boolean} majorChange
2710 applyStyleText: function(styleText
, majorChange
)
2712 this._applyStyleThrottler
.schedule(this._innerApplyStyleText
.bind(this, styleText
, majorChange
));
2716 * @param {string} styleText
2717 * @param {boolean} majorChange
2718 * @return {!Promise.<undefined>}
2720 _innerApplyStyleText: function(styleText
, majorChange
)
2722 if (!this.treeOutline
)
2723 return Promise
.resolve();
2725 var oldStyleRange
= this.style().range
;
2727 return Promise
.resolve();
2729 styleText
= styleText
.replace(/\s/g, " ").trim(); // Replace with whitespace.
2730 if (!styleText
.length
&& majorChange
&& this._newProperty
&& !this._propertyHasBeenEditedIncrementally
) {
2731 // The user deleted everything and never applied a new property value via Up/Down scrolling/live editing, so remove the tree element and update.
2732 var section
= this.section();
2733 this.parent
.removeChild(this);
2734 section
.afterUpdate();
2735 return Promise
.resolve();
2738 var currentNode
= this._parentPane
.node();
2739 this._parentPane
._userOperation
= true;
2742 * @param {boolean} success
2743 * @this {WebInspector.StylePropertyTreeElement}
2745 function callback(success
)
2747 delete this._parentPane
._userOperation
;
2751 // It did not apply, cancel editing.
2752 this._revertStyleUponEditingCanceled();
2754 this.styleTextAppliedForTest();
2757 this._styleTextEdited(oldStyleRange
);
2759 this._propertyHasBeenEditedIncrementally
= true;
2760 this.property
= this.style().propertyAt(this.property
.index
);
2762 // We are happy to update UI if user is not editing.
2763 if (!this._parentPane
._isEditingStyle
&& currentNode
=== this.node())
2766 this.styleTextAppliedForTest();
2769 // Append a ";" if the new text does not end in ";".
2770 // FIXME: this does not handle trailing comments.
2771 if (styleText
.length
&& !/;\s*$/.test(styleText
))
2773 var overwriteProperty
= !this._newProperty
|| this._propertyHasBeenEditedIncrementally
;
2774 return this.property
.setText(styleText
, majorChange
, overwriteProperty
)
2775 .then(callback
.bind(this));
2782 ondblclick: function()
2784 return true; // handled
2789 * @param {!Event} event
2792 isEventWithinDisclosureTriangle: function(event
)
2794 return event
.target
=== this._expandElement
;
2797 __proto__
: TreeElement
.prototype
2802 * @extends {WebInspector.TextPrompt}
2803 * @param {!WebInspector.CSSMetadata} cssCompletions
2804 * @param {!WebInspector.StylePropertyTreeElement} treeElement
2805 * @param {boolean} isEditingName
2807 WebInspector
.StylesSidebarPane
.CSSPropertyPrompt = function(cssCompletions
, treeElement
, isEditingName
)
2809 // Use the same callback both for applyItemCallback and acceptItemCallback.
2810 WebInspector
.TextPrompt
.call(this, this._buildPropertyCompletions
.bind(this), WebInspector
.StyleValueDelimiters
);
2811 this.setSuggestBoxEnabled(true);
2812 this._cssCompletions
= cssCompletions
;
2813 this._treeElement
= treeElement
;
2814 this._isEditingName
= isEditingName
;
2817 this.disableDefaultSuggestionForEmptyInput();
2820 WebInspector
.StylesSidebarPane
.CSSPropertyPrompt
.prototype = {
2823 * @param {!Event} event
2825 onKeyDown: function(event
)
2827 switch (event
.keyIdentifier
) {
2832 if (this._handleNameOrValueUpDown(event
)) {
2833 event
.preventDefault();
2838 if (this.autoCompleteElement
&& !this.autoCompleteElement
.textContent
.length
) {
2839 this.tabKeyPressed();
2845 WebInspector
.TextPrompt
.prototype.onKeyDown
.call(this, event
);
2850 * @param {!Event} event
2852 onMouseWheel: function(event
)
2854 if (this._handleNameOrValueUpDown(event
)) {
2855 event
.consume(true);
2858 WebInspector
.TextPrompt
.prototype.onMouseWheel
.call(this, event
);
2865 tabKeyPressed: function()
2867 this.acceptAutoComplete();
2869 // Always tab to the next field.
2874 * @param {!Event} event
2877 _handleNameOrValueUpDown: function(event
)
2880 * @param {string} originalValue
2881 * @param {string} replacementString
2882 * @this {WebInspector.StylesSidebarPane.CSSPropertyPrompt}
2884 function finishHandler(originalValue
, replacementString
)
2886 // Synthesize property text disregarding any comments, custom whitespace etc.
2887 this._treeElement
.applyStyleText(this._treeElement
.nameElement
.textContent
+ ": " + this._treeElement
.valueElement
.textContent
, false);
2891 * @param {string} prefix
2892 * @param {number} number
2893 * @param {string} suffix
2895 * @this {WebInspector.StylesSidebarPane.CSSPropertyPrompt}
2897 function customNumberHandler(prefix
, number
, suffix
)
2899 if (number
!== 0 && !suffix
.length
&& WebInspector
.CSSMetadata
.isLengthProperty(this._treeElement
.property
.name
))
2901 return prefix
+ number
+ suffix
;
2904 // Handle numeric value increment/decrement only at this point.
2905 if (!this._isEditingName
&& WebInspector
.handleElementValueModifications(event
, this._treeElement
.valueElement
, finishHandler
.bind(this), this._isValueSuggestion
.bind(this), customNumberHandler
.bind(this)))
2912 * @param {string} word
2915 _isValueSuggestion: function(word
)
2919 word
= word
.toLowerCase();
2920 return this._cssCompletions
.keySet().hasOwnProperty(word
);
2924 * @param {!Element} proxyElement
2925 * @param {!Range} wordRange
2926 * @param {boolean} force
2927 * @param {function(!Array.<string>, number=)} completionsReadyCallback
2929 _buildPropertyCompletions: function(proxyElement
, wordRange
, force
, completionsReadyCallback
)
2931 var prefix
= wordRange
.toString().toLowerCase();
2932 if (!prefix
&& !force
&& (this._isEditingName
|| proxyElement
.textContent
.length
)) {
2933 completionsReadyCallback([]);
2937 var results
= this._cssCompletions
.startsWith(prefix
);
2938 if (!this._isEditingName
&& !results
.length
&& prefix
.length
> 1 && "!important".startsWith(prefix
))
2939 results
.push("!important");
2940 var userEnteredText
= wordRange
.toString().replace("-", "");
2941 if (userEnteredText
&& (userEnteredText
=== userEnteredText
.toUpperCase())) {
2942 for (var i
= 0; i
< results
.length
; ++i
)
2943 results
[i
] = results
[i
].toUpperCase();
2945 var selectedIndex
= this._cssCompletions
.mostUsedOf(results
);
2946 completionsReadyCallback(results
, selectedIndex
);
2949 __proto__
: WebInspector
.TextPrompt
.prototype
2954 * @param {?WebInspector.CSSRule} rule
2955 * @param {?WebInspector.DOMNode} node
2956 * @param {string} name
2957 * @param {string} value
2959 WebInspector
.StylesSidebarPropertyRenderer = function(rule
, node
, name
, value
)
2963 this._propertyName
= name
;
2964 this._propertyValue
= value
;
2967 WebInspector
.StylesSidebarPropertyRenderer
._colorRegex
= /((?:rgb|hsl)a?\([^)]+\)|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3}|\b\w+\b(?!-))/g;
2968 WebInspector
.StylesSidebarPropertyRenderer
._bezierRegex
= /((cubic-bezier\([^)]+\))|\b(linear|ease-in-out|ease-in|ease-out|ease)\b)/g;
2971 * @param {string} value
2974 WebInspector
.StylesSidebarPropertyRenderer
._urlRegex = function(value
)
2976 // Heuristically choose between single-quoted, double-quoted or plain URL regex.
2977 if (/url\(\s*'.*\s*'\s*\)/.test(value
))
2978 return /url\(\s*('.+')\s*\)/g;
2979 if (/url\(\s*".*\s*"\s*\)/.test(value
))
2980 return /url\(\s*(".+")\s*\)/g;
2981 return /url\(\s*([^)]+)\s*\)/g;
2984 WebInspector
.StylesSidebarPropertyRenderer
.prototype = {
2986 * @param {function(string):!Node} handler
2988 setColorHandler: function(handler
)
2990 this._colorHandler
= handler
;
2994 * @param {function(string):!Node} handler
2996 setBezierHandler: function(handler
)
2998 this._bezierHandler
= handler
;
3002 * @return {!Element}
3004 renderName: function()
3006 var nameElement
= createElement("span");
3007 nameElement
.className
= "webkit-css-property";
3008 nameElement
.textContent
= this._propertyName
;
3009 nameElement
.normalize();
3014 * @return {!Element}
3016 renderValue: function()
3018 var valueElement
= createElement("span");
3019 valueElement
.className
= "value";
3021 if (!this._propertyValue
)
3022 return valueElement
;
3024 var formatter
= new WebInspector
.StringFormatter();
3025 formatter
.addProcessor(WebInspector
.StylesSidebarPropertyRenderer
._urlRegex(this._propertyValue
), this._processURL
.bind(this));
3026 if (this._bezierHandler
&& WebInspector
.CSSMetadata
.isBezierAwareProperty(this._propertyName
))
3027 formatter
.addProcessor(WebInspector
.StylesSidebarPropertyRenderer
._bezierRegex
, this._bezierHandler
);
3028 if (this._colorHandler
&& WebInspector
.CSSMetadata
.isColorAwareProperty(this._propertyName
))
3029 formatter
.addProcessor(WebInspector
.StylesSidebarPropertyRenderer
._colorRegex
, this._colorHandler
);
3031 valueElement
.appendChild(formatter
.formatText(this._propertyValue
));
3032 valueElement
.normalize();
3033 return valueElement
;
3037 * @param {string} url
3040 _processURL: function(url
)
3043 var match
= hrefUrl
.match(/['"]?([^'"]+)/);
3046 var container
= createDocumentFragment();
3047 container
.createTextChild("url(");
3048 if (this._rule
&& this._rule
.resourceURL())
3049 hrefUrl
= WebInspector
.ParsedURL
.completeURL(this._rule
.resourceURL(), hrefUrl
);
3050 else if (this._node
)
3051 hrefUrl
= this._node
.resolveURL(hrefUrl
);
3052 var hasResource
= hrefUrl
&& !!WebInspector
.resourceForURL(hrefUrl
);
3053 // FIXME: WebInspector.linkifyURLAsNode() should really use baseURI.
3054 container
.appendChild(WebInspector
.linkifyURLAsNode(hrefUrl
|| url
, url
, undefined, !hasResource
));
3055 container
.createTextChild(")");
3062 * @extends {WebInspector.Widget}
3063 * @param {!WebInspector.ToolbarItem} toolbarItem
3065 WebInspector
.StylesSidebarPane
.BaseToolbarPaneWidget = function(toolbarItem
)
3067 WebInspector
.Widget
.call(this);
3068 this._toolbarItem
= toolbarItem
;
3069 WebInspector
.context
.addFlavorChangeListener(WebInspector
.DOMNode
, this._nodeChanged
, this);
3072 WebInspector
.StylesSidebarPane
.BaseToolbarPaneWidget
.prototype = {
3073 _nodeChanged: function()
3075 if (!this.isShowing())
3078 var elementNode
= WebInspector
.SharedSidebarModel
.elementNode(WebInspector
.context
.flavor(WebInspector
.DOMNode
));
3079 this.onNodeChanged(elementNode
);
3083 * @param {?WebInspector.DOMNode} newNode
3086 onNodeChanged: function(newNode
)
3093 willHide: function()
3095 this._toolbarItem
.setToggled(false);
3101 wasShown: function()
3103 this._toolbarItem
.setToggled(true);
3104 this._nodeChanged();
3107 __proto__
: WebInspector
.Widget
.prototype
3112 * @implements {WebInspector.ToolbarItem.Provider}
3114 WebInspector
.StylesSidebarPane
.AddNewRuleButtonProvider = function()
3116 this._button
= new WebInspector
.ToolbarButton(WebInspector
.UIString("New Style Rule"), "add-toolbar-item");
3117 this._button
.makeLongClickEnabled();
3118 var stylesSidebarPane
= WebInspector
.ElementsPanel
.instance().sidebarPanes
.styles
;
3119 this._button
.addEventListener("click", stylesSidebarPane
._createNewRuleInViaInspectorStyleSheet
, stylesSidebarPane
);
3120 this._button
.addEventListener("longClickDown", stylesSidebarPane
._onAddButtonLongClick
, stylesSidebarPane
);
3121 WebInspector
.context
.addFlavorChangeListener(WebInspector
.DOMNode
, this._onNodeChanged
, this);
3122 this._onNodeChanged()
3125 WebInspector
.StylesSidebarPane
.AddNewRuleButtonProvider
.prototype = {
3126 _onNodeChanged: function()
3128 var node
= WebInspector
.context
.flavor(WebInspector
.DOMNode
);
3129 this.item().setEnabled(!!node
);
3134 * @return {?WebInspector.ToolbarItem}
3138 return this._button
;