• 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     * @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