Chromium Code Reviews| Index: Source/devtools/front_end/FilterController.js |
| diff --git a/Source/devtools/front_end/FilterController.js b/Source/devtools/front_end/FilterController.js |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..141fcb474b2ad7f58576ee63b7eba802e243417a |
| --- /dev/null |
| +++ b/Source/devtools/front_end/FilterController.js |
| @@ -0,0 +1,542 @@ |
| +/* |
| + * 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. |
| + */ |
| + |
| +/** |
| + * @interface |
| + * @extends {WebInspector.EventTarget} |
| + */ |
| +WebInspector.Filter = function() |
| +{ |
| +} |
| + |
| +WebInspector.Filter.Events = { |
| + FilterChanged: "FilterChanged" |
| +} |
| + |
| +WebInspector.Filter.prototype = { |
| + /** |
| + * @return {boolean} |
| + */ |
| + isActive: function() { }, |
| + |
| + /** |
| + * @return {Element} |
| + */ |
| + element: function() { } |
| +} |
| + |
| +/** |
| + * @constructor |
| + * @implements {WebInspector.Filter} |
| + * @extends {WebInspector.Object} |
| + */ |
| +WebInspector.TextFilter = function() |
| +{ |
| + this._filterElement = document.createElement("div"); |
| + this._filterElement.className = "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.TextFilter.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.Filter.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.Filter.Events.FilterChanged, null); |
| + }, |
| + |
| + __proto__: WebInspector.Object.prototype |
| +} |
| + |
| +/** |
| + * @constructor |
| + * @implements {WebInspector.Filter} |
| + * @extends {WebInspector.Object} |
| + * @param {Array.<{name: string, label: string}>} types |
| + */ |
| +WebInspector.TypesFilter = function(types) |
|
pfeldman
2013/10/22 15:15:01
NamedBitSetFilter
|
| +{ |
| + this._filtersElement = document.createElement("div"); |
| + this._filtersElement.className = "types-filter status-bar-item"; |
| + this._filtersElement.title = WebInspector.UIString("Use %s Click to select multiple types.", WebInspector.KeyboardShortcut.shortcutToString("", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta)); |
| + |
| + this._types = types; |
| + this._allowedTypes = {}; |
| + this._typeFilterElements = {}; |
| + this._addTypeFilter(WebInspector.TypesFilter.ALL_TYPES, WebInspector.UIString("All")); |
| + |
| + this._filtersElement.createChild("div", "types-filter-divider"); |
| + |
| + for (var i = 0; i < types.length; ++i) { |
|
pfeldman
2013/10/22 15:15:01
{}
|
| + this._addTypeFilter(types[i].name, types[i].label); |
| + } |
| + this._toggleTypeFilter(WebInspector.TypesFilter.ALL_TYPES, false); |
| +} |
| + |
| +WebInspector.TypesFilter.ALL_TYPES = "all"; |
| + |
| +WebInspector.TypesFilter.prototype = { |
| + /** |
| + * @return {boolean} |
| + */ |
| + isActive: function() |
| + { |
| + return !this._allowedTypes[WebInspector.TypesFilter.ALL_TYPES]; |
| + }, |
| + |
| + /** |
| + * @return {Element} |
| + */ |
| + element: function() |
| + { |
| + return this._filtersElement; |
| + }, |
| + |
| + /** |
| + * @param {string} typeName |
| + * @return {boolean} |
| + */ |
| + accept: function(typeName) |
| + { |
| + return !!this._allowedTypes[WebInspector.TypesFilter.ALL_TYPES] || !!this._allowedTypes[typeName]; |
| + }, |
| + |
| + /** |
| + * @return {Array.<string>} |
| + */ |
| + filteredOutTypes: function() |
| + { |
| + if (this._allowedTypes[WebInspector.TypesFilter.ALL_TYPES]) |
| + return []; |
| + var result = []; |
| + for (var i = 0; i < this._types.length; ++i) { |
| + var name = this._types[i].name; |
| + 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.TypesFilter.ALL_TYPES] = true; |
| + } else { |
| + for (var i = 0; i < this._types.length; ++i) { |
| + var name = this._types[i].name; |
| + 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.Filter.Events.FilterChanged, null); |
| + }, |
| + |
| + /** |
| + * @return {Array.<string>} |
| + */ |
| + acceptedTypes: function() |
| + { |
| + if (this._allowedTypes[WebInspector.TypesFilter.ALL_TYPES]) |
| + return [WebInspector.TypesFilter.ALL_TYPES]; |
| + var result = []; |
| + for (var i = 0; i < this._types.length; ++i) { |
| + var name = this._types[i].name; |
| + 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.Filter.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; |
| + }, |
| + |
| + /** |
| + * @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.TypesFilter.ALL_TYPES) |
| + this._typeFilterElements[WebInspector.TypesFilter.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.Filter.Events.FilterChanged, null); |
| + }, |
| + |
| + __proto__: WebInspector.Object.prototype |
| +} |
| + |
| +/** |
| + * @constructor |
| + * @implements {WebInspector.Filter} |
| + * @extends {WebInspector.Object} |
| + * @param {Array.<{value: *, label: string, title: string}>} options |
| + */ |
| +WebInspector.ComboBoxFilter = function(options) |
| +{ |
| + this._filterElement = document.createElement("div"); |
| + this._filterElement.className = "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.ComboBoxFilter.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.Filter.Events.FilterChanged, null); |
| + }, |
| + |
| + __proto__: WebInspector.Object.prototype |
| +} |
| + |
| +/** |
| + * @constructor |
| + * @implements {WebInspector.Filter} |
| + * @extends {WebInspector.Object} |
| + * @param {boolean} activeWhenChecked |
| + */ |
| +WebInspector.CheckboxFilter = function(className, title, activeWhenChecked) |
| +{ |
| + this._className = className; |
| + this._filterElement = document.createElement("div"); |
| + this._filterElement.classList.add("checkbox-filter", "checkbox-filter-" + this._className); |
| + this._activeWhenChecked = activeWhenChecked; |
| + this._createCheckbox(title); |
| +} |
| + |
| +WebInspector.CheckboxFilter.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.Filter.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 |
| +} |
| + |
| +/** |
| + * @constructor |
| + * @extends {WebInspector.Object} |
| + */ |
| +WebInspector.FilterController = function() |
|
pfeldman
2013/10/22 15:15:01
Make it the first class in file.
|
| +{ |
| + 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.FilterController.Events = { |
| + FiltersToggled: "FiltersToggled" |
| +} |
| + |
| +WebInspector.FilterController.FilterControllerState = { |
| + Inactive : "inactive", |
| + Active : "active", |
| + Shown : "shown" |
| +}; |
| + |
| +WebInspector.FilterController.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.Filter} filter |
| + */ |
| + addFilter: function(filter) |
| + { |
| + this._filters.push(filter); |
| + this._element.appendChild(filter.element()); |
| + filter.addEventListener(WebInspector.Filter.Events.FilterChanged, this._filterChanged, this); |
| + this._updateFilterButton(); |
| + }, |
| + |
| + /** |
| + * @param {WebInspector.Event} event |
| + */ |
| + _filterChanged: function(event) |
| + { |
| + this._updateFilterButton(); |
| + }, |
| + |
| + /** |
| + * @return {string} |
| + */ |
| + _filterControllerState: function() |
| + { |
| + if (this._filtersShown) |
| + return WebInspector.FilterController.FilterControllerState.Shown; |
| + var isActive = false; |
| + for (var i = 0; i < this._filters.length; ++i) { |
| + if (this._filters[i].isActive()) |
| + return WebInspector.FilterController.FilterControllerState.Active; |
| + } |
| + return WebInspector.FilterController.FilterControllerState.Inactive; |
| + }, |
| + |
| + _updateFilterButton: function() |
| + { |
| + this._filterButton.state = this._filterControllerState(); |
| + }, |
| + |
| + /** |
| + * @param {Event} event |
| + */ |
| + _handleFilterButtonClick: function(event) |
| + { |
| + this._filtersShown = !this._filtersShown; |
| + this._updateFilterButton(); |
| + this.dispatchEventToListeners(WebInspector.FilterController.Events.FiltersToggled, this._filtersShown); |
| + }, |
| + |
| + __proto__: WebInspector.Object.prototype |
| +} |