Index: Source/devtools/front_end/FilterBar.js |
diff --git a/Source/devtools/front_end/FilterBar.js b/Source/devtools/front_end/FilterBar.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e8e7ff8de083463479697e7f27cdd02dcf861ad9 |
--- /dev/null |
+++ b/Source/devtools/front_end/FilterBar.js |
@@ -0,0 +1,546 @@ |
+/* |
+ * Copyright (C) 2013 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. |
+ */ |
+ |
+/** |
+ * @constructor |
+ * @extends {WebInspector.Object} |
+ */ |
+WebInspector.FilterBar = function() |
+{ |
+ this._filtersShown = false; |
+ this._element = document.createElement("div"); |
+ this._element.className = "hbox"; |
+ |
+ this._filterButton = new WebInspector.StatusBarButton(WebInspector.UIString("Filter"), "filters-toggle", 3); |
+ this._filterButton.element.addEventListener("mousedown", this._handleFilterButtonClick.bind(this), false); |
+ |
+ this._filters = []; |
+} |
+ |
+WebInspector.FilterBar.Events = { |
+ FiltersToggled: "FiltersToggled" |
+} |
+ |
+WebInspector.FilterBar.FilterBarState = { |
+ Inactive : "inactive", |
+ Active : "active", |
+ Shown : "shown" |
+}; |
+ |
+WebInspector.FilterBar.prototype = { |
+ /** |
+ * @return {Element} |
+ */ |
+ filterButton: function() |
+ { |
+ return this._filterButton.element; |
+ }, |
+ |
+ /** |
+ * @return {Element} |
+ */ |
+ filtersElement: function() |
+ { |
+ return this._element; |
+ }, |
+ |
+ /** |
+ * @return {boolean} |
+ */ |
+ filtersToggled: function() |
+ { |
+ return this._filtersShown; |
+ }, |
+ |
+ /** |
+ * @param {WebInspector.FilterUI} filter |
+ */ |
+ addFilter: function(filter) |
+ { |
+ this._filters.push(filter); |
+ this._element.appendChild(filter.element()); |
+ filter.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged, this); |
+ this._updateFilterButton(); |
+ }, |
+ |
+ /** |
+ * @param {WebInspector.Event} event |
+ */ |
+ _filterChanged: function(event) |
+ { |
+ this._updateFilterButton(); |
+ }, |
+ |
+ /** |
+ * @return {string} |
+ */ |
+ _filterBarState: function() |
+ { |
+ if (this._filtersShown) |
+ return WebInspector.FilterBar.FilterBarState.Shown; |
+ var isActive = false; |
+ for (var i = 0; i < this._filters.length; ++i) { |
+ if (this._filters[i].isActive()) |
+ return WebInspector.FilterBar.FilterBarState.Active; |
+ } |
+ return WebInspector.FilterBar.FilterBarState.Inactive; |
+ }, |
+ |
+ _updateFilterButton: function() |
+ { |
+ this._filterButton.state = this._filterBarState(); |
+ }, |
+ |
+ /** |
+ * @param {Event} event |
+ */ |
+ _handleFilterButtonClick: function(event) |
+ { |
+ this._filtersShown = !this._filtersShown; |
+ this._updateFilterButton(); |
+ this.dispatchEventToListeners(WebInspector.FilterBar.Events.FiltersToggled, this._filtersShown); |
+ }, |
+ |
+ __proto__: WebInspector.Object.prototype |
+} |
+ |
+/** |
+ * @interface |
+ * @extends {WebInspector.EventTarget} |
+ */ |
+WebInspector.FilterUI = function() |
+{ |
+} |
+ |
+WebInspector.FilterUI.Events = { |
+ FilterChanged: "FilterChanged" |
+} |
+ |
+WebInspector.FilterUI.prototype = { |
+ /** |
+ * @return {boolean} |
+ */ |
+ isActive: function() { }, |
+ |
+ /** |
+ * @return {Element} |
+ */ |
+ element: function() { } |
+} |
+ |
+/** |
+ * @constructor |
+ * @implements {WebInspector.FilterUI} |
+ * @extends {WebInspector.Object} |
+ */ |
+WebInspector.TextFilterUI = function() |
+{ |
+ this._filterElement = document.createElement("div"); |
+ this._filterElement.className = "filter-text-filter"; |
+ |
+ this._filterInputElement = this._filterElement.createChild("input", "search-replace toolbar-replace-control"); |
+ this._filterInputElement.placeholder = WebInspector.UIString("Filter"); |
+ this._filterInputElement.id = "filter-input-field"; |
+ this._filterInputElement.addEventListener("mousedown", this._onFilterFieldManualFocus.bind(this), false); // when the search field is manually selected |
+ this._filterInputElement.addEventListener("input", this._onInput.bind(this), false); |
+} |
+ |
+WebInspector.TextFilterUI.prototype = { |
+ /** |
+ * @return {boolean} |
+ */ |
+ isActive: function() |
+ { |
+ return !!this._filterInputElement.value; |
+ }, |
+ |
+ /** |
+ * @return {Element} |
+ */ |
+ element: function() |
+ { |
+ return this._filterElement; |
+ }, |
+ |
+ /** |
+ * @return {string} |
+ */ |
+ value: function() |
+ { |
+ return this._filterInputElement.value; |
+ }, |
+ |
+ /** |
+ * @param {string} value |
+ */ |
+ setValue: function(value) |
+ { |
+ this._filterInputElement.value = value; |
+ this.dispatchEventToListeners(WebInspector.FilterUI.Events.FilterChanged, null); |
+ }, |
+ |
+ /** |
+ * @return {RegExp} |
+ */ |
+ regex: function() |
+ { |
+ var filterQuery = this._filterInputElement.value; |
+ return filterQuery ? createPlainTextSearchRegex(filterQuery, "i") : null; |
+ }, |
+ |
+ /** |
+ * @param {Event} event |
+ */ |
+ _onFilterFieldManualFocus: function(event) |
+ { |
+ WebInspector.setCurrentFocusElement(event.target); |
+ }, |
+ |
+ /** |
+ * @param {WebInspector.Event} event |
+ */ |
+ _onInput: function(event) |
+ { |
+ this.dispatchEventToListeners(WebInspector.FilterUI.Events.FilterChanged, null); |
+ }, |
+ |
+ __proto__: WebInspector.Object.prototype |
+} |
+ |
+/** |
+ * @constructor |
+ * @implements {WebInspector.FilterUI} |
+ * @extends {WebInspector.Object} |
+ */ |
+WebInspector.NamedBitSetFilterUI = function() |
+{ |
+ this._filtersElement = document.createElement("div"); |
+ this._filtersElement.className = "filter-bitset-filter status-bar-item"; |
+ this._filtersElement.title = WebInspector.UIString("Use %s Click to select multiple types.", WebInspector.KeyboardShortcut.shortcutToString("", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta)); |
+ |
+ this._names = []; |
+ this._allowedTypes = {}; |
+ this._typeFilterElements = {}; |
+ this._addTypeFilter(WebInspector.NamedBitSetFilterUI.ALL_TYPES, WebInspector.UIString("All")); |
+ this._filtersElement.createChild("div", "filter-bitset-filter-divider"); |
+ this._toggleTypeFilter(WebInspector.NamedBitSetFilterUI.ALL_TYPES, false); |
+} |
+ |
+WebInspector.NamedBitSetFilterUI.ALL_TYPES = "all"; |
+ |
+WebInspector.NamedBitSetFilterUI.prototype = { |
+ /** |
+ * @return {boolean} |
+ */ |
+ isActive: function() |
+ { |
+ return !this._allowedTypes[WebInspector.NamedBitSetFilterUI.ALL_TYPES]; |
+ }, |
+ |
+ /** |
+ * @param {string} name |
+ * @param {string} label |
+ */ |
+ addBit: function(name, label) |
+ { |
+ this._addTypeFilter(name, label); |
+ }, |
+ |
+ /** |
+ * @return {Element} |
+ */ |
+ element: function() |
+ { |
+ return this._filtersElement; |
+ }, |
+ |
+ /** |
+ * @param {string} typeName |
+ * @return {boolean} |
+ */ |
+ accept: function(typeName) |
+ { |
+ return !!this._allowedTypes[WebInspector.NamedBitSetFilterUI.ALL_TYPES] || !!this._allowedTypes[typeName]; |
+ }, |
+ |
+ /** |
+ * @return {Array.<string>} |
+ */ |
+ filteredOutTypes: function() |
+ { |
+ if (this._allowedTypes[WebInspector.NamedBitSetFilterUI.ALL_TYPES]) |
+ return []; |
+ var result = []; |
+ for (var i = 0; i < this._names.length; ++i) { |
+ var name = this._names[i]; |
+ if (!this._allowedTypes[name]) |
+ result.push(name); |
+ } |
+ return result; |
+ }, |
+ |
+ /** |
+ * @param {Array.<string>} filteredOutTypes |
+ */ |
+ setFilteredOutTypes: function(filteredOutTypes) |
+ { |
+ this._allowedTypes = {}; |
+ if (filteredOutTypes.length === 0) { |
+ this._allowedTypes[WebInspector.NamedBitSetFilterUI.ALL_TYPES] = true; |
+ } else { |
+ for (var i = 0; i < this._names.length; ++i) { |
+ var name = this._names[i]; |
+ this._allowedTypes[name] = true; |
+ } |
+ for (var i = 0; i < filteredOutTypes.length; ++i) |
+ delete this._allowedTypes[filteredOutTypes[i]]; |
+ } |
+ for (var typeName in this._typeFilterElements) |
+ this._typeFilterElements[typeName].enableStyleClass("selected", this._allowedTypes[typeName]); |
+ this.dispatchEventToListeners(WebInspector.FilterUI.Events.FilterChanged, null); |
+ }, |
+ |
+ /** |
+ * @return {Array.<string>} |
+ */ |
+ acceptedTypes: function() |
+ { |
+ if (this._allowedTypes[WebInspector.NamedBitSetFilterUI.ALL_TYPES]) |
+ return [WebInspector.NamedBitSetFilterUI.ALL_TYPES]; |
+ var result = []; |
+ for (var i = 0; i < this._names.length; ++i) { |
+ var name = this._names[i]; |
+ if (this._allowedTypes[name]) |
+ result.push(name); |
+ } |
+ return result; |
+ }, |
+ |
+ /** |
+ * @param {Array.<string>} acceptedTypes |
+ */ |
+ setAcceptedTypes: function(acceptedTypes) |
+ { |
+ this._allowedTypes = {}; |
+ for (var i = 0; i < acceptedTypes.length; ++i) |
+ this._allowedTypes[acceptedTypes[i]] = true; |
+ for (var typeName in this._typeFilterElements) |
+ this._typeFilterElements[typeName].enableStyleClass("selected", this._allowedTypes[typeName]); |
+ this.dispatchEventToListeners(WebInspector.FilterUI.Events.FilterChanged, null); |
+ }, |
+ |
+ /** |
+ * @param {string} typeName |
+ * @param {string} label |
+ */ |
+ _addTypeFilter: function(typeName, label) |
+ { |
+ var typeFilterElement = this._filtersElement.createChild("li", typeName); |
+ typeFilterElement.typeName = typeName; |
+ typeFilterElement.createTextChild(label); |
+ typeFilterElement.addEventListener("click", this._onTypeFilterClicked.bind(this), false); |
+ this._typeFilterElements[typeName] = typeFilterElement; |
+ this._names.push(typeName); |
+ }, |
+ |
+ /** |
+ * @param {!Event} e |
+ */ |
+ _onTypeFilterClicked: function(e) |
+ { |
+ var toggle; |
+ if (WebInspector.isMac()) |
+ toggle = e.metaKey && !e.ctrlKey && !e.altKey && !e.shiftKey; |
+ else |
+ toggle = e.ctrlKey && !e.metaKey && !e.altKey && !e.shiftKey; |
+ this._toggleTypeFilter(e.target.typeName, toggle); |
+ }, |
+ |
+ /** |
+ * @param {string} typeName |
+ * @param {boolean} allowMultiSelect |
+ */ |
+ _toggleTypeFilter: function(typeName, allowMultiSelect) |
+ { |
+ if (allowMultiSelect && typeName !== WebInspector.NamedBitSetFilterUI.ALL_TYPES) |
+ this._typeFilterElements[WebInspector.NamedBitSetFilterUI.ALL_TYPES].removeStyleClass("selected"); |
+ else { |
+ for (var key in this._typeFilterElements) |
+ this._typeFilterElements[key].removeStyleClass("selected"); |
+ } |
+ |
+ var filterElement = this._typeFilterElements[typeName]; |
+ filterElement.enableStyleClass("selected", !filterElement.hasStyleClass("selected")); |
+ |
+ this._allowedTypes = {}; |
+ for (var key in this._typeFilterElements) { |
+ if (this._typeFilterElements[key].hasStyleClass("selected")) |
+ this._allowedTypes[key] = true; |
+ } |
+ this.dispatchEventToListeners(WebInspector.FilterUI.Events.FilterChanged, null); |
+ }, |
+ |
+ __proto__: WebInspector.Object.prototype |
+} |
+ |
+/** |
+ * @constructor |
+ * @implements {WebInspector.FilterUI} |
+ * @extends {WebInspector.Object} |
+ * @param {Array.<{value: *, label: string, title: string}>} options |
+ */ |
+WebInspector.ComboBoxFilterUI = function(options) |
+{ |
+ this._filterElement = document.createElement("div"); |
+ this._filterElement.className = "filter-combobox-filter"; |
+ |
+ this._options = options; |
+ this._filterComboBox = new WebInspector.StatusBarComboBox(this._filterChanged.bind(this)); |
+ for (var i = 0; i < options.length; ++i) { |
+ var filterOption = options[i]; |
+ var option = document.createElement("option"); |
+ option.text = filterOption.label; |
+ option.title = filterOption.title; |
+ this._filterComboBox.addOption(option); |
+ this._filterComboBox.element.title = this._filterComboBox.selectedOption().title; |
+ } |
+ this._filterElement.appendChild(this._filterComboBox.element); |
+} |
+ |
+WebInspector.ComboBoxFilterUI.prototype = { |
+ /** |
+ * @return {boolean} |
+ */ |
+ isActive: function() |
+ { |
+ return this._filterComboBox.selectedIndex() !== 0; |
+ }, |
+ |
+ /** |
+ * @return {Element} |
+ */ |
+ element: function() |
+ { |
+ return this._filterElement; |
+ }, |
+ |
+ /** |
+ * @param {string} typeName |
+ * @return {*} |
+ */ |
+ value: function(typeName) |
+ { |
+ var option = this._options[this._filterComboBox.selectedIndex()]; |
+ return option.value; |
+ }, |
+ |
+ /** |
+ * @param {Event} event |
+ */ |
+ _filterChanged: function(event) |
+ { |
+ var option = this._options[this._filterComboBox.selectedIndex()]; |
+ this._filterComboBox.element.title = option.title; |
+ this.dispatchEventToListeners(WebInspector.FilterUI.Events.FilterChanged, null); |
+ }, |
+ |
+ __proto__: WebInspector.Object.prototype |
+} |
+ |
+/** |
+ * @constructor |
+ * @implements {WebInspector.FilterUI} |
+ * @extends {WebInspector.Object} |
+ * @param {boolean} activeWhenChecked |
+ */ |
+WebInspector.CheckboxFilterUI = function(className, title, activeWhenChecked) |
+{ |
+ this._className = className; |
+ this._filterElement = document.createElement("div"); |
+ this._filterElement.classList.add("filter-checkbox-filter", "filter-checkbox-filter-" + this._className); |
+ this._activeWhenChecked = activeWhenChecked; |
+ this._createCheckbox(title); |
+} |
+ |
+WebInspector.CheckboxFilterUI.prototype = { |
+ /** |
+ * @return {boolean} |
+ */ |
+ isActive: function() |
+ { |
+ return this._activeWhenChecked === this._checkElement.checked; |
+ }, |
+ |
+ /** |
+ * @return {Element} |
+ */ |
+ element: function() |
+ { |
+ return this._filterElement; |
+ }, |
+ |
+ /** |
+ * @return {boolean} |
+ */ |
+ checked: function() |
+ { |
+ return this._checkElement.checked; |
+ }, |
+ |
+ /** |
+ * @param {boolean} checked |
+ */ |
+ setChecked: function(checked) |
+ { |
+ this._checkElement.checked = checked; |
+ this._checkElement.enableStyleClass("checkbox-filter-checkbox-checked", this._checkElement.checked); |
+ this.dispatchEventToListeners(WebInspector.FilterUI.Events.FilterChanged, null); |
+ }, |
+ |
+ _createCheckbox: function(title) |
+ { |
+ var label = this._filterElement.createChild("label"); |
+ var checkBorder = label.createChild("div", "checkbox-filter-checkbox"); |
+ this._checkElement = checkBorder.createChild("div", "checkbox-filter-checkbox-check checkbox-filter-checkbox-checked"); |
+ this._checkElement.type = "checkbox"; |
+ this._checkElement.checked = true; |
+ this._filterElement.addEventListener("click", listener.bind(this), false); |
+ |
+ function listener(event) |
+ { |
+ this.setChecked(!this._checkElement.checked); |
+ } |
+ |
+ var typeElement = label.createChild("span", "type"); |
+ typeElement.textContent = title; |
+ }, |
+ |
+ __proto__: WebInspector.Object.prototype |
+} |