| /* |
| * 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. |
| */ |
| |
| WI.MultipleScopeBarItem = class MultipleScopeBarItem extends WI.Object |
| { |
| constructor(scopeBarItems) |
| { |
| super(); |
| |
| this._element = document.createElement("li"); |
| this._element.classList.add("multiple"); |
| |
| this._titleElement = document.createElement("span"); |
| this._element.appendChild(this._titleElement); |
| this._element.addEventListener("mousedown", this._handleMouseDown.bind(this)); |
| |
| this._selectElement = document.createElement("select"); |
| this._selectElement.addEventListener("change", this._selectElementSelectionChanged.bind(this)); |
| this._element.appendChild(this._selectElement); |
| |
| this._element.appendChild(WI.ImageUtilities.useSVGSymbol("Images/UpDownArrows.svg", "arrows")); |
| |
| this.scopeBarItems = scopeBarItems; |
| } |
| |
| // Public |
| |
| get element() |
| { |
| return this._element; |
| } |
| |
| get exclusive() |
| { |
| return false; |
| } |
| |
| get scopeBarItems() |
| { |
| return this._scopeBarItems; |
| } |
| |
| set scopeBarItems(scopeBarItems) |
| { |
| if (this._scopeBarItems) { |
| for (var scopeBarItem of this._scopeBarItems) |
| scopeBarItem.removeEventListener(null, null, this); |
| } |
| |
| this._scopeBarItems = scopeBarItems || []; |
| this._selectedScopeBarItem = null; |
| |
| this._selectElement.removeChildren(); |
| |
| function createOption(scopeBarItem) |
| { |
| var optionElement = document.createElement("option"); |
| var maxPopupMenuLength = 130; // <rdar://problem/13445374> <select> with very long option has clipped text and popup menu is still very wide |
| optionElement.textContent = scopeBarItem.label.length <= maxPopupMenuLength ? scopeBarItem.label : scopeBarItem.label.substring(0, maxPopupMenuLength) + ellipsis; |
| return optionElement; |
| } |
| |
| for (var scopeBarItem of this._visibleScopeBarItems) { |
| if (scopeBarItem.selected && !this._selectedScopeBarItem) |
| this._selectedScopeBarItem = scopeBarItem; |
| else if (scopeBarItem.selected) { |
| // Only one selected item is allowed at a time. |
| // Deselect any other items after the first. |
| scopeBarItem.selected = false; |
| } |
| |
| scopeBarItem.addEventListener(WI.ScopeBarItem.Event.SelectionChanged, this._itemSelectionDidChange, this); |
| scopeBarItem.addEventListener(WI.ScopeBarItem.Event.HiddenChanged, this._handleItemHiddenChanged, this); |
| |
| this._selectElement.appendChild(createOption(scopeBarItem)); |
| } |
| |
| this._lastSelectedScopeBarItem = this._selectedScopeBarItem || this._scopeBarItems[0] || null; |
| this._titleElement.textContent = this._lastSelectedScopeBarItem ? this._lastSelectedScopeBarItem.label : ""; |
| |
| this._element.classList.toggle("selected", !!this._selectedScopeBarItem); |
| this._selectElement.selectedIndex = this._scopeBarItems.indexOf(this._selectedScopeBarItem); |
| } |
| |
| get selected() |
| { |
| return !!this._selectedScopeBarItem; |
| } |
| |
| set selected(selected) |
| { |
| this.selectedScopeBarItem = selected ? this._lastSelectedScopeBarItem : null; |
| } |
| |
| get selectedScopeBarItem() |
| { |
| return this._selectedScopeBarItem; |
| } |
| |
| set selectedScopeBarItem(selectedScopeBarItem) |
| { |
| this._ignoreItemSelectedEvent = true; |
| |
| if (this._selectedScopeBarItem) { |
| this._selectedScopeBarItem.selected = false; |
| this._lastSelectedScopeBarItem = this._selectedScopeBarItem; |
| } else if (!this._lastSelectedScopeBarItem) |
| this._lastSelectedScopeBarItem = selectedScopeBarItem; |
| |
| this._element.classList.toggle("selected", !!selectedScopeBarItem); |
| this._selectedScopeBarItem = selectedScopeBarItem || null; |
| |
| let selectedIndex = this._visibleScopeBarItems.indexOf(this._selectedScopeBarItem); |
| if (selectedIndex < 0) |
| selectedIndex = 0; |
| this._selectElement.selectedIndex = selectedIndex; |
| |
| if (this._selectedScopeBarItem) { |
| this.displaySelectedItem(); |
| this._selectedScopeBarItem.selected = true; |
| } |
| |
| this.dispatchEventToListeners(WI.ScopeBarItem.Event.SelectionChanged, { |
| extendSelection: WI.modifierKeys.metaKey && !WI.modifierKeys.ctrlKey && !WI.modifierKeys.altKey && !WI.modifierKeys.shiftKey, |
| }); |
| |
| this._ignoreItemSelectedEvent = false; |
| } |
| |
| displaySelectedItem() |
| { |
| this._titleElement.textContent = (this._selectedScopeBarItem || this._scopeBarItems[0]).label; |
| } |
| |
| displayWidestItem() |
| { |
| let widestLabel = null; |
| let widestSize = 0; |
| for (let option of Array.from(this._selectElement.options)) { |
| this._titleElement.textContent = option.label; |
| if (this._titleElement.realOffsetWidth > widestSize) { |
| widestSize = this._titleElement.realOffsetWidth; |
| widestLabel = option.label; |
| } |
| } |
| this._titleElement.textContent = widestLabel; |
| } |
| |
| // Private |
| |
| get _visibleScopeBarItems() |
| { |
| return this._scopeBarItems.filter((item) => !item.hidden); |
| } |
| |
| _handleMouseDown(event) |
| { |
| // Only handle left mouse clicks. |
| if (event.button !== 0) |
| return; |
| |
| if (event.__original) |
| return; |
| |
| // Only support click to select when the item is not selected yet. |
| // Clicking when selected will cause the menu to appear instead. |
| if (this._element.classList.contains("selected")) { |
| let newEvent = new event.constructor(event.type, event); |
| newEvent.__original = event; |
| |
| event.stop(); |
| |
| this._selectElement.dispatchEvent(newEvent); |
| return; |
| } |
| |
| this.selected = true; |
| } |
| |
| _selectElementSelectionChanged(event) |
| { |
| this.selectedScopeBarItem = this._visibleScopeBarItems[this._selectElement.selectedIndex]; |
| } |
| |
| _itemSelectionDidChange(event) |
| { |
| if (this._ignoreItemSelectedEvent) |
| return; |
| this.selectedScopeBarItem = event.target.selected ? event.target : null; |
| } |
| |
| _handleItemHiddenChanged(event) |
| { |
| // Regenerate the <select> with the new options. |
| this.scopeBarItems = this._scopeBarItems; |
| } |
| }; |