2 * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2007 Matt Lilek (pewtermoose@gmail.com).
4 * Copyright (C) 2009 Joseph Pecoraro
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
16 * its contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 missingLocalizedStrings: {},
38 if (!("_platform" in this))
39 this._platform = InspectorFrontendHost.platform();
41 return this._platform;
46 if (!("_platformFlavor" in this))
47 this._platformFlavor = this._detectPlatformFlavor();
49 return this._platformFlavor;
52 _detectPlatformFlavor: function()
54 const userAgent = navigator.userAgent;
56 if (this.platform === "windows") {
57 var match = userAgent.match(/Windows NT (\d+)\.(?:\d+)/);
58 if (match && match[1] >= 6)
59 return WebInspector.PlatformFlavor.WindowsVista;
61 } else if (this.platform === "mac") {
62 var match = userAgent.match(/Mac OS X\s*(?:(\d+)_(\d+))?/);
63 if (!match || match[1] != 10)
64 return WebInspector.PlatformFlavor.MacSnowLeopard;
65 switch (Number(match[2])) {
67 return WebInspector.PlatformFlavor.MacTiger;
69 return WebInspector.PlatformFlavor.MacLeopard;
72 return WebInspector.PlatformFlavor.MacSnowLeopard;
81 if (!("_port" in this))
82 this._port = InspectorFrontendHost.port();
87 get previousFocusElement()
89 return this._previousFocusElement;
92 get currentFocusElement()
94 return this._currentFocusElement;
97 set currentFocusElement(x)
99 if (this._currentFocusElement !== x)
100 this._previousFocusElement = this._currentFocusElement;
101 this._currentFocusElement = x;
103 if (this._currentFocusElement) {
104 this._currentFocusElement.focus();
106 // Make a caret selection inside the new element if there isn't a range selection and
107 // there isn't already a caret selection inside.
108 var selection = window.getSelection();
109 if (selection.isCollapsed && !this._currentFocusElement.isInsertionCaretInside()) {
110 var selectionRange = this._currentFocusElement.ownerDocument.createRange();
111 selectionRange.setStart(this._currentFocusElement, 0);
112 selectionRange.setEnd(this._currentFocusElement, 0);
114 selection.removeAllRanges();
115 selection.addRange(selectionRange);
117 } else if (this._previousFocusElement)
118 this._previousFocusElement.blur();
121 currentPanel: function()
123 return this._currentPanel;
126 setCurrentPanel: function(x)
128 if (this._currentPanel === x)
131 if (this._currentPanel)
132 this._currentPanel.hide();
134 this._currentPanel = x;
138 WebInspector.searchController.activePanelChanged();
140 for (var panelName in WebInspector.panels) {
141 if (WebInspector.panels[panelName] === x) {
142 WebInspector.settings.lastActivePanel.set(panelName);
143 this._panelHistory.setPanel(panelName);
144 WebInspector.userMetrics.panelShown(panelName);
149 _createPanels: function()
151 if (WebInspector.WorkerManager.isWorkerFrontend()) {
152 this.panels.scripts = new WebInspector.ScriptsPanel(this.debuggerPresentationModel);
153 this.panels.console = new WebInspector.ConsolePanel();
156 var hiddenPanels = (InspectorFrontendHost.hiddenPanels() || "").split(',');
157 if (hiddenPanels.indexOf("elements") === -1)
158 this.panels.elements = new WebInspector.ElementsPanel();
159 if (hiddenPanels.indexOf("resources") === -1)
160 this.panels.resources = new WebInspector.ResourcesPanel();
161 if (hiddenPanels.indexOf("network") === -1)
162 this.panels.network = new WebInspector.NetworkPanel();
163 if (hiddenPanels.indexOf("scripts") === -1)
164 this.panels.scripts = new WebInspector.ScriptsPanel(this.debuggerPresentationModel);
165 if (hiddenPanels.indexOf("timeline") === -1)
166 this.panels.timeline = new WebInspector.TimelinePanel();
167 if (hiddenPanels.indexOf("profiles") === -1)
168 this.panels.profiles = new WebInspector.ProfilesPanel();
169 if (hiddenPanels.indexOf("audits") === -1)
170 this.panels.audits = new WebInspector.AuditsPanel();
171 if (hiddenPanels.indexOf("console") === -1)
172 this.panels.console = new WebInspector.ConsolePanel();
175 _createGlobalStatusBarItems: function()
177 this._dockToggleButton = new WebInspector.StatusBarButton(this._dockButtonTitle(), "dock-status-bar-item");
178 this._dockToggleButton.addEventListener("click", this._toggleAttach.bind(this), false);
179 this._dockToggleButton.toggled = !this.attached;
181 this._settingsButton = new WebInspector.StatusBarButton(WebInspector.UIString("Settings"), "settings-status-bar-item");
182 this._settingsButton.addEventListener("click", this._toggleSettings.bind(this), false);
184 var anchoredStatusBar = document.getElementById("anchored-status-bar-items");
185 anchoredStatusBar.appendChild(this._dockToggleButton.element);
186 anchoredStatusBar.appendChild(this.consoleView.toggleConsoleButton.element);
187 if (this.panels.elements)
188 anchoredStatusBar.appendChild(this.panels.elements.nodeSearchButton.element);
190 anchoredStatusBar.appendChild(this._settingsButton.element);
193 _dockButtonTitle: function()
195 return this.attached ? WebInspector.UIString("Undock into separate window.") : WebInspector.UIString("Dock to main window.");
198 _toggleAttach: function()
200 if (!this._attached) {
201 InspectorFrontendHost.requestAttachWindow();
202 WebInspector.userMetrics.WindowDocked.record();
204 InspectorFrontendHost.requestDetachWindow();
205 WebInspector.userMetrics.WindowUndocked.record();
209 _toggleSettings: function()
211 this._settingsButton.toggled = !this._settingsButton.toggled;
212 if (this._settingsButton.toggled)
213 this._showSettingsScreen();
215 this._hideSettingsScreen();
218 _showShortcutsScreen: function()
220 this._hideSettingsScreen();
221 WebInspector.shortcutsScreen.show();
224 _hideShortcutsScreen: function()
226 WebInspector.shortcutsScreen.hide();
229 _showSettingsScreen: function()
231 this._hideShortcutsScreen();
234 this._settingsButton.toggled = false;
235 delete this._settingsScreen;
238 if (!this._settingsScreen) {
239 this._settingsScreen = new WebInspector.SettingsScreen();
240 this._settingsScreen.show(onhide.bind(this));
244 _hideSettingsScreen: function()
246 if (this._settingsScreen) {
247 this._settingsScreen.hide();
248 this._settingsButton.toggled = false;
249 delete this._settingsScreen;
255 return this._attached;
260 if (this._attached === x)
265 var body = document.body;
268 body.removeStyleClass("detached");
269 body.addStyleClass("attached");
271 body.removeStyleClass("attached");
272 body.addStyleClass("detached");
275 if (this._dockToggleButton) {
276 this._dockToggleButton.title = this._dockButtonTitle();
277 this._dockToggleButton.toggled = !x;
280 // This may be called before onLoadedDone, hence the bulk of inspector objects may
281 // not be created yet.
282 if (WebInspector.toolbar)
283 WebInspector.toolbar.attached = x;
285 if (WebInspector.searchController)
286 WebInspector.searchController.updateSearchLabel();
288 if (WebInspector.drawer)
289 WebInspector.drawer.updateHeight();
292 _updateErrorAndWarningCounts: function()
294 var errorWarningElement = document.getElementById("error-warning-count");
295 if (!errorWarningElement)
298 var errors = WebInspector.console.errors;
299 var warnings = WebInspector.console.warnings;
300 if (!errors && !warnings) {
301 errorWarningElement.addStyleClass("hidden");
305 errorWarningElement.removeStyleClass("hidden");
307 errorWarningElement.removeChildren();
310 var errorElement = document.createElement("span");
311 errorElement.id = "error-count";
312 errorElement.textContent = errors;
313 errorWarningElement.appendChild(errorElement);
317 var warningsElement = document.createElement("span");
318 warningsElement.id = "warning-count";
319 warningsElement.textContent = warnings;
320 errorWarningElement.appendChild(warningsElement);
327 errorWarningElement.title = WebInspector.UIString("%d error, %d warning", errors, warnings);
329 errorWarningElement.title = WebInspector.UIString("%d error, %d warnings", errors, warnings);
330 } else if (warnings == 1)
331 errorWarningElement.title = WebInspector.UIString("%d errors, %d warning", errors, warnings);
333 errorWarningElement.title = WebInspector.UIString("%d errors, %d warnings", errors, warnings);
334 } else if (errors == 1)
335 errorWarningElement.title = WebInspector.UIString("%d error", errors);
337 errorWarningElement.title = WebInspector.UIString("%d errors", errors);
338 } else if (warnings == 1)
339 errorWarningElement.title = WebInspector.UIString("%d warning", warnings);
341 errorWarningElement.title = WebInspector.UIString("%d warnings", warnings);
343 errorWarningElement.title = null;
346 buildHighlightConfig: function(mode)
348 mode = mode || "all";
349 var highlightConfig = { showInfo: mode === "all" };
350 if (mode === "all" || mode === "content") {
351 highlightConfig.contentColor = WebInspector.Color.PageHighlight.Content.toProtocolRGBA();
352 highlightConfig.contentOutlineColor = WebInspector.Color.PageHighlight.ContentOutline.toProtocolRGBA();
355 if (mode === "all" || mode === "padding") {
356 highlightConfig.paddingColor = WebInspector.Color.PageHighlight.Padding.toProtocolRGBA();
357 highlightConfig.paddingOutlineColor = WebInspector.Color.PageHighlight.PaddingOutline.toProtocolRGBA();
360 if (mode === "all" || mode === "border") {
361 highlightConfig.borderColor = WebInspector.Color.PageHighlight.Border.toProtocolRGBA();
362 highlightConfig.borderOutlineColor = WebInspector.Color.PageHighlight.BorderOutline.toProtocolRGBA();
365 if (mode === "all" || mode === "margin") {
366 highlightConfig.marginColor = WebInspector.Color.PageHighlight.Margin.toProtocolRGBA();
367 highlightConfig.marginOutlineColor = WebInspector.Color.PageHighlight.MarginOutline.toProtocolRGBA();
370 return highlightConfig;
373 highlightDOMNode: function(nodeId, mode)
375 if ("_hideDOMNodeHighlightTimeout" in this) {
376 clearTimeout(this._hideDOMNodeHighlightTimeout);
377 delete this._hideDOMNodeHighlightTimeout;
380 if (this._highlightedDOMNodeId === nodeId)
383 this._highlightedDOMNodeId = nodeId;
385 DOMAgent.highlightNode(nodeId, this.buildHighlightConfig(mode));
387 DOMAgent.hideHighlight();
390 highlightDOMNodeForTwoSeconds: function(nodeId)
392 this.highlightDOMNode(nodeId);
393 this._hideDOMNodeHighlightTimeout = setTimeout(this.highlightDOMNode.bind(this, 0), 2000);
396 wireElementWithDOMNode: function(element, nodeId)
398 element.addEventListener("click", this._updateFocusedNode.bind(this, nodeId), false);
399 element.addEventListener("mouseover", this.highlightDOMNode.bind(this, nodeId, "all"), false);
400 element.addEventListener("mouseout", this.highlightDOMNode.bind(this, 0), false);
403 _updateFocusedNode: function(nodeId)
405 this.setCurrentPanel(this.panels.elements);
406 this.panels.elements.updateFocusedNode(nodeId);
409 networkResourceById: function(id)
411 return this.panels.network.resourceById(id);
414 forAllResources: function(callback)
416 WebInspector.resourceTreeModel.forAllResources(callback);
419 resourceForURL: function(url)
421 return this.resourceTreeModel.resourceForURL(url);
424 openLinkExternallyLabel: function()
426 return WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Open link in new tab" : "Open Link in New Tab");
429 copyLinkAddressLabel: function()
431 return WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy link address" : "Copy Link Address");
435 WebInspector.Events = {
436 InspectorClosing: "InspectorClosing"
439 {(function parseQueryParameters()
441 WebInspector.queryParamsObject = {};
442 var queryParams = window.location.search;
445 var params = queryParams.substring(1).split("&");
446 for (var i = 0; i < params.length; ++i) {
447 var pair = params[i].split("=");
448 WebInspector.queryParamsObject[pair[0]] = pair[1];
452 WebInspector.loaded = function()
454 if ("page" in WebInspector.queryParamsObject) {
455 var page = WebInspector.queryParamsObject.page;
456 var host = "host" in WebInspector.queryParamsObject ? WebInspector.queryParamsObject.host : window.location.host;
457 WebInspector.socket = new WebSocket("ws://" + host + "/devtools/page/" + page);
458 WebInspector.socket.onmessage = function(message) { InspectorBackend.dispatch(message.data); }
459 WebInspector.socket.onerror = function(error) { console.error(error); }
460 WebInspector.socket.onopen = function() {
461 InspectorFrontendHost.sendMessageToBackend = WebInspector.socket.send.bind(WebInspector.socket);
462 WebInspector.doLoadedDone();
466 WebInspector.WorkerManager.loaded();
467 WebInspector.doLoadedDone();
470 WebInspector.doLoadedDone = function()
472 InspectorFrontendHost.loaded();
474 this.notifications = new WebInspector.Object();
476 var platform = WebInspector.platform;
477 document.body.addStyleClass("platform-" + platform);
478 var flavor = WebInspector.platformFlavor;
480 document.body.addStyleClass("platform-" + flavor);
481 var port = WebInspector.port;
482 document.body.addStyleClass("port-" + port);
483 if (WebInspector.socket)
484 document.body.addStyleClass("remote");
486 this._registerShortcuts();
488 // set order of some sections explicitly
489 WebInspector.shortcutsScreen.section(WebInspector.UIString("Console"));
490 WebInspector.shortcutsScreen.section(WebInspector.UIString("Elements Panel"));
492 this.console = new WebInspector.ConsoleModel();
493 this.console.addEventListener(WebInspector.ConsoleModel.Events.ConsoleCleared, this._updateErrorAndWarningCounts, this);
494 this.console.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, this._updateErrorAndWarningCounts, this);
495 this.console.addEventListener(WebInspector.ConsoleModel.Events.RepeatCountUpdated, this._updateErrorAndWarningCounts, this);
497 this.drawer = new WebInspector.Drawer();
498 this.consoleView = new WebInspector.ConsoleView(this.drawer);
499 this.drawer.visibleView = this.consoleView;
501 this.networkManager = new WebInspector.NetworkManager();
502 this.resourceTreeModel = new WebInspector.ResourceTreeModel();
503 this.networkLog = new WebInspector.NetworkLog();
504 this.domAgent = new WebInspector.DOMAgent();
505 new WebInspector.JavaScriptContextManager(this.resourceTreeModel, this.consoleView);
507 InspectorBackend.registerInspectorDispatcher(this);
509 this.cssModel = new WebInspector.CSSStyleModel();
510 this.debuggerModel = new WebInspector.DebuggerModel();
511 this.debuggerPresentationModel = new WebInspector.DebuggerPresentationModel();
513 this.searchController = new WebInspector.SearchController();
515 if (Preferences.nativeInstrumentationEnabled)
516 this.domBreakpointsSidebarPane = new WebInspector.DOMBreakpointsSidebarPane();
519 this._createPanels();
520 this._createGlobalStatusBarItems();
522 this._panelHistory = new WebInspector.PanelHistory();
523 this.toolbar = new WebInspector.Toolbar();
524 this.toolbar.attached = WebInspector.attached;
526 this.panelOrder = [];
527 for (var panelName in this.panels)
528 this.addPanel(this.panels[panelName]);
530 this.addMainEventListeners(document);
532 window.addEventListener("resize", this.windowResize.bind(this), true);
534 var errorWarningCount = document.getElementById("error-warning-count");
535 errorWarningCount.addEventListener("click", this.showConsole.bind(this), false);
536 this._updateErrorAndWarningCounts();
538 this.extensionServer.initExtensions();
540 this.console.enableAgent();
542 DatabaseAgent.enable();
543 DOMStorageAgent.enable();
545 WebInspector.showPanel(WebInspector.settings.lastActivePanel.get());
547 function propertyNamesCallback(error, names)
550 WebInspector.cssNameCompletions = new WebInspector.CSSCompletions(names);
552 // As a DOMAgent method, this needs to happen after the frontend has loaded and the agent is available.
553 CSSAgent.getSupportedCSSProperties(propertyNamesCallback);
556 WebInspector.addPanel = function(panel)
558 this.panelOrder.push(panel);
559 this.toolbar.addPanel(panel);
562 var windowLoaded = function()
564 var localizedStringsURL = InspectorFrontendHost.localizedStringsURL();
565 if (localizedStringsURL) {
566 var localizedStringsScriptElement = document.createElement("script");
567 localizedStringsScriptElement.addEventListener("load", WebInspector.loaded.bind(WebInspector), false);
568 localizedStringsScriptElement.type = "text/javascript";
569 localizedStringsScriptElement.src = localizedStringsURL;
570 document.head.appendChild(localizedStringsScriptElement);
572 WebInspector.loaded();
574 WebInspector.setAttachedWindow(WebInspector.queryParamsObject.docked === "true");
576 window.removeEventListener("DOMContentLoaded", windowLoaded, false);
580 window.addEventListener("DOMContentLoaded", windowLoaded, false);
582 // We'd like to enforce asynchronous interaction between the inspector controller and the frontend.
583 // It is needed to prevent re-entering the backend code.
584 // Also, native dispatches do not guarantee setTimeouts to be serialized, so we
585 // enforce serialization using 'messagesToDispatch' queue. It is also important that JSC debugger
586 // tests require that each command was dispatch within individual timeout callback, so we don't batch them.
588 var messagesToDispatch = [];
590 WebInspector.dispatchQueueIsEmpty = function() {
591 return messagesToDispatch.length == 0;
594 WebInspector.dispatch = function(message) {
595 messagesToDispatch.push(message);
596 setTimeout(function() {
597 InspectorBackend.dispatch(messagesToDispatch.shift());
601 WebInspector.dispatchMessageFromBackend = function(messageObject)
603 WebInspector.dispatch(messageObject);
606 WebInspector.windowResize = function(event)
608 if (this.currentPanel())
609 this.currentPanel().doResize();
610 this.drawer.doResize();
611 this.toolbar.resize();
614 WebInspector.windowFocused = function(event)
616 // Fires after blur, so when focusing on either the main inspector
617 // or an <iframe> within the inspector we should always remove the
619 if (event.target.document.nodeType === Node.DOCUMENT_NODE)
620 document.body.removeStyleClass("inactive");
623 WebInspector.windowBlurred = function(event)
625 // Leaving the main inspector or an <iframe> within the inspector.
626 // We can add "inactive" now, and if we are moving the focus to another
627 // part of the inspector then windowFocused will correct this.
628 if (event.target.document.nodeType === Node.DOCUMENT_NODE)
629 document.body.addStyleClass("inactive");
632 WebInspector.focusChanged = function(event)
634 this.currentFocusElement = event.target;
637 WebInspector.setAttachedWindow = function(attached)
639 this.attached = attached;
642 WebInspector.close = function(event)
646 this._isClosing = true;
647 this.notifications.dispatchEventToListeners(WebInspector.Events.InspectorClosing);
648 InspectorFrontendHost.closeWindow();
651 WebInspector.disconnectFromBackend = function()
653 InspectorFrontendHost.disconnectFromBackend();
656 WebInspector.documentClick = function(event)
658 var anchor = event.target.enclosingNodeOrSelfWithNodeName("a");
659 if (!anchor || anchor.target === "_blank")
662 // Prevent the link from navigating, since we don't do any navigation by following links normally.
663 event.preventDefault();
664 event.stopPropagation();
666 function followLink()
668 if (WebInspector._showAnchorLocation(anchor))
671 const profileMatch = WebInspector.ProfileType.URLRegExp.exec(anchor.href);
673 WebInspector.showProfileForURL(anchor.href);
677 var parsedURL = anchor.href.asParsedURL();
678 if (parsedURL && parsedURL.scheme === "webkit-link-action") {
679 if (parsedURL.host === "show-panel") {
680 var panel = parsedURL.path.substring(1);
681 if (WebInspector.panels[panel])
682 WebInspector.showPanel(panel);
687 WebInspector.showPanel("resources");
690 if (WebInspector.followLinkTimeout)
691 clearTimeout(WebInspector.followLinkTimeout);
693 if (anchor.preventFollowOnDoubleClick) {
694 // Start a timeout if this is the first click, if the timeout is canceled
695 // before it fires, then a double clicked happened or another link was clicked.
696 if (event.detail === 1)
697 WebInspector.followLinkTimeout = setTimeout(followLink, 333);
704 WebInspector.openResource = function(resourceURL, inResourcesPanel)
706 var resource = WebInspector.resourceForURL(resourceURL);
707 if (inResourcesPanel && resource) {
708 WebInspector.panels.resources.showResource(resource);
709 WebInspector.showPanel("resources");
711 PageAgent.open(resourceURL, true);
714 WebInspector._registerShortcuts = function()
716 var shortcut = WebInspector.KeyboardShortcut;
717 var section = WebInspector.shortcutsScreen.section(WebInspector.UIString("All Panels"));
719 shortcut.shortcutToString("]", shortcut.Modifiers.CtrlOrMeta),
720 shortcut.shortcutToString("[", shortcut.Modifiers.CtrlOrMeta)
722 section.addRelatedKeys(keys, WebInspector.UIString("Next/previous panel"));
723 section.addKey(shortcut.shortcutToString(shortcut.Keys.Esc), WebInspector.UIString("Toggle console"));
724 section.addKey(shortcut.shortcutToString("f", shortcut.Modifiers.CtrlOrMeta), WebInspector.UIString("Search"));
725 if (WebInspector.isMac()) {
727 shortcut.shortcutToString("g", shortcut.Modifiers.Meta),
728 shortcut.shortcutToString("g", shortcut.Modifiers.Meta | shortcut.Modifiers.Shift)
730 section.addRelatedKeys(keys, WebInspector.UIString("Find next/previous"));
733 var goToShortcut = WebInspector.GoToLineDialog.createShortcut();
734 section.addKey(goToShortcut.name, WebInspector.UIString("Go to Line"));
737 WebInspector.documentKeyDown = function(event)
739 var isInputElement = event.target.nodeName === "INPUT";
740 var isInEditMode = event.target.enclosingNodeOrSelfWithClass("text-prompt") || WebInspector.isEditingAnyField();
741 const helpKey = WebInspector.isMac() ? "U+003F" : "U+00BF"; // "?" for both platforms
743 if (event.keyIdentifier === "F1" ||
744 (event.keyIdentifier === helpKey && event.shiftKey && (!isInEditMode && !isInputElement || event.metaKey))) {
745 this._showShortcutsScreen();
746 event.stopPropagation();
747 event.preventDefault();
751 if (this.currentFocusElement && this.currentFocusElement.handleKeyEvent) {
752 this.currentFocusElement.handleKeyEvent(event);
754 event.preventDefault();
759 if (this.currentPanel()) {
760 this.currentPanel().handleShortcut(event);
762 event.preventDefault();
767 WebInspector.searchController.handleShortcut(event);
769 event.preventDefault();
773 if (WebInspector.isEditingAnyField())
776 var isMac = WebInspector.isMac();
777 switch (event.keyIdentifier) {
779 var isBackKey = !isInEditMode && (isMac ? event.metaKey : event.ctrlKey);
780 if (isBackKey && this._panelHistory.canGoBack()) {
781 this._panelHistory.goBack();
782 event.preventDefault();
787 var isForwardKey = !isInEditMode && (isMac ? event.metaKey : event.ctrlKey);
788 if (isForwardKey && this._panelHistory.canGoForward()) {
789 this._panelHistory.goForward();
790 event.preventDefault();
794 case "U+001B": // Escape key
795 event.preventDefault();
796 if (this.drawer.fullPanel)
799 this.drawer.visible = !this.drawer.visible;
802 // Windows and Mac have two different definitions of [, so accept both.
804 case "U+00DB": // [ key
806 var isRotateLeft = event.metaKey && !event.shiftKey && !event.ctrlKey && !event.altKey;
808 var isRotateLeft = event.ctrlKey && !event.shiftKey && !event.metaKey && !event.altKey;
811 var index = this.panelOrder.indexOf(this.currentPanel());
812 index = (index === 0) ? this.panelOrder.length - 1 : index - 1;
813 this.panelOrder[index].toolbarItem.click();
814 event.preventDefault();
819 // Windows and Mac have two different definitions of ], so accept both.
821 case "U+00DD": // ] key
823 var isRotateRight = event.metaKey && !event.shiftKey && !event.ctrlKey && !event.altKey;
825 var isRotateRight = event.ctrlKey && !event.shiftKey && !event.metaKey && !event.altKey;
828 var index = this.panelOrder.indexOf(this.currentPanel());
829 index = (index + 1) % this.panelOrder.length;
830 this.panelOrder[index].toolbarItem.click();
831 event.preventDefault();
836 case "U+0052": // R key
837 if ((event.metaKey && isMac) || (event.ctrlKey && !isMac)) {
838 PageAgent.reload(event.shiftKey);
839 event.preventDefault();
844 PageAgent.reload(event.ctrlKey || event.shiftKey);
845 event.preventDefault();
851 WebInspector.documentCanCopy = function(event)
853 if (this.currentPanel() && this.currentPanel().handleCopyEvent)
854 event.preventDefault();
857 WebInspector.documentCopy = function(event)
859 if (this.currentPanel() && this.currentPanel().handleCopyEvent)
860 this.currentPanel().handleCopyEvent(event);
863 WebInspector.contextMenuEventFired = function(event)
865 if (event.handled || event.target.hasStyleClass("popup-glasspane"))
866 event.preventDefault();
869 WebInspector.animateStyle = function(animations, duration, callback)
873 var hasCompleted = false;
875 const intervalDuration = (1000 / 30); // 30 frames per second.
876 const animationsLength = animations.length;
877 const propertyUnit = {opacity: ""};
878 const defaultUnit = "px";
880 function cubicInOut(t, b, c, d)
882 if ((t/=d/2) < 1) return c/2*t*t*t + b;
883 return c/2*((t-=2)*t*t + 2) + b;
886 // Pre-process animations.
887 for (var i = 0; i < animationsLength; ++i) {
888 var animation = animations[i];
889 var element = null, start = null, end = null, key = null;
890 for (key in animation) {
891 if (key === "element")
892 element = animation[key];
893 else if (key === "start")
894 start = animation[key];
895 else if (key === "end")
896 end = animation[key];
899 if (!element || !end)
903 var computedStyle = element.ownerDocument.defaultView.getComputedStyle(element);
906 start[key] = parseInt(computedStyle.getPropertyValue(key));
907 animation.start = start;
910 element.style.setProperty(key, start[key] + (key in propertyUnit ? propertyUnit[key] : defaultUnit));
913 function animateLoop()
916 complete += intervalDuration;
917 var next = complete + intervalDuration;
919 // Make style changes.
920 for (var i = 0; i < animationsLength; ++i) {
921 var animation = animations[i];
922 var element = animation.element;
923 var start = animation.start;
924 var end = animation.end;
925 if (!element || !end)
928 var style = element.style;
930 var endValue = end[key];
931 if (next < duration) {
932 var startValue = start[key];
933 var newValue = cubicInOut(complete, startValue, endValue - startValue, duration);
934 style.setProperty(key, newValue + (key in propertyUnit ? propertyUnit[key] : defaultUnit));
936 style.setProperty(key, endValue + (key in propertyUnit ? propertyUnit[key] : defaultUnit));
941 if (complete >= duration) {
943 clearInterval(interval);
949 function forceComplete()
960 clearInterval(interval);
963 interval = setInterval(animateLoop, intervalDuration);
966 forceComplete: forceComplete
970 WebInspector.elementDragStart = function(element, dividerDrag, elementDragEnd, event, cursor)
972 if (this._elementDraggingEventListener || this._elementEndDraggingEventListener)
973 this.elementDragEnd(event);
975 this._elementDraggingEventListener = dividerDrag;
976 this._elementEndDraggingEventListener = elementDragEnd;
978 var targetDocument = event.target.ownerDocument;
979 targetDocument.addEventListener("mousemove", dividerDrag, true);
980 targetDocument.addEventListener("mouseup", elementDragEnd, true);
982 targetDocument.body.style.cursor = cursor;
984 event.preventDefault();
987 WebInspector.elementDragEnd = function(event)
989 var targetDocument = event.target.ownerDocument;
990 targetDocument.removeEventListener("mousemove", this._elementDraggingEventListener, true);
991 targetDocument.removeEventListener("mouseup", this._elementEndDraggingEventListener, true);
993 targetDocument.body.style.removeProperty("cursor");
995 delete this._elementDraggingEventListener;
996 delete this._elementEndDraggingEventListener;
998 event.preventDefault();
1001 WebInspector.toggleSearchingForNode = function()
1003 if (this.panels.elements) {
1004 this.showPanel("elements");
1005 this.panels.elements.toggleSearchingForNode();
1009 WebInspector.showConsole = function()
1011 this.drawer.showView(this.consoleView);
1014 WebInspector.showPanel = function(panel)
1016 if (!(panel in this.panels)) {
1017 if (WebInspector.WorkerManager.isWorkerFrontend())
1022 this.setCurrentPanel(this.panels[panel]);
1025 WebInspector.startUserInitiatedDebugging = function()
1027 this.setCurrentPanel(this.panels.scripts);
1028 WebInspector.debuggerModel.enableDebugger();
1031 WebInspector.reset = function()
1033 this.debuggerModel.reset();
1034 for (var panelName in this.panels) {
1035 var panel = this.panels[panelName];
1036 if ("reset" in panel)
1040 this.highlightDOMNode(0);
1042 if (!WebInspector.settings.preserveConsoleLog.get())
1043 this.console.clearMessages();
1044 this.extensionServer.notifyInspectorReset();
1045 if (this.workerManager)
1046 this.workerManager.reset();
1049 WebInspector.bringToFront = function()
1051 InspectorFrontendHost.bringToFront();
1054 WebInspector.didCreateWorker = function()
1056 var workersPane = WebInspector.panels.scripts.sidebarPanes.workers;
1058 workersPane.addWorker.apply(workersPane, arguments);
1061 WebInspector.didDestroyWorker = function()
1063 var workersPane = WebInspector.panels.scripts.sidebarPanes.workers;
1065 workersPane.removeWorker.apply(workersPane, arguments);
1068 WebInspector.log = function(message, messageLevel)
1070 // remember 'this' for setInterval() callback
1073 // return indication if we can actually log a message
1074 function isLogAvailable()
1076 return WebInspector.ConsoleMessage && WebInspector.RemoteObject && self.console;
1079 // flush the queue of pending messages
1080 function flushQueue()
1082 var queued = WebInspector.log.queued;
1086 for (var i = 0; i < queued.length; ++i)
1087 logMessage(queued[i]);
1089 delete WebInspector.log.queued;
1092 // flush the queue if it console is available
1093 // - this function is run on an interval
1094 function flushQueueIfAvailable()
1096 if (!isLogAvailable())
1099 clearInterval(WebInspector.log.interval);
1100 delete WebInspector.log.interval;
1105 // actually log the message
1106 function logMessage(message)
1108 var repeatCount = 1;
1109 if (message == WebInspector.log.lastMessage)
1110 repeatCount = WebInspector.log.repeatCount + 1;
1112 WebInspector.log.lastMessage = message;
1113 WebInspector.log.repeatCount = repeatCount;
1115 // ConsoleMessage expects a proxy object
1116 message = WebInspector.RemoteObject.fromPrimitiveValue(message);
1119 var msg = new WebInspector.ConsoleMessage(
1120 WebInspector.ConsoleMessage.MessageSource.Other,
1121 WebInspector.ConsoleMessage.MessageType.Log,
1122 messageLevel || WebInspector.ConsoleMessage.MessageLevel.Debug,
1130 self.console.addMessage(msg);
1133 // if we can't log the message, queue it
1134 if (!isLogAvailable()) {
1135 if (!WebInspector.log.queued)
1136 WebInspector.log.queued = [];
1138 WebInspector.log.queued.push(message);
1140 if (!WebInspector.log.interval)
1141 WebInspector.log.interval = setInterval(flushQueueIfAvailable, 1000);
1146 // flush the pending queue if any
1150 logMessage(message);
1153 WebInspector.inspect = function(payload, hints)
1155 var object = WebInspector.RemoteObject.fromPayload(payload);
1156 if (object.subtype === "node") {
1157 // Request node from backend and focus it.
1158 object.pushNodeToFrontend(WebInspector.updateFocusedNode.bind(WebInspector), object.release.bind(object));
1162 if (hints.databaseId) {
1163 WebInspector.setCurrentPanel(WebInspector.panels.resources);
1164 WebInspector.panels.resources.selectDatabase(hints.databaseId);
1165 } else if (hints.domStorageId) {
1166 WebInspector.setCurrentPanel(WebInspector.panels.resources);
1167 WebInspector.panels.resources.selectDOMStorage(hints.domStorageId);
1173 WebInspector.updateFocusedNode = function(nodeId)
1175 this._updateFocusedNode(nodeId);
1176 this.highlightDOMNodeForTwoSeconds(nodeId);
1179 WebInspector.displayNameForURL = function(url)
1184 var resource = this.resourceForURL(url);
1186 return resource.displayName;
1188 if (!WebInspector.mainResource)
1189 return url.trimURL("");
1191 var lastPathComponent = WebInspector.mainResource.lastPathComponent;
1192 var index = WebInspector.mainResource.url.indexOf(lastPathComponent);
1193 if (index !== -1 && index + lastPathComponent.length === WebInspector.mainResource.url.length) {
1194 var baseURL = WebInspector.mainResource.url.substring(0, index);
1195 if (url.indexOf(baseURL) === 0)
1196 return url.substring(index);
1199 return url.trimURL(WebInspector.mainResource.domain);
1202 WebInspector._showAnchorLocation = function(anchor)
1204 var preferedPanel = this.panels[anchor.getAttribute("preferred_panel") || "resources"];
1205 if (WebInspector._showAnchorLocationInPanel(anchor, preferedPanel))
1207 if (preferedPanel !== this.panels.resources && WebInspector._showAnchorLocationInPanel(anchor, this.panels.resources))
1212 WebInspector._showAnchorLocationInPanel = function(anchor, panel)
1214 if (!panel.canShowAnchorLocation(anchor))
1217 // FIXME: support webkit-html-external-link links here.
1218 if (anchor.hasStyleClass("webkit-html-external-link")) {
1219 anchor.removeStyleClass("webkit-html-external-link");
1220 anchor.addStyleClass("webkit-html-resource-link");
1223 this.setCurrentPanel(panel);
1225 this.drawer.immediatelyFinishAnimation();
1226 this.currentPanel().showAnchorLocation(anchor);
1230 WebInspector.linkifyStringAsFragment = function(string)
1232 var container = document.createDocumentFragment();
1233 var linkStringRegEx = /(?:[a-zA-Z][a-zA-Z0-9+.-]{2,}:\/\/|www\.)[\w$\-_+*'=\|\/\\(){}[\]%@&#~,:;.!?]{2,}[\w$\-_+*=\|\/\\({%@&#~]/;
1234 var lineColumnRegEx = /:(\d+)(:(\d+))?$/;
1237 var linkString = linkStringRegEx.exec(string);
1241 linkString = linkString[0];
1242 var title = linkString;
1243 var linkIndex = string.indexOf(linkString);
1244 var nonLink = string.substring(0, linkIndex);
1245 container.appendChild(document.createTextNode(nonLink));
1247 var profileStringMatches = WebInspector.ProfileType.URLRegExp.exec(title);
1248 if (profileStringMatches)
1249 title = WebInspector.panels.profiles.displayTitleForProfileLink(profileStringMatches[2], profileStringMatches[1]);
1251 var realURL = (linkString.indexOf("www.") === 0 ? "http://" + linkString : linkString);
1252 var lineColumnMatch = lineColumnRegEx.exec(realURL);
1253 if (lineColumnMatch)
1254 realURL = realURL.substring(0, realURL.length - lineColumnMatch[0].length);
1256 var hasResourceWithURL = !!WebInspector.resourceForURL(realURL);
1257 var urlNode = WebInspector.linkifyURLAsNode(realURL, title, null, hasResourceWithURL);
1258 container.appendChild(urlNode);
1259 if (lineColumnMatch) {
1260 urlNode.setAttribute("line_number", lineColumnMatch[1]);
1261 urlNode.setAttribute("preferred_panel", "scripts");
1263 string = string.substring(linkIndex + linkString.length, string.length);
1267 container.appendChild(document.createTextNode(string));
1272 WebInspector.showProfileForURL = function(url)
1274 WebInspector.showPanel("profiles");
1275 WebInspector.panels.profiles.showProfileForURL(url);
1278 WebInspector.linkifyURLAsNode = function(url, linkText, classes, isExternal, tooltipText)
1282 classes = (classes ? classes + " " : "");
1283 classes += isExternal ? "webkit-html-external-link" : "webkit-html-resource-link";
1285 var a = document.createElement("a");
1287 a.className = classes;
1288 if (typeof tooltipText === "undefined")
1290 else if (typeof tooltipText !== "string" || tooltipText.length)
1291 a.title = tooltipText;
1292 a.textContent = linkText;
1293 a.style.maxWidth = "100%";
1295 a.setAttribute("target", "_blank");
1300 WebInspector.linkifyURL = function(url, linkText, classes, isExternal, tooltipText)
1302 // Use the DOM version of this function so as to avoid needing to escape attributes.
1303 // FIXME: Get rid of linkifyURL entirely.
1304 return WebInspector.linkifyURLAsNode(url, linkText, classes, isExternal, tooltipText).outerHTML;
1307 WebInspector.formatLinkText = function(url, lineNumber)
1309 var text = WebInspector.displayNameForURL(url);
1310 if (lineNumber !== undefined)
1311 text += ":" + (lineNumber + 1);
1315 WebInspector.linkifyResourceAsNode = function(url, lineNumber, classes, tooltipText)
1317 var linkText = this.formatLinkText(url, lineNumber);
1318 var anchor = this.linkifyURLAsNode(url, linkText, classes, false, tooltipText);
1319 anchor.setAttribute("preferred_panel", "resources");
1320 anchor.setAttribute("line_number", lineNumber);
1324 WebInspector.resourceURLForRelatedNode = function(node, url)
1326 if (!url || url.indexOf("://") > 0)
1329 for (var frameOwnerCandidate = node; frameOwnerCandidate; frameOwnerCandidate = frameOwnerCandidate.parentNode) {
1330 if (frameOwnerCandidate.documentURL) {
1331 var result = WebInspector.completeURL(frameOwnerCandidate.documentURL, url);
1338 // documentURL not found or has bad value
1339 var resourceURL = url;
1340 function callback(resource)
1342 if (resource.path === url) {
1343 resourceURL = resource.url;
1347 WebInspector.forAllResources(callback);
1351 WebInspector.populateHrefContextMenu = function(contextMenu, contextNode, event)
1353 var anchorElement = event.target.enclosingNodeOrSelfWithClass("webkit-html-resource-link") || event.target.enclosingNodeOrSelfWithClass("webkit-html-external-link");
1357 var resourceURL = WebInspector.resourceURLForRelatedNode(contextNode, anchorElement.href);
1361 // Add resource-related actions.
1362 contextMenu.appendItem(WebInspector.openLinkExternallyLabel(), WebInspector.openResource.bind(WebInspector, resourceURL, false));
1363 if (WebInspector.resourceForURL(resourceURL))
1364 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Open link in Resources panel" : "Open Link in Resources Panel"), WebInspector.openResource.bind(null, resourceURL, true));
1365 contextMenu.appendItem(WebInspector.copyLinkAddressLabel(), InspectorFrontendHost.copyText.bind(InspectorFrontendHost, resourceURL));
1369 WebInspector.completeURL = function(baseURL, href)
1372 // Return absolute URLs as-is.
1373 var parsedHref = href.asParsedURL();
1374 if ((parsedHref && parsedHref.scheme) || href.indexOf("data:") === 0)
1378 var parsedURL = baseURL.asParsedURL();
1381 if (path.charAt(0) !== "/") {
1382 var basePath = parsedURL.path;
1383 // A href of "?foo=bar" implies "basePath?foo=bar".
1384 // With "basePath?a=b" and "?foo=bar" we should get "basePath?foo=bar".
1386 if (path.charAt(0) === "?") {
1387 var basePathCutIndex = basePath.indexOf("?");
1388 if (basePathCutIndex !== -1)
1389 prefix = basePath.substring(0, basePathCutIndex);
1393 prefix = basePath.substring(0, basePath.lastIndexOf("/")) + "/";
1395 path = prefix + path;
1396 } else if (path.length > 1 && path.charAt(1) === "/") {
1397 // href starts with "//" which is a full URL with the protocol dropped (use the baseURL protocol).
1398 return parsedURL.scheme + ":" + path;
1400 return parsedURL.scheme + "://" + parsedURL.host + (parsedURL.port ? (":" + parsedURL.port) : "") + path;
1405 WebInspector.addMainEventListeners = function(doc)
1407 doc.addEventListener("focus", this.focusChanged.bind(this), true);
1408 doc.addEventListener("keydown", this.documentKeyDown.bind(this), false);
1409 doc.addEventListener("beforecopy", this.documentCanCopy.bind(this), true);
1410 doc.addEventListener("copy", this.documentCopy.bind(this), true);
1411 doc.addEventListener("contextmenu", this.contextMenuEventFired.bind(this), true);
1413 doc.defaultView.addEventListener("focus", this.windowFocused.bind(this), false);
1414 doc.defaultView.addEventListener("blur", this.windowBlurred.bind(this), false);
1415 doc.addEventListener("click", this.documentClick.bind(this), true);
1418 WebInspector.frontendReused = function()
1420 this.resourceTreeModel.frontendReused();
1424 WebInspector.UIString = function(string)
1426 if (window.localizedStrings && string in window.localizedStrings)
1427 string = window.localizedStrings[string];
1429 if (!(string in WebInspector.missingLocalizedStrings)) {
1430 if (!WebInspector.InspectorBackendStub)
1431 console.warn("Localized string \"" + string + "\" not found.");
1432 WebInspector.missingLocalizedStrings[string] = true;
1435 if (Preferences.showMissingLocalizedStrings)
1436 string += " (not localized)";
1439 return String.vsprintf(string, Array.prototype.slice.call(arguments, 1));
1442 WebInspector.formatLocalized = function(format, substitutions, formatters, initialValue, append)
1444 return String.format(WebInspector.UIString(format), substitutions, formatters, initialValue, append);
1447 WebInspector.useLowerCaseMenuTitles = function()
1449 return WebInspector.platform === "windows" && Preferences.useLowerCaseMenuTitlesOnWindows;
1452 WebInspector.isBeingEdited = function(element)
1454 return element.__editing;
1457 WebInspector.markBeingEdited = function(element, value)
1460 if (element.__editing)
1462 element.__editing = true;
1463 WebInspector.__editingCount = (WebInspector.__editingCount || 0) + 1;
1465 if (!element.__editing)
1467 delete element.__editing;
1468 --WebInspector.__editingCount;
1473 WebInspector.isEditingAnyField = function()
1475 return !!WebInspector.__editingCount;
1478 // Available config fields (all optional):
1479 // context: Object - an arbitrary context object to be passed to the commit and cancel handlers
1480 // commitHandler: Function - handles editing "commit" outcome
1481 // cancelHandler: Function - handles editing "cancel" outcome
1482 // customFinishHandler: Function - custom finish handler for the editing session (invoked on keydown)
1483 // pasteHandler: Function - handles the "paste" event, return values are the same as those for customFinishHandler
1484 // multiline: Boolean - whether the edited element is multiline
1485 WebInspector.startEditing = function(element, config)
1487 if (!WebInspector.markBeingEdited(element, true))
1490 config = config || {};
1491 var committedCallback = config.commitHandler;
1492 var cancelledCallback = config.cancelHandler;
1493 var pasteCallback = config.pasteHandler;
1494 var context = config.context;
1495 var oldText = getContent(element);
1496 var moveDirection = "";
1498 element.addStyleClass("editing");
1500 var oldTabIndex = element.tabIndex;
1501 if (element.tabIndex < 0)
1502 element.tabIndex = 0;
1504 function blurEventListener() {
1505 editingCommitted.call(element);
1508 function getContent(element) {
1509 if (element.tagName === "INPUT" && element.type === "text")
1510 return element.value;
1512 return element.textContent;
1515 function cleanUpAfterEditing() {
1516 WebInspector.markBeingEdited(element, false);
1518 this.removeStyleClass("editing");
1519 this.tabIndex = oldTabIndex;
1521 this.scrollLeft = 0;
1523 element.removeEventListener("blur", blurEventListener, false);
1524 element.removeEventListener("keydown", keyDownEventListener, true);
1526 element.removeEventListener("paste", pasteEventListener, true);
1528 if (element === WebInspector.currentFocusElement || element.isAncestor(WebInspector.currentFocusElement))
1529 WebInspector.currentFocusElement = WebInspector.previousFocusElement;
1532 function editingCancelled() {
1533 if (this.tagName === "INPUT" && this.type === "text")
1534 this.value = oldText;
1536 this.textContent = oldText;
1538 cleanUpAfterEditing.call(this);
1540 if (cancelledCallback)
1541 cancelledCallback(this, context);
1544 function editingCommitted() {
1545 cleanUpAfterEditing.call(this);
1547 if (committedCallback)
1548 committedCallback(this, getContent(this), oldText, context, moveDirection);
1551 function defaultFinishHandler(event)
1553 var isMetaOrCtrl = WebInspector.isMac() ?
1554 event.metaKey && !event.shiftKey && !event.ctrlKey && !event.altKey :
1555 event.ctrlKey && !event.shiftKey && !event.metaKey && !event.altKey;
1556 if (isEnterKey(event) && (event.isMetaOrCtrlForTest || !config.multiline || isMetaOrCtrl))
1558 else if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code || event.keyIdentifier === "U+001B")
1560 else if (event.keyIdentifier === "U+0009") // Tab key
1561 return "move-" + (event.shiftKey ? "backward" : "forward");
1564 function handleEditingResult(result, event)
1566 if (result === "commit") {
1567 editingCommitted.call(element);
1568 event.preventDefault();
1569 event.stopPropagation();
1570 } else if (result === "cancel") {
1571 editingCancelled.call(element);
1572 event.preventDefault();
1573 event.stopPropagation();
1574 } else if (result && result.indexOf("move-") === 0) {
1575 moveDirection = result.substring(5);
1576 if (event.keyIdentifier !== "U+0009")
1577 blurEventListener();
1581 function pasteEventListener(event)
1583 var result = pasteCallback(event);
1584 handleEditingResult(result, event);
1587 function keyDownEventListener(event)
1589 var handler = config.customFinishHandler || defaultFinishHandler;
1590 var result = handler(event);
1591 handleEditingResult(result, event);
1594 element.addEventListener("blur", blurEventListener, false);
1595 element.addEventListener("keydown", keyDownEventListener, true);
1597 element.addEventListener("paste", pasteEventListener, true);
1599 WebInspector.currentFocusElement = element;
1601 cancel: editingCancelled.bind(element),
1602 commit: editingCommitted.bind(element)
1606 WebInspector._toolbarItemClicked = function(event)
1608 var toolbarItem = event.currentTarget;
1609 this.setCurrentPanel(toolbarItem.panel);
1612 WebInspector.PanelHistory = function()
1615 this._historyIterator = -1;
1618 WebInspector.PanelHistory.prototype = {
1619 canGoBack: function()
1621 return this._historyIterator > 0;
1626 this._inHistory = true;
1627 WebInspector.setCurrentPanel(WebInspector.panels[this._history[--this._historyIterator]]);
1628 delete this._inHistory;
1631 canGoForward: function()
1633 return this._historyIterator < this._history.length - 1;
1636 goForward: function()
1638 this._inHistory = true;
1639 WebInspector.setCurrentPanel(WebInspector.panels[this._history[++this._historyIterator]]);
1640 delete this._inHistory;
1643 setPanel: function(panelName)
1645 if (this._inHistory)
1648 this._history.splice(this._historyIterator + 1, this._history.length - this._historyIterator - 1);
1649 if (!this._history.length || this._history[this._history.length - 1] !== panelName)
1650 this._history.push(panelName);
1651 this._historyIterator = this._history.length - 1;
1655 Number.secondsToString = function(seconds, higherResolution)
1660 var ms = seconds * 1000;
1661 if (higherResolution && ms < 1000)
1662 return WebInspector.UIString("%.3fms", ms);
1664 return WebInspector.UIString("%.0fms", ms);
1667 return WebInspector.UIString("%.2fs", seconds);
1669 var minutes = seconds / 60;
1671 return WebInspector.UIString("%.1fmin", minutes);
1673 var hours = minutes / 60;
1675 return WebInspector.UIString("%.1fhrs", hours);
1677 var days = hours / 24;
1678 return WebInspector.UIString("%.1f days", days);
1681 Number.bytesToString = function(bytes, higherResolution)
1683 if (typeof higherResolution === "undefined")
1684 higherResolution = true;
1687 return WebInspector.UIString("%.0fB", bytes);
1689 var kilobytes = bytes / 1024;
1690 if (higherResolution && kilobytes < 1024)
1691 return WebInspector.UIString("%.2fKB", kilobytes);
1692 else if (kilobytes < 1024)
1693 return WebInspector.UIString("%.0fKB", kilobytes);
1695 var megabytes = kilobytes / 1024;
1696 if (higherResolution)
1697 return WebInspector.UIString("%.2fMB", megabytes);
1699 return WebInspector.UIString("%.0fMB", megabytes);