2 * Copyright (C) 2011 Google Inc. 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 Google Inc. 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.VBox}
35 WebInspector.InspectorView = function()
37 WebInspector.VBox.call(this);
38 WebInspector.Dialog.setModalHostView(this);
39 WebInspector.GlassPane.DefaultFocusedViewStack.unshift(this);
40 this.setMinimumSize(180, 72);
42 // DevTools sidebar is a vertical split of panels tabbed pane and a drawer.
43 this._drawerSplitView = new WebInspector.SplitView(false, true, "Inspector.drawerSplitViewState", 200, 200);
44 this._drawerSplitView.hideSidebar();
45 this._drawerSplitView.enableShowModeSaving();
46 this._drawerSplitView.show(this.element);
48 this._tabbedPane = new WebInspector.TabbedPane();
49 this._tabbedPane.setRetainTabOrder(true, WebInspector.moduleManager.orderComparator(WebInspector.Panel, "name", "order"));
50 this._tabbedPane.show(this._drawerSplitView.mainElement());
51 this._drawer = new WebInspector.Drawer(this._drawerSplitView);
53 // Patch tabbed pane header with toolbar actions.
54 this._toolbarElement = document.createElement("div");
55 this._toolbarElement.className = "toolbar toolbar-background";
56 var headerElement = this._tabbedPane.headerElement();
57 headerElement.parentElement.insertBefore(this._toolbarElement, headerElement);
59 this._leftToolbarElement = this._toolbarElement.createChild("div", "toolbar-controls-left");
60 this._toolbarElement.appendChild(headerElement);
61 this._rightToolbarElement = this._toolbarElement.createChild("div", "toolbar-controls-right");
63 if (WebInspector.experimentsSettings.devicesPanel.isEnabled()) {
64 this._remoteDeviceCountElement = this._rightToolbarElement.createChild("div", "hidden");
65 this._remoteDeviceCountElement.addEventListener("click", this.showViewInDrawer.bind(this, "devices", true), false);
66 this._remoteDeviceCountElement.id = "remote-device-count";
67 WebInspector.inspectorFrontendEventSink.addEventListener(WebInspector.InspectorView.Events.DeviceCountChanged, this._onDeviceCountChanged, this);
70 this._errorWarningCountElement = this._rightToolbarElement.createChild("div", "hidden");
71 this._errorWarningCountElement.id = "error-warning-count";
73 this._closeButtonToolbarItem = document.createElementWithClass("div", "toolbar-close-button-item");
74 var closeButtonElement = this._closeButtonToolbarItem.createChild("div", "close-button");
75 closeButtonElement.addEventListener("click", InspectorFrontendHost.closeWindow.bind(InspectorFrontendHost), true);
76 this._rightToolbarElement.appendChild(this._closeButtonToolbarItem);
78 this.appendToRightToolbar(this._drawer.toggleButtonElement());
82 WebInspector["panels"] = this._panels;
85 this._historyIterator = -1;
86 document.addEventListener("keydown", this._keyDown.bind(this), false);
87 document.addEventListener("keypress", this._keyPress.bind(this), false);
88 this._panelDescriptors = {};
90 // Windows and Mac have two different definitions of '[' and ']', so accept both of each.
91 this._openBracketIdentifiers = ["U+005B", "U+00DB"].keySet();
92 this._closeBracketIdentifiers = ["U+005D", "U+00DD"].keySet();
93 this._lastActivePanelSetting = WebInspector.settings.createSetting("lastActivePanel", "elements");
95 this._loadPanelDesciptors();
98 WebInspector.InspectorView.Events = {
99 DeviceCountChanged: "DeviceCountChanged"
102 WebInspector.InspectorView.prototype = {
103 _loadPanelDesciptors: function()
105 WebInspector.startBatchUpdate();
106 WebInspector.moduleManager.extensions(WebInspector.Panel).forEach(processPanelExtensions.bind(this));
108 * @param {!WebInspector.ModuleManager.Extension} extension
109 * @this {!WebInspector.InspectorView}
111 function processPanelExtensions(extension)
113 this.addPanel(new WebInspector.ModuleManagerExtensionPanelDescriptor(extension));
115 WebInspector.endBatchUpdate();
119 * @param {!Element} element
121 appendToLeftToolbar: function(element)
123 this._leftToolbarElement.appendChild(element);
127 * @param {!Element} element
129 appendToRightToolbar: function(element)
131 this._rightToolbarElement.insertBefore(element, this._closeButtonToolbarItem);
135 * @param {!WebInspector.PanelDescriptor} panelDescriptor
137 addPanel: function(panelDescriptor)
139 var panelName = panelDescriptor.name();
140 this._panelDescriptors[panelName] = panelDescriptor;
141 this._tabbedPane.appendTab(panelName, panelDescriptor.title(), new WebInspector.View());
142 if (this._lastActivePanelSetting.get() === panelName)
143 this._tabbedPane.selectTab(panelName);
147 * @param {string} panelName
150 hasPanel: function(panelName)
152 return !!this._panelDescriptors[panelName];
156 * @param {string} panelName
157 * @return {?WebInspector.Panel}
159 panel: function(panelName)
161 var panelDescriptor = this._panelDescriptors[panelName];
162 var panelOrder = this._tabbedPane.allTabs();
163 if (!panelDescriptor && panelOrder.length)
164 panelDescriptor = this._panelDescriptors[panelOrder[0]];
165 var panel = panelDescriptor ? panelDescriptor.panel() : null;
167 this._panels[panelName] = panel;
172 * @param {string} panelName
173 * @return {?WebInspector.Panel}
175 showPanel: function(panelName)
177 var panel = this.panel(panelName);
179 this.setCurrentPanel(panel);
184 * @return {!WebInspector.Panel}
186 currentPanel: function()
188 return this._currentPanel;
191 showInitialPanel: function()
193 this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this);
195 this._drawer.initialPanelShown();
198 showDrawerEditor: function()
200 this._drawer.showDrawerEditor();
206 isDrawerEditorShown: function()
208 return this._drawer.isDrawerEditorShown();
211 hideDrawerEditor: function()
213 this._drawer.hideDrawerEditor();
217 * @param {boolean} available
219 setDrawerEditorAvailable: function(available)
221 this._drawer.setDrawerEditorAvailable(available);
224 _tabSelected: function()
226 var panelName = this._tabbedPane.selectedTabId;
229 var panel = this._panelDescriptors[this._tabbedPane.selectedTabId].panel();
230 this._panels[panelName] = panel;
231 this._tabbedPane.changeTabView(panelName, panel);
233 this._currentPanel = panel;
234 this._lastActivePanelSetting.set(panel.name);
235 this._pushToHistory(panel.name);
236 WebInspector.userMetrics.panelShown(panel.name);
241 * @param {!WebInspector.Panel} x
243 setCurrentPanel: function(x)
245 if (this._currentPanel === x)
248 this._tabbedPane.changeTabView(x.name, x);
249 this._tabbedPane.selectTab(x.name);
255 closeViewInDrawer: function(id)
257 this._drawer.closeView(id);
262 * @param {string} title
263 * @param {!WebInspector.View} view
265 showCloseableViewInDrawer: function(id, title, view)
267 this._drawer.showCloseableView(id, title, view);
270 showDrawer: function()
272 this._drawer.showDrawer();
278 drawerVisible: function()
280 return this._drawer.isShowing();
285 * @param {boolean=} immediate
287 showViewInDrawer: function(id, immediate)
289 this._drawer.showView(id, immediate);
295 selectedViewInDrawer: function()
297 return this._drawer.selectedViewId();
300 closeDrawer: function()
302 this._drawer.closeDrawer();
308 defaultFocusedElement: function()
310 return this._currentPanel ? this._currentPanel.defaultFocusedElement() : null;
313 _keyPress: function(event)
315 // BUG 104250: Windows 7 posts a WM_CHAR message upon the Ctrl+']' keypress.
316 // Any charCode < 32 is not going to be a valid keypress.
317 if (event.charCode < 32 && WebInspector.isWin())
319 clearTimeout(this._keyDownTimer);
320 delete this._keyDownTimer;
323 _keyDown: function(event)
325 if (!WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event))
328 var keyboardEvent = /** @type {!KeyboardEvent} */ (event);
329 // Ctrl/Cmd + 1-9 should show corresponding panel.
330 var panelShortcutEnabled = WebInspector.settings.shortcutPanelSwitch.get();
331 if (panelShortcutEnabled && !event.shiftKey && !event.altKey) {
333 if (event.keyCode > 0x30 && event.keyCode < 0x3A)
334 panelIndex = event.keyCode - 0x31;
335 else if (event.keyCode > 0x60 && event.keyCode < 0x6A && keyboardEvent.location === KeyboardEvent.DOM_KEY_LOCATION_NUMPAD)
336 panelIndex = event.keyCode - 0x61;
337 if (panelIndex !== -1) {
338 var panelName = this._tabbedPane.allTabs()[panelIndex];
340 if (!WebInspector.Dialog.currentInstance())
341 this.showPanel(panelName);
348 // BUG85312: On French AZERTY keyboards, AltGr-]/[ combinations (synonymous to Ctrl-Alt-]/[ on Windows) are used to enter ]/[,
349 // so for a ]/[-related keydown we delay the panel switch using a timer, to see if there is a keypress event following this one.
350 // If there is, we cancel the timer and do not consider this a panel switch.
351 if (!WebInspector.isWin() || (!this._openBracketIdentifiers[event.keyIdentifier] && !this._closeBracketIdentifiers[event.keyIdentifier])) {
352 this._keyDownInternal(event);
356 this._keyDownTimer = setTimeout(this._keyDownInternal.bind(this, event), 0);
359 _keyDownInternal: function(event)
363 if (this._openBracketIdentifiers[event.keyIdentifier])
366 if (this._closeBracketIdentifiers[event.keyIdentifier])
372 if (!event.shiftKey && !event.altKey) {
373 if (!WebInspector.Dialog.currentInstance())
374 this._changePanelInDirection(direction);
379 if (event.altKey && this._moveInHistory(direction))
383 _changePanelInDirection: function(direction)
385 var panelOrder = this._tabbedPane.allTabs();
386 var index = panelOrder.indexOf(this.currentPanel().name);
387 index = (index + panelOrder.length + direction) % panelOrder.length;
388 this.showPanel(panelOrder[index]);
391 _moveInHistory: function(move)
393 var newIndex = this._historyIterator + move;
394 if (newIndex >= this._history.length || newIndex < 0)
397 this._inHistory = true;
398 this._historyIterator = newIndex;
399 if (!WebInspector.Dialog.currentInstance())
400 this.setCurrentPanel(this._panels[this._history[this._historyIterator]]);
401 delete this._inHistory;
406 _pushToHistory: function(panelName)
411 this._history.splice(this._historyIterator + 1, this._history.length - this._historyIterator - 1);
412 if (!this._history.length || this._history[this._history.length - 1] !== panelName)
413 this._history.push(panelName);
414 this._historyIterator = this._history.length - 1;
419 WebInspector.Dialog.modalHostRepositioned();
425 topResizerElement: function()
427 return this._tabbedPane.headerElement();
430 _createImagedCounterElementIfNeeded: function(parent, count, id, styleName)
435 var imageElement = parent.createChild("div", styleName);
436 var counterElement = parent.createChild("span");
437 counterElement.id = id;
438 counterElement.textContent = count;
442 * @param {number} errors
443 * @param {number} warnings
445 setErrorAndWarningCounts: function(errors, warnings)
447 if (this._errors === errors && this._warnings === warnings)
449 this._errors = errors;
450 this._warnings = warnings;
451 this._errorWarningCountElement.classList.toggle("hidden", !errors && !warnings);
452 this._errorWarningCountElement.removeChildren();
454 this._createImagedCounterElementIfNeeded(this._errorWarningCountElement, errors, "error-count", "error-icon-small");
455 this._createImagedCounterElementIfNeeded(this._errorWarningCountElement, warnings, "warning-count", "warning-icon-small");
457 var errorString = errors ? WebInspector.UIString("%d error%s", errors, errors > 1 ? "s" : "") : "";
458 var warningString = warnings ? WebInspector.UIString("%d warning%s", warnings, warnings > 1 ? "s" : "") : "";
459 var commaString = errors && warnings ? ", " : "";
460 this._errorWarningCountElement.title = errorString + commaString + warningString;
461 this._tabbedPane.headerResized();
465 * @param {!WebInspector.Event} event
467 _onDeviceCountChanged: function(event)
469 var count = /** @type {number} */ (event.data);
470 if (count === this.deviceCount_)
472 this.deviceCount_ = count;
473 this._remoteDeviceCountElement.classList.toggle("hidden", !count);
474 this._remoteDeviceCountElement.removeChildren();
475 this._createImagedCounterElementIfNeeded(this._remoteDeviceCountElement, count, "device-count", "device-icon-small");
476 this._remoteDeviceCountElement.title = WebInspector.UIString(((count > 1) ? "%d devices found" : "%d device found"), count);
477 this._tabbedPane.headerResized();
480 __proto__: WebInspector.VBox.prototype
484 * @type {!WebInspector.InspectorView}
486 WebInspector.inspectorView;
490 * @implements {WebInspector.ActionDelegate}
492 WebInspector.InspectorView.DrawerToggleActionDelegate = function()
496 WebInspector.InspectorView.DrawerToggleActionDelegate.prototype = {
500 handleAction: function()
502 if (WebInspector.inspectorView.drawerVisible()) {
503 WebInspector.inspectorView.closeDrawer();
506 WebInspector.inspectorView.showDrawer();
513 * @extends {WebInspector.VBox}
515 WebInspector.RootView = function()
517 WebInspector.VBox.call(this);
519 this.element.classList.add("root-view");
520 this.element.setAttribute("spellcheck", false);
521 window.addEventListener("resize", this.doResize.bind(this), true);
522 this._onScrollBound = this._onScroll.bind(this);
525 WebInspector.RootView.prototype = {
526 attachToBody: function()
529 this.show(document.body);
532 _onScroll: function()
534 // If we didn't have enough space at the start, we may have wrong scroll offsets.
535 if (document.body.scrollTop !== 0)
536 document.body.scrollTop = 0;
537 if (document.body.scrollLeft !== 0)
538 document.body.scrollLeft = 0;
543 var size = this.constraints().minimum;
544 var zoom = WebInspector.zoomManager.zoomFactor();
545 var right = Math.min(0, window.innerWidth - size.width / zoom);
546 this.element.style.right = right + "px";
547 var bottom = Math.min(0, window.innerHeight - size.height / zoom);
548 this.element.style.bottom = bottom + "px";
550 if (window.innerWidth < size.width || window.innerHeight < size.height)
551 window.addEventListener("scroll", this._onScrollBound, false);
553 window.removeEventListener("scroll", this._onScrollBound, false);
555 WebInspector.VBox.prototype.doResize.call(this);
559 __proto__: WebInspector.VBox.prototype