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 = "sources-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 WebInspector.TabbedEditorContainer.Events = { 72 EditorSelected: "EditorSelected", 73 EditorClosed: "EditorClosed" 74 } 75 76 WebInspector.TabbedEditorContainer._tabId = 0; 77 78 WebInspector.TabbedEditorContainer.maximalPreviouslyViewedFilesCount = 30; 79 80 WebInspector.TabbedEditorContainer.prototype = { 81 /** 82 * @return {!WebInspector.View} 83 */ 84 get view() 85 { 86 return this._tabbedPane; 87 }, 88 89 /** 90 * @type {!WebInspector.SourceFrame} 91 */ 92 get visibleView() 93 { 94 return this._tabbedPane.visibleView; 95 }, 96 97 /** 98 * @return {!Array.<!WebInspector.SourceFrame>} 99 */ 100 fileViews: function() 101 { 102 return /** @type {!Array.<!WebInspector.SourceFrame>} */ (this._tabbedPane.tabViews()); 103 }, 104 105 /** 106 * @param {!Element} parentElement 107 */ 108 show: function(parentElement) 109 { 110 this._tabbedPane.show(parentElement); 111 }, 112 113 /** 114 * @param {!WebInspector.UISourceCode} uiSourceCode 115 */ 116 showFile: function(uiSourceCode) 117 { 118 this._innerShowFile(uiSourceCode, true); 119 }, 120 121 /** 122 * @param {!WebInspector.UISourceCode} uiSourceCode 123 */ 124 closeFile: function(uiSourceCode) 125 { 126 var tabId = this._tabIds.get(uiSourceCode); 127 if (!tabId) 128 return; 129 this._closeTabs([tabId]); 130 }, 131 132 /** 133 * @return {!Array.<!WebInspector.UISourceCode>} 134 */ 135 historyUISourceCodes: function() 136 { 137 // FIXME: there should be a way to fetch UISourceCode for its uri. 138 var uriToUISourceCode = {}; 139 for (var id in this._files) { 140 var uiSourceCode = this._files[id]; 141 uriToUISourceCode[uiSourceCode.uri()] = uiSourceCode; 142 } 143 144 var result = []; 145 var uris = this._history._urls(); 146 for (var i = 0; i < uris.length; ++i) { 147 var uiSourceCode = uriToUISourceCode[uris[i]]; 148 if (uiSourceCode) 149 result.push(uiSourceCode); 150 } 151 return result; 152 }, 153 154 _addViewListeners: function() 155 { 156 if (!this._currentView) 157 return; 158 this._currentView.addEventListener(WebInspector.SourceFrame.Events.ScrollChanged, this._scrollChanged, this); 159 this._currentView.addEventListener(WebInspector.SourceFrame.Events.SelectionChanged, this._selectionChanged, this); 160 }, 161 162 _removeViewListeners: function() 163 { 164 if (!this._currentView) 165 return; 166 this._currentView.removeEventListener(WebInspector.SourceFrame.Events.ScrollChanged, this._scrollChanged, this); 167 this._currentView.removeEventListener(WebInspector.SourceFrame.Events.SelectionChanged, this._selectionChanged, this); 168 }, 169 170 /** 171 * @param {!WebInspector.Event} event 172 */ 173 _scrollChanged: function(event) 174 { 175 var lineNumber = /** @type {number} */ (event.data); 176 this._history.updateScrollLineNumber(this._currentFile.uri(), lineNumber); 177 this._history.save(this._previouslyViewedFilesSetting); 178 }, 179 180 /** 181 * @param {!WebInspector.Event} event 182 */ 183 _selectionChanged: function(event) 184 { 185 var range = /** @type {!WebInspector.TextRange} */ (event.data); 186 this._history.updateSelectionRange(this._currentFile.uri(), range); 187 this._history.save(this._previouslyViewedFilesSetting); 188 }, 189 190 /** 191 * @param {!WebInspector.UISourceCode} uiSourceCode 192 * @param {boolean=} userGesture 193 */ 194 _innerShowFile: function(uiSourceCode, userGesture) 195 { 196 if (this._currentFile === uiSourceCode) 197 return; 198 199 this._removeViewListeners(); 200 this._currentFile = uiSourceCode; 201 202 var tabId = this._tabIds.get(uiSourceCode) || this._appendFileTab(uiSourceCode, userGesture); 203 204 this._tabbedPane.selectTab(tabId, userGesture); 205 if (userGesture) 206 this._editorSelectedByUserAction(); 207 208 this._currentView = this.visibleView; 209 this._addViewListeners(); 210 211 var eventData = { currentFile: this._currentFile, userGesture: userGesture }; 212 this.dispatchEventToListeners(WebInspector.TabbedEditorContainer.Events.EditorSelected, eventData); 213 }, 214 215 /** 216 * @param {!WebInspector.UISourceCode} uiSourceCode 217 * @return {string} 218 */ 219 _titleForFile: function(uiSourceCode) 220 { 221 var maxDisplayNameLength = 30; 222 var title = uiSourceCode.displayName(true).trimMiddle(maxDisplayNameLength); 223 if (uiSourceCode.isDirty() || uiSourceCode.hasUnsavedCommittedChanges()) 224 title += "*"; 225 return title; 226 }, 227 228 /** 229 * @param {string} id 230 * @param {string} nextTabId 231 */ 232 _maybeCloseTab: function(id, nextTabId) 233 { 234 var uiSourceCode = this._files[id]; 235 var shouldPrompt = uiSourceCode.isDirty() && uiSourceCode.project().canSetFileContent(); 236 // FIXME: this should be replaced with common Save/Discard/Cancel dialog. 237 if (!shouldPrompt || confirm(WebInspector.UIString("Are you sure you want to close unsaved file: %s?", uiSourceCode.name()))) { 238 uiSourceCode.resetWorkingCopy(); 239 if (nextTabId) 240 this._tabbedPane.selectTab(nextTabId, true); 241 this._tabbedPane.closeTab(id, true); 242 return true; 243 } 244 return false; 245 }, 246 247 /** 248 * @param {!Array.<string>} ids 249 */ 250 _closeTabs: function(ids) 251 { 252 var dirtyTabs = []; 253 var cleanTabs = []; 254 for (var i = 0; i < ids.length; ++i) { 255 var id = ids[i]; 256 var uiSourceCode = this._files[id]; 257 if (uiSourceCode.isDirty()) 258 dirtyTabs.push(id); 259 else 260 cleanTabs.push(id); 261 } 262 if (dirtyTabs.length) 263 this._tabbedPane.selectTab(dirtyTabs[0], true); 264 this._tabbedPane.closeTabs(cleanTabs, true); 265 for (var i = 0; i < dirtyTabs.length; ++i) { 266 var nextTabId = i + 1 < dirtyTabs.length ? dirtyTabs[i + 1] : null; 267 if (!this._maybeCloseTab(dirtyTabs[i], nextTabId)) 268 break; 269 } 270 }, 271 272 /** 273 * @param {!WebInspector.UISourceCode} uiSourceCode 274 */ 275 addUISourceCode: function(uiSourceCode) 276 { 277 var uri = uiSourceCode.uri(); 278 if (this._userSelectedFiles) 279 return; 280 281 var index = this._history.index(uri) 282 if (index === -1) 283 return; 284 285 var tabId = this._tabIds.get(uiSourceCode) || this._appendFileTab(uiSourceCode, false); 286 287 // Select tab if this file was the last to be shown. 288 if (!index) { 289 this._innerShowFile(uiSourceCode, false); 290 return; 291 } 292 293 if (!this._currentFile) 294 return; 295 var currentProjectType = this._currentFile.project().type(); 296 var addedProjectType = uiSourceCode.project().type(); 297 var snippetsProjectType = WebInspector.projectTypes.Snippets; 298 if (this._history.index(this._currentFile.uri()) && currentProjectType === snippetsProjectType && addedProjectType !== snippetsProjectType) 299 this._innerShowFile(uiSourceCode, false); 300 }, 301 302 /** 303 * @param {!WebInspector.UISourceCode} uiSourceCode 304 */ 305 removeUISourceCode: function(uiSourceCode) 306 { 307 this.removeUISourceCodes([uiSourceCode]); 308 }, 309 310 /** 311 * @param {!Array.<!WebInspector.UISourceCode>} uiSourceCodes 312 */ 313 removeUISourceCodes: function(uiSourceCodes) 314 { 315 var tabIds = []; 316 for (var i = 0; i < uiSourceCodes.length; ++i) { 317 var uiSourceCode = uiSourceCodes[i]; 318 var tabId = this._tabIds.get(uiSourceCode); 319 if (tabId) 320 tabIds.push(tabId); 321 } 322 this._tabbedPane.closeTabs(tabIds); 323 }, 324 325 /** 326 * @param {!WebInspector.UISourceCode} uiSourceCode 327 */ 328 _editorClosedByUserAction: function(uiSourceCode) 329 { 330 this._userSelectedFiles = true; 331 this._history.remove(uiSourceCode.uri()); 332 this._updateHistory(); 333 }, 334 335 _editorSelectedByUserAction: function() 336 { 337 this._userSelectedFiles = true; 338 this._updateHistory(); 339 }, 340 341 _updateHistory: function() 342 { 343 var tabIds = this._tabbedPane.lastOpenedTabIds(WebInspector.TabbedEditorContainer.maximalPreviouslyViewedFilesCount); 344 345 /** 346 * @param {string} tabId 347 * @this {WebInspector.TabbedEditorContainer} 348 */ 349 function tabIdToURI(tabId) 350 { 351 return this._files[tabId].uri(); 352 } 353 354 this._history.update(tabIds.map(tabIdToURI.bind(this))); 355 this._history.save(this._previouslyViewedFilesSetting); 356 }, 357 358 /** 359 * @param {!WebInspector.UISourceCode} uiSourceCode 360 * @return {string} 361 */ 362 _tooltipForFile: function(uiSourceCode) 363 { 364 return uiSourceCode.originURL(); 365 }, 366 367 /** 368 * @param {!WebInspector.UISourceCode} uiSourceCode 369 * @param {boolean=} userGesture 370 * @return {string} 371 */ 372 _appendFileTab: function(uiSourceCode, userGesture) 373 { 374 var view = this._delegate.viewForFile(uiSourceCode); 375 var title = this._titleForFile(uiSourceCode); 376 var tooltip = this._tooltipForFile(uiSourceCode); 377 378 var tabId = this._generateTabId(); 379 this._tabIds.set(uiSourceCode, tabId); 380 this._files[tabId] = uiSourceCode; 381 382 var savedSelectionRange = this._history.selectionRange(uiSourceCode.uri()); 383 if (savedSelectionRange) 384 view.setSelection(savedSelectionRange); 385 var savedScrollLineNumber = this._history.scrollLineNumber(uiSourceCode.uri()); 386 if (savedScrollLineNumber) 387 view.scrollToLine(savedScrollLineNumber); 388 389 this._tabbedPane.appendTab(tabId, title, view, tooltip, userGesture); 390 391 this._updateFileTitle(uiSourceCode); 392 this._addUISourceCodeListeners(uiSourceCode); 393 return tabId; 394 }, 395 396 /** 397 * @param {!WebInspector.Event} event 398 */ 399 _tabClosed: function(event) 400 { 401 var tabId = /** @type {string} */ (event.data.tabId); 402 var userGesture = /** @type {boolean} */ (event.data.isUserGesture); 403 404 var uiSourceCode = this._files[tabId]; 405 if (this._currentFile === uiSourceCode) { 406 this._removeViewListeners(); 407 delete this._currentView; 408 delete this._currentFile; 409 } 410 this._tabIds.remove(uiSourceCode); 411 delete this._files[tabId]; 412 413 this._removeUISourceCodeListeners(uiSourceCode); 414 415 this.dispatchEventToListeners(WebInspector.TabbedEditorContainer.Events.EditorClosed, uiSourceCode); 416 417 if (userGesture) 418 this._editorClosedByUserAction(uiSourceCode); 419 }, 420 421 /** 422 * @param {!WebInspector.Event} event 423 */ 424 _tabSelected: function(event) 425 { 426 var tabId = /** @type {string} */ (event.data.tabId); 427 var userGesture = /** @type {boolean} */ (event.data.isUserGesture); 428 429 var uiSourceCode = this._files[tabId]; 430 this._innerShowFile(uiSourceCode, userGesture); 431 }, 432 433 /** 434 * @param {!WebInspector.UISourceCode} uiSourceCode 435 */ 436 _addUISourceCodeListeners: function(uiSourceCode) 437 { 438 uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.TitleChanged, this._uiSourceCodeTitleChanged, this); 439 uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._uiSourceCodeWorkingCopyChanged, this); 440 uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._uiSourceCodeWorkingCopyCommitted, this); 441 uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.SavedStateUpdated, this._uiSourceCodeSavedStateUpdated, this); 442 }, 443 444 /** 445 * @param {!WebInspector.UISourceCode} uiSourceCode 446 */ 447 _removeUISourceCodeListeners: function(uiSourceCode) 448 { 449 uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.TitleChanged, this._uiSourceCodeTitleChanged, this); 450 uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._uiSourceCodeWorkingCopyChanged, this); 451 uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._uiSourceCodeWorkingCopyCommitted, this); 452 uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.SavedStateUpdated, this._uiSourceCodeSavedStateUpdated, this); 453 }, 454 455 /** 456 * @param {!WebInspector.UISourceCode} uiSourceCode 457 */ 458 _updateFileTitle: function(uiSourceCode) 459 { 460 var tabId = this._tabIds.get(uiSourceCode); 461 if (tabId) { 462 var title = this._titleForFile(uiSourceCode); 463 this._tabbedPane.changeTabTitle(tabId, title); 464 if (uiSourceCode.hasUnsavedCommittedChanges()) 465 this._tabbedPane.setTabIcon(tabId, "editor-container-unsaved-committed-changes-icon", WebInspector.UIString("Changes to this file were not saved to file system.")); 466 else 467 this._tabbedPane.setTabIcon(tabId, ""); 468 } 469 }, 470 471 _uiSourceCodeTitleChanged: function(event) 472 { 473 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.target); 474 this._updateFileTitle(uiSourceCode); 475 this._updateHistory(); 476 }, 477 478 _uiSourceCodeWorkingCopyChanged: function(event) 479 { 480 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.target); 481 this._updateFileTitle(uiSourceCode); 482 }, 483 484 _uiSourceCodeWorkingCopyCommitted: function(event) 485 { 486 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.target); 487 this._updateFileTitle(uiSourceCode); 488 }, 489 490 _uiSourceCodeSavedStateUpdated: function(event) 491 { 492 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.target); 493 this._updateFileTitle(uiSourceCode); 494 }, 495 496 reset: function() 497 { 498 delete this._userSelectedFiles; 499 }, 500 501 /** 502 * @return {string} 503 */ 504 _generateTabId: function() 505 { 506 return "tab_" + (WebInspector.TabbedEditorContainer._tabId++); 507 }, 508 509 /** 510 * @return {!WebInspector.UISourceCode} uiSourceCode 511 */ 512 currentFile: function() 513 { 514 return this._currentFile; 515 }, 516 517 __proto__: WebInspector.Object.prototype 518 } 519 520 /** 521 * @constructor 522 * @param {string} url 523 * @param {!WebInspector.TextRange=} selectionRange 524 * @param {number=} scrollLineNumber 525 */ 526 WebInspector.TabbedEditorContainer.HistoryItem = function(url, selectionRange, scrollLineNumber) 527 { 528 /** @const */ this.url = url; 529 /** @const */ this._isSerializable = url.length < WebInspector.TabbedEditorContainer.HistoryItem.serializableUrlLengthLimit; 530 this.selectionRange = selectionRange; 531 this.scrollLineNumber = scrollLineNumber; 532 } 533 534 WebInspector.TabbedEditorContainer.HistoryItem.serializableUrlLengthLimit = 4096; 535 536 /** 537 * @param {!Object} serializedHistoryItem 538 * @return {!WebInspector.TabbedEditorContainer.HistoryItem} 539 */ 540 WebInspector.TabbedEditorContainer.HistoryItem.fromObject = function (serializedHistoryItem) 541 { 542 var selectionRange = serializedHistoryItem.selectionRange ? WebInspector.TextRange.fromObject(serializedHistoryItem.selectionRange) : undefined; 543 return new WebInspector.TabbedEditorContainer.HistoryItem(serializedHistoryItem.url, selectionRange, serializedHistoryItem.scrollLineNumber); 544 } 545 546 WebInspector.TabbedEditorContainer.HistoryItem.prototype = { 547 /** 548 * @return {?Object} 549 */ 550 serializeToObject: function() 551 { 552 if (!this._isSerializable) 553 return null; 554 var serializedHistoryItem = {}; 555 serializedHistoryItem.url = this.url; 556 serializedHistoryItem.selectionRange = this.selectionRange; 557 serializedHistoryItem.scrollLineNumber = this.scrollLineNumber; 558 return serializedHistoryItem; 559 } 560 } 561 562 /** 563 * @constructor 564 * @param {!Array.<!WebInspector.TabbedEditorContainer.HistoryItem>} items 565 */ 566 WebInspector.TabbedEditorContainer.History = function(items) 567 { 568 this._items = items; 569 this._rebuildItemIndex(); 570 } 571 572 /** 573 * @param {!Array.<!Object>} serializedHistory 574 * @return {!WebInspector.TabbedEditorContainer.History} 575 */ 576 WebInspector.TabbedEditorContainer.History.fromObject = function(serializedHistory) 577 { 578 var items = []; 579 for (var i = 0; i < serializedHistory.length; ++i) 580 items.push(WebInspector.TabbedEditorContainer.HistoryItem.fromObject(serializedHistory[i])); 581 return new WebInspector.TabbedEditorContainer.History(items); 582 } 583 584 WebInspector.TabbedEditorContainer.History.prototype = { 585 /** 586 * @param {string} url 587 * @return {number} 588 */ 589 index: function(url) 590 { 591 var index = this._itemsIndex[url]; 592 if (typeof index === "number") 593 return index; 594 return -1; 595 }, 596 597 _rebuildItemIndex: function() 598 { 599 this._itemsIndex = {}; 600 for (var i = 0; i < this._items.length; ++i) { 601 console.assert(!this._itemsIndex.hasOwnProperty(this._items[i].url)); 602 this._itemsIndex[this._items[i].url] = i; 603 } 604 }, 605 606 /** 607 * @param {string} url 608 * @return {!WebInspector.TextRange|undefined} 609 */ 610 selectionRange: function(url) 611 { 612 var index = this.index(url); 613 return index !== -1 ? this._items[index].selectionRange : undefined; 614 }, 615 616 /** 617 * @param {string} url 618 * @param {!WebInspector.TextRange=} selectionRange 619 */ 620 updateSelectionRange: function(url, selectionRange) 621 { 622 if (!selectionRange) 623 return; 624 var index = this.index(url); 625 if (index === -1) 626 return; 627 this._items[index].selectionRange = selectionRange; 628 }, 629 630 /** 631 * @param {string} url 632 * @return {number|undefined} 633 */ 634 scrollLineNumber: function(url) 635 { 636 var index = this.index(url); 637 return index !== -1 ? this._items[index].scrollLineNumber : undefined; 638 }, 639 640 /** 641 * @param {string} url 642 * @param {number} scrollLineNumber 643 */ 644 updateScrollLineNumber: function(url, scrollLineNumber) 645 { 646 var index = this.index(url); 647 if (index === -1) 648 return; 649 this._items[index].scrollLineNumber = scrollLineNumber; 650 }, 651 652 /** 653 * @param {!Array.<string>} urls 654 */ 655 update: function(urls) 656 { 657 for (var i = urls.length - 1; i >= 0; --i) { 658 var index = this.index(urls[i]); 659 var item; 660 if (index !== -1) { 661 item = this._items[index]; 662 this._items.splice(index, 1); 663 } else 664 item = new WebInspector.TabbedEditorContainer.HistoryItem(urls[i]); 665 this._items.unshift(item); 666 this._rebuildItemIndex(); 667 } 668 }, 669 670 /** 671 * @param {string} url 672 */ 673 remove: function(url) 674 { 675 var index = this.index(url); 676 if (index !== -1) { 677 this._items.splice(index, 1); 678 this._rebuildItemIndex(); 679 } 680 }, 681 682 /** 683 * @param {!WebInspector.Setting} setting 684 */ 685 save: function(setting) 686 { 687 setting.set(this._serializeToObject()); 688 }, 689 690 /** 691 * @return {!Array.<!Object>} 692 */ 693 _serializeToObject: function() 694 { 695 var serializedHistory = []; 696 for (var i = 0; i < this._items.length; ++i) { 697 var serializedItem = this._items[i].serializeToObject(); 698 if (serializedItem) 699 serializedHistory.push(serializedItem); 700 if (serializedHistory.length === WebInspector.TabbedEditorContainer.maximalPreviouslyViewedFilesCount) 701 break; 702 } 703 return serializedHistory; 704 }, 705 706 707 /** 708 * @return {!Array.<string>} 709 */ 710 _urls: function() 711 { 712 var result = []; 713 for (var i = 0; i < this._items.length; ++i) 714 result.push(this._items[i].url); 715 return result; 716 } 717 } 718 719 /** 720 * @constructor 721 * @implements {WebInspector.TabbedPaneTabDelegate} 722 * @param {!WebInspector.TabbedEditorContainer} editorContainer 723 */ 724 WebInspector.EditorContainerTabDelegate = function(editorContainer) 725 { 726 this._editorContainer = editorContainer; 727 } 728 729 WebInspector.EditorContainerTabDelegate.prototype = { 730 /** 731 * @param {!WebInspector.TabbedPane} tabbedPane 732 * @param {!Array.<string>} ids 733 */ 734 closeTabs: function(tabbedPane, ids) 735 { 736 this._editorContainer._closeTabs(ids); 737 } 738 } 739