OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2007 Apple Inc. All rights reserved. | |
3 * | |
4 * Redistribution and use in source and binary forms, with or without | |
5 * modification, are permitted provided that the following conditions | |
6 * are met: | |
7 * | |
8 * 1. Redistributions of source code must retain the above copyright | |
9 * notice, this list of conditions and the following disclaimer. | |
10 * 2. Redistributions in binary form must reproduce the above copyright | |
11 * notice, this list of conditions and the following disclaimer in the | |
12 * documentation and/or other materials provided with the distribution. | |
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of | |
14 * its contributors may be used to endorse or promote products derived | |
15 * from this software without specific prior written permission. | |
16 * | |
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY | |
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY | |
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
27 */ | |
28 | |
29 WebInspector.StylesSidebarPane = function() | |
30 { | |
31 WebInspector.SidebarPane.call(this, WebInspector.UIString("Styles")); | |
32 } | |
33 | |
34 WebInspector.StylesSidebarPane.prototype = { | |
35 update: function(node, editedSection, forceUpdate) | |
36 { | |
37 var refresh = false; | |
38 | |
39 if (forceUpdate) | |
40 delete this.node; | |
41 | |
42 if (!forceUpdate && (!node || node === this.node)) | |
43 refresh = true; | |
44 | |
45 if (node && node.nodeType === Node.TEXT_NODE && node.parentNode) | |
46 node = node.parentNode; | |
47 | |
48 if (node && node.nodeType !== Node.ELEMENT_NODE) | |
49 node = null; | |
50 | |
51 if (node) | |
52 this.node = node; | |
53 else | |
54 node = this.node; | |
55 | |
56 var body = this.bodyElement; | |
57 if (!refresh || !node) { | |
58 body.removeChildren(); | |
59 this.sections = []; | |
60 } | |
61 | |
62 if (!node) | |
63 return; | |
64 | |
65 var styleRules = []; | |
66 | |
67 if (refresh) { | |
68 for (var i = 0; i < this.sections.length; ++i) { | |
69 var section = this.sections[i]; | |
70 if (section.computedStyle) | |
71 section.styleRule.style = node.ownerDocument.defaultView.get
ComputedStyle(node); | |
72 var styleRule = { section: section, style: section.styleRule.sty
le, computedStyle: section.computedStyle }; | |
73 styleRules.push(styleRule); | |
74 } | |
75 } else { | |
76 var computedStyle = node.ownerDocument.defaultView.getComputedStyle(
node); | |
77 styleRules.push({ computedStyle: true, selectorText: WebInspector.UI
String("Computed Style"), style: computedStyle, editable: false }); | |
78 | |
79 var nodeName = node.nodeName.toLowerCase(); | |
80 for (var i = 0; i < node.attributes.length; ++i) { | |
81 var attr = node.attributes[i]; | |
82 if (attr.style) { | |
83 var attrStyle = { style: attr.style, editable: false }; | |
84 attrStyle.subtitle = WebInspector.UIString("element’s “%s” a
ttribute", attr.name); | |
85 attrStyle.selectorText = nodeName + "[" + attr.name; | |
86 if (attr.value.length) | |
87 attrStyle.selectorText += "=" + attr.value; | |
88 attrStyle.selectorText += "]"; | |
89 styleRules.push(attrStyle); | |
90 } | |
91 } | |
92 | |
93 if (node.style && (node.style.length || Object.hasProperties(node.st
yle.__disabledProperties))) { | |
94 var inlineStyle = { selectorText: WebInspector.UIString("Inline
Style Attribute"), style: node.style }; | |
95 inlineStyle.subtitle = WebInspector.UIString("element’s “%s” att
ribute", "style"); | |
96 styleRules.push(inlineStyle); | |
97 } | |
98 | |
99 var matchedStyleRules = node.ownerDocument.defaultView.getMatchedCSS
Rules(node, "", !Preferences.showUserAgentStyles); | |
100 if (matchedStyleRules) { | |
101 // Add rules in reverse order to match the cascade order. | |
102 for (var i = (matchedStyleRules.length - 1); i >= 0; --i) { | |
103 var rule = matchedStyleRules[i]; | |
104 styleRules.push({ style: rule.style, selectorText: rule.sele
ctorText, parentStyleSheet: rule.parentStyleSheet }); | |
105 } | |
106 } | |
107 } | |
108 | |
109 function deleteDisabledProperty(style, name) | |
110 { | |
111 if (!style || !name) | |
112 return; | |
113 if (style.__disabledPropertyValues) | |
114 delete style.__disabledPropertyValues[name]; | |
115 if (style.__disabledPropertyPriorities) | |
116 delete style.__disabledPropertyPriorities[name]; | |
117 if (style.__disabledProperties) | |
118 delete style.__disabledProperties[name]; | |
119 } | |
120 | |
121 var usedProperties = {}; | |
122 var disabledComputedProperties = {}; | |
123 var priorityUsed = false; | |
124 | |
125 // Walk the style rules and make a list of all used and overloaded prope
rties. | |
126 for (var i = 0; i < styleRules.length; ++i) { | |
127 var styleRule = styleRules[i]; | |
128 if (styleRule.computedStyle) | |
129 continue; | |
130 | |
131 styleRule.usedProperties = {}; | |
132 | |
133 var style = styleRule.style; | |
134 for (var j = 0; j < style.length; ++j) { | |
135 var name = style[j]; | |
136 | |
137 if (!priorityUsed && style.getPropertyPriority(name).length) | |
138 priorityUsed = true; | |
139 | |
140 // If the property name is already used by another rule then thi
s rule's | |
141 // property is overloaded, so don't add it to the rule's usedPro
perties. | |
142 if (!(name in usedProperties)) | |
143 styleRule.usedProperties[name] = true; | |
144 | |
145 if (name === "font") { | |
146 // The font property is not reported as a shorthand. Report
finding the individual | |
147 // properties so they are visible in computed style. | |
148 // FIXME: remove this when http://bugs.webkit.org/show_bug.c
gi?id=15598 is fixed. | |
149 styleRule.usedProperties["font-family"] = true; | |
150 styleRule.usedProperties["font-size"] = true; | |
151 styleRule.usedProperties["font-style"] = true; | |
152 styleRule.usedProperties["font-variant"] = true; | |
153 styleRule.usedProperties["font-weight"] = true; | |
154 styleRule.usedProperties["line-height"] = true; | |
155 } | |
156 | |
157 // Delete any disabled properties, since the property does exist
. | |
158 // This prevents it from showing twice. | |
159 deleteDisabledProperty(style, name); | |
160 deleteDisabledProperty(style, style.getPropertyShorthand(name)); | |
161 } | |
162 | |
163 // Add all the properties found in this style to the used properties
list. | |
164 // Do this here so only future rules are affect by properties used i
n this rule. | |
165 for (var name in styleRules[i].usedProperties) | |
166 usedProperties[name] = true; | |
167 | |
168 // Remember all disabled properties so they show up in computed styl
e. | |
169 if (style.__disabledProperties) | |
170 for (var name in style.__disabledProperties) | |
171 disabledComputedProperties[name] = true; | |
172 } | |
173 | |
174 if (priorityUsed) { | |
175 // Walk the properties again and account for !important. | |
176 var foundPriorityProperties = []; | |
177 | |
178 // Walk in reverse to match the order !important overrides. | |
179 for (var i = (styleRules.length - 1); i >= 0; --i) { | |
180 if (styleRules[i].computedStyle) | |
181 continue; | |
182 | |
183 var style = styleRules[i].style; | |
184 var uniqueProperties = getUniqueStyleProperties(style); | |
185 for (var j = 0; j < uniqueProperties.length; ++j) { | |
186 var name = uniqueProperties[j]; | |
187 if (style.getPropertyPriority(name).length) { | |
188 if (!(name in foundPriorityProperties)) | |
189 styleRules[i].usedProperties[name] = true; | |
190 else | |
191 delete styleRules[i].usedProperties[name]; | |
192 foundPriorityProperties[name] = true; | |
193 } else if (name in foundPriorityProperties) | |
194 delete styleRules[i].usedProperties[name]; | |
195 } | |
196 } | |
197 } | |
198 | |
199 if (refresh) { | |
200 // Walk the style rules and update the sections with new overloaded
and used properties. | |
201 for (var i = 0; i < styleRules.length; ++i) { | |
202 var styleRule = styleRules[i]; | |
203 var section = styleRule.section; | |
204 if (styleRule.computedStyle) | |
205 section.disabledComputedProperties = disabledComputedPropert
ies; | |
206 section._usedProperties = (styleRule.usedProperties || usedPrope
rties); | |
207 section.update((section === editedSection) || styleRule.computed
Style); | |
208 } | |
209 } else { | |
210 // Make a property section for each style rule. | |
211 for (var i = 0; i < styleRules.length; ++i) { | |
212 var styleRule = styleRules[i]; | |
213 var subtitle = styleRule.subtitle; | |
214 delete styleRule.subtitle; | |
215 | |
216 var computedStyle = styleRule.computedStyle; | |
217 delete styleRule.computedStyle; | |
218 | |
219 var ruleUsedProperties = styleRule.usedProperties; | |
220 delete styleRule.usedProperties; | |
221 | |
222 var editable = styleRule.editable; | |
223 delete styleRule.editable; | |
224 | |
225 // Default editable to true if it was omitted. | |
226 if (typeof editable === "undefined") | |
227 editable = true; | |
228 | |
229 var section = new WebInspector.StylePropertiesSection(styleRule,
subtitle, computedStyle, (ruleUsedProperties || usedProperties), editable); | |
230 if (computedStyle) | |
231 section.disabledComputedProperties = disabledComputedPropert
ies; | |
232 section.pane = this; | |
233 | |
234 if (Preferences.styleRulesExpandedState && section.identifier in
Preferences.styleRulesExpandedState) | |
235 section.expanded = Preferences.styleRulesExpandedState[secti
on.identifier]; | |
236 else if (computedStyle) | |
237 section.collapse(true); | |
238 else | |
239 section.expand(true); | |
240 | |
241 body.appendChild(section.element); | |
242 this.sections.push(section); | |
243 } | |
244 } | |
245 } | |
246 } | |
247 | |
248 WebInspector.StylesSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.pr
ototype; | |
249 | |
250 WebInspector.StylePropertiesSection = function(styleRule, subtitle, computedStyl
e, usedProperties, editable) | |
251 { | |
252 WebInspector.PropertiesSection.call(this, styleRule.selectorText); | |
253 | |
254 this.styleRule = styleRule; | |
255 this.computedStyle = computedStyle; | |
256 this.editable = (editable && !computedStyle); | |
257 | |
258 // Prevent editing the user agent and user rules. | |
259 var isUserAgent = this.styleRule.parentStyleSheet && !this.styleRule.parentS
tyleSheet.ownerNode && !this.styleRule.parentStyleSheet.href; | |
260 var isUser = this.styleRule.parentStyleSheet && this.styleRule.parentStyleSh
eet.ownerNode && this.styleRule.parentStyleSheet.ownerNode.nodeName == '#documen
t'; | |
261 if (isUserAgent || isUser) | |
262 this.editable = false; | |
263 | |
264 this._usedProperties = usedProperties; | |
265 | |
266 if (computedStyle) { | |
267 this.element.addStyleClass("computed-style"); | |
268 | |
269 if (Preferences.showInheritedComputedStyleProperties) | |
270 this.element.addStyleClass("show-inherited"); | |
271 | |
272 var showInheritedLabel = document.createElement("label"); | |
273 var showInheritedInput = document.createElement("input"); | |
274 showInheritedInput.type = "checkbox"; | |
275 showInheritedInput.checked = Preferences.showInheritedComputedStylePrope
rties; | |
276 | |
277 var computedStyleSection = this; | |
278 var showInheritedToggleFunction = function(event) { | |
279 Preferences.showInheritedComputedStyleProperties = showInheritedInpu
t.checked; | |
280 if (Preferences.showInheritedComputedStyleProperties) | |
281 computedStyleSection.element.addStyleClass("show-inherited"); | |
282 else | |
283 computedStyleSection.element.removeStyleClass("show-inherited"); | |
284 event.stopPropagation(); | |
285 }; | |
286 | |
287 showInheritedLabel.addEventListener("click", showInheritedToggleFunction
, false); | |
288 | |
289 showInheritedLabel.appendChild(showInheritedInput); | |
290 showInheritedLabel.appendChild(document.createTextNode(WebInspector.UISt
ring("Show inherited"))); | |
291 this.subtitleElement.appendChild(showInheritedLabel); | |
292 } else { | |
293 if (!subtitle) { | |
294 if (this.styleRule.parentStyleSheet && this.styleRule.parentStyleShe
et.href) { | |
295 var url = this.styleRule.parentStyleSheet.href; | |
296 subtitle = WebInspector.linkifyURL(url, WebInspector.displayName
ForURL(url)); | |
297 this.subtitleElement.addStyleClass("file"); | |
298 } else if (isUserAgent) | |
299 subtitle = WebInspector.UIString("user agent stylesheet"); | |
300 else if (isUser) | |
301 subtitle = WebInspector.UIString("user stylesheet"); | |
302 else | |
303 subtitle = WebInspector.UIString("inline stylesheet"); | |
304 } | |
305 | |
306 this.subtitle = subtitle; | |
307 } | |
308 | |
309 this.identifier = styleRule.selectorText; | |
310 if (this.subtitle) | |
311 this.identifier += ":" + this.subtitleElement.textContent; | |
312 } | |
313 | |
314 WebInspector.StylePropertiesSection.prototype = { | |
315 get usedProperties() | |
316 { | |
317 return this._usedProperties || {}; | |
318 }, | |
319 | |
320 set usedProperties(x) | |
321 { | |
322 this._usedProperties = x; | |
323 this.update(); | |
324 }, | |
325 | |
326 expand: function(dontRememberState) | |
327 { | |
328 WebInspector.PropertiesSection.prototype.expand.call(this); | |
329 if (dontRememberState) | |
330 return; | |
331 | |
332 if (!Preferences.styleRulesExpandedState) | |
333 Preferences.styleRulesExpandedState = {}; | |
334 Preferences.styleRulesExpandedState[this.identifier] = true; | |
335 }, | |
336 | |
337 collapse: function(dontRememberState) | |
338 { | |
339 WebInspector.PropertiesSection.prototype.collapse.call(this); | |
340 if (dontRememberState) | |
341 return; | |
342 | |
343 if (!Preferences.styleRulesExpandedState) | |
344 Preferences.styleRulesExpandedState = {}; | |
345 Preferences.styleRulesExpandedState[this.identifier] = false; | |
346 }, | |
347 | |
348 isPropertyInherited: function(property) | |
349 { | |
350 if (!this.computedStyle || !this._usedProperties) | |
351 return false; | |
352 // These properties should always show for Computed Style. | |
353 var alwaysShowComputedProperties = { "display": true, "height": true, "w
idth": true }; | |
354 return !(property in this.usedProperties) && !(property in alwaysShowCom
putedProperties) && !(property in this.disabledComputedProperties); | |
355 }, | |
356 | |
357 isPropertyOverloaded: function(property, shorthand) | |
358 { | |
359 if (this.computedStyle || !this._usedProperties) | |
360 return false; | |
361 | |
362 var used = (property in this.usedProperties); | |
363 if (used || !shorthand) | |
364 return !used; | |
365 | |
366 // Find out if any of the individual longhand properties of the shorthan
d | |
367 // are used, if none are then the shorthand is overloaded too. | |
368 var longhandProperties = getLonghandProperties(this.styleRule.style, pro
perty); | |
369 for (var j = 0; j < longhandProperties.length; ++j) { | |
370 var individualProperty = longhandProperties[j]; | |
371 if (individualProperty in this.usedProperties) | |
372 return false; | |
373 } | |
374 | |
375 return true; | |
376 }, | |
377 | |
378 update: function(full) | |
379 { | |
380 if (full || this.computedStyle) { | |
381 this.propertiesTreeOutline.removeChildren(); | |
382 this.populated = false; | |
383 } else { | |
384 var child = this.propertiesTreeOutline.children[0]; | |
385 while (child) { | |
386 child.overloaded = this.isPropertyOverloaded(child.name, child.s
horthand); | |
387 child = child.traverseNextTreeElement(false, null, true); | |
388 } | |
389 } | |
390 }, | |
391 | |
392 onpopulate: function() | |
393 { | |
394 var style = this.styleRule.style; | |
395 | |
396 var foundShorthands = {}; | |
397 var uniqueProperties = getUniqueStyleProperties(style); | |
398 var disabledProperties = style.__disabledPropertyValues || {}; | |
399 | |
400 for (var name in disabledProperties) | |
401 uniqueProperties.push(name); | |
402 | |
403 uniqueProperties.sort(); | |
404 | |
405 for (var i = 0; i < uniqueProperties.length; ++i) { | |
406 var name = uniqueProperties[i]; | |
407 var disabled = name in disabledProperties; | |
408 if (!disabled && this.disabledComputedProperties && !(name in this.u
sedProperties) && name in this.disabledComputedProperties) | |
409 disabled = true; | |
410 | |
411 var shorthand = !disabled ? style.getPropertyShorthand(name) : null; | |
412 | |
413 if (shorthand && shorthand in foundShorthands) | |
414 continue; | |
415 | |
416 if (shorthand) { | |
417 foundShorthands[shorthand] = true; | |
418 name = shorthand; | |
419 } | |
420 | |
421 var isShorthand = (shorthand ? true : false); | |
422 var inherited = this.isPropertyInherited(name); | |
423 var overloaded = this.isPropertyOverloaded(name, isShorthand); | |
424 | |
425 var item = new WebInspector.StylePropertyTreeElement(style, name, is
Shorthand, inherited, overloaded, disabled); | |
426 this.propertiesTreeOutline.appendChild(item); | |
427 } | |
428 } | |
429 } | |
430 | |
431 WebInspector.StylePropertiesSection.prototype.__proto__ = WebInspector.Propertie
sSection.prototype; | |
432 | |
433 WebInspector.StylePropertyTreeElement = function(style, name, shorthand, inherit
ed, overloaded, disabled) | |
434 { | |
435 this.style = style; | |
436 this.name = name; | |
437 this.shorthand = shorthand; | |
438 this._inherited = inherited; | |
439 this._overloaded = overloaded; | |
440 this._disabled = disabled; | |
441 | |
442 // Pass an empty title, the title gets made later in onattach. | |
443 TreeElement.call(this, "", null, shorthand); | |
444 } | |
445 | |
446 WebInspector.StylePropertyTreeElement.prototype = { | |
447 get inherited() | |
448 { | |
449 return this._inherited; | |
450 }, | |
451 | |
452 set inherited(x) | |
453 { | |
454 if (x === this._inherited) | |
455 return; | |
456 this._inherited = x; | |
457 this.updateState(); | |
458 }, | |
459 | |
460 get overloaded() | |
461 { | |
462 return this._overloaded; | |
463 }, | |
464 | |
465 set overloaded(x) | |
466 { | |
467 if (x === this._overloaded) | |
468 return; | |
469 this._overloaded = x; | |
470 this.updateState(); | |
471 }, | |
472 | |
473 get disabled() | |
474 { | |
475 return this._disabled; | |
476 }, | |
477 | |
478 set disabled(x) | |
479 { | |
480 if (x === this._disabled) | |
481 return; | |
482 this._disabled = x; | |
483 this.updateState(); | |
484 }, | |
485 | |
486 get priority() | |
487 { | |
488 if (this.disabled && this.style.__disabledPropertyPriorities && this.nam
e in this.style.__disabledPropertyPriorities) | |
489 return this.style.__disabledPropertyPriorities[this.name]; | |
490 return (this.shorthand ? getShorthandPriority(this.style, this.name) : t
his.style.getPropertyPriority(this.name)); | |
491 }, | |
492 | |
493 get value() | |
494 { | |
495 if (this.disabled && this.style.__disabledPropertyValues && this.name in
this.style.__disabledPropertyValues) | |
496 return this.style.__disabledPropertyValues[this.name]; | |
497 return (this.shorthand ? getShorthandValue(this.style, this.name) : this
.style.getPropertyValue(this.name)); | |
498 }, | |
499 | |
500 onattach: function() | |
501 { | |
502 this.updateTitle(); | |
503 }, | |
504 | |
505 updateTitle: function() | |
506 { | |
507 // "Nicknames" for some common values that are easier to read. | |
508 var valueNicknames = { | |
509 "rgb(0, 0, 0)": "black", | |
510 "#000": "black", | |
511 "#000000": "black", | |
512 "rgb(255, 255, 255)": "white", | |
513 "#fff": "white", | |
514 "#ffffff": "white", | |
515 "#FFF": "white", | |
516 "#FFFFFF": "white", | |
517 "rgba(0, 0, 0, 0)": "transparent", | |
518 "rgb(255, 0, 0)": "red", | |
519 "rgb(0, 255, 0)": "lime", | |
520 "rgb(0, 0, 255)": "blue", | |
521 "rgb(255, 255, 0)": "yellow", | |
522 "rgb(255, 0, 255)": "magenta", | |
523 "rgb(0, 255, 255)": "cyan" | |
524 }; | |
525 | |
526 var priority = this.priority; | |
527 var value = this.value; | |
528 var htmlValue = value; | |
529 | |
530 if (priority && !priority.length) | |
531 delete priority; | |
532 if (priority) | |
533 priority = "!" + priority; | |
534 | |
535 if (value) { | |
536 var urls = value.match(/url\([^)]+\)/); | |
537 if (urls) { | |
538 for (var i = 0; i < urls.length; ++i) { | |
539 var url = urls[i].substring(4, urls[i].length - 1); | |
540 htmlValue = htmlValue.replace(urls[i], "url(" + WebInspector
.linkifyURL(url) + ")"); | |
541 } | |
542 } else { | |
543 if (value in valueNicknames) | |
544 htmlValue = valueNicknames[value]; | |
545 htmlValue = htmlValue.escapeHTML(); | |
546 } | |
547 } else | |
548 htmlValue = value = ""; | |
549 | |
550 this.updateState(); | |
551 | |
552 var enabledCheckboxElement = document.createElement("input"); | |
553 enabledCheckboxElement.className = "enabled-button"; | |
554 enabledCheckboxElement.type = "checkbox"; | |
555 enabledCheckboxElement.checked = !this.disabled; | |
556 enabledCheckboxElement.addEventListener("change", this.toggleEnabled.bin
d(this), false); | |
557 | |
558 var nameElement = document.createElement("span"); | |
559 nameElement.className = "name"; | |
560 nameElement.textContent = this.name; | |
561 | |
562 var valueElement = document.createElement("span"); | |
563 valueElement.className = "value"; | |
564 valueElement.innerHTML = htmlValue; | |
565 | |
566 if (priority) { | |
567 var priorityElement = document.createElement("span"); | |
568 priorityElement.className = "priority"; | |
569 priorityElement.textContent = priority; | |
570 } | |
571 | |
572 this.listItemElement.removeChildren(); | |
573 | |
574 // Append the checkbox for root elements of an editable section. | |
575 if (this.treeOutline.section && this.treeOutline.section.editable && thi
s.parent.root) | |
576 this.listItemElement.appendChild(enabledCheckboxElement); | |
577 this.listItemElement.appendChild(nameElement); | |
578 this.listItemElement.appendChild(document.createTextNode(": ")); | |
579 this.listItemElement.appendChild(valueElement); | |
580 | |
581 if (priorityElement) { | |
582 this.listItemElement.appendChild(document.createTextNode(" ")); | |
583 this.listItemElement.appendChild(priorityElement); | |
584 } | |
585 | |
586 this.listItemElement.appendChild(document.createTextNode(";")); | |
587 | |
588 if (value) { | |
589 // FIXME: this dosen't catch keyword based colors like black and whi
te | |
590 var colors = value.match(/((rgb|hsl)a?\([^)]+\))|(#[0-9a-fA-F]{6})|(
#[0-9a-fA-F]{3})/g); | |
591 if (colors) { | |
592 var colorsLength = colors.length; | |
593 for (var i = 0; i < colorsLength; ++i) { | |
594 var swatchElement = document.createElement("span"); | |
595 swatchElement.className = "swatch"; | |
596 swatchElement.style.setProperty("background-color", colors[i
]); | |
597 this.listItemElement.appendChild(swatchElement); | |
598 } | |
599 } | |
600 } | |
601 | |
602 this.tooltip = this.name + ": " + (valueNicknames[value] || value) + (pr
iority ? " " + priority : ""); | |
603 }, | |
604 | |
605 updateAll: function(updateAllRules) | |
606 { | |
607 if (updateAllRules && this.treeOutline.section && this.treeOutline.secti
on.pane) | |
608 this.treeOutline.section.pane.update(null, this.treeOutline.section)
; | |
609 else if (this.treeOutline.section) | |
610 this.treeOutline.section.update(true); | |
611 else | |
612 this.updateTitle(); // FIXME: this will not show new properties. But
we don't hit his case yet. | |
613 }, | |
614 | |
615 toggleEnabled: function(event) | |
616 { | |
617 var disabled = !event.target.checked; | |
618 | |
619 if (disabled) { | |
620 if (!this.style.__disabledPropertyValues || !this.style.__disabledPr
opertyPriorities) { | |
621 var inspectedWindow = InspectorController.inspectedWindow(); | |
622 this.style.__disabledProperties = new inspectedWindow.Object; | |
623 this.style.__disabledPropertyValues = new inspectedWindow.Object
; | |
624 this.style.__disabledPropertyPriorities = new inspectedWindow.Ob
ject; | |
625 } | |
626 | |
627 this.style.__disabledPropertyValues[this.name] = this.value; | |
628 this.style.__disabledPropertyPriorities[this.name] = this.priority; | |
629 | |
630 if (this.shorthand) { | |
631 var longhandProperties = getLonghandProperties(this.style, this.
name); | |
632 for (var i = 0; i < longhandProperties.length; ++i) { | |
633 this.style.__disabledProperties[longhandProperties[i]] = tru
e; | |
634 this.style.removeProperty(longhandProperties[i]); | |
635 } | |
636 } else { | |
637 this.style.__disabledProperties[this.name] = true; | |
638 this.style.removeProperty(this.name); | |
639 } | |
640 } else { | |
641 this.style.setProperty(this.name, this.value, this.priority); | |
642 delete this.style.__disabledProperties[this.name]; | |
643 delete this.style.__disabledPropertyValues[this.name]; | |
644 delete this.style.__disabledPropertyPriorities[this.name]; | |
645 } | |
646 | |
647 // Set the disabled property here, since the code above replies on it no
t changing | |
648 // until after the value and priority are retrieved. | |
649 this.disabled = disabled; | |
650 | |
651 if (this.treeOutline.section && this.treeOutline.section.pane) | |
652 this.treeOutline.section.pane.dispatchEventToListeners("style proper
ty toggled"); | |
653 | |
654 this.updateAll(true); | |
655 }, | |
656 | |
657 updateState: function() | |
658 { | |
659 if (!this.listItemElement) | |
660 return; | |
661 | |
662 if (this.style.isPropertyImplicit(this.name) || this.value === "initial"
) | |
663 this.listItemElement.addStyleClass("implicit"); | |
664 else | |
665 this.listItemElement.removeStyleClass("implicit"); | |
666 | |
667 if (this.inherited) | |
668 this.listItemElement.addStyleClass("inherited"); | |
669 else | |
670 this.listItemElement.removeStyleClass("inherited"); | |
671 | |
672 if (this.overloaded) | |
673 this.listItemElement.addStyleClass("overloaded"); | |
674 else | |
675 this.listItemElement.removeStyleClass("overloaded"); | |
676 | |
677 if (this.disabled) | |
678 this.listItemElement.addStyleClass("disabled"); | |
679 else | |
680 this.listItemElement.removeStyleClass("disabled"); | |
681 }, | |
682 | |
683 onpopulate: function() | |
684 { | |
685 // Only populate once and if this property is a shorthand. | |
686 if (this.children.length || !this.shorthand) | |
687 return; | |
688 | |
689 var longhandProperties = getLonghandProperties(this.style, this.name); | |
690 for (var i = 0; i < longhandProperties.length; ++i) { | |
691 var name = longhandProperties[i]; | |
692 | |
693 if (this.treeOutline.section) { | |
694 var inherited = this.treeOutline.section.isPropertyInherited(nam
e); | |
695 var overloaded = this.treeOutline.section.isPropertyOverloaded(n
ame); | |
696 } | |
697 | |
698 var item = new WebInspector.StylePropertyTreeElement(this.style, nam
e, false, inherited, overloaded); | |
699 this.appendChild(item); | |
700 } | |
701 }, | |
702 | |
703 ondblclick: function(element, event) | |
704 { | |
705 this.startEditing(event.target); | |
706 }, | |
707 | |
708 startEditing: function(selectElement) | |
709 { | |
710 // FIXME: we don't allow editing of longhand properties under a shorthan
d right now. | |
711 if (this.parent.shorthand) | |
712 return; | |
713 | |
714 if (WebInspector.isBeingEdited(this.listItemElement) || (this.treeOutlin
e.section && !this.treeOutline.section.editable)) | |
715 return; | |
716 | |
717 var context = { expanded: this.expanded, hasChildren: this.hasChildren }
; | |
718 | |
719 // Lie about our children to prevent expanding on double click and to co
llapse shorthands. | |
720 this.hasChildren = false; | |
721 | |
722 if (!selectElement) | |
723 selectElement = this.listItemElement; | |
724 | |
725 this.listItemElement.handleKeyEvent = this.editingKeyDown.bind(this); | |
726 | |
727 WebInspector.startEditing(this.listItemElement, this.editingCommitted.bi
nd(this), this.editingCancelled.bind(this), context); | |
728 window.getSelection().setBaseAndExtent(selectElement, 0, selectElement,
1); | |
729 }, | |
730 | |
731 editingKeyDown: function(event) | |
732 { | |
733 var arrowKeyPressed = (event.keyIdentifier === "Up" || event.keyIdentifi
er === "Down"); | |
734 var pageKeyPressed = (event.keyIdentifier === "PageUp" || event.keyIdent
ifier === "PageDown"); | |
735 if (!arrowKeyPressed && !pageKeyPressed) | |
736 return; | |
737 | |
738 var selection = window.getSelection(); | |
739 if (!selection.rangeCount) | |
740 return; | |
741 | |
742 var selectionRange = selection.getRangeAt(0); | |
743 if (selectionRange.commonAncestorContainer !== this.listItemElement && !
selectionRange.commonAncestorContainer.isDescendant(this.listItemElement)) | |
744 return; | |
745 | |
746 const styleValueDelimeters = " \t\n\"':;,/()"; | |
747 var wordRange = selectionRange.startContainer.rangeOfWord(selectionRange
.startOffset, styleValueDelimeters, this.listItemElement); | |
748 var wordString = wordRange.toString(); | |
749 var replacementString = wordString; | |
750 | |
751 var matches = /(.*?)(-?\d+(?:\.\d+)?)(.*)/.exec(wordString); | |
752 if (matches && matches.length) { | |
753 var prefix = matches[1]; | |
754 var number = parseFloat(matches[2]); | |
755 var suffix = matches[3]; | |
756 | |
757 // If the number is near zero or the number is one and the direction
will take it near zero. | |
758 var numberNearZero = (number < 1 && number > -1); | |
759 if (number === 1 && event.keyIdentifier === "Down") | |
760 numberNearZero = true; | |
761 else if (number === -1 && event.keyIdentifier === "Up") | |
762 numberNearZero = true; | |
763 | |
764 if (numberNearZero && event.altKey && arrowKeyPressed) { | |
765 if (event.keyIdentifier === "Down") | |
766 number = Math.ceil(number - 1); | |
767 else | |
768 number = Math.floor(number + 1); | |
769 } else { | |
770 // Jump by 10 when shift is down or jump by 0.1 when near zero o
r Alt/Option is down. | |
771 // Also jump by 10 for page up and down, or by 100 if shift is h
eld with a page key. | |
772 var changeAmount = 1; | |
773 if (event.shiftKey && pageKeyPressed) | |
774 changeAmount = 100; | |
775 else if (event.shiftKey || pageKeyPressed) | |
776 changeAmount = 10; | |
777 else if (event.altKey || numberNearZero) | |
778 changeAmount = 0.1; | |
779 | |
780 if (event.keyIdentifier === "Down" || event.keyIdentifier === "P
ageDown") | |
781 changeAmount *= -1; | |
782 | |
783 // Make the new number and constrain it to a precision of 6, thi
s matches numbers the engine returns. | |
784 // Use the Number constructor to forget the fixed precision, so
1.100000 will print as 1.1. | |
785 number = Number((number + changeAmount).toFixed(6)); | |
786 } | |
787 | |
788 replacementString = prefix + number + suffix; | |
789 } else { | |
790 // FIXME: this should cycle through known keywords for the current p
roperty name. | |
791 return; | |
792 } | |
793 | |
794 var replacementTextNode = document.createTextNode(replacementString); | |
795 | |
796 wordRange.deleteContents(); | |
797 wordRange.insertNode(replacementTextNode); | |
798 | |
799 var finalSelectionRange = document.createRange(); | |
800 finalSelectionRange.setStart(replacementTextNode, 0); | |
801 finalSelectionRange.setEnd(replacementTextNode, replacementString.length
); | |
802 | |
803 selection.removeAllRanges(); | |
804 selection.addRange(finalSelectionRange); | |
805 | |
806 event.preventDefault(); | |
807 event.handled = true; | |
808 | |
809 if (!this.originalCSSText) { | |
810 // Remember the rule's original CSS text, so it can be restored | |
811 // if the editing is canceled and before each apply. | |
812 this.originalCSSText = getStyleTextWithShorthands(this.style); | |
813 } else { | |
814 // Restore the original CSS text before applying user changes. This
is needed to prevent | |
815 // new properties from sticking around if the user adds one, then re
moves it. | |
816 this.style.cssText = this.originalCSSText; | |
817 } | |
818 | |
819 this.applyStyleText(this.listItemElement.textContent); | |
820 }, | |
821 | |
822 editingEnded: function(context) | |
823 { | |
824 this.hasChildren = context.hasChildren; | |
825 if (context.expanded) | |
826 this.expand(); | |
827 delete this.listItemElement.handleKeyEvent; | |
828 delete this.originalCSSText; | |
829 }, | |
830 | |
831 editingCancelled: function(element, context) | |
832 { | |
833 if (this.originalCSSText) { | |
834 this.style.cssText = this.originalCSSText; | |
835 | |
836 if (this.treeOutline.section && this.treeOutline.section.pane) | |
837 this.treeOutline.section.pane.dispatchEventToListeners("style ed
ited"); | |
838 | |
839 this.updateAll(); | |
840 } else | |
841 this.updateTitle(); | |
842 | |
843 this.editingEnded(context); | |
844 }, | |
845 | |
846 editingCommitted: function(element, userInput, previousContent, context) | |
847 { | |
848 this.editingEnded(context); | |
849 | |
850 if (userInput === previousContent) | |
851 return; // nothing changed, so do nothing else | |
852 | |
853 this.applyStyleText(userInput, true); | |
854 }, | |
855 | |
856 applyStyleText: function(styleText, updateInterface) | |
857 { | |
858 var styleTextLength = styleText.trimWhitespace().length; | |
859 | |
860 // Create a new element to parse the user input CSS. | |
861 var parseElement = document.createElement("span"); | |
862 parseElement.setAttribute("style", styleText); | |
863 | |
864 var tempStyle = parseElement.style; | |
865 if (tempStyle.length || !styleTextLength) { | |
866 // The input was parsable or the user deleted everything, so remove
the | |
867 // original property from the real style declaration. If this repres
ents | |
868 // a shorthand remove all the longhand properties. | |
869 if (this.shorthand) { | |
870 var longhandProperties = getLonghandProperties(this.style, this.
name); | |
871 for (var i = 0; i < longhandProperties.length; ++i) | |
872 this.style.removeProperty(longhandProperties[i]); | |
873 } else | |
874 this.style.removeProperty(this.name); | |
875 } | |
876 | |
877 if (!styleTextLength) { | |
878 if (updateInterface) { | |
879 // The user deleted the everything, so remove the tree element a
nd update. | |
880 if (this.treeOutline.section && this.treeOutline.section.pane) | |
881 this.treeOutline.section.pane.update(); | |
882 this.parent.removeChild(this); | |
883 } | |
884 return; | |
885 } | |
886 | |
887 if (!tempStyle.length) { | |
888 // The user typed something, but it didn't parse. Just abort and res
tore | |
889 // the original title for this property. | |
890 if (updateInterface) | |
891 this.updateTitle(); | |
892 return; | |
893 } | |
894 | |
895 // Iterate of the properties on the test element's style declaration and | |
896 // add them to the real style declaration. We take care to move shorthan
ds. | |
897 var foundShorthands = {}; | |
898 var uniqueProperties = getUniqueStyleProperties(tempStyle); | |
899 for (var i = 0; i < uniqueProperties.length; ++i) { | |
900 var name = uniqueProperties[i]; | |
901 var shorthand = tempStyle.getPropertyShorthand(name); | |
902 | |
903 if (shorthand && shorthand in foundShorthands) | |
904 continue; | |
905 | |
906 if (shorthand) { | |
907 var value = getShorthandValue(tempStyle, shorthand); | |
908 var priority = getShorthandPriority(tempStyle, shorthand); | |
909 foundShorthands[shorthand] = true; | |
910 } else { | |
911 var value = tempStyle.getPropertyValue(name); | |
912 var priority = tempStyle.getPropertyPriority(name); | |
913 } | |
914 | |
915 // Set the property on the real style declaration. | |
916 this.style.setProperty((shorthand || name), value, priority); | |
917 } | |
918 | |
919 if (this.treeOutline.section && this.treeOutline.section.pane) | |
920 this.treeOutline.section.pane.dispatchEventToListeners("style edited
"); | |
921 | |
922 if (updateInterface) | |
923 this.updateAll(true); | |
924 } | |
925 } | |
926 | |
927 WebInspector.StylePropertyTreeElement.prototype.__proto__ = TreeElement.prototyp
e; | |
OLD | NEW |