• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
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
13 * distribution.
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.
17 *
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.
29 */
30
31/**
32 * @constructor
33 * @extends {WebInspector.Object}
34 */
35WebInspector.FilterBar = function()
36{
37    this._filtersShown = false;
38    this._element = document.createElement("div");
39    this._element.className = "hbox";
40
41    this._filterButton = new WebInspector.StatusBarButton(WebInspector.UIString("Filter"), "filters-toggle", 3);
42    this._filterButton.element.addEventListener("mousedown", this._handleFilterButtonClick.bind(this), false);
43
44    this._filters = [];
45}
46
47WebInspector.FilterBar.Events = {
48    FiltersToggled: "FiltersToggled"
49}
50
51WebInspector.FilterBar.FilterBarState = {
52    Inactive : "inactive",
53    Active : "active",
54    Shown : "shown"
55};
56
57WebInspector.FilterBar.prototype = {
58    /**
59     * @return {!WebInspector.StatusBarButton}
60     */
61    filterButton: function()
62    {
63        return this._filterButton;
64    },
65
66    /**
67     * @return {!Element}
68     */
69    filtersElement: function()
70    {
71        return this._element;
72    },
73
74    /**
75     * @return {boolean}
76     */
77    filtersToggled: function()
78    {
79        return this._filtersShown;
80    },
81
82    /**
83     * @param {!WebInspector.FilterUI} filter
84     */
85    addFilter: function(filter)
86    {
87        this._filters.push(filter);
88        this._element.appendChild(filter.element());
89        filter.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged, this);
90        this._updateFilterButton();
91    },
92
93    /**
94     * @param {!WebInspector.Event} event
95     */
96    _filterChanged: function(event)
97    {
98        this._updateFilterButton();
99    },
100
101    /**
102     * @return {string}
103     */
104    _filterBarState: function()
105    {
106        if (this._filtersShown)
107            return WebInspector.FilterBar.FilterBarState.Shown;
108        var isActive = false;
109        for (var i = 0; i < this._filters.length; ++i) {
110            if (this._filters[i].isActive())
111                return WebInspector.FilterBar.FilterBarState.Active;
112        }
113        return WebInspector.FilterBar.FilterBarState.Inactive;
114    },
115
116    _updateFilterButton: function()
117    {
118        this._filterButton.state = this._filterBarState();
119    },
120
121    /**
122     * @param {?Event} event
123     */
124    _handleFilterButtonClick: function(event)
125    {
126        this._filtersShown = !this._filtersShown;
127        this._updateFilterButton();
128        this.dispatchEventToListeners(WebInspector.FilterBar.Events.FiltersToggled, this._filtersShown);
129    },
130
131    clear: function()
132    {
133        this._element.removeChildren();
134        this._filters = [];
135        this._updateFilterButton();
136    },
137
138    __proto__: WebInspector.Object.prototype
139}
140
141/**
142 * @interface
143 * @extends {WebInspector.EventTarget}
144 */
145WebInspector.FilterUI = function()
146{
147}
148
149WebInspector.FilterUI.Events = {
150    FilterChanged: "FilterChanged"
151}
152
153WebInspector.FilterUI.prototype = {
154    /**
155     * @return {boolean}
156     */
157    isActive: function() { },
158
159    /**
160     * @return {!Element}
161     */
162    element: function() { }
163}
164
165/**
166 * @constructor
167 * @implements {WebInspector.FilterUI}
168 * @extends {WebInspector.Object}
169 * @param {boolean=} supportRegex
170 */
171WebInspector.TextFilterUI = function(supportRegex)
172{
173    this._supportRegex = !!supportRegex;
174    this._regex = null;
175
176    this._filterElement = document.createElement("div");
177    this._filterElement.className = "filter-text-filter";
178
179    this._filterInputElement = this._filterElement.createChild("input", "search-replace toolbar-replace-control");
180    this._filterInputElement.placeholder = WebInspector.UIString("Filter");
181    this._filterInputElement.id = "filter-input-field";
182    this._filterInputElement.addEventListener("mousedown", this._onFilterFieldManualFocus.bind(this), false); // when the search field is manually selected
183    this._filterInputElement.addEventListener("input", this._onInput.bind(this), false);
184    this._filterInputElement.addEventListener("change", this._onInput.bind(this), false);
185
186    if (this._supportRegex) {
187        this._filterElement.classList.add("supports-regex");
188        this._regexCheckBox = this._filterElement.createChild("input");
189        this._regexCheckBox.type = "checkbox";
190        this._regexCheckBox.id = "text-filter-regex";
191        this._regexCheckBox.addEventListener("change", this._onInput.bind(this), false);
192
193        this._regexLabel = this._filterElement.createChild("label");
194        this._regexLabel.htmlFor = "text-filter-regex";
195        this._regexLabel.textContent = WebInspector.UIString("Regex");
196    }
197}
198
199WebInspector.TextFilterUI.prototype = {
200    /**
201     * @return {boolean}
202     */
203    isActive: function()
204    {
205        return !!this._filterInputElement.value;
206    },
207
208    /**
209     * @return {!Element}
210     */
211    element: function()
212    {
213        return this._filterElement;
214    },
215
216    /**
217     * @return {string}
218     */
219    value: function()
220    {
221        return this._filterInputElement.value;
222    },
223
224    /**
225     * @param {string} value
226     */
227    setValue: function(value)
228    {
229        this._filterInputElement.value = value;
230        this._valueChanged();
231    },
232
233    /**
234     * @return {?RegExp}
235     */
236    regex: function()
237    {
238        return this._regex;
239    },
240
241    /**
242     * @param {?Event} event
243     */
244    _onFilterFieldManualFocus: function(event)
245    {
246        WebInspector.setCurrentFocusElement(event.target);
247    },
248
249    /**
250     * @param {!WebInspector.Event} event
251     */
252    _onInput: function(event)
253    {
254        this._valueChanged();
255    },
256
257    _valueChanged: function() {
258        var filterQuery = this.value();
259
260        this._regex = null;
261        this._filterInputElement.classList.remove("filter-text-invalid");
262        if (filterQuery) {
263            if (this._supportRegex && this._regexCheckBox.checked) {
264                try {
265                    this._regex = new RegExp(filterQuery, "i");
266                } catch (e) {
267                    this._filterInputElement.classList.add("filter-text-invalid");
268                }
269            } else {
270                this._regex = createPlainTextSearchRegex(filterQuery, "i");
271            }
272        }
273
274        this.dispatchEventToListeners(WebInspector.FilterUI.Events.FilterChanged, null);
275    },
276
277    __proto__: WebInspector.Object.prototype
278}
279
280/**
281 * @constructor
282 * @implements {WebInspector.FilterUI}
283 * @extends {WebInspector.Object}
284 */
285WebInspector.NamedBitSetFilterUI = function()
286{
287    this._filtersElement = document.createElement("div");
288    this._filtersElement.className = "filter-bitset-filter status-bar-item";
289    this._filtersElement.title = WebInspector.UIString("Use %s Click to select multiple types.", WebInspector.KeyboardShortcut.shortcutToString("", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta));
290
291    this._allowedTypes = {};
292    this._typeFilterElements = {};
293    this.addBit(WebInspector.NamedBitSetFilterUI.ALL_TYPES, WebInspector.UIString("All"));
294    this._filtersElement.createChild("div", "filter-bitset-filter-divider");
295    this._toggleTypeFilter(WebInspector.NamedBitSetFilterUI.ALL_TYPES, false);
296}
297
298WebInspector.NamedBitSetFilterUI.ALL_TYPES = "all";
299
300WebInspector.NamedBitSetFilterUI.prototype = {
301    /**
302     * @return {boolean}
303     */
304    isActive: function()
305    {
306        return !this._allowedTypes[WebInspector.NamedBitSetFilterUI.ALL_TYPES];
307    },
308
309    /**
310     * @param {!WebInspector.Setting} setting
311     */
312    bindSetting: function(setting)
313    {
314        console.assert(!this._setting);
315        this._setting = setting;
316        setting.addChangeListener(this._settingChanged.bind(this));
317        this._settingChanged();
318    },
319
320    /**
321     * @return {!Element}
322     */
323    element: function()
324    {
325        return this._filtersElement;
326    },
327
328    /**
329     * @param {string} typeName
330     * @return {boolean}
331     */
332    accept: function(typeName)
333    {
334        return !!this._allowedTypes[WebInspector.NamedBitSetFilterUI.ALL_TYPES] || !!this._allowedTypes[typeName];
335    },
336
337    _settingChanged: function()
338    {
339        var allowedTypes = this._setting.get();
340        this._allowedTypes = {};
341        for (var typeName in this._typeFilterElements) {
342            if (allowedTypes[typeName])
343                this._allowedTypes[typeName] = true;
344        }
345        this._update();
346    },
347
348    _update: function()
349    {
350        if ((Object.keys(this._allowedTypes).length === 0) || this._allowedTypes[WebInspector.NamedBitSetFilterUI.ALL_TYPES]) {
351            this._allowedTypes = {};
352            this._allowedTypes[WebInspector.NamedBitSetFilterUI.ALL_TYPES] = true;
353        }
354        for (var typeName in this._typeFilterElements)
355            this._typeFilterElements[typeName].enableStyleClass("selected", this._allowedTypes[typeName]);
356        this.dispatchEventToListeners(WebInspector.FilterUI.Events.FilterChanged, null);
357    },
358
359    /**
360     * @param {string} name
361     * @param {string} label
362     */
363    addBit: function(name, label)
364    {
365        var typeFilterElement = this._filtersElement.createChild("li", name);
366        typeFilterElement.typeName = name;
367        typeFilterElement.createTextChild(label);
368        typeFilterElement.addEventListener("click", this._onTypeFilterClicked.bind(this), false);
369        this._typeFilterElements[name] = typeFilterElement;
370    },
371
372    /**
373     * @param {!Event} e
374     */
375    _onTypeFilterClicked: function(e)
376    {
377        var toggle;
378        if (WebInspector.isMac())
379            toggle = e.metaKey && !e.ctrlKey && !e.altKey && !e.shiftKey;
380        else
381            toggle = e.ctrlKey && !e.metaKey && !e.altKey && !e.shiftKey;
382        this._toggleTypeFilter(e.target.typeName, toggle);
383    },
384
385    /**
386     * @param {string} typeName
387     * @param {boolean} allowMultiSelect
388     */
389    _toggleTypeFilter: function(typeName, allowMultiSelect)
390    {
391        if (allowMultiSelect && typeName !== WebInspector.NamedBitSetFilterUI.ALL_TYPES)
392            this._allowedTypes[WebInspector.NamedBitSetFilterUI.ALL_TYPES] = false;
393        else
394            this._allowedTypes = {};
395
396        this._allowedTypes[typeName] = !this._allowedTypes[typeName];
397
398        if (this._setting)
399            this._setting.set(this._allowedTypes);
400        else
401            this._update();
402    },
403
404    __proto__: WebInspector.Object.prototype
405}
406
407/**
408 * @constructor
409 * @implements {WebInspector.FilterUI}
410 * @extends {WebInspector.Object}
411 * @param {!Array.<{value: *, label: string, title: string}>} options
412 */
413WebInspector.ComboBoxFilterUI = function(options)
414{
415    this._filterElement = document.createElement("div");
416    this._filterElement.className = "filter-combobox-filter";
417
418    this._options = options;
419    this._filterComboBox = new WebInspector.StatusBarComboBox(this._filterChanged.bind(this));
420    for (var i = 0; i < options.length; ++i) {
421        var filterOption = options[i];
422        var option = document.createElement("option");
423        option.text = filterOption.label;
424        option.title = filterOption.title;
425        this._filterComboBox.addOption(option);
426        this._filterComboBox.element.title = this._filterComboBox.selectedOption().title;
427    }
428    this._filterElement.appendChild(this._filterComboBox.element);
429}
430
431WebInspector.ComboBoxFilterUI.prototype = {
432    /**
433     * @return {boolean}
434     */
435    isActive: function()
436    {
437        return this._filterComboBox.selectedIndex() !== 0;
438    },
439
440    /**
441     * @return {!Element}
442     */
443    element: function()
444    {
445        return this._filterElement;
446    },
447
448    /**
449     * @param {string} typeName
450     * @return {*}
451     */
452    value: function(typeName)
453    {
454        var option = this._options[this._filterComboBox.selectedIndex()];
455        return option.value;
456    },
457
458    /**
459     * @param {?Event} event
460     */
461    _filterChanged: function(event)
462    {
463        var option = this._options[this._filterComboBox.selectedIndex()];
464        this._filterComboBox.element.title = option.title;
465        this.dispatchEventToListeners(WebInspector.FilterUI.Events.FilterChanged, null);
466    },
467
468    __proto__: WebInspector.Object.prototype
469}
470
471/**
472 * @constructor
473 * @implements {WebInspector.FilterUI}
474 * @extends {WebInspector.Object}
475 * @param {string} className
476 * @param {string} title
477 * @param {boolean=} activeWhenChecked
478 * @param {!WebInspector.Setting=} setting
479 */
480WebInspector.CheckboxFilterUI = function(className, title, activeWhenChecked, setting)
481{
482    this._filterElement = document.createElement("div");
483    this._filterElement.classList.add("filter-checkbox-filter", "filter-checkbox-filter-" + className);
484    this._activeWhenChecked = !!activeWhenChecked;
485    this._createCheckbox(title);
486
487    if (setting) {
488        this._setting = setting;
489        setting.addChangeListener(this._settingChanged.bind(this));
490        this._settingChanged();
491    } else {
492        this._checked = !this._activeWhenChecked;
493        this._update();
494    }
495}
496
497WebInspector.CheckboxFilterUI.prototype = {
498    /**
499     * @return {boolean}
500     */
501    isActive: function()
502    {
503        return this._activeWhenChecked === this._checked;
504    },
505
506    /**
507     * @return {!Element}
508     */
509    element: function()
510    {
511        return this._filterElement;
512    },
513
514    /**
515     * @return {boolean}
516     */
517    checked: function()
518    {
519        return this._checked;
520    },
521
522    _update: function()
523    {
524        this._checkElement.enableStyleClass("checkbox-filter-checkbox-checked", this._checked);
525        this.dispatchEventToListeners(WebInspector.FilterUI.Events.FilterChanged, null);
526    },
527
528    _settingChanged: function()
529    {
530        this._checked = this._setting.get();
531        this._update();
532    },
533
534    /**
535     * @param {?Event} event
536     */
537    _onClick: function(event)
538    {
539        this._checked = !this._checked;
540        if (this._setting)
541            this._setting.set(this._checked);
542        else
543            this._update();
544    },
545
546    /**
547     * @param {string} title
548     */
549    _createCheckbox: function(title)
550    {
551        var label = this._filterElement.createChild("label");
552        var checkBorder = label.createChild("div", "checkbox-filter-checkbox");
553        this._checkElement = checkBorder.createChild("div", "checkbox-filter-checkbox-check");
554        this._filterElement.addEventListener("click", this._onClick.bind(this), false);
555        var typeElement = label.createChild("span", "type");
556        typeElement.textContent = title;
557    },
558
559    __proto__: WebInspector.Object.prototype
560}
561