2 * Copyright (C) IBM Corp. 2009 All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
33 * @extends {WebInspector.SidebarPane}
35 WebInspector.WatchExpressionsSidebarPane = function()
37 WebInspector.SidebarPane.call(this, WebInspector.UIString("Watch"));
38 this.registerRequiredCSS("components/objectValue.css");
40 this._requiresUpdate = true;
41 /** @type {!Array.<!WebInspector.WatchExpression>} */
42 this._watchExpressions = [];
43 this._watchExpressionsSetting = WebInspector.settings.createLocalSetting("watchExpressions", []);
45 var addButton = new WebInspector.ToolbarButton(WebInspector.UIString("Add expression"), "add-toolbar-item");
46 addButton.addEventListener("click", this._addButtonClicked.bind(this));
47 this.toolbar().appendToolbarItem(addButton);
48 var refreshButton = new WebInspector.ToolbarButton(WebInspector.UIString("Refresh"), "refresh-toolbar-item");
49 refreshButton.addEventListener("click", this._refreshButtonClicked.bind(this));
50 this.toolbar().appendToolbarItem(refreshButton);
52 this._bodyElement = this.element.createChild("div", "vbox watch-expressions");
53 this._bodyElement.addEventListener("contextmenu", this._contextMenu.bind(this), false);
55 WebInspector.context.addFlavorChangeListener(WebInspector.ExecutionContext, this.refreshExpressions, this);
58 WebInspector.WatchExpressionsSidebarPane.prototype = {
61 this._refreshExpressionsIfNeeded();
64 refreshExpressions: function()
66 this._requiresUpdate = true;
67 this._refreshExpressionsIfNeeded();
71 * @param {string} expressionString
73 addExpression: function(expressionString)
76 if (this._requiresUpdate) {
77 this._rebuildWatchExpressions();
78 delete this._requiresUpdate;
80 this._createWatchExpression(expressionString);
81 this._saveExpressions();
84 expandIfNecessary: function()
86 if (this._watchExpressionsSetting.get().length)
90 _saveExpressions: function()
93 for (var i = 0; i < this._watchExpressions.length; i++)
94 if (this._watchExpressions[i].expression())
95 toSave.push(this._watchExpressions[i].expression());
97 this._watchExpressionsSetting.set(toSave);
100 _refreshExpressionsIfNeeded: function()
102 if (this._requiresUpdate && this.isShowing()) {
103 this._rebuildWatchExpressions();
104 delete this._requiresUpdate;
106 this._requiresUpdate = true;
110 * @param {!WebInspector.Event=} event
112 _addButtonClicked: function(event)
117 this._createWatchExpression(null).startEditing();
121 * @param {!WebInspector.Event} event
123 _refreshButtonClicked: function(event)
126 this.refreshExpressions();
129 _rebuildWatchExpressions: function()
131 this._bodyElement.removeChildren();
132 this._watchExpressions = [];
133 this._emptyElement = this._bodyElement.createChild("div", "info");
134 this._emptyElement.textContent = WebInspector.UIString("No Watch Expressions");
135 var watchExpressionStrings = this._watchExpressionsSetting.get();
136 for (var i = 0; i < watchExpressionStrings.length; ++i) {
137 var expression = watchExpressionStrings[i];
141 this._createWatchExpression(expression);
146 * @param {?string} expression
147 * @return {!WebInspector.WatchExpression}
149 _createWatchExpression: function(expression)
151 this._emptyElement.classList.add("hidden");
152 var watchExpression = new WebInspector.WatchExpression(expression);
153 watchExpression.addEventListener(WebInspector.WatchExpression.Events.ExpressionUpdated, this._watchExpressionUpdated.bind(this));
154 this._bodyElement.appendChild(watchExpression.element());
155 this._watchExpressions.push(watchExpression);
156 return watchExpression;
160 * @param {!WebInspector.Event} event
162 _watchExpressionUpdated: function(event)
164 var watchExpression = /** @type {!WebInspector.WatchExpression} */ (event.target);
165 if (!watchExpression.expression()) {
166 this._watchExpressions.remove(watchExpression);
167 this._bodyElement.removeChild(watchExpression.element());
168 this._emptyElement.classList.toggle("hidden", !!this._watchExpressions.length);
171 this._saveExpressions();
175 * @param {!Event} event
177 _contextMenu: function(event)
179 var contextMenu = new WebInspector.ContextMenu(event);
180 this._populateContextMenu(contextMenu, event);
185 * @param {!WebInspector.ContextMenu} contextMenu
186 * @param {!Event} event
188 _populateContextMenu: function(contextMenu, event)
190 var isEditing = false;
191 for (var watchExpression of this._watchExpressions)
192 isEditing |= watchExpression.isEditing();
195 contextMenu.appendItem(WebInspector.UIString.capitalize("Add ^watch ^expression"), this._addButtonClicked.bind(this));
197 if (this._watchExpressions.length > 1)
198 contextMenu.appendItem(WebInspector.UIString.capitalize("Delete ^all ^watch ^expressions"), this._deleteAllButtonClicked.bind(this));
200 for (var watchExpression of this._watchExpressions)
201 if (watchExpression.element().containsEventPoint(event))
202 watchExpression._populateContextMenu(contextMenu, event);
205 _deleteAllButtonClicked: function()
207 this._watchExpressions = [];
208 this._saveExpressions();
209 this._rebuildWatchExpressions();
212 __proto__: WebInspector.SidebarPane.prototype
217 * @extends {WebInspector.Object}
218 * @param {?string} expression
220 WebInspector.WatchExpression = function(expression)
222 this._expression = expression;
223 this._element = createElementWithClass("div", "watch-expression monospace");
224 this._editing = false;
226 this._createWatchExpression(null, false);
230 WebInspector.WatchExpression._watchObjectGroupId = "watch-group";
232 WebInspector.WatchExpression.Events = {
233 ExpressionUpdated: "ExpressionUpdated"
236 WebInspector.WatchExpression.prototype = {
243 return this._element;
249 expression: function()
251 return this._expression;
256 var currentExecutionContext = WebInspector.context.flavor(WebInspector.ExecutionContext);
257 if (currentExecutionContext && this._expression)
258 currentExecutionContext.evaluate(this._expression, WebInspector.WatchExpression._watchObjectGroupId, false, true, false, false, this._createWatchExpression.bind(this));
261 startEditing: function()
263 this._editing = true;
264 this._element.removeChild(this._objectPresentationElement);
265 var newDiv = this._element.createChild("div");
266 newDiv.textContent = this._nameElement.textContent;
267 this._textPrompt = new WebInspector.ObjectPropertyPrompt();
268 this._textPrompt.renderAsBlock();
269 var proxyElement = this._textPrompt.attachAndStartEditing(newDiv, this._finishEditing.bind(this));
270 proxyElement.classList.add("watch-expression-text-prompt-proxy");
271 proxyElement.addEventListener("keydown", this._promptKeyDown.bind(this), false);
272 this._element.getComponentSelection().setBaseAndExtent(newDiv, 0, newDiv, 1);
278 isEditing: function()
280 return !!this._editing;
284 * @param {!Event} event
285 * @param {boolean=} canceled
287 _finishEditing: function(event, canceled)
292 this._editing = false;
293 this._textPrompt.detach();
294 var newExpression = canceled ? this._expression : this._textPrompt.text();
295 delete this._textPrompt;
296 this._element.removeChildren();
297 this._element.appendChild(this._objectPresentationElement);
298 this._updateExpression(newExpression);
302 * @param {!Event} event
304 _dblClickOnWatchExpression: function(event)
307 if (!this.isEditing())
312 * @param {?string} newExpression
314 _updateExpression: function(newExpression)
316 this._expression = newExpression;
318 this.dispatchEventToListeners(WebInspector.WatchExpression.Events.ExpressionUpdated);
322 * @param {!Event} event
324 _deleteWatchExpression: function(event)
327 this._updateExpression(null);
331 * @param {?WebInspector.RemoteObject} result
332 * @param {boolean} wasThrown
334 _createWatchExpression: function(result, wasThrown)
336 this._result = result;
338 var headerElement= createElementWithClass("div", "watch-expression-header");
339 var deleteButton = headerElement.createChild("button", "watch-expression-delete-button");
340 deleteButton.title = WebInspector.UIString("Delete watch expression");
341 deleteButton.addEventListener("click", this._deleteWatchExpression.bind(this), false);
343 var titleElement = headerElement.createChild("div", "watch-expression-title");
344 this._nameElement = WebInspector.ObjectPropertiesSection.createNameElement(this._expression);
345 if (wasThrown || !result) {
346 this._valueElement = createElementWithClass("span", "error-message value");
347 titleElement.classList.add("dimmed");
348 this._valueElement.textContent = WebInspector.UIString("<not available>");
350 this._valueElement = WebInspector.ObjectPropertiesSection.createValueElementWithCustomSupport(result, wasThrown, titleElement);
352 var separatorElement = createElementWithClass("span", "watch-expressions-separator");
353 separatorElement.textContent = ": ";
354 titleElement.appendChildren(this._nameElement, separatorElement, this._valueElement);
356 this._element.removeChildren();
357 this._objectPropertiesSection = null;
358 if (!wasThrown && result && result.hasChildren && !result.customPreview()) {
359 this._objectPropertiesSection = new WebInspector.ObjectPropertiesSection(result, headerElement);
360 this._objectPresentationElement = this._objectPropertiesSection.element;
361 var objectTreeElement = this._objectPropertiesSection.objectTreeElement();
362 objectTreeElement.toggleOnClick = false;
363 objectTreeElement.listItemElement.addEventListener("click", this._onSectionClick.bind(this), false);
364 objectTreeElement.listItemElement.addEventListener("dblclick", this._dblClickOnWatchExpression.bind(this));
366 this._objectPresentationElement = headerElement;
367 this._objectPresentationElement.addEventListener("dblclick", this._dblClickOnWatchExpression.bind(this));
370 this._element.appendChild(this._objectPresentationElement);
374 * @param {!Event} event
376 _onSectionClick: function(event)
379 if (event.detail == 1) {
380 this._preventClickTimeout = setTimeout(handleClick.bind(this), 333);
382 clearTimeout(this._preventClickTimeout);
383 delete this._preventClickTimeout;
387 * @this {WebInspector.WatchExpression}
389 function handleClick()
391 if (!this._objectPropertiesSection)
394 var objectTreeElement = this._objectPropertiesSection.objectTreeElement();
395 if (objectTreeElement.expanded)
396 objectTreeElement.collapse();
398 objectTreeElement.expand();
403 * @param {!Event} event
405 _promptKeyDown: function(event)
407 if (isEnterKey(event) || isEscKey(event))
408 this._finishEditing(event, isEscKey(event));
412 * @param {!WebInspector.ContextMenu} contextMenu
413 * @param {!Event} event
415 _populateContextMenu: function(contextMenu, event)
417 if (!this.isEditing())
418 contextMenu.appendItem(WebInspector.UIString.capitalize("Delete ^watch ^expression"), this._updateExpression.bind(this, null));
420 if (!this.isEditing() && this._result && (this._result.type === "number" || this._result.type === "string"))
421 contextMenu.appendItem(WebInspector.UIString.capitalize("Copy ^value"), this._copyValueButtonClicked.bind(this));
423 if (this._valueElement.containsEventPoint(event))
424 contextMenu.appendApplicableItems(this._result);
427 _copyValueButtonClicked: function()
429 InspectorFrontendHost.copyText(this._valueElement.textContent);
432 __proto__: WebInspector.Object.prototype