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.View}
35 WebInspector.InspectorView = function()
37 WebInspector.View.call(this);
39 this.element.classList.add("fill", "inspector-view");
40 this.element.setAttribute("spellcheck", false);
42 // We can use split view either for docking or screencast, but not together.
43 var settingName = WebInspector.queryParamsObject["can_dock"] ? "InspectorView.splitView" : "InspectorView.screencastSplitView";
44 this._splitView = new WebInspector.SplitView(false, settingName, 300, 300);
45 this._splitView.setSecondIsSidebar(true);
46 this._updateConstraints();
47 WebInspector.dockController.addEventListener(WebInspector.DockController.Events.DockSideChanged, this._updateSplitView.bind(this));
49 this._splitView.element.id = "inspector-split-view";
50 this._splitView.show(this.element);
52 this._overlayView = new WebInspector.InspectorView.OverlayView();
53 this._splitView.setMainView(this._overlayView);
54 this._zoomFactor = WebInspector.zoomFactor();
55 WebInspector.settings.zoomLevel.addChangeListener(this._onZoomChanged, this);
57 this._devtoolsView = new WebInspector.View();
58 this._splitView.setSidebarView(this._devtoolsView);
60 this._tabbedPane = new WebInspector.TabbedPane();
61 this._tabbedPane.setRetainTabOrder(true, WebInspector.moduleManager.orderComparator(WebInspector.Panel, "name", "order"));
62 this._tabbedPane.show(this._devtoolsView.element);
64 this._toolbarElement = document.createElement("div");
65 this._toolbarElement.className = "toolbar toolbar-background";
66 var headerElement = this._tabbedPane.headerElement();
67 headerElement.parentElement.insertBefore(this._toolbarElement, headerElement);
69 this._leftToolbarElement = this._toolbarElement.createChild("div", "toolbar-controls-left");
70 this._toolbarElement.appendChild(headerElement);
71 this._rightToolbarElement = this._toolbarElement.createChild("div", "toolbar-controls-right");
73 this._errorWarningCountElement = this._rightToolbarElement.createChild("div", "hidden");
74 this._errorWarningCountElement.id = "error-warning-count";
76 this._closeButtonToolbarItem = document.createElementWithClass("div", "toolbar-close-button-item");
77 var closeButtonElement = this._closeButtonToolbarItem.createChild("div", "close-button");
78 closeButtonElement.addEventListener("click", WebInspector.close.bind(WebInspector), true);
79 this._rightToolbarElement.appendChild(this._closeButtonToolbarItem);
81 this._drawer = new WebInspector.Drawer(this);
82 this.appendToRightToolbar(this._drawer.toggleButtonElement());
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._updateSplitView();
100 WebInspector.InspectorView.Constraints = {
107 WebInspector.InspectorView.prototype = {
108 _initialize: function()
110 WebInspector.startBatchUpdate();
111 WebInspector.moduleManager.extensions(WebInspector.Panel).forEach(processPanelExtensions.bind(this));
113 * @param {!WebInspector.ModuleManager.Extension} extension
114 * @this {!WebInspector.InspectorView}
116 function processPanelExtensions(extension)
118 this.addPanel(new WebInspector.ModuleManagerExtensionPanelDescriptor(extension));
120 WebInspector.endBatchUpdate();
124 * @param {!Element} element
126 appendToLeftToolbar: function(element)
128 this._leftToolbarElement.appendChild(element);
132 * @param {!Element} element
134 appendToRightToolbar: function(element)
136 this._rightToolbarElement.insertBefore(element, this._closeButtonToolbarItem);
140 * @return {!WebInspector.Drawer}
150 devtoolsElement: function()
152 return this._devtoolsView.element;
156 * @param {!WebInspector.PanelDescriptor} panelDescriptor
158 addPanel: function(panelDescriptor)
160 var panelName = panelDescriptor.name();
161 this._panelDescriptors[panelName] = panelDescriptor;
162 this._tabbedPane.appendTab(panelName, panelDescriptor.title(), new WebInspector.View());
163 if (this._lastActivePanelSetting.get() === panelName)
164 this._tabbedPane.selectTab(panelName);
168 * @param {string} panelName
169 * @return {?WebInspector.Panel}
171 panel: function(panelName)
173 var panelDescriptor = this._panelDescriptors[panelName];
174 var panelOrder = this._tabbedPane.allTabs();
175 if (!panelDescriptor && panelOrder.length)
176 panelDescriptor = this._panelDescriptors[panelOrder[0]];
177 return panelDescriptor ? panelDescriptor.panel() : null;
181 * @param {string} panelName
182 * @return {?WebInspector.Panel}
184 showPanel: function(panelName)
186 var panel = this.panel(panelName);
188 this.setCurrentPanel(panel);
193 * @return {!WebInspector.Panel}
195 currentPanel: function()
197 return this._currentPanel;
200 showInitialPanel: function()
202 this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this);
204 this._drawer.showOnLoadIfNecessary();
207 _tabSelected: function()
209 var panelName = this._tabbedPane.selectedTabId;
210 var panel = this._panelDescriptors[this._tabbedPane.selectedTabId].panel();
211 this._tabbedPane.changeTabView(panelName, panel);
213 this._currentPanel = panel;
214 this._lastActivePanelSetting.set(panel.name);
215 this._pushToHistory(panel.name);
216 WebInspector.userMetrics.panelShown(panel.name);
221 * @param {!WebInspector.Panel} x
223 setCurrentPanel: function(x)
225 if (this._currentPanel === x)
228 this._tabbedPane.changeTabView(x.name, x);
229 this._tabbedPane.selectTab(x.name);
235 closeViewInDrawer: function(id)
237 this._drawer.closeView(id);
242 * @param {string} title
243 * @param {!WebInspector.View} view
245 showCloseableViewInDrawer: function(id, title, view)
247 this._drawer.showCloseableView(id, title, view);
253 showViewInDrawer: function(id)
255 this._drawer.showView(id);
261 selectedViewInDrawer: function()
263 return this._drawer.selectedViewId();
266 closeDrawer: function()
274 defaultFocusedElement: function()
276 return this._currentPanel ? this._currentPanel.defaultFocusedElement() : null;
279 _keyPress: function(event)
281 // BUG 104250: Windows 7 posts a WM_CHAR message upon the Ctrl+']' keypress.
282 // Any charCode < 32 is not going to be a valid keypress.
283 if (event.charCode < 32 && WebInspector.isWin())
285 clearTimeout(this._keyDownTimer);
286 delete this._keyDownTimer;
289 _keyDown: function(event)
291 if (!WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event))
294 var keyboardEvent = /** @type {!KeyboardEvent} */ (event);
295 // Ctrl/Cmd + 1-9 should show corresponding panel.
296 var panelShortcutEnabled = WebInspector.settings.shortcutPanelSwitch.get();
297 if (panelShortcutEnabled && !event.shiftKey && !event.altKey) {
299 if (event.keyCode > 0x30 && event.keyCode < 0x3A)
300 panelIndex = event.keyCode - 0x31;
301 else if (event.keyCode > 0x60 && event.keyCode < 0x6A && keyboardEvent.location === KeyboardEvent.DOM_KEY_LOCATION_NUMPAD)
302 panelIndex = event.keyCode - 0x61;
303 if (panelIndex !== -1) {
304 var panelName = this._tabbedPane.allTabs()[panelIndex];
306 this.showPanel(panelName);
313 // BUG85312: On French AZERTY keyboards, AltGr-]/[ combinations (synonymous to Ctrl-Alt-]/[ on Windows) are used to enter ]/[,
314 // so for a ]/[-related keydown we delay the panel switch using a timer, to see if there is a keypress event following this one.
315 // If there is, we cancel the timer and do not consider this a panel switch.
316 if (!WebInspector.isWin() || (!this._openBracketIdentifiers[event.keyIdentifier] && !this._closeBracketIdentifiers[event.keyIdentifier])) {
317 this._keyDownInternal(event);
321 this._keyDownTimer = setTimeout(this._keyDownInternal.bind(this, event), 0);
324 _keyDownInternal: function(event)
326 if (this._openBracketIdentifiers[event.keyIdentifier]) {
327 var isRotateLeft = !event.shiftKey && !event.altKey;
329 var panelOrder = this._tabbedPane.allTabs();
330 var index = panelOrder.indexOf(this.currentPanel().name);
331 index = (index === 0) ? panelOrder.length - 1 : index - 1;
332 this.showPanel(panelOrder[index]);
337 var isGoBack = event.altKey;
338 if (isGoBack && this._canGoBackInHistory()) {
339 this._goBackInHistory();
345 if (this._closeBracketIdentifiers[event.keyIdentifier]) {
346 var isRotateRight = !event.shiftKey && !event.altKey;
348 var panelOrder = this._tabbedPane.allTabs();
349 var index = panelOrder.indexOf(this.currentPanel().name);
350 index = (index + 1) % panelOrder.length;
351 this.showPanel(panelOrder[index]);
356 var isGoForward = event.altKey;
357 if (isGoForward && this._canGoForwardInHistory()) {
358 this._goForwardInHistory();
365 _canGoBackInHistory: function()
367 return this._historyIterator > 0;
370 _goBackInHistory: function()
372 this._inHistory = true;
373 this.setCurrentPanel(WebInspector.panels[this._history[--this._historyIterator]]);
374 delete this._inHistory;
377 _canGoForwardInHistory: function()
379 return this._historyIterator < this._history.length - 1;
382 _goForwardInHistory: function()
384 this._inHistory = true;
385 this.setCurrentPanel(WebInspector.panels[this._history[++this._historyIterator]]);
386 delete this._inHistory;
389 _pushToHistory: function(panelName)
394 this._history.splice(this._historyIterator + 1, this._history.length - this._historyIterator - 1);
395 if (!this._history.length || this._history[this._history.length - 1] !== panelName)
396 this._history.push(panelName);
397 this._historyIterator = this._history.length - 1;
402 // FIXME: make drawer a view.
404 this._drawer.resize();
407 _updateSplitView: function()
409 var dockSide = WebInspector.dockController.dockSide();
410 if (dockSide !== WebInspector.DockController.State.Undocked) {
411 var vertical = WebInspector.dockController.isVertical();
412 this._splitView.setVertical(vertical);
414 if (dockSide === WebInspector.DockController.State.DockedToRight)
415 this._overlayView.setMargins(false, true, false, false);
417 this._overlayView.setMargins(false, false, false, true);
418 this._splitView.setSecondIsSidebar(dockSide === WebInspector.DockController.State.DockedToRight);
419 this._splitView.uninstallResizer(this._tabbedPane.headerElement());
420 this._splitView.installResizer(this._splitView.resizerElement());
422 this._overlayView.setMargins(false, false, false, false);
423 this._splitView.setSecondIsSidebar(true);
424 this._splitView.uninstallResizer(this._splitView.resizerElement());
425 this._splitView.installResizer(this._tabbedPane.headerElement());
427 this._splitView.setMainView(this._overlayView);
428 this._splitView.setSidebarView(this._devtoolsView);
429 this._splitView.showBoth();
431 this._overlayView.setMargins(false, false, false, false);
432 this._splitView.setSecondIsSidebar(true);
433 this._splitView.setMainView(this._overlayView);
434 this._splitView.setSidebarView(this._devtoolsView);
435 this._splitView.showOnlySecond();
436 this._splitView.uninstallResizer(this._tabbedPane.headerElement());
437 this._splitView.uninstallResizer(this._splitView.resizerElement());
441 _onZoomChanged: function()
443 this._updateConstraints();
444 var zoomFactor = WebInspector.zoomFactor();
445 if (zoomFactor !== this._zoomFactor)
446 this._splitView.setSidebarSize(this._splitView.sidebarSize() * this._zoomFactor / zoomFactor, true);
447 this._zoomFactor = zoomFactor;
450 _updateConstraints: function()
452 var zoomFactor = WebInspector.zoomFactor();
453 this._splitView.setSidebarElementConstraints(WebInspector.InspectorView.Constraints.DevToolsWidth / zoomFactor,
454 WebInspector.InspectorView.Constraints.DevToolsHeight / zoomFactor);
455 this._splitView.setMainElementConstraints(WebInspector.InspectorView.Constraints.OverlayWidth / zoomFactor,
456 WebInspector.InspectorView.Constraints.OverlayHeight / zoomFactor);
460 * @param {!WebInspector.View} view
461 * @param {boolean} vertical
463 showScreencastView: function(view, vertical)
465 if (view.parentView() !== this._overlayView)
466 view.show(this._overlayView.element);
467 this._splitView.setVertical(vertical);
468 this._splitView.installResizer(this._splitView.resizerElement());
469 this._splitView.showBoth();
472 hideScreencastView: function()
474 this._splitView.showOnlySecond();
478 * @param {number} errors
479 * @param {number} warnings
481 setErrorAndWarningCounts: function(errors, warnings)
483 if (!errors && !warnings) {
484 this._errorWarningCountElement.classList.add("hidden");
485 this._tabbedPane.headerResized();
489 this._errorWarningCountElement.classList.remove("hidden");
490 this._errorWarningCountElement.removeChildren();
493 var errorImageElement = this._errorWarningCountElement.createChild("div", "error-icon-small");
494 var errorElement = this._errorWarningCountElement.createChild("span");
495 errorElement.id = "error-count";
496 errorElement.textContent = errors;
500 var warningsImageElement = this._errorWarningCountElement.createChild("div", "warning-icon-small");
501 var warningsElement = this._errorWarningCountElement.createChild("span");
502 warningsElement.id = "warning-count";
503 warningsElement.textContent = warnings;
510 this._errorWarningCountElement.title = WebInspector.UIString("%d error, %d warning", errors, warnings);
512 this._errorWarningCountElement.title = WebInspector.UIString("%d error, %d warnings", errors, warnings);
513 } else if (warnings == 1)
514 this._errorWarningCountElement.title = WebInspector.UIString("%d errors, %d warning", errors, warnings);
516 this._errorWarningCountElement.title = WebInspector.UIString("%d errors, %d warnings", errors, warnings);
517 } else if (errors == 1)
518 this._errorWarningCountElement.title = WebInspector.UIString("%d error", errors);
520 this._errorWarningCountElement.title = WebInspector.UIString("%d errors", errors);
521 } else if (warnings == 1)
522 this._errorWarningCountElement.title = WebInspector.UIString("%d warning", warnings);
524 this._errorWarningCountElement.title = WebInspector.UIString("%d warnings", warnings);
526 this._errorWarningCountElement.title = null;
528 this._tabbedPane.headerResized();
531 __proto__: WebInspector.View.prototype
536 * @extends {WebInspector.View}
538 WebInspector.InspectorView.OverlayView = function()
540 WebInspector.View.call(this);
543 WebInspector.InspectorView.OverlayView.prototype = {
545 * @param {boolean} top
546 * @param {boolean} right
547 * @param {boolean} bottom
548 * @param {boolean} left
550 setMargins: function(top, right, bottom, left)
552 var marginValue = Math.round(3 * WebInspector.zoomFactor()) + "px ";
553 var margings = top ? marginValue : "0 ";
554 margings += right ? marginValue : "0 ";
555 margings += bottom ? marginValue : "0 ";
556 margings += left ? marginValue : "0 ";
557 this.element.style.margin = margings;
562 var dockSide = WebInspector.dockController.dockSide();
563 if (dockSide !== WebInspector.DockController.State.Undocked) {
564 if (this._setContentsInsetsId)
565 window.cancelAnimationFrame(this._setContentsInsetsId);
566 this._setContentsInsetsId = window.requestAnimationFrame(this._setContentsInsets.bind(this));
569 // FIXME: make drawer a view.
570 WebInspector.inspectorView._drawer.resize();
573 _setContentsInsets: function()
575 delete this._setContentsInsetsId;
577 var dockSide = WebInspector.dockController.dockSide();
578 var zoomFactor = WebInspector.zoomFactor();
579 var totalWidth = document.body.offsetWidth;
580 var totalHeight = document.body.offsetHeight;
581 var boundingRect = this.element.getBoundingClientRect();
583 InspectorFrontendHost.setContentsInsets(
584 Math.round(boundingRect.top * zoomFactor),
585 Math.round(boundingRect.left * zoomFactor),
586 Math.round((totalHeight - boundingRect.bottom) * zoomFactor),
587 Math.round((totalWidth - boundingRect.right) * zoomFactor));
590 __proto__: WebInspector.View.prototype
594 * @type {!WebInspector.InspectorView}
596 WebInspector.inspectorView;