1/* 2 * Copyright (C) IBM Corp. 2009 All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of IBM Corp. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31WebInspector.WatchExpressionsSidebarPane = function() 32{ 33 WebInspector.SidebarPane.call(this, WebInspector.UIString("Watch Expressions")); 34 this.reset(); 35} 36 37WebInspector.WatchExpressionsSidebarPane.prototype = { 38 reset: function() 39 { 40 this.bodyElement.removeChildren(); 41 42 this.expanded = WebInspector.settings.watchExpressions.length > 0; 43 this.section = new WebInspector.WatchExpressionsSection(); 44 this.bodyElement.appendChild(this.section.element); 45 46 var addElement = document.createElement("button"); 47 addElement.setAttribute("type", "button"); 48 addElement.textContent = WebInspector.UIString("Add"); 49 addElement.addEventListener("click", this.section.addExpression.bind(this.section), false); 50 51 var refreshElement = document.createElement("button"); 52 refreshElement.setAttribute("type", "button"); 53 refreshElement.textContent = WebInspector.UIString("Refresh"); 54 refreshElement.addEventListener("click", this.section.update.bind(this.section), false); 55 56 var centerElement = document.createElement("div"); 57 centerElement.addStyleClass("watch-expressions-buttons-container"); 58 centerElement.appendChild(addElement); 59 centerElement.appendChild(refreshElement); 60 this.bodyElement.appendChild(centerElement); 61 62 this.onexpand = this.refreshExpressions.bind(this); 63 }, 64 65 refreshExpressions: function() 66 { 67 if (this.section) 68 this.section.update(); 69 } 70} 71 72WebInspector.WatchExpressionsSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype; 73 74WebInspector.WatchExpressionsSection = function() 75{ 76 this._watchObjectGroupId = "watch-group"; 77 78 WebInspector.ObjectPropertiesSection.call(this); 79 80 this.watchExpressions = WebInspector.settings.watchExpressions; 81 82 this.headerElement.className = "hidden"; 83 this.editable = true; 84 this.expanded = true; 85 this.propertiesElement.addStyleClass("watch-expressions"); 86} 87 88WebInspector.WatchExpressionsSection.NewWatchExpression = "\xA0"; 89 90WebInspector.WatchExpressionsSection.prototype = { 91 update: function() 92 { 93 function appendResult(expression, watchIndex, result) 94 { 95 var property = new WebInspector.RemoteObjectProperty(expression, result); 96 property.watchIndex = watchIndex; 97 98 // To clarify what's going on here: 99 // In the outer function, we calculate the number of properties 100 // that we're going to be updating, and set that in the 101 // propertyCount variable. 102 // In this function, we test to see when we are processing the 103 // last property, and then call the superclass's updateProperties() 104 // method to get all the properties refreshed at once. 105 properties.push(property); 106 107 if (properties.length == propertyCount) { 108 this.updateProperties(properties, WebInspector.WatchExpressionTreeElement, WebInspector.WatchExpressionsSection.CompareProperties); 109 110 // check to see if we just added a new watch expression, 111 // which will always be the last property 112 if (this._newExpressionAdded) { 113 delete this._newExpressionAdded; 114 115 treeElement = this.findAddedTreeElement(); 116 if (treeElement) 117 treeElement.startEditing(); 118 } 119 } 120 } 121 122 // TODO: pass exact injected script id. 123 RuntimeAgent.releaseObjectGroup(this._watchObjectGroupId) 124 var properties = []; 125 126 // Count the properties, so we known when to call this.updateProperties() 127 // in appendResult() 128 var propertyCount = 0; 129 for (var i = 0; i < this.watchExpressions.length; ++i) { 130 if (!this.watchExpressions[i]) 131 continue; 132 ++propertyCount; 133 } 134 135 // Now process all the expressions, since we have the actual count, 136 // which is checked in the appendResult inner function. 137 for (var i = 0; i < this.watchExpressions.length; ++i) { 138 var expression = this.watchExpressions[i]; 139 if (!expression) 140 continue; 141 142 WebInspector.console.evalInInspectedWindow("(" + expression + ")", this._watchObjectGroupId, false, appendResult.bind(this, expression, i)); 143 } 144 145 // note this is setting the expansion of the tree, not the section; 146 // with no expressions, and expanded tree, we get some extra vertical 147 // white space 148 // FIXME: should change to use header buttons instead of the buttons 149 // at the bottom of the section, then we can add a "No Watch Expressions 150 // element when there are no watch expressions, and this issue should 151 // go away. 152 this.expanded = (propertyCount != 0); 153 }, 154 155 addExpression: function() 156 { 157 this._newExpressionAdded = true; 158 this.watchExpressions.push(WebInspector.WatchExpressionsSection.NewWatchExpression); 159 this.update(); 160 }, 161 162 updateExpression: function(element, value) 163 { 164 this.watchExpressions[element.property.watchIndex] = value; 165 this.saveExpressions(); 166 this.update(); 167 }, 168 169 findAddedTreeElement: function() 170 { 171 var children = this.propertiesTreeOutline.children; 172 for (var i = 0; i < children.length; ++i) 173 if (children[i].property.name === WebInspector.WatchExpressionsSection.NewWatchExpression) 174 return children[i]; 175 }, 176 177 saveExpressions: function() 178 { 179 var toSave = []; 180 for (var i = 0; i < this.watchExpressions.length; i++) 181 if (this.watchExpressions[i]) 182 toSave.push(this.watchExpressions[i]); 183 184 WebInspector.settings.watchExpressions = toSave; 185 return toSave.length; 186 } 187} 188 189WebInspector.WatchExpressionsSection.prototype.__proto__ = WebInspector.ObjectPropertiesSection.prototype; 190 191WebInspector.WatchExpressionsSection.CompareProperties = function(propertyA, propertyB) 192{ 193 if (propertyA.watchIndex == propertyB.watchIndex) 194 return 0; 195 else if (propertyA.watchIndex < propertyB.watchIndex) 196 return -1; 197 else 198 return 1; 199} 200 201WebInspector.WatchExpressionTreeElement = function(property) 202{ 203 WebInspector.ObjectPropertyTreeElement.call(this, property); 204} 205 206WebInspector.WatchExpressionTreeElement.prototype = { 207 update: function() 208 { 209 WebInspector.ObjectPropertyTreeElement.prototype.update.call(this); 210 211 if (this.property.value.isError()) 212 this.valueElement.addStyleClass("watch-expressions-error-level"); 213 214 var deleteButton = document.createElement("input"); 215 deleteButton.type = "button"; 216 deleteButton.title = WebInspector.UIString("Delete watch expression."); 217 deleteButton.addStyleClass("enabled-button"); 218 deleteButton.addStyleClass("delete-button"); 219 deleteButton.addEventListener("click", this._deleteButtonClicked.bind(this), false); 220 221 this.listItemElement.insertBefore(deleteButton, this.listItemElement.firstChild); 222 }, 223 224 _deleteButtonClicked: function() 225 { 226 this.treeOutline.section.updateExpression(this, null); 227 }, 228 229 startEditing: function() 230 { 231 if (WebInspector.isBeingEdited(this.nameElement) || !this.treeOutline.section.editable) 232 return; 233 234 this.nameElement.textContent = this.property.name.trim(); 235 236 var context = { expanded: this.expanded }; 237 238 // collapse temporarily, if required 239 this.hasChildren = false; 240 241 this.listItemElement.addStyleClass("editing-sub-part"); 242 243 WebInspector.startEditing(this.nameElement, { 244 context: context, 245 commitHandler: this.editingCommitted.bind(this), 246 cancelHandler: this.editingCancelled.bind(this) 247 }); 248 }, 249 250 editingCancelled: function(element, context) 251 { 252 if (!this.nameElement.textContent) 253 this.treeOutline.section.updateExpression(this, null); 254 255 this.update(); 256 this.editingEnded(context); 257 }, 258 259 applyExpression: function(expression, updateInterface) 260 { 261 expression = expression.trim(); 262 263 if (!expression) 264 expression = null; 265 266 this.property.name = expression; 267 this.treeOutline.section.updateExpression(this, expression); 268 } 269} 270 271WebInspector.WatchExpressionTreeElement.prototype.__proto__ = WebInspector.ObjectPropertyTreeElement.prototype; 272