1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 /** 6 * @constructor 7 * @implements {WebInspector.TabbedEditorContainerDelegate} 8 * @implements {WebInspector.Searchable} 9 * @implements {WebInspector.Replaceable} 10 * @extends {WebInspector.VBox} 11 * @param {!WebInspector.Workspace} workspace 12 * @param {!WebInspector.SourcesPanel} sourcesPanel 13 */ 14 WebInspector.SourcesView = function(workspace, sourcesPanel) 15 { 16 WebInspector.VBox.call(this); 17 this.registerRequiredCSS("sourcesView.css"); 18 this.element.id = "sources-panel-sources-view"; 19 this.setMinimumAndPreferredSizes(50, 25, 150, 100); 20 21 this._workspace = workspace; 22 this._sourcesPanel = sourcesPanel; 23 24 this._searchableView = new WebInspector.SearchableView(this); 25 this._searchableView.setMinimalSearchQuerySize(0); 26 this._searchableView.show(this.element); 27 28 /** @type {!Map.<!WebInspector.UISourceCode, !WebInspector.UISourceCodeFrame>} */ 29 this._sourceFramesByUISourceCode = new Map(); 30 31 var tabbedEditorPlaceholderText = WebInspector.isMac() ? WebInspector.UIString("Hit Cmd+P to open a file") : WebInspector.UIString("Hit Ctrl+P to open a file"); 32 this._editorContainer = new WebInspector.TabbedEditorContainer(this, "previouslyViewedFiles", tabbedEditorPlaceholderText); 33 this._editorContainer.show(this._searchableView.element); 34 this._editorContainer.addEventListener(WebInspector.TabbedEditorContainer.Events.EditorSelected, this._editorSelected, this); 35 this._editorContainer.addEventListener(WebInspector.TabbedEditorContainer.Events.EditorClosed, this._editorClosed, this); 36 37 this._historyManager = new WebInspector.EditingLocationHistoryManager(this, this.currentSourceFrame.bind(this)); 38 39 this._statusBarContainerElement = this.element.createChild("div", "sources-status-bar"); 40 41 /** 42 * @this {WebInspector.SourcesView} 43 * @param {!WebInspector.SourcesView.EditorAction} EditorAction 44 */ 45 function appendButtonForExtension(EditorAction) 46 { 47 this._statusBarContainerElement.appendChild(EditorAction.button(this)); 48 } 49 var editorActions = /** @type {!Array.<!WebInspector.SourcesView.EditorAction>} */ (self.runtime.instances(WebInspector.SourcesView.EditorAction)); 50 editorActions.forEach(appendButtonForExtension.bind(this)); 51 52 this._scriptViewStatusBarItemsContainer = this._statusBarContainerElement.createChild("div", "inline-block"); 53 this._scriptViewStatusBarTextContainer = this._statusBarContainerElement.createChild("div", "hbox"); 54 55 WebInspector.startBatchUpdate(); 56 this._workspace.uiSourceCodes().forEach(this._addUISourceCode.bind(this)); 57 WebInspector.endBatchUpdate(); 58 59 this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this); 60 this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeRemoved, this._uiSourceCodeRemoved, this); 61 this._workspace.addEventListener(WebInspector.Workspace.Events.ProjectRemoved, this._projectRemoved.bind(this), this); 62 63 function handleBeforeUnload(event) 64 { 65 if (event.returnValue) 66 return; 67 var unsavedSourceCodes = WebInspector.workspace.unsavedSourceCodes(); 68 if (!unsavedSourceCodes.length) 69 return; 70 71 event.returnValue = WebInspector.UIString("DevTools have unsaved changes that will be permanently lost."); 72 WebInspector.inspectorView.showPanel("sources"); 73 for (var i = 0; i < unsavedSourceCodes.length; ++i) 74 WebInspector.Revealer.reveal(unsavedSourceCodes[i]); 75 } 76 window.addEventListener("beforeunload", handleBeforeUnload, true); 77 78 this._shortcuts = {}; 79 this.element.addEventListener("keydown", this._handleKeyDown.bind(this), false); 80 } 81 82 WebInspector.SourcesView.Events = { 83 EditorClosed: "EditorClosed", 84 EditorSelected: "EditorSelected", 85 } 86 87 WebInspector.SourcesView.prototype = { 88 /** 89 * @param {function(!Array.<!WebInspector.KeyboardShortcut.Descriptor>, function(!Event=):boolean)} registerShortcutDelegate 90 */ 91 registerShortcuts: function(registerShortcutDelegate) 92 { 93 /** 94 * @this {WebInspector.SourcesView} 95 * @param {!Array.<!WebInspector.KeyboardShortcut.Descriptor>} shortcuts 96 * @param {function(!Event=):boolean} handler 97 */ 98 function registerShortcut(shortcuts, handler) 99 { 100 registerShortcutDelegate(shortcuts, handler); 101 this._registerShortcuts(shortcuts, handler); 102 } 103 104 registerShortcut.call(this, WebInspector.ShortcutsScreen.SourcesPanelShortcuts.JumpToPreviousLocation, this._onJumpToPreviousLocation.bind(this)); 105 registerShortcut.call(this, WebInspector.ShortcutsScreen.SourcesPanelShortcuts.JumpToNextLocation, this._onJumpToNextLocation.bind(this)); 106 registerShortcut.call(this, WebInspector.ShortcutsScreen.SourcesPanelShortcuts.CloseEditorTab, this._onCloseEditorTab.bind(this)); 107 registerShortcut.call(this, WebInspector.ShortcutsScreen.SourcesPanelShortcuts.GoToLine, this._showGoToLineDialog.bind(this)); 108 registerShortcut.call(this, WebInspector.ShortcutsScreen.SourcesPanelShortcuts.GoToMember, this._showOutlineDialog.bind(this)); 109 registerShortcut.call(this, [WebInspector.KeyboardShortcut.makeDescriptor("o", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta | WebInspector.KeyboardShortcut.Modifiers.Shift)], this._showOutlineDialog.bind(this)); 110 registerShortcut.call(this, WebInspector.ShortcutsScreen.SourcesPanelShortcuts.ToggleBreakpoint, this._toggleBreakpoint.bind(this)); 111 registerShortcut.call(this, WebInspector.ShortcutsScreen.SourcesPanelShortcuts.Save, this._save.bind(this)); 112 registerShortcut.call(this, WebInspector.ShortcutsScreen.SourcesPanelShortcuts.SaveAll, this._saveAll.bind(this)); 113 }, 114 115 /** 116 * @param {!Array.<!WebInspector.KeyboardShortcut.Descriptor>} keys 117 * @param {function(!Event=):boolean} handler 118 */ 119 _registerShortcuts: function(keys, handler) 120 { 121 for (var i = 0; i < keys.length; ++i) 122 this._shortcuts[keys[i].key] = handler; 123 }, 124 125 _handleKeyDown: function(event) 126 { 127 var shortcutKey = WebInspector.KeyboardShortcut.makeKeyFromEvent(event); 128 var handler = this._shortcuts[shortcutKey]; 129 if (handler && handler()) 130 event.consume(true); 131 }, 132 133 wasShown: function() 134 { 135 WebInspector.VBox.prototype.wasShown.call(this); 136 WebInspector.context.setFlavor(WebInspector.SourcesView, this); 137 }, 138 139 willHide: function() 140 { 141 WebInspector.context.setFlavor(WebInspector.SourcesView, null); 142 WebInspector.VBox.prototype.willHide.call(this); 143 }, 144 145 /** 146 * @return {!Element} 147 */ 148 statusBarContainerElement: function() 149 { 150 return this._statusBarContainerElement; 151 }, 152 153 /** 154 * @return {!Element} 155 */ 156 defaultFocusedElement: function() 157 { 158 return this._editorContainer.view.defaultFocusedElement(); 159 }, 160 161 /** 162 * @return {!WebInspector.SearchableView} 163 */ 164 searchableView: function() 165 { 166 return this._searchableView; 167 }, 168 169 /** 170 * @return {!WebInspector.View} 171 */ 172 visibleView: function() 173 { 174 return this._editorContainer.visibleView; 175 }, 176 177 /** 178 * @return {?WebInspector.SourceFrame} 179 */ 180 currentSourceFrame: function() 181 { 182 var view = this.visibleView(); 183 if (!(view instanceof WebInspector.SourceFrame)) 184 return null; 185 return /** @type {!WebInspector.SourceFrame} */ (view); 186 }, 187 188 /** 189 * @return {?WebInspector.UISourceCode} 190 */ 191 currentUISourceCode: function() 192 { 193 return this._currentUISourceCode; 194 }, 195 196 /** 197 * @param {!Event=} event 198 */ 199 _onCloseEditorTab: function(event) 200 { 201 var uiSourceCode = this.currentUISourceCode(); 202 if (!uiSourceCode) 203 return false; 204 this._editorContainer.closeFile(uiSourceCode); 205 return true; 206 }, 207 208 /** 209 * @param {!Event=} event 210 */ 211 _onJumpToPreviousLocation: function(event) 212 { 213 this._historyManager.rollback(); 214 return true; 215 }, 216 217 /** 218 * @param {!Event=} event 219 */ 220 _onJumpToNextLocation: function(event) 221 { 222 this._historyManager.rollover(); 223 return true; 224 }, 225 226 /** 227 * @param {!WebInspector.Event} event 228 */ 229 _uiSourceCodeAdded: function(event) 230 { 231 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data); 232 this._addUISourceCode(uiSourceCode); 233 }, 234 235 /** 236 * @param {!WebInspector.UISourceCode} uiSourceCode 237 */ 238 _addUISourceCode: function(uiSourceCode) 239 { 240 if (uiSourceCode.project().isServiceProject()) 241 return; 242 this._editorContainer.addUISourceCode(uiSourceCode); 243 // Replace debugger script-based uiSourceCode with a network-based one. 244 var currentUISourceCode = this._currentUISourceCode; 245 if (currentUISourceCode && currentUISourceCode.project().isServiceProject() && currentUISourceCode !== uiSourceCode && currentUISourceCode.url === uiSourceCode.url) { 246 this._showFile(uiSourceCode); 247 this._editorContainer.removeUISourceCode(currentUISourceCode); 248 } 249 }, 250 251 _uiSourceCodeRemoved: function(event) 252 { 253 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data); 254 this._removeUISourceCodes([uiSourceCode]); 255 }, 256 257 /** 258 * @param {!Array.<!WebInspector.UISourceCode>} uiSourceCodes 259 */ 260 _removeUISourceCodes: function(uiSourceCodes) 261 { 262 this._editorContainer.removeUISourceCodes(uiSourceCodes); 263 for (var i = 0; i < uiSourceCodes.length; ++i) { 264 this._removeSourceFrame(uiSourceCodes[i]); 265 this._historyManager.removeHistoryForSourceCode(uiSourceCodes[i]); 266 } 267 }, 268 269 _projectRemoved: function(event) 270 { 271 var project = event.data; 272 var uiSourceCodes = project.uiSourceCodes(); 273 this._removeUISourceCodes(uiSourceCodes); 274 if (project.type() === WebInspector.projectTypes.Network) 275 this._editorContainer.reset(); 276 }, 277 278 _updateScriptViewStatusBarItems: function() 279 { 280 this._scriptViewStatusBarItemsContainer.removeChildren(); 281 this._scriptViewStatusBarTextContainer.removeChildren(); 282 var sourceFrame = this.currentSourceFrame(); 283 if (!sourceFrame) 284 return; 285 286 var statusBarItems = sourceFrame.statusBarItems() || []; 287 for (var i = 0; i < statusBarItems.length; ++i) 288 this._scriptViewStatusBarItemsContainer.appendChild(statusBarItems[i]); 289 var statusBarText = sourceFrame.statusBarText(); 290 if (statusBarText) 291 this._scriptViewStatusBarTextContainer.appendChild(statusBarText); 292 }, 293 294 /** 295 * @param {!WebInspector.UISourceCode} uiSourceCode 296 * @param {number=} lineNumber 0-based 297 * @param {number=} columnNumber 298 * @param {boolean=} omitFocus 299 * @param {boolean=} omitHighlight 300 */ 301 showSourceLocation: function(uiSourceCode, lineNumber, columnNumber, omitFocus, omitHighlight) 302 { 303 this._historyManager.updateCurrentState(); 304 var sourceFrame = this._showFile(uiSourceCode); 305 if (typeof lineNumber === "number") 306 sourceFrame.revealPosition(lineNumber, columnNumber, !omitHighlight); 307 this._historyManager.pushNewState(); 308 if (!omitFocus) 309 sourceFrame.focus(); 310 WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, { 311 action: WebInspector.UserMetrics.UserActionNames.OpenSourceLink, 312 url: uiSourceCode.originURL(), 313 lineNumber: lineNumber 314 }); 315 }, 316 317 /** 318 * @param {!WebInspector.UISourceCode} uiSourceCode 319 * @return {!WebInspector.SourceFrame} 320 */ 321 _showFile: function(uiSourceCode) 322 { 323 var sourceFrame = this._getOrCreateSourceFrame(uiSourceCode); 324 if (this._currentUISourceCode === uiSourceCode) 325 return sourceFrame; 326 327 this._currentUISourceCode = uiSourceCode; 328 this._editorContainer.showFile(uiSourceCode); 329 this._updateScriptViewStatusBarItems(); 330 return sourceFrame; 331 }, 332 333 /** 334 * @param {!WebInspector.UISourceCode} uiSourceCode 335 * @return {!WebInspector.UISourceCodeFrame} 336 */ 337 _createSourceFrame: function(uiSourceCode) 338 { 339 var sourceFrame; 340 switch (uiSourceCode.contentType()) { 341 case WebInspector.resourceTypes.Script: 342 sourceFrame = new WebInspector.JavaScriptSourceFrame(this._sourcesPanel, uiSourceCode); 343 break; 344 case WebInspector.resourceTypes.Document: 345 sourceFrame = new WebInspector.JavaScriptSourceFrame(this._sourcesPanel, uiSourceCode); 346 break; 347 case WebInspector.resourceTypes.Stylesheet: 348 sourceFrame = new WebInspector.CSSSourceFrame(uiSourceCode); 349 break; 350 default: 351 sourceFrame = new WebInspector.UISourceCodeFrame(uiSourceCode); 352 break; 353 } 354 sourceFrame.setHighlighterType(uiSourceCode.highlighterType()); 355 this._sourceFramesByUISourceCode.set(uiSourceCode, sourceFrame); 356 this._historyManager.trackSourceFrameCursorJumps(sourceFrame); 357 return sourceFrame; 358 }, 359 360 /** 361 * @param {!WebInspector.UISourceCode} uiSourceCode 362 * @return {!WebInspector.UISourceCodeFrame} 363 */ 364 _getOrCreateSourceFrame: function(uiSourceCode) 365 { 366 return this._sourceFramesByUISourceCode.get(uiSourceCode) || this._createSourceFrame(uiSourceCode); 367 }, 368 369 /** 370 * @param {!WebInspector.SourceFrame} sourceFrame 371 * @param {!WebInspector.UISourceCode} uiSourceCode 372 * @return {boolean} 373 */ 374 _sourceFrameMatchesUISourceCode: function(sourceFrame, uiSourceCode) 375 { 376 switch (uiSourceCode.contentType()) { 377 case WebInspector.resourceTypes.Script: 378 case WebInspector.resourceTypes.Document: 379 return sourceFrame instanceof WebInspector.JavaScriptSourceFrame; 380 case WebInspector.resourceTypes.Stylesheet: 381 return sourceFrame instanceof WebInspector.CSSSourceFrame; 382 default: 383 return !(sourceFrame instanceof WebInspector.JavaScriptSourceFrame); 384 } 385 }, 386 387 /** 388 * @param {!WebInspector.UISourceCode} uiSourceCode 389 */ 390 _recreateSourceFrameIfNeeded: function(uiSourceCode) 391 { 392 var oldSourceFrame = this._sourceFramesByUISourceCode.get(uiSourceCode); 393 if (!oldSourceFrame) 394 return; 395 if (this._sourceFrameMatchesUISourceCode(oldSourceFrame, uiSourceCode)) { 396 oldSourceFrame.setHighlighterType(uiSourceCode.highlighterType()); 397 } else { 398 this._editorContainer.removeUISourceCode(uiSourceCode); 399 this._removeSourceFrame(uiSourceCode); 400 } 401 }, 402 403 /** 404 * @param {!WebInspector.UISourceCode} uiSourceCode 405 * @return {!WebInspector.UISourceCodeFrame} 406 */ 407 viewForFile: function(uiSourceCode) 408 { 409 return this._getOrCreateSourceFrame(uiSourceCode); 410 }, 411 412 /** 413 * @param {!WebInspector.UISourceCode} uiSourceCode 414 */ 415 _removeSourceFrame: function(uiSourceCode) 416 { 417 var sourceFrame = this._sourceFramesByUISourceCode.get(uiSourceCode); 418 if (!sourceFrame) 419 return; 420 this._sourceFramesByUISourceCode.remove(uiSourceCode); 421 sourceFrame.dispose(); 422 }, 423 424 clearCurrentExecutionLine: function() 425 { 426 if (this._executionSourceFrame) 427 this._executionSourceFrame.clearExecutionLine(); 428 delete this._executionSourceFrame; 429 }, 430 431 setExecutionLine: function(uiLocation) 432 { 433 var sourceFrame = this._getOrCreateSourceFrame(uiLocation.uiSourceCode); 434 sourceFrame.setExecutionLine(uiLocation.lineNumber); 435 this._executionSourceFrame = sourceFrame; 436 }, 437 438 _editorClosed: function(event) 439 { 440 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data); 441 this._historyManager.removeHistoryForSourceCode(uiSourceCode); 442 443 var wasSelected = false; 444 if (this._currentUISourceCode === uiSourceCode) { 445 delete this._currentUISourceCode; 446 wasSelected = true; 447 } 448 449 // SourcesNavigator does not need to update on EditorClosed. 450 this._updateScriptViewStatusBarItems(); 451 this._searchableView.resetSearch(); 452 453 var data = {}; 454 data.uiSourceCode = uiSourceCode; 455 data.wasSelected = wasSelected; 456 this.dispatchEventToListeners(WebInspector.SourcesView.Events.EditorClosed, data); 457 }, 458 459 _editorSelected: function(event) 460 { 461 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data.currentFile); 462 var shouldUseHistoryManager = uiSourceCode !== this._currentUISourceCode && event.data.userGesture; 463 if (shouldUseHistoryManager) 464 this._historyManager.updateCurrentState(); 465 var sourceFrame = this._showFile(uiSourceCode); 466 if (shouldUseHistoryManager) 467 this._historyManager.pushNewState(); 468 469 this._searchableView.setReplaceable(!!sourceFrame && sourceFrame.canEditSource()); 470 this._searchableView.resetSearch(); 471 472 this.dispatchEventToListeners(WebInspector.SourcesView.Events.EditorSelected, uiSourceCode); 473 }, 474 475 /** 476 * @param {!WebInspector.UISourceCode} uiSourceCode 477 */ 478 sourceRenamed: function(uiSourceCode) 479 { 480 this._recreateSourceFrameIfNeeded(uiSourceCode); 481 }, 482 483 searchCanceled: function() 484 { 485 if (this._searchView) 486 this._searchView.searchCanceled(); 487 488 delete this._searchView; 489 delete this._searchQuery; 490 }, 491 492 /** 493 * @param {string} query 494 * @param {boolean} shouldJump 495 * @param {boolean=} jumpBackwards 496 */ 497 performSearch: function(query, shouldJump, jumpBackwards) 498 { 499 this._searchableView.updateSearchMatchesCount(0); 500 501 var sourceFrame = this.currentSourceFrame(); 502 if (!sourceFrame) 503 return; 504 505 this._searchView = sourceFrame; 506 this._searchQuery = query; 507 508 /** 509 * @param {!WebInspector.View} view 510 * @param {number} searchMatches 511 * @this {WebInspector.SourcesView} 512 */ 513 function finishedCallback(view, searchMatches) 514 { 515 if (!searchMatches) 516 return; 517 518 this._searchableView.updateSearchMatchesCount(searchMatches); 519 } 520 521 /** 522 * @param {number} currentMatchIndex 523 * @this {WebInspector.SourcesView} 524 */ 525 function currentMatchChanged(currentMatchIndex) 526 { 527 this._searchableView.updateCurrentMatchIndex(currentMatchIndex); 528 } 529 530 /** 531 * @this {WebInspector.SourcesView} 532 */ 533 function searchResultsChanged() 534 { 535 this.performSearch(query, false, false); 536 } 537 538 this._searchView.performSearch(query, shouldJump, !!jumpBackwards, finishedCallback.bind(this), currentMatchChanged.bind(this), searchResultsChanged.bind(this)); 539 }, 540 541 jumpToNextSearchResult: function() 542 { 543 if (!this._searchView) 544 return; 545 546 if (this._searchView !== this.currentSourceFrame()) { 547 this.performSearch(this._searchQuery, true); 548 return; 549 } 550 551 this._searchView.jumpToNextSearchResult(); 552 }, 553 554 jumpToPreviousSearchResult: function() 555 { 556 if (!this._searchView) 557 return; 558 559 if (this._searchView !== this.currentSourceFrame()) { 560 this.performSearch(this._searchQuery, true); 561 if (this._searchView) 562 this._searchView.jumpToLastSearchResult(); 563 return; 564 } 565 566 this._searchView.jumpToPreviousSearchResult(); 567 }, 568 569 /** 570 * @param {string} text 571 */ 572 replaceSelectionWith: function(text) 573 { 574 var sourceFrame = this.currentSourceFrame(); 575 if (!sourceFrame) { 576 console.assert(sourceFrame); 577 return; 578 } 579 sourceFrame.replaceSelectionWith(text); 580 }, 581 582 /** 583 * @param {string} query 584 * @param {string} text 585 */ 586 replaceAllWith: function(query, text) 587 { 588 var sourceFrame = this.currentSourceFrame(); 589 if (!sourceFrame) { 590 console.assert(sourceFrame); 591 return; 592 } 593 sourceFrame.replaceAllWith(query, text); 594 }, 595 596 /** 597 * @param {!Event=} event 598 * @return {boolean} 599 */ 600 _showOutlineDialog: function(event) 601 { 602 var uiSourceCode = this._editorContainer.currentFile(); 603 if (!uiSourceCode) 604 return false; 605 606 switch (uiSourceCode.contentType()) { 607 case WebInspector.resourceTypes.Document: 608 case WebInspector.resourceTypes.Script: 609 WebInspector.JavaScriptOutlineDialog.show(this, uiSourceCode, this.showSourceLocation.bind(this, uiSourceCode)); 610 return true; 611 case WebInspector.resourceTypes.Stylesheet: 612 WebInspector.StyleSheetOutlineDialog.show(this, uiSourceCode, this.showSourceLocation.bind(this, uiSourceCode)); 613 return true; 614 default: 615 // We don't want default browser shortcut to be executed, so pretend to handle this event. 616 return true; 617 } 618 }, 619 620 /** 621 * @param {string=} query 622 */ 623 showOpenResourceDialog: function(query) 624 { 625 var uiSourceCodes = this._editorContainer.historyUISourceCodes(); 626 /** @type {!Map.<!WebInspector.UISourceCode, number>} */ 627 var defaultScores = new Map(); 628 for (var i = 1; i < uiSourceCodes.length; ++i) // Skip current element 629 defaultScores.set(uiSourceCodes[i], uiSourceCodes.length - i); 630 WebInspector.OpenResourceDialog.show(this, this.element, query, defaultScores); 631 }, 632 633 /** 634 * @param {!Event=} event 635 * @return {boolean} 636 */ 637 _showGoToLineDialog: function(event) 638 { 639 if (this._currentUISourceCode) 640 this.showOpenResourceDialog(":"); 641 return true; 642 }, 643 644 /** 645 * @return {boolean} 646 */ 647 _save: function() 648 { 649 this._saveSourceFrame(this.currentSourceFrame()); 650 return true; 651 }, 652 653 /** 654 * @return {boolean} 655 */ 656 _saveAll: function() 657 { 658 var sourceFrames = this._editorContainer.fileViews(); 659 sourceFrames.forEach(this._saveSourceFrame.bind(this)); 660 return true; 661 }, 662 663 /** 664 * @param {?WebInspector.SourceFrame} sourceFrame 665 */ 666 _saveSourceFrame: function(sourceFrame) 667 { 668 if (!sourceFrame) 669 return; 670 if (!(sourceFrame instanceof WebInspector.UISourceCodeFrame)) 671 return; 672 var uiSourceCodeFrame = /** @type {!WebInspector.UISourceCodeFrame} */ (sourceFrame); 673 uiSourceCodeFrame.commitEditing(); 674 }, 675 /** 676 * @return {boolean} 677 */ 678 _toggleBreakpoint: function() 679 { 680 var sourceFrame = this.currentSourceFrame(); 681 if (!sourceFrame) 682 return false; 683 684 if (sourceFrame instanceof WebInspector.JavaScriptSourceFrame) { 685 var javaScriptSourceFrame = /** @type {!WebInspector.JavaScriptSourceFrame} */ (sourceFrame); 686 javaScriptSourceFrame.toggleBreakpointOnCurrentLine(); 687 return true; 688 } 689 return false; 690 }, 691 692 /** 693 * @param {boolean} active 694 */ 695 toggleBreakpointsActiveState: function(active) 696 { 697 this._editorContainer.view.element.classList.toggle("breakpoints-deactivated", !active); 698 }, 699 700 __proto__: WebInspector.VBox.prototype 701 } 702 703 /** 704 * @interface 705 */ 706 WebInspector.SourcesView.EditorAction = function() 707 { 708 } 709 710 WebInspector.SourcesView.EditorAction.prototype = { 711 /** 712 * @param {!WebInspector.SourcesView} sourcesView 713 * @return {!Element} 714 */ 715 button: function(sourcesView) { } 716 } 717 718 /** 719 * @constructor 720 * @implements {WebInspector.ActionDelegate} 721 */ 722 WebInspector.SourcesView.SwitchFileActionDelegate = function() 723 { 724 } 725 726 /** 727 * @param {!WebInspector.UISourceCode} currentUISourceCode 728 * @return {?WebInspector.UISourceCode} 729 */ 730 WebInspector.SourcesView.SwitchFileActionDelegate._nextFile = function(currentUISourceCode) 731 { 732 /** 733 * @param {string} name 734 * @return {string} 735 */ 736 function fileNamePrefix(name) 737 { 738 var lastDotIndex = name.lastIndexOf("."); 739 var namePrefix = name.substr(0, lastDotIndex !== -1 ? lastDotIndex : name.length); 740 return namePrefix.toLowerCase(); 741 } 742 743 var uiSourceCodes = currentUISourceCode.project().uiSourceCodes(); 744 var candidates = []; 745 var path = currentUISourceCode.parentPath(); 746 var name = currentUISourceCode.name(); 747 var namePrefix = fileNamePrefix(name); 748 for (var i = 0; i < uiSourceCodes.length; ++i) { 749 var uiSourceCode = uiSourceCodes[i]; 750 if (path !== uiSourceCode.parentPath()) 751 continue; 752 if (fileNamePrefix(uiSourceCode.name()) === namePrefix) 753 candidates.push(uiSourceCode.name()); 754 } 755 candidates.sort(String.naturalOrderComparator); 756 var index = mod(candidates.indexOf(name) + 1, candidates.length); 757 var fullPath = (path ? path + "/" : "") + candidates[index]; 758 var nextUISourceCode = currentUISourceCode.project().uiSourceCode(fullPath); 759 return nextUISourceCode !== currentUISourceCode ? nextUISourceCode : null; 760 }, 761 762 763 WebInspector.SourcesView.SwitchFileActionDelegate.prototype = { 764 /** 765 * @return {boolean} 766 */ 767 handleAction: function() 768 { 769 var sourcesView = WebInspector.context.flavor(WebInspector.SourcesView); 770 if (!sourcesView) 771 return false; 772 var currentUISourceCode = sourcesView.currentUISourceCode(); 773 if (!currentUISourceCode) 774 return true; 775 var nextUISourceCode = WebInspector.SourcesView.SwitchFileActionDelegate._nextFile(currentUISourceCode); 776 if (!nextUISourceCode) 777 return true; 778 sourcesView.showSourceLocation(nextUISourceCode); 779 return true; 780 } 781 } 782