| /* |
| * Copyright (C) 2013, 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.FilterBar = class FilterBar extends WI.Object |
| { |
| constructor(element) |
| { |
| super(); |
| |
| this._element = element || document.createElement("div"); |
| this._element.classList.add("filter-bar"); |
| |
| this._filterFunctionsMap = new Map; |
| |
| this._inputField = document.createElement("input"); |
| this._inputField.type = "search"; |
| this._inputField.placeholder = WI.UIString("Filter"); |
| this._inputField.spellcheck = false; |
| this._inputField.incremental = true; |
| this._inputField.addEventListener("search", this._handleFilterChanged.bind(this)); |
| this._inputField.addEventListener("input", this._handleFilterInputEvent.bind(this)); |
| this._element.appendChild(this._inputField); |
| |
| this._filtersNavigationBar = new WI.NavigationBar; |
| this._element.appendChild(this._filtersNavigationBar.element); |
| |
| this._lastFilterValue = this.filters; |
| } |
| |
| // Public |
| |
| get element() |
| { |
| return this._element; |
| } |
| |
| get inputField() |
| { |
| return this._inputField; |
| } |
| |
| get placeholder() |
| { |
| return this._inputField.placeholder; |
| } |
| |
| set placeholder(text) |
| { |
| this._inputField.placeholder = text; |
| } |
| |
| get incremental() |
| { |
| return this._inputField.incremental; |
| } |
| |
| set incremental(incremental) |
| { |
| this._inputField.incremental = incremental; |
| } |
| |
| get filters() |
| { |
| return {text: this._inputField.value, functions: [...this._filterFunctionsMap.values()]}; |
| } |
| |
| set filters(filters) |
| { |
| filters = filters || {}; |
| |
| var oldTextValue = this._inputField.value; |
| this._inputField.value = filters.text || ""; |
| if (oldTextValue !== this._inputField.value) |
| this._handleFilterChanged(); |
| } |
| |
| focus() |
| { |
| // FIXME: Workaround for: <https://webkit.org/b/149504> Caret missing from <input> after clearing text and calling select() |
| if (!this._inputField.value.length) |
| this._inputField.focus(); |
| else |
| this._inputField.select(); |
| } |
| |
| clear() |
| { |
| this._filterFunctionsMap.clear(); |
| this.filters = null; |
| |
| // Only toggle the `WI.FilterBarButton`s after clearing the function map, as otherwise each |
| // toggle will fire another WI.FilterBar.Event.FilterDidChange event. |
| for (let navigationItem of this._filtersNavigationBar.navigationItems) { |
| if (navigationItem instanceof WI.FilterBarButton) |
| navigationItem.toggle(false); |
| } |
| |
| this._inputField.value = null; // Get the placeholder to show again. |
| } |
| |
| addFilterNavigationItem(navigationItem) |
| { |
| console.assert(navigationItem instanceof WI.NavigationItem); |
| this._filtersNavigationBar.addNavigationItem(navigationItem); |
| } |
| |
| addFilterBarButton(identifier, filterFunction, activatedByDefault, defaultToolTip, activatedToolTip, image, imageWidth, imageHeight) |
| { |
| var filterBarButton = new WI.FilterBarButton(identifier, filterFunction, activatedByDefault, defaultToolTip, activatedToolTip, image, imageWidth, imageHeight); |
| filterBarButton.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleFilterBarButtonClicked, this); |
| filterBarButton.addEventListener(WI.FilterBarButton.Event.ActivatedStateToggled, this._handleFilterButtonToggled, this); |
| this.addFilterNavigationItem(filterBarButton); |
| if (filterBarButton.activated) { |
| this._filterFunctionsMap.set(filterBarButton.identifier, filterBarButton.filterFunction); |
| this._handleFilterChanged(); |
| } |
| } |
| |
| hasActiveFilters() |
| { |
| return !!this._inputField.value || !!this._filterFunctionsMap.size; |
| } |
| |
| hasFilterChanged() |
| { |
| var currentFunctions = this.filters.functions; |
| |
| if (this._lastFilterValue.text !== this._inputField.value || this._lastFilterValue.functions.length !== currentFunctions.length) |
| return true; |
| |
| for (var i = 0; i < currentFunctions.length; ++i) { |
| if (this._lastFilterValue.functions[i] !== currentFunctions[i]) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // Private |
| |
| _handleFilterBarButtonClicked(event) |
| { |
| var filterBarButton = event.target; |
| filterBarButton.toggle(); |
| } |
| |
| _handleFilterButtonToggled(event) |
| { |
| var filterBarButton = event.target; |
| if (filterBarButton.activated) |
| this._filterFunctionsMap.set(filterBarButton.identifier, filterBarButton.filterFunction); |
| else |
| this._filterFunctionsMap.delete(filterBarButton.identifier); |
| this._handleFilterChanged(); |
| } |
| |
| _handleFilterChanged() |
| { |
| if (this.hasFilterChanged()) { |
| this._lastFilterValue = this.filters; |
| this.dispatchEventToListeners(WI.FilterBar.Event.FilterDidChange); |
| } else if (!this._inputField.value) |
| this._inputField.blur(); |
| } |
| |
| _handleFilterInputEvent(event) |
| { |
| // When not incremental we still want to detect if the field becomes empty. |
| |
| if (this.incremental) |
| return; |
| |
| if (!this._inputField.value) |
| this._handleFilterChanged(); |
| } |
| }; |
| |
| WI.FilterBar.Event = { |
| FilterDidChange: "filter-bar-text-filter-did-change" |
| }; |