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 |