1 /* 2 * Copyright (C) 2011 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. AND ITS CONTRIBUTORS 17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. 20 * OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /** 30 * @interface 31 */ 32 WebInspector.TabbedEditorContainerDelegate = function() { } 33 34 WebInspector.TabbedEditorContainerDelegate.prototype = { 35 /** 36 * @param {WebInspector.UISourceCode} uiSourceCode 37 * @return {WebInspector.SourceFrame} 38 */ 39 viewForFile: function(uiSourceCode) { } 40 } 41 42 /** 43 * @constructor 44 * @extends {WebInspector.Object} 45 * @param {WebInspector.TabbedEditorContainerDelegate} delegate 46 * @param {string} settingName 47 * @param {string} placeholderText 48 */ 49 WebInspector.TabbedEditorContainer = function(delegate, settingName, placeholderText) 50 { 51 WebInspector.Object.call(this); 52 this._delegate = delegate; 53 54 this._tabbedPane = new WebInspector.TabbedPane(); 55 this._tabbedPane.setPlaceholderText(placeholderText); 56 this._tabbedPane.setTabDelegate(new WebInspector.EditorContainerTabDelegate(this)); 57 58 this._tabbedPane.closeableTabs = true; 59 this._tabbedPane.element.id = "scripts-editor-container-tabbed-pane"; 60 61 this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabClosed, this._tabClosed, this); 62 this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this); 63 64 this._tabIds = new Map(); 65 this._files = {}; 66 67 this._previouslyViewedFilesSetting = WebInspector.settings.createSetting(settingName, []); 68 this._history = WebInspector.TabbedEditorContainer.History.fromObject(this._previouslyViewedFilesSetting.get()); 69 } 70 71 72 WebInspector.TabbedEditorContainer.Events = { 73 EditorSelected: "EditorSelected", 74 EditorClosed: "EditorClosed" 75 } 76 77 WebInspector.TabbedEditorContainer._tabId = 0; 78 79 WebInspector.TabbedEditorContainer.maximalPreviouslyViewedFilesCount = 30; 80 81 WebInspector.TabbedEditorContainer.prototype = { 82 /** 83 * @return {WebInspector.View} 84 */ 85 get view() 86 { 87 return this._tabbedPane; 88 }, 89 90 /** 91 * @type {WebInspector.SourceFrame} 92 */ 93 get visibleView() 94 { 95 return this._tabbedPane.visibleView; 96 }, 97 98 /** 99 * @param {Element} parentElement 100 */ 101 show: function(parentElement) 102 { 103 this._tabbedPane.show(parentElement); 104 }, 105 106 /** 107 * @param {WebInspector.UISourceCode} uiSourceCode 108 */ 109 showFile: function(uiSourceCode) 110 { 111 this._innerShowFile(uiSourceCode, true); 112 }, 113 114 /** 115 * @return {Array.<WebInspector.UISourceCode>} 116 */ 117 historyUISourceCodes: function() 118 { 119 // FIXME: there should be a way to fetch UISourceCode for its uri. 120 var uriToUISourceCode = {}; 121 for (var id in this._files) { 122 var uiSourceCode = this._files[id]; 123 uriToUISourceCode[uiSourceCode.uri()] = uiSourceCode; 124 } 125 126 var result = []; 127 var uris = this._history._urls(); 128 for (var i = 0; i < uris.length; ++i) { 129 var uiSourceCode = uriToUISourceCode[uris[i]]; 130 if (uiSourceCode) 131 result.push(uiSourceCode); 132 } 133 return result; 134 }, 135 136 _addScrollAndSelectionListeners: function() 137 { 138 if (!this._currentView) 139 return; 140 this._currentView.addEventListener(WebInspector.SourceFrame.Events.ScrollChanged, this._scrollChanged, this); 141 this._currentView.addEventListener(WebInspector.SourceFrame.Events.SelectionChanged, this._selectionChanged, this); 142 }, 143 144 _removeScrollAndSelectionListeners: function() 145 { 146 if (!this._currentView) 147 return; 148 this._currentView.removeEventListener(WebInspector.SourceFrame.Events.ScrollChanged, this._scrollChanged, this); 149 this._currentView.removeEventListener(WebInspector.SourceFrame.Events.SelectionChanged, this._selectionChanged, this); 150 }, 151 152 _scrollChanged: function(event) 153 { 154 var lineNumber = /** @type {number} */ (event.data); 155 this._history.updateScrollLineNumber(this._currentFile.uri(), lineNumber); 156 this._history.save(this._previouslyViewedFilesSetting); 157 }, 158 159 _selectionChanged: function(event) 160 { 161 var range = /** @type {WebInspector.TextRange} */ (event.data); 162 this._history.updateSelectionRange(this._currentFile.uri(), range); 163 this._history.save(this._previouslyViewedFilesSetting); 164 }, 165 166 /** 167 * @param {WebInspector.UISourceCode} uiSourceCode 168 * @param {boolean=} userGesture 169 */ 170 _innerShowFile: function(uiSourceCode, userGesture) 171 { 172 if (this._currentFile === uiSourceCode) 173 return; 174 this._removeScrollAndSelectionListeners(); 175 this._currentFile = uiSourceCode; 176 177 var tabId = this._tabIds.get(uiSourceCode) || this._appendFileTab(uiSourceCode, userGesture); 178 179 this._tabbedPane.selectTab(tabId, userGesture); 180 if (userGesture) 181 this._editorSelectedByUserAction(); 182 183 this._currentView = this.visibleView; 184 this._addScrollAndSelectionListeners(); 185 186 this.dispatchEventToListeners(WebInspector.TabbedEditorContainer.Events.EditorSelected, this._currentFile); 187 }, 188 189 /** 190 * @param {WebInspector.UISourceCode} uiSourceCode 191 * @return {string} 192 */ 193 _titleForFile: function(uiSourceCode) 194 { 195 var maxDisplayNameLength = 30; 196 var title = uiSourceCode.displayName(true).trimMiddle(maxDisplayNameLength); 197 if (uiSourceCode.isDirty()) 198 title += "*"; 199 return title; 200 }, 201 202 /** 203 * @param {string} id 204 * @param {string} nextTabId 205 */ 206 _maybeCloseTab: function(id, nextTabId) 207 { 208 var uiSourceCode = this._files[id]; 209 var shouldPrompt = uiSourceCode.isDirty() && uiSourceCode.project().canSetFileContent(); 210 // FIXME: this should be replaced with common Save/Discard/Cancel dialog. 211 if (!shouldPrompt || confirm(WebInspector.UIString("Are you sure you want to close unsaved file: %s?", uiSourceCode.name()))) { 212 uiSourceCode.resetWorkingCopy(); 213 if (nextTabId) 214 this._tabbedPane.selectTab(nextTabId, true); 215 this._tabbedPane.closeTab(id, true); 216 return true; 217 } 218 return false; 219 }, 220 221 /** 222 * @param {Array.<string>} ids 223 */ 224 _closeTabs: function(ids) 225 { 226 var dirtyTabs = []; 227 var cleanTabs = []; 228 for (var i = 0; i < ids.length; ++i) { 229 var id = ids[i]; 230 var uiSourceCode = this._files[id]; 231 if (uiSourceCode.isDirty()) 232 dirtyTabs.push(id); 233 else 234 cleanTabs.push(id); 235 } 236 if (dirtyTabs.length) 237 this._tabbedPane.selectTab(dirtyTabs[0], true); 238 this._tabbedPane.closeTabs(cleanTabs, true); 239 for (var i = 0; i < dirtyTabs.length; ++i) { 240 var nextTabId = i + 1 < dirtyTabs.length ? dirtyTabs[i + 1] : null; 241 if (!this._maybeCloseTab(dirtyTabs[i], nextTabId)) 242 break; 243 } 244 }, 245 246 /** 247 * @param {WebInspector.UISourceCode} uiSourceCode 248 */ 249 addUISourceCode: function(uiSourceCode) 250 { 251 var uri = uiSourceCode.uri(); 252 if (this._userSelectedFiles) 253 return; 254 255 var index = this._history.index(uri) 256 if (index === -1) 257 return; 258 259 var tabId = this._tabIds.get(uiSourceCode) || this._appendFileTab(uiSourceCode, false); 260 261 if (!this._currentFile) 262 return; 263 264 // Select tab if this file was the last to be shown. 265 if (!index) { 266 this._innerShowFile(uiSourceCode, false); 267 return; 268 } 269 270 var currentProjectType = this._currentFile.project().type(); 271 var addedProjectType = uiSourceCode.project().type(); 272 var snippetsProjectType = WebInspector.projectTypes.Snippets; 273 if (this._history.index(this._currentFile.uri()) && currentProjectType === snippetsProjectType && addedProjectType !== snippetsProjectType) 274 this._innerShowFile(uiSourceCode, false); 275 }, 276 277 /** 278 * @param {WebInspector.UISourceCode} uiSourceCode 279 */ 280 removeUISourceCode: function(uiSourceCode) 281 { 282 this.removeUISourceCodes([uiSourceCode]); 283 }, 284 285 /** 286 * @param {Array.<WebInspector.UISourceCode>} uiSourceCodes 287 */ 288 removeUISourceCodes: function(uiSourceCodes) 289 { 290 var tabIds = []; 291 for (var i = 0; i < uiSourceCodes.length; ++i) { 292 var uiSourceCode = uiSourceCodes[i]; 293 var tabId = this._tabIds.get(uiSourceCode); 294 if (tabId) 295 tabIds.push(tabId); 296 } 297 this._tabbedPane.closeTabs(tabIds); 298 }, 299 300 /** 301 * @param {WebInspector.UISourceCode} uiSourceCode 302 */ 303 _editorClosedByUserAction: function(uiSourceCode) 304 { 305 this._userSelectedFiles = true; 306 this._history.remove(uiSourceCode.uri()); 307 this._updateHistory(); 308 }, 309 310 _editorSelectedByUserAction: function() 311 { 312 this._userSelectedFiles = true; 313 this._updateHistory(); 314 }, 315 316 _updateHistory: function() 317 { 318 var tabIds = this._tabbedPane.lastOpenedTabIds(WebInspector.TabbedEditorContainer.maximalPreviouslyViewedFilesCount); 319 320 function tabIdToURI(tabId) 321 { 322 return this._files[tabId].uri(); 323 } 324 325 this._history.update(tabIds.map(tabIdToURI.bind(this))); 326 this._history.save(this._previouslyViewedFilesSetting); 327 }, 328 329 /** 330 * @param {WebInspector.UISourceCode} uiSourceCode 331 * @return {string} 332 */ 333 _tooltipForFile: function(uiSourceCode) 334 { 335 return uiSourceCode.originURL(); 336 }, 337 338 /** 339 * @param {WebInspector.UISourceCode} uiSourceCode 340 * @param {boolean=} userGesture 341 * @return {string} 342 */ 343 _appendFileTab: function(uiSourceCode, userGesture) 344 { 345 var view = this._delegate.viewForFile(uiSourceCode); 346 var title = this._titleForFile(uiSourceCode); 347 var tooltip = this._tooltipForFile(uiSourceCode); 348 349 var tabId = this._generateTabId(); 350 this._tabIds.put(uiSourceCode, tabId); 351 this._files[tabId] = uiSourceCode; 352 353 var savedSelectionRange = this._history.selectionRange(uiSourceCode.uri()); 354 if (savedSelectionRange) 355 view.setSelection(savedSelectionRange); 356 var savedScrollLineNumber = this._history.scrollLineNumber(uiSourceCode.uri()); 357 if (savedScrollLineNumber) 358 view.scrollToLine(savedScrollLineNumber); 359 360 this._tabbedPane.appendTab(tabId, title, view, tooltip, userGesture); 361 362 this._addUISourceCodeListeners(uiSourceCode); 363 return tabId; 364 }, 365 366 /** 367 * @param {WebInspector.Event} event 368 */ 369 _tabClosed: function(event) 370 { 371 var tabId = /** @type {string} */ (event.data.tabId); 372 var userGesture = /** @type {boolean} */ (event.data.isUserGesture); 373 374 var uiSourceCode = this._files[tabId]; 375 if (this._currentFile === uiSourceCode) { 376 this._removeScrollAndSelectionListeners(); 377 delete this._currentView; 378 delete this._currentFile; 379 } 380 this._tabIds.remove(uiSourceCode); 381 delete this._files[tabId]; 382 383 this._removeUISourceCodeListeners(uiSourceCode); 384 385 this.dispatchEventToListeners(WebInspector.TabbedEditorContainer.Events.EditorClosed, uiSourceCode); 386 387 if (userGesture) 388 this._editorClosedByUserAction(uiSourceCode); 389 }, 390 391 /** 392 * @param {WebInspector.Event} event 393 */ 394 _tabSelected: function(event) 395 { 396 var tabId = /** @type {string} */ (event.data.tabId); 397 var userGesture = /** @type {boolean} */ (event.data.isUserGesture); 398 399 var uiSourceCode = this._files[tabId]; 400 this._innerShowFile(uiSourceCode, userGesture); 401 }, 402 403 /** 404 * @param {WebInspector.UISourceCode} uiSourceCode 405 */ 406 _addUISourceCodeListeners: function(uiSourceCode) 407 { 408 uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.TitleChanged, this._uiSourceCodeTitleChanged, this); 409 uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._uiSourceCodeWorkingCopyChanged, this); 410 uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._uiSourceCodeWorkingCopyCommitted, this); 411 uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.SavedStateUpdated, this._uiSourceCodeSavedStateUpdated, this); 412 uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.FormattedChanged, this._uiSourceCodeFormattedChanged, this); 413 }, 414 415 /** 416 * @param {WebInspector.UISourceCode} uiSourceCode 417 */ 418 _removeUISourceCodeListeners: function(uiSourceCode) 419 { 420 uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.TitleChanged, this._uiSourceCodeTitleChanged, this); 421 uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._uiSourceCodeWorkingCopyChanged, this); 422 uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._uiSourceCodeWorkingCopyCommitted, this); 423 uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.SavedStateUpdated, this._uiSourceCodeSavedStateUpdated, this); 424 uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.FormattedChanged, this._uiSourceCodeFormattedChanged, this); 425 }, 426 427 /** 428 * @param {WebInspector.UISourceCode} uiSourceCode 429 */ 430 _updateFileTitle: function(uiSourceCode) 431 { 432 var tabId = this._tabIds.get(uiSourceCode); 433 if (tabId) { 434 var title = this._titleForFile(uiSourceCode); 435 this._tabbedPane.changeTabTitle(tabId, title); 436 if (uiSourceCode.hasUnsavedCommittedChanges()) 437 this._tabbedPane.setTabIcon(tabId, "editor-container-unsaved-committed-changes-icon", WebInspector.UIString("Changes to this file were not saved to file system.")); 438 else 439 this._tabbedPane.setTabIcon(tabId, ""); 440 } 441 }, 442 443 _uiSourceCodeTitleChanged: function(event) 444 { 445 var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.target); 446 this._updateFileTitle(uiSourceCode); 447 this._updateHistory(); 448 }, 449 450 _uiSourceCodeWorkingCopyChanged: function(event) 451 { 452 var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.target); 453 this._updateFileTitle(uiSourceCode); 454 }, 455 456 _uiSourceCodeWorkingCopyCommitted: function(event) 457 { 458 var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.target); 459 this._updateFileTitle(uiSourceCode); 460 }, 461 462 _uiSourceCodeSavedStateUpdated: function(event) 463 { 464 var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.target); 465 this._updateFileTitle(uiSourceCode); 466 }, 467 468 _uiSourceCodeFormattedChanged: function(event) 469 { 470 var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.target); 471 this._updateFileTitle(uiSourceCode); 472 }, 473 474 reset: function() 475 { 476 delete this._userSelectedFiles; 477 }, 478 479 /** 480 * @return {string} 481 */ 482 _generateTabId: function() 483 { 484 return "tab_" + (WebInspector.TabbedEditorContainer._tabId++); 485 }, 486 487 /** 488 * @return {WebInspector.UISourceCode} uiSourceCode 489 */ 490 currentFile: function() 491 { 492 return this._currentFile; 493 }, 494 495 __proto__: WebInspector.Object.prototype 496 } 497 498 /** 499 * @constructor 500 * @param {string} url 501 * @param {WebInspector.TextRange=} selectionRange 502 * @param {number=} scrollLineNumber 503 */ 504 WebInspector.TabbedEditorContainer.HistoryItem = function(url, selectionRange, scrollLineNumber) 505 { 506 /** @const */ this.url = url; 507 /** @const */ this._isSerializable = url.length < WebInspector.TabbedEditorContainer.HistoryItem.serializableUrlLengthLimit; 508 this.selectionRange = selectionRange; 509 this.scrollLineNumber = scrollLineNumber; 510 } 511 512 WebInspector.TabbedEditorContainer.HistoryItem.serializableUrlLengthLimit = 4096; 513 514 /** 515 * @param {Object} serializedHistoryItem 516 * @return {WebInspector.TabbedEditorContainer.HistoryItem} 517 */ 518 WebInspector.TabbedEditorContainer.HistoryItem.fromObject = function (serializedHistoryItem) 519 { 520 var selectionRange = serializedHistoryItem.selectionRange ? WebInspector.TextRange.fromObject(serializedHistoryItem.selectionRange) : null; 521 return new WebInspector.TabbedEditorContainer.HistoryItem(serializedHistoryItem.url, selectionRange, serializedHistoryItem.scrollLineNumber); 522 } 523 524 WebInspector.TabbedEditorContainer.HistoryItem.prototype = { 525 /** 526 * @return {?Object} 527 */ 528 serializeToObject: function() 529 { 530 if (!this._isSerializable) 531 return null; 532 var serializedHistoryItem = {}; 533 serializedHistoryItem.url = this.url; 534 serializedHistoryItem.selectionRange = this.selectionRange; 535 serializedHistoryItem.scrollLineNumber = this.scrollLineNumber; 536 return serializedHistoryItem; 537 }, 538 539 __proto__: WebInspector.Object.prototype 540 } 541 542 /** 543 * @constructor 544 * @param {Array.<WebInspector.TabbedEditorContainer.HistoryItem>} items 545 */ 546 WebInspector.TabbedEditorContainer.History = function(items) 547 { 548 this._items = items; 549 this._rebuildItemIndex(); 550 } 551 552 /** 553 * @param {!Array.<!Object>} serializedHistory 554 * @return {WebInspector.TabbedEditorContainer.History} 555 */ 556 WebInspector.TabbedEditorContainer.History.fromObject = function(serializedHistory) 557 { 558 var items = []; 559 for (var i = 0; i < serializedHistory.length; ++i) 560 items.push(WebInspector.TabbedEditorContainer.HistoryItem.fromObject(serializedHistory[i])); 561 return new WebInspector.TabbedEditorContainer.History(items); 562 } 563 564 WebInspector.TabbedEditorContainer.History.prototype = { 565 /** 566 * @param {string} url 567 * @return {number} 568 */ 569 index: function(url) 570 { 571 var index = this._itemsIndex[url]; 572 if (typeof index === "number") 573 return index; 574 return -1; 575 }, 576 577 _rebuildItemIndex: function() 578 { 579 this._itemsIndex = {}; 580 for (var i = 0; i < this._items.length; ++i) { 581 console.assert(!this._itemsIndex.hasOwnProperty(this._items[i].url)); 582 this._itemsIndex[this._items[i].url] = i; 583 } 584 }, 585 586 /** 587 * @param {string} url 588 * @return {WebInspector.TextRange|undefined} 589 */ 590 selectionRange: function(url) 591 { 592 var index = this.index(url); 593 return index !== -1 ? this._items[index].selectionRange : undefined; 594 }, 595 596 /** 597 * @param {string} url 598 * @param {WebInspector.TextRange} selectionRange 599 */ 600 updateSelectionRange: function(url, selectionRange) 601 { 602 if (!selectionRange) 603 return; 604 var index = this.index(url); 605 if (index === -1) 606 return; 607 this._items[index].selectionRange = selectionRange; 608 }, 609 610 /** 611 * @param {string} url 612 * @return {number|undefined} 613 */ 614 scrollLineNumber: function(url) 615 { 616 var index = this.index(url); 617 return index !== -1 ? this._items[index].scrollLineNumber : undefined; 618 }, 619 620 /** 621 * @param {string} url 622 * @param {number} scrollLineNumber 623 */ 624 updateScrollLineNumber: function(url, scrollLineNumber) 625 { 626 var index = this.index(url); 627 if (index === -1) 628 return; 629 this._items[index].scrollLineNumber = scrollLineNumber; 630 }, 631 632 /** 633 * @param {Array.<string>} urls 634 */ 635 update: function(urls) 636 { 637 for (var i = urls.length - 1; i >= 0; --i) { 638 var index = this.index(urls[i]); 639 var item; 640 if (index !== -1) { 641 item = this._items[index]; 642 this._items.splice(index, 1); 643 } else 644 item = new WebInspector.TabbedEditorContainer.HistoryItem(urls[i]); 645 this._items.unshift(item); 646 this._rebuildItemIndex(); 647 } 648 }, 649 650 /** 651 * @param {string} url 652 */ 653 remove: function(url) 654 { 655 var index = this.index(url); 656 if (index !== -1) { 657 this._items.splice(index, 1); 658 this._rebuildItemIndex(); 659 } 660 }, 661 662 /** 663 * @param {WebInspector.Setting} setting 664 */ 665 save: function(setting) 666 { 667 setting.set(this._serializeToObject()); 668 }, 669 670 /** 671 * @return {!Array.<!Object>} 672 */ 673 _serializeToObject: function() 674 { 675 var serializedHistory = []; 676 for (var i = 0; i < this._items.length; ++i) { 677 var serializedItem = this._items[i].serializeToObject(); 678 if (serializedItem) 679 serializedHistory.push(serializedItem); 680 if (serializedHistory.length === WebInspector.TabbedEditorContainer.maximalPreviouslyViewedFilesCount) 681 break; 682 } 683 return serializedHistory; 684 }, 685 686 687 /** 688 * @return {Array.<string>} 689 */ 690 _urls: function() 691 { 692 var result = []; 693 for (var i = 0; i < this._items.length; ++i) 694 result.push(this._items[i].url); 695 return result; 696 }, 697 698 __proto__: WebInspector.Object.prototype 699 } 700 701 /** 702 * @constructor 703 * @implements {WebInspector.TabbedPaneTabDelegate} 704 * @param {WebInspector.TabbedEditorContainer} editorContainer 705 */ 706 WebInspector.EditorContainerTabDelegate = function(editorContainer) 707 { 708 this._editorContainer = editorContainer; 709 } 710 711 WebInspector.EditorContainerTabDelegate.prototype = { 712 /** 713 * @param {WebInspector.TabbedPane} tabbedPane 714 * @param {Array.<string>} ids 715 */ 716 closeTabs: function(tabbedPane, ids) 717 { 718 this._editorContainer._closeTabs(ids); 719 } 720 } 721