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(this.app
ropriateSelectorForNode()); |
| 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 appropriateSelectorForNode: function() |
| 373 { |
| 374 var node = this.node; |
| 375 if (!node) |
| 376 return ""; |
| 377 |
| 378 var id = node.getAttribute("id"); |
| 379 if (id) |
| 380 return "#" + id; |
| 381 |
| 382 var className = node.getAttribute("class"); |
| 383 if (className) |
| 384 return "." + className.replace(/\s+/, "."); |
| 385 |
| 386 var nodeName = node.nodeName.toLowerCase(); |
| 387 if (nodeName === "input" && node.getAttribute("type")) |
| 388 return nodeName + "[type=\"" + node.getAttribute("type") + "\"]"; |
| 389 |
| 390 return nodeName; |
| 391 } |
| 392 } |
| 393 |
| 394 WebInspector.StylesSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.pr
ototype; |
| 395 |
| 396 WebInspector.StylePropertiesSection = function(styleRule, subtitle, computedStyl
e, usedProperties, editable) |
| 397 { |
| 398 WebInspector.PropertiesSection.call(this, styleRule.selectorText); |
| 399 |
| 400 this.titleElement.addEventListener("click", function(e) { e.stopPropagation(
); }, false); |
| 401 this.titleElement.addEventListener("dblclick", this._dblclickSelector.bind(t
his), false); |
| 402 this.element.addEventListener("dblclick", this._dblclickEmptySpace.bind(this
), false); |
| 403 |
| 404 this.styleRule = styleRule; |
| 405 this.rule = this.styleRule.rule; |
| 406 this.computedStyle = computedStyle; |
| 407 this.editable = (editable && !computedStyle); |
| 408 |
| 409 // Prevent editing the user agent and user rules. |
| 410 var isUserAgent = this.rule && this.rule.isUserAgent; |
| 411 var isUser = this.rule && this.rule.isUser; |
| 412 var isViaInspector = this.rule && this.rule.isViaInspector; |
| 413 |
| 414 if (isUserAgent || isUser) |
| 415 this.editable = false; |
| 416 |
| 417 this._usedProperties = usedProperties; |
| 418 |
| 419 if (computedStyle) { |
| 420 this.element.addStyleClass("computed-style"); |
| 421 |
| 422 if (Preferences.showInheritedComputedStyleProperties) |
| 423 this.element.addStyleClass("show-inherited"); |
| 424 |
| 425 var showInheritedLabel = document.createElement("label"); |
| 426 var showInheritedInput = document.createElement("input"); |
| 427 showInheritedInput.type = "checkbox"; |
| 428 showInheritedInput.checked = Preferences.showInheritedComputedStylePrope
rties; |
| 429 |
| 430 var computedStyleSection = this; |
| 431 var showInheritedToggleFunction = function(event) { |
| 432 Preferences.showInheritedComputedStyleProperties = showInheritedInpu
t.checked; |
| 433 if (Preferences.showInheritedComputedStyleProperties) |
| 434 computedStyleSection.element.addStyleClass("show-inherited"); |
| 435 else |
| 436 computedStyleSection.element.removeStyleClass("show-inherited"); |
| 437 event.stopPropagation(); |
| 438 }; |
| 439 |
| 440 showInheritedLabel.addEventListener("click", showInheritedToggleFunction
, false); |
| 441 |
| 442 showInheritedLabel.appendChild(showInheritedInput); |
| 443 showInheritedLabel.appendChild(document.createTextNode(WebInspector.UISt
ring("Show inherited"))); |
| 444 this.subtitleElement.appendChild(showInheritedLabel); |
| 445 } else { |
| 446 if (!subtitle) { |
| 447 if (this.styleRule.parentStyleSheet && this.styleRule.parentStyleShe
et.href) { |
| 448 var url = this.styleRule.parentStyleSheet.href; |
| 449 subtitle = WebInspector.linkifyURL(url, WebInspector.displayName
ForURL(url)); |
| 450 this.subtitleElement.addStyleClass("file"); |
| 451 } else if (isUserAgent) |
| 452 subtitle = WebInspector.UIString("user agent stylesheet"); |
| 453 else if (isUser) |
| 454 subtitle = WebInspector.UIString("user stylesheet"); |
| 455 else if (isViaInspector) |
| 456 subtitle = WebInspector.UIString("via inspector"); |
| 457 else |
| 458 subtitle = WebInspector.UIString("inline stylesheet"); |
| 459 } |
| 460 |
| 461 this.subtitle = subtitle; |
| 462 } |
| 463 |
| 464 this.identifier = styleRule.selectorText; |
| 465 if (this.subtitle) |
| 466 this.identifier += ":" + this.subtitleElement.textContent; |
| 467 } |
| 468 |
| 469 WebInspector.StylePropertiesSection.prototype = { |
| 470 get usedProperties() |
| 471 { |
| 472 return this._usedProperties || {}; |
| 473 }, |
| 474 |
| 475 set usedProperties(x) |
| 476 { |
| 477 this._usedProperties = x; |
| 478 this.update(); |
| 479 }, |
| 480 |
| 481 expand: function(dontRememberState) |
| 482 { |
| 483 WebInspector.PropertiesSection.prototype.expand.call(this); |
| 484 if (dontRememberState) |
| 485 return; |
| 486 |
| 487 if (!Preferences.styleRulesExpandedState) |
| 488 Preferences.styleRulesExpandedState = {}; |
| 489 Preferences.styleRulesExpandedState[this.identifier] = true; |
| 490 }, |
| 491 |
| 492 collapse: function(dontRememberState) |
| 493 { |
| 494 WebInspector.PropertiesSection.prototype.collapse.call(this); |
| 495 if (dontRememberState) |
| 496 return; |
| 497 |
| 498 if (!Preferences.styleRulesExpandedState) |
| 499 Preferences.styleRulesExpandedState = {}; |
| 500 Preferences.styleRulesExpandedState[this.identifier] = false; |
| 501 }, |
| 502 |
| 503 isPropertyInherited: function(property) |
| 504 { |
| 505 if (!this.computedStyle || !this._usedProperties || this.noAffect) |
| 506 return false; |
| 507 // These properties should always show for Computed Style. |
| 508 var alwaysShowComputedProperties = { "display": true, "height": true, "w
idth": true }; |
| 509 return !(property in this.usedProperties) && !(property in alwaysShowCom
putedProperties) && !(property in this.disabledComputedProperties); |
| 510 }, |
| 511 |
| 512 isPropertyOverloaded: function(property, shorthand) |
| 513 { |
| 514 if (this.computedStyle || !this._usedProperties || this.noAffect) |
| 515 return false; |
| 516 |
| 517 var used = (property in this.usedProperties); |
| 518 if (used || !shorthand) |
| 519 return !used; |
| 520 |
| 521 // Find out if any of the individual longhand properties of the shorthan
d |
| 522 // are used, if none are then the shorthand is overloaded too. |
| 523 var longhandProperties = this.styleRule.style.getLonghandProperties(prop
erty); |
| 524 for (var j = 0; j < longhandProperties.length; ++j) { |
| 525 var individualProperty = longhandProperties[j]; |
| 526 if (individualProperty in this.usedProperties) |
| 527 return false; |
| 528 } |
| 529 |
| 530 return true; |
| 531 }, |
| 532 |
| 533 isInspectorStylesheet: function() |
| 534 { |
| 535 return (this.styleRule.parentStyleSheet === WebInspector.panels.elements
.stylesheet); |
| 536 }, |
| 537 |
| 538 update: function(full) |
| 539 { |
| 540 if (full || this.computedStyle) { |
| 541 this.propertiesTreeOutline.removeChildren(); |
| 542 this.populated = false; |
| 543 } else { |
| 544 var child = this.propertiesTreeOutline.children[0]; |
| 545 while (child) { |
| 546 child.overloaded = this.isPropertyOverloaded(child.name, child.s
horthand); |
| 547 child = child.traverseNextTreeElement(false, null, true); |
| 548 } |
| 549 } |
| 550 |
| 551 if (this._afterUpdate) { |
| 552 this._afterUpdate(this); |
| 553 delete this._afterUpdate; |
| 554 } |
| 555 }, |
| 556 |
| 557 onpopulate: function() |
| 558 { |
| 559 var style = this.styleRule.style; |
| 560 |
| 561 var foundShorthands = {}; |
| 562 var uniqueProperties = style.uniqueStyleProperties; |
| 563 var disabledProperties = style.__disabledPropertyValues || {}; |
| 564 |
| 565 for (var name in disabledProperties) |
| 566 uniqueProperties.push(name); |
| 567 |
| 568 uniqueProperties.sort(); |
| 569 |
| 570 for (var i = 0; i < uniqueProperties.length; ++i) { |
| 571 var name = uniqueProperties[i]; |
| 572 var disabled = name in disabledProperties; |
| 573 if (!disabled && this.disabledComputedProperties && !(name in this.u
sedProperties) && name in this.disabledComputedProperties) |
| 574 disabled = true; |
| 575 |
| 576 var shorthand = !disabled ? style.getPropertyShorthand(name) : null; |
| 577 |
| 578 if (shorthand && shorthand in foundShorthands) |
| 579 continue; |
| 580 |
| 581 if (shorthand) { |
| 582 foundShorthands[shorthand] = true; |
| 583 name = shorthand; |
| 584 } |
| 585 |
| 586 var isShorthand = (shorthand ? true : false); |
| 587 var inherited = this.isPropertyInherited(name); |
| 588 var overloaded = this.isPropertyOverloaded(name, isShorthand); |
| 589 |
| 590 var item = new WebInspector.StylePropertyTreeElement(this.styleRule,
style, name, isShorthand, inherited, overloaded, disabled); |
| 591 this.propertiesTreeOutline.appendChild(item); |
| 592 } |
| 593 }, |
| 594 |
| 595 findTreeElementWithName: function(name) |
| 596 { |
| 597 var treeElement = this.propertiesTreeOutline.children[0]; |
| 598 while (treeElement) { |
| 599 if (treeElement.name === name) |
| 600 return treeElement; |
| 601 treeElement = treeElement.traverseNextTreeElement(true, null, true); |
| 602 } |
| 603 return null; |
| 604 }, |
| 605 |
| 606 addNewBlankProperty: function() |
| 607 { |
| 608 var item = new WebInspector.StylePropertyTreeElement(this.styleRule, thi
s.styleRule.style, "", false, false, false, false); |
| 609 this.propertiesTreeOutline.appendChild(item); |
| 610 item.listItemElement.textContent = ""; |
| 611 item._newProperty = true; |
| 612 return item; |
| 613 }, |
| 614 |
| 615 _dblclickEmptySpace: function(event) |
| 616 { |
| 617 this.expand(); |
| 618 this.addNewBlankProperty().startEditing(); |
| 619 }, |
| 620 |
| 621 _dblclickSelector: function(event) |
| 622 { |
| 623 if (!this.editable) |
| 624 return; |
| 625 |
| 626 if (!this.rule && this.propertiesTreeOutline.children.length === 0) { |
| 627 this.expand(); |
| 628 this.addNewBlankProperty().startEditing(); |
| 629 return; |
| 630 } |
| 631 |
| 632 if (!this.rule) |
| 633 return; |
| 634 |
| 635 this.startEditingSelector(); |
| 636 event.stopPropagation(); |
| 637 }, |
| 638 |
| 639 startEditingSelector: function() |
| 640 { |
| 641 var element = this.titleElement; |
| 642 if (WebInspector.isBeingEdited(element)) |
| 643 return; |
| 644 |
| 645 WebInspector.startEditing(this.titleElement, this.editingSelectorCommitt
ed.bind(this), this.editingSelectorCancelled.bind(this), null); |
| 646 window.getSelection().setBaseAndExtent(element, 0, element, 1); |
| 647 }, |
| 648 |
| 649 editingSelectorCommitted: function(element, newContent, oldContent, context,
moveDirection) |
| 650 { |
| 651 function moveToNextIfNeeded() { |
| 652 if (!moveDirection || moveDirection !== "forward") |
| 653 return; |
| 654 |
| 655 this.expand(); |
| 656 if (this.propertiesTreeOutline.children.length === 0) |
| 657 this.addNewBlankProperty().startEditing(); |
| 658 else { |
| 659 var item = this.propertiesTreeOutline.children[0] |
| 660 item.startEditing(item.valueElement); |
| 661 } |
| 662 } |
| 663 |
| 664 if (newContent === oldContent) |
| 665 return moveToNextIfNeeded.call(this); |
| 666 |
| 667 var self = this; |
| 668 function callback(result) |
| 669 { |
| 670 if (!result) { |
| 671 // Invalid Syntax for a Selector |
| 672 moveToNextIfNeeded.call(self); |
| 673 return; |
| 674 } |
| 675 |
| 676 var newRulePayload = result[0]; |
| 677 var doesAffectSelectedNode = result[1]; |
| 678 if (!doesAffectSelectedNode) { |
| 679 self.noAffect = true; |
| 680 self.element.addStyleClass("no-affect"); |
| 681 } else { |
| 682 delete self.noAffect; |
| 683 self.element.removeStyleClass("no-affect"); |
| 684 } |
| 685 |
| 686 var newRule = WebInspector.CSSStyleDeclaration.parseRule(newRulePayl
oad); |
| 687 self.rule = newRule; |
| 688 self.styleRule = { section: self, style: newRule.style, selectorText
: newRule.selectorText, parentStyleSheet: newRule.parentStyleSheet, rule: newRul
e }; |
| 689 |
| 690 var oldIdentifier = this.identifier; |
| 691 self.identifier = newRule.selectorText + ":" + self.subtitleElement.
textContent; |
| 692 |
| 693 self.pane.update(); |
| 694 |
| 695 WebInspector.panels.elements.renameSelector(oldIdentifier, this.iden
tifier, oldContent, newContent); |
| 696 |
| 697 moveToNextIfNeeded.call(self); |
| 698 } |
| 699 |
| 700 InjectedScriptAccess.applyStyleRuleText(this.rule.id, newContent, this.p
ane.node.id, callback); |
| 701 }, |
| 702 |
| 703 editingSelectorCancelled: function() |
| 704 { |
| 705 // Do nothing, this is overridden by BlankStylePropertiesSection. |
| 706 } |
| 707 } |
| 708 |
| 709 WebInspector.StylePropertiesSection.prototype.__proto__ = WebInspector.Propertie
sSection.prototype; |
| 710 |
| 711 WebInspector.BlankStylePropertiesSection = function(defaultSelectorText) |
| 712 { |
| 713 WebInspector.StylePropertiesSection.call(this, {selectorText: defaultSelecto
rText, rule: {isViaInspector: true}}, "", false, {}, false); |
| 714 |
| 715 this.element.addStyleClass("blank-section"); |
| 716 } |
| 717 |
| 718 WebInspector.BlankStylePropertiesSection.prototype = { |
| 719 expand: function() |
| 720 { |
| 721 // Do nothing, blank sections are not expandable. |
| 722 }, |
| 723 |
| 724 editingSelectorCommitted: function(element, newContent, oldContent, context) |
| 725 { |
| 726 var self = this; |
| 727 function callback(result) |
| 728 { |
| 729 if (!result) { |
| 730 // Invalid Syntax for a Selector |
| 731 self.editingSelectorCancelled(); |
| 732 return; |
| 733 } |
| 734 |
| 735 var rule = result[0]; |
| 736 var doesSelectorAffectSelectedNode = result[1]; |
| 737 |
| 738 var styleRule = WebInspector.CSSStyleDeclaration.parseRule(rule); |
| 739 styleRule.rule = rule; |
| 740 |
| 741 self.makeNormal(styleRule); |
| 742 |
| 743 if (!doesSelectorAffectSelectedNode) { |
| 744 self.noAffect = true; |
| 745 self.element.addStyleClass("no-affect"); |
| 746 } |
| 747 |
| 748 self.subtitleElement.textContent = WebInspector.UIString("via inspec
tor"); |
| 749 self.expand(); |
| 750 |
| 751 self.addNewBlankProperty().startEditing(); |
| 752 } |
| 753 |
| 754 InjectedScriptAccess.addStyleSelector(newContent, this.pane.node.id, cal
lback); |
| 755 }, |
| 756 |
| 757 editingSelectorCancelled: function() |
| 758 { |
| 759 this.pane.removeSection(this); |
| 760 }, |
| 761 |
| 762 makeNormal: function(styleRule) |
| 763 { |
| 764 this.element.removeStyleClass("blank-section"); |
| 765 |
| 766 this.styleRule = styleRule; |
| 767 this.rule = styleRule.rule; |
| 768 this.computedStyle = false; |
| 769 this.editable = true; |
| 770 this.identifier = styleRule.selectorText + ":via inspector"; |
| 771 |
| 772 this.__proto__ = WebInspector.StylePropertiesSection.prototype; |
| 773 } |
| 774 } |
| 775 |
| 776 WebInspector.BlankStylePropertiesSection.prototype.__proto__ = WebInspector.Styl
ePropertiesSection.prototype; |
| 777 |
| 778 WebInspector.StylePropertyTreeElement = function(styleRule, style, name, shortha
nd, inherited, overloaded, disabled) |
| 779 { |
| 780 this._styleRule = styleRule; |
| 781 this.style = style; |
| 782 this.name = name; |
| 783 this.shorthand = shorthand; |
| 784 this._inherited = inherited; |
| 785 this._overloaded = overloaded; |
| 786 this._disabled = disabled; |
| 787 |
| 788 // Pass an empty title, the title gets made later in onattach. |
| 789 TreeElement.call(this, "", null, shorthand); |
| 790 } |
| 791 |
| 792 WebInspector.StylePropertyTreeElement.prototype = { |
| 793 get inherited() |
| 794 { |
| 795 return this._inherited; |
| 796 }, |
| 797 |
| 798 set inherited(x) |
| 799 { |
| 800 if (x === this._inherited) |
| 801 return; |
| 802 this._inherited = x; |
| 803 this.updateState(); |
| 804 }, |
| 805 |
| 806 get overloaded() |
| 807 { |
| 808 return this._overloaded; |
| 809 }, |
| 810 |
| 811 set overloaded(x) |
| 812 { |
| 813 if (x === this._overloaded) |
| 814 return; |
| 815 this._overloaded = x; |
| 816 this.updateState(); |
| 817 }, |
| 818 |
| 819 get disabled() |
| 820 { |
| 821 return this._disabled; |
| 822 }, |
| 823 |
| 824 set disabled(x) |
| 825 { |
| 826 if (x === this._disabled) |
| 827 return; |
| 828 this._disabled = x; |
| 829 this.updateState(); |
| 830 }, |
| 831 |
| 832 get priority() |
| 833 { |
| 834 if (this.disabled && this.style.__disabledPropertyPriorities && this.nam
e in this.style.__disabledPropertyPriorities) |
| 835 return this.style.__disabledPropertyPriorities[this.name]; |
| 836 return (this.shorthand ? this.style.getShorthandPriority(this.name) : th
is.style.getPropertyPriority(this.name)); |
| 837 }, |
| 838 |
| 839 get value() |
| 840 { |
| 841 if (this.disabled && this.style.__disabledPropertyValues && this.name in
this.style.__disabledPropertyValues) |
| 842 return this.style.__disabledPropertyValues[this.name]; |
| 843 return (this.shorthand ? this.style.getShorthandValue(this.name) : this.
style.getPropertyValue(this.name)); |
| 844 }, |
| 845 |
| 846 onattach: function() |
| 847 { |
| 848 this.updateTitle(); |
| 849 }, |
| 850 |
| 851 updateTitle: function() |
| 852 { |
| 853 var priority = this.priority; |
| 854 var value = this.value; |
| 855 |
| 856 if (priority && !priority.length) |
| 857 delete priority; |
| 858 if (priority) |
| 859 priority = "!" + priority; |
| 860 |
| 861 this.updateState(); |
| 862 |
| 863 var enabledCheckboxElement = document.createElement("input"); |
| 864 enabledCheckboxElement.className = "enabled-button"; |
| 865 enabledCheckboxElement.type = "checkbox"; |
| 866 enabledCheckboxElement.checked = !this.disabled; |
| 867 enabledCheckboxElement.addEventListener("change", this.toggleEnabled.bin
d(this), false); |
| 868 |
| 869 var nameElement = document.createElement("span"); |
| 870 nameElement.className = "name"; |
| 871 nameElement.textContent = this.name; |
| 872 this.nameElement = nameElement; |
| 873 |
| 874 var valueElement = document.createElement("span"); |
| 875 valueElement.className = "value"; |
| 876 this.valueElement = valueElement; |
| 877 |
| 878 if (value) { |
| 879 function processValue(regex, processor, nextProcessor, valueText) |
| 880 { |
| 881 var container = document.createDocumentFragment(); |
| 882 |
| 883 var items = valueText.replace(regex, "\0$1\0").split("\0"); |
| 884 for (var i = 0; i < items.length; ++i) { |
| 885 if ((i % 2) === 0) { |
| 886 if (nextProcessor) |
| 887 container.appendChild(nextProcessor(items[i])); |
| 888 else |
| 889 container.appendChild(document.createTextNode(items[
i])); |
| 890 } else { |
| 891 var processedNode = processor(items[i]); |
| 892 if (processedNode) |
| 893 container.appendChild(processedNode); |
| 894 } |
| 895 } |
| 896 |
| 897 return container; |
| 898 } |
| 899 |
| 900 function linkifyURL(url) |
| 901 { |
| 902 var container = document.createDocumentFragment(); |
| 903 container.appendChild(document.createTextNode("url(")); |
| 904 container.appendChild(WebInspector.linkifyURLAsNode(url, url, nu
ll, (url in WebInspector.resourceURLMap))); |
| 905 container.appendChild(document.createTextNode(")")); |
| 906 return container; |
| 907 } |
| 908 |
| 909 function processColor(text) |
| 910 { |
| 911 try { |
| 912 var color = new WebInspector.Color(text); |
| 913 } catch (e) { |
| 914 return document.createTextNode(text); |
| 915 } |
| 916 |
| 917 var swatchElement = document.createElement("span"); |
| 918 swatchElement.title = WebInspector.UIString("Click to change col
or format"); |
| 919 swatchElement.className = "swatch"; |
| 920 swatchElement.style.setProperty("background-color", text); |
| 921 |
| 922 swatchElement.addEventListener("click", changeColorDisplay, fals
e); |
| 923 swatchElement.addEventListener("dblclick", function(event) { eve
nt.stopPropagation() }, false); |
| 924 |
| 925 var format; |
| 926 if (Preferences.showColorNicknames && color.nickname) |
| 927 format = "nickname"; |
| 928 else if (Preferences.colorFormat === "rgb") |
| 929 format = (color.simple ? "rgb" : "rgba"); |
| 930 else if (Preferences.colorFormat === "hsl") |
| 931 format = (color.simple ? "hsl" : "hsla"); |
| 932 else if (color.simple) |
| 933 format = (color.hasShortHex() ? "shorthex" : "hex"); |
| 934 else |
| 935 format = "rgba"; |
| 936 |
| 937 var colorValueElement = document.createElement("span"); |
| 938 colorValueElement.textContent = color.toString(format); |
| 939 |
| 940 function changeColorDisplay(event) |
| 941 { |
| 942 switch (format) { |
| 943 case "rgb": |
| 944 format = "hsl"; |
| 945 break; |
| 946 |
| 947 case "shorthex": |
| 948 format = "hex"; |
| 949 break; |
| 950 |
| 951 case "hex": |
| 952 format = "rgb"; |
| 953 break; |
| 954 |
| 955 case "nickname": |
| 956 if (color.simple) { |
| 957 if (color.hasShortHex()) |
| 958 format = "shorthex"; |
| 959 else |
| 960 format = "hex"; |
| 961 break; |
| 962 } |
| 963 |
| 964 format = "rgba"; |
| 965 break; |
| 966 |
| 967 case "hsl": |
| 968 if (color.nickname) |
| 969 format = "nickname"; |
| 970 else if (color.hasShortHex()) |
| 971 format = "shorthex"; |
| 972 else |
| 973 format = "hex"; |
| 974 break; |
| 975 |
| 976 case "rgba": |
| 977 format = "hsla"; |
| 978 break; |
| 979 |
| 980 case "hsla": |
| 981 if (color.nickname) |
| 982 format = "nickname"; |
| 983 else |
| 984 format = "rgba"; |
| 985 break; |
| 986 } |
| 987 |
| 988 colorValueElement.textContent = color.toString(format); |
| 989 } |
| 990 |
| 991 var container = document.createDocumentFragment(); |
| 992 container.appendChild(swatchElement); |
| 993 container.appendChild(colorValueElement); |
| 994 return container; |
| 995 } |
| 996 |
| 997 var colorRegex = /((?:rgb|hsl)a?\([^)]+\)|#[0-9a-fA-F]{6}|#[0-9a-fA-
F]{3}|\b\w+\b)/g; |
| 998 var colorProcessor = processValue.bind(window, colorRegex, processCo
lor, null); |
| 999 |
| 1000 valueElement.appendChild(processValue(/url\(([^)]+)\)/g, linkifyURL,
colorProcessor, value)); |
| 1001 } |
| 1002 |
| 1003 if (priority) { |
| 1004 var priorityElement = document.createElement("span"); |
| 1005 priorityElement.className = "priority"; |
| 1006 priorityElement.textContent = priority; |
| 1007 } |
| 1008 |
| 1009 this.listItemElement.removeChildren(); |
| 1010 |
| 1011 // Append the checkbox for root elements of an editable section. |
| 1012 if (this.treeOutline.section && this.treeOutline.section.editable && thi
s.parent.root) |
| 1013 this.listItemElement.appendChild(enabledCheckboxElement); |
| 1014 this.listItemElement.appendChild(nameElement); |
| 1015 this.listItemElement.appendChild(document.createTextNode(": ")); |
| 1016 this.listItemElement.appendChild(valueElement); |
| 1017 |
| 1018 if (priorityElement) { |
| 1019 this.listItemElement.appendChild(document.createTextNode(" ")); |
| 1020 this.listItemElement.appendChild(priorityElement); |
| 1021 } |
| 1022 |
| 1023 this.listItemElement.appendChild(document.createTextNode(";")); |
| 1024 |
| 1025 this.tooltip = this.name + ": " + valueElement.textContent + (priority ?
" " + priority : ""); |
| 1026 }, |
| 1027 |
| 1028 updateAll: function(updateAllRules) |
| 1029 { |
| 1030 if (updateAllRules && this.treeOutline.section && this.treeOutline.secti
on.pane) |
| 1031 this.treeOutline.section.pane.update(null, this.treeOutline.section)
; |
| 1032 else if (this.treeOutline.section) |
| 1033 this.treeOutline.section.update(true); |
| 1034 else |
| 1035 this.updateTitle(); // FIXME: this will not show new properties. But
we don't hit his case yet. |
| 1036 }, |
| 1037 |
| 1038 toggleEnabled: function(event) |
| 1039 { |
| 1040 var disabled = !event.target.checked; |
| 1041 |
| 1042 var self = this; |
| 1043 function callback(newPayload) |
| 1044 { |
| 1045 if (!newPayload) |
| 1046 return; |
| 1047 |
| 1048 self.style = WebInspector.CSSStyleDeclaration.parseStyle(newPayload)
; |
| 1049 self._styleRule.style = self.style; |
| 1050 |
| 1051 // Set the disabled property here, since the code above replies on i
t not changing |
| 1052 // until after the value and priority are retrieved. |
| 1053 self.disabled = disabled; |
| 1054 |
| 1055 if (self.treeOutline.section && self.treeOutline.section.pane) |
| 1056 self.treeOutline.section.pane.dispatchEventToListeners("style pr
operty toggled"); |
| 1057 |
| 1058 self.updateAll(true); |
| 1059 } |
| 1060 |
| 1061 InjectedScriptAccess.toggleStyleEnabled(this.style.id, this.name, disabl
ed, callback); |
| 1062 }, |
| 1063 |
| 1064 updateState: function() |
| 1065 { |
| 1066 if (!this.listItemElement) |
| 1067 return; |
| 1068 |
| 1069 if (this.style.isPropertyImplicit(this.name) || this.value === "initial"
) |
| 1070 this.listItemElement.addStyleClass("implicit"); |
| 1071 else |
| 1072 this.listItemElement.removeStyleClass("implicit"); |
| 1073 |
| 1074 if (this.inherited) |
| 1075 this.listItemElement.addStyleClass("inherited"); |
| 1076 else |
| 1077 this.listItemElement.removeStyleClass("inherited"); |
| 1078 |
| 1079 if (this.overloaded) |
| 1080 this.listItemElement.addStyleClass("overloaded"); |
| 1081 else |
| 1082 this.listItemElement.removeStyleClass("overloaded"); |
| 1083 |
| 1084 if (this.disabled) |
| 1085 this.listItemElement.addStyleClass("disabled"); |
| 1086 else |
| 1087 this.listItemElement.removeStyleClass("disabled"); |
| 1088 }, |
| 1089 |
| 1090 onpopulate: function() |
| 1091 { |
| 1092 // Only populate once and if this property is a shorthand. |
| 1093 if (this.children.length || !this.shorthand) |
| 1094 return; |
| 1095 |
| 1096 var longhandProperties = this.style.getLonghandProperties(this.name); |
| 1097 for (var i = 0; i < longhandProperties.length; ++i) { |
| 1098 var name = longhandProperties[i]; |
| 1099 |
| 1100 if (this.treeOutline.section) { |
| 1101 var inherited = this.treeOutline.section.isPropertyInherited(nam
e); |
| 1102 var overloaded = this.treeOutline.section.isPropertyOverloaded(n
ame); |
| 1103 } |
| 1104 |
| 1105 var item = new WebInspector.StylePropertyTreeElement(this._styleRule
, this.style, name, false, inherited, overloaded); |
| 1106 this.appendChild(item); |
| 1107 } |
| 1108 }, |
| 1109 |
| 1110 ondblclick: function(element, event) |
| 1111 { |
| 1112 this.startEditing(event.target); |
| 1113 event.stopPropagation(); |
| 1114 }, |
| 1115 |
| 1116 startEditing: function(selectElement) |
| 1117 { |
| 1118 // FIXME: we don't allow editing of longhand properties under a shorthan
d right now. |
| 1119 if (this.parent.shorthand) |
| 1120 return; |
| 1121 |
| 1122 if (WebInspector.isBeingEdited(this.listItemElement) || (this.treeOutlin
e.section && !this.treeOutline.section.editable)) |
| 1123 return; |
| 1124 |
| 1125 var context = { expanded: this.expanded, hasChildren: this.hasChildren }
; |
| 1126 |
| 1127 // Lie about our children to prevent expanding on double click and to co
llapse shorthands. |
| 1128 this.hasChildren = false; |
| 1129 |
| 1130 if (!selectElement) |
| 1131 selectElement = this.listItemElement; |
| 1132 |
| 1133 this.listItemElement.handleKeyEvent = this.editingKeyDown.bind(this); |
| 1134 |
| 1135 WebInspector.startEditing(this.listItemElement, this.editingCommitted.bi
nd(this), this.editingCancelled.bind(this), context); |
| 1136 window.getSelection().setBaseAndExtent(selectElement, 0, selectElement,
1); |
| 1137 }, |
| 1138 |
| 1139 editingKeyDown: function(event) |
| 1140 { |
| 1141 var arrowKeyPressed = (event.keyIdentifier === "Up" || event.keyIdentifi
er === "Down"); |
| 1142 var pageKeyPressed = (event.keyIdentifier === "PageUp" || event.keyIdent
ifier === "PageDown"); |
| 1143 if (!arrowKeyPressed && !pageKeyPressed) |
| 1144 return; |
| 1145 |
| 1146 var selection = window.getSelection(); |
| 1147 if (!selection.rangeCount) |
| 1148 return; |
| 1149 |
| 1150 var selectionRange = selection.getRangeAt(0); |
| 1151 if (selectionRange.commonAncestorContainer !== this.listItemElement && !
selectionRange.commonAncestorContainer.isDescendant(this.listItemElement)) |
| 1152 return; |
| 1153 |
| 1154 const styleValueDelimeters = " \t\n\"':;,/()"; |
| 1155 var wordRange = selectionRange.startContainer.rangeOfWord(selectionRange
.startOffset, styleValueDelimeters, this.listItemElement); |
| 1156 var wordString = wordRange.toString(); |
| 1157 var replacementString = wordString; |
| 1158 |
| 1159 var matches = /(.*?)(-?\d+(?:\.\d+)?)(.*)/.exec(wordString); |
| 1160 if (matches && matches.length) { |
| 1161 var prefix = matches[1]; |
| 1162 var number = parseFloat(matches[2]); |
| 1163 var suffix = matches[3]; |
| 1164 |
| 1165 // If the number is near zero or the number is one and the direction
will take it near zero. |
| 1166 var numberNearZero = (number < 1 && number > -1); |
| 1167 if (number === 1 && event.keyIdentifier === "Down") |
| 1168 numberNearZero = true; |
| 1169 else if (number === -1 && event.keyIdentifier === "Up") |
| 1170 numberNearZero = true; |
| 1171 |
| 1172 if (numberNearZero && event.altKey && arrowKeyPressed) { |
| 1173 if (event.keyIdentifier === "Down") |
| 1174 number = Math.ceil(number - 1); |
| 1175 else |
| 1176 number = Math.floor(number + 1); |
| 1177 } else { |
| 1178 // Jump by 10 when shift is down or jump by 0.1 when near zero o
r Alt/Option is down. |
| 1179 // Also jump by 10 for page up and down, or by 100 if shift is h
eld with a page key. |
| 1180 var changeAmount = 1; |
| 1181 if (event.shiftKey && pageKeyPressed) |
| 1182 changeAmount = 100; |
| 1183 else if (event.shiftKey || pageKeyPressed) |
| 1184 changeAmount = 10; |
| 1185 else if (event.altKey || numberNearZero) |
| 1186 changeAmount = 0.1; |
| 1187 |
| 1188 if (event.keyIdentifier === "Down" || event.keyIdentifier === "P
ageDown") |
| 1189 changeAmount *= -1; |
| 1190 |
| 1191 // Make the new number and constrain it to a precision of 6, thi
s matches numbers the engine returns. |
| 1192 // Use the Number constructor to forget the fixed precision, so
1.100000 will print as 1.1. |
| 1193 number = Number((number + changeAmount).toFixed(6)); |
| 1194 } |
| 1195 |
| 1196 replacementString = prefix + number + suffix; |
| 1197 } else { |
| 1198 // FIXME: this should cycle through known keywords for the current p
roperty name. |
| 1199 return; |
| 1200 } |
| 1201 |
| 1202 var replacementTextNode = document.createTextNode(replacementString); |
| 1203 |
| 1204 wordRange.deleteContents(); |
| 1205 wordRange.insertNode(replacementTextNode); |
| 1206 |
| 1207 var finalSelectionRange = document.createRange(); |
| 1208 finalSelectionRange.setStart(replacementTextNode, 0); |
| 1209 finalSelectionRange.setEnd(replacementTextNode, replacementString.length
); |
| 1210 |
| 1211 selection.removeAllRanges(); |
| 1212 selection.addRange(finalSelectionRange); |
| 1213 |
| 1214 event.preventDefault(); |
| 1215 event.handled = true; |
| 1216 |
| 1217 if (!this.originalCSSText) { |
| 1218 // Remember the rule's original CSS text, so it can be restored |
| 1219 // if the editing is canceled and before each apply. |
| 1220 this.originalCSSText = this.style.styleTextWithShorthands(); |
| 1221 } else { |
| 1222 // Restore the original CSS text before applying user changes. This
is needed to prevent |
| 1223 // new properties from sticking around if the user adds one, then re
moves it. |
| 1224 InjectedScriptAccess.setStyleText(this.style.id, this.originalCSSTex
t); |
| 1225 } |
| 1226 |
| 1227 this.applyStyleText(this.listItemElement.textContent); |
| 1228 }, |
| 1229 |
| 1230 editingEnded: function(context) |
| 1231 { |
| 1232 this.hasChildren = context.hasChildren; |
| 1233 if (context.expanded) |
| 1234 this.expand(); |
| 1235 delete this.listItemElement.handleKeyEvent; |
| 1236 delete this.originalCSSText; |
| 1237 }, |
| 1238 |
| 1239 editingCancelled: function(element, context) |
| 1240 { |
| 1241 if (this._newProperty) |
| 1242 this.treeOutline.removeChild(this); |
| 1243 else if (this.originalCSSText) { |
| 1244 InjectedScriptAccess.setStyleText(this.style.id, this.originalCSSTex
t); |
| 1245 |
| 1246 if (this.treeOutline.section && this.treeOutline.section.pane) |
| 1247 this.treeOutline.section.pane.dispatchEventToListeners("style ed
ited"); |
| 1248 |
| 1249 this.updateAll(); |
| 1250 } else |
| 1251 this.updateTitle(); |
| 1252 |
| 1253 this.editingEnded(context); |
| 1254 }, |
| 1255 |
| 1256 editingCommitted: function(element, userInput, previousContent, context, mov
eDirection) |
| 1257 { |
| 1258 this.editingEnded(context); |
| 1259 |
| 1260 // Determine where to move to before making changes |
| 1261 var newProperty, moveToPropertyName, moveToSelector; |
| 1262 var moveTo = (moveDirection === "forward" ? this.nextSibling : this.prev
iousSibling); |
| 1263 if (moveTo) |
| 1264 moveToPropertyName = moveTo.name; |
| 1265 else if (moveDirection === "forward") |
| 1266 newProperty = true; |
| 1267 else if (moveDirection === "backward" && this.treeOutline.section.rule) |
| 1268 moveToSelector = true; |
| 1269 |
| 1270 // Make the Changes and trigger the moveToNextCallback after updating |
| 1271 var blankInput = /^\s*$/.test(userInput); |
| 1272 if (userInput !== previousContent || (this._newProperty && blankInput))
{ // only if something changed, or adding a new style and it was blank |
| 1273 this.treeOutline.section._afterUpdate = moveToNextCallback.bind(this
, this._newProperty, !blankInput); |
| 1274 this.applyStyleText(userInput, true); |
| 1275 } else |
| 1276 moveToNextCallback(this._newProperty, false, this.treeOutline.sectio
n, false); |
| 1277 |
| 1278 // The Callback to start editing the next property |
| 1279 function moveToNextCallback(alreadyNew, valueChanged, section) |
| 1280 { |
| 1281 if (!moveDirection) |
| 1282 return; |
| 1283 |
| 1284 // User just tabbed through without changes |
| 1285 if (moveTo && moveTo.parent) { |
| 1286 moveTo.startEditing(moveTo.valueElement); |
| 1287 return; |
| 1288 } |
| 1289 |
| 1290 // User has made a change then tabbed, wiping all the original treeE
lements, |
| 1291 // recalculate the new treeElement for the same property we were goi
ng to edit next |
| 1292 if (moveTo && !moveTo.parent) { |
| 1293 var treeElement = section.findTreeElementWithName(moveToProperty
Name); |
| 1294 if (treeElement) |
| 1295 treeElement.startEditing(treeElement.valueElement); |
| 1296 return; |
| 1297 } |
| 1298 |
| 1299 // Create a new attribute in this section |
| 1300 if (newProperty) { |
| 1301 if (alreadyNew && !valueChanged) |
| 1302 return; |
| 1303 |
| 1304 var item = section.addNewBlankProperty(); |
| 1305 item.startEditing(); |
| 1306 return; |
| 1307 } |
| 1308 |
| 1309 if (moveToSelector) |
| 1310 section.startEditingSelector(); |
| 1311 } |
| 1312 }, |
| 1313 |
| 1314 applyStyleText: function(styleText, updateInterface) |
| 1315 { |
| 1316 var section = this.treeOutline.section; |
| 1317 var elementsPanel = WebInspector.panels.elements; |
| 1318 var styleTextLength = styleText.trimWhitespace().length; |
| 1319 if (!styleTextLength && updateInterface) { |
| 1320 if (this._newProperty) { |
| 1321 // The user deleted everything, so remove the tree element and u
pdate. |
| 1322 this.parent.removeChild(this); |
| 1323 return; |
| 1324 } else { |
| 1325 delete section._afterUpdate; |
| 1326 } |
| 1327 } |
| 1328 |
| 1329 var self = this; |
| 1330 function callback(result) |
| 1331 { |
| 1332 if (!result) { |
| 1333 // The user typed something, but it didn't parse. Just abort and
restore |
| 1334 // the original title for this property. If this was a new attr
ibute and |
| 1335 // we couldn't parse, then just remove it. |
| 1336 if (self._newProperty) { |
| 1337 self.parent.removeChild(self); |
| 1338 return; |
| 1339 } |
| 1340 if (updateInterface) |
| 1341 self.updateTitle(); |
| 1342 return; |
| 1343 } |
| 1344 |
| 1345 var newPayload = result[0]; |
| 1346 var changedProperties = result[1]; |
| 1347 elementsPanel.removeStyleChange(section.identifier, self.style, self
.name); |
| 1348 |
| 1349 if (!styleTextLength) { |
| 1350 // Do remove ourselves from UI when the property removal is conf
irmed. |
| 1351 self.parent.removeChild(self); |
| 1352 } else { |
| 1353 self.style = WebInspector.CSSStyleDeclaration.parseStyle(newPayl
oad); |
| 1354 for (var i = 0; i < changedProperties.length; ++i) |
| 1355 elementsPanel.addStyleChange(section.identifier, self.style,
changedProperties[i]); |
| 1356 self._styleRule.style = self.style; |
| 1357 } |
| 1358 |
| 1359 if (section && section.pane) |
| 1360 section.pane.dispatchEventToListeners("style edited"); |
| 1361 |
| 1362 if (updateInterface) |
| 1363 self.updateAll(true); |
| 1364 |
| 1365 if (!self.rule) |
| 1366 WebInspector.panels.elements.treeOutline.update(); |
| 1367 } |
| 1368 |
| 1369 InjectedScriptAccess.applyStyleText(this.style.id, styleText.trimWhitesp
ace(), this.name, callback); |
| 1370 } |
| 1371 } |
| 1372 |
| 1373 WebInspector.StylePropertyTreeElement.prototype.__proto__ = TreeElement.prototyp
e; |
OLD | NEW |