diff --git a/devtools/client/themes/toolbars.css b/devtools/client/themes/toolbars.css index bc221521b65d..5d8a7b6fb96e 100644 --- a/devtools/client/themes/toolbars.css +++ b/devtools/client/themes/toolbars.css @@ -248,6 +248,8 @@ .devtools-button[checked]:empty::before, .devtools-button[open]:empty::before, +.devtools-button.active::before, +.theme-light .devtools-button.active::before, .devtools-toolbarbutton:not([label])[checked=true] > image, .devtools-toolbarbutton:not([label])[open=true] > image { filter: var(--checked-icon-filter); @@ -308,6 +310,10 @@ background-image: var(--clear-icon-url); } +.devtools-button.devtools-filter-icon::before { + background-image: var(--filter-image); +} + .devtools-toolbarbutton.devtools-clear-icon { list-style-image: var(--clear-icon-url); } @@ -649,3 +655,28 @@ transform: rotate(360deg); } } + +/* Filter buttons */ +.menu-filter-button { + -moz-appearance: none; + background: rgba(128,128,128,0.1); + border: none; + border-radius: 2px; + min-width: 0; + padding: 0 5px; + margin: 2px; + color: var(--theme-body-color); +} + +.menu-filter-button:hover { + background: rgba(128,128,128,0.2); +} + +.menu-filter-button:hover:active { + background-color: var(--theme-selection-background-semitransparent); +} + +.menu-filter-button:not(:active).active { + background-color: var(--theme-selection-background); + color: var(--theme-selection-color); +} diff --git a/devtools/client/themes/webconsole.css b/devtools/client/themes/webconsole.css index 1c5ff544405c..0f4d1ec8c1d0 100644 --- a/devtools/client/themes/webconsole.css +++ b/devtools/client/themes/webconsole.css @@ -151,6 +151,7 @@ a { direction: ltr; overflow: auto; -moz-user-select: text; + position: relative; } /* The width on #output-container is set to a hardcoded px in webconsole.js @@ -624,3 +625,41 @@ a.learn-more-link.webconsole-learn-more-link { border-left: 1px solid #D7D7D7; border-right: 1px solid #D7D7D7; } + + +/* NEW CONSOLE STYLES */ + +#output-wrapper > div { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; +} + +#output-container { + height: 100%; +} + +.webconsole-output-wrapper { + display: flex; + flex-direction: column; + height: 100%; +} + +.webconsole-filteringbar-wrapper { + flex-grow: 0; +} + +.webconsole-output { + flex: 1; + overflow: auto; +} + +.webconsole-filteringbar-primary { + display: flex; +} + +.webconsole-filteringbar-primary .devtools-searchinput { + flex: 1 1 100%; +} diff --git a/devtools/client/webconsole/new-console-output/actions/messages.js b/devtools/client/webconsole/new-console-output/actions/messages.js index 6c80aa05c6da..f1b51d9d3282 100644 --- a/devtools/client/webconsole/new-console-output/actions/messages.js +++ b/devtools/client/webconsole/new-console-output/actions/messages.js @@ -13,6 +13,9 @@ const { const { MESSAGE_ADD, MESSAGES_CLEAR, + SEVERITY_FILTER, + MESSAGES_SEARCH, + FILTERS_CLEAR, } = require("../constants"); function messageAdd(packet) { @@ -29,5 +32,29 @@ function messagesClear() { }; } +function severityFilter(filter, toggled) { + return { + type: SEVERITY_FILTER, + filter, + toggled + }; +} + +function filtersClear() { + return { + type: FILTERS_CLEAR + }; +} + +function messagesSearch(searchText) { + return { + type: MESSAGES_SEARCH, + searchText + }; +} + exports.messageAdd = messageAdd; exports.messagesClear = messagesClear; +exports.severityFilter = severityFilter; +exports.filtersClear = filtersClear; +exports.messagesSearch = messagesSearch; diff --git a/devtools/client/webconsole/new-console-output/actions/moz.build b/devtools/client/webconsole/new-console-output/actions/moz.build index bac6526ee95d..e52d20fc2d6f 100644 --- a/devtools/client/webconsole/new-console-output/actions/moz.build +++ b/devtools/client/webconsole/new-console-output/actions/moz.build @@ -5,4 +5,5 @@ DevToolsModules( 'messages.js', + 'ui.js', ) diff --git a/devtools/client/webconsole/new-console-output/actions/ui.js b/devtools/client/webconsole/new-console-output/actions/ui.js new file mode 100644 index 000000000000..19224430f9a3 --- /dev/null +++ b/devtools/client/webconsole/new-console-output/actions/ui.js @@ -0,0 +1,19 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const { + FILTERBAR_TOGGLE, +} = require("../constants"); + +function filterBarToggle(show) { + return { + type: FILTERBAR_TOGGLE + }; +} + +exports.filterBarToggle = filterBarToggle; diff --git a/devtools/client/webconsole/new-console-output/components/console-output.js b/devtools/client/webconsole/new-console-output/components/console-output.js index 4bd4bc3da6a8..4ae90c33c802 100644 --- a/devtools/client/webconsole/new-console-output/components/console-output.js +++ b/devtools/client/webconsole/new-console-output/components/console-output.js @@ -48,7 +48,7 @@ const ConsoleOutput = createClass({ ); }); return ( - dom.div({}, messageNodes) + dom.div({className: "webconsole-output"}, messageNodes) ); } }); diff --git a/devtools/client/webconsole/new-console-output/components/filter-bar.js b/devtools/client/webconsole/new-console-output/components/filter-bar.js new file mode 100644 index 000000000000..8578733680e3 --- /dev/null +++ b/devtools/client/webconsole/new-console-output/components/filter-bar.js @@ -0,0 +1,133 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const { + createFactory, + createClass, + DOM: dom, + PropTypes +} = require("devtools/client/shared/vendor/react"); +const { connect } = require("devtools/client/shared/vendor/react-redux"); +const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters"); +const { getAllUi } = require("devtools/client/webconsole/new-console-output/selectors/ui"); +const messagesActions = require("devtools/client/webconsole/new-console-output/actions/messages"); +const uiActions = require("devtools/client/webconsole/new-console-output/actions/ui"); +const { store } = require("devtools/client/webconsole/new-console-output/store"); +const { + SEVERITY_FILTER +} = require("../constants"); +const FilterToggleButton = createFactory(require("devtools/client/webconsole/new-console-output/components/filter-toggle-button").FilterToggleButton); + +const FilterBar = createClass({ + + displayName: "FilterBar", + + propTypes: { + filter: PropTypes.object.isRequired, + ui: PropTypes.object.isRequired + }, + + onClearOutputButtonClick: function () { + store.dispatch(messagesActions.messagesClear()); + }, + + onToggleFilterConfigBarButtonClick: function () { + store.dispatch(uiActions.filterBarToggle()); + }, + + onClearFiltersButtonClick: function () { + store.dispatch(messagesActions.filtersClear()); + }, + + onSearchInput: function (e) { + store.dispatch(messagesActions.messagesSearch(e.target.value)); + }, + + render() { + const {filter, ui} = this.props; + let configFilterBarVisible = ui.configFilterBarVisible; + let children = []; + + children.push(dom.div({className: "devtools-toolbar webconsole-filteringbar-primary"}, + dom.button({ + className: "devtools-button devtools-clear-icon", + title: "Clear output", + onClick: this.onClearOutputButtonClick + }), + dom.button({ + className: "devtools-button devtools-filter-icon" + ( + configFilterBarVisible ? " active" : ""), + title: "Toggle filter bar", + onClick: this.onToggleFilterConfigBarButtonClick + }), + dom.input({ + className: "devtools-searchinput", + type: "search", + value: filter.searchText, + placeholder: "Filter output", + onInput: this.onSearchInput + }) + )); + + if (configFilterBarVisible) { + children.push( + dom.div({className: "devtools-toolbar"}, + FilterToggleButton({ + active: filter.error, + label: "Errors", + filterType: SEVERITY_FILTER, + filterKey: "error"}), + FilterToggleButton({ + active: filter.warn, + label: "Warnings", + filterType: SEVERITY_FILTER, + filterKey: "warn"}), + FilterToggleButton({ + active: filter.log, + label: "Logs", + filterType: SEVERITY_FILTER, + filterKey: "log"}), + FilterToggleButton({ + active: filter.info, + label: "Info", + filterType: SEVERITY_FILTER, + filterKey: "info"}) + ) + ); + } + + if (ui.filteredMessageVisible) { + children.push( + dom.div({className: "devtools-toolbar"}, + dom.span({ + className: "clear"}, + "You have filters set that may hide some results. " + + "Learn more about our filtering syntax ", + dom.a({}, "here"), + "."), + dom.button({ + className: "menu-filter-button", + onClick: this.onClearFiltersButtonClick + }, "Remove filters") + ) + ); + } + + return ( + dom.div({className: "webconsole-filteringbar-wrapper"}, + children + ) + ); + } +}); + +function mapStateToProps(state) { + return { + filter: getAllFilters(state), + ui: getAllUi(state) + }; +} + +module.exports = connect(mapStateToProps)(FilterBar); diff --git a/devtools/client/webconsole/new-console-output/components/filter-toggle-button.js b/devtools/client/webconsole/new-console-output/components/filter-toggle-button.js new file mode 100644 index 000000000000..f719ec555c7d --- /dev/null +++ b/devtools/client/webconsole/new-console-output/components/filter-toggle-button.js @@ -0,0 +1,50 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const { + createClass, + DOM: dom, + PropTypes +} = require("devtools/client/shared/vendor/react"); +const { store } = require("devtools/client/webconsole/new-console-output/store"); +const actions = require("devtools/client/webconsole/new-console-output/actions/messages"); +const { + SEVERITY_FILTER +} = require("../constants"); + +const FilterToggleButton = createClass({ + + displayName: "FilterToggleButton", + + propTypes: { + label: PropTypes.string.isRequired, + filterType: PropTypes.string.isRequired, + filterKey: PropTypes.string.isRequired, + active: PropTypes.bool.isRequired, + }, + + onClick: function () { + if (this.props.filterType === SEVERITY_FILTER) { + store.dispatch(actions.severityFilter( + this.props.filterKey, !this.props.active)); + } + }, + + render() { + const {label, active} = this.props; + + let classList = ["menu-filter-button"]; + if (active) { + classList.push("active"); + } + + return dom.button({ + className: classList.join(" "), + onClick: this.onClick + }, label); + } +}); + +exports.FilterToggleButton = FilterToggleButton; diff --git a/devtools/client/webconsole/new-console-output/components/moz.build b/devtools/client/webconsole/new-console-output/components/moz.build index 1f3443fc246d..b8c4a542193f 100644 --- a/devtools/client/webconsole/new-console-output/components/moz.build +++ b/devtools/client/webconsole/new-console-output/components/moz.build @@ -9,6 +9,8 @@ DIRS += [ DevToolsModules( 'console-output.js', + 'filter-bar.js', + 'filter-toggle-button.js', 'grip-message-body.js', 'message-container.js', 'message-icon.js', diff --git a/devtools/client/webconsole/new-console-output/constants.js b/devtools/client/webconsole/new-console-output/constants.js index e0a3e552cf0b..d9e1735ef02e 100644 --- a/devtools/client/webconsole/new-console-output/constants.js +++ b/devtools/client/webconsole/new-console-output/constants.js @@ -8,6 +8,10 @@ const actionTypes = { MESSAGE_ADD: "MESSAGE_ADD", MESSAGES_CLEAR: "MESSAGES_CLEAR", + SEVERITY_FILTER: "SEVERITY_FILTER", + MESSAGES_SEARCH: "MESSAGES_SEARCH", + FILTERS_CLEAR: "FILTERS_CLEAR", + FILTERBAR_TOGGLE: "FILTERBAR_TOGGLE", }; const categories = { @@ -92,6 +96,10 @@ const chromeRDPEnums = { } }; +const filterTypes = { + SEVERITY_FILTER: "SEVERITY_FILTER" +}; + // Combine into a single constants object module.exports = Object.assign({}, actionTypes, categories, severities, levels, - chromeRDPEnums); + chromeRDPEnums, filterTypes); diff --git a/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js b/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js index b10a61027e58..0c3024e4c16c 100644 --- a/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js +++ b/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js @@ -12,11 +12,19 @@ const actions = require("devtools/client/webconsole/new-console-output/actions/m const { store } = require("devtools/client/webconsole/new-console-output/store"); const ConsoleOutput = React.createFactory(require("devtools/client/webconsole/new-console-output/components/console-output")); +const FilterBar = React.createFactory(require("devtools/client/webconsole/new-console-output/components/filter-bar")); function NewConsoleOutputWrapper(parentNode, jsterm) { let childComponent = ConsoleOutput({ jsterm }); + let filterBar = FilterBar({}); let provider = React.createElement( - Provider, { store: store }, childComponent); + Provider, + { store: store }, + React.DOM.div( + {className: "webconsole-output-wrapper"}, + filterBar, + childComponent + )); this.body = ReactDOM.render(provider, parentNode); } diff --git a/devtools/client/webconsole/new-console-output/reducers/filters.js b/devtools/client/webconsole/new-console-output/reducers/filters.js new file mode 100644 index 000000000000..a3c11df502bb --- /dev/null +++ b/devtools/client/webconsole/new-console-output/reducers/filters.js @@ -0,0 +1,26 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const constants = require("devtools/client/webconsole/new-console-output/constants"); +const {Filters} = require("devtools/client/webconsole/new-console-output/store"); + +function filters(state = new Filters(), action) { + switch (action.type) { + case constants.SEVERITY_FILTER: + let {filter, toggled} = action; + return state.set(filter, toggled); + case constants.FILTERS_CLEAR: + return new Filters(); + case constants.MESSAGES_SEARCH: + let {searchText} = action; + return state.set("searchText", searchText); + } + + return state; +} + +exports.filters = filters; diff --git a/devtools/client/webconsole/new-console-output/reducers/index.js b/devtools/client/webconsole/new-console-output/reducers/index.js index 3bccd0058eec..6ab10d565f21 100644 --- a/devtools/client/webconsole/new-console-output/reducers/index.js +++ b/devtools/client/webconsole/new-console-output/reducers/index.js @@ -5,10 +5,14 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; +const { filters } = require("./filters"); const { messages } = require("./messages"); const { prefs } = require("./prefs"); +const { ui } = require("./ui"); exports.reducers = { + filters, messages, prefs, + ui, }; diff --git a/devtools/client/webconsole/new-console-output/reducers/moz.build b/devtools/client/webconsole/new-console-output/reducers/moz.build index 33e4db68112b..651512f8508e 100644 --- a/devtools/client/webconsole/new-console-output/reducers/moz.build +++ b/devtools/client/webconsole/new-console-output/reducers/moz.build @@ -4,7 +4,9 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. DevToolsModules( + 'filters.js', 'index.js', 'messages.js', 'prefs.js', + 'ui.js', ) diff --git a/devtools/client/webconsole/new-console-output/reducers/prefs.js b/devtools/client/webconsole/new-console-output/reducers/prefs.js index 047f159097aa..e6d9d0d0ba39 100644 --- a/devtools/client/webconsole/new-console-output/reducers/prefs.js +++ b/devtools/client/webconsole/new-console-output/reducers/prefs.js @@ -5,7 +5,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; -function prefs(state = {}, action) { +const {Prefs} = require("devtools/client/webconsole/new-console-output/store"); + +function prefs(state = new Prefs(), action) { return state; } diff --git a/devtools/client/webconsole/new-console-output/reducers/ui.js b/devtools/client/webconsole/new-console-output/reducers/ui.js new file mode 100644 index 000000000000..8496fea1ed11 --- /dev/null +++ b/devtools/client/webconsole/new-console-output/reducers/ui.js @@ -0,0 +1,20 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const constants = require("devtools/client/webconsole/new-console-output/constants"); +const {Ui} = require("devtools/client/webconsole/new-console-output/store"); + +function ui(state = new Ui(), action) { + switch (action.type) { + case constants.FILTERBAR_TOGGLE: + return state.set("configFilterBarVisible", !state.configFilterBarVisible); + } + + return state; +} + +exports.ui = ui; diff --git a/devtools/client/webconsole/new-console-output/selectors/filters.js b/devtools/client/webconsole/new-console-output/selectors/filters.js new file mode 100644 index 000000000000..36afa60cc481 --- /dev/null +++ b/devtools/client/webconsole/new-console-output/selectors/filters.js @@ -0,0 +1,12 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +function getAllFilters(state) { + return state.filters; +} + +exports.getAllFilters = getAllFilters; diff --git a/devtools/client/webconsole/new-console-output/selectors/messages.js b/devtools/client/webconsole/new-console-output/selectors/messages.js index 7c1fd4e54a00..c0e3f07eee1c 100644 --- a/devtools/client/webconsole/new-console-output/selectors/messages.js +++ b/devtools/client/webconsole/new-console-output/selectors/messages.js @@ -5,13 +5,46 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; +const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters"); const { getLogLimit } = require("devtools/client/webconsole/new-console-output/selectors/prefs"); function getAllMessages(state) { let messages = state.messages; - let messageCount = messages.count(); let logLimit = getLogLimit(state); + let filters = getAllFilters(state); + return prune( + search( + filterSeverity(messages, filters), + filters.searchText + ), + logLimit + ); +} + +function filterSeverity(messages, filters) { + return messages.filter((message) => filters[message.severity] === true); +} + +function search(messages, searchText = "") { + if (searchText === "") { + return messages; + } + + return messages.filter(function (message) { + // @TODO: message.parameters can be a grip, see how we can handle that + if (!Array.isArray(message.parameters)) { + return true; + } + return message + .parameters.join("") + .toLocaleLowerCase() + .includes(searchText.toLocaleLowerCase()); + }); +} + +function prune(messages, logLimit) { + let messageCount = messages.count(); if (messageCount > logLimit) { return messages.splice(0, messageCount - logLimit); } diff --git a/devtools/client/webconsole/new-console-output/selectors/moz.build b/devtools/client/webconsole/new-console-output/selectors/moz.build index af84b1afe337..547f53542a26 100644 --- a/devtools/client/webconsole/new-console-output/selectors/moz.build +++ b/devtools/client/webconsole/new-console-output/selectors/moz.build @@ -4,6 +4,8 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. DevToolsModules( + 'filters.js', 'messages.js', 'prefs.js', + 'ui.js', ) diff --git a/devtools/client/webconsole/new-console-output/selectors/ui.js b/devtools/client/webconsole/new-console-output/selectors/ui.js new file mode 100644 index 000000000000..32d104815d00 --- /dev/null +++ b/devtools/client/webconsole/new-console-output/selectors/ui.js @@ -0,0 +1,12 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +function getAllUi(state) { + return state.ui; +} + +exports.getAllUi = getAllUi; diff --git a/devtools/client/webconsole/new-console-output/store.js b/devtools/client/webconsole/new-console-output/store.js index 691a1e4eb0c8..2cae750e3341 100644 --- a/devtools/client/webconsole/new-console-output/store.js +++ b/devtools/client/webconsole/new-console-output/store.js @@ -3,17 +3,50 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; -const { combineReducers, createStore } = require("devtools/client/shared/vendor/redux"); const Immutable = require("devtools/client/shared/vendor/immutable"); -const { reducers } = require("./reducers/index"); const Services = require("Services"); +const Filters = Immutable.Record({ + error: true, + warn: true, + info: true, + log: true, + searchText: "" +}); + +const Prefs = Immutable.Record({ + logLimit: 1000 +}); + +const Ui = Immutable.Record({ + configFilterBarVisible: false, + filteredMessageVisible: false +}); + +module.exports.Prefs = Prefs; +module.exports.Filters = Filters; +module.exports.Ui = Ui; + +const { combineReducers, createStore } = require("devtools/client/shared/vendor/redux"); +const { reducers } = require("./reducers/index"); + function storeFactory() { const initialState = { messages: Immutable.List(), - prefs: { - logLimit: Math.max(Services.prefs.getIntPref("devtools.hud.loglimit"), 1) - } + prefs: new Prefs({ + logLimit: Math.max(Services.prefs.getIntPref("devtools.hud.loglimit"), 1), + }), + filters: new Filters({ + error: Services.prefs.getBoolPref("devtools.webconsole.filter.error"), + warn: Services.prefs.getBoolPref("devtools.webconsole.filter.warn"), + info: Services.prefs.getBoolPref("devtools.webconsole.filter.info"), + log: Services.prefs.getBoolPref("devtools.webconsole.filter.log"), + searchText: "" + }), + ui: new Ui({ + configFilterBarVisible: false, + filteredMessageVisible: false + }) }; return createStore(combineReducers(reducers), initialState); diff --git a/devtools/client/webconsole/webconsole.js b/devtools/client/webconsole/webconsole.js index 417cf05605a7..9fb99de537fd 100644 --- a/devtools/client/webconsole/webconsole.js +++ b/devtools/client/webconsole/webconsole.js @@ -553,6 +553,9 @@ WebConsoleFrame.prototype = { // in JSTerm is still necessary. this.newConsoleOutput = new this.window.NewConsoleOutput(this.experimentalOutputNode, this.jsterm); console.log("Created newConsoleOutput", this.newConsoleOutput); + + let filterToolbar = doc.querySelector(".hud-console-filter-toolbar"); + filterToolbar.hidden = true; } this.resize(); @@ -586,6 +589,13 @@ WebConsoleFrame.prototype = { return; } + // Do not focus if a search input was clicked on the new frontend + if (this.NEW_CONSOLE_OUTPUT_ENABLED && + event.target.nodeName.toLowerCase() === "input" && + event.target.getAttribute("type").toLowerCase() === "search") { + return; + } + this.jsterm.focus(); });