| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2008 Apple Inc. All Rights Reserved. | 2 * Copyright (C) 2008 Apple Inc. All Rights Reserved. |
| 3 * Copyright (C) 2009 Joseph Pecoraro |
| 3 * | 4 * |
| 4 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions | 6 * modification, are permitted provided that the following conditions |
| 6 * are met: | 7 * are met: |
| 7 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
| 8 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 9 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
| 10 * notice, this list of conditions and the following disclaimer in the | 11 * notice, this list of conditions and the following disclaimer in the |
| 11 * documentation and/or other materials provided with the distribution. | 12 * documentation and/or other materials provided with the distribution. |
| 12 * | 13 * |
| 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | 14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 24 */ | 25 */ |
| 25 | 26 |
| 26 WebInspector.ObjectPropertiesSection = function(object, title, subtitle, emptyPl
aceholder, ignoreHasOwnProperty, extraProperties, treeElementConstructor) | 27 WebInspector.ObjectPropertiesSection = function(object, title, subtitle, emptyPl
aceholder, ignoreHasOwnProperty, extraProperties, treeElementConstructor) |
| 27 { | 28 { |
| 28 if (!title) { | |
| 29 title = Object.describe(object); | |
| 30 if (title.match(/Prototype$/)) { | |
| 31 title = title.replace(/Prototype$/, ""); | |
| 32 if (!subtitle) | |
| 33 subtitle = WebInspector.UIString("Prototype"); | |
| 34 } | |
| 35 } | |
| 36 | |
| 37 this.emptyPlaceholder = (emptyPlaceholder || WebInspector.UIString("No Prope
rties")); | 29 this.emptyPlaceholder = (emptyPlaceholder || WebInspector.UIString("No Prope
rties")); |
| 38 this.object = object; | 30 this.object = object; |
| 39 this.ignoreHasOwnProperty = ignoreHasOwnProperty; | 31 this.ignoreHasOwnProperty = ignoreHasOwnProperty; |
| 40 this.extraProperties = extraProperties; | 32 this.extraProperties = extraProperties; |
| 41 this.treeElementConstructor = treeElementConstructor || WebInspector.ObjectP
ropertyTreeElement; | 33 this.treeElementConstructor = treeElementConstructor || WebInspector.ObjectP
ropertyTreeElement; |
| 42 this.editable = true; | 34 this.editable = true; |
| 43 | 35 |
| 44 WebInspector.PropertiesSection.call(this, title, subtitle); | 36 WebInspector.PropertiesSection.call(this, title, subtitle); |
| 45 } | 37 } |
| 46 | 38 |
| 47 WebInspector.ObjectPropertiesSection.prototype = { | 39 WebInspector.ObjectPropertiesSection.prototype = { |
| 48 onpopulate: function() | 40 onpopulate: function() |
| 49 { | 41 { |
| 50 this.update(); | 42 this.update(); |
| 51 }, | 43 }, |
| 52 | 44 |
| 53 update: function() | 45 update: function() |
| 54 { | 46 { |
| 55 var properties = []; | 47 var self = this; |
| 56 for (var prop in this.object) | 48 var callback = function(properties) { |
| 57 properties.push(prop); | 49 if (!properties) |
| 50 return; |
| 51 self._update(properties); |
| 52 }; |
| 53 InspectorController.getProperties(this.object, this.ignoreHasOwnProperty
, callback); |
| 54 }, |
| 55 |
| 56 _update: function(properties) |
| 57 { |
| 58 if (this.extraProperties) | 58 if (this.extraProperties) |
| 59 for (var prop in this.extraProperties) | 59 for (var i = 0; i < this.extraProperties.length; ++i) |
| 60 properties.push(prop); | 60 properties.push(this.extraProperties[i]); |
| 61 properties.sort(); | 61 properties.sort(this._displaySort); |
| 62 | 62 |
| 63 this.propertiesTreeOutline.removeChildren(); | 63 this.propertiesTreeOutline.removeChildren(); |
| 64 | 64 |
| 65 for (var i = 0; i < properties.length; ++i) { | 65 for (var i = 0; i < properties.length; ++i) |
| 66 var object = this.object; | 66 this.propertiesTreeOutline.appendChild(new this.treeElementConstruct
or(properties[i])); |
| 67 var propertyName = properties[i]; | |
| 68 if (this.extraProperties && propertyName in this.extraProperties) | |
| 69 object = this.extraProperties; | |
| 70 if (propertyName === "__treeElementIdentifier") | |
| 71 continue; | |
| 72 if (!this.ignoreHasOwnProperty && "hasOwnProperty" in object && !obj
ect.hasOwnProperty(propertyName)) | |
| 73 continue; | |
| 74 this.propertiesTreeOutline.appendChild(new this.treeElementConstruct
or(object, propertyName)); | |
| 75 } | |
| 76 | 67 |
| 77 if (!this.propertiesTreeOutline.children.length) { | 68 if (!this.propertiesTreeOutline.children.length) { |
| 78 var title = "<div class=\"info\">" + this.emptyPlaceholder + "</div>
"; | 69 var title = "<div class=\"info\">" + this.emptyPlaceholder + "</div>
"; |
| 79 var infoElement = new TreeElement(title, null, false); | 70 var infoElement = new TreeElement(title, null, false); |
| 80 this.propertiesTreeOutline.appendChild(infoElement); | 71 this.propertiesTreeOutline.appendChild(infoElement); |
| 81 } | 72 } |
| 73 }, |
| 74 |
| 75 _displaySort: function(propertyA, propertyB) { |
| 76 var a = propertyA.name; |
| 77 var b = propertyB.name; |
| 78 |
| 79 // if used elsewhere make sure to |
| 80 // - convert a and b to strings (not needed here, properties are all st
rings) |
| 81 // - check if a == b (not needed here, no two properties can be the sam
e) |
| 82 |
| 83 var diff = 0; |
| 84 var chunk = /^\d+|^\D+/; |
| 85 var chunka, chunkb, anum, bnum; |
| 86 while (diff === 0) { |
| 87 if (!a && b) |
| 88 return -1; |
| 89 if (!b && a) |
| 90 return 1; |
| 91 chunka = a.match(chunk)[0]; |
| 92 chunkb = b.match(chunk)[0]; |
| 93 anum = !isNaN(chunka); |
| 94 bnum = !isNaN(chunkb); |
| 95 if (anum && !bnum) |
| 96 return -1; |
| 97 if (bnum && !anum) |
| 98 return 1; |
| 99 if (anum && bnum) { |
| 100 diff = chunka - chunkb; |
| 101 if (diff === 0 && chunka.length !== chunkb.length) { |
| 102 if (!+chunka && !+chunkb) // chunks are strings of all 0s (s
pecial case) |
| 103 return chunka.length - chunkb.length; |
| 104 else |
| 105 return chunkb.length - chunka.length; |
| 106 } |
| 107 } else if (chunka !== chunkb) |
| 108 return (chunka < chunkb) ? -1 : 1; |
| 109 a = a.substring(chunka.length); |
| 110 b = b.substring(chunkb.length); |
| 111 } |
| 112 return diff; |
| 82 } | 113 } |
| 83 } | 114 } |
| 84 | 115 |
| 85 WebInspector.ObjectPropertiesSection.prototype.__proto__ = WebInspector.Properti
esSection.prototype; | 116 WebInspector.ObjectPropertiesSection.prototype.__proto__ = WebInspector.Properti
esSection.prototype; |
| 86 | 117 |
| 87 WebInspector.ObjectPropertyTreeElement = function(parentObject, propertyName) | 118 WebInspector.ObjectPropertyTreeElement = function(property) |
| 88 { | 119 { |
| 89 this.parentObject = parentObject; | 120 this.property = property; |
| 90 this.propertyName = propertyName; | |
| 91 | 121 |
| 92 // Pass an empty title, the title gets made later in onattach. | 122 // Pass an empty title, the title gets made later in onattach. |
| 93 TreeElement.call(this, "", null, false); | 123 TreeElement.call(this, "", null, false); |
| 94 } | 124 } |
| 95 | 125 |
| 96 WebInspector.ObjectPropertyTreeElement.prototype = { | 126 WebInspector.ObjectPropertyTreeElement.prototype = { |
| 97 safePropertyValue: function(object, propertyName) | |
| 98 { | |
| 99 if (object["__lookupGetter__"] && object.__lookupGetter__(propertyName)) | |
| 100 return; | |
| 101 return object[propertyName]; | |
| 102 }, | |
| 103 | |
| 104 onpopulate: function() | 127 onpopulate: function() |
| 105 { | 128 { |
| 106 if (this.children.length && !this.shouldRefreshChildren) | 129 if (this.children.length && !this.shouldRefreshChildren) |
| 107 return; | 130 return; |
| 108 | 131 |
| 109 this.removeChildren(); | 132 var self = this; |
| 133 var callback = function(properties) { |
| 134 self.removeChildren(); |
| 135 if (!properties) |
| 136 return; |
| 110 | 137 |
| 111 var childObject = this.safePropertyValue(this.parentObject, this.propert
yName); | 138 properties.sort(self._displaySort); |
| 112 var properties = Object.sortedProperties(childObject); | 139 for (var i = 0; i < properties.length; ++i) { |
| 113 for (var i = 0; i < properties.length; ++i) { | 140 self.appendChild(new self.treeOutline.section.treeElementConstru
ctor(properties[i])); |
| 114 var propertyName = properties[i]; | 141 } |
| 115 if (propertyName === "__treeElementIdentifier") | 142 }; |
| 116 continue; | 143 InspectorController.getProperties(this.property.value, false, callback); |
| 117 this.appendChild(new this.treeOutline.section.treeElementConstructor
(childObject, propertyName)); | |
| 118 } | |
| 119 }, | 144 }, |
| 120 | 145 |
| 121 ondblclick: function(element, event) | 146 ondblclick: function(element, event) |
| 122 { | 147 { |
| 123 this.startEditing(); | 148 this.startEditing(); |
| 124 }, | 149 }, |
| 125 | 150 |
| 126 onattach: function() | 151 onattach: function() |
| 127 { | 152 { |
| 128 this.update(); | 153 this.update(); |
| 129 }, | 154 }, |
| 130 | 155 |
| 131 update: function() | 156 update: function() |
| 132 { | 157 { |
| 133 var childObject = this.safePropertyValue(this.parentObject, this.propert
yName); | |
| 134 var isGetter = ("__lookupGetter__" in this.parentObject && this.parentOb
ject.__lookupGetter__(this.propertyName)); | |
| 135 | |
| 136 var nameElement = document.createElement("span"); | 158 var nameElement = document.createElement("span"); |
| 137 nameElement.className = "name"; | 159 nameElement.className = "name"; |
| 138 nameElement.textContent = this.propertyName; | 160 nameElement.textContent = this.property.name; |
| 139 | 161 |
| 140 this.valueElement = document.createElement("span"); | 162 this.valueElement = document.createElement("span"); |
| 141 this.valueElement.className = "value"; | 163 this.valueElement.className = "value"; |
| 142 if (!isGetter) { | 164 this.valueElement.textContent = this.property.value.description; |
| 143 this.valueElement.textContent = Object.describe(childObject, true); | 165 if (this.property.isGetter) |
| 144 } else { | 166 this.valueElement.addStyleClass("dimmed"); |
| 145 // FIXME: this should show something like "getter" (bug 16734). | |
| 146 this.valueElement.textContent = "\u2014"; // em dash | |
| 147 this.valueElement.addStyleClass("dimmed"); | |
| 148 } | |
| 149 | 167 |
| 150 this.listItemElement.removeChildren(); | 168 this.listItemElement.removeChildren(); |
| 151 | 169 |
| 152 this.listItemElement.appendChild(nameElement); | 170 this.listItemElement.appendChild(nameElement); |
| 153 this.listItemElement.appendChild(document.createTextNode(": ")); | 171 this.listItemElement.appendChild(document.createTextNode(": ")); |
| 154 this.listItemElement.appendChild(this.valueElement); | 172 this.listItemElement.appendChild(this.valueElement); |
| 155 | 173 this.hasChildren = this.property.value.hasChildren; |
| 156 var hasSubProperties = false; | |
| 157 var type = typeof childObject; | |
| 158 if (childObject && (type === "object" || type === "function")) { | |
| 159 for (subPropertyName in childObject) { | |
| 160 if (subPropertyName === "__treeElementIdentifier") | |
| 161 continue; | |
| 162 hasSubProperties = true; | |
| 163 break; | |
| 164 } | |
| 165 } | |
| 166 | |
| 167 this.hasChildren = hasSubProperties; | |
| 168 }, | 174 }, |
| 169 | 175 |
| 170 updateSiblings: function() | 176 updateSiblings: function() |
| 171 { | 177 { |
| 172 if (this.parent.root) | 178 if (this.parent.root) |
| 173 this.treeOutline.section.update(); | 179 this.treeOutline.section.update(); |
| 174 else | 180 else |
| 175 this.parent.shouldRefreshChildren = true; | 181 this.parent.shouldRefreshChildren = true; |
| 176 }, | 182 }, |
| 177 | 183 |
| (...skipping 29 matching lines...) Expand all Loading... |
| 207 editingCommitted: function(element, userInput, previousContent, context) | 213 editingCommitted: function(element, userInput, previousContent, context) |
| 208 { | 214 { |
| 209 if (userInput === previousContent) | 215 if (userInput === previousContent) |
| 210 return this.editingCancelled(element, context); // nothing changed,
so cancel | 216 return this.editingCancelled(element, context); // nothing changed,
so cancel |
| 211 | 217 |
| 212 this.applyExpression(userInput, true); | 218 this.applyExpression(userInput, true); |
| 213 | 219 |
| 214 this.editingEnded(context); | 220 this.editingEnded(context); |
| 215 }, | 221 }, |
| 216 | 222 |
| 217 evaluateExpression: function(expression) | |
| 218 { | |
| 219 // Evaluate in the currently selected call frame if the debugger is paus
ed. | |
| 220 // Otherwise evaluate in against the inspected window. | |
| 221 if (WebInspector.panels.scripts && WebInspector.panels.scripts.paused &&
this.treeOutline.section.editInSelectedCallFrameWhenPaused) | |
| 222 return WebInspector.panels.scripts.evaluateInSelectedCallFrame(expre
ssion, false); | |
| 223 return InspectorController.inspectedWindow().eval(expression); | |
| 224 }, | |
| 225 | |
| 226 applyExpression: function(expression, updateInterface) | 223 applyExpression: function(expression, updateInterface) |
| 227 { | 224 { |
| 228 var expressionLength = expression.trimWhitespace().length; | 225 expression = expression.trimWhitespace(); |
| 226 var expressionLength = expression.length; |
| 227 var self = this; |
| 228 var callback = function(success) { |
| 229 if (!updateInterface) |
| 230 return; |
| 229 | 231 |
| 230 if (!expressionLength) { | 232 if (!success) |
| 231 // The user deleted everything, so try to delete the property. | 233 self.update(); |
| 232 delete this.parentObject[this.propertyName]; | |
| 233 | 234 |
| 234 if (updateInterface) { | 235 if (!expressionLength) { |
| 235 if (this.propertyName in this.parentObject) { | 236 // The property was deleted, so remove this tree element. |
| 236 // The property was not deleted, so update. | 237 self.parent.removeChild(this); |
| 237 this.update(); | 238 } else { |
| 238 } else { | 239 // Call updateSiblings since their value might be based on the v
alue that just changed. |
| 239 // The property was deleted, so remove this tree element. | 240 self.updateSiblings(); |
| 240 this.parent.removeChild(this); | |
| 241 } | |
| 242 } | 241 } |
| 243 | 242 }; |
| 244 return; | 243 InspectorController.setPropertyValue(this.property.parentObjectProxy, th
is.property.name, expression.trimWhitespace(), callback); |
| 245 } | |
| 246 | |
| 247 try { | |
| 248 // Surround the expression in parenthesis so the result of the eval
is the result | |
| 249 // of the whole expression not the last potential sub-expression. | |
| 250 var result = this.evaluateExpression("(" + expression + ")"); | |
| 251 | |
| 252 // Store the result in the property. | |
| 253 this.parentObject[this.propertyName] = result; | |
| 254 } catch(e) { | |
| 255 try { | |
| 256 // Try to update as a string | |
| 257 var result = this.evaluateExpression("\"" + expression.escapeCha
racters("\"") + "\""); | |
| 258 | |
| 259 // Store the result in the property. | |
| 260 this.parentObject[this.propertyName] = result; | |
| 261 } catch(e) { | |
| 262 // The expression failed so don't change the value. So just upda
te and return. | |
| 263 if (updateInterface) | |
| 264 this.update(); | |
| 265 return; | |
| 266 } | |
| 267 } | |
| 268 | |
| 269 if (updateInterface) { | |
| 270 // Call updateSiblings since their value might be based on the value
that just changed. | |
| 271 this.updateSiblings(); | |
| 272 } | |
| 273 } | 244 } |
| 274 } | 245 } |
| 275 | 246 |
| 276 WebInspector.ObjectPropertyTreeElement.prototype.__proto__ = TreeElement.prototy
pe; | 247 WebInspector.ObjectPropertyTreeElement.prototype.__proto__ = TreeElement.prototy
pe; |
| OLD | NEW |