tizen beta release
[framework/web/webkit-efl.git] / debian / libwebkit-engine / usr / share / ewebkit-0 / webinspector / ObjectPropertiesSection.js
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 /**
28  * @constructor
29  * @extends {WebInspector.PropertiesSection}
30  * @param {*=} object
31  * @param {string=} title
32  * @param {string=} subtitle
33  * @param {string=} emptyPlaceholder
34  * @param {boolean=} ignoreHasOwnProperty
35  * @param {Array.<WebInspector.RemoteObjectProperty>=} extraProperties
36  * @param {function()=} treeElementConstructor
37  */
38 WebInspector.ObjectPropertiesSection = function(object, title, subtitle, emptyPlaceholder, ignoreHasOwnProperty, extraProperties, treeElementConstructor)
39 {
40     this.emptyPlaceholder = (emptyPlaceholder || WebInspector.UIString("No Properties"));
41     this.object = object;
42     this.ignoreHasOwnProperty = ignoreHasOwnProperty;
43     this.extraProperties = extraProperties;
44     this.treeElementConstructor = treeElementConstructor || WebInspector.ObjectPropertyTreeElement;
45     this.editable = true;
46
47     WebInspector.PropertiesSection.call(this, title, subtitle);
48 }
49
50 WebInspector.ObjectPropertiesSection.prototype = {
51     onpopulate: function()
52     {
53         this.update();
54     },
55
56     update: function()
57     {
58         var self = this;
59         function callback(properties)
60         {
61             if (!properties)
62                 return;
63             self.updateProperties(properties);
64         }
65         if (this.ignoreHasOwnProperty)
66             this.object.getAllProperties(callback);
67         else
68             this.object.getOwnProperties(callback);
69     },
70
71     updateProperties: function(properties, rootTreeElementConstructor, rootPropertyComparer)
72     {
73         if (!rootTreeElementConstructor)
74             rootTreeElementConstructor = this.treeElementConstructor;
75
76         if (!rootPropertyComparer)
77             rootPropertyComparer = WebInspector.ObjectPropertiesSection.CompareProperties;
78
79         if (this.extraProperties)
80             for (var i = 0; i < this.extraProperties.length; ++i)
81                 properties.push(this.extraProperties[i]);
82
83         properties.sort(rootPropertyComparer);
84
85         this.propertiesTreeOutline.removeChildren();
86
87         for (var i = 0; i < properties.length; ++i) {
88             properties[i].parentObject = this.object;
89             this.propertiesTreeOutline.appendChild(new rootTreeElementConstructor(properties[i]));
90         }
91
92         if (!this.propertiesTreeOutline.children.length) {
93             var title = document.createElement("div");
94             title.className = "info";
95             title.textContent = this.emptyPlaceholder;
96             var infoElement = new TreeElement(title, null, false);
97             this.propertiesTreeOutline.appendChild(infoElement);
98         }
99         this.propertiesForTest = properties;
100     }
101 }
102
103 WebInspector.ObjectPropertiesSection.prototype.__proto__ = WebInspector.PropertiesSection.prototype;
104
105 WebInspector.ObjectPropertiesSection.CompareProperties = function(propertyA, propertyB)
106 {
107     var a = propertyA.name;
108     var b = propertyB.name;
109     if (a === "__proto__")
110         return 1;
111     if (b === "__proto__")
112         return -1;
113
114     // if used elsewhere make sure to
115     //  - convert a and b to strings (not needed here, properties are all strings)
116     //  - check if a == b (not needed here, no two properties can be the same)
117
118     var diff = 0;
119     var chunk = /^\d+|^\D+/;
120     var chunka, chunkb, anum, bnum;
121     while (diff === 0) {
122         if (!a && b)
123             return -1;
124         if (!b && a)
125             return 1;
126         chunka = a.match(chunk)[0];
127         chunkb = b.match(chunk)[0];
128         anum = !isNaN(chunka);
129         bnum = !isNaN(chunkb);
130         if (anum && !bnum)
131             return -1;
132         if (bnum && !anum)
133             return 1;
134         if (anum && bnum) {
135             diff = chunka - chunkb;
136             if (diff === 0 && chunka.length !== chunkb.length) {
137                 if (!+chunka && !+chunkb) // chunks are strings of all 0s (special case)
138                     return chunka.length - chunkb.length;
139                 else
140                     return chunkb.length - chunka.length;
141             }
142         } else if (chunka !== chunkb)
143             return (chunka < chunkb) ? -1 : 1;
144         a = a.substring(chunka.length);
145         b = b.substring(chunkb.length);
146     }
147     return diff;
148 }
149
150 /**
151  * @constructor
152  * @extends {TreeElement}
153  */
154 WebInspector.ObjectPropertyTreeElement = function(property)
155 {
156     this.property = property;
157
158     // Pass an empty title, the title gets made later in onattach.
159     TreeElement.call(this, "", null, false);
160     this.toggleOnClick = true;
161     this.selectable = false;
162 }
163
164 WebInspector.ObjectPropertyTreeElement.prototype = {
165     onpopulate: function()
166     {
167         if (this.children.length && !this.shouldRefreshChildren)
168             return;
169
170         var callback = function(properties) {
171             this.removeChildren();
172             if (!properties)
173                 return;
174
175             properties.sort(WebInspector.ObjectPropertiesSection.CompareProperties);
176             for (var i = 0; i < properties.length; ++i) {
177                 this.appendChild(new this.treeOutline.section.treeElementConstructor(properties[i]));
178             }
179         };
180         this.property.value.getOwnProperties(callback.bind(this));
181     },
182
183     ondblclick: function(event)
184     {
185         if (this.property.writable)
186             this.startEditing();
187     },
188
189     onattach: function()
190     {
191         this.update();
192     },
193
194     update: function()
195     {
196         this.nameElement = document.createElement("span");
197         this.nameElement.className = "name";
198         this.nameElement.textContent = this.property.name;
199         if (!this.property.enumerable)
200             this.nameElement.addStyleClass("dimmed");
201
202         var separatorElement = document.createElement("span");
203         separatorElement.className = "separator";
204         separatorElement.textContent = ": ";
205
206         this.valueElement = document.createElement("span");
207         this.valueElement.className = "value";
208
209         var description = this.property.value.description;
210         // Render \n as a nice unicode cr symbol.
211         if (this.property.wasThrown)
212             this.valueElement.textContent = "[Exception: " + description + "]";
213         else if (this.property.value.type === "string" && typeof description === "string") {
214             this.valueElement.textContent = "\"" + description.replace(/\n/g, "\u21B5") + "\"";
215             this.valueElement._originalTextContent = "\"" + description + "\"";
216         } else if (this.property.value.type === "function" && typeof description === "string") {
217             this.valueElement.textContent = /.*/.exec(description)[0].replace(/ +$/g, "");
218             this.valueElement._originalTextContent = description;
219         } else
220             this.valueElement.textContent = description;
221
222         if (this.property.value.type === "function")
223             this.valueElement.addEventListener("contextmenu", this._functionContextMenuEventFired.bind(this), false);
224
225         if (this.property.wasThrown)
226             this.valueElement.addStyleClass("error");
227         if (this.property.value.subtype)
228             this.valueElement.addStyleClass("console-formatted-" + this.property.value.subtype);
229         else if (this.property.value.type)
230             this.valueElement.addStyleClass("console-formatted-" + this.property.value.type);
231         if (this.property.value.subtype === "node")
232             this.valueElement.addEventListener("contextmenu", this._contextMenuEventFired.bind(this), false);
233
234         this.listItemElement.removeChildren();
235
236         this.listItemElement.appendChild(this.nameElement);
237         this.listItemElement.appendChild(separatorElement);
238         this.listItemElement.appendChild(this.valueElement);
239         this.hasChildren = this.property.value.hasChildren && !this.property.wasThrown;
240     },
241
242     _contextMenuEventFired: function(event)
243     {
244         function selectNode(nodeId)
245         {
246             if (nodeId)
247                 WebInspector.domAgent.inspectElement(nodeId);
248         }
249
250         function revealElement()
251         {
252             this.property.value.pushNodeToFrontend(selectNode);
253         }
254
255         var contextMenu = new WebInspector.ContextMenu();
256         contextMenu.appendItem(WebInspector.UIString("Reveal in Elements Panel"), revealElement.bind(this));
257         contextMenu.show(event);
258     },
259
260     _functionContextMenuEventFired: function(event)
261     {
262         function didGetLocation(error, response)
263         {
264             if (error) {
265                 console.error(error);
266                 return;
267             }
268             WebInspector.panels.scripts.showFunctionDefinition(response);
269         }
270
271         function revealFunction()
272         {
273             DebuggerAgent.getFunctionLocation(this.property.value.objectId, didGetLocation.bind(this));
274         }
275
276         var contextMenu = new WebInspector.ContextMenu();
277         contextMenu.appendItem(WebInspector.UIString("Show function definition"), revealFunction.bind(this));
278         contextMenu.show(event);
279     },
280
281     updateSiblings: function()
282     {
283         if (this.parent.root)
284             this.treeOutline.section.update();
285         else
286             this.parent.shouldRefreshChildren = true;
287     },
288
289     startEditing: function()
290     {
291         if (WebInspector.isBeingEdited(this.valueElement) || !this.treeOutline.section.editable)
292             return;
293
294         var context = { expanded: this.expanded };
295
296         // Lie about our children to prevent expanding on double click and to collapse subproperties.
297         this.hasChildren = false;
298
299         this.listItemElement.addStyleClass("editing-sub-part");
300
301         // Edit original source.
302         if (typeof this.valueElement._originalTextContent === "string")
303             this.valueElement.textContent = this.valueElement._originalTextContent;
304
305         var config = new WebInspector.EditingConfig(this.editingCommitted.bind(this), this.editingCancelled.bind(this), context);
306         WebInspector.startEditing(this.valueElement, config);
307     },
308
309     editingEnded: function(context)
310     {
311         this.listItemElement.scrollLeft = 0;
312         this.listItemElement.removeStyleClass("editing-sub-part");
313         if (context.expanded)
314             this.expand();
315     },
316
317     editingCancelled: function(element, context)
318     {
319         this.update();
320         this.editingEnded(context);
321     },
322
323     editingCommitted: function(element, userInput, previousContent, context)
324     {
325         if (userInput === previousContent)
326             return this.editingCancelled(element, context); // nothing changed, so cancel
327
328         this.applyExpression(userInput, true);
329
330         this.editingEnded(context);
331     },
332
333     applyExpression: function(expression, updateInterface)
334     {
335         expression = expression.trim();
336         var expressionLength = expression.length;
337         function callback(error)
338         {
339             if (!updateInterface)
340                 return;
341
342             if (error)
343                 this.update();
344
345             if (!expressionLength) {
346                 // The property was deleted, so remove this tree element.
347                 this.parent.removeChild(this);
348             } else {
349                 // Call updateSiblings since their value might be based on the value that just changed.
350                 this.updateSiblings();
351             }
352         };
353         this.property.parentObject.setPropertyValue(this.property.name, expression.trim(), callback.bind(this));
354     }
355 }
356
357 WebInspector.ObjectPropertyTreeElement.prototype.__proto__ = TreeElement.prototype;