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 */ 32WebInspector.TabbedEditorContainerDelegate = function() { } 33 34WebInspector.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 */ 49WebInspector.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 71WebInspector.TabbedEditorContainer.Events = { 72 EditorSelected: "EditorSelected", 73 EditorClosed: "EditorClosed" 74} 75 76WebInspector.TabbedEditorContainer._tabId = 0; 77 78WebInspector.TabbedEditorContainer.maximalPreviouslyViewedFilesCount = 30; 79 80WebInspector.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 if (!this._currentFile) 288 return; 289 290 // Select tab if this file was the last to be shown. 291 if (!index) { 292 this._innerShowFile(uiSourceCode, false); 293 return; 294 } 295 296 var currentProjectType = this._currentFile.project().type(); 297 var addedProjectType = uiSourceCode.project().type(); 298 var snippetsProjectType = WebInspector.projectTypes.Snippets; 299 if (this._history.index(this._currentFile.uri()) && currentProjectType === snippetsProjectType && addedProjectType !== snippetsProjectType) 300 this._innerShowFile(uiSourceCode, false); 301 }, 302 303 /** 304 * @param {!WebInspector.UISourceCode} uiSourceCode 305 */ 306 removeUISourceCode: function(uiSourceCode) 307 { 308 this.removeUISourceCodes([uiSourceCode]); 309 }, 310 311 /** 312 * @param {!Array.<!WebInspector.UISourceCode>} uiSourceCodes 313 */ 314 removeUISourceCodes: function(uiSourceCodes) 315 { 316 var tabIds = []; 317 for (var i = 0; i < uiSourceCodes.length; ++i) { 318 var uiSourceCode = uiSourceCodes[i]; 319 var tabId = this._tabIds.get(uiSourceCode); 320 if (tabId) 321 tabIds.push(tabId); 322 } 323 this._tabbedPane.closeTabs(tabIds); 324 }, 325 326 /** 327 * @param {!WebInspector.UISourceCode} uiSourceCode 328 */ 329 _editorClosedByUserAction: function(uiSourceCode) 330 { 331 this._userSelectedFiles = true; 332 this._history.remove(uiSourceCode.uri()); 333 this._updateHistory(); 334 }, 335 336 _editorSelectedByUserAction: function() 337 { 338 this._userSelectedFiles = true; 339 this._updateHistory(); 340 }, 341 342 _updateHistory: function() 343 { 344 var tabIds = this._tabbedPane.lastOpenedTabIds(WebInspector.TabbedEditorContainer.maximalPreviouslyViewedFilesCount); 345 346 /** 347 * @param {string} tabId 348 * @this {WebInspector.TabbedEditorContainer} 349 */ 350 function tabIdToURI(tabId) 351 { 352 return this._files[tabId].uri(); 353 } 354 355 this._history.update(tabIds.map(tabIdToURI.bind(this))); 356 this._history.save(this._previouslyViewedFilesSetting); 357 }, 358 359 /** 360 * @param {!WebInspector.UISourceCode} uiSourceCode 361 * @return {string} 362 */ 363 _tooltipForFile: function(uiSourceCode) 364 { 365 return uiSourceCode.originURL(); 366 }, 367 368 /** 369 * @param {!WebInspector.UISourceCode} uiSourceCode 370 * @param {boolean=} userGesture 371 * @return {string} 372 */ 373 _appendFileTab: function(uiSourceCode, userGesture) 374 { 375 var view = this._delegate.viewForFile(uiSourceCode); 376 var title = this._titleForFile(uiSourceCode); 377 var tooltip = this._tooltipForFile(uiSourceCode); 378 379 var tabId = this._generateTabId(); 380 this._tabIds.put(uiSourceCode, tabId); 381 this._files[tabId] = uiSourceCode; 382 383 var savedSelectionRange = this._history.selectionRange(uiSourceCode.uri()); 384 if (savedSelectionRange) 385 view.setSelection(savedSelectionRange); 386 var savedScrollLineNumber = this._history.scrollLineNumber(uiSourceCode.uri()); 387 if (savedScrollLineNumber) 388 view.scrollToLine(savedScrollLineNumber); 389 390 this._tabbedPane.appendTab(tabId, title, view, tooltip, userGesture); 391 392 this._updateFileTitle(uiSourceCode); 393 this._addUISourceCodeListeners(uiSourceCode); 394 return tabId; 395 }, 396 397 /** 398 * @param {!WebInspector.Event} event 399 */ 400 _tabClosed: function(event) 401 { 402 var tabId = /** @type {string} */ (event.data.tabId); 403 var userGesture = /** @type {boolean} */ (event.data.isUserGesture); 404 405 var uiSourceCode = this._files[tabId]; 406 if (this._currentFile === uiSourceCode) { 407 this._removeViewListeners(); 408 delete this._currentView; 409 delete this._currentFile; 410 } 411 this._tabIds.remove(uiSourceCode); 412 delete this._files[tabId]; 413 414 this._removeUISourceCodeListeners(uiSourceCode); 415 416 this.dispatchEventToListeners(WebInspector.TabbedEditorContainer.Events.EditorClosed, uiSourceCode); 417 418 if (userGesture) 419 this._editorClosedByUserAction(uiSourceCode); 420 }, 421 422 /** 423 * @param {!WebInspector.Event} event 424 */ 425 _tabSelected: function(event) 426 { 427 var tabId = /** @type {string} */ (event.data.tabId); 428 var userGesture = /** @type {boolean} */ (event.data.isUserGesture); 429 430 var uiSourceCode = this._files[tabId]; 431 this._innerShowFile(uiSourceCode, userGesture); 432 }, 433 434 /** 435 * @param {!WebInspector.UISourceCode} uiSourceCode 436 */ 437 _addUISourceCodeListeners: function(uiSourceCode) 438 { 439 uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.TitleChanged, this._uiSourceCodeTitleChanged, this); 440 uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._uiSourceCodeWorkingCopyChanged, this); 441 uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._uiSourceCodeWorkingCopyCommitted, this); 442 uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.SavedStateUpdated, this._uiSourceCodeSavedStateUpdated, this); 443 }, 444 445 /** 446 * @param {!WebInspector.UISourceCode} uiSourceCode 447 */ 448 _removeUISourceCodeListeners: function(uiSourceCode) 449 { 450 uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.TitleChanged, this._uiSourceCodeTitleChanged, this); 451 uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._uiSourceCodeWorkingCopyChanged, this); 452 uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._uiSourceCodeWorkingCopyCommitted, this); 453 uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.SavedStateUpdated, this._uiSourceCodeSavedStateUpdated, this); 454 }, 455 456 /** 457 * @param {!WebInspector.UISourceCode} uiSourceCode 458 */ 459 _updateFileTitle: function(uiSourceCode) 460 { 461 var tabId = this._tabIds.get(uiSourceCode); 462 if (tabId) { 463 var title = this._titleForFile(uiSourceCode); 464 this._tabbedPane.changeTabTitle(tabId, title); 465 if (uiSourceCode.hasUnsavedCommittedChanges()) 466 this._tabbedPane.setTabIcon(tabId, "editor-container-unsaved-committed-changes-icon", WebInspector.UIString("Changes to this file were not saved to file system.")); 467 else 468 this._tabbedPane.setTabIcon(tabId, ""); 469 } 470 }, 471 472 _uiSourceCodeTitleChanged: function(event) 473 { 474 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.target); 475 this._updateFileTitle(uiSourceCode); 476 this._updateHistory(); 477 }, 478 479 _uiSourceCodeWorkingCopyChanged: function(event) 480 { 481 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.target); 482 this._updateFileTitle(uiSourceCode); 483 }, 484 485 _uiSourceCodeWorkingCopyCommitted: function(event) 486 { 487 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.target); 488 this._updateFileTitle(uiSourceCode); 489 }, 490 491 _uiSourceCodeSavedStateUpdated: function(event) 492 { 493 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.target); 494 this._updateFileTitle(uiSourceCode); 495 }, 496 497 reset: function() 498 { 499 delete this._userSelectedFiles; 500 }, 501 502 /** 503 * @return {string} 504 */ 505 _generateTabId: function() 506 { 507 return "tab_" + (WebInspector.TabbedEditorContainer._tabId++); 508 }, 509 510 /** 511 * @return {!WebInspector.UISourceCode} uiSourceCode 512 */ 513 currentFile: function() 514 { 515 return this._currentFile; 516 }, 517 518 __proto__: WebInspector.Object.prototype 519} 520 521/** 522 * @constructor 523 * @param {string} url 524 * @param {!WebInspector.TextRange=} selectionRange 525 * @param {number=} scrollLineNumber 526 */ 527WebInspector.TabbedEditorContainer.HistoryItem = function(url, selectionRange, scrollLineNumber) 528{ 529 /** @const */ this.url = url; 530 /** @const */ this._isSerializable = url.length < WebInspector.TabbedEditorContainer.HistoryItem.serializableUrlLengthLimit; 531 this.selectionRange = selectionRange; 532 this.scrollLineNumber = scrollLineNumber; 533} 534 535WebInspector.TabbedEditorContainer.HistoryItem.serializableUrlLengthLimit = 4096; 536 537/** 538 * @param {!Object} serializedHistoryItem 539 * @return {!WebInspector.TabbedEditorContainer.HistoryItem} 540 */ 541WebInspector.TabbedEditorContainer.HistoryItem.fromObject = function (serializedHistoryItem) 542{ 543 var selectionRange = serializedHistoryItem.selectionRange ? WebInspector.TextRange.fromObject(serializedHistoryItem.selectionRange) : undefined; 544 return new WebInspector.TabbedEditorContainer.HistoryItem(serializedHistoryItem.url, selectionRange, serializedHistoryItem.scrollLineNumber); 545} 546 547WebInspector.TabbedEditorContainer.HistoryItem.prototype = { 548 /** 549 * @return {?Object} 550 */ 551 serializeToObject: function() 552 { 553 if (!this._isSerializable) 554 return null; 555 var serializedHistoryItem = {}; 556 serializedHistoryItem.url = this.url; 557 serializedHistoryItem.selectionRange = this.selectionRange; 558 serializedHistoryItem.scrollLineNumber = this.scrollLineNumber; 559 return serializedHistoryItem; 560 } 561} 562 563/** 564 * @constructor 565 * @param {!Array.<!WebInspector.TabbedEditorContainer.HistoryItem>} items 566 */ 567WebInspector.TabbedEditorContainer.History = function(items) 568{ 569 this._items = items; 570 this._rebuildItemIndex(); 571} 572 573/** 574 * @param {!Array.<!Object>} serializedHistory 575 * @return {!WebInspector.TabbedEditorContainer.History} 576 */ 577WebInspector.TabbedEditorContainer.History.fromObject = function(serializedHistory) 578{ 579 var items = []; 580 for (var i = 0; i < serializedHistory.length; ++i) 581 items.push(WebInspector.TabbedEditorContainer.HistoryItem.fromObject(serializedHistory[i])); 582 return new WebInspector.TabbedEditorContainer.History(items); 583} 584 585WebInspector.TabbedEditorContainer.History.prototype = { 586 /** 587 * @param {string} url 588 * @return {number} 589 */ 590 index: function(url) 591 { 592 var index = this._itemsIndex[url]; 593 if (typeof index === "number") 594 return index; 595 return -1; 596 }, 597 598 _rebuildItemIndex: function() 599 { 600 this._itemsIndex = {}; 601 for (var i = 0; i < this._items.length; ++i) { 602 console.assert(!this._itemsIndex.hasOwnProperty(this._items[i].url)); 603 this._itemsIndex[this._items[i].url] = i; 604 } 605 }, 606 607 /** 608 * @param {string} url 609 * @return {!WebInspector.TextRange|undefined} 610 */ 611 selectionRange: function(url) 612 { 613 var index = this.index(url); 614 return index !== -1 ? this._items[index].selectionRange : undefined; 615 }, 616 617 /** 618 * @param {string} url 619 * @param {!WebInspector.TextRange=} selectionRange 620 */ 621 updateSelectionRange: function(url, selectionRange) 622 { 623 if (!selectionRange) 624 return; 625 var index = this.index(url); 626 if (index === -1) 627 return; 628 this._items[index].selectionRange = selectionRange; 629 }, 630 631 /** 632 * @param {string} url 633 * @return {number|undefined} 634 */ 635 scrollLineNumber: function(url) 636 { 637 var index = this.index(url); 638 return index !== -1 ? this._items[index].scrollLineNumber : undefined; 639 }, 640 641 /** 642 * @param {string} url 643 * @param {number} scrollLineNumber 644 */ 645 updateScrollLineNumber: function(url, scrollLineNumber) 646 { 647 var index = this.index(url); 648 if (index === -1) 649 return; 650 this._items[index].scrollLineNumber = scrollLineNumber; 651 }, 652 653 /** 654 * @param {!Array.<string>} urls 655 */ 656 update: function(urls) 657 { 658 for (var i = urls.length - 1; i >= 0; --i) { 659 var index = this.index(urls[i]); 660 var item; 661 if (index !== -1) { 662 item = this._items[index]; 663 this._items.splice(index, 1); 664 } else 665 item = new WebInspector.TabbedEditorContainer.HistoryItem(urls[i]); 666 this._items.unshift(item); 667 this._rebuildItemIndex(); 668 } 669 }, 670 671 /** 672 * @param {string} url 673 */ 674 remove: function(url) 675 { 676 var index = this.index(url); 677 if (index !== -1) { 678 this._items.splice(index, 1); 679 this._rebuildItemIndex(); 680 } 681 }, 682 683 /** 684 * @param {!WebInspector.Setting} setting 685 */ 686 save: function(setting) 687 { 688 setting.set(this._serializeToObject()); 689 }, 690 691 /** 692 * @return {!Array.<!Object>} 693 */ 694 _serializeToObject: function() 695 { 696 var serializedHistory = []; 697 for (var i = 0; i < this._items.length; ++i) { 698 var serializedItem = this._items[i].serializeToObject(); 699 if (serializedItem) 700 serializedHistory.push(serializedItem); 701 if (serializedHistory.length === WebInspector.TabbedEditorContainer.maximalPreviouslyViewedFilesCount) 702 break; 703 } 704 return serializedHistory; 705 }, 706 707 708 /** 709 * @return {!Array.<string>} 710 */ 711 _urls: function() 712 { 713 var result = []; 714 for (var i = 0; i < this._items.length; ++i) 715 result.push(this._items[i].url); 716 return result; 717 } 718} 719 720/** 721 * @constructor 722 * @implements {WebInspector.TabbedPaneTabDelegate} 723 * @param {!WebInspector.TabbedEditorContainer} editorContainer 724 */ 725WebInspector.EditorContainerTabDelegate = function(editorContainer) 726{ 727 this._editorContainer = editorContainer; 728} 729 730WebInspector.EditorContainerTabDelegate.prototype = { 731 /** 732 * @param {!WebInspector.TabbedPane} tabbedPane 733 * @param {!Array.<string>} ids 734 */ 735 closeTabs: function(tabbedPane, ids) 736 { 737 this._editorContainer._closeTabs(ids); 738 } 739} 740