1/* 2 * Copyright (C) 2011 Google Inc. 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 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. 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 31/** 32 * @constructor 33 * @extends {WebInspector.View} 34 */ 35WebInspector.InspectorView = function() 36{ 37 WebInspector.View.call(this); 38 this.markAsRoot(); 39 this.element.classList.add("fill", "vbox", "inspector-view"); 40 this.element.setAttribute("spellcheck", false); 41 42 this._splitView = new WebInspector.SplitView(false, "InspectorView.splitView", 300, 300); 43 this._splitView.setSecondIsSidebar(true); 44 this._splitView.setSidebarElementConstraints(150, 50); 45 this._splitView.setMainElementConstraints(50, 50); 46 WebInspector.dockController.addEventListener(WebInspector.DockController.Events.DockSideChanged, this._updateSplitView.bind(this)); 47 48 this._splitView.element.id = "inspector-split-view"; 49 this._splitView.show(this.element); 50 51 this._overlayView = new WebInspector.ViewWithResizeCallback(this._onOverlayResized.bind(this)); 52 this._overlayView.show(this._splitView.mainElement); 53 54 this._devtoolsElement = this._splitView.sidebarElement; 55 this._devtoolsElement.classList.add("vbox"); 56 57 this._tabbedPane = new WebInspector.TabbedPane(); 58 this._tabbedPane.setRetainTabsOrder(true); 59 this._tabbedPane.show(this._devtoolsElement); 60 61 this._toolbarElement = document.createElement("div"); 62 this._toolbarElement.className = "toolbar toolbar-background"; 63 var headerElement = this._tabbedPane.headerElement(); 64 headerElement.parentElement.insertBefore(this._toolbarElement, headerElement); 65 66 this._leftToolbarElement = this._toolbarElement.createChild("div", "toolbar-controls-left"); 67 this._toolbarElement.appendChild(headerElement); 68 this._rightToolbarElement = this._toolbarElement.createChild("div", "toolbar-controls-right"); 69 70 this._errorWarningCountElement = this._rightToolbarElement.createChild("div", "hidden"); 71 this._errorWarningCountElement.id = "error-warning-count"; 72 73 this._drawer = new WebInspector.Drawer(this); 74 this.appendToRightToolbar(this._drawer.toggleButtonElement()); 75 76 this._history = []; 77 this._historyIterator = -1; 78 document.addEventListener("keydown", this._keyDown.bind(this), false); 79 document.addEventListener("keypress", this._keyPress.bind(this), false); 80 this._panelDescriptors = {}; 81 82 // Windows and Mac have two different definitions of '[' and ']', so accept both of each. 83 this._openBracketIdentifiers = ["U+005B", "U+00DB"].keySet(); 84 this._closeBracketIdentifiers = ["U+005D", "U+00DD"].keySet(); 85 this._lastActivePanelSetting = WebInspector.settings.createSetting("lastActivePanel", "elements"); 86 87 this._updateSplitView(); 88} 89 90WebInspector.InspectorView.prototype = { 91 /** 92 * @param {!Element} element 93 */ 94 appendToLeftToolbar: function(element) 95 { 96 this._leftToolbarElement.appendChild(element); 97 }, 98 99 /** 100 * @param {!Element} element 101 */ 102 appendToRightToolbar: function(element) 103 { 104 this._rightToolbarElement.appendChild(element); 105 }, 106 107 /** 108 * @return {!WebInspector.Drawer} 109 */ 110 drawer: function() 111 { 112 return this._drawer; 113 }, 114 115 /** 116 * @return {!Element} 117 */ 118 devtoolsElement: function() 119 { 120 return this._devtoolsElement; 121 }, 122 123 /** 124 * @param {!WebInspector.PanelDescriptor} panelDescriptor 125 */ 126 addPanel: function(panelDescriptor) 127 { 128 var panelName = panelDescriptor.name(); 129 this._panelDescriptors[panelName] = panelDescriptor; 130 this._tabbedPane.appendTab(panelName, panelDescriptor.title(), new WebInspector.View()); 131 if (this._lastActivePanelSetting.get() === panelName) 132 this._tabbedPane.selectTab(panelName); 133 }, 134 135 /** 136 * @param {string} panelName 137 * @return {?WebInspector.Panel} 138 */ 139 panel: function(panelName) 140 { 141 var panelDescriptor = this._panelDescriptors[panelName]; 142 var panelOrder = this._tabbedPane.allTabs(); 143 if (!panelDescriptor && panelOrder.length) 144 panelDescriptor = this._panelDescriptors[panelOrder[0]]; 145 return panelDescriptor ? panelDescriptor.panel() : null; 146 }, 147 148 /** 149 * @param {string} panelName 150 * @return {?WebInspector.Panel} 151 */ 152 showPanel: function(panelName) 153 { 154 var panel = this.panel(panelName); 155 if (panel) 156 this.setCurrentPanel(panel); 157 return panel; 158 }, 159 160 /** 161 * @return {!WebInspector.Panel} 162 */ 163 currentPanel: function() 164 { 165 return this._currentPanel; 166 }, 167 168 showInitialPanel: function() 169 { 170 this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this); 171 this._tabSelected(); 172 this._drawer.showOnLoadIfNecessary(); 173 }, 174 175 _tabSelected: function() 176 { 177 var panelName = this._tabbedPane.selectedTabId; 178 var panel = this._panelDescriptors[this._tabbedPane.selectedTabId].panel(); 179 this._tabbedPane.changeTabView(panelName, panel); 180 181 this._currentPanel = panel; 182 this._lastActivePanelSetting.set(panel.name); 183 this._pushToHistory(panel.name); 184 WebInspector.userMetrics.panelShown(panel.name); 185 panel.focus(); 186 }, 187 188 /** 189 * @param {!WebInspector.Panel} x 190 */ 191 setCurrentPanel: function(x) 192 { 193 if (this._currentPanel === x) 194 return; 195 196 this._tabbedPane.changeTabView(x.name, x); 197 this._tabbedPane.selectTab(x.name); 198 }, 199 200 /** 201 * @param {string} id 202 */ 203 closeViewInDrawer: function(id) 204 { 205 return this._drawer.closeView(id); 206 }, 207 208 /** 209 * @param {string} id 210 * @param {string} title 211 * @param {!WebInspector.View} view 212 */ 213 showCloseableViewInDrawer: function(id, title, view) 214 { 215 this._drawer.showCloseableView(id, title, view); 216 }, 217 218 /** 219 * @param {string} id 220 * @param {string} title 221 * @param {!WebInspector.ViewFactory} factory 222 */ 223 registerViewInDrawer: function(id, title, factory) 224 { 225 this._drawer.registerView(id, title, factory); 226 }, 227 228 /** 229 * @param {string} id 230 */ 231 unregisterViewInDrawer: function(id) 232 { 233 this._drawer.unregisterView(id); 234 }, 235 236 /** 237 * @param {string} id 238 */ 239 showViewInDrawer: function(id) 240 { 241 this._drawer.showView(id); 242 }, 243 244 /** 245 * @return {string} 246 */ 247 selectedViewInDrawer: function() 248 { 249 return this._drawer.selectedViewId(); 250 }, 251 252 closeDrawer: function() 253 { 254 this._drawer.hide(); 255 }, 256 257 /** 258 * @return {!Element} 259 */ 260 defaultFocusedElement: function() 261 { 262 return this._currentPanel ? this._currentPanel.defaultFocusedElement() : null; 263 }, 264 265 _keyPress: function(event) 266 { 267 // BUG 104250: Windows 7 posts a WM_CHAR message upon the Ctrl+']' keypress. 268 // Any charCode < 32 is not going to be a valid keypress. 269 if (event.charCode < 32 && WebInspector.isWin()) 270 return; 271 clearTimeout(this._keyDownTimer); 272 delete this._keyDownTimer; 273 }, 274 275 _keyDown: function(event) 276 { 277 if (!WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event)) 278 return; 279 280 var keyboardEvent = /** @type {!KeyboardEvent} */ (event); 281 // Ctrl/Cmd + 1-9 should show corresponding panel. 282 var panelShortcutEnabled = WebInspector.settings.shortcutPanelSwitch.get(); 283 if (panelShortcutEnabled && !event.shiftKey && !event.altKey) { 284 var panelIndex = -1; 285 if (event.keyCode > 0x30 && event.keyCode < 0x3A) 286 panelIndex = event.keyCode - 0x31; 287 else if (event.keyCode > 0x60 && event.keyCode < 0x6A && keyboardEvent.location === KeyboardEvent.DOM_KEY_LOCATION_NUMPAD) 288 panelIndex = event.keyCode - 0x61; 289 if (panelIndex !== -1) { 290 var panelName = this._tabbedPane.allTabs()[panelIndex]; 291 if (panelName) { 292 this.showPanel(panelName); 293 event.consume(true); 294 } 295 return; 296 } 297 } 298 299 // BUG85312: On French AZERTY keyboards, AltGr-]/[ combinations (synonymous to Ctrl-Alt-]/[ on Windows) are used to enter ]/[, 300 // so for a ]/[-related keydown we delay the panel switch using a timer, to see if there is a keypress event following this one. 301 // If there is, we cancel the timer and do not consider this a panel switch. 302 if (!WebInspector.isWin() || (!this._openBracketIdentifiers[event.keyIdentifier] && !this._closeBracketIdentifiers[event.keyIdentifier])) { 303 this._keyDownInternal(event); 304 return; 305 } 306 307 this._keyDownTimer = setTimeout(this._keyDownInternal.bind(this, event), 0); 308 }, 309 310 _keyDownInternal: function(event) 311 { 312 if (this._openBracketIdentifiers[event.keyIdentifier]) { 313 var isRotateLeft = !event.shiftKey && !event.altKey; 314 if (isRotateLeft) { 315 var panelOrder = this._tabbedPane.allTabs(); 316 var index = panelOrder.indexOf(this.currentPanel().name); 317 index = (index === 0) ? panelOrder.length - 1 : index - 1; 318 this.showPanel(panelOrder[index]); 319 event.consume(true); 320 return; 321 } 322 323 var isGoBack = event.altKey; 324 if (isGoBack && this._canGoBackInHistory()) { 325 this._goBackInHistory(); 326 event.consume(true); 327 } 328 return; 329 } 330 331 if (this._closeBracketIdentifiers[event.keyIdentifier]) { 332 var isRotateRight = !event.shiftKey && !event.altKey; 333 if (isRotateRight) { 334 var panelOrder = this._tabbedPane.allTabs(); 335 var index = panelOrder.indexOf(this.currentPanel().name); 336 index = (index + 1) % panelOrder.length; 337 this.showPanel(panelOrder[index]); 338 event.consume(true); 339 return; 340 } 341 342 var isGoForward = event.altKey; 343 if (isGoForward && this._canGoForwardInHistory()) { 344 this._goForwardInHistory(); 345 event.consume(true); 346 } 347 return; 348 } 349 }, 350 351 _canGoBackInHistory: function() 352 { 353 return this._historyIterator > 0; 354 }, 355 356 _goBackInHistory: function() 357 { 358 this._inHistory = true; 359 this.setCurrentPanel(WebInspector.panels[this._history[--this._historyIterator]]); 360 delete this._inHistory; 361 }, 362 363 _canGoForwardInHistory: function() 364 { 365 return this._historyIterator < this._history.length - 1; 366 }, 367 368 _goForwardInHistory: function() 369 { 370 this._inHistory = true; 371 this.setCurrentPanel(WebInspector.panels[this._history[++this._historyIterator]]); 372 delete this._inHistory; 373 }, 374 375 _pushToHistory: function(panelName) 376 { 377 if (this._inHistory) 378 return; 379 380 this._history.splice(this._historyIterator + 1, this._history.length - this._historyIterator - 1); 381 if (!this._history.length || this._history[this._history.length - 1] !== panelName) 382 this._history.push(panelName); 383 this._historyIterator = this._history.length - 1; 384 }, 385 386 onResize: function() 387 { 388 // FIXME: make drawer a view. 389 this.doResize(); 390 this._drawer.resize(); 391 }, 392 393 _updateSplitView: function() 394 { 395 var dockSide = WebInspector.dockController.dockSide(); 396 if (WebInspector.queryParamsObject["overlayContents"] && dockSide !== WebInspector.DockController.State.Undocked) { 397 this._splitView.showBoth(); 398 var vertical = dockSide === WebInspector.DockController.State.DockedToRight; 399 this._splitView.setVertical(vertical); 400 if (vertical) { 401 this._splitView.uninstallResizer(this._tabbedPane.headerElement()); 402 this._splitView.installResizer(this._splitView.resizerElement()); 403 } else { 404 this._splitView.uninstallResizer(this._splitView.resizerElement()); 405 this._splitView.installResizer(this._tabbedPane.headerElement()); 406 } 407 } else { 408 this._splitView.showOnlySecond(); 409 } 410 }, 411 412 _onOverlayResized: function() 413 { 414 var dockSide = WebInspector.dockController.dockSide(); 415 if (WebInspector.queryParamsObject["overlayContents"] && dockSide !== WebInspector.DockController.State.Undocked) { 416 // Leave 3px room for resizer. 417 var bottom = this._splitView.isVertical() ? 0 : this._splitView.sidebarSize(); 418 var right = this._splitView.isVertical() ? this._splitView.sidebarSize() + 3 : 0; 419 InspectorFrontendHost.setContentsInsets(0, 0, bottom, right); 420 } 421 422 // FIXME: make drawer a view. 423 this._drawer.resize(); 424 }, 425 426 /** 427 * @param {number} errors 428 * @param {number} warnings 429 */ 430 setErrorAndWarningCounts: function(errors, warnings) 431 { 432 if (!errors && !warnings) { 433 this._errorWarningCountElement.classList.add("hidden"); 434 this._tabbedPane.headerResized(); 435 return; 436 } 437 438 this._errorWarningCountElement.classList.remove("hidden"); 439 this._errorWarningCountElement.removeChildren(); 440 441 if (errors) { 442 var errorImageElement = this._errorWarningCountElement.createChild("div", "error-icon-small"); 443 var errorElement = this._errorWarningCountElement.createChild("span"); 444 errorElement.id = "error-count"; 445 errorElement.textContent = errors; 446 } 447 448 if (warnings) { 449 var warningsImageElement = this._errorWarningCountElement.createChild("div", "warning-icon-small"); 450 var warningsElement = this._errorWarningCountElement.createChild("span"); 451 warningsElement.id = "warning-count"; 452 warningsElement.textContent = warnings; 453 } 454 455 if (errors) { 456 if (warnings) { 457 if (errors == 1) { 458 if (warnings == 1) 459 this._errorWarningCountElement.title = WebInspector.UIString("%d error, %d warning", errors, warnings); 460 else 461 this._errorWarningCountElement.title = WebInspector.UIString("%d error, %d warnings", errors, warnings); 462 } else if (warnings == 1) 463 this._errorWarningCountElement.title = WebInspector.UIString("%d errors, %d warning", errors, warnings); 464 else 465 this._errorWarningCountElement.title = WebInspector.UIString("%d errors, %d warnings", errors, warnings); 466 } else if (errors == 1) 467 this._errorWarningCountElement.title = WebInspector.UIString("%d error", errors); 468 else 469 this._errorWarningCountElement.title = WebInspector.UIString("%d errors", errors); 470 } else if (warnings == 1) 471 this._errorWarningCountElement.title = WebInspector.UIString("%d warning", warnings); 472 else if (warnings) 473 this._errorWarningCountElement.title = WebInspector.UIString("%d warnings", warnings); 474 else 475 this._errorWarningCountElement.title = null; 476 477 this._tabbedPane.headerResized(); 478 }, 479 480 __proto__: WebInspector.View.prototype 481}; 482 483/** 484 * @type {!WebInspector.InspectorView} 485 */ 486WebInspector.inspectorView; 487