2 * Copyright (C) 2013 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 * @extends {WebInspector.Object}
35 WebInspector.FilterBar = function()
37 this._filtersShown = false;
38 this._element = document.createElement("div");
39 this._element.className = "hbox";
41 this._filterButton = new WebInspector.StatusBarButton(WebInspector.UIString("Filter"), "filters-toggle", 3);
42 this._filterButton.element.addEventListener("click", this._handleFilterButtonClick.bind(this), false);
47 WebInspector.FilterBar.Events = {
48 FiltersToggled: "FiltersToggled"
51 WebInspector.FilterBar.FilterBarState = {
52 Inactive : "inactive",
57 WebInspector.FilterBar.prototype = {
59 * @param {string} name
61 setName: function(name)
63 this._stateSetting = WebInspector.settings.createSetting("filterBar-" + name + "-toggled", false);
64 this._setState(this._stateSetting.get());
68 * @return {!WebInspector.StatusBarButton}
70 filterButton: function()
72 return this._filterButton;
78 filtersElement: function()
86 filtersToggled: function()
88 return this._filtersShown;
92 * @param {!WebInspector.FilterUI} filter
94 addFilter: function(filter)
96 this._filters.push(filter);
97 this._element.appendChild(filter.element());
98 filter.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged, this);
99 this._updateFilterButton();
103 * @param {!WebInspector.Event} event
105 _filterChanged: function(event)
107 this._updateFilterButton();
113 _filterBarState: function()
115 if (this._filtersShown)
116 return WebInspector.FilterBar.FilterBarState.Shown;
117 var isActive = false;
118 for (var i = 0; i < this._filters.length; ++i) {
119 if (this._filters[i].isActive())
120 return WebInspector.FilterBar.FilterBarState.Active;
122 return WebInspector.FilterBar.FilterBarState.Inactive;
125 _updateFilterButton: function()
127 this._filterButton.state = this._filterBarState();
131 * @param {?Event} event
133 _handleFilterButtonClick: function(event)
135 this._setState(!this._filtersShown);
139 * @param {boolean} filtersShown
141 _setState: function(filtersShown)
143 if (this._filtersShown === filtersShown)
146 this._filtersShown = filtersShown;
147 if (this._stateSetting)
148 this._stateSetting.set(filtersShown);
150 this._updateFilterButton();
151 this.dispatchEventToListeners(WebInspector.FilterBar.Events.FiltersToggled, this._filtersShown);
152 if (this._filtersShown) {
153 for (var i = 0; i < this._filters.length; ++i) {
154 if (this._filters[i] instanceof WebInspector.TextFilterUI) {
155 var textFilterUI = /** @type {!WebInspector.TextFilterUI} */ (this._filters[i]);
156 textFilterUI.focus();
164 this._element.removeChildren();
166 this._updateFilterButton();
169 __proto__: WebInspector.Object.prototype
174 * @extends {WebInspector.EventTarget}
176 WebInspector.FilterUI = function()
180 WebInspector.FilterUI.Events = {
181 FilterChanged: "FilterChanged"
184 WebInspector.FilterUI.prototype = {
188 isActive: function() { },
193 element: function() { }
198 * @extends {WebInspector.Object}
199 * @implements {WebInspector.FilterUI}
200 * @implements {WebInspector.SuggestBoxDelegate}
201 * @param {boolean=} supportRegex
203 WebInspector.TextFilterUI = function(supportRegex)
205 this._supportRegex = !!supportRegex;
208 this._filterElement = document.createElement("div");
209 this._filterElement.className = "filter-text-filter";
211 this._filterInputElement = this._filterElement.createChild("input", "search-replace toolbar-replace-control");
212 this._filterInputElement.placeholder = WebInspector.UIString("Filter");
213 this._filterInputElement.id = "filter-input-field";
214 this._filterInputElement.addEventListener("mousedown", this._onFilterFieldManualFocus.bind(this), false); // when the search field is manually selected
215 this._filterInputElement.addEventListener("input", this._onInput.bind(this), false);
216 this._filterInputElement.addEventListener("change", this._onChange.bind(this), false);
217 this._filterInputElement.addEventListener("keydown", this._onInputKeyDown.bind(this), true);
218 this._filterInputElement.addEventListener("blur", this._onBlur.bind(this), true);
220 /** @type {?WebInspector.TextFilterUI.SuggestionBuilder} */
221 this._suggestionBuilder = null;
223 this._suggestBox = new WebInspector.SuggestBox(this);
225 if (this._supportRegex) {
226 this._filterElement.classList.add("supports-regex");
227 this._regexCheckBox = this._filterElement.createChild("input");
228 this._regexCheckBox.type = "checkbox";
229 this._regexCheckBox.id = "text-filter-regex";
230 this._regexCheckBox.addEventListener("change", this._onInput.bind(this), false);
232 this._regexLabel = this._filterElement.createChild("label");
233 this._regexLabel.htmlFor = "text-filter-regex";
234 this._regexLabel.textContent = WebInspector.UIString("Regex");
238 WebInspector.TextFilterUI.prototype = {
244 return !!this._filterInputElement.value;
252 return this._filterElement;
260 return this._filterInputElement.value;
264 * @param {string} value
266 setValue: function(value)
268 this._filterInputElement.value = value;
269 this._valueChanged(false);
281 * @param {?Event} event
283 _onFilterFieldManualFocus: function(event)
285 WebInspector.setCurrentFocusElement(event.target);
289 * @param {?Event} event
291 _onBlur: function(event)
293 this._cancelSuggestion();
296 _cancelSuggestion: function()
298 if (this._suggestionBuilder && this._suggestBox.visible) {
299 this._suggestionBuilder.unapplySuggestion(this._filterInputElement);
300 this._suggestBox.hide();
305 * @param {!WebInspector.Event} event
307 _onInput: function(event)
309 this._valueChanged(true);
313 * @param {!WebInspector.Event} event
315 _onChange: function(event)
317 this._valueChanged(false);
322 this._filterInputElement.focus();
326 * @param {?WebInspector.TextFilterUI.SuggestionBuilder} suggestionBuilder
328 setSuggestionBuilder: function(suggestionBuilder)
330 this._cancelSuggestion();
331 this._suggestionBuilder = suggestionBuilder;
334 _updateSuggestions: function()
336 if (!this._suggestionBuilder)
338 var suggestions = this._suggestionBuilder.buildSuggestions(this._filterInputElement);
339 if (suggestions && suggestions.length) {
340 if (this._suppressSuggestion)
341 delete this._suppressSuggestion;
343 this._suggestionBuilder.applySuggestion(this._filterInputElement, suggestions[0], true);
344 var anchorBox = this._filterInputElement.boxInWindow().relativeTo(new AnchorBox(-3, 0));
345 this._suggestBox.updateSuggestions(anchorBox, suggestions, 0, true, "");
347 this._suggestBox.hide();
352 * @param {boolean} showSuggestions
354 _valueChanged: function(showSuggestions)
357 this._updateSuggestions();
359 this._suggestBox.hide();
361 var filterQuery = this.value();
364 this._filterInputElement.classList.remove("filter-text-invalid");
366 if (this._supportRegex && this._regexCheckBox.checked) {
368 this._regex = new RegExp(filterQuery, "i");
370 this._filterInputElement.classList.add("filter-text-invalid");
373 this._regex = createPlainTextSearchRegex(filterQuery, "i");
377 this._dispatchFilterChanged();
380 _dispatchFilterChanged: function()
382 this.dispatchEventToListeners(WebInspector.FilterUI.Events.FilterChanged, null);
386 * @param {!KeyboardEvent} event
389 _onInputKeyDown: function(event)
392 if (event.keyIdentifier === "U+0008") { // Backspace
393 this._suppressSuggestion = true;
394 } else if (this._suggestBox.visible()) {
395 if (event.keyIdentifier === "U+001B") { // Esc
396 this._cancelSuggestion();
398 } else if (event.keyIdentifier === "U+0009") { // Tab
399 this._suggestBox.acceptSuggestion();
400 this._valueChanged(true);
403 handled = this._suggestBox.keyPressed(event);
413 * @param {string} suggestion
414 * @param {boolean=} isIntermediateSuggestion
416 applySuggestion: function(suggestion, isIntermediateSuggestion)
418 if (!this._suggestionBuilder)
420 this._suggestionBuilder.applySuggestion(this._filterInputElement, suggestion, !!isIntermediateSuggestion);
421 if (isIntermediateSuggestion)
422 this._dispatchFilterChanged();
426 acceptSuggestion: function()
428 this._filterInputElement.scrollLeft = this._filterInputElement.scrollWidth;
429 this._valueChanged(true);
432 __proto__: WebInspector.Object.prototype
438 WebInspector.TextFilterUI.SuggestionBuilder = function()
442 WebInspector.TextFilterUI.SuggestionBuilder.prototype = {
444 * @param {!HTMLInputElement} input
445 * @return {?Array.<string>}
447 buildSuggestions: function(input) { },
450 * @param {!HTMLInputElement} input
451 * @param {string} suggestion
452 * @param {boolean} isIntermediate
454 applySuggestion: function(input, suggestion, isIntermediate) { },
457 * @param {!HTMLInputElement} input
459 unapplySuggestion: function(input) { }
464 * @extends {WebInspector.Object}
465 * @implements {WebInspector.FilterUI}
466 * @param {!Array.<!WebInspector.NamedBitSetFilterUI.Item>} items
467 * @param {!WebInspector.Setting=} setting
469 WebInspector.NamedBitSetFilterUI = function(items, setting)
471 this._filtersElement = document.createElement("div");
472 this._filtersElement.className = "filter-bitset-filter status-bar-item";
473 this._filtersElement.title = WebInspector.UIString("Use %s Click to select multiple types.", WebInspector.KeyboardShortcut.shortcutToString("", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta));
475 this._allowedTypes = {};
476 this._typeFilterElements = {};
477 this._addBit(WebInspector.NamedBitSetFilterUI.ALL_TYPES, WebInspector.UIString("All"));
478 this._filtersElement.createChild("div", "filter-bitset-filter-divider");
480 for (var i = 0; i < items.length; ++i)
481 this._addBit(items[i].name, items[i].label);
484 this._setting = setting;
485 setting.addChangeListener(this._settingChanged.bind(this));
486 this._settingChanged();
488 this._toggleTypeFilter(WebInspector.NamedBitSetFilterUI.ALL_TYPES, false);
492 /** @typedef {{name: string, label: string}} */
493 WebInspector.NamedBitSetFilterUI.Item;
495 WebInspector.NamedBitSetFilterUI.ALL_TYPES = "all";
497 WebInspector.NamedBitSetFilterUI.prototype = {
503 return !this._allowedTypes[WebInspector.NamedBitSetFilterUI.ALL_TYPES];
511 return this._filtersElement;
515 * @param {string} typeName
518 accept: function(typeName)
520 return !!this._allowedTypes[WebInspector.NamedBitSetFilterUI.ALL_TYPES] || !!this._allowedTypes[typeName];
523 _settingChanged: function()
525 var allowedTypes = this._setting.get();
526 this._allowedTypes = {};
527 for (var typeName in this._typeFilterElements) {
528 if (allowedTypes[typeName])
529 this._allowedTypes[typeName] = true;
536 if ((Object.keys(this._allowedTypes).length === 0) || this._allowedTypes[WebInspector.NamedBitSetFilterUI.ALL_TYPES]) {
537 this._allowedTypes = {};
538 this._allowedTypes[WebInspector.NamedBitSetFilterUI.ALL_TYPES] = true;
540 for (var typeName in this._typeFilterElements)
541 this._typeFilterElements[typeName].classList.toggle("selected", this._allowedTypes[typeName]);
542 this.dispatchEventToListeners(WebInspector.FilterUI.Events.FilterChanged, null);
546 * @param {string} name
547 * @param {string} label
549 _addBit: function(name, label)
551 var typeFilterElement = this._filtersElement.createChild("li", name);
552 typeFilterElement.typeName = name;
553 typeFilterElement.createTextChild(label);
554 typeFilterElement.addEventListener("click", this._onTypeFilterClicked.bind(this), false);
555 this._typeFilterElements[name] = typeFilterElement;
561 _onTypeFilterClicked: function(e)
564 if (WebInspector.isMac())
565 toggle = e.metaKey && !e.ctrlKey && !e.altKey && !e.shiftKey;
567 toggle = e.ctrlKey && !e.metaKey && !e.altKey && !e.shiftKey;
568 this._toggleTypeFilter(e.target.typeName, toggle);
572 * @param {string} typeName
573 * @param {boolean} allowMultiSelect
575 _toggleTypeFilter: function(typeName, allowMultiSelect)
577 if (allowMultiSelect && typeName !== WebInspector.NamedBitSetFilterUI.ALL_TYPES)
578 this._allowedTypes[WebInspector.NamedBitSetFilterUI.ALL_TYPES] = false;
580 this._allowedTypes = {};
582 this._allowedTypes[typeName] = !this._allowedTypes[typeName];
585 this._setting.set(this._allowedTypes);
590 __proto__: WebInspector.Object.prototype
595 * @implements {WebInspector.FilterUI}
596 * @extends {WebInspector.Object}
597 * @param {!Array.<!{value: *, label: string, title: string}>} options
599 WebInspector.ComboBoxFilterUI = function(options)
601 this._filterElement = document.createElement("div");
602 this._filterElement.className = "filter-combobox-filter";
604 this._options = options;
605 this._filterComboBox = new WebInspector.StatusBarComboBox(this._filterChanged.bind(this));
606 for (var i = 0; i < options.length; ++i) {
607 var filterOption = options[i];
608 var option = document.createElement("option");
609 option.text = filterOption.label;
610 option.title = filterOption.title;
611 this._filterComboBox.addOption(option);
612 this._filterComboBox.element.title = this._filterComboBox.selectedOption().title;
614 this._filterElement.appendChild(this._filterComboBox.element);
617 WebInspector.ComboBoxFilterUI.prototype = {
623 return this._filterComboBox.selectedIndex() !== 0;
631 return this._filterElement;
635 * @param {string} typeName
638 value: function(typeName)
640 var option = this._options[this._filterComboBox.selectedIndex()];
645 * @param {number} index
647 setSelectedIndex: function(index)
649 this._filterComboBox.setSelectedIndex(index);
655 selectedIndex: function(index)
657 return this._filterComboBox.selectedIndex();
661 * @param {?Event} event
663 _filterChanged: function(event)
665 var option = this._options[this._filterComboBox.selectedIndex()];
666 this._filterComboBox.element.title = option.title;
667 this.dispatchEventToListeners(WebInspector.FilterUI.Events.FilterChanged, null);
670 __proto__: WebInspector.Object.prototype
675 * @implements {WebInspector.FilterUI}
676 * @extends {WebInspector.Object}
677 * @param {string} className
678 * @param {string} title
679 * @param {boolean=} activeWhenChecked
680 * @param {!WebInspector.Setting=} setting
682 WebInspector.CheckboxFilterUI = function(className, title, activeWhenChecked, setting)
684 this._filterElement = document.createElement("div");
685 this._filterElement.classList.add("filter-checkbox-filter", "filter-checkbox-filter-" + className);
686 this._activeWhenChecked = !!activeWhenChecked;
687 this._createCheckbox(title);
690 this._setting = setting;
691 setting.addChangeListener(this._settingChanged.bind(this));
692 this._settingChanged();
694 this._checked = !this._activeWhenChecked;
699 WebInspector.CheckboxFilterUI.prototype = {
705 return this._activeWhenChecked === this._checked;
713 return this._filterElement;
721 return this._checked;
725 * @param {boolean} state
727 setState: function(state)
729 this._checked = state;
735 this._checkElement.classList.toggle("checkbox-filter-checkbox-checked", this._checked);
736 this.dispatchEventToListeners(WebInspector.FilterUI.Events.FilterChanged, null);
739 _settingChanged: function()
741 this._checked = this._setting.get();
746 * @param {?Event} event
748 _onClick: function(event)
750 this._checked = !this._checked;
752 this._setting.set(this._checked);
758 * @param {string} title
760 _createCheckbox: function(title)
762 var label = this._filterElement.createChild("label");
763 var checkBorder = label.createChild("div", "checkbox-filter-checkbox");
764 this._checkElement = checkBorder.createChild("div", "checkbox-filter-checkbox-check");
765 this._filterElement.addEventListener("click", this._onClick.bind(this), false);
766 var typeElement = label.createChild("span", "type");
767 typeElement.textContent = title;
770 __proto__: WebInspector.Object.prototype