blob: 3c3ba2976f66099398a1d9cd3d65d90febcb8580 [file] [log] [blame]
/*
* Copyright (C) 2011 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY GOOGLE INC. AND ITS CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC.
* OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @interface
*/
Sources.TabbedEditorContainerDelegate = function() {};
Sources.TabbedEditorContainerDelegate.prototype = {
/**
* @param {!Workspace.UISourceCode} uiSourceCode
* @return {!UI.Widget}
*/
viewForFile(uiSourceCode) {},
/**
* @param {!Sources.UISourceCodeFrame} sourceFrame
* @param {!Workspace.UISourceCode} uiSourceCode
*/
recycleUISourceCodeFrame(sourceFrame, uiSourceCode) {},
};
/**
* @unrestricted
*/
Sources.TabbedEditorContainer = class extends Common.Object {
/**
* @param {!Sources.TabbedEditorContainerDelegate} delegate
* @param {!Common.Setting} setting
* @param {!Element} placeholderElement
*/
constructor(delegate, setting, placeholderElement) {
super();
this._delegate = delegate;
this._tabbedPane = new UI.TabbedPane();
this._tabbedPane.setPlaceholderElement(placeholderElement);
this._tabbedPane.setTabDelegate(new Sources.EditorContainerTabDelegate(this));
this._tabbedPane.setCloseableTabs(true);
this._tabbedPane.setAllowTabReorder(true, true);
this._tabbedPane.addEventListener(UI.TabbedPane.Events.TabClosed, this._tabClosed, this);
this._tabbedPane.addEventListener(UI.TabbedPane.Events.TabSelected, this._tabSelected, this);
Persistence.persistence.addEventListener(
Persistence.Persistence.Events.BindingCreated, this._onBindingCreated, this);
Persistence.persistence.addEventListener(
Persistence.Persistence.Events.BindingRemoved, this._onBindingRemoved, this);
this._tabIds = new Map();
this._files = {};
this._previouslyViewedFilesSetting = setting;
this._history = Sources.TabbedEditorContainer.History.fromObject(this._previouslyViewedFilesSetting.get());
}
/**
* @param {!Common.Event} event
*/
_onBindingCreated(event) {
const binding = /** @type {!Persistence.PersistenceBinding} */ (event.data);
this._updateFileTitle(binding.fileSystem);
const networkTabId = this._tabIds.get(binding.network);
let fileSystemTabId = this._tabIds.get(binding.fileSystem);
const wasSelectedInNetwork = this._currentFile === binding.network;
const currentSelectionRange = this._history.selectionRange(binding.network.url());
const currentScrollLineNumber = this._history.scrollLineNumber(binding.network.url());
this._history.remove(binding.network.url());
if (!networkTabId)
return;
if (!fileSystemTabId) {
const networkView = this._tabbedPane.tabView(networkTabId);
const tabIndex = this._tabbedPane.tabIndex(networkTabId);
if (networkView instanceof Sources.UISourceCodeFrame) {
this._delegate.recycleUISourceCodeFrame(networkView, binding.fileSystem);
fileSystemTabId = this._appendFileTab(binding.fileSystem, false, tabIndex, networkView);
} else {
fileSystemTabId = this._appendFileTab(binding.fileSystem, false, tabIndex);
const fileSystemTabView = /** @type {!UI.Widget} */ (this._tabbedPane.tabView(fileSystemTabId));
this._restoreEditorProperties(fileSystemTabView, currentSelectionRange, currentScrollLineNumber);
}
}
this._closeTabs([networkTabId], true);
if (wasSelectedInNetwork)
this._tabbedPane.selectTab(fileSystemTabId, false);
this._updateHistory();
}
/**
* @param {!Common.Event} event
*/
_onBindingRemoved(event) {
const binding = /** @type {!Persistence.PersistenceBinding} */ (event.data);
this._updateFileTitle(binding.fileSystem);
}
/**
* @return {!UI.Widget}
*/
get view() {
return this._tabbedPane;
}
/**
* @return {?UI.Widget}
*/
get visibleView() {
return this._tabbedPane.visibleView;
}
/**
* @return {!Array.<!UI.Widget>}
*/
fileViews() {
return /** @type {!Array.<!UI.Widget>} */ (this._tabbedPane.tabViews());
}
/**
* @return {!UI.Toolbar}
*/
leftToolbar() {
return this._tabbedPane.leftToolbar();
}
/**
* @return {!UI.Toolbar}
*/
rightToolbar() {
return this._tabbedPane.rightToolbar();
}
/**
* @param {!Element} parentElement
*/
show(parentElement) {
this._tabbedPane.show(parentElement);
}
/**
* @param {!Workspace.UISourceCode} uiSourceCode
*/
showFile(uiSourceCode) {
this._innerShowFile(uiSourceCode, true);
}
/**
* @param {!Workspace.UISourceCode} uiSourceCode
*/
closeFile(uiSourceCode) {
const tabId = this._tabIds.get(uiSourceCode);
if (!tabId)
return;
this._closeTabs([tabId]);
}
closeAllFiles() {
this._closeTabs(this._tabbedPane.allTabs());
}
/**
* @return {!Array.<!Workspace.UISourceCode>}
*/
historyUISourceCodes() {
// FIXME: there should be a way to fetch UISourceCode for its uri.
const uriToUISourceCode = {};
for (const id in this._files) {
const uiSourceCode = this._files[id];
uriToUISourceCode[uiSourceCode.url()] = uiSourceCode;
}
const result = [];
const uris = this._history._urls();
for (let i = 0; i < uris.length; ++i) {
const uiSourceCode = uriToUISourceCode[uris[i]];
if (uiSourceCode)
result.push(uiSourceCode);
}
return result;
}
_addViewListeners() {
if (!this._currentView || !this._currentView.textEditor)
return;
this._currentView.textEditor.addEventListener(
SourceFrame.SourcesTextEditor.Events.ScrollChanged, this._scrollChanged, this);
this._currentView.textEditor.addEventListener(
SourceFrame.SourcesTextEditor.Events.SelectionChanged, this._selectionChanged, this);
}
_removeViewListeners() {
if (!this._currentView || !this._currentView.textEditor)
return;
this._currentView.textEditor.removeEventListener(
SourceFrame.SourcesTextEditor.Events.ScrollChanged, this._scrollChanged, this);
this._currentView.textEditor.removeEventListener(
SourceFrame.SourcesTextEditor.Events.SelectionChanged, this._selectionChanged, this);
}
/**
* @param {!Common.Event} event
*/
_scrollChanged(event) {
if (this._scrollTimer)
clearTimeout(this._scrollTimer);
const lineNumber = /** @type {number} */ (event.data);
this._scrollTimer = setTimeout(saveHistory.bind(this), 100);
this._history.updateScrollLineNumber(this._currentFile.url(), lineNumber);
/**
* @this {Sources.TabbedEditorContainer}
*/
function saveHistory() {
this._history.save(this._previouslyViewedFilesSetting);
}
}
/**
* @param {!Common.Event} event
*/
_selectionChanged(event) {
const range = /** @type {!TextUtils.TextRange} */ (event.data);
this._history.updateSelectionRange(this._currentFile.url(), range);
this._history.save(this._previouslyViewedFilesSetting);
Extensions.extensionServer.sourceSelectionChanged(this._currentFile.url(), range);
}
/**
* @param {!Workspace.UISourceCode} uiSourceCode
* @param {boolean=} userGesture
*/
_innerShowFile(uiSourceCode, userGesture) {
const binding = Persistence.persistence.binding(uiSourceCode);
uiSourceCode = binding ? binding.fileSystem : uiSourceCode;
if (this._currentFile === uiSourceCode)
return;
this._removeViewListeners();
this._currentFile = uiSourceCode;
const tabId = this._tabIds.get(uiSourceCode) || this._appendFileTab(uiSourceCode, userGesture);
this._tabbedPane.selectTab(tabId, userGesture);
if (userGesture)
this._editorSelectedByUserAction();
const previousView = this._currentView;
this._currentView = this.visibleView;
this._addViewListeners();
const eventData = {
currentFile: this._currentFile,
currentView: this._currentView,
previousView: previousView,
userGesture: userGesture
};
this.dispatchEventToListeners(Sources.TabbedEditorContainer.Events.EditorSelected, eventData);
}
/**
* @param {!Workspace.UISourceCode} uiSourceCode
* @return {string}
*/
_titleForFile(uiSourceCode) {
const maxDisplayNameLength = 30;
let title = uiSourceCode.displayName(true).trimMiddle(maxDisplayNameLength);
if (uiSourceCode.isDirty())
title += '*';
return title;
}
/**
* @param {string} id
* @param {string} nextTabId
*/
_maybeCloseTab(id, nextTabId) {
const uiSourceCode = this._files[id];
const shouldPrompt = uiSourceCode.isDirty() && uiSourceCode.project().canSetFileContent();
// FIXME: this should be replaced with common Save/Discard/Cancel dialog.
if (!shouldPrompt ||
confirm(Common.UIString('Are you sure you want to close unsaved file: %s?', uiSourceCode.name()))) {
uiSourceCode.resetWorkingCopy();
if (nextTabId)
this._tabbedPane.selectTab(nextTabId, true);
this._tabbedPane.closeTab(id, true);
return true;
}
return false;
}
/**
* @param {!Array.<string>} ids
* @param {boolean=} forceCloseDirtyTabs
*/
_closeTabs(ids, forceCloseDirtyTabs) {
const dirtyTabs = [];
const cleanTabs = [];
for (let i = 0; i < ids.length; ++i) {
const id = ids[i];
const uiSourceCode = this._files[id];
if (!forceCloseDirtyTabs && uiSourceCode.isDirty())
dirtyTabs.push(id);
else
cleanTabs.push(id);
}
if (dirtyTabs.length)
this._tabbedPane.selectTab(dirtyTabs[0], true);
this._tabbedPane.closeTabs(cleanTabs, true);
for (let i = 0; i < dirtyTabs.length; ++i) {
const nextTabId = i + 1 < dirtyTabs.length ? dirtyTabs[i + 1] : null;
if (!this._maybeCloseTab(dirtyTabs[i], nextTabId))
break;
}
}
/**
* @param {string} tabId
* @param {!UI.ContextMenu} contextMenu
*/
_onContextMenu(tabId, contextMenu) {
const uiSourceCode = this._files[tabId];
if (uiSourceCode)
contextMenu.appendApplicableItems(uiSourceCode);
}
/**
* @param {!Workspace.UISourceCode} uiSourceCode
*/
addUISourceCode(uiSourceCode) {
const binding = Persistence.persistence.binding(uiSourceCode);
uiSourceCode = binding ? binding.fileSystem : uiSourceCode;
if (this._currentFile === uiSourceCode)
return;
const uri = uiSourceCode.url();
const index = this._history.index(uri);
if (index === -1)
return;
if (!this._tabIds.has(uiSourceCode))
this._appendFileTab(uiSourceCode, false);
// Select tab if this file was the last to be shown.
if (!index) {
this._innerShowFile(uiSourceCode, false);
return;
}
if (!this._currentFile)
return;
const currentProjectType = this._currentFile.project().type();
const addedProjectType = uiSourceCode.project().type();
const snippetsProjectType = Workspace.projectTypes.Snippets;
if (this._history.index(this._currentFile.url()) && currentProjectType === snippetsProjectType &&
addedProjectType !== snippetsProjectType)
this._innerShowFile(uiSourceCode, false);
}
/**
* @param {!Workspace.UISourceCode} uiSourceCode
*/
removeUISourceCode(uiSourceCode) {
this.removeUISourceCodes([uiSourceCode]);
}
/**
* @param {!Array.<!Workspace.UISourceCode>} uiSourceCodes
*/
removeUISourceCodes(uiSourceCodes) {
const tabIds = [];
for (let i = 0; i < uiSourceCodes.length; ++i) {
const uiSourceCode = uiSourceCodes[i];
const tabId = this._tabIds.get(uiSourceCode);
if (tabId)
tabIds.push(tabId);
}
this._tabbedPane.closeTabs(tabIds);
}
/**
* @param {!Workspace.UISourceCode} uiSourceCode
*/
_editorClosedByUserAction(uiSourceCode) {
this._history.remove(uiSourceCode.url());
this._updateHistory();
}
_editorSelectedByUserAction() {
this._updateHistory();
}
_updateHistory() {
const tabIds = this._tabbedPane.lastOpenedTabIds(Sources.TabbedEditorContainer.maximalPreviouslyViewedFilesCount);
/**
* @param {string} tabId
* @this {Sources.TabbedEditorContainer}
*/
function tabIdToURI(tabId) {
return this._files[tabId].url();
}
this._history.update(tabIds.map(tabIdToURI.bind(this)));
this._history.save(this._previouslyViewedFilesSetting);
}
/**
* @param {!Workspace.UISourceCode} uiSourceCode
* @return {string}
*/
_tooltipForFile(uiSourceCode) {
uiSourceCode = Persistence.persistence.network(uiSourceCode) || uiSourceCode;
return uiSourceCode.url();
}
/**
* @param {!Workspace.UISourceCode} uiSourceCode
* @param {boolean=} userGesture
* @param {number=} index
* @param {!UI.Widget=} replaceView
* @return {string}
*/
_appendFileTab(uiSourceCode, userGesture, index, replaceView) {
const view = replaceView || this._delegate.viewForFile(uiSourceCode);
const title = this._titleForFile(uiSourceCode);
const tooltip = this._tooltipForFile(uiSourceCode);
const tabId = this._generateTabId();
this._tabIds.set(uiSourceCode, tabId);
this._files[tabId] = uiSourceCode;
if (!replaceView) {
const savedSelectionRange = this._history.selectionRange(uiSourceCode.url());
const savedScrollLineNumber = this._history.scrollLineNumber(uiSourceCode.url());
this._restoreEditorProperties(view, savedSelectionRange, savedScrollLineNumber);
}
this._tabbedPane.appendTab(tabId, title, view, tooltip, userGesture, undefined, index);
this._updateFileTitle(uiSourceCode);
this._addUISourceCodeListeners(uiSourceCode);
return tabId;
}
/**
* @param {!UI.Widget} editorView
* @param {!TextUtils.TextRange=} selection
* @param {number=} firstLineNumber
*/
_restoreEditorProperties(editorView, selection, firstLineNumber) {
const sourceFrame =
editorView instanceof SourceFrame.SourceFrame ? /** @type {!SourceFrame.SourceFrame} */ (editorView) : null;
if (!sourceFrame)
return;
if (selection)
sourceFrame.setSelection(selection);
if (typeof firstLineNumber === 'number')
sourceFrame.scrollToLine(firstLineNumber);
}
/**
* @param {!Common.Event} event
*/
_tabClosed(event) {
const tabId = /** @type {string} */ (event.data.tabId);
const userGesture = /** @type {boolean} */ (event.data.isUserGesture);
const uiSourceCode = this._files[tabId];
if (this._currentFile === uiSourceCode) {
this._removeViewListeners();
delete this._currentView;
delete this._currentFile;
}
this._tabIds.remove(uiSourceCode);
delete this._files[tabId];
this._removeUISourceCodeListeners(uiSourceCode);
this.dispatchEventToListeners(Sources.TabbedEditorContainer.Events.EditorClosed, uiSourceCode);
if (userGesture)
this._editorClosedByUserAction(uiSourceCode);
}
/**
* @param {!Common.Event} event
*/
_tabSelected(event) {
const tabId = /** @type {string} */ (event.data.tabId);
const userGesture = /** @type {boolean} */ (event.data.isUserGesture);
const uiSourceCode = this._files[tabId];
this._innerShowFile(uiSourceCode, userGesture);
}
/**
* @param {!Workspace.UISourceCode} uiSourceCode
*/
_addUISourceCodeListeners(uiSourceCode) {
uiSourceCode.addEventListener(Workspace.UISourceCode.Events.TitleChanged, this._uiSourceCodeTitleChanged, this);
uiSourceCode.addEventListener(
Workspace.UISourceCode.Events.WorkingCopyChanged, this._uiSourceCodeWorkingCopyChanged, this);
uiSourceCode.addEventListener(
Workspace.UISourceCode.Events.WorkingCopyCommitted, this._uiSourceCodeWorkingCopyCommitted, this);
}
/**
* @param {!Workspace.UISourceCode} uiSourceCode
*/
_removeUISourceCodeListeners(uiSourceCode) {
uiSourceCode.removeEventListener(Workspace.UISourceCode.Events.TitleChanged, this._uiSourceCodeTitleChanged, this);
uiSourceCode.removeEventListener(
Workspace.UISourceCode.Events.WorkingCopyChanged, this._uiSourceCodeWorkingCopyChanged, this);
uiSourceCode.removeEventListener(
Workspace.UISourceCode.Events.WorkingCopyCommitted, this._uiSourceCodeWorkingCopyCommitted, this);
}
/**
* @param {!Workspace.UISourceCode} uiSourceCode
*/
_updateFileTitle(uiSourceCode) {
const tabId = this._tabIds.get(uiSourceCode);
if (tabId) {
const title = this._titleForFile(uiSourceCode);
this._tabbedPane.changeTabTitle(tabId, title);
let icon = null;
if (Persistence.persistence.hasUnsavedCommittedChanges(uiSourceCode)) {
icon = UI.Icon.create('smallicon-warning');
icon.title = Common.UIString('Changes to this file were not saved to file system.');
} else {
icon = Persistence.PersistenceUtils.iconForUISourceCode(uiSourceCode);
}
this._tabbedPane.setTabIcon(tabId, icon);
}
}
/**
* @param {!Common.Event} event
*/
_uiSourceCodeTitleChanged(event) {
const uiSourceCode = /** @type {!Workspace.UISourceCode} */ (event.data);
this._updateFileTitle(uiSourceCode);
this._updateHistory();
}
/**
* @param {!Common.Event} event
*/
_uiSourceCodeWorkingCopyChanged(event) {
const uiSourceCode = /** @type {!Workspace.UISourceCode} */ (event.data);
this._updateFileTitle(uiSourceCode);
}
/**
* @param {!Common.Event} event
*/
_uiSourceCodeWorkingCopyCommitted(event) {
const uiSourceCode = /** @type {!Workspace.UISourceCode} */ (event.data.uiSourceCode);
this._updateFileTitle(uiSourceCode);
}
/**
* @return {string}
*/
_generateTabId() {
return 'tab_' + (Sources.TabbedEditorContainer._tabId++);
}
/**
* @return {?Workspace.UISourceCode} uiSourceCode
*/
currentFile() {
return this._currentFile || null;
}
};
/** @enum {symbol} */
Sources.TabbedEditorContainer.Events = {
EditorSelected: Symbol('EditorSelected'),
EditorClosed: Symbol('EditorClosed')
};
Sources.TabbedEditorContainer._tabId = 0;
Sources.TabbedEditorContainer.maximalPreviouslyViewedFilesCount = 30;
/**
* @unrestricted
*/
Sources.TabbedEditorContainer.HistoryItem = class {
/**
* @param {string} url
* @param {!TextUtils.TextRange=} selectionRange
* @param {number=} scrollLineNumber
*/
constructor(url, selectionRange, scrollLineNumber) {
/** @const */ this.url = url;
/** @const */ this._isSerializable =
url.length < Sources.TabbedEditorContainer.HistoryItem.serializableUrlLengthLimit;
this.selectionRange = selectionRange;
this.scrollLineNumber = scrollLineNumber;
}
/**
* @param {!Object} serializedHistoryItem
* @return {!Sources.TabbedEditorContainer.HistoryItem}
*/
static fromObject(serializedHistoryItem) {
const selectionRange = serializedHistoryItem.selectionRange ?
TextUtils.TextRange.fromObject(serializedHistoryItem.selectionRange) :
undefined;
return new Sources.TabbedEditorContainer.HistoryItem(
serializedHistoryItem.url, selectionRange, serializedHistoryItem.scrollLineNumber);
}
/**
* @return {?Object}
*/
serializeToObject() {
if (!this._isSerializable)
return null;
const serializedHistoryItem = {};
serializedHistoryItem.url = this.url;
serializedHistoryItem.selectionRange = this.selectionRange;
serializedHistoryItem.scrollLineNumber = this.scrollLineNumber;
return serializedHistoryItem;
}
};
Sources.TabbedEditorContainer.HistoryItem.serializableUrlLengthLimit = 4096;
/**
* @unrestricted
*/
Sources.TabbedEditorContainer.History = class {
/**
* @param {!Array.<!Sources.TabbedEditorContainer.HistoryItem>} items
*/
constructor(items) {
this._items = items;
this._rebuildItemIndex();
}
/**
* @param {!Array.<!Object>} serializedHistory
* @return {!Sources.TabbedEditorContainer.History}
*/
static fromObject(serializedHistory) {
const items = [];
for (let i = 0; i < serializedHistory.length; ++i)
items.push(Sources.TabbedEditorContainer.HistoryItem.fromObject(serializedHistory[i]));
return new Sources.TabbedEditorContainer.History(items);
}
/**
* @param {string} url
* @return {number}
*/
index(url) {
return this._itemsIndex.has(url) ? /** @type {number} */ (this._itemsIndex.get(url)) : -1;
}
_rebuildItemIndex() {
/** @type {!Map<string, number>} */
this._itemsIndex = new Map();
for (let i = 0; i < this._items.length; ++i) {
console.assert(!this._itemsIndex.has(this._items[i].url));
this._itemsIndex.set(this._items[i].url, i);
}
}
/**
* @param {string} url
* @return {!TextUtils.TextRange|undefined}
*/
selectionRange(url) {
const index = this.index(url);
return index !== -1 ? this._items[index].selectionRange : undefined;
}
/**
* @param {string} url
* @param {!TextUtils.TextRange=} selectionRange
*/
updateSelectionRange(url, selectionRange) {
if (!selectionRange)
return;
const index = this.index(url);
if (index === -1)
return;
this._items[index].selectionRange = selectionRange;
}
/**
* @param {string} url
* @return {number|undefined}
*/
scrollLineNumber(url) {
const index = this.index(url);
return index !== -1 ? this._items[index].scrollLineNumber : undefined;
}
/**
* @param {string} url
* @param {number} scrollLineNumber
*/
updateScrollLineNumber(url, scrollLineNumber) {
const index = this.index(url);
if (index === -1)
return;
this._items[index].scrollLineNumber = scrollLineNumber;
}
/**
* @param {!Array.<string>} urls
*/
update(urls) {
for (let i = urls.length - 1; i >= 0; --i) {
const index = this.index(urls[i]);
let item;
if (index !== -1) {
item = this._items[index];
this._items.splice(index, 1);
} else {
item = new Sources.TabbedEditorContainer.HistoryItem(urls[i]);
}
this._items.unshift(item);
this._rebuildItemIndex();
}
}
/**
* @param {string} url
*/
remove(url) {
const index = this.index(url);
if (index !== -1) {
this._items.splice(index, 1);
this._rebuildItemIndex();
}
}
/**
* @param {!Common.Setting} setting
*/
save(setting) {
setting.set(this._serializeToObject());
}
/**
* @return {!Array.<!Object>}
*/
_serializeToObject() {
const serializedHistory = [];
for (let i = 0; i < this._items.length; ++i) {
const serializedItem = this._items[i].serializeToObject();
if (serializedItem)
serializedHistory.push(serializedItem);
if (serializedHistory.length === Sources.TabbedEditorContainer.maximalPreviouslyViewedFilesCount)
break;
}
return serializedHistory;
}
/**
* @return {!Array.<string>}
*/
_urls() {
const result = [];
for (let i = 0; i < this._items.length; ++i)
result.push(this._items[i].url);
return result;
}
};
/**
* @implements {UI.TabbedPaneTabDelegate}
* @unrestricted
*/
Sources.EditorContainerTabDelegate = class {
/**
* @param {!Sources.TabbedEditorContainer} editorContainer
*/
constructor(editorContainer) {
this._editorContainer = editorContainer;
}
/**
* @override
* @param {!UI.TabbedPane} tabbedPane
* @param {!Array.<string>} ids
*/
closeTabs(tabbedPane, ids) {
this._editorContainer._closeTabs(ids);
}
/**
* @override
* @param {string} tabId
* @param {!UI.ContextMenu} contextMenu
*/
onContextMenu(tabId, contextMenu) {
this._editorContainer._onContextMenu(tabId, contextMenu);
}
};