1/* 2 * Copyright (C) 2013 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 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26WebInspector.CSSProperty = function(index, text, name, value, priority, enabled, overridden, implicit, anonymous, valid, styleSheetTextRange, styleDeclarationTextRange) 27{ 28 WebInspector.Object.call(this); 29 30 this._ownerStyle = null; 31 this._index = index; 32 33 this.update(text, name, value, priority, enabled, overridden, implicit, anonymous, valid, styleSheetTextRange, styleDeclarationTextRange, true); 34}; 35 36WebInspector.Object.addConstructorFunctions(WebInspector.CSSProperty); 37 38WebInspector.CSSProperty.Event = { 39 Changed: "css-property-changed", 40 OverriddenStatusChanged: "css-property-overridden-status-changed" 41}; 42 43WebInspector.CSSProperty.prototype = { 44 constructor: WebInspector.CSSProperty, 45 46 // Public 47 48 get ownerStyle() 49 { 50 return this._ownerStyle; 51 }, 52 53 set ownerStyle(ownerStyle) 54 { 55 this._ownerStyle = ownerStyle || null; 56 }, 57 58 get index() 59 { 60 return this._index; 61 }, 62 63 set index(index) 64 { 65 this._index = index; 66 }, 67 68 update: function(text, name, value, priority, enabled, overridden, implicit, anonymous, valid, styleSheetTextRange, styleDeclarationTextRange, dontFireEvents) 69 { 70 text = text || ""; 71 name = name || ""; 72 value = value || ""; 73 priority = priority || ""; 74 enabled = enabled || false; 75 overridden = overridden || false; 76 implicit = implicit || false; 77 anonymous = anonymous || false; 78 valid = valid || false; 79 80 var changed = false; 81 82 if (!dontFireEvents) { 83 changed = this._name !== name || this._value !== value || this._priority !== priority || 84 this._enabled !== enabled || this._implicit !== implicit || this._anonymous !== anonymous || this._valid !== valid; 85 } 86 87 // Use the setter for overridden if we want to fire events since the 88 // OverriddenStatusChanged event coalesces changes before it fires. 89 if (!dontFireEvents) 90 this.overridden = overridden; 91 else 92 this._overridden = overridden; 93 94 this._text = text; 95 this._name = name; 96 this._value = value; 97 this._priority = priority; 98 this._enabled = enabled; 99 this._implicit = implicit; 100 this._anonymous = anonymous; 101 this._inherited = name in WebInspector.CSSKeywordCompletions.InheritedProperties; 102 this._valid = valid; 103 this._styleSheetTextRange = styleSheetTextRange || null; 104 105 if (styleDeclarationTextRange) 106 this._styleDeclarationTextRange = styleDeclarationTextRange; 107 else 108 delete this._styleDeclarationTextRange; 109 110 this._relatedShorthandProperty = null; 111 this._relatedLonghandProperties = []; 112 113 delete this._canonicalName; 114 delete this._hasOtherVendorNameOrKeyword; 115 116 if (!this._updatePropertySoonTimeout) { 117 delete this._pendingName; 118 delete this._pendingValue; 119 delete this._pendingPriority; 120 } 121 122 if (changed) 123 this.dispatchEventToListeners(WebInspector.CSSProperty.Event.Changed); 124 }, 125 126 get synthesizedText() 127 { 128 var name = this.name; 129 if (!name) 130 return ""; 131 132 var priority = this.priority; 133 return name + ": " + this.value.trim() + (priority ? " !" + priority : "") + ";"; 134 }, 135 136 get text() 137 { 138 return this._text || this.synthesizedText; 139 }, 140 141 set text(text) 142 { 143 if (!this._ownerStyle || !this._ownerStyle.editable) 144 return; 145 146 if (this._text === text) 147 return; 148 149 if (isNaN(this._index)) { 150 this._text = text || ""; 151 152 // Clear the name, value and priority since they might not match the text now. 153 this._name = ""; 154 this._value = ""; 155 this._priority = ""; 156 157 // Ditto for the canonical and pending properties. 158 delete this._canonicalName; 159 delete this._pendingName; 160 delete this._pendingValue; 161 delete this._pendingPriority; 162 163 return; 164 } 165 166 this._cancelPendingUpdate(); 167 this._ownerStyle.nodeStyles.changePropertyText(this, text); 168 }, 169 170 get name() 171 { 172 if (isNaN(this._index)) 173 return this._pendingName || this._name; 174 return this._name; 175 }, 176 177 set name(name) 178 { 179 if (!this._ownerStyle || !this._ownerStyle.editable) 180 return; 181 182 if (this._name === name) 183 return; 184 185 if (isNaN(this._index)) { 186 this._name = name; 187 this._text = ""; 188 189 delete this._canonicalName; 190 } else { 191 this._pendingName = name; 192 this._updatePropertySoon(); 193 } 194 }, 195 196 get canonicalName() 197 { 198 if (this._canonicalName) 199 return this._canonicalName; 200 201 this._canonicalName = WebInspector.cssStyleManager.canonicalNameForPropertyName(this.name); 202 203 return this._canonicalName; 204 }, 205 206 get value() 207 { 208 if (isNaN(this._index)) 209 return this._pendingValue || this._value; 210 return this._value; 211 }, 212 213 set value(value) 214 { 215 if (!this._ownerStyle || !this._ownerStyle.editable) 216 return; 217 218 if (this._value === value) 219 return; 220 221 if (isNaN(this._index)) { 222 this._value = value; 223 this._text = ""; 224 } else { 225 this._pendingValue = value; 226 this._updatePropertySoon(); 227 } 228 }, 229 230 get important() 231 { 232 return this.priority === "important"; 233 }, 234 235 set important(important) 236 { 237 this.priority = important ? "important" : ""; 238 }, 239 240 get priority() 241 { 242 if (isNaN(this._index)) 243 return this._pendingPriority || this._priority; 244 return this._priority; 245 }, 246 247 set priority(priority) 248 { 249 if (!this._ownerStyle || !this._ownerStyle.editable) 250 return; 251 252 if (this._priority === priority) 253 return; 254 255 if (isNaN(this._index)) { 256 this._priority = priority; 257 this._text = ""; 258 } else { 259 this._pendingPriority = priority; 260 this._updatePropertySoon(); 261 } 262 }, 263 264 get enabled() 265 { 266 return this._enabled && this._ownerStyle && (!isNaN(this._index) || this._ownerStyle.type === WebInspector.CSSStyleDeclaration.Type.Computed); 267 }, 268 269 set enabled(enabled) 270 { 271 if (!this._ownerStyle || !this._ownerStyle.editable) 272 return; 273 274 this._ownerStyle.nodeStyles.changePropertyEnabledState(this, enabled); 275 }, 276 277 get overridden() 278 { 279 return this._overridden; 280 }, 281 282 set overridden(overridden) 283 { 284 overridden = overridden || false; 285 286 if (this._overridden === overridden) 287 return; 288 289 var previousOverridden = this._overridden; 290 291 this._overridden = overridden; 292 293 if (this._overriddenStatusChangedTimeout) 294 return; 295 296 function delayed() 297 { 298 delete this._overriddenStatusChangedTimeout; 299 300 if (this._overridden === previousOverridden) 301 return; 302 303 this.dispatchEventToListeners(WebInspector.CSSProperty.Event.OverriddenStatusChanged); 304 } 305 306 this._overriddenStatusChangedTimeout = setTimeout(delayed.bind(this), 0); 307 }, 308 309 get implicit() 310 { 311 return this._implicit; 312 }, 313 314 get anonymous() 315 { 316 return this._anonymous; 317 }, 318 319 get inherited() 320 { 321 return this._inherited; 322 }, 323 324 get valid() 325 { 326 return this._valid; 327 }, 328 329 get styleSheetTextRange() 330 { 331 return this._styleSheetTextRange; 332 }, 333 334 get styleDeclarationTextRange() 335 { 336 if ("_styleDeclarationTextRange" in this) 337 return this._styleDeclarationTextRange; 338 339 if (!this._ownerStyle || !this._styleSheetTextRange) 340 return null; 341 342 var styleTextRange = this._ownerStyle.styleSheetTextRange; 343 if (!styleTextRange) 344 return null; 345 346 var startLine = this._styleSheetTextRange.startLine - styleTextRange.startLine; 347 var endLine = this._styleSheetTextRange.endLine - styleTextRange.startLine; 348 349 var startColumn = this._styleSheetTextRange.startColumn; 350 if (!startLine) 351 startColumn -= styleTextRange.startColumn; 352 353 var endColumn = this._styleSheetTextRange.endColumn; 354 if (!endLine) 355 endColumn -= styleTextRange.startColumn; 356 357 this._styleDeclarationTextRange = new WebInspector.TextRange(startLine, startColumn, endLine, endColumn); 358 359 return this._styleDeclarationTextRange; 360 }, 361 362 get relatedShorthandProperty() 363 { 364 return this._relatedShorthandProperty; 365 }, 366 367 set relatedShorthandProperty(property) 368 { 369 this._relatedShorthandProperty = property || null; 370 }, 371 372 get relatedLonghandProperties() 373 { 374 return this._relatedLonghandProperties; 375 }, 376 377 addRelatedLonghandProperty: function(property) 378 { 379 this._relatedLonghandProperties.push(property); 380 }, 381 382 clearRelatedLonghandProperties: function(property) 383 { 384 this._relatedLonghandProperties = []; 385 }, 386 387 hasOtherVendorNameOrKeyword: function() 388 { 389 if ("_hasOtherVendorNameOrKeyword" in this) 390 return this._hasOtherVendorNameOrKeyword; 391 392 this._hasOtherVendorNameOrKeyword = WebInspector.cssStyleManager.propertyNameHasOtherVendorPrefix(this.name) || WebInspector.cssStyleManager.propertyValueHasOtherVendorKeyword(this.value); 393 394 return this._hasOtherVendorNameOrKeyword; 395 }, 396 397 add: function() 398 { 399 // We can only add if the index is NaN. Return early otherwise. 400 if (!this._ownerStyle || !this._ownerStyle.editable || !isNaN(this._index)) 401 return; 402 403 this._cancelPendingUpdate(); 404 this._ownerStyle.addProperty(this); 405 }, 406 407 remove: function() 408 { 409 // We can only remove if the index is not NaN. Return early otherwise. 410 if (!this._ownerStyle || !this._ownerStyle.editable || isNaN(this._index)) 411 return; 412 413 this._cancelPendingUpdate(); 414 this._ownerStyle.removeProperty(this); 415 }, 416 417 // Private 418 419 _updatePropertySoon: function() 420 { 421 if (!this._ownerStyle || isNaN(this._index) || this._updatePropertySoonTimeout) 422 return; 423 424 function performUpdate() 425 { 426 delete this._updatePropertySoonTimeout; 427 428 if (!this._ownerStyle || isNaN(this._index)) 429 return; 430 431 var name = "_pendingName" in this ? this._pendingName : this._name; 432 var value = "_pendingValue" in this ? this._pendingValue : this._value; 433 var priority = "_pendingPriority" in this ? this._pendingPriority : this._priority; 434 435 delete this._pendingName; 436 delete this._pendingValue; 437 delete this._pendingPriority; 438 439 this._ownerStyle.nodeStyles.changeProperty(this, name, value, priority); 440 } 441 442 this._updatePropertySoonTimeout = setTimeout(performUpdate.bind(this), 0); 443 }, 444 445 _cancelPendingUpdate: function() 446 { 447 if (!this._updatePropertySoonTimeout) 448 return; 449 clearTimeout(this._updatePropertySoonTimeout); 450 delete this._updatePropertySoonTimeout; 451 } 452}; 453 454WebInspector.CSSProperty.prototype.__proto__ = WebInspector.Object.prototype; 455