UNPKG

monaca-lib

Version:

Monaca cloud API bindings for JavaScript

1,318 lines (1,161 loc) 38.6 kB
/* * Copyright (C) 2010 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: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 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. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT * OWNER OR 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. */ /** * @extends {WebInspector.VBox} * @constructor */ WebInspector.TabbedPane = function() { WebInspector.VBox.call(this, true); this.registerRequiredCSS("ui/tabbedPane.css"); this.element.classList.add("tabbed-pane"); this.contentElement.classList.add("tabbed-pane-shadow"); this.contentElement.tabIndex = -1; this._headerElement = this.contentElement.createChild("div", "tabbed-pane-header toolbar-colors"); this._headerElement.createChild("content").select = ".tabbed-pane-header-before"; this._headerContentsElement = this._headerElement.createChild("div", "tabbed-pane-header-contents"); this._headerElement.createChild("content").select = ".tabbed-pane-header-after"; this._tabsElement = this._headerContentsElement.createChild("div", "tabbed-pane-header-tabs"); this._contentElement = this.contentElement.createChild("div", "tabbed-pane-content"); this._contentElement.createChild("content"); /** @type {!Array.<!WebInspector.TabbedPaneTab>} */ this._tabs = []; /** @type {!Array.<!WebInspector.TabbedPaneTab>} */ this._tabsHistory = []; /** @type {!Object.<string, !WebInspector.TabbedPaneTab>} */ this._tabsById = {}; this._currentTabLocked = false; this._dropDownButton = this._createDropDownButton(); WebInspector.zoomManager.addEventListener(WebInspector.ZoomManager.Events.ZoomChanged, this._zoomChanged, this); } WebInspector.TabbedPane.EventTypes = { TabSelected: "TabSelected", TabClosed: "TabClosed" } WebInspector.TabbedPane.prototype = { /** * @param {boolean} locked */ setCurrentTabLocked: function(locked) { this._currentTabLocked = locked; this._headerElement.classList.toggle("locked", this._currentTabLocked); }, /** * @return {?WebInspector.View} */ get visibleView() { return this._currentTab ? this._currentTab.view : null; }, /** * @return {!Array.<!WebInspector.View>} */ tabViews: function() { /** * @param {!WebInspector.TabbedPaneTab} tab * @return {!WebInspector.View} */ function tabToView(tab) { return tab.view; } return this._tabs.map(tabToView); }, /** * @return {?string} */ get selectedTabId() { return this._currentTab ? this._currentTab.id : null; }, /** * @param {boolean} shrinkableTabs */ setShrinkableTabs: function(shrinkableTabs) { this._shrinkableTabs = shrinkableTabs; }, /** * @param {boolean} verticalTabLayout */ setVerticalTabLayout: function(verticalTabLayout) { this._verticalTabLayout = verticalTabLayout; this.contentElement.classList.add("vertical-tab-layout"); this.invalidateConstraints(); }, /** * @param {boolean} closeableTabs */ setCloseableTabs: function(closeableTabs) { this._closeableTabs = closeableTabs; }, /** * @param {boolean} retainTabOrder * @param {function(string, string):number=} tabOrderComparator */ setRetainTabOrder: function(retainTabOrder, tabOrderComparator) { this._retainTabOrder = retainTabOrder; this._tabOrderComparator = tabOrderComparator; }, /** * @override * @return {!Element} */ defaultFocusedElement: function() { return this.visibleView ? this.visibleView.defaultFocusedElement() : this.contentElement; }, focus: function() { if (this.visibleView) this.visibleView.focus(); else this.contentElement.focus(); }, /** * @return {!Element} */ headerElement: function() { return this._headerElement; }, /** * @param {string} id * @return {boolean} */ isTabCloseable: function(id) { var tab = this._tabsById[id]; return tab ? tab.isCloseable() : false; }, /** * @param {!WebInspector.TabbedPaneTabDelegate} delegate */ setTabDelegate: function(delegate) { var tabs = this._tabs.slice(); for (var i = 0; i < tabs.length; ++i) tabs[i].setDelegate(delegate); this._delegate = delegate; }, /** * @param {string} id * @param {string} tabTitle * @param {!WebInspector.View} view * @param {string=} tabTooltip * @param {boolean=} userGesture * @param {boolean=} isCloseable */ appendTab: function(id, tabTitle, view, tabTooltip, userGesture, isCloseable) { isCloseable = typeof isCloseable === "boolean" ? isCloseable : this._closeableTabs; var tab = new WebInspector.TabbedPaneTab(this, id, tabTitle, isCloseable, view, tabTooltip); tab.setDelegate(this._delegate); this._tabsById[id] = tab; /** * @param {!WebInspector.TabbedPaneTab} tab1 * @param {!WebInspector.TabbedPaneTab} tab2 * @this {WebInspector.TabbedPane} * @return {number} */ function comparator(tab1, tab2) { return this._tabOrderComparator(tab1.id, tab2.id); } if (this._tabOrderComparator) this._tabs.splice(insertionIndexForObjectInListSortedByFunction(tab, this._tabs, comparator.bind(this)), 0, tab); else this._tabs.push(tab); this._tabsHistory.push(tab); if (this._tabsHistory[0] === tab && this.isShowing()) this.selectTab(tab.id, userGesture); this._updateTabElements(); }, /** * @param {string} id * @param {boolean=} userGesture */ closeTab: function(id, userGesture) { this.closeTabs([id], userGesture); }, /** * @param {!Array.<string>} ids * @param {boolean=} userGesture */ closeTabs: function(ids, userGesture) { var focused = this.hasFocus(); for (var i = 0; i < ids.length; ++i) this._innerCloseTab(ids[i], userGesture); this._updateTabElements(); if (this._tabsHistory.length) this.selectTab(this._tabsHistory[0].id, false); if (focused) this.focus(); }, /** * @param {string} id * @param {boolean=} userGesture */ _innerCloseTab: function(id, userGesture) { if (!this._tabsById[id]) return; if (userGesture && !this._tabsById[id]._closeable) return; if (this._currentTab && this._currentTab.id === id) this._hideCurrentTab(); var tab = this._tabsById[id]; delete this._tabsById[id]; this._tabsHistory.splice(this._tabsHistory.indexOf(tab), 1); this._tabs.splice(this._tabs.indexOf(tab), 1); if (tab._shown) this._hideTabElement(tab); var eventData = { tabId: id, view: tab.view, isUserGesture: userGesture }; this.dispatchEventToListeners(WebInspector.TabbedPane.EventTypes.TabClosed, eventData); return true; }, /** * @param {string} tabId * @return {boolean} */ hasTab: function(tabId) { return !!this._tabsById[tabId]; }, /** * @return {!Array.<string>} */ allTabs: function() { return this._tabs.map(function (tab) { return tab.id; }); }, /** * @param {string} id * @return {!Array.<string>} */ otherTabs: function(id) { var result = []; for (var i = 0; i < this._tabs.length; ++i) { if (this._tabs[i].id !== id) result.push(this._tabs[i].id); } return result; }, /** * @param {string} id * @return {!Array.<string>} */ _tabsToTheRight: function(id) { var index = -1; for (var i = 0; i < this._tabs.length; ++i) { if (this._tabs[i].id === id) { index = i; break; } } if (index === -1) return []; return this._tabs.slice(index + 1).map(function (tab) { return tab.id; }); }, /** * @param {string} id * @param {boolean=} userGesture * @return {boolean} */ selectTab: function(id, userGesture) { if (this._currentTabLocked) return false; var focused = this.hasFocus(); var tab = this._tabsById[id]; if (!tab) return false; if (this._currentTab && this._currentTab.id === id) return true; this._hideCurrentTab(); this._showTab(tab); this._currentTab = tab; this._tabsHistory.splice(this._tabsHistory.indexOf(tab), 1); this._tabsHistory.splice(0, 0, tab); this._updateTabElements(); if (focused) this.focus(); var eventData = { tabId: id, view: tab.view, isUserGesture: userGesture }; this.dispatchEventToListeners(WebInspector.TabbedPane.EventTypes.TabSelected, eventData); return true; }, /** * @param {number} tabsCount * @return {!Array.<string>} */ lastOpenedTabIds: function(tabsCount) { function tabToTabId(tab) { return tab.id; } return this._tabsHistory.slice(0, tabsCount).map(tabToTabId); }, /** * @param {string} id * @param {string} iconClass * @param {string=} iconTooltip */ setTabIcon: function(id, iconClass, iconTooltip) { var tab = this._tabsById[id]; if (tab._setIconClass(iconClass, iconTooltip)) this._updateTabElements(); }, /** * @param {string} id * @param {string} className * @param {boolean=} force */ toggleTabClass: function(id, className, force) { var tab = this._tabsById[id]; if (tab._toggleClass(className, force)) this._updateTabElements(); }, /** * @param {!WebInspector.Event} event */ _zoomChanged: function(event) { for (var i = 0; i < this._tabs.length; ++i) delete this._tabs[i]._measuredWidth; if (this.isShowing()) this._updateTabElements(); }, /** * @param {string} id * @param {string} tabTitle */ changeTabTitle: function(id, tabTitle) { var tab = this._tabsById[id]; if (tab.title === tabTitle) return; tab.title = tabTitle; this._updateTabElements(); }, /** * @param {string} id * @param {!WebInspector.View} view */ changeTabView: function(id, view) { var tab = this._tabsById[id]; if (this._currentTab && this._currentTab.id === tab.id) { if (tab.view !== view) this._hideTab(tab); tab.view = view; this._showTab(tab); } else tab.view = view; }, /** * @param {string} id * @param {string=} tabTooltip */ changeTabTooltip: function(id, tabTooltip) { var tab = this._tabsById[id]; tab.tooltip = tabTooltip; }, onResize: function() { this._updateTabElements(); }, headerResized: function() { this._updateTabElements(); }, wasShown: function() { var effectiveTab = this._currentTab || this._tabsHistory[0]; if (effectiveTab) this.selectTab(effectiveTab.id); }, /** * @override * @return {!Constraints} */ calculateConstraints: function() { var constraints = WebInspector.VBox.prototype.calculateConstraints.call(this); var minContentConstraints = new Constraints(new Size(0, 0), new Size(50, 50)); constraints = constraints.widthToMax(minContentConstraints).heightToMax(minContentConstraints); if (this._verticalTabLayout) constraints = constraints.addWidth(new Constraints(new Size(this._headerElement.offsetWidth, 0))); else constraints = constraints.addHeight(new Constraints(new Size(0, this._headerElement.offsetHeight))); return constraints; }, _updateTabElements: function() { WebInspector.invokeOnceAfterBatchUpdate(this, this._innerUpdateTabElements); }, /** * @param {string} text */ setPlaceholderText: function(text) { this._noTabsMessage = text; }, _innerUpdateTabElements: function() { if (!this.isShowing()) return; if (!this._tabs.length) { this._contentElement.classList.add("has-no-tabs"); if (this._noTabsMessage && !this._noTabsMessageElement) { this._noTabsMessageElement = this._contentElement.createChild("div", "tabbed-pane-placeholder fill"); this._noTabsMessageElement.textContent = this._noTabsMessage; } } else { this._contentElement.classList.remove("has-no-tabs"); if (this._noTabsMessageElement) { this._noTabsMessageElement.remove(); delete this._noTabsMessageElement; } } if (!this._measuredDropDownButtonWidth) this._measureDropDownButton(); this._updateWidths(); this._updateTabsDropDown(); }, /** * @param {number} index * @param {!WebInspector.TabbedPaneTab} tab */ _showTabElement: function(index, tab) { if (index >= this._tabsElement.children.length) this._tabsElement.appendChild(tab.tabElement); else this._tabsElement.insertBefore(tab.tabElement, this._tabsElement.children[index]); tab._shown = true; }, /** * @param {!WebInspector.TabbedPaneTab} tab */ _hideTabElement: function(tab) { this._tabsElement.removeChild(tab.tabElement); tab._shown = false; }, _createDropDownButton: function() { var dropDownContainer = createElementWithClass("div", "tabbed-pane-header-tabs-drop-down-container"); dropDownContainer.createTextChild("\u00bb"); this._dropDownMenu = new WebInspector.DropDownMenu(dropDownContainer); this._dropDownMenu.addEventListener(WebInspector.DropDownMenu.Events.ItemSelected, this._dropDownMenuItemSelected, this); return dropDownContainer; }, /** * @param {!WebInspector.Event} event */ _dropDownMenuItemSelected: function(event) { var tabId = /** @type {string} */ (event.data); this.selectTab(tabId, true); }, _totalWidth: function() { return this._headerContentsElement.getBoundingClientRect().width; }, _updateTabsDropDown: function() { var tabsToShowIndexes = this._tabsToShowIndexes(this._tabs, this._tabsHistory, this._totalWidth(), this._measuredDropDownButtonWidth); for (var i = 0; i < this._tabs.length; ++i) { if (this._tabs[i]._shown && tabsToShowIndexes.indexOf(i) === -1) this._hideTabElement(this._tabs[i]); } for (var i = 0; i < tabsToShowIndexes.length; ++i) { var tab = this._tabs[tabsToShowIndexes[i]]; if (!tab._shown) this._showTabElement(i, tab); } this._populateDropDownFromIndex(); }, _populateDropDownFromIndex: function() { if (this._dropDownButton.parentElement) this._headerContentsElement.removeChild(this._dropDownButton); this._dropDownMenu.clear(); var tabsToShow = []; for (var i = 0; i < this._tabs.length; ++i) { if (!this._tabs[i]._shown) tabsToShow.push(this._tabs[i]); continue; } function compareFunction(tab1, tab2) { return tab1.title.localeCompare(tab2.title); } if (!this._retainTabOrder) tabsToShow.sort(compareFunction); var selectedId = null; for (var i = 0; i < tabsToShow.length; ++i) { var tab = tabsToShow[i]; this._dropDownMenu.addItem(tab.id, tab.title); if (this._tabsHistory[0] === tab) selectedId = tab.id; } if (tabsToShow.length) { this._headerContentsElement.appendChild(this._dropDownButton); this._dropDownMenu.selectItem(selectedId); } }, _measureDropDownButton: function() { this._dropDownButton.classList.add("measuring"); this._headerContentsElement.appendChild(this._dropDownButton); this._measuredDropDownButtonWidth = this._dropDownButton.getBoundingClientRect().width; this._headerContentsElement.removeChild(this._dropDownButton); this._dropDownButton.classList.remove("measuring"); }, _updateWidths: function() { var measuredWidths = this._measureWidths(); var maxWidth = this._shrinkableTabs ? this._calculateMaxWidth(measuredWidths.slice(), this._totalWidth()) : Number.MAX_VALUE; var i = 0; for (var tabId in this._tabs) { var tab = this._tabs[tabId]; tab.setWidth(this._verticalTabLayout ? -1 : Math.min(maxWidth, measuredWidths[i++])); } }, _measureWidths: function() { // Add all elements to measure into this._tabsElement this._tabsElement.style.setProperty("width", "2000px"); var measuringTabElements = []; for (var tabId in this._tabs) { var tab = this._tabs[tabId]; if (typeof tab._measuredWidth === "number") continue; var measuringTabElement = tab._createTabElement(true); measuringTabElement.__tab = tab; measuringTabElements.push(measuringTabElement); this._tabsElement.appendChild(measuringTabElement); } // Perform measurement for (var i = 0; i < measuringTabElements.length; ++i) { var width = measuringTabElements[i].getBoundingClientRect().width; measuringTabElements[i].__tab._measuredWidth = width; } // Nuke elements from the UI for (var i = 0; i < measuringTabElements.length; ++i) measuringTabElements[i].remove(); // Combine the results. var measuredWidths = []; for (var tabId in this._tabs) measuredWidths.push(this._tabs[tabId]._measuredWidth); this._tabsElement.style.removeProperty("width"); return measuredWidths; }, /** * @param {!Array.<number>} measuredWidths * @param {number} totalWidth */ _calculateMaxWidth: function(measuredWidths, totalWidth) { if (!measuredWidths.length) return 0; measuredWidths.sort(function(x, y) { return x - y; }); var totalMeasuredWidth = 0; for (var i = 0; i < measuredWidths.length; ++i) totalMeasuredWidth += measuredWidths[i]; if (totalWidth >= totalMeasuredWidth) return measuredWidths[measuredWidths.length - 1]; var totalExtraWidth = 0; for (var i = measuredWidths.length - 1; i > 0; --i) { var extraWidth = measuredWidths[i] - measuredWidths[i - 1]; totalExtraWidth += (measuredWidths.length - i) * extraWidth; if (totalWidth + totalExtraWidth >= totalMeasuredWidth) return measuredWidths[i - 1] + (totalWidth + totalExtraWidth - totalMeasuredWidth) / (measuredWidths.length - i); } return totalWidth / measuredWidths.length; }, /** * @param {!Array.<!WebInspector.TabbedPaneTab>} tabsOrdered * @param {!Array.<!WebInspector.TabbedPaneTab>} tabsHistory * @param {number} totalWidth * @param {number} measuredDropDownButtonWidth * @return {!Array.<number>} */ _tabsToShowIndexes: function(tabsOrdered, tabsHistory, totalWidth, measuredDropDownButtonWidth) { var tabsToShowIndexes = []; var totalTabsWidth = 0; var tabCount = tabsOrdered.length; for (var i = 0; i < tabCount; ++i) { var tab = this._retainTabOrder ? tabsOrdered[i] : tabsHistory[i]; totalTabsWidth += tab.width(); var minimalRequiredWidth = totalTabsWidth; if (i !== tabCount - 1) minimalRequiredWidth += measuredDropDownButtonWidth; if (!this._verticalTabLayout && minimalRequiredWidth > totalWidth) break; tabsToShowIndexes.push(tabsOrdered.indexOf(tab)); } tabsToShowIndexes.sort(function(x, y) { return x - y; }); return tabsToShowIndexes; }, _hideCurrentTab: function() { if (!this._currentTab) return; this._hideTab(this._currentTab); delete this._currentTab; }, /** * @param {!WebInspector.TabbedPaneTab} tab */ _showTab: function(tab) { tab.tabElement.classList.add("selected"); tab.view.show(this.element); }, /** * @param {!WebInspector.TabbedPaneTab} tab */ _hideTab: function(tab) { tab.tabElement.classList.remove("selected"); tab.view.detach(); }, /** * @override * @return {!Array.<!Element>} */ elementsToRestoreScrollPositionsFor: function() { return [ this._contentElement ]; }, /** * @param {!WebInspector.TabbedPaneTab} tab * @param {number} index */ _insertBefore: function(tab, index) { this._tabsElement.insertBefore(tab._tabElement || null, this._tabsElement.childNodes[index]); var oldIndex = this._tabs.indexOf(tab); this._tabs.splice(oldIndex, 1); if (oldIndex < index) --index; this._tabs.splice(index, 0, tab); }, /** * @param {!Element} element */ insertBeforeTabStrip: function(element) { element.classList.add("tabbed-pane-header-before"); this.element.appendChild(element); }, /** * @param {!Element} element */ appendAfterTabStrip: function(element) { element.classList.add("tabbed-pane-header-after"); this.element.appendChild(element); }, __proto__: WebInspector.VBox.prototype } /** * @constructor * @param {!WebInspector.TabbedPane} tabbedPane * @param {string} id * @param {string} title * @param {boolean} closeable * @param {!WebInspector.View} view * @param {string=} tooltip */ WebInspector.TabbedPaneTab = function(tabbedPane, id, title, closeable, view, tooltip) { this._closeable = closeable; this._tabbedPane = tabbedPane; this._id = id; this._title = title; this._tooltip = tooltip; this._view = view; this._shown = false; /** @type {number} */ this._measuredWidth; /** @type {!Element|undefined} */ this._tabElement; } WebInspector.TabbedPaneTab.prototype = { /** * @return {string} */ get id() { return this._id; }, /** * @return {string} */ get title() { return this._title; }, set title(title) { if (title === this._title) return; this._title = title; if (this._titleElement) this._titleElement.textContent = title; delete this._measuredWidth; }, /** * @return {string} */ iconClass: function() { return this._iconClass; }, /** * @return {boolean} */ isCloseable: function() { return this._closeable; }, /** * @param {string} iconClass * @param {string=} iconTooltip * @return {boolean} */ _setIconClass: function(iconClass, iconTooltip) { if (iconClass === this._iconClass && iconTooltip === this._iconTooltip) return false; this._iconClass = iconClass; this._iconTooltip = iconTooltip; if (this._iconElement) this._iconElement.remove(); if (this._iconClass && this._tabElement) this._iconElement = this._createIconElement(this._tabElement, this._titleElement); delete this._measuredWidth; return true; }, /** * @param {string} className * @param {boolean=} force * @return {boolean} */ _toggleClass: function(className, force) { var element = this.tabElement; var hasClass = element.classList.contains(className); if (hasClass === force) return false; element.classList.toggle(className, force); delete this._measuredWidth; return true; }, /** * @return {!WebInspector.View} */ get view() { return this._view; }, set view(view) { this._view = view; }, /** * @return {string|undefined} */ get tooltip() { return this._tooltip; }, set tooltip(tooltip) { this._tooltip = tooltip; if (this._titleElement) this._titleElement.title = tooltip || ""; }, /** * @return {!Element} */ get tabElement() { if (!this._tabElement) this._tabElement = this._createTabElement(false); return this._tabElement; }, /** * @return {number} */ width: function() { return this._width; }, /** * @param {number} width */ setWidth: function(width) { this.tabElement.style.width = width === -1 ? "" : (width + "px"); this._width = width; }, /** * @param {!WebInspector.TabbedPaneTabDelegate} delegate */ setDelegate: function(delegate) { this._delegate = delegate; }, _createIconElement: function(tabElement, titleElement) { var iconElement = createElementWithClass("span", "tabbed-pane-header-tab-icon " + this._iconClass); if (this._iconTooltip) iconElement.title = this._iconTooltip; tabElement.insertBefore(iconElement, titleElement); return iconElement; }, /** * @param {boolean} measuring * @return {!Element} */ _createTabElement: function(measuring) { var tabElement = createElementWithClass("div", "tabbed-pane-header-tab"); tabElement.id = "tab-" + this._id; tabElement.tabIndex = -1; tabElement.selectTabForTest = this._tabbedPane.selectTab.bind(this._tabbedPane, this.id, true); var titleElement = tabElement.createChild("span", "tabbed-pane-header-tab-title"); titleElement.textContent = this.title; titleElement.title = this.tooltip || ""; if (this._iconClass) this._createIconElement(tabElement, titleElement); if (!measuring) this._titleElement = titleElement; if (this._closeable) tabElement.createChild("div", "tabbed-pane-close-button-gray"); if (measuring) { tabElement.classList.add("measuring"); } else { tabElement.addEventListener("click", this._tabClicked.bind(this), false); tabElement.addEventListener("mousedown", this._tabMouseDown.bind(this), false); tabElement.addEventListener("mouseup", this._tabMouseUp.bind(this), false); if (this._closeable) { tabElement.addEventListener("contextmenu", this._tabContextMenu.bind(this), false); WebInspector.installDragHandle(tabElement, this._startTabDragging.bind(this), this._tabDragging.bind(this), this._endTabDragging.bind(this), "pointer"); } } return tabElement; }, /** * @param {!Event} event */ _tabClicked: function(event) { var middleButton = event.button === 1; var shouldClose = this._closeable && (middleButton || event.target.classList.contains("tabbed-pane-close-button-gray")); if (!shouldClose) { this._tabbedPane.focus(); return; } this._closeTabs([this.id]); event.consume(true); }, /** * @param {!Event} event */ _tabMouseDown: function(event) { if (event.target.classList.contains("tabbed-pane-close-button-gray") || event.button === 1) return; this._tabbedPane.selectTab(this.id, true); }, /** * @param {!Event} event */ _tabMouseUp: function(event) { // This is needed to prevent middle-click pasting on linux when tabs are clicked. if (event.button === 1) event.consume(true); }, /** * @param {!Array.<string>} ids */ _closeTabs: function(ids) { if (this._delegate) { this._delegate.closeTabs(this._tabbedPane, ids); return; } this._tabbedPane.closeTabs(ids, true); }, _tabContextMenu: function(event) { /** * @this {WebInspector.TabbedPaneTab} */ function close() { this._closeTabs([this.id]); } /** * @this {WebInspector.TabbedPaneTab} */ function closeOthers() { this._closeTabs(this._tabbedPane.otherTabs(this.id)); } /** * @this {WebInspector.TabbedPaneTab} */ function closeAll() { this._closeTabs(this._tabbedPane.allTabs()); } /** * @this {WebInspector.TabbedPaneTab} */ function closeToTheRight() { this._closeTabs(this._tabbedPane._tabsToTheRight(this.id)); } var contextMenu = new WebInspector.ContextMenu(event); contextMenu.appendItem(WebInspector.UIString.capitalize("Close"), close.bind(this)); contextMenu.appendItem(WebInspector.UIString.capitalize("Close ^others"), closeOthers.bind(this)); contextMenu.appendItem(WebInspector.UIString.capitalize("Close ^tabs to the ^right"), closeToTheRight.bind(this)); contextMenu.appendItem(WebInspector.UIString.capitalize("Close ^all"), closeAll.bind(this)); contextMenu.show(); }, /** * @param {!Event} event * @return {boolean} */ _startTabDragging: function(event) { if (event.target.classList.contains("tabbed-pane-close-button-gray")) return false; this._dragStartX = event.pageX; return true; }, /** * @param {!Event} event */ _tabDragging: function(event) { var tabElements = this._tabbedPane._tabsElement.childNodes; for (var i = 0; i < tabElements.length; ++i) { var tabElement = tabElements[i]; if (tabElement === this._tabElement) continue; var intersects = tabElement.offsetLeft + tabElement.clientWidth > this._tabElement.offsetLeft && this._tabElement.offsetLeft + this._tabElement.clientWidth > tabElement.offsetLeft; if (!intersects) continue; if (Math.abs(event.pageX - this._dragStartX) < tabElement.clientWidth / 2 + 5) break; if (event.pageX - this._dragStartX > 0) { tabElement = tabElement.nextSibling; ++i; } var oldOffsetLeft = this._tabElement.offsetLeft; this._tabbedPane._insertBefore(this, i); this._dragStartX += this._tabElement.offsetLeft - oldOffsetLeft; break; } if (!this._tabElement.previousSibling && event.pageX - this._dragStartX < 0) { this._tabElement.style.setProperty("left", "0px"); return; } if (!this._tabElement.nextSibling && event.pageX - this._dragStartX > 0) { this._tabElement.style.setProperty("left", "0px"); return; } this._tabElement.style.setProperty("position", "relative"); this._tabElement.style.setProperty("left", (event.pageX - this._dragStartX) + "px"); }, /** * @param {!Event} event */ _endTabDragging: function(event) { this._tabElement.style.removeProperty("position"); this._tabElement.style.removeProperty("left"); delete this._dragStartX; } } /** * @interface */ WebInspector.TabbedPaneTabDelegate = function() { } WebInspector.TabbedPaneTabDelegate.prototype = { /** * @param {!WebInspector.TabbedPane} tabbedPane * @param {!Array.<string>} ids */ closeTabs: function(tabbedPane, ids) { } } /** * @constructor * @param {!WebInspector.TabbedPane} tabbedPane * @param {string} extensionPoint * @param {function(string, !WebInspector.View)=} viewCallback */ WebInspector.ExtensibleTabbedPaneController = function(tabbedPane, extensionPoint, viewCallback) { this._tabbedPane = tabbedPane; this._extensionPoint = extensionPoint; this._viewCallback = viewCallback; this._tabOrders = {}; /** @type {!Object.<string, !Promise.<?WebInspector.View>>} */ this._promiseForId = {}; this._tabbedPane.setRetainTabOrder(true, this._tabOrderComparator.bind(this)); this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this); /** @type {!Map.<string, ?WebInspector.View>} */ this._views = new Map(); this._initialize(); } WebInspector.ExtensibleTabbedPaneController.prototype = { _initialize: function() { /** @type {!Map.<string, !Runtime.Extension>} */ this._extensions = new Map(); var extensions = self.runtime.extensions(this._extensionPoint); for (var i = 0; i < extensions.length; ++i) { var descriptor = extensions[i].descriptor(); var id = descriptor["name"]; this._tabOrders[id] = i; var title = WebInspector.UIString(descriptor["title"]); var settingName = descriptor["setting"]; var setting = settingName ? /** @type {!WebInspector.Setting|undefined} */ (WebInspector.settings[settingName]) : null; this._extensions.set(id, extensions[i]); if (setting) { setting.addChangeListener(this._toggleSettingBasedView.bind(this, id, title, setting)); if (setting.get()) this._tabbedPane.appendTab(id, title, new WebInspector.View()); } else { this._tabbedPane.appendTab(id, title, new WebInspector.View()); } } }, /** * @param {string} id * @param {string} title * @param {!WebInspector.Setting} setting */ _toggleSettingBasedView: function(id, title, setting) { this._tabbedPane.closeTab(id); if (setting.get()) this._tabbedPane.appendTab(id, title, new WebInspector.View()); }, /** * @param {!WebInspector.Event} event */ _tabSelected: function(event) { var tabId = /** @type {string} */ (event.data.tabId); this.viewForId(tabId).then(viewLoaded.bind(this)); /** * @this {WebInspector.ExtensibleTabbedPaneController} * @param {?WebInspector.View} view */ function viewLoaded(view) { if (!view) return; this._tabbedPane.changeTabView(tabId, view); var shouldFocus = this._tabbedPane.visibleView.element.isSelfOrAncestor(WebInspector.currentFocusElement()); if (shouldFocus) view.focus(); } }, /** * @return {!Array.<string>} */ viewIds: function() { return this._extensions.keysArray(); }, /** * @param {string} id * @return {!Promise.<?WebInspector.View>} */ viewForId: function(id) { if (this._views.has(id)) return Promise.resolve(/** @type {?WebInspector.View} */ (this._views.get(id))); if (!this._extensions.has(id)) return Promise.resolve(/** @type {?WebInspector.View} */ (null)); if (this._promiseForId[id]) return this._promiseForId[id]; var promise = this._extensions.get(id).instancePromise(); this._promiseForId[id] = /** @type {!Promise.<?WebInspector.View>} */ (promise); return promise.then(cacheView.bind(this)); /** * @param {!Object} object * @this {WebInspector.ExtensibleTabbedPaneController} */ function cacheView(object) { var view = /** @type {!WebInspector.View} */ (object); delete this._promiseForId[id]; this._views.set(id, view); if (this._viewCallback && view) this._viewCallback(id, view); return view; } }, /** * @param {string} id1 * @param {string} id2 * @return {number} */ _tabOrderComparator: function(id1, id2) { return this._tabOrders[id2] = this._tabOrders[id1]; } }