• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     * @param {!Element} parentElement
99     */
100    show: function(parentElement)
101    {
102        this._tabbedPane.show(parentElement);
103    },
104
105    /**
106     * @param {!WebInspector.UISourceCode} uiSourceCode
107     */
108    showFile: function(uiSourceCode)
109    {
110        this._innerShowFile(uiSourceCode, true);
111    },
112
113    /**
114     * @return {!Array.<!WebInspector.UISourceCode>}
115     */
116    historyUISourceCodes: function()
117    {
118        // FIXME: there should be a way to fetch UISourceCode for its uri.
119        var uriToUISourceCode = {};
120        for (var id in this._files) {
121            var uiSourceCode = this._files[id];
122            uriToUISourceCode[uiSourceCode.uri()] = uiSourceCode;
123        }
124
125        var result = [];
126        var uris = this._history._urls();
127        for (var i = 0; i < uris.length; ++i) {
128            var uiSourceCode = uriToUISourceCode[uris[i]];
129            if (uiSourceCode)
130                result.push(uiSourceCode);
131        }
132        return result;
133    },
134
135    _addScrollAndSelectionListeners: function()
136    {
137        if (!this._currentView)
138            return;
139        this._currentView.addEventListener(WebInspector.SourceFrame.Events.ScrollChanged, this._scrollChanged, this);
140        this._currentView.addEventListener(WebInspector.SourceFrame.Events.SelectionChanged, this._selectionChanged, this);
141    },
142
143    _removeScrollAndSelectionListeners: function()
144    {
145        if (!this._currentView)
146            return;
147        this._currentView.removeEventListener(WebInspector.SourceFrame.Events.ScrollChanged, this._scrollChanged, this);
148        this._currentView.removeEventListener(WebInspector.SourceFrame.Events.SelectionChanged, this._selectionChanged, this);
149    },
150
151    _scrollChanged: function(event)
152    {
153        var lineNumber = /** @type {number} */ (event.data);
154        this._history.updateScrollLineNumber(this._currentFile.uri(), lineNumber);
155        this._history.save(this._previouslyViewedFilesSetting);
156    },
157
158    _selectionChanged: function(event)
159    {
160        var range = /** @type {!WebInspector.TextRange} */ (event.data);
161        this._history.updateSelectionRange(this._currentFile.uri(), range);
162        this._history.save(this._previouslyViewedFilesSetting);
163    },
164
165    /**
166     * @param {!WebInspector.UISourceCode} uiSourceCode
167     * @param {boolean=} userGesture
168     */
169    _innerShowFile: function(uiSourceCode, userGesture)
170    {
171        if (this._currentFile === uiSourceCode)
172            return;
173
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() || uiSourceCode.hasUnsavedCommittedChanges())
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        /**
321         * @param {string} tabId
322         * @this {WebInspector.TabbedEditorContainer}
323         */
324        function tabIdToURI(tabId)
325        {
326            return this._files[tabId].uri();
327        }
328
329        this._history.update(tabIds.map(tabIdToURI.bind(this)));
330        this._history.save(this._previouslyViewedFilesSetting);
331    },
332
333    /**
334     * @param {!WebInspector.UISourceCode} uiSourceCode
335     * @return {string}
336     */
337    _tooltipForFile: function(uiSourceCode)
338    {
339        return uiSourceCode.originURL();
340    },
341
342    /**
343     * @param {!WebInspector.UISourceCode} uiSourceCode
344     * @param {boolean=} userGesture
345     * @return {string}
346     */
347    _appendFileTab: function(uiSourceCode, userGesture)
348    {
349        var view = this._delegate.viewForFile(uiSourceCode);
350        var title = this._titleForFile(uiSourceCode);
351        var tooltip = this._tooltipForFile(uiSourceCode);
352
353        var tabId = this._generateTabId();
354        this._tabIds.put(uiSourceCode, tabId);
355        this._files[tabId] = uiSourceCode;
356
357        var savedSelectionRange = this._history.selectionRange(uiSourceCode.uri());
358        if (savedSelectionRange)
359            view.setSelection(savedSelectionRange);
360        var savedScrollLineNumber = this._history.scrollLineNumber(uiSourceCode.uri());
361        if (savedScrollLineNumber)
362            view.scrollToLine(savedScrollLineNumber);
363
364        this._tabbedPane.appendTab(tabId, title, view, tooltip, userGesture);
365
366        this._updateFileTitle(uiSourceCode);
367        this._addUISourceCodeListeners(uiSourceCode);
368        return tabId;
369    },
370
371    /**
372     * @param {!WebInspector.Event} event
373     */
374    _tabClosed: function(event)
375    {
376        var tabId = /** @type {string} */ (event.data.tabId);
377        var userGesture = /** @type {boolean} */ (event.data.isUserGesture);
378
379        var uiSourceCode = this._files[tabId];
380        if (this._currentFile === uiSourceCode) {
381            this._removeScrollAndSelectionListeners();
382            delete this._currentView;
383            delete this._currentFile;
384        }
385        this._tabIds.remove(uiSourceCode);
386        delete this._files[tabId];
387
388        this._removeUISourceCodeListeners(uiSourceCode);
389
390        this.dispatchEventToListeners(WebInspector.TabbedEditorContainer.Events.EditorClosed, uiSourceCode);
391
392        if (userGesture)
393            this._editorClosedByUserAction(uiSourceCode);
394    },
395
396    /**
397     * @param {!WebInspector.Event} event
398     */
399    _tabSelected: 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        this._innerShowFile(uiSourceCode, userGesture);
406    },
407
408    /**
409     * @param {!WebInspector.UISourceCode} uiSourceCode
410     */
411    _addUISourceCodeListeners: function(uiSourceCode)
412    {
413        uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.TitleChanged, this._uiSourceCodeTitleChanged, this);
414        uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._uiSourceCodeWorkingCopyChanged, this);
415        uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._uiSourceCodeWorkingCopyCommitted, this);
416        uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.SavedStateUpdated, this._uiSourceCodeSavedStateUpdated, this);
417        uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.FormattedChanged, this._uiSourceCodeFormattedChanged, this);
418    },
419
420    /**
421     * @param {!WebInspector.UISourceCode} uiSourceCode
422     */
423    _removeUISourceCodeListeners: function(uiSourceCode)
424    {
425        uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.TitleChanged, this._uiSourceCodeTitleChanged, this);
426        uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._uiSourceCodeWorkingCopyChanged, this);
427        uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._uiSourceCodeWorkingCopyCommitted, this);
428        uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.SavedStateUpdated, this._uiSourceCodeSavedStateUpdated, this);
429        uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.FormattedChanged, this._uiSourceCodeFormattedChanged, this);
430    },
431
432    /**
433     * @param {!WebInspector.UISourceCode} uiSourceCode
434     */
435    _updateFileTitle: function(uiSourceCode)
436    {
437        var tabId = this._tabIds.get(uiSourceCode);
438        if (tabId) {
439            var title = this._titleForFile(uiSourceCode);
440            this._tabbedPane.changeTabTitle(tabId, title);
441            if (uiSourceCode.hasUnsavedCommittedChanges())
442                this._tabbedPane.setTabIcon(tabId, "editor-container-unsaved-committed-changes-icon", WebInspector.UIString("Changes to this file were not saved to file system."));
443            else
444                this._tabbedPane.setTabIcon(tabId, "");
445        }
446    },
447
448    _uiSourceCodeTitleChanged: function(event)
449    {
450        var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.target);
451        this._updateFileTitle(uiSourceCode);
452        this._updateHistory();
453    },
454
455    _uiSourceCodeWorkingCopyChanged: function(event)
456    {
457        var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.target);
458        this._updateFileTitle(uiSourceCode);
459    },
460
461    _uiSourceCodeWorkingCopyCommitted: function(event)
462    {
463        var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.target);
464        this._updateFileTitle(uiSourceCode);
465    },
466
467    _uiSourceCodeSavedStateUpdated: function(event)
468    {
469        var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.target);
470        this._updateFileTitle(uiSourceCode);
471    },
472
473    _uiSourceCodeFormattedChanged: function(event)
474    {
475        var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.target);
476        this._updateFileTitle(uiSourceCode);
477    },
478
479    reset: function()
480    {
481        delete this._userSelectedFiles;
482    },
483
484    /**
485     * @return {string}
486     */
487    _generateTabId: function()
488    {
489        return "tab_" + (WebInspector.TabbedEditorContainer._tabId++);
490    },
491
492    /**
493     * @return {!WebInspector.UISourceCode} uiSourceCode
494     */
495    currentFile: function()
496    {
497        return this._currentFile;
498    },
499
500    __proto__: WebInspector.Object.prototype
501}
502
503/**
504 * @constructor
505 * @param {string} url
506 * @param {!WebInspector.TextRange=} selectionRange
507 * @param {number=} scrollLineNumber
508 */
509WebInspector.TabbedEditorContainer.HistoryItem = function(url, selectionRange, scrollLineNumber)
510{
511    /** @const */ this.url = url;
512    /** @const */ this._isSerializable = url.length < WebInspector.TabbedEditorContainer.HistoryItem.serializableUrlLengthLimit;
513    this.selectionRange = selectionRange;
514    this.scrollLineNumber = scrollLineNumber;
515}
516
517WebInspector.TabbedEditorContainer.HistoryItem.serializableUrlLengthLimit = 4096;
518
519/**
520 * @param {!Object} serializedHistoryItem
521 * @return {!WebInspector.TabbedEditorContainer.HistoryItem}
522 */
523WebInspector.TabbedEditorContainer.HistoryItem.fromObject = function (serializedHistoryItem)
524{
525    var selectionRange = serializedHistoryItem.selectionRange ? WebInspector.TextRange.fromObject(serializedHistoryItem.selectionRange) : undefined;
526    return new WebInspector.TabbedEditorContainer.HistoryItem(serializedHistoryItem.url, selectionRange, serializedHistoryItem.scrollLineNumber);
527}
528
529WebInspector.TabbedEditorContainer.HistoryItem.prototype = {
530    /**
531     * @return {?Object}
532     */
533    serializeToObject: function()
534    {
535        if (!this._isSerializable)
536            return null;
537        var serializedHistoryItem = {};
538        serializedHistoryItem.url = this.url;
539        serializedHistoryItem.selectionRange = this.selectionRange;
540        serializedHistoryItem.scrollLineNumber = this.scrollLineNumber;
541        return serializedHistoryItem;
542    },
543
544    __proto__: WebInspector.Object.prototype
545}
546
547/**
548 * @constructor
549 * @param {!Array.<!WebInspector.TabbedEditorContainer.HistoryItem>} items
550 */
551WebInspector.TabbedEditorContainer.History = function(items)
552{
553    this._items = items;
554    this._rebuildItemIndex();
555}
556
557/**
558 * @param {!Array.<!Object>} serializedHistory
559 * @return {!WebInspector.TabbedEditorContainer.History}
560 */
561WebInspector.TabbedEditorContainer.History.fromObject = function(serializedHistory)
562{
563    var items = [];
564    for (var i = 0; i < serializedHistory.length; ++i)
565        items.push(WebInspector.TabbedEditorContainer.HistoryItem.fromObject(serializedHistory[i]));
566    return new WebInspector.TabbedEditorContainer.History(items);
567}
568
569WebInspector.TabbedEditorContainer.History.prototype = {
570    /**
571     * @param {string} url
572     * @return {number}
573     */
574    index: function(url)
575    {
576        var index = this._itemsIndex[url];
577        if (typeof index === "number")
578            return index;
579        return -1;
580    },
581
582    _rebuildItemIndex: function()
583    {
584        this._itemsIndex = {};
585        for (var i = 0; i < this._items.length; ++i) {
586            console.assert(!this._itemsIndex.hasOwnProperty(this._items[i].url));
587            this._itemsIndex[this._items[i].url] = i;
588        }
589    },
590
591    /**
592     * @param {string} url
593     * @return {!WebInspector.TextRange|undefined}
594     */
595    selectionRange: function(url)
596    {
597        var index = this.index(url);
598        return index !== -1 ? this._items[index].selectionRange : undefined;
599    },
600
601    /**
602     * @param {string} url
603     * @param {!WebInspector.TextRange=} selectionRange
604     */
605    updateSelectionRange: function(url, selectionRange)
606    {
607        if (!selectionRange)
608            return;
609        var index = this.index(url);
610        if (index === -1)
611            return;
612        this._items[index].selectionRange = selectionRange;
613    },
614
615    /**
616     * @param {string} url
617     * @return {number|undefined}
618     */
619    scrollLineNumber: function(url)
620    {
621        var index = this.index(url);
622        return index !== -1 ? this._items[index].scrollLineNumber : undefined;
623    },
624
625    /**
626     * @param {string} url
627     * @param {number} scrollLineNumber
628     */
629    updateScrollLineNumber: function(url, scrollLineNumber)
630    {
631        var index = this.index(url);
632        if (index === -1)
633            return;
634        this._items[index].scrollLineNumber = scrollLineNumber;
635    },
636
637    /**
638     * @param {!Array.<string>} urls
639     */
640    update: function(urls)
641    {
642        for (var i = urls.length - 1; i >= 0; --i) {
643            var index = this.index(urls[i]);
644            var item;
645            if (index !== -1) {
646                item = this._items[index];
647                this._items.splice(index, 1);
648            } else
649                item = new WebInspector.TabbedEditorContainer.HistoryItem(urls[i]);
650            this._items.unshift(item);
651            this._rebuildItemIndex();
652        }
653    },
654
655    /**
656     * @param {string} url
657     */
658    remove: function(url)
659    {
660        var index = this.index(url);
661        if (index !== -1) {
662            this._items.splice(index, 1);
663            this._rebuildItemIndex();
664        }
665    },
666
667    /**
668     * @param {!WebInspector.Setting} setting
669     */
670    save: function(setting)
671    {
672        setting.set(this._serializeToObject());
673    },
674
675    /**
676     * @return {!Array.<!Object>}
677     */
678    _serializeToObject: function()
679    {
680        var serializedHistory = [];
681        for (var i = 0; i < this._items.length; ++i) {
682            var serializedItem = this._items[i].serializeToObject();
683            if (serializedItem)
684                serializedHistory.push(serializedItem);
685            if (serializedHistory.length === WebInspector.TabbedEditorContainer.maximalPreviouslyViewedFilesCount)
686                break;
687        }
688        return serializedHistory;
689    },
690
691
692    /**
693     * @return {!Array.<string>}
694     */
695    _urls: function()
696    {
697        var result = [];
698        for (var i = 0; i < this._items.length; ++i)
699            result.push(this._items[i].url);
700        return result;
701    },
702
703    __proto__: WebInspector.Object.prototype
704}
705
706/**
707 * @constructor
708 * @implements {WebInspector.TabbedPaneTabDelegate}
709 * @param {!WebInspector.TabbedEditorContainer} editorContainer
710 */
711WebInspector.EditorContainerTabDelegate = function(editorContainer)
712{
713    this._editorContainer = editorContainer;
714}
715
716WebInspector.EditorContainerTabDelegate.prototype = {
717    /**
718     * @param {!WebInspector.TabbedPane} tabbedPane
719     * @param {!Array.<string>} ids
720     */
721    closeTabs: function(tabbedPane, ids)
722    {
723        this._editorContainer._closeTabs(ids);
724    }
725}
726