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.
31 // Keep this ; so that concatenated version of the script worked.
32 ;(function preloadImages()
34 (new Image()).src = "Images/clearConsoleButtonGlyph.png";
35 (new Image()).src = "Images/consoleButtonGlyph.png";
36 (new Image()).src = "Images/dockButtonGlyph.png";
37 (new Image()).src = "Images/enableOutlineButtonGlyph.png";
38 (new Image()).src = "Images/enableSolidButtonGlyph.png";
39 (new Image()).src = "Images/excludeButtonGlyph.png";
40 (new Image()).src = "Images/focusButtonGlyph.png";
41 (new Image()).src = "Images/largerResourcesButtonGlyph.png";
42 (new Image()).src = "Images/nodeSearchButtonGlyph.png";
43 (new Image()).src = "Images/pauseOnExceptionButtonGlyph.png";
44 (new Image()).src = "Images/percentButtonGlyph.png";
45 (new Image()).src = "Images/recordButtonGlyph.png";
46 (new Image()).src = "Images/recordToggledButtonGlyph.png";
47 (new Image()).src = "Images/reloadButtonGlyph.png";
48 (new Image()).src = "Images/undockButtonGlyph.png";
53 missingLocalizedStrings: {},
58 if (!("_platform" in this))
59 this._platform = InspectorFrontendHost.platform();
61 return this._platform;
66 if (!("_platformFlavor" in this))
67 this._platformFlavor = this._detectPlatformFlavor();
69 return this._platformFlavor;
72 _detectPlatformFlavor: function()
74 const userAgent = navigator.userAgent;
76 if (this.platform === "windows") {
77 var match = userAgent.match(/Windows NT (\d+)\.(?:\d+)/);
78 if (match && match[1] >= 6)
79 return WebInspector.PlatformFlavor.WindowsVista;
81 } else if (this.platform === "mac") {
82 var match = userAgent.match(/Mac OS X\s*(?:(\d+)_(\d+))?/);
83 if (!match || match[1] != 10)
84 return WebInspector.PlatformFlavor.MacSnowLeopard;
85 switch (Number(match[2])) {
87 return WebInspector.PlatformFlavor.MacTiger;
89 return WebInspector.PlatformFlavor.MacLeopard;
92 return WebInspector.PlatformFlavor.MacSnowLeopard;
101 if (!("_port" in this))
102 this._port = InspectorFrontendHost.port();
107 get previousFocusElement()
109 return this._previousFocusElement;
112 get currentFocusElement()
114 return this._currentFocusElement;
117 set currentFocusElement(x)
119 if (this._currentFocusElement !== x)
120 this._previousFocusElement = this._currentFocusElement;
121 this._currentFocusElement = x;
123 if (this._currentFocusElement) {
124 this._currentFocusElement.focus();
126 // Make a caret selection inside the new element if there isn't a range selection and
127 // there isn't already a caret selection inside.
128 var selection = window.getSelection();
129 if (selection.isCollapsed && !this._currentFocusElement.isInsertionCaretInside()) {
130 var selectionRange = this._currentFocusElement.ownerDocument.createRange();
131 selectionRange.setStart(this._currentFocusElement, 0);
132 selectionRange.setEnd(this._currentFocusElement, 0);
134 selection.removeAllRanges();
135 selection.addRange(selectionRange);
137 } else if (this._previousFocusElement)
138 this._previousFocusElement.blur();
141 resetFocusElement: function()
143 this.currentFocusElement = null;
144 this._previousFocusElement = null;
149 return this._currentPanel;
154 if (this._currentPanel === x)
157 if (this._currentPanel)
158 this._currentPanel.hide();
160 this._currentPanel = x;
164 WebInspector.searchController.activePanelChanged();
166 for (var panelName in WebInspector.panels) {
167 if (WebInspector.panels[panelName] === x) {
168 WebInspector.settings.lastActivePanel = panelName;
169 this._panelHistory.setPanel(panelName);
174 createDOMBreakpointsSidebarPane: function()
176 var pane = new WebInspector.NativeBreakpointsSidebarPane(WebInspector.UIString("DOM Breakpoints"));
177 function breakpointAdded(event)
179 pane.addBreakpointItem(new WebInspector.BreakpointItem(event.data));
181 WebInspector.breakpointManager.addEventListener(WebInspector.BreakpointManager.Events.DOMBreakpointAdded, breakpointAdded);
185 createXHRBreakpointsSidebarPane: function()
187 var pane = new WebInspector.XHRBreakpointsSidebarPane();
188 function breakpointAdded(event)
190 pane.addBreakpointItem(new WebInspector.BreakpointItem(event.data));
192 WebInspector.breakpointManager.addEventListener(WebInspector.BreakpointManager.Events.XHRBreakpointAdded, breakpointAdded);
196 _createPanels: function()
198 var hiddenPanels = (InspectorFrontendHost.hiddenPanels() || "").split(',');
199 if (hiddenPanels.indexOf("elements") === -1)
200 this.panels.elements = new WebInspector.ElementsPanel();
201 if (hiddenPanels.indexOf("resources") === -1)
202 this.panels.resources = new WebInspector.ResourcesPanel();
203 if (hiddenPanels.indexOf("network") === -1)
204 this.panels.network = new WebInspector.NetworkPanel();
205 if (hiddenPanels.indexOf("scripts") === -1)
206 this.panels.scripts = new WebInspector.ScriptsPanel();
207 if (hiddenPanels.indexOf("timeline") === -1)
208 this.panels.timeline = new WebInspector.TimelinePanel();
209 if (hiddenPanels.indexOf("profiles") === -1)
210 this.panels.profiles = new WebInspector.ProfilesPanel();
211 if (hiddenPanels.indexOf("audits") === -1)
212 this.panels.audits = new WebInspector.AuditsPanel();
213 if (hiddenPanels.indexOf("console") === -1)
214 this.panels.console = new WebInspector.ConsolePanel();
219 return this._attached;
224 if (this._attached === x)
229 var dockToggleButton = document.getElementById("dock-status-bar-item");
230 var body = document.body;
233 body.removeStyleClass("detached");
234 body.addStyleClass("attached");
235 dockToggleButton.title = WebInspector.UIString("Undock into separate window.");
237 body.removeStyleClass("attached");
238 body.addStyleClass("detached");
239 dockToggleButton.title = WebInspector.UIString("Dock to main window.");
242 // This may be called before onLoadedDone, hence the bulk of inspector objects may
243 // not be created yet.
244 if (WebInspector.searchController)
245 WebInspector.searchController.updateSearchLabel();
250 return this._errors || 0;
257 if (this._errors === x)
260 this._updateErrorAndWarningCounts();
265 return this._warnings || 0;
272 if (this._warnings === x)
275 this._updateErrorAndWarningCounts();
278 _updateErrorAndWarningCounts: function()
280 var errorWarningElement = document.getElementById("error-warning-count");
281 if (!errorWarningElement)
284 if (!this.errors && !this.warnings) {
285 errorWarningElement.addStyleClass("hidden");
289 errorWarningElement.removeStyleClass("hidden");
291 errorWarningElement.removeChildren();
294 var errorElement = document.createElement("span");
295 errorElement.id = "error-count";
296 errorElement.textContent = this.errors;
297 errorWarningElement.appendChild(errorElement);
301 var warningsElement = document.createElement("span");
302 warningsElement.id = "warning-count";
303 warningsElement.textContent = this.warnings;
304 errorWarningElement.appendChild(warningsElement);
309 if (this.errors == 1) {
310 if (this.warnings == 1)
311 errorWarningElement.title = WebInspector.UIString("%d error, %d warning", this.errors, this.warnings);
313 errorWarningElement.title = WebInspector.UIString("%d error, %d warnings", this.errors, this.warnings);
314 } else if (this.warnings == 1)
315 errorWarningElement.title = WebInspector.UIString("%d errors, %d warning", this.errors, this.warnings);
317 errorWarningElement.title = WebInspector.UIString("%d errors, %d warnings", this.errors, this.warnings);
318 } else if (this.errors == 1)
319 errorWarningElement.title = WebInspector.UIString("%d error", this.errors);
321 errorWarningElement.title = WebInspector.UIString("%d errors", this.errors);
322 } else if (this.warnings == 1)
323 errorWarningElement.title = WebInspector.UIString("%d warning", this.warnings);
324 else if (this.warnings)
325 errorWarningElement.title = WebInspector.UIString("%d warnings", this.warnings);
327 errorWarningElement.title = null;
330 highlightDOMNode: function(nodeId)
332 if ("_hideDOMNodeHighlightTimeout" in this) {
333 clearTimeout(this._hideDOMNodeHighlightTimeout);
334 delete this._hideDOMNodeHighlightTimeout;
337 if (this._highlightedDOMNodeId === nodeId)
340 this._highlightedDOMNodeId = nodeId;
342 InspectorAgent.highlightDOMNode(nodeId);
344 InspectorAgent.hideDOMNodeHighlight();
347 highlightDOMNodeForTwoSeconds: function(nodeId)
349 this.highlightDOMNode(nodeId);
350 this._hideDOMNodeHighlightTimeout = setTimeout(this.highlightDOMNode.bind(this, 0), 2000);
353 wireElementWithDOMNode: function(element, nodeId)
355 element.addEventListener("click", this._updateFocusedNode.bind(this, nodeId), false);
356 element.addEventListener("mouseover", this.highlightDOMNode.bind(this, nodeId), false);
357 element.addEventListener("mouseout", this.highlightDOMNode.bind(this, 0), false);
360 _updateFocusedNode: function(nodeId)
362 this.currentPanel = this.panels.elements;
363 this.panels.elements.updateFocusedNode(nodeId);
366 get networkResources()
368 return this.panels.network.resources;
371 networkResourceById: function(id)
373 return this.panels.network.resourceById(id);
376 forAllResources: function(callback)
378 WebInspector.resourceTreeModel.forAllResources(callback);
381 resourceForURL: function(url)
383 return this.resourceTreeModel.resourceForURL(url);
386 openLinkExternallyLabel: function()
388 return WebInspector.UIString("Open Link in New Window");
392 WebInspector.PlatformFlavor = {
393 WindowsVista: "windows-vista",
394 MacTiger: "mac-tiger",
395 MacLeopard: "mac-leopard",
396 MacSnowLeopard: "mac-snowleopard"
399 (function parseQueryParameters()
401 WebInspector.queryParamsObject = {};
402 var queryParams = window.location.search;
405 var params = queryParams.substring(1).split("&");
406 for (var i = 0; i < params.length; ++i) {
407 var pair = params[i].split("=");
408 WebInspector.queryParamsObject[pair[0]] = pair[1];
412 WebInspector.loaded = function()
414 if ("page" in WebInspector.queryParamsObject) {
415 var page = WebInspector.queryParamsObject.page;
416 var host = "host" in WebInspector.queryParamsObject ? WebInspector.queryParamsObject.host : window.location.host;
417 WebInspector.socket = new WebSocket("ws://" + host + "/devtools/page/" + page);
418 WebInspector.socket.onmessage = function(message) { InspectorBackend.dispatch(message.data); }
419 WebInspector.socket.onerror = function(error) { console.error(error); }
420 WebInspector.socket.onopen = function() {
421 InspectorFrontendHost.sendMessageToBackend = WebInspector.socket.send.bind(WebInspector.socket);
422 InspectorFrontendHost.loaded = WebInspector.socket.send.bind(WebInspector.socket, "loaded");
423 WebInspector.doLoadedDone();
427 WebInspector.doLoadedDone();
430 WebInspector.doLoadedDone = function()
432 InspectorFrontendHost.loaded();
434 var platform = WebInspector.platform;
435 document.body.addStyleClass("platform-" + platform);
436 var flavor = WebInspector.platformFlavor;
438 document.body.addStyleClass("platform-" + flavor);
439 var port = WebInspector.port;
440 document.body.addStyleClass("port-" + port);
442 WebInspector.settings = new WebInspector.Settings();
444 this._registerShortcuts();
446 // set order of some sections explicitly
447 WebInspector.shortcutsHelp.section(WebInspector.UIString("Console"));
448 WebInspector.shortcutsHelp.section(WebInspector.UIString("Elements Panel"));
450 this.drawer = new WebInspector.Drawer();
451 this.console = new WebInspector.ConsoleView(this.drawer);
452 this.drawer.visibleView = this.console;
453 this.resourceTreeModel = new WebInspector.ResourceTreeModel();
454 this.networkManager = new WebInspector.NetworkManager(this.resourceTreeModel);
455 this.domAgent = new WebInspector.DOMAgent();
457 InspectorBackend.registerDomainDispatcher("Inspector", this);
459 this.resourceCategories = {
460 documents: new WebInspector.ResourceCategory("documents", WebInspector.UIString("Documents"), "rgb(47,102,236)"),
461 stylesheets: new WebInspector.ResourceCategory("stylesheets", WebInspector.UIString("Stylesheets"), "rgb(157,231,119)"),
462 images: new WebInspector.ResourceCategory("images", WebInspector.UIString("Images"), "rgb(164,60,255)"),
463 scripts: new WebInspector.ResourceCategory("scripts", WebInspector.UIString("Scripts"), "rgb(255,121,0)"),
464 xhr: new WebInspector.ResourceCategory("xhr", WebInspector.UIString("XHR"), "rgb(231,231,10)"),
465 fonts: new WebInspector.ResourceCategory("fonts", WebInspector.UIString("Fonts"), "rgb(255,82,62)"),
466 websockets: new WebInspector.ResourceCategory("websockets", WebInspector.UIString("WebSockets"), "rgb(186,186,186)"), // FIXME: Decide the color.
467 other: new WebInspector.ResourceCategory("other", WebInspector.UIString("Other"), "rgb(186,186,186)")
470 this.cssModel = new WebInspector.CSSStyleModel();
471 this.debuggerModel = new WebInspector.DebuggerModel();
473 this.breakpointManager = new WebInspector.BreakpointManager();
474 this.searchController = new WebInspector.SearchController();
477 this._createPanels();
478 this._panelHistory = new WebInspector.PanelHistory();
479 this.toolbar = new WebInspector.Toolbar();
481 this.panelOrder = [];
482 for (var panelName in this.panels)
483 this.addPanel(this.panels[panelName]);
486 ResourceNotCompressed: {id: 0, message: WebInspector.UIString("You could save bandwidth by having your web server compress this transfer with gzip or zlib.")}
490 IncorrectMIMEType: {id: 0, message: WebInspector.UIString("Resource interpreted as %s but transferred with MIME type %s.")}
493 this.addMainEventListeners(document);
495 window.addEventListener("resize", this.windowResize.bind(this), true);
497 document.addEventListener("focus", this.focusChanged.bind(this), true);
498 document.addEventListener("keydown", this.documentKeyDown.bind(this), false);
499 document.addEventListener("beforecopy", this.documentCanCopy.bind(this), true);
500 document.addEventListener("copy", this.documentCopy.bind(this), true);
501 document.addEventListener("contextmenu", this.contextMenuEventFired.bind(this), true);
503 var dockToggleButton = document.getElementById("dock-status-bar-item");
504 dockToggleButton.addEventListener("click", this.toggleAttach.bind(this), false);
507 dockToggleButton.title = WebInspector.UIString("Undock into separate window.");
509 dockToggleButton.title = WebInspector.UIString("Dock to main window.");
511 var errorWarningCount = document.getElementById("error-warning-count");
512 errorWarningCount.addEventListener("click", this.showConsole.bind(this), false);
513 this._updateErrorAndWarningCounts();
515 this.extensionServer.initExtensions();
517 function onPopulateScriptObjects(error)
519 if (!error && !WebInspector.currentPanel)
520 WebInspector.showPanel(WebInspector.settings.lastActivePanel);
522 InspectorAgent.populateScriptObjects(onPopulateScriptObjects);
524 if (Preferences.debuggerAlwaysEnabled || WebInspector.settings.debuggerEnabled)
525 this.debuggerModel.enableDebugger();
526 if (Preferences.profilerAlwaysEnabled || WebInspector.settings.profilerEnabled)
527 InspectorAgent.enableProfiler();
528 if (WebInspector.settings.monitoringXHREnabled)
529 ConsoleAgent.setMonitoringXHREnabled(true);
531 ConsoleAgent.setConsoleMessagesEnabled(true);
533 function propertyNamesCallback(error, names)
536 WebInspector.cssNameCompletions = new WebInspector.CSSCompletions(names);
538 // As a DOMAgent method, this needs to happen after the frontend has loaded and the agent is available.
539 CSSAgent.getSupportedCSSProperties(propertyNamesCallback);
542 WebInspector.addPanel = function(panel)
544 this.panelOrder.push(panel);
545 this.toolbar.addPanel(panel);
548 var windowLoaded = function()
550 var localizedStringsURL = InspectorFrontendHost.localizedStringsURL();
551 if (localizedStringsURL) {
552 var localizedStringsScriptElement = document.createElement("script");
553 localizedStringsScriptElement.addEventListener("load", WebInspector.loaded.bind(WebInspector), false);
554 localizedStringsScriptElement.type = "text/javascript";
555 localizedStringsScriptElement.src = localizedStringsURL;
556 document.head.appendChild(localizedStringsScriptElement);
558 WebInspector.loaded();
560 window.removeEventListener("DOMContentLoaded", windowLoaded, false);
564 window.addEventListener("DOMContentLoaded", windowLoaded, false);
566 // We'd like to enforce asynchronous interaction between the inspector controller and the frontend.
567 // It is needed to prevent re-entering the backend code.
568 // Also, native dispatches do not guarantee setTimeouts to be serialized, so we
569 // enforce serialization using 'messagesToDispatch' queue. It is also important that JSC debugger
570 // tests require that each command was dispatch within individual timeout callback, so we don't batch them.
572 var messagesToDispatch = [];
574 WebInspector.dispatch = function(message) {
575 messagesToDispatch.push(message);
576 setTimeout(function() {
577 InspectorBackend.dispatch(messagesToDispatch.shift());
581 WebInspector.dispatchMessageFromBackend = function(messageObject)
583 WebInspector.dispatch(messageObject);
586 WebInspector.windowResize = function(event)
588 if (this.currentPanel)
589 this.currentPanel.resize();
590 this.drawer.resize();
591 this.toolbar.resize();
594 WebInspector.windowFocused = function(event)
596 // Fires after blur, so when focusing on either the main inspector
597 // or an <iframe> within the inspector we should always remove the
599 if (event.target.document.nodeType === Node.DOCUMENT_NODE)
600 document.body.removeStyleClass("inactive");
603 WebInspector.windowBlurred = function(event)
605 // Leaving the main inspector or an <iframe> within the inspector.
606 // We can add "inactive" now, and if we are moving the focus to another
607 // part of the inspector then windowFocused will correct this.
608 if (event.target.document.nodeType === Node.DOCUMENT_NODE)
609 document.body.addStyleClass("inactive");
612 WebInspector.focusChanged = function(event)
614 this.currentFocusElement = event.target;
617 WebInspector.setAttachedWindow = function(attached)
619 this.attached = attached;
622 WebInspector.close = function(event)
626 this._isClosing = true;
627 InspectorFrontendHost.closeWindow();
630 WebInspector.disconnectFromBackend = function()
632 InspectorFrontendHost.disconnectFromBackend();
635 WebInspector.documentClick = function(event)
637 var anchor = event.target.enclosingNodeOrSelfWithNodeName("a");
638 if (!anchor || anchor.target === "_blank")
641 // Prevent the link from navigating, since we don't do any navigation by following links normally.
642 event.preventDefault();
643 event.stopPropagation();
645 function followLink()
647 // FIXME: support webkit-html-external-link links here.
648 if (WebInspector.canShowSourceLine(anchor.href, anchor.getAttribute("line_number"), anchor.getAttribute("preferred_panel"))) {
649 if (anchor.hasStyleClass("webkit-html-external-link")) {
650 anchor.removeStyleClass("webkit-html-external-link");
651 anchor.addStyleClass("webkit-html-resource-link");
654 WebInspector.showSourceLine(anchor.href, anchor.getAttribute("line_number"), anchor.getAttribute("preferred_panel"));
658 const profileMatch = WebInspector.ProfileType.URLRegExp.exec(anchor.href);
660 WebInspector.showProfileForURL(anchor.href);
664 var parsedURL = anchor.href.asParsedURL();
665 if (parsedURL && parsedURL.scheme === "webkit-link-action") {
666 if (parsedURL.host === "show-panel") {
667 var panel = parsedURL.path.substring(1);
668 if (WebInspector.panels[panel])
669 WebInspector.showPanel(panel);
674 WebInspector.showPanel("resources");
677 if (WebInspector.followLinkTimeout)
678 clearTimeout(WebInspector.followLinkTimeout);
680 if (anchor.preventFollowOnDoubleClick) {
681 // Start a timeout if this is the first click, if the timeout is canceled
682 // before it fires, then a double clicked happened or another link was clicked.
683 if (event.detail === 1)
684 WebInspector.followLinkTimeout = setTimeout(followLink, 333);
691 WebInspector.openResource = function(resourceURL, inResourcesPanel)
693 var resource = WebInspector.resourceForURL(resourceURL);
694 if (inResourcesPanel && resource) {
695 WebInspector.panels.resources.showResource(resource);
696 WebInspector.showPanel("resources");
698 InspectorAgent.openInInspectedWindow(resource ? resource.url : resourceURL);
701 WebInspector._registerShortcuts = function()
703 var shortcut = WebInspector.KeyboardShortcut;
704 var section = WebInspector.shortcutsHelp.section(WebInspector.UIString("All Panels"));
706 shortcut.shortcutToString("]", shortcut.Modifiers.CtrlOrMeta),
707 shortcut.shortcutToString("[", shortcut.Modifiers.CtrlOrMeta)
709 section.addRelatedKeys(keys, WebInspector.UIString("Next/previous panel"));
710 section.addKey(shortcut.shortcutToString(shortcut.Keys.Esc), WebInspector.UIString("Toggle console"));
711 section.addKey(shortcut.shortcutToString("f", shortcut.Modifiers.CtrlOrMeta), WebInspector.UIString("Search"));
712 if (WebInspector.isMac()) {
714 shortcut.shortcutToString("g", shortcut.Modifiers.Meta),
715 shortcut.shortcutToString("g", shortcut.Modifiers.Meta | shortcut.Modifiers.Shift)
717 section.addRelatedKeys(keys, WebInspector.UIString("Find next/previous"));
721 WebInspector.documentKeyDown = function(event)
723 var isInputElement = event.target.nodeName === "INPUT";
724 var isInEditMode = event.target.enclosingNodeOrSelfWithClass("text-prompt") || WebInspector.isEditingAnyField();
725 const helpKey = WebInspector.isMac() ? "U+003F" : "U+00BF"; // "?" for both platforms
727 if (event.keyIdentifier === "F1" ||
728 (event.keyIdentifier === helpKey && event.shiftKey && (!isInEditMode && !isInputElement || event.metaKey))) {
729 WebInspector.shortcutsHelp.show();
730 event.stopPropagation();
731 event.preventDefault();
735 if (WebInspector.isEditingAnyField())
738 if (this.currentFocusElement && this.currentFocusElement.handleKeyEvent) {
739 this.currentFocusElement.handleKeyEvent(event);
741 event.preventDefault();
746 if (this.currentPanel && this.currentPanel.handleShortcut) {
747 this.currentPanel.handleShortcut(event);
749 event.preventDefault();
754 WebInspector.searchController.handleShortcut(event);
756 event.preventDefault();
760 var isMac = WebInspector.isMac();
761 switch (event.keyIdentifier) {
763 var isBackKey = !isInEditMode && (isMac ? event.metaKey : event.ctrlKey);
764 if (isBackKey && this._panelHistory.canGoBack()) {
765 this._panelHistory.goBack();
766 event.preventDefault();
771 var isForwardKey = !isInEditMode && (isMac ? event.metaKey : event.ctrlKey);
772 if (isForwardKey && this._panelHistory.canGoForward()) {
773 this._panelHistory.goForward();
774 event.preventDefault();
778 case "U+001B": // Escape key
779 event.preventDefault();
780 if (this.drawer.fullPanel)
783 this.drawer.visible = !this.drawer.visible;
786 // Windows and Mac have two different definitions of [, so accept both.
788 case "U+00DB": // [ key
790 var isRotateLeft = event.metaKey && !event.shiftKey && !event.ctrlKey && !event.altKey;
792 var isRotateLeft = event.ctrlKey && !event.shiftKey && !event.metaKey && !event.altKey;
795 var index = this.panelOrder.indexOf(this.currentPanel);
796 index = (index === 0) ? this.panelOrder.length - 1 : index - 1;
797 this.panelOrder[index].toolbarItem.click();
798 event.preventDefault();
803 // Windows and Mac have two different definitions of ], so accept both.
805 case "U+00DD": // ] key
807 var isRotateRight = event.metaKey && !event.shiftKey && !event.ctrlKey && !event.altKey;
809 var isRotateRight = event.ctrlKey && !event.shiftKey && !event.metaKey && !event.altKey;
812 var index = this.panelOrder.indexOf(this.currentPanel);
813 index = (index + 1) % this.panelOrder.length;
814 this.panelOrder[index].toolbarItem.click();
815 event.preventDefault();
820 case "U+0052": // R key
821 if ((event.metaKey && isMac) || (event.ctrlKey && !isMac)) {
822 InspectorAgent.reloadPage(event.shiftKey);
823 event.preventDefault();
828 InspectorAgent.reloadPage(event.ctrlKey || event.shiftKey);
833 WebInspector.documentCanCopy = function(event)
835 if (this.currentPanel && this.currentPanel.handleCopyEvent)
836 event.preventDefault();
839 WebInspector.documentCopy = function(event)
841 if (this.currentPanel && this.currentPanel.handleCopyEvent)
842 this.currentPanel.handleCopyEvent(event);
845 WebInspector.contextMenuEventFired = function(event)
847 if (event.handled || event.target.hasStyleClass("popup-glasspane"))
848 event.preventDefault();
851 WebInspector.animateStyle = function(animations, duration, callback)
855 var hasCompleted = false;
857 const intervalDuration = (1000 / 30); // 30 frames per second.
858 const animationsLength = animations.length;
859 const propertyUnit = {opacity: ""};
860 const defaultUnit = "px";
862 function cubicInOut(t, b, c, d)
864 if ((t/=d/2) < 1) return c/2*t*t*t + b;
865 return c/2*((t-=2)*t*t + 2) + b;
868 // Pre-process animations.
869 for (var i = 0; i < animationsLength; ++i) {
870 var animation = animations[i];
871 var element = null, start = null, end = null, key = null;
872 for (key in animation) {
873 if (key === "element")
874 element = animation[key];
875 else if (key === "start")
876 start = animation[key];
877 else if (key === "end")
878 end = animation[key];
881 if (!element || !end)
885 var computedStyle = element.ownerDocument.defaultView.getComputedStyle(element);
888 start[key] = parseInt(computedStyle.getPropertyValue(key));
889 animation.start = start;
892 element.style.setProperty(key, start[key] + (key in propertyUnit ? propertyUnit[key] : defaultUnit));
895 function animateLoop()
898 complete += intervalDuration;
899 var next = complete + intervalDuration;
901 // Make style changes.
902 for (var i = 0; i < animationsLength; ++i) {
903 var animation = animations[i];
904 var element = animation.element;
905 var start = animation.start;
906 var end = animation.end;
907 if (!element || !end)
910 var style = element.style;
912 var endValue = end[key];
913 if (next < duration) {
914 var startValue = start[key];
915 var newValue = cubicInOut(complete, startValue, endValue - startValue, duration);
916 style.setProperty(key, newValue + (key in propertyUnit ? propertyUnit[key] : defaultUnit));
918 style.setProperty(key, endValue + (key in propertyUnit ? propertyUnit[key] : defaultUnit));
923 if (complete >= duration) {
925 clearInterval(interval);
931 function forceComplete()
942 clearInterval(interval);
945 interval = setInterval(animateLoop, intervalDuration);
948 forceComplete: forceComplete
952 WebInspector.toggleAttach = function()
955 InspectorFrontendHost.requestAttachWindow();
957 InspectorFrontendHost.requestDetachWindow();
960 WebInspector.elementDragStart = function(element, dividerDrag, elementDragEnd, event, cursor)
962 if (this._elementDraggingEventListener || this._elementEndDraggingEventListener)
963 this.elementDragEnd(event);
965 this._elementDraggingEventListener = dividerDrag;
966 this._elementEndDraggingEventListener = elementDragEnd;
968 document.addEventListener("mousemove", dividerDrag, true);
969 document.addEventListener("mouseup", elementDragEnd, true);
971 document.body.style.cursor = cursor;
973 event.preventDefault();
976 WebInspector.elementDragEnd = function(event)
978 document.removeEventListener("mousemove", this._elementDraggingEventListener, true);
979 document.removeEventListener("mouseup", this._elementEndDraggingEventListener, true);
981 document.body.style.removeProperty("cursor");
983 delete this._elementDraggingEventListener;
984 delete this._elementEndDraggingEventListener;
986 event.preventDefault();
989 WebInspector.toggleSearchingForNode = function()
991 if (this.panels.elements) {
992 this.showPanel("elements");
993 this.panels.elements.toggleSearchingForNode();
997 WebInspector.showConsole = function()
999 this.drawer.showView(this.console);
1002 WebInspector.showPanel = function(panel)
1004 if (!(panel in this.panels))
1006 this.currentPanel = this.panels[panel];
1009 WebInspector.domContentEventFired = function(time)
1011 this.panels.audits.mainResourceDOMContentTime = time;
1012 if (this.panels.network)
1013 this.panels.network.mainResourceDOMContentTime = time;
1014 this.extensionServer.notifyPageDOMContentLoaded((time - WebInspector.mainResource.startTime) * 1000);
1015 this.mainResourceDOMContentTime = time;
1018 WebInspector.loadEventFired = function(time)
1020 this.panels.audits.mainResourceLoadTime = time;
1021 this.panels.network.mainResourceLoadTime = time;
1022 this.panels.resources.loadEventFired();
1023 this.extensionServer.notifyPageLoaded((time - WebInspector.mainResource.startTime) * 1000);
1024 this.mainResourceLoadTime = time;
1027 WebInspector.searchingForNodeWasEnabled = function()
1029 this.panels.elements.searchingForNodeWasEnabled();
1032 WebInspector.searchingForNodeWasDisabled = function()
1034 this.panels.elements.searchingForNodeWasDisabled();
1037 WebInspector.reset = function()
1039 this.debuggerModel.reset();
1041 for (var panelName in this.panels) {
1042 var panel = this.panels[panelName];
1043 if ("reset" in panel)
1047 this.resources = {};
1048 this.highlightDOMNode(0);
1050 this.console.clearMessages();
1051 this.extensionServer.notifyInspectorReset();
1054 WebInspector.bringToFront = function()
1056 InspectorFrontendHost.bringToFront();
1059 WebInspector.inspectedURLChanged = function(url)
1061 InspectorFrontendHost.inspectedURLChanged(url);
1062 this.settings.inspectedURLChanged(url);
1063 this.extensionServer.notifyInspectedURLChanged();
1066 WebInspector.log = function(message, messageLevel)
1068 // remember 'this' for setInterval() callback
1071 // return indication if we can actually log a message
1072 function isLogAvailable()
1074 return WebInspector.ConsoleMessage && WebInspector.RemoteObject && self.console;
1077 // flush the queue of pending messages
1078 function flushQueue()
1080 var queued = WebInspector.log.queued;
1084 for (var i = 0; i < queued.length; ++i)
1085 logMessage(queued[i]);
1087 delete WebInspector.log.queued;
1090 // flush the queue if it console is available
1091 // - this function is run on an interval
1092 function flushQueueIfAvailable()
1094 if (!isLogAvailable())
1097 clearInterval(WebInspector.log.interval);
1098 delete WebInspector.log.interval;
1103 // actually log the message
1104 function logMessage(message)
1106 var repeatCount = 1;
1107 if (message == WebInspector.log.lastMessage)
1108 repeatCount = WebInspector.log.repeatCount + 1;
1110 WebInspector.log.lastMessage = message;
1111 WebInspector.log.repeatCount = repeatCount;
1113 // ConsoleMessage expects a proxy object
1114 message = new WebInspector.RemoteObject.fromPrimitiveValue(message);
1117 var msg = new WebInspector.ConsoleMessage(
1118 WebInspector.ConsoleMessage.MessageSource.Other,
1119 WebInspector.ConsoleMessage.MessageType.Log,
1120 messageLevel || WebInspector.ConsoleMessage.MessageLevel.Debug,
1128 self.console.addMessage(msg);
1131 // if we can't log the message, queue it
1132 if (!isLogAvailable()) {
1133 if (!WebInspector.log.queued)
1134 WebInspector.log.queued = [];
1136 WebInspector.log.queued.push(message);
1138 if (!WebInspector.log.interval)
1139 WebInspector.log.interval = setInterval(flushQueueIfAvailable, 1000);
1144 // flush the pending queue if any
1148 logMessage(message);
1151 WebInspector.drawLoadingPieChart = function(canvas, percent) {
1152 var g = canvas.getContext("2d");
1153 var darkColor = "rgb(122, 168, 218)";
1154 var lightColor = "rgb(228, 241, 251)";
1160 g.arc(cx, cy, r, 0, Math.PI * 2, false);
1164 g.strokeStyle = darkColor;
1165 g.fillStyle = lightColor;
1169 var startangle = -Math.PI / 2;
1170 var endangle = startangle + (percent * Math.PI * 2);
1174 g.arc(cx, cy, r, startangle, endangle, false);
1177 g.fillStyle = darkColor;
1181 WebInspector.inspect = function(objectId, hints)
1183 var object = WebInspector.RemoteObject.fromPayload(objectId);
1184 if (object.type === "node") {
1185 // Request node from backend and focus it.
1186 object.pushNodeToFrontend(WebInspector.updateFocusedNode.bind(WebInspector));
1187 } else if (hints.databaseId) {
1188 WebInspector.currentPanel = WebInspector.panels.resources;
1189 WebInspector.panels.resources.selectDatabase(hints.databaseId);
1190 } else if (hints.domStorageId) {
1191 WebInspector.currentPanel = WebInspector.panels.resources;
1192 WebInspector.panels.resources.selectDOMStorage(hints.domStorageId);
1198 WebInspector.updateFocusedNode = function(nodeId)
1200 this._updateFocusedNode(nodeId);
1201 this.highlightDOMNodeForTwoSeconds(nodeId);
1204 WebInspector.displayNameForURL = function(url)
1209 var resource = this.resourceForURL(url);
1211 return resource.displayName;
1213 if (!WebInspector.mainResource)
1214 return url.trimURL("");
1216 var lastPathComponent = WebInspector.mainResource.lastPathComponent;
1217 var index = WebInspector.mainResource.url.indexOf(lastPathComponent);
1218 if (index !== -1 && index + lastPathComponent.length === WebInspector.mainResource.url.length) {
1219 var baseURL = WebInspector.mainResource.url.substring(0, index);
1220 if (url.indexOf(baseURL) === 0)
1221 return url.substring(index);
1224 return url.trimURL(WebInspector.mainResource.domain);
1227 WebInspector._choosePanelToShowSourceLine = function(url, line, preferredPanel)
1229 preferredPanel = preferredPanel || "resources";
1231 var panel = this.panels[preferredPanel];
1232 if (panel && panel.canShowSourceLine(url, line))
1234 panel = this.panels.resources;
1235 return panel.canShowSourceLine(url, line) ? panel : null;
1238 WebInspector.canShowSourceLine = function(url, line, preferredPanel)
1240 return !!this._choosePanelToShowSourceLine(url, line, preferredPanel);
1243 WebInspector.showSourceLine = function(url, line, preferredPanel)
1245 this.currentPanel = this._choosePanelToShowSourceLine(url, line, preferredPanel);
1246 if (!this.currentPanel)
1249 this.drawer.immediatelyFinishAnimation();
1250 this.currentPanel.showSourceLine(url, line);
1254 WebInspector.linkifyStringAsFragment = function(string)
1256 var container = document.createDocumentFragment();
1257 var linkStringRegEx = /(?:[a-zA-Z][a-zA-Z0-9+.-]{2,}:\/\/|www\.)[\w$\-_+*'=\|\/\\(){}[\]%@&#~,:;.!?]{2,}[\w$\-_+*=\|\/\\({%@&#~]/;
1258 var lineColumnRegEx = /:(\d+)(:(\d+))?$/;
1261 var linkString = linkStringRegEx.exec(string);
1265 linkString = linkString[0];
1266 var title = linkString;
1267 var linkIndex = string.indexOf(linkString);
1268 var nonLink = string.substring(0, linkIndex);
1269 container.appendChild(document.createTextNode(nonLink));
1271 var profileStringMatches = WebInspector.ProfileType.URLRegExp.exec(title);
1272 if (profileStringMatches)
1273 title = WebInspector.panels.profiles.displayTitleForProfileLink(profileStringMatches[2], profileStringMatches[1]);
1275 var realURL = (linkString.indexOf("www.") === 0 ? "http://" + linkString : linkString);
1276 var lineColumnMatch = lineColumnRegEx.exec(realURL);
1277 if (lineColumnMatch)
1278 realURL = realURL.substring(0, realURL.length - lineColumnMatch[0].length);
1280 var hasResourceWithURL = !!WebInspector.resourceForURL(realURL);
1281 var urlNode = WebInspector.linkifyURLAsNode(realURL, title, null, hasResourceWithURL);
1282 container.appendChild(urlNode);
1283 if (lineColumnMatch) {
1284 urlNode.setAttribute("line_number", lineColumnMatch[1]);
1285 urlNode.setAttribute("preferred_panel", "scripts");
1287 string = string.substring(linkIndex + linkString.length, string.length);
1291 container.appendChild(document.createTextNode(string));
1296 WebInspector.showProfileForURL = function(url)
1298 WebInspector.showPanel("profiles");
1299 WebInspector.panels.profiles.showProfileForURL(url);
1302 WebInspector.linkifyURLAsNode = function(url, linkText, classes, isExternal, tooltipText)
1306 classes = (classes ? classes + " " : "");
1307 classes += isExternal ? "webkit-html-external-link" : "webkit-html-resource-link";
1309 var a = document.createElement("a");
1311 a.className = classes;
1312 if (typeof tooltipText === "undefined")
1314 else if (typeof tooltipText !== "string" || tooltipText.length)
1315 a.title = tooltipText;
1316 a.textContent = linkText;
1321 WebInspector.linkifyURL = function(url, linkText, classes, isExternal, tooltipText)
1323 // Use the DOM version of this function so as to avoid needing to escape attributes.
1324 // FIXME: Get rid of linkifyURL entirely.
1325 return WebInspector.linkifyURLAsNode(url, linkText, classes, isExternal, tooltipText).outerHTML;
1328 WebInspector.linkifyResourceAsNode = function(url, preferredPanel, lineNumber, classes, tooltipText)
1330 var linkText = WebInspector.displayNameForURL(url);
1332 linkText += ":" + lineNumber;
1333 var node = WebInspector.linkifyURLAsNode(url, linkText, classes, false, tooltipText);
1334 node.setAttribute("line_number", lineNumber);
1335 node.setAttribute("preferred_panel", preferredPanel);
1339 WebInspector.resourceURLForRelatedNode = function(node, url)
1341 if (!url || url.indexOf("://") > 0)
1344 for (var frameOwnerCandidate = node; frameOwnerCandidate; frameOwnerCandidate = frameOwnerCandidate.parentNode) {
1345 if (frameOwnerCandidate.documentURL) {
1346 var result = WebInspector.completeURL(frameOwnerCandidate.documentURL, url);
1353 // documentURL not found or has bad value
1354 var resourceURL = url;
1355 function callback(resource)
1357 if (resource.path === url) {
1358 resourceURL = resource.url;
1362 WebInspector.forAllResources(callback);
1366 WebInspector.completeURL = function(baseURL, href)
1369 // Return absolute URLs as-is.
1370 var parsedHref = href.asParsedURL();
1371 if (parsedHref && parsedHref.scheme)
1375 var parsedURL = baseURL.asParsedURL();
1378 if (path.charAt(0) !== "/") {
1379 var basePath = parsedURL.path;
1380 // A href of "?foo=bar" implies "basePath?foo=bar".
1381 // With "basePath?a=b" and "?foo=bar" we should get "basePath?foo=bar".
1383 if (path.charAt(0) === "?") {
1384 var basePathCutIndex = basePath.indexOf("?");
1385 if (basePathCutIndex !== -1)
1386 prefix = basePath.substring(0, basePathCutIndex);
1390 prefix = basePath.substring(0, basePath.lastIndexOf("/")) + "/";
1392 path = prefix + path;
1393 } else if (path.length > 1 && path.charAt(1) === "/") {
1394 // href starts with "//" which is a full URL with the protocol dropped (use the baseURL protocol).
1395 return parsedURL.scheme + ":" + path;
1397 return parsedURL.scheme + "://" + parsedURL.host + (parsedURL.port ? (":" + parsedURL.port) : "") + path;
1402 WebInspector.addMainEventListeners = function(doc)
1404 doc.defaultView.addEventListener("focus", this.windowFocused.bind(this), false);
1405 doc.defaultView.addEventListener("blur", this.windowBlurred.bind(this), false);
1406 doc.addEventListener("click", this.documentClick.bind(this), true);
1409 WebInspector.frontendReused = function()
1411 this.networkManager.frontendReused();
1415 WebInspector.UIString = function(string)
1417 if (window.localizedStrings && string in window.localizedStrings)
1418 string = window.localizedStrings[string];
1420 if (!(string in WebInspector.missingLocalizedStrings)) {
1421 if (!WebInspector.InspectorBackendStub)
1422 console.warn("Localized string \"" + string + "\" not found.");
1423 WebInspector.missingLocalizedStrings[string] = true;
1426 if (Preferences.showMissingLocalizedStrings)
1427 string += " (not localized)";
1430 return String.vsprintf(string, Array.prototype.slice.call(arguments, 1));
1433 WebInspector.formatLocalized = function(format, substitutions, formatters, initialValue, append)
1435 return String.format(WebInspector.UIString(format), substitutions, formatters, initialValue, append);
1438 WebInspector.isMac = function()
1440 if (!("_isMac" in this))
1441 this._isMac = WebInspector.platform === "mac";
1446 WebInspector.isBeingEdited = function(element)
1448 return element.__editing;
1451 WebInspector.isEditingAnyField = function()
1453 return this.__editing;
1456 // Available config fields (all optional):
1457 // context: Object - an arbitrary context object to be passed to the commit and cancel handlers
1458 // commitHandler: Function - handles editing "commit" outcome
1459 // cancelHandler: Function - handles editing "cancel" outcome
1460 // customFinishHandler: Function - custom finish handler for the editing session (invoked on keydown)
1461 // pasteHandler: Function - handles the "paste" event, return values are the same as those for customFinishHandler
1462 // multiline: Boolean - whether the edited element is multiline
1463 WebInspector.startEditing = function(element, config)
1465 if (element.__editing)
1467 element.__editing = true;
1468 WebInspector.__editing = true;
1470 config = config || {};
1471 var committedCallback = config.commitHandler;
1472 var cancelledCallback = config.cancelHandler;
1473 var pasteCallback = config.pasteHandler;
1474 var context = config.context;
1475 var oldText = getContent(element);
1476 var moveDirection = "";
1478 element.addStyleClass("editing");
1480 var oldTabIndex = element.tabIndex;
1481 if (element.tabIndex < 0)
1482 element.tabIndex = 0;
1484 function blurEventListener() {
1485 editingCommitted.call(element);
1488 function getContent(element) {
1489 if (element.tagName === "INPUT" && element.type === "text")
1490 return element.value;
1492 return element.textContent;
1495 function cleanUpAfterEditing() {
1496 delete this.__editing;
1497 delete WebInspector.__editing;
1499 this.removeStyleClass("editing");
1500 this.tabIndex = oldTabIndex;
1502 this.scrollLeft = 0;
1504 element.removeEventListener("blur", blurEventListener, false);
1505 element.removeEventListener("keydown", keyDownEventListener, true);
1507 element.removeEventListener("paste", pasteEventListener, true);
1509 if (element === WebInspector.currentFocusElement || element.isAncestor(WebInspector.currentFocusElement))
1510 WebInspector.currentFocusElement = WebInspector.previousFocusElement;
1513 function editingCancelled() {
1514 if (this.tagName === "INPUT" && this.type === "text")
1515 this.value = oldText;
1517 this.textContent = oldText;
1519 cleanUpAfterEditing.call(this);
1521 if (cancelledCallback)
1522 cancelledCallback(this, context);
1525 function editingCommitted() {
1526 cleanUpAfterEditing.call(this);
1528 if (committedCallback)
1529 committedCallback(this, getContent(this), oldText, context, moveDirection);
1532 function defaultFinishHandler(event)
1534 var isMetaOrCtrl = WebInspector.isMac() ?
1535 event.metaKey && !event.shiftKey && !event.ctrlKey && !event.altKey :
1536 event.ctrlKey && !event.shiftKey && !event.metaKey && !event.altKey;
1537 if (isEnterKey(event) && (!config.multiline || isMetaOrCtrl))
1539 else if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code)
1541 else if (event.keyIdentifier === "U+0009") // Tab key
1542 return "move-" + (event.shiftKey ? "backward" : "forward");
1545 function handleEditingResult(result, event)
1547 if (result === "commit") {
1548 editingCommitted.call(element);
1549 event.preventDefault();
1550 event.stopPropagation();
1551 } else if (result === "cancel") {
1552 editingCancelled.call(element);
1553 event.preventDefault();
1554 event.stopPropagation();
1555 } else if (result && result.indexOf("move-") === 0) {
1556 moveDirection = result.substring(5);
1557 if (event.keyIdentifier !== "U+0009")
1558 blurEventListener();
1562 function pasteEventListener(event)
1564 var result = pasteCallback(event);
1565 handleEditingResult(result, event);
1568 function keyDownEventListener(event)
1570 var handler = config.customFinishHandler || defaultFinishHandler;
1571 var result = handler(event);
1572 handleEditingResult(result, event);
1575 element.addEventListener("blur", blurEventListener, false);
1576 element.addEventListener("keydown", keyDownEventListener, true);
1578 element.addEventListener("paste", pasteEventListener, true);
1580 WebInspector.currentFocusElement = element;
1582 cancel: editingCancelled.bind(element),
1583 commit: editingCommitted.bind(element)
1587 WebInspector._toolbarItemClicked = function(event)
1589 var toolbarItem = event.currentTarget;
1590 this.currentPanel = toolbarItem.panel;
1593 // This table maps MIME types to the Resource.Types which are valid for them.
1594 // The following line:
1595 // "text/html": {0: 1},
1596 // means that text/html is a valid MIME type for resources that have type
1597 // WebInspector.Resource.Type.Document (which has a value of 0).
1598 WebInspector.MIMETypes = {
1599 "text/html": {0: true},
1600 "text/xml": {0: true},
1601 "text/plain": {0: true},
1602 "application/xhtml+xml": {0: true},
1603 "text/css": {1: true},
1604 "text/xsl": {1: true},
1605 "image/jpeg": {2: true},
1606 "image/png": {2: true},
1607 "image/gif": {2: true},
1608 "image/bmp": {2: true},
1609 "image/vnd.microsoft.icon": {2: true},
1610 "image/x-icon": {2: true},
1611 "image/x-xbitmap": {2: true},
1612 "font/ttf": {3: true},
1613 "font/opentype": {3: true},
1614 "application/x-font-type1": {3: true},
1615 "application/x-font-ttf": {3: true},
1616 "application/x-font-woff": {3: true},
1617 "application/x-truetype-font": {3: true},
1618 "text/javascript": {4: true},
1619 "text/ecmascript": {4: true},
1620 "application/javascript": {4: true},
1621 "application/ecmascript": {4: true},
1622 "application/x-javascript": {4: true},
1623 "text/javascript1.1": {4: true},
1624 "text/javascript1.2": {4: true},
1625 "text/javascript1.3": {4: true},
1626 "text/jscript": {4: true},
1627 "text/livescript": {4: true},
1630 WebInspector.PanelHistory = function()
1633 this._historyIterator = -1;
1636 WebInspector.PanelHistory.prototype = {
1637 canGoBack: function()
1639 return this._historyIterator > 0;
1644 this._inHistory = true;
1645 WebInspector.currentPanel = WebInspector.panels[this._history[--this._historyIterator]];
1646 delete this._inHistory;
1649 canGoForward: function()
1651 return this._historyIterator < this._history.length - 1;
1654 goForward: function()
1656 this._inHistory = true;
1657 WebInspector.currentPanel = WebInspector.panels[this._history[++this._historyIterator]];
1658 delete this._inHistory;
1661 setPanel: function(panelName)
1663 if (this._inHistory)
1666 this._history.splice(this._historyIterator + 1, this._history.length - this._historyIterator - 1);
1667 if (!this._history.length || this._history[this._history.length - 1] !== panelName)
1668 this._history.push(panelName);
1669 this._historyIterator = this._history.length - 1;