| 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 |