summaryrefslogtreecommitdiff
path: root/Source/WebInspectorUI/UserInterface/Views/TabBar.js
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebInspectorUI/UserInterface/Views/TabBar.js
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebInspectorUI/UserInterface/Views/TabBar.js')
-rw-r--r--Source/WebInspectorUI/UserInterface/Views/TabBar.js764
1 files changed, 764 insertions, 0 deletions
diff --git a/Source/WebInspectorUI/UserInterface/Views/TabBar.js b/Source/WebInspectorUI/UserInterface/Views/TabBar.js
new file mode 100644
index 000000000..d79393d8a
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Views/TabBar.js
@@ -0,0 +1,764 @@
+/*
+ * Copyright (C) 2015 Apple 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 APPLE 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 APPLE 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.
+ */
+
+WebInspector.TabBar = class TabBar extends WebInspector.View
+{
+ constructor(element, tabBarItems)
+ {
+ super(element);
+
+ this.element.classList.add("tab-bar");
+ this.element.setAttribute("role", "tablist");
+ this.element.addEventListener("mousedown", this._handleMouseDown.bind(this));
+ this.element.addEventListener("click", this._handleClick.bind(this));
+ this.element.addEventListener("mouseleave", this._handleMouseLeave.bind(this));
+
+ this.element.createChild("div", "top-border");
+
+ this._tabBarItems = [];
+
+ if (tabBarItems) {
+ for (let tabBarItem in tabBarItems)
+ this.addTabBarItem(tabBarItem);
+ }
+
+ this.addTabBarItem(WebInspector.settingsTabContentView.tabBarItem, true);
+
+ this._newTabTabBarItem = new WebInspector.PinnedTabBarItem("Images/NewTabPlus.svg", WebInspector.UIString("Create a new tab"));
+ this._newTabTabBarItem.element.addEventListener("mouseenter", this._handleNewTabMouseEnter.bind(this));
+ this._newTabTabBarItem.element.addEventListener("click", this._handleNewTabClick.bind(this));
+ this.addTabBarItem(this._newTabTabBarItem, true);
+ }
+
+ // Public
+
+ get newTabTabBarItem() { return this._newTabTabBarItem; }
+
+ updateNewTabTabBarItemState()
+ {
+ let newTabExists = !WebInspector.isNewTabWithTypeAllowed(WebInspector.NewTabContentView.Type);
+ this._newTabTabBarItem.disabled = newTabExists;
+ }
+
+ addTabBarItem(tabBarItem, doNotAnimate)
+ {
+ return this.insertTabBarItem(tabBarItem, this._tabBarItems.length, doNotAnimate);
+ }
+
+ insertTabBarItem(tabBarItem, index, doNotAnimate)
+ {
+ console.assert(tabBarItem instanceof WebInspector.TabBarItem);
+ if (!(tabBarItem instanceof WebInspector.TabBarItem))
+ return null;
+
+ if (tabBarItem.parentTabBar === this)
+ return;
+
+ if (this._tabAnimatedClosedSinceMouseEnter) {
+ // Delay adding the new tab until we can expand the tabs after a closed tab.
+ this._finishExpandingTabsAfterClose().then(() => {
+ this.insertTabBarItem(tabBarItem, index, doNotAnimate);
+ });
+ return;
+ }
+
+ if (tabBarItem.parentTabBar)
+ tabBarItem.parentTabBar.removeTabBarItem(tabBarItem);
+
+ tabBarItem.parentTabBar = this;
+
+ index = Number.constrain(index, 0, this.normalTabCount);
+
+ if (this.element.classList.contains("animating")) {
+ requestAnimationFrame(removeStyles.bind(this));
+ doNotAnimate = true;
+ }
+
+ var beforeTabSizesAndPositions;
+ if (!doNotAnimate)
+ beforeTabSizesAndPositions = this._recordTabBarItemSizesAndPositions();
+
+ this._tabBarItems.splice(index, 0, tabBarItem);
+
+ var nextSibling = this._tabBarItems[index + 1];
+ let nextSiblingElement = nextSibling ? nextSibling.element : this._tabBarItems.lastValue.element;
+
+ if (this.element.isAncestor(nextSiblingElement))
+ this.element.insertBefore(tabBarItem.element, nextSiblingElement);
+ else
+ this.element.appendChild(tabBarItem.element);
+
+ this.element.classList.toggle("single-tab", !this._hasMoreThanOneNormalTab());
+
+ tabBarItem.element.style.left = null;
+ tabBarItem.element.style.width = null;
+
+ function animateTabs()
+ {
+ this.element.classList.add("animating");
+ this.element.classList.add("inserting-tab");
+
+ this._applyTabBarItemSizesAndPositions(afterTabSizesAndPositions);
+
+ this.element.addEventListener("webkitTransitionEnd", removeStylesListener);
+ }
+
+ function removeStyles()
+ {
+ this.element.classList.remove("static-layout");
+ this.element.classList.remove("animating");
+ this.element.classList.remove("inserting-tab");
+
+ tabBarItem.element.classList.remove("being-inserted");
+
+ this._clearTabBarItemSizesAndPositions();
+
+ this.element.removeEventListener("webkitTransitionEnd", removeStylesListener);
+ }
+
+ if (!doNotAnimate) {
+ var afterTabSizesAndPositions = this._recordTabBarItemSizesAndPositions();
+
+ this.updateLayout();
+
+ let tabBarItems = this._tabBarItemsFromLeftToRight();
+ let previousTabBarItem = tabBarItems[tabBarItems.indexOf(tabBarItem) - 1] || null;
+ let previousTabBarItemSizeAndPosition = previousTabBarItem ? beforeTabSizesAndPositions.get(previousTabBarItem) : null;
+
+ if (previousTabBarItemSizeAndPosition)
+ beforeTabSizesAndPositions.set(tabBarItem, {left: previousTabBarItemSizeAndPosition.left + previousTabBarItemSizeAndPosition.width, width: 0});
+ else
+ beforeTabSizesAndPositions.set(tabBarItem, {left: 0, width: 0});
+
+ this.element.classList.add("static-layout");
+ tabBarItem.element.classList.add("being-inserted");
+
+ this._applyTabBarItemSizesAndPositions(beforeTabSizesAndPositions);
+
+ var removeStylesListener = removeStyles.bind(this);
+
+ requestAnimationFrame(animateTabs.bind(this));
+ } else
+ this.needsLayout();
+
+ if (!(tabBarItem instanceof WebInspector.PinnedTabBarItem))
+ this.updateNewTabTabBarItemState();
+
+ this.dispatchEventToListeners(WebInspector.TabBar.Event.TabBarItemAdded, {tabBarItem});
+
+ return tabBarItem;
+ }
+
+ removeTabBarItem(tabBarItemOrIndex, doNotAnimate, doNotExpand)
+ {
+ let tabBarItem = this._findTabBarItem(tabBarItemOrIndex);
+ if (!tabBarItem || tabBarItem instanceof WebInspector.PinnedTabBarItem)
+ return null;
+
+ tabBarItem.parentTabBar = null;
+
+ if (this._selectedTabBarItem === tabBarItem) {
+ var index = this._tabBarItems.indexOf(tabBarItem);
+ var nextTabBarItem = this._tabBarItems[index + 1];
+ if (!nextTabBarItem || nextTabBarItem instanceof WebInspector.PinnedTabBarItem)
+ nextTabBarItem = this._tabBarItems[index - 1];
+
+ this.selectedTabBarItem = nextTabBarItem;
+ }
+
+ if (this.element.classList.contains("animating")) {
+ requestAnimationFrame(removeStyles.bind(this));
+ doNotAnimate = true;
+ }
+
+ var beforeTabSizesAndPositions;
+ if (!doNotAnimate)
+ beforeTabSizesAndPositions = this._recordTabBarItemSizesAndPositions();
+
+ // Subtract 1 from normalTabCount since arrays begin indexing at 0.
+ let wasLastNormalTab = this._tabBarItems.indexOf(tabBarItem) === this.normalTabCount - 1;
+
+ this._tabBarItems.remove(tabBarItem);
+ tabBarItem.element.remove();
+
+ var hasMoreThanOneNormalTab = this._hasMoreThanOneNormalTab();
+ this.element.classList.toggle("single-tab", !hasMoreThanOneNormalTab);
+
+ const shouldOpenDefaultTab = !tabBarItem.isDefaultTab && !this.normalTabCount;
+ if (shouldOpenDefaultTab)
+ doNotAnimate = true;
+
+ if (!hasMoreThanOneNormalTab || wasLastNormalTab || !doNotExpand) {
+ if (!doNotAnimate) {
+ this._tabAnimatedClosedSinceMouseEnter = true;
+ this._finishExpandingTabsAfterClose(beforeTabSizesAndPositions);
+ } else
+ this.needsLayout();
+
+ this.updateNewTabTabBarItemState();
+
+ this.dispatchEventToListeners(WebInspector.TabBar.Event.TabBarItemRemoved, {tabBarItem});
+
+ if (shouldOpenDefaultTab)
+ this._openDefaultTab();
+
+ return tabBarItem;
+ }
+
+ var lastNormalTabBarItem;
+
+ function animateTabs()
+ {
+ this.element.classList.add("animating");
+ this.element.classList.add("closing-tab");
+
+ // For RTL, we need to place extra space between pinned tab and first normal tab.
+ // From left to right there is pinned tabs, extra space, then normal tabs. Compute
+ // how much extra space we need to additionally add for normal tab items.
+ let extraSpaceBetweenNormalAndPinnedTabs = 0;
+ if (WebInspector.resolvedLayoutDirection() === WebInspector.LayoutDirection.RTL) {
+ extraSpaceBetweenNormalAndPinnedTabs = this.element.getBoundingClientRect().width;
+ for (let currentTabBarItem of this._tabBarItemsFromLeftToRight())
+ extraSpaceBetweenNormalAndPinnedTabs -= currentTabBarItem.element.getBoundingClientRect().width;
+ }
+
+ let left = 0;
+ for (let currentTabBarItem of this._tabBarItemsFromLeftToRight()) {
+ let sizeAndPosition = beforeTabSizesAndPositions.get(currentTabBarItem);
+
+ if (!(currentTabBarItem instanceof WebInspector.PinnedTabBarItem)) {
+ currentTabBarItem.element.style.left = extraSpaceBetweenNormalAndPinnedTabs + left + "px";
+ left += sizeAndPosition.width;
+ lastNormalTabBarItem = currentTabBarItem;
+ } else
+ left = sizeAndPosition.left + sizeAndPosition.width;
+ }
+
+ // The selected tab and last tab need to draw a right border as well, so make them 1px wider.
+ if (this._selectedTabBarItem)
+ this._selectedTabBarItem.element.style.width = (parseFloat(this._selectedTabBarItem.element.style.width) + 1) + "px";
+
+ if (lastNormalTabBarItem !== this._selectedTabBarItem)
+ lastNormalTabBarItem.element.style.width = (parseFloat(lastNormalTabBarItem.element.style.width) + 1) + "px";
+
+ this.element.addEventListener("webkitTransitionEnd", removeStylesListener);
+ }
+
+ function removeStyles()
+ {
+ // The selected tab needs to stop drawing the right border, so make it 1px smaller. Only if it isn't the last.
+ if (this._selectedTabBarItem && this._selectedTabBarItem !== lastNormalTabBarItem)
+ this._selectedTabBarItem.element.style.width = (parseFloat(this._selectedTabBarItem.element.style.width) - 1) + "px";
+
+ this.element.classList.remove("animating");
+ this.element.classList.remove("closing-tab");
+
+ this.updateLayout();
+
+ this.element.removeEventListener("webkitTransitionEnd", removeStylesListener);
+ }
+
+ if (!doNotAnimate) {
+ this.element.classList.add("static-layout");
+
+ this._tabAnimatedClosedSinceMouseEnter = true;
+
+ this._applyTabBarItemSizesAndPositions(beforeTabSizesAndPositions);
+
+ var removeStylesListener = removeStyles.bind(this);
+
+ requestAnimationFrame(animateTabs.bind(this));
+ } else
+ this.needsLayout();
+
+ this.updateNewTabTabBarItemState();
+
+ this.dispatchEventToListeners(WebInspector.TabBar.Event.TabBarItemRemoved, {tabBarItem});
+
+ if (shouldOpenDefaultTab)
+ this._openDefaultTab();
+
+ return tabBarItem;
+ }
+
+ selectPreviousTab()
+ {
+ if (this._tabBarItems.length <= 1)
+ return;
+
+ var startIndex = this._tabBarItems.indexOf(this._selectedTabBarItem);
+ var newIndex = startIndex;
+ do {
+ if (newIndex === 0)
+ newIndex = this._tabBarItems.length - 1;
+ else
+ newIndex--;
+
+ if (!(this._tabBarItems[newIndex] instanceof WebInspector.PinnedTabBarItem))
+ break;
+ } while (newIndex !== startIndex);
+
+ if (newIndex === startIndex)
+ return;
+
+ this.selectedTabBarItem = this._tabBarItems[newIndex];
+ }
+
+ selectNextTab()
+ {
+ if (this._tabBarItems.length <= 1)
+ return;
+
+ var startIndex = this._tabBarItems.indexOf(this._selectedTabBarItem);
+ var newIndex = startIndex;
+ do {
+ if (newIndex === this._tabBarItems.length - 1)
+ newIndex = 0;
+ else
+ newIndex++;
+
+ if (!(this._tabBarItems[newIndex] instanceof WebInspector.PinnedTabBarItem))
+ break;
+ } while (newIndex !== startIndex);
+
+ if (newIndex === startIndex)
+ return;
+
+ this.selectedTabBarItem = this._tabBarItems[newIndex];
+ }
+
+ get selectedTabBarItem()
+ {
+ return this._selectedTabBarItem;
+ }
+
+ set selectedTabBarItem(tabBarItemOrIndex)
+ {
+ let tabBarItem = this._findTabBarItem(tabBarItemOrIndex);
+ if (tabBarItem === this._newTabTabBarItem) {
+ // Get the item before the New-Tab item since it is not selectable.
+ tabBarItem = this._tabBarItems[this.normalTabCount - 1];
+ }
+
+ if (this._selectedTabBarItem === tabBarItem)
+ return;
+
+ if (this._selectedTabBarItem)
+ this._selectedTabBarItem.selected = false;
+
+ this._selectedTabBarItem = tabBarItem || null;
+
+ if (this._selectedTabBarItem)
+ this._selectedTabBarItem.selected = true;
+
+ this.dispatchEventToListeners(WebInspector.TabBar.Event.TabBarItemSelected);
+ }
+
+ get tabBarItems()
+ {
+ return this._tabBarItems;
+ }
+
+ get normalTabCount()
+ {
+ return this._tabBarItems.filter((item) => !(item instanceof WebInspector.PinnedTabBarItem)).length;
+ }
+
+ // Protected
+
+ layout()
+ {
+ if (this.element.classList.contains("static-layout"))
+ return;
+
+ this.element.classList.remove("hide-titles");
+ this.element.classList.remove("collapsed");
+
+ let firstNormalTabItem = null;
+ for (let tabItem of this._tabBarItems) {
+ if (tabItem instanceof WebInspector.PinnedTabBarItem)
+ continue;
+
+ firstNormalTabItem = tabItem;
+ break;
+ }
+
+ if (!firstNormalTabItem)
+ return;
+
+ if (firstNormalTabItem.element.offsetWidth >= 120)
+ return;
+
+ this.element.classList.add("collapsed");
+
+ if (firstNormalTabItem.element.offsetWidth >= 75)
+ return;
+
+ this.element.classList.add("hide-titles");
+ }
+
+ // Private
+
+ _tabBarItemsFromLeftToRight()
+ {
+ return WebInspector.resolvedLayoutDirection() === WebInspector.LayoutDirection.LTR ? this._tabBarItems : this._tabBarItems.slice().reverse();
+ }
+
+ _findTabBarItem(tabBarItemOrIndex)
+ {
+ if (typeof tabBarItemOrIndex === "number")
+ return this._tabBarItems[tabBarItemOrIndex] || null;
+
+ if (tabBarItemOrIndex instanceof WebInspector.TabBarItem) {
+ if (this._tabBarItems.includes(tabBarItemOrIndex))
+ return tabBarItemOrIndex;
+ }
+
+ return null;
+ }
+
+ _hasMoreThanOneNormalTab()
+ {
+ let normalTabCount = 0;
+ for (let tabBarItem of this._tabBarItems) {
+ if (tabBarItem instanceof WebInspector.PinnedTabBarItem)
+ continue;
+
+ ++normalTabCount;
+ if (normalTabCount >= 2)
+ return true;
+ }
+
+ return false;
+ }
+
+ _openDefaultTab()
+ {
+ this.dispatchEventToListeners(WebInspector.TabBar.Event.OpenDefaultTab);
+ }
+
+ _recordTabBarItemSizesAndPositions()
+ {
+ var tabBarItemSizesAndPositions = new Map;
+
+ const barRect = this.element.getBoundingClientRect();
+
+ for (var tabBarItem of this._tabBarItems) {
+ var boundingRect = tabBarItem.element.getBoundingClientRect();
+ tabBarItemSizesAndPositions.set(tabBarItem, {left: boundingRect.left - barRect.left, width: boundingRect.width});
+ }
+
+ return tabBarItemSizesAndPositions;
+ }
+
+ _applyTabBarItemSizesAndPositions(tabBarItemSizesAndPositions, skipTabBarItem)
+ {
+ for (var [tabBarItem, sizeAndPosition] of tabBarItemSizesAndPositions) {
+ if (skipTabBarItem && tabBarItem === skipTabBarItem)
+ continue;
+ tabBarItem.element.style.left = sizeAndPosition.left + "px";
+ tabBarItem.element.style.width = sizeAndPosition.width + "px";
+ }
+ }
+
+ _clearTabBarItemSizesAndPositions(skipTabBarItem)
+ {
+ for (var tabBarItem of this._tabBarItems) {
+ if (skipTabBarItem && tabBarItem === skipTabBarItem)
+ continue;
+ tabBarItem.element.style.left = null;
+ tabBarItem.element.style.width = null;
+ }
+ }
+
+ _finishExpandingTabsAfterClose(beforeTabSizesAndPositions)
+ {
+ return new Promise(function(resolve, reject) {
+ console.assert(this._tabAnimatedClosedSinceMouseEnter);
+ this._tabAnimatedClosedSinceMouseEnter = false;
+
+ if (!beforeTabSizesAndPositions)
+ beforeTabSizesAndPositions = this._recordTabBarItemSizesAndPositions();
+
+ this.element.classList.remove("static-layout");
+ this._clearTabBarItemSizesAndPositions();
+
+ var afterTabSizesAndPositions = this._recordTabBarItemSizesAndPositions();
+
+ this._applyTabBarItemSizesAndPositions(beforeTabSizesAndPositions);
+ this.element.classList.add("static-layout");
+
+ function animateTabs()
+ {
+ this.element.classList.add("static-layout");
+ this.element.classList.add("animating");
+ this.element.classList.add("expanding-tabs");
+
+ this._applyTabBarItemSizesAndPositions(afterTabSizesAndPositions);
+
+ this.element.addEventListener("webkitTransitionEnd", removeStylesListener);
+ }
+
+ function removeStyles()
+ {
+ this.element.classList.remove("static-layout");
+ this.element.classList.remove("animating");
+ this.element.classList.remove("expanding-tabs");
+
+ this._clearTabBarItemSizesAndPositions();
+
+ this.updateLayout();
+
+ this.element.removeEventListener("webkitTransitionEnd", removeStylesListener);
+
+ resolve();
+ }
+
+ var removeStylesListener = removeStyles.bind(this);
+
+ requestAnimationFrame(animateTabs.bind(this));
+ }.bind(this));
+ }
+
+ _handleMouseDown(event)
+ {
+ // Only consider left mouse clicks for tab movement.
+ if (event.button !== 0 || event.ctrlKey)
+ return;
+
+ let itemElement = event.target.enclosingNodeOrSelfWithClass(WebInspector.TabBarItem.StyleClassName);
+ if (!itemElement)
+ return;
+
+ let tabBarItem = itemElement[WebInspector.TabBarItem.ElementReferenceSymbol];
+ if (!tabBarItem)
+ return;
+
+ if (tabBarItem.disabled)
+ return;
+
+ if (tabBarItem === this._newTabTabBarItem)
+ return;
+
+ let closeButtonElement = event.target.enclosingNodeOrSelfWithClass(WebInspector.TabBarItem.CloseButtonStyleClassName);
+ if (closeButtonElement)
+ return;
+
+ this.selectedTabBarItem = tabBarItem;
+
+ if (tabBarItem instanceof WebInspector.PinnedTabBarItem || !this._hasMoreThanOneNormalTab())
+ return;
+
+ this._firstNormalTabItemIndex = 0;
+ for (let i = 0; i < this._tabBarItems.length; ++i) {
+ if (this._tabBarItems[i] instanceof WebInspector.PinnedTabBarItem)
+ continue;
+
+ this._firstNormalTabItemIndex = i;
+ break;
+ }
+
+ this._mouseIsDown = true;
+
+ this._mouseMovedEventListener = this._handleMouseMoved.bind(this);
+ this._mouseUpEventListener = this._handleMouseUp.bind(this);
+
+ // Register these listeners on the document so we can track the mouse if it leaves the tab bar.
+ document.addEventListener("mousemove", this._mouseMovedEventListener, true);
+ document.addEventListener("mouseup", this._mouseUpEventListener, true);
+
+ event.preventDefault();
+ event.stopPropagation();
+ }
+
+ _handleClick(event)
+ {
+ var itemElement = event.target.enclosingNodeOrSelfWithClass(WebInspector.TabBarItem.StyleClassName);
+ if (!itemElement)
+ return;
+
+ var tabBarItem = itemElement[WebInspector.TabBarItem.ElementReferenceSymbol];
+ if (!tabBarItem)
+ return;
+
+ if (tabBarItem.disabled)
+ return;
+
+ const clickedMiddleButton = event.button === 1;
+
+ var closeButtonElement = event.target.enclosingNodeOrSelfWithClass(WebInspector.TabBarItem.CloseButtonStyleClassName);
+ if (closeButtonElement || clickedMiddleButton) {
+ // Disallow closing the default tab if it is the only tab.
+ if (tabBarItem.isDefaultTab && this.element.classList.contains("single-tab"))
+ return;
+
+ this.removeTabBarItem(tabBarItem, false, true);
+ }
+ }
+
+ _handleMouseMoved(event)
+ {
+ console.assert(event.button === 0);
+ console.assert(this._mouseIsDown);
+ if (!this._mouseIsDown)
+ return;
+
+ console.assert(this._selectedTabBarItem);
+ if (!this._selectedTabBarItem)
+ return;
+
+ event.preventDefault();
+ event.stopPropagation();
+
+ if (!this.element.classList.contains("static-layout")) {
+ this._applyTabBarItemSizesAndPositions(this._recordTabBarItemSizesAndPositions());
+ this.element.classList.add("static-layout");
+ this.element.classList.add("dragging-tab");
+ }
+
+ if (this._mouseOffset === undefined)
+ this._mouseOffset = event.pageX - this._selectedTabBarItem.element.totalOffsetLeft;
+
+ var tabBarMouseOffset = event.pageX - this.element.totalOffsetLeft;
+ var newLeft = tabBarMouseOffset - this._mouseOffset;
+
+ this._selectedTabBarItem.element.style.left = newLeft + "px";
+
+ var selectedTabMidX = newLeft + (this._selectedTabBarItem.element.realOffsetWidth / 2);
+
+ var currentIndex = this._tabBarItems.indexOf(this._selectedTabBarItem);
+ var newIndex = currentIndex;
+
+ for (let tabBarItem of this._tabBarItems) {
+ if (tabBarItem === this._selectedTabBarItem)
+ continue;
+
+ var tabBarItemRect = tabBarItem.element.getBoundingClientRect();
+
+ if (selectedTabMidX < tabBarItemRect.left || selectedTabMidX > tabBarItemRect.right)
+ continue;
+
+ newIndex = this._tabBarItems.indexOf(tabBarItem);
+ break;
+ }
+
+ // Subtract 1 from normalTabCount since arrays begin indexing at 0.
+ newIndex = Number.constrain(newIndex, this._firstNormalTabItemIndex, this.normalTabCount - 1);
+
+ if (currentIndex === newIndex)
+ return;
+
+ this._tabBarItems.splice(currentIndex, 1);
+ this._tabBarItems.splice(newIndex, 0, this._selectedTabBarItem);
+
+ let nextSibling = this._tabBarItems[newIndex + 1];
+ let nextSiblingElement = nextSibling ? nextSibling.element : this._newTabTabBarItem.element;
+
+ this.element.insertBefore(this._selectedTabBarItem.element, nextSiblingElement);
+
+ // FIXME: Animate the tabs that move to make room for the selected tab. This was causing me trouble when I tried.
+
+ let left = 0;
+ for (let tabBarItem of this._tabBarItemsFromLeftToRight()) {
+ if (tabBarItem !== this._selectedTabBarItem && tabBarItem !== this._newTabTabBarItem && parseFloat(tabBarItem.element.style.left) !== left)
+ tabBarItem.element.style.left = left + "px";
+ left += parseFloat(tabBarItem.element.style.width);
+ }
+ }
+
+ _handleMouseUp(event)
+ {
+ console.assert(event.button === 0);
+ console.assert(this._mouseIsDown);
+ if (!this._mouseIsDown)
+ return;
+
+ this.element.classList.remove("dragging-tab");
+
+ if (!this._tabAnimatedClosedSinceMouseEnter) {
+ this.element.classList.remove("static-layout");
+ this._clearTabBarItemSizesAndPositions();
+ } else {
+ let left = 0;
+ for (let tabBarItem of this._tabBarItemsFromLeftToRight()) {
+ if (tabBarItem === this._selectedTabBarItem)
+ tabBarItem.element.style.left = left + "px";
+ left += parseFloat(tabBarItem.element.style.width);
+ }
+ }
+
+ this._mouseIsDown = false;
+ this._mouseOffset = undefined;
+
+ document.removeEventListener("mousemove", this._mouseMovedEventListener, true);
+ document.removeEventListener("mouseup", this._mouseUpEventListener, true);
+
+ this._mouseMovedEventListener = null;
+ this._mouseUpEventListener = null;
+
+ event.preventDefault();
+ event.stopPropagation();
+
+ this.dispatchEventToListeners(WebInspector.TabBar.Event.TabBarItemsReordered);
+ }
+
+ _handleMouseLeave(event)
+ {
+ if (this._mouseIsDown || !this._tabAnimatedClosedSinceMouseEnter || !this.element.classList.contains("static-layout") || this.element.classList.contains("animating"))
+ return;
+
+ // This event can still fire when the mouse is inside the element if DOM nodes are added, removed or generally change inside.
+ // Check if the mouse really did leave the element by checking the bounds.
+ // FIXME: Is this a WebKit bug or correct behavior?
+ const barRect = this.element.getBoundingClientRect();
+ const newTabItemRect = this._newTabTabBarItem.element.getBoundingClientRect();
+ if (event.pageY > barRect.top && event.pageY < barRect.bottom && event.pageX > barRect.left && event.pageX < (newTabItemRect ? newTabItemRect.right : barRect.right))
+ return;
+
+ this._finishExpandingTabsAfterClose();
+ }
+
+ _handleNewTabClick(event)
+ {
+ const shouldAnimate = true;
+ WebInspector.showNewTabTab(shouldAnimate);
+ }
+
+ _handleNewTabMouseEnter(event)
+ {
+ if (!this._tabAnimatedClosedSinceMouseEnter || !this.element.classList.contains("static-layout") || this.element.classList.contains("animating"))
+ return;
+
+ this._finishExpandingTabsAfterClose();
+ }
+};
+
+WebInspector.TabBar.Event = {
+ TabBarItemSelected: "tab-bar-tab-bar-item-selected",
+ TabBarItemAdded: "tab-bar-tab-bar-item-added",
+ TabBarItemRemoved: "tab-bar-tab-bar-item-removed",
+ TabBarItemsReordered: "tab-bar-tab-bar-items-reordered",
+ OpenDefaultTab: "tab-bar-open-default-tab"
+};