diff options
Diffstat (limited to 'Source/WebCore/inspector/front-end/ObjectPropertiesSection.js')
-rw-r--r-- | Source/WebCore/inspector/front-end/ObjectPropertiesSection.js | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/Source/WebCore/inspector/front-end/ObjectPropertiesSection.js b/Source/WebCore/inspector/front-end/ObjectPropertiesSection.js new file mode 100644 index 0000000..e4794f3 --- /dev/null +++ b/Source/WebCore/inspector/front-end/ObjectPropertiesSection.js @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2009 Joseph Pecoraro + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +WebInspector.ObjectPropertiesSection = function(object, title, subtitle, emptyPlaceholder, ignoreHasOwnProperty, extraProperties, treeElementConstructor) +{ + this.emptyPlaceholder = (emptyPlaceholder || WebInspector.UIString("No Properties")); + this.object = object; + this.ignoreHasOwnProperty = ignoreHasOwnProperty; + this.extraProperties = extraProperties; + this.treeElementConstructor = treeElementConstructor || WebInspector.ObjectPropertyTreeElement; + this.editable = true; + + WebInspector.PropertiesSection.call(this, title, subtitle); +} + +WebInspector.ObjectPropertiesSection.prototype = { + onpopulate: function() + { + this.update(); + }, + + update: function() + { + var self = this; + var callback = function(properties) { + if (!properties) + return; + self.updateProperties(properties); + }; + this.object.getProperties(this.ignoreHasOwnProperty, true, callback); + }, + + updateProperties: function(properties, rootTreeElementConstructor, rootPropertyComparer) + { + if (!rootTreeElementConstructor) + rootTreeElementConstructor = this.treeElementConstructor; + + if (!rootPropertyComparer) + rootPropertyComparer = WebInspector.ObjectPropertiesSection.CompareProperties; + + if (this.extraProperties) + for (var i = 0; i < this.extraProperties.length; ++i) + properties.push(this.extraProperties[i]); + + properties.sort(rootPropertyComparer); + + this.propertiesTreeOutline.removeChildren(); + + for (var i = 0; i < properties.length; ++i) { + properties[i].parentObject = this.object; + this.propertiesTreeOutline.appendChild(new rootTreeElementConstructor(properties[i])); + } + + if (!this.propertiesTreeOutline.children.length) { + var title = "<div class=\"info\">" + this.emptyPlaceholder + "</div>"; + var infoElement = new TreeElement(null, null, false); + infoElement.titleHTML = title; + this.propertiesTreeOutline.appendChild(infoElement); + } + this.propertiesForTest = properties; + } +} + +WebInspector.ObjectPropertiesSection.prototype.__proto__ = WebInspector.PropertiesSection.prototype; + +WebInspector.ObjectPropertiesSection.CompareProperties = function(propertyA, propertyB) +{ + var a = propertyA.name; + var b = propertyB.name; + if (a === "__proto__") + return 1; + if (b === "__proto__") + return -1; + + // if used elsewhere make sure to + // - convert a and b to strings (not needed here, properties are all strings) + // - check if a == b (not needed here, no two properties can be the same) + + var diff = 0; + var chunk = /^\d+|^\D+/; + var chunka, chunkb, anum, bnum; + while (diff === 0) { + if (!a && b) + return -1; + if (!b && a) + return 1; + chunka = a.match(chunk)[0]; + chunkb = b.match(chunk)[0]; + anum = !isNaN(chunka); + bnum = !isNaN(chunkb); + if (anum && !bnum) + return -1; + if (bnum && !anum) + return 1; + if (anum && bnum) { + diff = chunka - chunkb; + if (diff === 0 && chunka.length !== chunkb.length) { + if (!+chunka && !+chunkb) // chunks are strings of all 0s (special case) + return chunka.length - chunkb.length; + else + return chunkb.length - chunka.length; + } + } else if (chunka !== chunkb) + return (chunka < chunkb) ? -1 : 1; + a = a.substring(chunka.length); + b = b.substring(chunkb.length); + } + return diff; +} + +WebInspector.ObjectPropertyTreeElement = function(property) +{ + this.property = property; + + // Pass an empty title, the title gets made later in onattach. + TreeElement.call(this, "", null, false); +} + +WebInspector.ObjectPropertyTreeElement.prototype = { + onpopulate: function() + { + if (this.children.length && !this.shouldRefreshChildren) + return; + + var callback = function(properties) { + this.removeChildren(); + if (!properties) + return; + + properties.sort(WebInspector.ObjectPropertiesSection.CompareProperties); + for (var i = 0; i < properties.length; ++i) { + this.appendChild(new this.treeOutline.section.treeElementConstructor(properties[i])); + } + }; + this.property.value.getOwnProperties(true, callback.bind(this)); + }, + + ondblclick: function(event) + { + this.startEditing(); + }, + + onattach: function() + { + this.update(); + }, + + update: function() + { + this.nameElement = document.createElement("span"); + this.nameElement.className = "name"; + this.nameElement.textContent = this.property.name; + + var separatorElement = document.createElement("span"); + separatorElement.className = "separator"; + separatorElement.textContent = ": "; + + this.valueElement = document.createElement("span"); + this.valueElement.className = "value"; + this.valueElement.textContent = this.property.value.description; + if (this.property.isGetter) + this.valueElement.addStyleClass("dimmed"); + if (this.property.value.isError()) + this.valueElement.addStyleClass("error"); + if (this.property.value.type) + this.valueElement.addStyleClass("console-formatted-" + this.property.value.type); + if (this.property.value.type === "node") + this.valueElement.addEventListener("contextmenu", this._contextMenuEventFired.bind(this), true); + + this.listItemElement.removeChildren(); + + this.listItemElement.appendChild(this.nameElement); + this.listItemElement.appendChild(separatorElement); + this.listItemElement.appendChild(this.valueElement); + this.hasChildren = this.property.value.hasChildren; + }, + + _contextMenuEventFired: function() + { + function selectNode(nodeId) + { + if (nodeId) { + WebInspector.currentPanel = WebInspector.panels.elements; + WebInspector.panels.elements.focusedDOMNode = WebInspector.domAgent.nodeForId(nodeId); + } + } + + function revealElement() + { + this.property.value.pushNodeToFrontend(selectNode); + } + + var contextMenu = new WebInspector.ContextMenu(); + contextMenu.appendItem(WebInspector.UIString("Reveal in Elements Panel"), revealElement.bind(this)); + contextMenu.show(event); + }, + + updateSiblings: function() + { + if (this.parent.root) + this.treeOutline.section.update(); + else + this.parent.shouldRefreshChildren = true; + }, + + startEditing: function() + { + if (WebInspector.isBeingEdited(this.valueElement) || !this.treeOutline.section.editable) + return; + + var context = { expanded: this.expanded }; + + // Lie about our children to prevent expanding on double click and to collapse subproperties. + this.hasChildren = false; + + this.listItemElement.addStyleClass("editing-sub-part"); + + WebInspector.startEditing(this.valueElement, { + context: context, + commitHandler: this.editingCommitted.bind(this), + cancelHandler: this.editingCancelled.bind(this) + }); + }, + + editingEnded: function(context) + { + this.listItemElement.scrollLeft = 0; + this.listItemElement.removeStyleClass("editing-sub-part"); + if (context.expanded) + this.expand(); + }, + + editingCancelled: function(element, context) + { + this.update(); + this.editingEnded(context); + }, + + editingCommitted: function(element, userInput, previousContent, context) + { + if (userInput === previousContent) + return this.editingCancelled(element, context); // nothing changed, so cancel + + this.applyExpression(userInput, true); + + this.editingEnded(context); + }, + + applyExpression: function(expression, updateInterface) + { + expression = expression.trim(); + var expressionLength = expression.length; + var self = this; + var callback = function(success) { + if (!updateInterface) + return; + + if (!success) + self.update(); + + if (!expressionLength) { + // The property was deleted, so remove this tree element. + self.parent.removeChild(this); + } else { + // Call updateSiblings since their value might be based on the value that just changed. + self.updateSiblings(); + } + }; + this.property.parentObject.setPropertyValue(this.property.name, expression.trim(), callback); + } +} + +WebInspector.ObjectPropertyTreeElement.prototype.__proto__ = TreeElement.prototype; |