OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2007 Apple Inc. All rights reserved. | |
3 * Copyright (C) 2009 Joseph Pecoraro | |
4 * | |
5 * Redistribution and use in source and binary forms, with or without | |
6 * modification, are permitted provided that the following conditions | |
7 * are met: | |
8 * | |
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. | |
17 * | |
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. | |
28 */ | |
29 | |
30 WebInspector.StylesSidebarPane = function() | |
31 { | |
32 WebInspector.SidebarPane.call(this, WebInspector.UIString("Styles")); | |
33 | |
34 this.settingsSelectElement = document.createElement("select"); | |
35 | |
36 var option = document.createElement("option"); | |
37 option.value = "hex"; | |
38 option.action = this._changeColorFormat.bind(this); | |
39 if (Preferences.colorFormat === "hex") | |
40 option.selected = true; | |
41 option.label = WebInspector.UIString("Hex Colors"); | |
42 this.settingsSelectElement.appendChild(option); | |
43 | |
44 option = document.createElement("option"); | |
45 option.value = "rgb"; | |
46 option.action = this._changeColorFormat.bind(this); | |
47 if (Preferences.colorFormat === "rgb") | |
48 option.selected = true; | |
49 option.label = WebInspector.UIString("RGB Colors"); | |
50 this.settingsSelectElement.appendChild(option); | |
51 | |
52 option = document.createElement("option"); | |
53 option.value = "hsl"; | |
54 option.action = this._changeColorFormat.bind(this); | |
55 if (Preferences.colorFormat === "hsl") | |
56 option.selected = true; | |
57 option.label = WebInspector.UIString("HSL Colors"); | |
58 this.settingsSelectElement.appendChild(option); | |
59 | |
60 this.settingsSelectElement.appendChild(document.createElement("hr")); | |
61 | |
62 option = document.createElement("option"); | |
63 option.action = this._createNewRule.bind(this); | |
64 option.label = WebInspector.UIString("New Style Rule"); | |
65 this.settingsSelectElement.appendChild(option); | |
66 | |
67 this.settingsSelectElement.addEventListener("click", function(event) { event
.stopPropagation() }, false); | |
68 this.settingsSelectElement.addEventListener("change", this._changeSetting.bi
nd(this), false); | |
69 | |
70 this.titleElement.appendChild(this.settingsSelectElement); | |
71 } | |
72 | |
73 WebInspector.StylesSidebarPane.prototype = { | |
74 update: function(node, editedSection, forceUpdate) | |
75 { | |
76 var refresh = false; | |
77 | |
78 if (forceUpdate) | |
79 delete this.node; | |
80 | |
81 if (!forceUpdate && (!node || node === this.node)) | |
82 refresh = true; | |
83 | |
84 if (node && node.nodeType === Node.TEXT_NODE && node.parentNode) | |
85 node = node.parentNode; | |
86 | |
87 if (node && node.nodeType !== Node.ELEMENT_NODE) | |
88 node = null; | |
89 | |
90 if (node) | |
91 this.node = node; | |
92 else | |
93 node = this.node; | |
94 | |
95 var body = this.bodyElement; | |
96 if (!refresh || !node) { | |
97 body.removeChildren(); | |
98 this.sections = []; | |
99 } | |
100 | |
101 if (!node) | |
102 return; | |
103 | |
104 var self = this; | |
105 function callback(styles) | |
106 { | |
107 if (!styles) | |
108 return; | |
109 node._setStyles(styles.computedStyle, styles.inlineStyle, styles.sty
leAttributes, styles.matchedCSSRules); | |
110 self._update(refresh, body, node, editedSection, forceUpdate); | |
111 } | |
112 | |
113 InjectedScriptAccess.getStyles(node.id, !Preferences.showUserAgentStyles
, callback); | |
114 }, | |
115 | |
116 _update: function(refresh, body, node, editedSection, forceUpdate) | |
117 { | |
118 if (!refresh) { | |
119 body.removeChildren(); | |
120 this.sections = []; | |
121 } | |
122 | |
123 var styleRules = []; | |
124 | |
125 if (refresh) { | |
126 for (var i = 0; i < this.sections.length; ++i) { | |
127 var section = this.sections[i]; | |
128 if (section instanceof WebInspector.BlankStylePropertiesSection) | |
129 continue; | |
130 if (section.computedStyle) | |
131 section.styleRule.style = node.ownerDocument.defaultView.get
ComputedStyle(node); | |
132 var styleRule = { section: section, style: section.styleRule.sty
le, computedStyle: section.computedStyle, rule: section.rule }; | |
133 styleRules.push(styleRule); | |
134 } | |
135 } else { | |
136 var computedStyle = node.ownerDocument.defaultView.getComputedStyle(
node); | |
137 styleRules.push({ computedStyle: true, selectorText: WebInspector.UI
String("Computed Style"), style: computedStyle, editable: false }); | |
138 | |
139 var nodeName = node.nodeName.toLowerCase(); | |
140 for (var i = 0; i < node.attributes.length; ++i) { | |
141 var attr = node.attributes[i]; | |
142 if (attr.style) { | |
143 var attrStyle = { style: attr.style, editable: false }; | |
144 attrStyle.subtitle = WebInspector.UIString("element’s “%s” a
ttribute", attr.name); | |
145 attrStyle.selectorText = nodeName + "[" + attr.name; | |
146 if (attr.value.length) | |
147 attrStyle.selectorText += "=" + attr.value; | |
148 attrStyle.selectorText += "]"; | |
149 styleRules.push(attrStyle); | |
150 } | |
151 } | |
152 | |
153 // Always Show element's Style Attributes | |
154 if (node.nodeType === Node.ELEMENT_NODE) { | |
155 var inlineStyle = { selectorText: WebInspector.UIString("Style A
ttribute"), style: node.style, isAttribute: true }; | |
156 inlineStyle.subtitle = WebInspector.UIString("element’s “%s” att
ribute", "style"); | |
157 styleRules.push(inlineStyle); | |
158 } | |
159 | |
160 var matchedStyleRules = node.ownerDocument.defaultView.getMatchedCSS
Rules(node, "", !Preferences.showUserAgentStyles); | |
161 if (matchedStyleRules) { | |
162 // Add rules in reverse order to match the cascade order. | |
163 for (var i = (matchedStyleRules.length - 1); i >= 0; --i) { | |
164 var rule = matchedStyleRules[i]; | |
165 styleRules.push({ style: rule.style, selectorText: rule.sele
ctorText, parentStyleSheet: rule.parentStyleSheet, rule: rule }); | |
166 } | |
167 } | |
168 } | |
169 | |
170 function deleteDisabledProperty(style, name) | |
171 { | |
172 if (!style || !name) | |
173 return; | |
174 if (style.__disabledPropertyValues) | |
175 delete style.__disabledPropertyValues[name]; | |
176 if (style.__disabledPropertyPriorities) | |
177 delete style.__disabledPropertyPriorities[name]; | |
178 if (style.__disabledProperties) | |
179 delete style.__disabledProperties[name]; | |
180 } | |
181 | |
182 var usedProperties = {}; | |
183 var disabledComputedProperties = {}; | |
184 var priorityUsed = false; | |
185 | |
186 // Walk the style rules and make a list of all used and overloaded prope
rties. | |
187 for (var i = 0; i < styleRules.length; ++i) { | |
188 var styleRule = styleRules[i]; | |
189 if (styleRule.computedStyle) | |
190 continue; | |
191 if (styleRule.section && styleRule.section.noAffect) | |
192 continue; | |
193 | |
194 styleRule.usedProperties = {}; | |
195 | |
196 var style = styleRule.style; | |
197 for (var j = 0; j < style.length; ++j) { | |
198 var name = style[j]; | |
199 | |
200 if (!priorityUsed && style.getPropertyPriority(name).length) | |
201 priorityUsed = true; | |
202 | |
203 // If the property name is already used by another rule then thi
s rule's | |
204 // property is overloaded, so don't add it to the rule's usedPro
perties. | |
205 if (!(name in usedProperties)) | |
206 styleRule.usedProperties[name] = true; | |
207 | |
208 if (name === "font") { | |
209 // The font property is not reported as a shorthand. Report
finding the individual | |
210 // properties so they are visible in computed style. | |
211 // FIXME: remove this when http://bugs.webkit.org/show_bug.c
gi?id=15598 is fixed. | |
212 styleRule.usedProperties["font-family"] = true; | |
213 styleRule.usedProperties["font-size"] = true; | |
214 styleRule.usedProperties["font-style"] = true; | |
215 styleRule.usedProperties["font-variant"] = true; | |
216 styleRule.usedProperties["font-weight"] = true; | |
217 styleRule.usedProperties["line-height"] = true; | |
218 } | |
219 | |
220 // Delete any disabled properties, since the property does exist
. | |
221 // This prevents it from showing twice. | |
222 deleteDisabledProperty(style, name); | |
223 deleteDisabledProperty(style, style.getPropertyShorthand(name)); | |
224 } | |
225 | |
226 // Add all the properties found in this style to the used properties
list. | |
227 // Do this here so only future rules are affect by properties used i
n this rule. | |
228 for (var name in styleRules[i].usedProperties) | |
229 usedProperties[name] = true; | |
230 | |
231 // Remember all disabled properties so they show up in computed styl
e. | |
232 if (style.__disabledProperties) | |
233 for (var name in style.__disabledProperties) | |
234 disabledComputedProperties[name] = true; | |
235 } | |
236 | |
237 if (priorityUsed) { | |
238 // Walk the properties again and account for !important. | |
239 var foundPriorityProperties = []; | |
240 | |
241 // Walk in reverse to match the order !important overrides. | |
242 for (var i = (styleRules.length - 1); i >= 0; --i) { | |
243 if (styleRules[i].computedStyle) | |
244 continue; | |
245 | |
246 var style = styleRules[i].style; | |
247 var uniqueProperties = style.uniqueStyleProperties; | |
248 for (var j = 0; j < uniqueProperties.length; ++j) { | |
249 var name = uniqueProperties[j]; | |
250 if (style.getPropertyPriority(name).length) { | |
251 if (!(name in foundPriorityProperties)) | |
252 styleRules[i].usedProperties[name] = true; | |
253 else | |
254 delete styleRules[i].usedProperties[name]; | |
255 foundPriorityProperties[name] = true; | |
256 } else if (name in foundPriorityProperties) | |
257 delete styleRules[i].usedProperties[name]; | |
258 } | |
259 } | |
260 } | |
261 | |
262 if (refresh) { | |
263 // Walk the style rules and update the sections with new overloaded
and used properties. | |
264 for (var i = 0; i < styleRules.length; ++i) { | |
265 var styleRule = styleRules[i]; | |
266 var section = styleRule.section; | |
267 if (styleRule.computedStyle) | |
268 section.disabledComputedProperties = disabledComputedPropert
ies; | |
269 section._usedProperties = (styleRule.usedProperties || usedPrope
rties); | |
270 section.update((section === editedSection) || styleRule.computed
Style); | |
271 } | |
272 } else { | |
273 // Make a property section for each style rule. | |
274 for (var i = 0; i < styleRules.length; ++i) { | |
275 var styleRule = styleRules[i]; | |
276 var subtitle = styleRule.subtitle; | |
277 delete styleRule.subtitle; | |
278 | |
279 var computedStyle = styleRule.computedStyle; | |
280 delete styleRule.computedStyle; | |
281 | |
282 var ruleUsedProperties = styleRule.usedProperties; | |
283 delete styleRule.usedProperties; | |
284 | |
285 var editable = styleRule.editable; | |
286 delete styleRule.editable; | |
287 | |
288 var isAttribute = styleRule.isAttribute; | |
289 delete styleRule.isAttribute; | |
290 | |
291 // Default editable to true if it was omitted. | |
292 if (typeof editable === "undefined") | |
293 editable = true; | |
294 | |
295 var section = new WebInspector.StylePropertiesSection(styleRule,
subtitle, computedStyle, (ruleUsedProperties || usedProperties), editable); | |
296 if (computedStyle) | |
297 section.disabledComputedProperties = disabledComputedPropert
ies; | |
298 section.pane = this; | |
299 | |
300 if (Preferences.styleRulesExpandedState && section.identifier in
Preferences.styleRulesExpandedState) | |
301 section.expanded = Preferences.styleRulesExpandedState[secti
on.identifier]; | |
302 else if (computedStyle) | |
303 section.collapse(true); | |
304 else if (isAttribute && styleRule.style.length === 0) | |
305 section.collapse(true); | |
306 else | |
307 section.expand(true); | |
308 | |
309 body.appendChild(section.element); | |
310 this.sections.push(section); | |
311 } | |
312 } | |
313 }, | |
314 | |
315 _changeSetting: function(event) | |
316 { | |
317 var options = this.settingsSelectElement.options; | |
318 var selectedOption = options[this.settingsSelectElement.selectedIndex]; | |
319 selectedOption.action(event); | |
320 | |
321 // Select the correct color format setting again, since it needs to be s
elected. | |
322 var selectedIndex = 0; | |
323 for (var i = 0; i < options.length; ++i) { | |
324 if (options[i].value === Preferences.colorFormat) { | |
325 selectedIndex = i; | |
326 break; | |
327 } | |
328 } | |
329 | |
330 this.settingsSelectElement.selectedIndex = selectedIndex; | |
331 }, | |
332 | |
333 _changeColorFormat: function(event) | |
334 { | |
335 var selectedOption = this.settingsSelectElement[this.settingsSelectEleme
nt.selectedIndex]; | |
336 Preferences.colorFormat = selectedOption.value; | |
337 | |
338 InspectorController.setSetting("color-format", Preferences.colorFormat); | |
339 | |
340 for (var i = 0; i < this.sections.length; ++i) | |
341 this.sections[i].update(true); | |
342 }, | |
343 | |
344 _createNewRule: function(event) | |
345 { | |
346 this.addBlankSection().startEditingSelector(); | |
347 }, | |
348 | |
349 addBlankSection: function() | |
350 { | |
351 var blankSection = new WebInspector.BlankStylePropertiesSection(appropri
ateSelectorForNode(this.node, true)); | |
352 blankSection.pane = this; | |
353 | |
354 var elementStyleSection = this.sections[1]; | |
355 this.bodyElement.insertBefore(blankSection.element, elementStyleSection.
element.nextSibling); | |
356 | |
357 this.sections.splice(2, 0, blankSection); | |
358 | |
359 return blankSection; | |
360 }, | |
361 | |
362 removeSection: function(section) | |
363 { | |
364 var index = this.sections.indexOf(section); | |
365 if (index === -1) | |
366 return; | |
367 this.sections.splice(index, 1); | |
368 if (section.element.parentNode) | |
369 section.element.parentNode.removeChild(section.element); | |
370 } | |
371 } | |
372 | |
373 WebInspector.StylesSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.pr
ototype; | |
374 | |
375 WebInspector.StylePropertiesSection = function(styleRule, subtitle, computedStyl
e, usedProperties, editable) | |
376 { | |
377 WebInspector.PropertiesSection.call(this, styleRule.selectorText); | |
378 | |
379 this.titleElement.addEventListener("click", this._clickSelector.bind(this),
false); | |
380 this.titleElement.addEventListener("dblclick", this._dblclickSelector.bind(t
his), false); | |
381 this.element.addEventListener("dblclick", this._dblclickEmptySpace.bind(this
), false); | |
382 | |
383 this.styleRule = styleRule; | |
384 this.rule = this.styleRule.rule; | |
385 this.computedStyle = computedStyle; | |
386 this.editable = (editable && !computedStyle); | |
387 | |
388 // Prevent editing the user agent and user rules. | |
389 var isUserAgent = this.rule && this.rule.isUserAgent; | |
390 var isUser = this.rule && this.rule.isUser; | |
391 var isViaInspector = this.rule && this.rule.isViaInspector; | |
392 | |
393 if (isUserAgent || isUser) | |
394 this.editable = false; | |
395 | |
396 this._usedProperties = usedProperties; | |
397 | |
398 if (computedStyle) { | |
399 this.element.addStyleClass("computed-style"); | |
400 | |
401 if (Preferences.showInheritedComputedStyleProperties) | |
402 this.element.addStyleClass("show-inherited"); | |
403 | |
404 var showInheritedLabel = document.createElement("label"); | |
405 var showInheritedInput = document.createElement("input"); | |
406 showInheritedInput.type = "checkbox"; | |
407 showInheritedInput.checked = Preferences.showInheritedComputedStylePrope
rties; | |
408 | |
409 var computedStyleSection = this; | |
410 var showInheritedToggleFunction = function(event) { | |
411 Preferences.showInheritedComputedStyleProperties = showInheritedInpu
t.checked; | |
412 if (Preferences.showInheritedComputedStyleProperties) | |
413 computedStyleSection.element.addStyleClass("show-inherited"); | |
414 else | |
415 computedStyleSection.element.removeStyleClass("show-inherited"); | |
416 event.stopPropagation(); | |
417 }; | |
418 | |
419 showInheritedLabel.addEventListener("click", showInheritedToggleFunction
, false); | |
420 | |
421 showInheritedLabel.appendChild(showInheritedInput); | |
422 showInheritedLabel.appendChild(document.createTextNode(WebInspector.UISt
ring("Show inherited"))); | |
423 this.subtitleElement.appendChild(showInheritedLabel); | |
424 } else { | |
425 if (!subtitle) { | |
426 if (this.styleRule.parentStyleSheet && this.styleRule.parentStyleShe
et.href) { | |
427 var url = this.styleRule.parentStyleSheet.href; | |
428 subtitle = WebInspector.linkifyURL(url, WebInspector.displayName
ForURL(url)); | |
429 this.subtitleElement.addStyleClass("file"); | |
430 } else if (isUserAgent) | |
431 subtitle = WebInspector.UIString("user agent stylesheet"); | |
432 else if (isUser) | |
433 subtitle = WebInspector.UIString("user stylesheet"); | |
434 else if (isViaInspector) | |
435 subtitle = WebInspector.UIString("via inspector"); | |
436 else | |
437 subtitle = WebInspector.UIString("inline stylesheet"); | |
438 } | |
439 | |
440 this.subtitle = subtitle; | |
441 } | |
442 | |
443 this.identifier = styleRule.selectorText; | |
444 if (this.subtitle) | |
445 this.identifier += ":" + this.subtitleElement.textContent; | |
446 } | |
447 | |
448 WebInspector.StylePropertiesSection.prototype = { | |
449 get usedProperties() | |
450 { | |
451 return this._usedProperties || {}; | |
452 }, | |
453 | |
454 set usedProperties(x) | |
455 { | |
456 this._usedProperties = x; | |
457 this.update(); | |
458 }, | |
459 | |
460 expand: function(dontRememberState) | |
461 { | |
462 WebInspector.PropertiesSection.prototype.expand.call(this); | |
463 if (dontRememberState) | |
464 return; | |
465 | |
466 if (!Preferences.styleRulesExpandedState) | |
467 Preferences.styleRulesExpandedState = {}; | |
468 Preferences.styleRulesExpandedState[this.identifier] = true; | |
469 }, | |
470 | |
471 collapse: function(dontRememberState) | |
472 { | |
473 WebInspector.PropertiesSection.prototype.collapse.call(this); | |
474 if (dontRememberState) | |
475 return; | |
476 | |
477 if (!Preferences.styleRulesExpandedState) | |
478 Preferences.styleRulesExpandedState = {}; | |
479 Preferences.styleRulesExpandedState[this.identifier] = false; | |
480 }, | |
481 | |
482 isPropertyInherited: function(property) | |
483 { | |
484 if (!this.computedStyle || !this._usedProperties || this.noAffect) | |
485 return false; | |
486 // These properties should always show for Computed Style. | |
487 var alwaysShowComputedProperties = { "display": true, "height": true, "w
idth": true }; | |
488 return !(property in this.usedProperties) && !(property in alwaysShowCom
putedProperties) && !(property in this.disabledComputedProperties); | |
489 }, | |
490 | |
491 isPropertyOverloaded: function(property, shorthand) | |
492 { | |
493 if (this.computedStyle || !this._usedProperties || this.noAffect) | |
494 return false; | |
495 | |
496 var used = (property in this.usedProperties); | |
497 if (used || !shorthand) | |
498 return !used; | |
499 | |
500 // Find out if any of the individual longhand properties of the shorthan
d | |
501 // are used, if none are then the shorthand is overloaded too. | |
502 var longhandProperties = this.styleRule.style.getLonghandProperties(prop
erty); | |
503 for (var j = 0; j < longhandProperties.length; ++j) { | |
504 var individualProperty = longhandProperties[j]; | |
505 if (individualProperty in this.usedProperties) | |
506 return false; | |
507 } | |
508 | |
509 return true; | |
510 }, | |
511 | |
512 isInspectorStylesheet: function() | |
513 { | |
514 return (this.styleRule.parentStyleSheet === WebInspector.panels.elements
.stylesheet); | |
515 }, | |
516 | |
517 update: function(full) | |
518 { | |
519 if (full || this.computedStyle) { | |
520 this.propertiesTreeOutline.removeChildren(); | |
521 this.populated = false; | |
522 } else { | |
523 var child = this.propertiesTreeOutline.children[0]; | |
524 while (child) { | |
525 child.overloaded = this.isPropertyOverloaded(child.name, child.s
horthand); | |
526 child = child.traverseNextTreeElement(false, null, true); | |
527 } | |
528 } | |
529 | |
530 if (this._afterUpdate) { | |
531 this._afterUpdate(this); | |
532 delete this._afterUpdate; | |
533 } | |
534 }, | |
535 | |
536 onpopulate: function() | |
537 { | |
538 var style = this.styleRule.style; | |
539 | |
540 var foundShorthands = {}; | |
541 var uniqueProperties = style.uniqueStyleProperties; | |
542 var disabledProperties = style.__disabledPropertyValues || {}; | |
543 | |
544 for (var name in disabledProperties) | |
545 uniqueProperties.push(name); | |
546 | |
547 uniqueProperties.sort(); | |
548 | |
549 for (var i = 0; i < uniqueProperties.length; ++i) { | |
550 var name = uniqueProperties[i]; | |
551 var disabled = name in disabledProperties; | |
552 if (!disabled && this.disabledComputedProperties && !(name in this.u
sedProperties) && name in this.disabledComputedProperties) | |
553 disabled = true; | |
554 | |
555 var shorthand = !disabled ? style.getPropertyShorthand(name) : null; | |
556 | |
557 if (shorthand && shorthand in foundShorthands) | |
558 continue; | |
559 | |
560 if (shorthand) { | |
561 foundShorthands[shorthand] = true; | |
562 name = shorthand; | |
563 } | |
564 | |
565 var isShorthand = (shorthand ? true : false); | |
566 var inherited = this.isPropertyInherited(name); | |
567 var overloaded = this.isPropertyOverloaded(name, isShorthand); | |
568 | |
569 var item = new WebInspector.StylePropertyTreeElement(this.styleRule,
style, name, isShorthand, inherited, overloaded, disabled); | |
570 this.propertiesTreeOutline.appendChild(item); | |
571 } | |
572 }, | |
573 | |
574 findTreeElementWithName: function(name) | |
575 { | |
576 var treeElement = this.propertiesTreeOutline.children[0]; | |
577 while (treeElement) { | |
578 if (treeElement.name === name) | |
579 return treeElement; | |
580 treeElement = treeElement.traverseNextTreeElement(true, null, true); | |
581 } | |
582 return null; | |
583 }, | |
584 | |
585 addNewBlankProperty: function() | |
586 { | |
587 var item = new WebInspector.StylePropertyTreeElement(this.styleRule, thi
s.styleRule.style, "", false, false, false, false); | |
588 this.propertiesTreeOutline.appendChild(item); | |
589 item.listItemElement.textContent = ""; | |
590 item._newProperty = true; | |
591 return item; | |
592 }, | |
593 | |
594 _clickSelector: function(event) | |
595 { | |
596 event.stopPropagation(); | |
597 | |
598 // Don't search "Computed Styles", "Style Attribute", or Mapped Attribut
es. | |
599 if (this.computedStyle || !this.rule || this.rule.isUser) | |
600 return; | |
601 | |
602 var searchElement = document.getElementById("search"); | |
603 searchElement.value = this.styleRule.selectorText; | |
604 WebInspector.performSearch({ target: searchElement }); | |
605 }, | |
606 | |
607 _dblclickEmptySpace: function(event) | |
608 { | |
609 this.expand(); | |
610 this.addNewBlankProperty().startEditing(); | |
611 }, | |
612 | |
613 _dblclickSelector: function(event) | |
614 { | |
615 if (!this.editable) | |
616 return; | |
617 | |
618 if (!this.rule && this.propertiesTreeOutline.children.length === 0) { | |
619 this.expand(); | |
620 this.addNewBlankProperty().startEditing(); | |
621 return; | |
622 } | |
623 | |
624 if (!this.rule) | |
625 return; | |
626 | |
627 this.startEditingSelector(); | |
628 event.stopPropagation(); | |
629 }, | |
630 | |
631 startEditingSelector: function() | |
632 { | |
633 var element = this.titleElement; | |
634 if (WebInspector.isBeingEdited(element)) | |
635 return; | |
636 | |
637 WebInspector.startEditing(this.titleElement, this.editingSelectorCommitt
ed.bind(this), this.editingSelectorCancelled.bind(this), null); | |
638 window.getSelection().setBaseAndExtent(element, 0, element, 1); | |
639 }, | |
640 | |
641 editingSelectorCommitted: function(element, newContent, oldContent, context,
moveDirection) | |
642 { | |
643 function moveToNextIfNeeded() { | |
644 if (!moveDirection || moveDirection !== "forward") | |
645 return; | |
646 | |
647 this.expand(); | |
648 if (this.propertiesTreeOutline.children.length === 0) | |
649 this.addNewBlankProperty().startEditing(); | |
650 else { | |
651 var item = this.propertiesTreeOutline.children[0] | |
652 item.startEditing(item.valueElement); | |
653 } | |
654 } | |
655 | |
656 if (newContent === oldContent) | |
657 return moveToNextIfNeeded.call(this); | |
658 | |
659 var self = this; | |
660 function callback(result) | |
661 { | |
662 if (!result) { | |
663 // Invalid Syntax for a Selector | |
664 moveToNextIfNeeded.call(self); | |
665 return; | |
666 } | |
667 | |
668 var newRulePayload = result[0]; | |
669 var doesAffectSelectedNode = result[1]; | |
670 if (!doesAffectSelectedNode) { | |
671 self.noAffect = true; | |
672 self.element.addStyleClass("no-affect"); | |
673 } else { | |
674 delete self.noAffect; | |
675 self.element.removeStyleClass("no-affect"); | |
676 } | |
677 | |
678 var newRule = WebInspector.CSSStyleDeclaration.parseRule(newRulePayl
oad); | |
679 self.rule = newRule; | |
680 self.styleRule = { section: self, style: newRule.style, selectorText
: newRule.selectorText, parentStyleSheet: newRule.parentStyleSheet, rule: newRul
e }; | |
681 | |
682 var oldIdentifier = this.identifier; | |
683 self.identifier = newRule.selectorText + ":" + self.subtitleElement.
textContent; | |
684 | |
685 self.pane.update(); | |
686 | |
687 WebInspector.panels.elements.renameSelector(oldIdentifier, this.iden
tifier, oldContent, newContent); | |
688 | |
689 moveToNextIfNeeded.call(self); | |
690 } | |
691 | |
692 InjectedScriptAccess.applyStyleRuleText(this.rule.id, newContent, this.p
ane.node.id, callback); | |
693 }, | |
694 | |
695 editingSelectorCancelled: function() | |
696 { | |
697 // Do nothing, this is overridden by BlankStylePropertiesSection. | |
698 } | |
699 } | |
700 | |
701 WebInspector.StylePropertiesSection.prototype.__proto__ = WebInspector.Propertie
sSection.prototype; | |
702 | |
703 WebInspector.BlankStylePropertiesSection = function(defaultSelectorText) | |
704 { | |
705 WebInspector.StylePropertiesSection.call(this, {selectorText: defaultSelecto
rText, rule: {isViaInspector: true}}, "", false, {}, false); | |
706 | |
707 this.element.addStyleClass("blank-section"); | |
708 } | |
709 | |
710 WebInspector.BlankStylePropertiesSection.prototype = { | |
711 expand: function() | |
712 { | |
713 // Do nothing, blank sections are not expandable. | |
714 }, | |
715 | |
716 editingSelectorCommitted: function(element, newContent, oldContent, context) | |
717 { | |
718 var self = this; | |
719 function callback(result) | |
720 { | |
721 if (!result) { | |
722 // Invalid Syntax for a Selector | |
723 self.editingSelectorCancelled(); | |
724 return; | |
725 } | |
726 | |
727 var rule = result[0]; | |
728 var doesSelectorAffectSelectedNode = result[1]; | |
729 | |
730 var styleRule = WebInspector.CSSStyleDeclaration.parseRule(rule); | |
731 styleRule.rule = rule; | |
732 | |
733 self.makeNormal(styleRule); | |
734 | |
735 if (!doesSelectorAffectSelectedNode) { | |
736 self.noAffect = true; | |
737 self.element.addStyleClass("no-affect"); | |
738 } | |
739 | |
740 self.subtitleElement.textContent = WebInspector.UIString("via inspec
tor"); | |
741 self.expand(); | |
742 | |
743 self.addNewBlankProperty().startEditing(); | |
744 } | |
745 | |
746 InjectedScriptAccess.addStyleSelector(newContent, this.pane.node.id, cal
lback); | |
747 }, | |
748 | |
749 editingSelectorCancelled: function() | |
750 { | |
751 this.pane.removeSection(this); | |
752 }, | |
753 | |
754 makeNormal: function(styleRule) | |
755 { | |
756 this.element.removeStyleClass("blank-section"); | |
757 | |
758 this.styleRule = styleRule; | |
759 this.rule = styleRule.rule; | |
760 this.computedStyle = false; | |
761 this.editable = true; | |
762 this.identifier = styleRule.selectorText + ":via inspector"; | |
763 | |
764 this.__proto__ = WebInspector.StylePropertiesSection.prototype; | |
765 } | |
766 } | |
767 | |
768 WebInspector.BlankStylePropertiesSection.prototype.__proto__ = WebInspector.Styl
ePropertiesSection.prototype; | |
769 | |
770 WebInspector.StylePropertyTreeElement = function(styleRule, style, name, shortha
nd, inherited, overloaded, disabled) | |
771 { | |
772 this._styleRule = styleRule; | |
773 this.style = style; | |
774 this.name = name; | |
775 this.shorthand = shorthand; | |
776 this._inherited = inherited; | |
777 this._overloaded = overloaded; | |
778 this._disabled = disabled; | |
779 | |
780 // Pass an empty title, the title gets made later in onattach. | |
781 TreeElement.call(this, "", null, shorthand); | |
782 } | |
783 | |
784 WebInspector.StylePropertyTreeElement.prototype = { | |
785 get inherited() | |
786 { | |
787 return this._inherited; | |
788 }, | |
789 | |
790 set inherited(x) | |
791 { | |
792 if (x === this._inherited) | |
793 return; | |
794 this._inherited = x; | |
795 this.updateState(); | |
796 }, | |
797 | |
798 get overloaded() | |
799 { | |
800 return this._overloaded; | |
801 }, | |
802 | |
803 set overloaded(x) | |
804 { | |
805 if (x === this._overloaded) | |
806 return; | |
807 this._overloaded = x; | |
808 this.updateState(); | |
809 }, | |
810 | |
811 get disabled() | |
812 { | |
813 return this._disabled; | |
814 }, | |
815 | |
816 set disabled(x) | |
817 { | |
818 if (x === this._disabled) | |
819 return; | |
820 this._disabled = x; | |
821 this.updateState(); | |
822 }, | |
823 | |
824 get priority() | |
825 { | |
826 if (this.disabled && this.style.__disabledPropertyPriorities && this.nam
e in this.style.__disabledPropertyPriorities) | |
827 return this.style.__disabledPropertyPriorities[this.name]; | |
828 return (this.shorthand ? this.style.getShorthandPriority(this.name) : th
is.style.getPropertyPriority(this.name)); | |
829 }, | |
830 | |
831 get value() | |
832 { | |
833 if (this.disabled && this.style.__disabledPropertyValues && this.name in
this.style.__disabledPropertyValues) | |
834 return this.style.__disabledPropertyValues[this.name]; | |
835 return (this.shorthand ? this.style.getShorthandValue(this.name) : this.
style.getPropertyValue(this.name)); | |
836 }, | |
837 | |
838 onattach: function() | |
839 { | |
840 this.updateTitle(); | |
841 }, | |
842 | |
843 updateTitle: function() | |
844 { | |
845 var priority = this.priority; | |
846 var value = this.value; | |
847 | |
848 if (priority && !priority.length) | |
849 delete priority; | |
850 if (priority) | |
851 priority = "!" + priority; | |
852 | |
853 this.updateState(); | |
854 | |
855 var enabledCheckboxElement = document.createElement("input"); | |
856 enabledCheckboxElement.className = "enabled-button"; | |
857 enabledCheckboxElement.type = "checkbox"; | |
858 enabledCheckboxElement.checked = !this.disabled; | |
859 enabledCheckboxElement.addEventListener("change", this.toggleEnabled.bin
d(this), false); | |
860 | |
861 var nameElement = document.createElement("span"); | |
862 nameElement.className = "name"; | |
863 nameElement.textContent = this.name; | |
864 this.nameElement = nameElement; | |
865 | |
866 var valueElement = document.createElement("span"); | |
867 valueElement.className = "value"; | |
868 this.valueElement = valueElement; | |
869 | |
870 if (value) { | |
871 function processValue(regex, processor, nextProcessor, valueText) | |
872 { | |
873 var container = document.createDocumentFragment(); | |
874 | |
875 var items = valueText.replace(regex, "\0$1\0").split("\0"); | |
876 for (var i = 0; i < items.length; ++i) { | |
877 if ((i % 2) === 0) { | |
878 if (nextProcessor) | |
879 container.appendChild(nextProcessor(items[i])); | |
880 else | |
881 container.appendChild(document.createTextNode(items[
i])); | |
882 } else { | |
883 var processedNode = processor(items[i]); | |
884 if (processedNode) | |
885 container.appendChild(processedNode); | |
886 } | |
887 } | |
888 | |
889 return container; | |
890 } | |
891 | |
892 function linkifyURL(url) | |
893 { | |
894 var container = document.createDocumentFragment(); | |
895 container.appendChild(document.createTextNode("url(")); | |
896 container.appendChild(WebInspector.linkifyURLAsNode(url, url, nu
ll, (url in WebInspector.resourceURLMap))); | |
897 container.appendChild(document.createTextNode(")")); | |
898 return container; | |
899 } | |
900 | |
901 function processColor(text) | |
902 { | |
903 try { | |
904 var color = new WebInspector.Color(text); | |
905 } catch (e) { | |
906 return document.createTextNode(text); | |
907 } | |
908 | |
909 var swatchElement = document.createElement("span"); | |
910 swatchElement.title = WebInspector.UIString("Click to change col
or format"); | |
911 swatchElement.className = "swatch"; | |
912 swatchElement.style.setProperty("background-color", text); | |
913 | |
914 swatchElement.addEventListener("click", changeColorDisplay, fals
e); | |
915 swatchElement.addEventListener("dblclick", function(event) { eve
nt.stopPropagation() }, false); | |
916 | |
917 var format; | |
918 if (Preferences.showColorNicknames && color.nickname) | |
919 format = "nickname"; | |
920 else if (Preferences.colorFormat === "rgb") | |
921 format = (color.simple ? "rgb" : "rgba"); | |
922 else if (Preferences.colorFormat === "hsl") | |
923 format = (color.simple ? "hsl" : "hsla"); | |
924 else if (color.simple) | |
925 format = (color.hasShortHex() ? "shorthex" : "hex"); | |
926 else | |
927 format = "rgba"; | |
928 | |
929 var colorValueElement = document.createElement("span"); | |
930 colorValueElement.textContent = color.toString(format); | |
931 | |
932 function changeColorDisplay(event) | |
933 { | |
934 switch (format) { | |
935 case "rgb": | |
936 format = "hsl"; | |
937 break; | |
938 | |
939 case "shorthex": | |
940 format = "hex"; | |
941 break; | |
942 | |
943 case "hex": | |
944 format = "rgb"; | |
945 break; | |
946 | |
947 case "nickname": | |
948 if (color.simple) { | |
949 if (color.hasShortHex()) | |
950 format = "shorthex"; | |
951 else | |
952 format = "hex"; | |
953 break; | |
954 } | |
955 | |
956 format = "rgba"; | |
957 break; | |
958 | |
959 case "hsl": | |
960 if (color.nickname) | |
961 format = "nickname"; | |
962 else if (color.hasShortHex()) | |
963 format = "shorthex"; | |
964 else | |
965 format = "hex"; | |
966 break; | |
967 | |
968 case "rgba": | |
969 format = "hsla"; | |
970 break; | |
971 | |
972 case "hsla": | |
973 if (color.nickname) | |
974 format = "nickname"; | |
975 else | |
976 format = "rgba"; | |
977 break; | |
978 } | |
979 | |
980 colorValueElement.textContent = color.toString(format); | |
981 } | |
982 | |
983 var container = document.createDocumentFragment(); | |
984 container.appendChild(swatchElement); | |
985 container.appendChild(colorValueElement); | |
986 return container; | |
987 } | |
988 | |
989 var colorRegex = /((?:rgb|hsl)a?\([^)]+\)|#[0-9a-fA-F]{6}|#[0-9a-fA-
F]{3}|\b\w+\b)/g; | |
990 var colorProcessor = processValue.bind(window, colorRegex, processCo
lor, null); | |
991 | |
992 valueElement.appendChild(processValue(/url\(([^)]+)\)/g, linkifyURL,
colorProcessor, value)); | |
993 } | |
994 | |
995 if (priority) { | |
996 var priorityElement = document.createElement("span"); | |
997 priorityElement.className = "priority"; | |
998 priorityElement.textContent = priority; | |
999 } | |
1000 | |
1001 this.listItemElement.removeChildren(); | |
1002 | |
1003 // Append the checkbox for root elements of an editable section. | |
1004 if (this.treeOutline.section && this.treeOutline.section.editable && thi
s.parent.root) | |
1005 this.listItemElement.appendChild(enabledCheckboxElement); | |
1006 this.listItemElement.appendChild(nameElement); | |
1007 this.listItemElement.appendChild(document.createTextNode(": ")); | |
1008 this.listItemElement.appendChild(valueElement); | |
1009 | |
1010 if (priorityElement) { | |
1011 this.listItemElement.appendChild(document.createTextNode(" ")); | |
1012 this.listItemElement.appendChild(priorityElement); | |
1013 } | |
1014 | |
1015 this.listItemElement.appendChild(document.createTextNode(";")); | |
1016 | |
1017 this.tooltip = this.name + ": " + valueElement.textContent + (priority ?
" " + priority : ""); | |
1018 }, | |
1019 | |
1020 updateAll: function(updateAllRules) | |
1021 { | |
1022 if (updateAllRules && this.treeOutline.section && this.treeOutline.secti
on.pane) | |
1023 this.treeOutline.section.pane.update(null, this.treeOutline.section)
; | |
1024 else if (this.treeOutline.section) | |
1025 this.treeOutline.section.update(true); | |
1026 else | |
1027 this.updateTitle(); // FIXME: this will not show new properties. But
we don't hit his case yet. | |
1028 }, | |
1029 | |
1030 toggleEnabled: function(event) | |
1031 { | |
1032 var disabled = !event.target.checked; | |
1033 | |
1034 var self = this; | |
1035 function callback(newPayload) | |
1036 { | |
1037 if (!newPayload) | |
1038 return; | |
1039 | |
1040 self.style = WebInspector.CSSStyleDeclaration.parseStyle(newPayload)
; | |
1041 self._styleRule.style = self.style; | |
1042 | |
1043 // Set the disabled property here, since the code above replies on i
t not changing | |
1044 // until after the value and priority are retrieved. | |
1045 self.disabled = disabled; | |
1046 | |
1047 if (self.treeOutline.section && self.treeOutline.section.pane) | |
1048 self.treeOutline.section.pane.dispatchEventToListeners("style pr
operty toggled"); | |
1049 | |
1050 self.updateAll(true); | |
1051 } | |
1052 | |
1053 InjectedScriptAccess.toggleStyleEnabled(this.style.id, this.name, disabl
ed, callback); | |
1054 }, | |
1055 | |
1056 updateState: function() | |
1057 { | |
1058 if (!this.listItemElement) | |
1059 return; | |
1060 | |
1061 if (this.style.isPropertyImplicit(this.name) || this.value === "initial"
) | |
1062 this.listItemElement.addStyleClass("implicit"); | |
1063 else | |
1064 this.listItemElement.removeStyleClass("implicit"); | |
1065 | |
1066 if (this.inherited) | |
1067 this.listItemElement.addStyleClass("inherited"); | |
1068 else | |
1069 this.listItemElement.removeStyleClass("inherited"); | |
1070 | |
1071 if (this.overloaded) | |
1072 this.listItemElement.addStyleClass("overloaded"); | |
1073 else | |
1074 this.listItemElement.removeStyleClass("overloaded"); | |
1075 | |
1076 if (this.disabled) | |
1077 this.listItemElement.addStyleClass("disabled"); | |
1078 else | |
1079 this.listItemElement.removeStyleClass("disabled"); | |
1080 }, | |
1081 | |
1082 onpopulate: function() | |
1083 { | |
1084 // Only populate once and if this property is a shorthand. | |
1085 if (this.children.length || !this.shorthand) | |
1086 return; | |
1087 | |
1088 var longhandProperties = this.style.getLonghandProperties(this.name); | |
1089 for (var i = 0; i < longhandProperties.length; ++i) { | |
1090 var name = longhandProperties[i]; | |
1091 | |
1092 if (this.treeOutline.section) { | |
1093 var inherited = this.treeOutline.section.isPropertyInherited(nam
e); | |
1094 var overloaded = this.treeOutline.section.isPropertyOverloaded(n
ame); | |
1095 } | |
1096 | |
1097 var item = new WebInspector.StylePropertyTreeElement(this._styleRule
, this.style, name, false, inherited, overloaded); | |
1098 this.appendChild(item); | |
1099 } | |
1100 }, | |
1101 | |
1102 ondblclick: function(element, event) | |
1103 { | |
1104 this.startEditing(event.target); | |
1105 event.stopPropagation(); | |
1106 }, | |
1107 | |
1108 startEditing: function(selectElement) | |
1109 { | |
1110 // FIXME: we don't allow editing of longhand properties under a shorthan
d right now. | |
1111 if (this.parent.shorthand) | |
1112 return; | |
1113 | |
1114 if (WebInspector.isBeingEdited(this.listItemElement) || (this.treeOutlin
e.section && !this.treeOutline.section.editable)) | |
1115 return; | |
1116 | |
1117 var context = { expanded: this.expanded, hasChildren: this.hasChildren }
; | |
1118 | |
1119 // Lie about our children to prevent expanding on double click and to co
llapse shorthands. | |
1120 this.hasChildren = false; | |
1121 | |
1122 if (!selectElement) | |
1123 selectElement = this.listItemElement; | |
1124 | |
1125 this.listItemElement.handleKeyEvent = this.editingKeyDown.bind(this); | |
1126 | |
1127 WebInspector.startEditing(this.listItemElement, this.editingCommitted.bi
nd(this), this.editingCancelled.bind(this), context); | |
1128 window.getSelection().setBaseAndExtent(selectElement, 0, selectElement,
1); | |
1129 }, | |
1130 | |
1131 editingKeyDown: function(event) | |
1132 { | |
1133 var arrowKeyPressed = (event.keyIdentifier === "Up" || event.keyIdentifi
er === "Down"); | |
1134 var pageKeyPressed = (event.keyIdentifier === "PageUp" || event.keyIdent
ifier === "PageDown"); | |
1135 if (!arrowKeyPressed && !pageKeyPressed) | |
1136 return; | |
1137 | |
1138 var selection = window.getSelection(); | |
1139 if (!selection.rangeCount) | |
1140 return; | |
1141 | |
1142 var selectionRange = selection.getRangeAt(0); | |
1143 if (selectionRange.commonAncestorContainer !== this.listItemElement && !
selectionRange.commonAncestorContainer.isDescendant(this.listItemElement)) | |
1144 return; | |
1145 | |
1146 const styleValueDelimeters = " \t\n\"':;,/()"; | |
1147 var wordRange = selectionRange.startContainer.rangeOfWord(selectionRange
.startOffset, styleValueDelimeters, this.listItemElement); | |
1148 var wordString = wordRange.toString(); | |
1149 var replacementString = wordString; | |
1150 | |
1151 var matches = /(.*?)(-?\d+(?:\.\d+)?)(.*)/.exec(wordString); | |
1152 if (matches && matches.length) { | |
1153 var prefix = matches[1]; | |
1154 var number = parseFloat(matches[2]); | |
1155 var suffix = matches[3]; | |
1156 | |
1157 // If the number is near zero or the number is one and the direction
will take it near zero. | |
1158 var numberNearZero = (number < 1 && number > -1); | |
1159 if (number === 1 && event.keyIdentifier === "Down") | |
1160 numberNearZero = true; | |
1161 else if (number === -1 && event.keyIdentifier === "Up") | |
1162 numberNearZero = true; | |
1163 | |
1164 if (numberNearZero && event.altKey && arrowKeyPressed) { | |
1165 if (event.keyIdentifier === "Down") | |
1166 number = Math.ceil(number - 1); | |
1167 else | |
1168 number = Math.floor(number + 1); | |
1169 } else { | |
1170 // Jump by 10 when shift is down or jump by 0.1 when near zero o
r Alt/Option is down. | |
1171 // Also jump by 10 for page up and down, or by 100 if shift is h
eld with a page key. | |
1172 var changeAmount = 1; | |
1173 if (event.shiftKey && pageKeyPressed) | |
1174 changeAmount = 100; | |
1175 else if (event.shiftKey || pageKeyPressed) | |
1176 changeAmount = 10; | |
1177 else if (event.altKey || numberNearZero) | |
1178 changeAmount = 0.1; | |
1179 | |
1180 if (event.keyIdentifier === "Down" || event.keyIdentifier === "P
ageDown") | |
1181 changeAmount *= -1; | |
1182 | |
1183 // Make the new number and constrain it to a precision of 6, thi
s matches numbers the engine returns. | |
1184 // Use the Number constructor to forget the fixed precision, so
1.100000 will print as 1.1. | |
1185 number = Number((number + changeAmount).toFixed(6)); | |
1186 } | |
1187 | |
1188 replacementString = prefix + number + suffix; | |
1189 } else { | |
1190 // FIXME: this should cycle through known keywords for the current p
roperty name. | |
1191 return; | |
1192 } | |
1193 | |
1194 var replacementTextNode = document.createTextNode(replacementString); | |
1195 | |
1196 wordRange.deleteContents(); | |
1197 wordRange.insertNode(replacementTextNode); | |
1198 | |
1199 var finalSelectionRange = document.createRange(); | |
1200 finalSelectionRange.setStart(replacementTextNode, 0); | |
1201 finalSelectionRange.setEnd(replacementTextNode, replacementString.length
); | |
1202 | |
1203 selection.removeAllRanges(); | |
1204 selection.addRange(finalSelectionRange); | |
1205 | |
1206 event.preventDefault(); | |
1207 event.handled = true; | |
1208 | |
1209 if (!this.originalCSSText) { | |
1210 // Remember the rule's original CSS text, so it can be restored | |
1211 // if the editing is canceled and before each apply. | |
1212 this.originalCSSText = this.style.styleTextWithShorthands(); | |
1213 } else { | |
1214 // Restore the original CSS text before applying user changes. This
is needed to prevent | |
1215 // new properties from sticking around if the user adds one, then re
moves it. | |
1216 InjectedScriptAccess.setStyleText(this.style.id, this.originalCSSTex
t); | |
1217 } | |
1218 | |
1219 this.applyStyleText(this.listItemElement.textContent); | |
1220 }, | |
1221 | |
1222 editingEnded: function(context) | |
1223 { | |
1224 this.hasChildren = context.hasChildren; | |
1225 if (context.expanded) | |
1226 this.expand(); | |
1227 delete this.listItemElement.handleKeyEvent; | |
1228 delete this.originalCSSText; | |
1229 }, | |
1230 | |
1231 editingCancelled: function(element, context) | |
1232 { | |
1233 if (this._newProperty) | |
1234 this.treeOutline.removeChild(this); | |
1235 else if (this.originalCSSText) { | |
1236 InjectedScriptAccess.setStyleText(this.style.id, this.originalCSSTex
t); | |
1237 | |
1238 if (this.treeOutline.section && this.treeOutline.section.pane) | |
1239 this.treeOutline.section.pane.dispatchEventToListeners("style ed
ited"); | |
1240 | |
1241 this.updateAll(); | |
1242 } else | |
1243 this.updateTitle(); | |
1244 | |
1245 this.editingEnded(context); | |
1246 }, | |
1247 | |
1248 editingCommitted: function(element, userInput, previousContent, context, mov
eDirection) | |
1249 { | |
1250 this.editingEnded(context); | |
1251 | |
1252 // Determine where to move to before making changes | |
1253 var newProperty, moveToPropertyName, moveToSelector; | |
1254 var moveTo = (moveDirection === "forward" ? this.nextSibling : this.prev
iousSibling); | |
1255 if (moveTo) | |
1256 moveToPropertyName = moveTo.name; | |
1257 else if (moveDirection === "forward") | |
1258 newProperty = true; | |
1259 else if (moveDirection === "backward" && this.treeOutline.section.rule) | |
1260 moveToSelector = true; | |
1261 | |
1262 // Make the Changes and trigger the moveToNextCallback after updating | |
1263 var blankInput = /^\s*$/.test(userInput); | |
1264 if (userInput !== previousContent || (this._newProperty && blankInput))
{ // only if something changed, or adding a new style and it was blank | |
1265 this.treeOutline.section._afterUpdate = moveToNextCallback.bind(this
, this._newProperty, !blankInput); | |
1266 this.applyStyleText(userInput, true); | |
1267 } else | |
1268 moveToNextCallback(this._newProperty, false, this.treeOutline.sectio
n, false); | |
1269 | |
1270 // The Callback to start editing the next property | |
1271 function moveToNextCallback(alreadyNew, valueChanged, section) | |
1272 { | |
1273 if (!moveDirection) | |
1274 return; | |
1275 | |
1276 // User just tabbed through without changes | |
1277 if (moveTo && moveTo.parent) { | |
1278 moveTo.startEditing(moveTo.valueElement); | |
1279 return; | |
1280 } | |
1281 | |
1282 // User has made a change then tabbed, wiping all the original treeE
lements, | |
1283 // recalculate the new treeElement for the same property we were goi
ng to edit next | |
1284 if (moveTo && !moveTo.parent) { | |
1285 var treeElement = section.findTreeElementWithName(moveToProperty
Name); | |
1286 if (treeElement) | |
1287 treeElement.startEditing(treeElement.valueElement); | |
1288 return; | |
1289 } | |
1290 | |
1291 // Create a new attribute in this section | |
1292 if (newProperty) { | |
1293 if (alreadyNew && !valueChanged) | |
1294 return; | |
1295 | |
1296 var item = section.addNewBlankProperty(); | |
1297 item.startEditing(); | |
1298 return; | |
1299 } | |
1300 | |
1301 if (moveToSelector) | |
1302 section.startEditingSelector(); | |
1303 } | |
1304 }, | |
1305 | |
1306 applyStyleText: function(styleText, updateInterface) | |
1307 { | |
1308 var section = this.treeOutline.section; | |
1309 var elementsPanel = WebInspector.panels.elements; | |
1310 var styleTextLength = styleText.trimWhitespace().length; | |
1311 if (!styleTextLength && updateInterface) { | |
1312 if (this._newProperty) { | |
1313 // The user deleted everything, so remove the tree element and u
pdate. | |
1314 this.parent.removeChild(this); | |
1315 return; | |
1316 } else { | |
1317 delete section._afterUpdate; | |
1318 } | |
1319 } | |
1320 | |
1321 var self = this; | |
1322 function callback(result) | |
1323 { | |
1324 if (!result) { | |
1325 // The user typed something, but it didn't parse. Just abort and
restore | |
1326 // the original title for this property. If this was a new attr
ibute and | |
1327 // we couldn't parse, then just remove it. | |
1328 if (self._newProperty) { | |
1329 self.parent.removeChild(self); | |
1330 return; | |
1331 } | |
1332 if (updateInterface) | |
1333 self.updateTitle(); | |
1334 return; | |
1335 } | |
1336 | |
1337 var newPayload = result[0]; | |
1338 var changedProperties = result[1]; | |
1339 elementsPanel.removeStyleChange(section.identifier, self.style, self
.name); | |
1340 | |
1341 if (!styleTextLength) { | |
1342 // Do remove ourselves from UI when the property removal is conf
irmed. | |
1343 self.parent.removeChild(self); | |
1344 } else { | |
1345 self.style = WebInspector.CSSStyleDeclaration.parseStyle(newPayl
oad); | |
1346 for (var i = 0; i < changedProperties.length; ++i) | |
1347 elementsPanel.addStyleChange(section.identifier, self.style,
changedProperties[i]); | |
1348 self._styleRule.style = self.style; | |
1349 } | |
1350 | |
1351 if (section && section.pane) | |
1352 section.pane.dispatchEventToListeners("style edited"); | |
1353 | |
1354 if (updateInterface) | |
1355 self.updateAll(true); | |
1356 | |
1357 if (!self.rule) | |
1358 WebInspector.panels.elements.treeOutline.update(); | |
1359 } | |
1360 | |
1361 InjectedScriptAccess.applyStyleText(this.style.id, styleText.trimWhitesp
ace(), this.name, callback); | |
1362 } | |
1363 } | |
1364 | |
1365 WebInspector.StylePropertyTreeElement.prototype.__proto__ = TreeElement.prototyp
e; | |
OLD | NEW |