OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * Copyright (C) 2008 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 * 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 * |
| 14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 25 */ |
| 26 |
| 27 WebInspector.ObjectPropertiesSection = function(object, title, subtitle, emptyPl
aceholder, ignoreHasOwnProperty, extraProperties, treeElementConstructor) |
| 28 { |
| 29 this.emptyPlaceholder = (emptyPlaceholder || WebInspector.UIString("No Prope
rties")); |
| 30 this.object = object; |
| 31 this.ignoreHasOwnProperty = ignoreHasOwnProperty; |
| 32 this.extraProperties = extraProperties; |
| 33 this.treeElementConstructor = treeElementConstructor || WebInspector.ObjectP
ropertyTreeElement; |
| 34 this.editable = true; |
| 35 |
| 36 WebInspector.PropertiesSection.call(this, title, subtitle); |
| 37 } |
| 38 |
| 39 WebInspector.ObjectPropertiesSection.prototype = { |
| 40 onpopulate: function() |
| 41 { |
| 42 this.update(); |
| 43 }, |
| 44 |
| 45 update: function() |
| 46 { |
| 47 var self = this; |
| 48 var callback = function(properties) { |
| 49 if (!properties) |
| 50 return; |
| 51 self.updateProperties(properties); |
| 52 }; |
| 53 InjectedScriptAccess.getProperties(this.object, this.ignoreHasOwnPropert
y, callback); |
| 54 }, |
| 55 |
| 56 updateProperties: function(properties, rootTreeElementConstructor, rootPrope
rtyComparer) |
| 57 { |
| 58 if (!rootTreeElementConstructor) |
| 59 rootTreeElementConstructor = this.treeElementConstructor; |
| 60 |
| 61 if (!rootPropertyComparer) |
| 62 rootPropertyComparer = WebInspector.ObjectPropertiesSection.CompareP
roperties; |
| 63 |
| 64 if (this.extraProperties) |
| 65 for (var i = 0; i < this.extraProperties.length; ++i) |
| 66 properties.push(this.extraProperties[i]); |
| 67 |
| 68 properties.sort(rootPropertyComparer); |
| 69 |
| 70 this.propertiesTreeOutline.removeChildren(); |
| 71 |
| 72 for (var i = 0; i < properties.length; ++i) |
| 73 this.propertiesTreeOutline.appendChild(new rootTreeElementConstructo
r(properties[i])); |
| 74 |
| 75 if (!this.propertiesTreeOutline.children.length) { |
| 76 var title = "<div class=\"info\">" + this.emptyPlaceholder + "</div>
"; |
| 77 var infoElement = new TreeElement(title, null, false); |
| 78 this.propertiesTreeOutline.appendChild(infoElement); |
| 79 } |
| 80 } |
| 81 } |
| 82 |
| 83 WebInspector.ObjectPropertiesSection.prototype.__proto__ = WebInspector.Properti
esSection.prototype; |
| 84 |
| 85 WebInspector.ObjectPropertiesSection.CompareProperties = function(propertyA, pro
pertyB) |
| 86 { |
| 87 var a = propertyA.name; |
| 88 var b = propertyB.name; |
| 89 |
| 90 // if used elsewhere make sure to |
| 91 // - convert a and b to strings (not needed here, properties are all string
s) |
| 92 // - check if a == b (not needed here, no two properties can be the same) |
| 93 |
| 94 var diff = 0; |
| 95 var chunk = /^\d+|^\D+/; |
| 96 var chunka, chunkb, anum, bnum; |
| 97 while (diff === 0) { |
| 98 if (!a && b) |
| 99 return -1; |
| 100 if (!b && a) |
| 101 return 1; |
| 102 chunka = a.match(chunk)[0]; |
| 103 chunkb = b.match(chunk)[0]; |
| 104 anum = !isNaN(chunka); |
| 105 bnum = !isNaN(chunkb); |
| 106 if (anum && !bnum) |
| 107 return -1; |
| 108 if (bnum && !anum) |
| 109 return 1; |
| 110 if (anum && bnum) { |
| 111 diff = chunka - chunkb; |
| 112 if (diff === 0 && chunka.length !== chunkb.length) { |
| 113 if (!+chunka && !+chunkb) // chunks are strings of all 0s (speci
al case) |
| 114 return chunka.length - chunkb.length; |
| 115 else |
| 116 return chunkb.length - chunka.length; |
| 117 } |
| 118 } else if (chunka !== chunkb) |
| 119 return (chunka < chunkb) ? -1 : 1; |
| 120 a = a.substring(chunka.length); |
| 121 b = b.substring(chunkb.length); |
| 122 } |
| 123 return diff; |
| 124 } |
| 125 |
| 126 WebInspector.ObjectPropertyTreeElement = function(property) |
| 127 { |
| 128 this.property = property; |
| 129 |
| 130 // Pass an empty title, the title gets made later in onattach. |
| 131 TreeElement.call(this, "", null, false); |
| 132 } |
| 133 |
| 134 WebInspector.ObjectPropertyTreeElement.prototype = { |
| 135 onpopulate: function() |
| 136 { |
| 137 if (this.children.length && !this.shouldRefreshChildren) |
| 138 return; |
| 139 |
| 140 var callback = function(properties) { |
| 141 this.removeChildren(); |
| 142 if (!properties) |
| 143 return; |
| 144 |
| 145 properties.sort(WebInspector.ObjectPropertiesSection.CompareProperti
es); |
| 146 for (var i = 0; i < properties.length; ++i) { |
| 147 this.appendChild(new this.treeOutline.section.treeElementConstru
ctor(properties[i])); |
| 148 } |
| 149 }; |
| 150 InjectedScriptAccess.getProperties(this.property.value, false, callback.
bind(this)); |
| 151 }, |
| 152 |
| 153 ondblclick: function(element, event) |
| 154 { |
| 155 this.startEditing(); |
| 156 }, |
| 157 |
| 158 onattach: function() |
| 159 { |
| 160 this.update(); |
| 161 }, |
| 162 |
| 163 update: function() |
| 164 { |
| 165 this.nameElement = document.createElement("span"); |
| 166 this.nameElement.className = "name"; |
| 167 this.nameElement.textContent = this.property.name; |
| 168 |
| 169 var separatorElement = document.createElement("span"); |
| 170 separatorElement.className = "separator"; |
| 171 separatorElement.textContent = ": "; |
| 172 |
| 173 this.valueElement = document.createElement("span"); |
| 174 this.valueElement.className = "value"; |
| 175 this.valueElement.textContent = this.property.value.description; |
| 176 if (this.property.isGetter) |
| 177 this.valueElement.addStyleClass("dimmed"); |
| 178 |
| 179 this.listItemElement.removeChildren(); |
| 180 |
| 181 this.listItemElement.appendChild(this.nameElement); |
| 182 this.listItemElement.appendChild(separatorElement); |
| 183 this.listItemElement.appendChild(this.valueElement); |
| 184 this.hasChildren = this.property.value.hasChildren; |
| 185 }, |
| 186 |
| 187 updateSiblings: function() |
| 188 { |
| 189 if (this.parent.root) |
| 190 this.treeOutline.section.update(); |
| 191 else |
| 192 this.parent.shouldRefreshChildren = true; |
| 193 }, |
| 194 |
| 195 startEditing: function() |
| 196 { |
| 197 if (WebInspector.isBeingEdited(this.valueElement) || !this.treeOutline.s
ection.editable) |
| 198 return; |
| 199 |
| 200 var context = { expanded: this.expanded }; |
| 201 |
| 202 // Lie about our children to prevent expanding on double click and to co
llapse subproperties. |
| 203 this.hasChildren = false; |
| 204 |
| 205 this.listItemElement.addStyleClass("editing-sub-part"); |
| 206 |
| 207 WebInspector.startEditing(this.valueElement, this.editingCommitted.bind(
this), this.editingCancelled.bind(this), context); |
| 208 }, |
| 209 |
| 210 editingEnded: function(context) |
| 211 { |
| 212 this.listItemElement.scrollLeft = 0; |
| 213 this.listItemElement.removeStyleClass("editing-sub-part"); |
| 214 if (context.expanded) |
| 215 this.expand(); |
| 216 }, |
| 217 |
| 218 editingCancelled: function(element, context) |
| 219 { |
| 220 this.update(); |
| 221 this.editingEnded(context); |
| 222 }, |
| 223 |
| 224 editingCommitted: function(element, userInput, previousContent, context) |
| 225 { |
| 226 if (userInput === previousContent) |
| 227 return this.editingCancelled(element, context); // nothing changed,
so cancel |
| 228 |
| 229 this.applyExpression(userInput, true); |
| 230 |
| 231 this.editingEnded(context); |
| 232 }, |
| 233 |
| 234 applyExpression: function(expression, updateInterface) |
| 235 { |
| 236 expression = expression.trimWhitespace(); |
| 237 var expressionLength = expression.length; |
| 238 var self = this; |
| 239 var callback = function(success) { |
| 240 if (!updateInterface) |
| 241 return; |
| 242 |
| 243 if (!success) |
| 244 self.update(); |
| 245 |
| 246 if (!expressionLength) { |
| 247 // The property was deleted, so remove this tree element. |
| 248 self.parent.removeChild(this); |
| 249 } else { |
| 250 // Call updateSiblings since their value might be based on the v
alue that just changed. |
| 251 self.updateSiblings(); |
| 252 } |
| 253 }; |
| 254 InjectedScriptAccess.setPropertyValue(this.property.parentObjectProxy, t
his.property.name, expression.trimWhitespace(), callback); |
| 255 } |
| 256 } |
| 257 |
| 258 WebInspector.ObjectPropertyTreeElement.prototype.__proto__ = TreeElement.prototy
pe; |
OLD | NEW |