UNPKG

coveo-search-ui

Version:

Coveo JavaScript Search Framework

1,029 lines (1,011 loc) • 223 kB
webpackJsonpCoveo__temporary([9,10,11,79],{ /***/ 133: /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var Result_1 = __webpack_require__(52); var Result_2 = __webpack_require__(52); exports.notWordStart = ' ()[],$@\'"'; exports.notInWord = ' ()[],:'; exports.Basic = { basicExpressions: ['Word', 'DoubleQuoted'], grammars: { DoubleQuoted: '"[NotDoubleQuote]"', NotDoubleQuote: /[^"]*/, SingleQuoted: "'[NotSingleQuote]'", NotSingleQuote: /[^']*/, Number: /-?(0|[1-9]\d*)(\.\d+)?/, Word: function (input, end, expression) { var regex = new RegExp('[^' + exports.notWordStart.replace(/(.)/g, '\\$1') + '][^' + exports.notInWord.replace(/(.)/g, '\\$1') + ']*'); var groups = input.match(regex); if (groups != null && groups.index != 0) { groups = null; } var result = new Result_1.Result(groups != null ? groups[0] : null, expression, input); if (result.isSuccess() && end && input.length > result.value.length) { return new Result_2.EndOfInputResult(result); } return result; } } }; /***/ }), /***/ 139: /***/ (function(module, exports, __webpack_require__) { "use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); Object.defineProperty(exports, "__esModule", { value: true }); __webpack_require__(566); var QueryEvents_1 = __webpack_require__(11); var StandaloneSearchInterfaceEvents_1 = __webpack_require__(100); var GlobalExports_1 = __webpack_require__(3); var Grammar_1 = __webpack_require__(176); var MagicBox_1 = __webpack_require__(222); var Assert_1 = __webpack_require__(5); var Model_1 = __webpack_require__(18); var QueryStateModel_1 = __webpack_require__(13); var Strings_1 = __webpack_require__(6); var Dom_1 = __webpack_require__(1); var AnalyticsActionListMeta_1 = __webpack_require__(10); var Component_1 = __webpack_require__(7); var ComponentOptions_1 = __webpack_require__(8); var Initialization_1 = __webpack_require__(2); var QueryboxOptionsProcessing_1 = __webpack_require__(511); var QueryboxQueryParameters_1 = __webpack_require__(140); /** * The `Querybox` component renders an input which the end user can interact with to enter and submit queries. * * When the end user submits a search request, the `Querybox` component triggers a query and logs the corresponding * usage analytics data. * * For technical reasons, it is necessary to instantiate this component on a `div` element rather than on an `input` * element. * * See also the [`Searchbox`]{@link Searchbox} component, which can automatically instantiate a `Querybox` along with an * optional [`SearchButton`]{@link SearchButton} component. */ var Querybox = /** @class */ (function (_super) { __extends(Querybox, _super); /** * Creates a new `Querybox component`. Creates a new `Coveo.Magicbox` instance and wraps the Magicbox methods * (`onblur`, `onsubmit` etc.). Binds event on `buildingQuery` and before redirection (for standalone box). * @param element The HTMLElement on which to instantiate the component. This cannot be an HTMLInputElement for * technical reasons. * @param options The options for the `Querybox` component. * @param bindings The bindings that the component requires to function normally. If not set, these will be * automatically resolved (with a slower execution time). */ function Querybox(element, options, bindings) { var _this = _super.call(this, element, Querybox.ID, bindings) || this; _this.element = element; _this.options = options; _this.bindings = bindings; if (element instanceof HTMLInputElement) { _this.logger.error('Querybox cannot be used on an HTMLInputElement'); } _this.options = ComponentOptions_1.ComponentOptions.initComponentOptions(element, Querybox, options); new QueryboxOptionsProcessing_1.QueryboxOptionsProcessing(_this).postProcess(); Dom_1.$$(_this.element).toggleClass('coveo-query-syntax-disabled', _this.options.enableQuerySyntax == false); _this.magicBox = MagicBox_1.createMagicBox(element, new Grammar_1.Grammar('Query', { Query: '[Term*][Spaces?]', Term: '[Spaces?][Word]', Spaces: / +/, Word: /[^ ]+/ }), { inline: true }); var input = Dom_1.$$(_this.magicBox.element).find('input'); if (input) { Dom_1.$$(input).setAttribute('aria-label', _this.options.placeholder || Strings_1.l('Search')); } _this.bind.onRootElement(QueryEvents_1.QueryEvents.buildingQuery, function (args) { return _this.handleBuildingQuery(args); }); _this.bind.onRootElement(StandaloneSearchInterfaceEvents_1.StandaloneSearchInterfaceEvents.beforeRedirect, function () { return _this.updateQueryState(); }); _this.bind.onQueryState(Model_1.MODEL_EVENTS.CHANGE_ONE, QueryStateModel_1.QUERY_STATE_ATTRIBUTES.Q, function (args) { return _this.handleQueryStateChanged(args); }); if (_this.options.enableSearchAsYouType) { Dom_1.$$(_this.element).addClass('coveo-search-as-you-type'); _this.magicBox.onchange = function () { _this.searchAsYouType(); }; } _this.magicBox.onsubmit = function () { _this.submit(); }; _this.magicBox.onblur = function () { _this.updateQueryState(); }; _this.magicBox.onclear = function () { _this.updateQueryState(); if (_this.options.triggerQueryOnClear) { _this.usageAnalytics.logSearchEvent(AnalyticsActionListMeta_1.analyticsActionCauseList.searchboxClear, {}); _this.triggerNewQuery(false); } }; return _this; } /** * Adds the current content of the input to the query and triggers a query if the current content of the input has * changed since last submit. * * Also logs the `serachboxSubmit` event in the usage analytics. */ Querybox.prototype.submit = function () { this.magicBox.clearSuggestion(); this.updateQueryState(); this.usageAnalytics.logSearchEvent(AnalyticsActionListMeta_1.analyticsActionCauseList.searchboxSubmit, {}); this.triggerNewQuery(false); }; /** * Sets the content of the input. * * @param text The string to set in the input. */ Querybox.prototype.setText = function (text) { this.magicBox.setText(text); this.updateQueryState(); }; /** * Clears the content of the input. * * See also the [`triggerQueryOnClear`]{@link Querybox.options.triggerQueryOnClear} option. */ Querybox.prototype.clear = function () { this.magicBox.clear(); }; /** * Gets the content of the input. * * @returns {string} The content of the input. */ Querybox.prototype.getText = function () { return this.magicBox.getText(); }; /** * Gets the result from the input. * * @returns {Result} The result. */ Querybox.prototype.getResult = function () { return this.magicBox.getResult(); }; /** * Gets the displayed result from the input. * * @returns {Result} The displayed result. */ Querybox.prototype.getDisplayedResult = function () { return this.magicBox.getDisplayedResult(); }; /** * Gets the current cursor position in the input. * * @returns {number} The cursor position (index starts at 0). */ Querybox.prototype.getCursor = function () { return this.magicBox.getCursor(); }; /** * Gets the result at cursor position. * * @param match {string | { (result): boolean }} The match condition. * * @returns {Result[]} The result. */ Querybox.prototype.resultAtCursor = function (match) { return this.magicBox.resultAtCursor(match); }; Querybox.prototype.handleBuildingQuery = function (args) { Assert_1.Assert.exists(args); Assert_1.Assert.exists(args.queryBuilder); this.updateQueryState(); this.lastQuery = this.magicBox.getText(); new QueryboxQueryParameters_1.QueryboxQueryParameters(this.options).addParameters(args.queryBuilder, this.lastQuery); }; Querybox.prototype.triggerNewQuery = function (searchAsYouType) { clearTimeout(this.searchAsYouTypeTimeout); var text = this.magicBox.getText(); if (this.lastQuery != text && text != null) { this.lastQuery = text; this.queryController.executeQuery({ searchAsYouType: searchAsYouType, logInActionsHistory: true }); } }; Querybox.prototype.updateQueryState = function () { this.queryStateModel.set(QueryStateModel_1.QueryStateModel.attributesEnum.q, this.magicBox.getText()); }; Querybox.prototype.handleQueryStateChanged = function (args) { Assert_1.Assert.exists(args); var q = args.value; if (q != this.magicBox.getText()) { this.magicBox.setText(q); } }; Querybox.prototype.searchAsYouType = function () { var _this = this; clearTimeout(this.searchAsYouTypeTimeout); this.searchAsYouTypeTimeout = window.setTimeout(function () { _this.usageAnalytics.logSearchAsYouType(AnalyticsActionListMeta_1.analyticsActionCauseList.searchboxAsYouType, {}); _this.triggerNewQuery(true); }, this.options.searchAsYouTypeDelay); }; Querybox.ID = 'Querybox'; Querybox.doExport = function () { GlobalExports_1.exportGlobally({ Querybox: Querybox, QueryboxQueryParameters: QueryboxQueryParameters_1.QueryboxQueryParameters }); }; /** * The options for the Querybox. * @componentOptions */ Querybox.options = { /** * Whether to enable the search-as-you-type feature. * * **Note:** Enabling this feature can consume lots of queries per month (QPM), especially if the [`searchAsYouTypeDelay`]{@link Querybox.options.searchAsYouTypeDelay} option is set to a low value. * * Default value is `false`. */ enableSearchAsYouType: ComponentOptions_1.ComponentOptions.buildBooleanOption({ defaultValue: false, section: 'Advanced Options' }), /** * If the [`enableSearchAsYouType`]{@link Querybox.options.enableSearchAsYouType} option is `true`, specifies how * long to wait (in milliseconds) between each key press before triggering a new query. * * Default value is `50`. Minimum value is `0` */ searchAsYouTypeDelay: ComponentOptions_1.ComponentOptions.buildNumberOption({ defaultValue: 50, min: 0, section: 'Advanced Options' }), /** * Specifies whether to interpret special query syntax (e.g., `@objecttype=message`) when the end user types * a query in the `Querybox` (see * [Coveo Query Syntax Reference](https://docs.coveo.com/en/1552/searching-with-coveo/coveo-cloud-query-syntax)). Setting this * option to `true` also causes the `Querybox` to highlight any query syntax. * * Regardless of the value of this option, the Coveo Cloud REST Search API always interprets expressions surrounded * by double quotes (`"`) as exact phrase match requests. * * See also [`enableLowercaseOperators`]{@link Querybox.options.enableLowercaseOperators}. * * **Notes:** * > * End user preferences can override the value you specify for this option. * > * > If the end user selects a value other than **Automatic** for the **Enable query syntax** setting (see * > the [`enableQuerySyntax`]{@link ResultsPreferences.options.enableQuerySyntax} option of the * > [`ResultsPreferences`]{@link ResultsPreferences} component), the end user preference takes precedence over this * > option. * > * > * On-premises versions of the Coveo Search API require this option to be set to `true` in order to interpret * > expressions surrounded by double quotes (`"`) as exact phrase match requests. * * Default value is `false`. */ enableQuerySyntax: ComponentOptions_1.ComponentOptions.buildBooleanOption({ defaultValue: false, section: 'Advanced Options' }), /** * Specifies whether to expand basic expression keywords containing wildcards characters (`*`) to the possible * matching keywords in order to broaden the query (see * [Using Wildcards in Queries](https://docs.coveo.com/en/1580/)). * * See also [`enableQuestionMarks`]{@link Querybox.options.enableQuestionMarks}. * * **Note:** * > If you are using an on-premises version of the Coveo Search API, you need to set the * > [`enableQuerySyntax`]{@link Querybox.options.enableQuerySyntax} option to `true` to be able to set * > `enableWildcards` to `true`. * * Default value is `false`. */ enableWildcards: ComponentOptions_1.ComponentOptions.buildBooleanOption({ defaultValue: false, section: 'Advanced Options' }), /** * If [`enableWildcards`]{@link Querybox.options.enableWildcards} is `true`, specifies whether to expand basic * expression keywords containing question mark characters (`?`) to the possible matching keywords in order to * broaden the query (see * [Using Wildcards in Queries](https://docs.coveo.com/en/1580/)). * * **Note:** * > If you are using an on-premises version of the Coveo Search API, you also need to set the * > [`enableQuerySyntax`]{@link Querybox.options.enableQuerySyntax} option to `true` in order to be able to set * > `enableQuestionMarks` to `true`. * * Default value is `false`. */ enableQuestionMarks: ComponentOptions_1.ComponentOptions.buildBooleanOption({ defaultValue: false, depend: 'enableWildcards' }), /** * If the [`enableQuerySyntax`]{@link Querybox.options.enableQuerySyntax} option is `true`, specifies whether to * interpret the `AND`, `NOT`, `OR`, and `NEAR` keywords in the `Querybox` as query operators in the query, even if * the end user types those keywords in lowercase. * * This option applies to all query operators (see * [Coveo Query Syntax Reference](https://docs.coveo.com/en/1552/searching-with-coveo/coveo-cloud-query-syntax)). * * **Example:** * > If this option and the `enableQuerySyntax` option are both `true`, the Coveo Platform interprets the `near` * > keyword in a query such as `service center near me` as the `NEAR` query operator (not as a query term). * * > Otherwise, if the `enableQuerySyntax` option is `true` and this option is `false`, the end user has to type the * > `NEAR` keyword in uppercase for the Coveo Platform to interpret it as a query operator. * * Default value is `false`. */ enableLowercaseOperators: ComponentOptions_1.ComponentOptions.buildBooleanOption({ defaultValue: false, depend: 'enableQuerySyntax' }), /** * Whether to convert a basic expression containing at least a certain number of keywords (see the * [`partialMatchKeywords`]{@link Querybox.options.partialMatchKeywords} option) to *partial match expression*, so * that items containing at least a certain number of those keywords (see the * [`partialMatchThreshold`]{@link Querybox.options.partialMatchThreshold} option) will match the expression. * * **Notes:** * - Only the basic expression of the query (see [`q`]{@link q}) can be converted to a partial match expression. * - When the [`enableQuerySyntax`]{@link Querybox.options.enableQuerySyntax} option is set to `true`, this feature has no effect if the basic expression contains advanced query syntax (field expressions, operators, etc.). * * @notSupportedIn salesforcefree */ enablePartialMatch: ComponentOptions_1.ComponentOptions.buildBooleanOption({ defaultValue: false }), /** * The minimum number of keywords that need to be present in the basic expression to convert it to a partial match expression. * * See also the [`partialMatchThreshold`]{@link Querybox.options.partialMatchThreshold} option. * * **Notes:** * * - Repeated keywords count as a single keyword. * - Thesaurus expansions count towards the `partialMatchKeywords` count. * - Stemming expansions **do not** count towards the `partialMatchKeywords` count. * * @notSupportedIn salesforcefree */ partialMatchKeywords: ComponentOptions_1.ComponentOptions.buildNumberOption({ defaultValue: 5, min: 1, depend: 'enablePartialMatch' }), /** * An absolute or relative value indicating the minimum number of partial match expression keywords an item must contain to match the expression. * * See also the [`partialMatchKeywords`]{@link Querybox.options.partialMatchKeywords} option. * * **Notes:** * - A keyword and its stemming expansions count as a single keyword when evaluating whether an item meets the `partialMatchThreshold`. * - When a relative `partialMatchThreshold` does not yield a whole integer, the fractional part is truncated (e.g., `3.6` becomes `3`). * * @notSupportedIn salesforcefree */ partialMatchThreshold: ComponentOptions_1.ComponentOptions.buildStringOption({ defaultValue: '50%', depend: 'enablePartialMatch' }), /** * Whether to trigger a query when clearing the `Querybox`. * * Default value is `false`. */ triggerQueryOnClear: ComponentOptions_1.ComponentOptions.buildBooleanOption({ defaultValue: false }) }; return Querybox; }(Component_1.Component)); exports.Querybox = Querybox; Initialization_1.Initialization.registerAutoCreateComponent(Querybox); /***/ }), /***/ 176: /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var ExpressionRef_1 = __webpack_require__(493); var ExpressionOptions_1 = __webpack_require__(495); var ExpressionRegExp_1 = __webpack_require__(497); var _ = __webpack_require__(0); var ExpressionFunction_1 = __webpack_require__(498); var ExpressionConstant_1 = __webpack_require__(219); var ExpressionList_1 = __webpack_require__(499); var Grammar = /** @class */ (function () { function Grammar(start, expressions) { if (expressions === void 0) { expressions = {}; } this.expressions = {}; this.start = new ExpressionRef_1.ExpressionRef(start, null, 'start', this); this.addExpressions(expressions); } Grammar.prototype.addExpressions = function (expressions) { var _this = this; _.each(expressions, function (basicExpression, id) { _this.addExpression(id, basicExpression); }); }; Grammar.prototype.addExpression = function (id, basicExpression) { if (id in this.expressions) { throw new Error('Grammar already contain the id:' + id); } this.expressions[id] = Grammar.buildExpression(basicExpression, id, this); }; Grammar.prototype.getExpression = function (id) { return this.expressions[id]; }; Grammar.prototype.parse = function (value) { return this.start.parse(value, true); }; Grammar.buildExpression = function (value, id, grammar) { var type = typeof value; if (type == 'undefined') { throw new Error('Invalid Expression: ' + value); } if (_.isString(value)) { return this.buildStringExpression(value, id, grammar); } if (_.isArray(value)) { return new ExpressionOptions_1.ExpressionOptions(_.map(value, function (v, i) { return new ExpressionRef_1.ExpressionRef(v, null, id + '_' + i, grammar); }), id); } if (_.isRegExp(value)) { return new ExpressionRegExp_1.ExpressionRegExp(value, id, grammar); } if (_.isFunction(value)) { return new ExpressionFunction_1.ExpressionFunction(value, id, grammar); } throw new Error('Invalid Expression: ' + value); }; Grammar.buildStringExpression = function (value, id, grammar) { var matchs = Grammar.stringMatch(value, Grammar.spliter); var expressions = _.map(matchs, function (match, i) { if (match[1]) { // Ref var ref = match[1]; var occurrence = match[3] ? Number(match[3]) : match[2] || null; return new ExpressionRef_1.ExpressionRef(ref, occurrence, id + '_' + i, grammar); } else { // Constant return new ExpressionConstant_1.ExpressionConstant(match[4], id + '_' + i); } }); if (expressions.length == 1) { var expression = expressions[0]; expression.id = id; return expression; } else { return new ExpressionList_1.ExpressionList(expressions, id); } }; Grammar.stringMatch = function (str, re) { var groups = []; var cloneRegExp = new RegExp(re.source, 'g'); var group = cloneRegExp.exec(str); while (group !== null) { groups.push(group); group = cloneRegExp.exec(str); } return groups; }; Grammar.spliter = /\[(\w+)(\*|\+|\?|\{([1-9][0-9]*)\})?\]|(.[^\[]*)/; return Grammar; }()); exports.Grammar = Grammar; /***/ }), /***/ 193: /***/ (function(module, exports, __webpack_require__) { "use strict"; ///<reference path="FieldAddon.ts" /> ///<reference path="QueryExtensionAddon.ts" /> ///<reference path="QuerySuggestAddon.ts" /> ///<reference path="OldOmniboxAddon.ts" /> var __extends = (this && this.__extends) || (function () { var extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __assign = (this && this.__assign) || Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; Object.defineProperty(exports, "__esModule", { value: true }); __webpack_require__(574); var _ = __webpack_require__(0); var underscore_1 = __webpack_require__(0); var BreadcrumbEvents_1 = __webpack_require__(35); var OmniboxEvents_1 = __webpack_require__(34); var QueryEvents_1 = __webpack_require__(11); var StandaloneSearchInterfaceEvents_1 = __webpack_require__(100); var GlobalExports_1 = __webpack_require__(3); var Grammar_1 = __webpack_require__(176); var Complete_1 = __webpack_require__(508); var Expressions_1 = __webpack_require__(509); var MagicBox_1 = __webpack_require__(222); var Assert_1 = __webpack_require__(5); var ComponentOptionsModel_1 = __webpack_require__(28); var Model_1 = __webpack_require__(18); var QueryStateModel_1 = __webpack_require__(13); var Strings_1 = __webpack_require__(6); var Dom_1 = __webpack_require__(1); var Utils_1 = __webpack_require__(4); var AnalyticsActionListMeta_1 = __webpack_require__(10); var PendingSearchAsYouTypeSearchEvent_1 = __webpack_require__(128); var SharedAnalyticsCalls_1 = __webpack_require__(126); var Component_1 = __webpack_require__(7); var ComponentOptions_1 = __webpack_require__(8); var Initialization_1 = __webpack_require__(2); var Querybox_1 = __webpack_require__(139); var QueryboxOptionsProcessing_1 = __webpack_require__(511); var QueryboxQueryParameters_1 = __webpack_require__(140); var SearchInterface_1 = __webpack_require__(19); var FieldAddon_1 = __webpack_require__(575); var OldOmniboxAddon_1 = __webpack_require__(576); var QueryExtensionAddon_1 = __webpack_require__(577); var QuerySuggestAddon_1 = __webpack_require__(578); var MINIMUM_EXECUTABLE_CONFIDENCE = 0.8; /** * The `Omnibox` component extends the [`Querybox`]{@link Querybox}, and thus provides the same basic options and * behaviors. Furthermore, the `Omnibox` adds a type-ahead capability to the search input. * * You can configure the type-ahead feature by enabling or disabling certain addons, which the Coveo JavaScript Search * Framework provides out-of-the-box (see the [`enableFieldAddon`]{@link Omnibox.options.enableFieldAddon}, * [`enableQueryExtension`]{@link Omnibox.options.enableQueryExtensionAddon}, and * [`enableQuerySuggestAddon`]{@link Omnibox.options.enableQuerySuggestAddon} options). * * Custom components and external code can also extend or customize the type-ahead feature and the query completion * suggestions it provides by attaching their own handlers to the * [`populateOmniboxSuggestions`]{@link OmniboxEvents.populateOmniboxSuggestions} event. * * See also the [`Searchbox`]{@link Searchbox} component, which can automatically instantiate an `Omnibox` along with an * optional {@link SearchButton}. */ var Omnibox = /** @class */ (function (_super) { __extends(Omnibox, _super); /** * Creates a new Omnibox component. Also enables necessary addons and binds events on various query events. * @param element The HTMLElement on which to instantiate the component. * @param options The options for the Omnibox component. * @param bindings The bindings that the component requires to function normally. If not set, these will be * automatically resolved (with a slower execution time). */ function Omnibox(element, options, bindings) { var _this = _super.call(this, element, Omnibox.ID, bindings) || this; _this.element = element; _this.options = options; _this.lastSuggestions = []; _this.movedOnce = false; _this.skipAutoSuggest = false; _this.options = ComponentOptions_1.ComponentOptions.initComponentOptions(element, Omnibox, options); var originalValueForQuerySyntax = _this.options.enableQuerySyntax; new QueryboxOptionsProcessing_1.QueryboxOptionsProcessing(_this).postProcess(); _this.omniboxAnalytics = _this.searchInterface.getOmniboxAnalytics(); Dom_1.$$(_this.element).toggleClass('coveo-query-syntax-disabled', _this.options.enableQuerySyntax == false); _this.suggestionAddon = _this.options.enableQuerySuggestAddon ? new QuerySuggestAddon_1.QuerySuggestAddon(_this) : new QuerySuggestAddon_1.VoidQuerySuggestAddon(); new OldOmniboxAddon_1.OldOmniboxAddon(_this); _this.createMagicBox(); _this.bind.onRootElement(QueryEvents_1.QueryEvents.newQuery, function (args) { return _this.handleNewQuery(args); }); _this.bind.onRootElement(QueryEvents_1.QueryEvents.buildingQuery, function (args) { return _this.handleBuildingQuery(args); }); _this.bind.onRootElement(StandaloneSearchInterfaceEvents_1.StandaloneSearchInterfaceEvents.beforeRedirect, function () { return _this.handleBeforeRedirect(); }); _this.bind.onRootElement(QueryEvents_1.QueryEvents.querySuccess, function () { return _this.handleQuerySuccess(); }); _this.bind.onQueryState(Model_1.MODEL_EVENTS.CHANGE_ONE, QueryStateModel_1.QUERY_STATE_ATTRIBUTES.Q, function (args) { return _this.handleQueryStateChanged(args); }); if (_this.isAutoSuggestion()) { _this.bind.onRootElement(QueryEvents_1.QueryEvents.duringQuery, function (args) { return _this.handleDuringQuery(args); }); } _this.bind.onComponentOptions(Model_1.MODEL_EVENTS.CHANGE_ONE, ComponentOptionsModel_1.COMPONENT_OPTIONS_ATTRIBUTES.SEARCH_BOX, function (args) { if (args.value.enableQuerySyntax != null) { _this.options.enableQuerySyntax = args.value.enableQuerySyntax; } else { _this.options.enableQuerySyntax = originalValueForQuerySyntax; } _this.updateGrammar(); }); _this.bind.onRootElement(OmniboxEvents_1.OmniboxEvents.querySuggestGetFocus, function (args) { return _this.handleQuerySuggestGetFocus(args); }); return _this; } /** * Adds the current content of the input to the query and triggers a query if the current content of the input has * changed since last submit. * * Also logs a `searchboxSubmit` event in the usage analytics. */ Omnibox.prototype.submit = function () { var _this = this; this.magicBox.clearSuggestion(); this.updateQueryState(); this.triggerNewQuery(false, function () { SharedAnalyticsCalls_1.logSearchBoxSubmitEvent(_this.usageAnalytics); }); this.magicBox.blur(); }; /** * Gets the current content of the input. * @returns {string} The current content of the input. */ Omnibox.prototype.getText = function () { return this.magicBox.getText(); }; /** * Sets the content of the input * @param text The string to set in the input. */ Omnibox.prototype.setText = function (text) { this.magicBox.setText(text); this.updateQueryState(); }; /** * Clears the content of the input. */ Omnibox.prototype.clear = function () { this.magicBox.clear(); }; /** * Gets the `HTMLInputElement` of the Omnibox. */ Omnibox.prototype.getInput = function () { return this.magicBox.element.querySelector('input'); }; Omnibox.prototype.getResult = function () { return this.magicBox.getResult(); }; Omnibox.prototype.getDisplayedResult = function () { return this.magicBox.getDisplayedResult(); }; Omnibox.prototype.getCursor = function () { return this.magicBox.getCursor(); }; Omnibox.prototype.resultAtCursor = function (match) { return this.magicBox.resultAtCursor(match); }; Omnibox.prototype.createGrammar = function () { var grammar = null; if (this.options.enableQuerySyntax) { grammar = Expressions_1.Expressions(Complete_1.Complete); if (this.options.enableFieldAddon) { new FieldAddon_1.FieldAddon(this); } if (this.options.fieldAlias != null) { this.options.listOfFields = this.options.listOfFields || []; this.options.listOfFields = this.options.listOfFields.concat(_.keys(this.options.fieldAlias)); } if (this.options.enableQueryExtensionAddon) { new QueryExtensionAddon_1.QueryExtensionAddon(this); } } else { grammar = { start: 'Any', expressions: { Any: /.*/ } }; } if (this.options.grammar != null) { grammar = this.options.grammar(grammar); } return grammar; }; Omnibox.prototype.updateGrammar = function () { var grammar = this.createGrammar(); this.magicBox.grammar = new Grammar_1.Grammar(grammar.start, grammar.expressions); this.magicBox.setText(this.magicBox.getText()); }; Omnibox.prototype.createMagicBox = function () { var grammar = this.createGrammar(); this.magicBox = MagicBox_1.createMagicBox(this.element, new Grammar_1.Grammar(grammar.start, grammar.expressions), { inline: this.options.inline, selectableSuggestionClass: 'coveo-omnibox-selectable', selectedSuggestionClass: 'coveo-omnibox-selected', suggestionTimeout: this.options.omniboxTimeout }); var input = Dom_1.$$(this.magicBox.element).find('input'); if (input) { Dom_1.$$(input).setAttribute('aria-label', Strings_1.l('Search')); } this.setupMagicBox(); }; Omnibox.prototype.setupMagicBox = function () { var _this = this; this.magicBox.onmove = function () { // We assume that once the user has moved its selection, it becomes an explicit omnibox analytics event if (_this.isAutoSuggestion()) { _this.modifyEventTo = _this.getOmniboxAnalyticsEventCause(); } _this.movedOnce = true; }; this.magicBox.onfocus = function () { if (_this.isAutoSuggestion()) { // This flag is used to block the automatic query when the UI is loaded with a query (#q=foo) // and then the input is focused. We want to block that query, even if it match the suggestion // Only when there is an actual change in the input (user typing something) is when we want the automatic query to kick in _this.skipAutoSuggest = true; } }; this.magicBox.onSuggestions = function (suggestions) { // If text is empty, this can mean that user selected text from the search box // and hit "delete" : Reset the partial queries in this case if (Utils_1.Utils.isEmptyString(_this.getText())) { _this.omniboxAnalytics.partialQueries = []; } _this.movedOnce = false; _this.lastSuggestions = suggestions; if (_this.isAutoSuggestion() && !_this.skipAutoSuggest) { _this.searchAsYouType(); } }; if (this.options.enableSearchAsYouType) { Dom_1.$$(this.element).addClass('coveo-magicbox-search-as-you-type'); } this.magicBox.onchange = function () { _this.skipAutoSuggest = false; var text = _this.getText(); if (text != undefined && text != '') { if (_this.isAutoSuggestion()) { if (_this.movedOnce) { _this.searchAsYouType(true); } } else if (_this.options.enableSearchAsYouType) { _this.searchAsYouType(true); } } else { _this.clear(); } }; if (this.options.placeholder) { this.magicBox.element.querySelector('input').placeholder = this.options.placeholder; } this.magicBox.onsubmit = function () { return _this.submit(); }; this.magicBox.onselect = function (suggestion) { var index = _.indexOf(_this.lastSuggestions, suggestion); var suggestions = _.compact(_.map(_this.lastSuggestions, function (suggestion) { return suggestion.text; })); _this.magicBox.clearSuggestion(); _this.updateQueryState(); // A bit tricky here : When it's machine learning auto suggestions // the mouse selection and keyboard selection acts differently : // keyboard selection will automatically do the query (which will log a search as you type event -> further modified by this.modifyEventTo if needed) // mouse selection will not "auto" send the query. // the movedOnce variable detect the keyboard movement, and is used to differentiate mouse vs keyboard if (!_this.isAutoSuggestion()) { _this.usageAnalytics.cancelAllPendingEvents(); _this.triggerNewQuery(false, function () { _this.usageAnalytics.logSearchEvent(_this.getOmniboxAnalyticsEventCause(), _this.buildCustomDataForPartialQueries(index, suggestions)); }); } else if (_this.isAutoSuggestion() && _this.movedOnce) { _this.handleAutoSuggestionWithKeyboard(index, suggestions); } else if (_this.isAutoSuggestion() && !_this.movedOnce) { _this.handleAutoSuggestionsWithMouse(index, suggestions); } // Consider a selection like a reset of the partial queries (it's the end of a suggestion pattern) if (_this.isAutoSuggestion()) { _this.omniboxAnalytics.partialQueries = []; } }; this.magicBox.onblur = function () { if (_this.isAutoSuggestion()) { _this.setText(_this.getQuery(true)); _this.usageAnalytics.sendAllPendingEvents(); } }; this.magicBox.onclear = function () { _this.updateQueryState(); if (_this.options.triggerQueryOnClear || _this.options.enableSearchAsYouType) { _this.triggerNewQuery(false, function () { _this.usageAnalytics.logSearchEvent(AnalyticsActionListMeta_1.analyticsActionCauseList.searchboxClear, {}); }); } }; this.magicBox.ontabpress = function () { _this.handleTabPress(); }; this.magicBox.getSuggestions = function () { return _this.handleSuggestions(); }; }; Omnibox.prototype.handleAutoSuggestionWithKeyboard = function (index, suggestions) { var _this = this; if (this.searchAsYouTypeTimeout) { // Here, there is currently a search as you typed queued up : // Think : user typed very quickly, then very quickly selected a suggestion (without waiting for the search as you type) // Cancel the search as you type query, then immediately do the query with the selection (+analytics event with the selection) this.usageAnalytics.cancelAllPendingEvents(); clearTimeout(this.searchAsYouTypeTimeout); this.searchAsYouTypeTimeout = undefined; this.triggerNewQuery(false, function () { _this.usageAnalytics.logSearchEvent(_this.getOmniboxAnalyticsEventCause(), _this.buildCustomDataForPartialQueries(index, suggestions)); }); } else { // Here, the search as you type query has returned, but the analytics event has not ye been sent. // Think : user typed slowly, the query returned, and then the user selected a suggestion. // Since the analytics event has not yet been sent (search as you type event have a 5 sec delay) // modify the pending event, then send the newly modified analytics event immediately. this.modifyEventTo = this.getOmniboxAnalyticsEventCause(); this.modifyCustomDataOnPending(index, suggestions); this.modifyQueryContentOnPending(); this.usageAnalytics.sendAllPendingEvents(); } }; Omnibox.prototype.handleAutoSuggestionsWithMouse = function (index, suggestions) { var _this = this; if (this.searchAsYouTypeTimeout || index != 0) { // Here : the user either very quickly chose the first suggestion, and the search as you type is still queued up. // OR // the user chose something different then the first suggestion. // Remove the search as you type if it's there, and do the query with the suggestion directly. this.clearSearchAsYouType(); this.usageAnalytics.cancelAllPendingEvents(); this.triggerNewQuery(false, function () { _this.usageAnalytics.logSearchEvent(_this.getOmniboxAnalyticsEventCause(), _this.buildCustomDataForPartialQueries(index, suggestions)); }); } else { // Here : the user either very slowly chose a suggestion, and there is no search as you typed queued up // AND // the user chose the first suggestion. // this means the query is already returned, but the analytics event is still queued up. // modify the analytics event, and send it. this.modifyEventTo = this.getOmniboxAnalyticsEventCause(); this.modifyCustomDataOnPending(index, suggestions); this.modifyQueryContentOnPending(); this.usageAnalytics.sendAllPendingEvents(); // This should happen if : // users did a (short) query first, which does not match the first suggestion. (eg: typed "t", then search) // then, refocus the search box. The same suggestion(s) will re-appear. // By selecting the first one with the mouse, we need to retrigger a query because this means the search as you type could not // kick in and do the query automatically. if (this.lastQuery != this.getText()) { this.triggerNewQuery(false, function () { _this.usageAnalytics.logSearchEvent(_this.getOmniboxAnalyticsEventCause(), _this.buildCustomDataForPartialQueries(index, suggestions)); }); } } }; Omnibox.prototype.modifyCustomDataOnPending = function (index, suggestions) { var pendingEvt = this.usageAnalytics.getPendingSearchEvent(); if (pendingEvt instanceof PendingSearchAsYouTypeSearchEvent_1.PendingSearchAsYouTypeSearchEvent) { var newCustomData_1 = this.buildCustomDataForPartialQueries(index, suggestions); _.each(_.keys(newCustomData_1), function (k) { pendingEvt.modifyCustomData(k, newCustomData_1[k]); }); } }; Omnibox.prototype.modifyQueryContentOnPending = function () { var pendingEvt = this.usageAnalytics.getPendingSearchEvent(); if (pendingEvt instanceof PendingSearchAsYouTypeSearchEvent_1.PendingSearchAsYouTypeSearchEvent) { var queryContent = this.getQuery(this.options.enableSearchAsYouType); pendingEvt.modifyQueryContent(queryContent); } }; Omnibox.prototype.buildCustomDataForPartialQueries = function (index, suggestions) { this.updateOmniboxAnalytics(suggestions, index); return this.omniboxAnalytics.buildCustomDataForPartialQueries(); }; Omnibox.prototype.handleQuerySuggestGetFocus = function (_a) { var suggestion = _a.suggestion; var suggestions = _.compact(_.map(this.lastSuggestions, function (suggestion) { return suggestion.text; })); var ranking = this.lastSuggestions.indexOf(underscore_1.findWhere(this.lastSuggestions, { text: suggestion })); this.updateOmniboxAnalytics(suggestions, ranking); }; Omnibox.prototype.updateOmniboxAnalytics = function (suggestions, suggestionRanking) { this.omniboxAnalytics.suggestions = suggestions; this.omniboxAnalytics.suggestionRanking = suggestionRanking; }; Omnibox.prototype.handleSuggestions = function () { var text = this.getText(); if (this.options.querySuggestCharacterThreshold <= text.length) { var suggestionsEventArgs = { suggestions: [], omnibox: this }; this.triggerOmniboxSuggestions(suggestionsEventArgs); if (!Utils_1.Utils.isNullOrEmptyString(text)) { this.omniboxAnalytics.partialQueries.push(text); } return _.compact(suggestionsEventArgs.suggestions); } else { return []; } }; Omnibox.prototype.triggerOmniboxSuggestions = function (args) { this.bind.trigger(this.element, OmniboxEvents_1.OmniboxEvents.populateOmniboxSuggestions, args); if (!Dom_1.$$(this.element).isDescendant(this.root)) { this.bind.trigger(this.root, OmniboxEvents_1.OmniboxEvents.populateOmniboxSuggestions, args); } }; Omnibox.prototype.handleBeforeRedirect = function () { this.updateQueryState(); }; Omnibox.prototype.handleBuildingQuery = function (data) { var _this = this; Assert_1.Assert.exists(data); Assert_1.Assert.exists(data.queryBuilder); this.updateQueryState(); this.lastQuery = this.getQuery(data.searchAsYouType); var result = this.lastQuery == this.magicBox.getDisplayedResult().input ? this.magicBox.getDisplayedResult().clone() : this.magicBox.grammar.parse(this.lastQuery).clean(); var preprocessResultForQueryArgs = { result: result }; if (this.options.enableQuerySyntax) { var notQuotedValues = preprocessResultForQueryArgs.result.findAll('FieldValueNotQuoted'); _.each(notQuotedValues, function (value) { return (value.value = '"' + value.value.replace(/"|\u00A0/g, ' ') + '"'); }); if (this.options.fieldAlias) { var fieldNames = preprocessResultForQueryArgs.result.findAll(function (result) { return result.expression.id == 'FieldName' && result.isSuccess(); }); _.each(fieldNames, function (result) { var alias = _.find(_.keys(_this.options.fieldAlias), function (alias) { return alias.toLowerCase() == result.value.toLowerCase(); }); if (alias != null) { result.value = _this.options.fieldAlias[alias]; } }); } } this.triggerOmniboxPreprocessResultForQuery(preprocessResultForQueryArgs); var query = preprocessResultForQueryArgs.result.toString(); new QueryboxQueryParameters_1.QueryboxQueryParameters(this.options).addParameters(data.queryBuilder, query); }; Omnibox.prototype.triggerOmniboxPreprocessResultForQuery = function (args) { this.bind.trigger(this.element, OmniboxEvents_1.OmniboxEvents.omniboxPreprocessResultForQuery, args); if (!Dom_1.$$(this.element).isDescendant(this.root)) { this.bind.trigger(this.root, OmniboxEvents_1.OmniboxEvents.omniboxPreprocessResultForQuery, args); } }; Omnibox.prototype.handleNewQuery = function (data) { Assert_1.Assert.exists(data); this.options.clearFiltersOnNewQuery && this.clearFiltersIfNewQuery(data); }; Omnibox.prototype.clearFiltersIfNewQuery = function (_a) { var origin = _a.origin, searchAsYouType = _a.searchAsYouType; if (this.queryController.firstQuery) { return; } // Prevent queries triggered by unrelated components to clear the the filters // e.g., a facet selection var validOrigins = [Omnibox.ID, 'SearchButton']; if (!origin || validOrigins.indexOf(origin.type) === -1) { return; } var lastQuery = this.queryController.getLastQuery().q || ''; var newQuery = this.getQuery(searchAsYouType); if (lastQuery !== newQuery) { this.bind.trigger(this.root, BreadcrumbEvents_1.BreadcrumbEvents.clearBreadcrumb); } }; Omnibox.prototype.handleTabPress = function () { if (this.options.enableQuerySuggestAddon) { this.handleTabPressForSuggestions(); } this.handleTabPressForOldOmniboxAddon(); }; Omnibox.prototype.handleTabPressForSuggestions = function () { if (!this.options.enableSearchAsYouType) { var suggestions = _.compact(_.map(this.lastSuggestions, function (suggestion) { return suggestion.text; })); this.usageAnalytics.logCustomEvent(this.getOmniboxAnalyticsEventCause(), this.buildCustomDataForPartialQueries(0, suggestions), this.element); } else { this.setText(this.getQuery(true)); } }; Omnibox.prototype.handleTabPressForOldOmniboxAddon = function () { var domSuggestions = this.lastSuggestions.filter(function (suggestions) { return suggestions.dom; }).map(function (suggestions) { return Dom_1.$$(suggestions.dom); }); var selected = this.findAllElementsWithClass(domSuggestions, '.coveo-omnibox-selected'); if (selected.length > 0) { Dom_1.$$(selected[0]).trigger('tabSelect'); } else if (!this.options.enableQuerySuggestAddon) { var selectable = this.findAllElementsWithClass(domSuggestions, '.coveo-omnibox-selectable'); if (selectable.length > 0) { Dom_1.$$(selectable[0]).trigger('tabSelect'); } } }; Omnibox.prototype.findAllElementsWithClass = function (elements, className) { return elements .map(function (element) { return element.find(className); }) .filter(function (s) { return s; }) .reduce(function (total, s) { return total.concat(s); }, []); }; Omnibox.prototype.triggerNewQuery = function (searchAsYouType, analyticsEvent) { clearTimeout(this.searchAsYouTypeTimeout); var shouldExecuteQuery = this.shouldExecuteQuery(searchAsYouType); this.lastQuery = this.getQuery(searchAsYouType)