UNPKG

coveo-search-ui

Version:

Coveo JavaScript Search Framework

1,105 lines (1,067 loc) • 135 kB
webpackJsonpCoveo__temporary([11],{ /***/ 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; /***/ }), /***/ 218: /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ExpressionEndOfInput = { id: 'end of input', parse: null }; /***/ }), /***/ 219: /***/ (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); var ExpressionConstant = /** @class */ (function () { function ExpressionConstant(value, id) { this.value = value; this.id = id; } ExpressionConstant.prototype.parse = function (input, end) { // the value must be at the start of the input var success = input.indexOf(this.value) == 0; var result = new Result_1.Result(success ? this.value : null, this, input); // if is successful and we require the end but the length of parsed is // lower than the input length, return a EndOfInputExpected Result if (success && end && input.length > this.value.length) { return new Result_2.EndOfInputResult(result); } return result; }; ExpressionConstant.prototype.toString = function () { return this.value; }; return ExpressionConstant; }()); exports.ExpressionConstant = ExpressionConstant; /***/ }), /***/ 220: /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var Basic_1 = __webpack_require__(133); var Date_1 = __webpack_require__(501); exports.Field = { basicExpressions: ['FieldSimpleQuery', 'FieldQuery', 'Field'], grammars: { FieldQuery: '[Field][OptionalSpaces][FieldQueryOperation]', FieldQueryOperation: ['FieldQueryValue', 'FieldQueryNumeric'], FieldQueryValue: '[FieldOperator][OptionalSpaces][FieldValue]', FieldQueryNumeric: '[FieldOperatorNumeric][OptionalSpaces][FieldValueNumeric]', FieldSimpleQuery: '[FieldName]:[OptionalSpaces][FieldValue]', Field: '@[FieldName]', FieldName: /[a-zA-Z][a-zA-Z0-9\.\_]*/, FieldOperator: /==|=|<>/, FieldOperatorNumeric: /<=|>=|<|>/, FieldValue: ['DateRange', 'NumberRange', 'DateRelative', 'Date', 'Number', 'FieldValueList', 'FieldValueString'], FieldValueNumeric: ['DateRelative', 'Date', 'Number'], FieldValueString: ['DoubleQuoted', 'FieldValueNotQuoted'], FieldValueList: '([FieldValueString][FieldValueStringList*])', FieldValueStringList: '[FieldValueSeparator][FieldValueString]', FieldValueSeparator: / *, */, FieldValueNotQuoted: /[^ \(\)\[\],]+/, NumberRange: '[Number][Spaces?]..[Spaces?][Number]' }, include: [Date_1.Date, Basic_1.Basic] }; /***/ }), /***/ 221: /***/ (function(module, exports, __webpack_require__) { "use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [0, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; Object.defineProperty(exports, "__esModule", { value: true }); var underscore_1 = __webpack_require__(0); var OmniboxEvents_1 = __webpack_require__(34); var Component_1 = __webpack_require__(7); var Dom_1 = __webpack_require__(1); var ResultPreviewsManager_1 = __webpack_require__(564); var QueryProcessor_1 = __webpack_require__(505); var QueryUtils_1 = __webpack_require__(21); var Direction; (function (Direction) { Direction["Up"] = "Up"; Direction["Down"] = "Down"; Direction["Left"] = "Left"; Direction["Right"] = "Right"; })(Direction = exports.Direction || (exports.Direction = {})); var SuggestionsManager = /** @class */ (function () { function SuggestionsManager(element, magicBoxContainer, inputManager, options) { var _this = this; this.element = element; this.magicBoxContainer = magicBoxContainer; this.inputManager = inputManager; this.suggestionListboxID = "coveo-magicbox-suggestions-" + QueryUtils_1.QueryUtils.createGuid(); this.suggestionListboxClassName = "coveo-magicbox-suggestions"; this.root = Component_1.Component.resolveRoot(element); this.options = underscore_1.defaults(options, { suggestionClass: 'magic-box-suggestion', selectedClass: 'magic-box-selected' }); // Put in a sane default, so as to not reject every suggestions if not set on initialization if (this.options.timeout == undefined) { this.options.timeout = 500; } Dom_1.$$(this.element).on('mouseover', function (e) { _this.handleMouseOver(e); }); Dom_1.$$(this.element).on('mouseout', function (e) { _this.handleMouseOut(e); }); this.suggestionsProcessor = new QueryProcessor_1.QueryProcessor({ timeout: this.options.timeout }); this.resultPreviewsManager = new ResultPreviewsManager_1.ResultPreviewsManager(element, { selectedClass: this.options.selectedClass, timeout: this.options.timeout }); this.suggestionsListbox = this.buildSuggestionsContainer(); Dom_1.$$(this.element).append(this.suggestionsListbox.el); this.addAccessibilityProperties(); this.appendEmptySuggestionOption(); } Object.defineProperty(SuggestionsManager.prototype, "hasSuggestions", { get: function () { return this.currentSuggestions && this.currentSuggestions.length > 0; }, enumerable: true, configurable: true }); Object.defineProperty(SuggestionsManager.prototype, "hasFocus", { get: function () { return Dom_1.$$(this.element).findClass(this.options.selectedClass).length > 0; }, enumerable: true, configurable: true }); Object.defineProperty(SuggestionsManager.prototype, "hasPreviews", { get: function () { return this.resultPreviewsManager.hasPreviews; }, enumerable: true, configurable: true }); Object.defineProperty(SuggestionsManager.prototype, "focusedSuggestion", { get: function () { var _this = this; return underscore_1.find(this.currentSuggestions, function (suggestion) { return Dom_1.$$(suggestion.dom).hasClass(_this.options.selectedClass) || Dom_1.$$(suggestion.dom).findClass(_this.options.selectedClass).length > 0; }); }, enumerable: true, configurable: true }); SuggestionsManager.prototype.handleMouseOver = function (e) { var target = Dom_1.$$(e.target); var parents = target.parents(this.options.suggestionClass); if (target.hasClass(this.options.suggestionClass)) { this.processMouseSelection(target.el); } else if (parents.length > 0 && this.element.contains(parents[0])) { this.processMouseSelection(parents[0]); } }; SuggestionsManager.prototype.handleMouseOut = function (e) { var target = Dom_1.$$(e.target); var targetParents = target.parents(this.options.suggestionClass); //e.relatedTarget is not available if moving off the browser window or is an empty object `{}` when moving out of namespace in LockerService. if (e.relatedTarget && Dom_1.$$(e.relatedTarget).isValid()) { var relatedTargetParents = Dom_1.$$(e.relatedTarget).parents(this.options.suggestionClass); if (target.hasClass(this.options.selectedClass) && !Dom_1.$$(e.relatedTarget).hasClass(this.options.suggestionClass)) { this.removeSelectedStatus(target.el); } else if (relatedTargetParents.length == 0 && targetParents.length > 0) { this.removeSelectedStatus(targetParents[0]); } } else { if (target.hasClass(this.options.selectedClass)) { this.removeSelectedStatus(target.el); } else if (targetParents.length > 0) { this.removeSelectedStatus(targetParents[0]); } } Dom_1.$$(this.root).trigger(OmniboxEvents_1.OmniboxEvents.querySuggestLoseFocus); }; SuggestionsManager.prototype.moveDown = function () { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.move(Direction.Down)]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; SuggestionsManager.prototype.moveUp = function () { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.move(Direction.Up)]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; SuggestionsManager.prototype.moveLeft = function () { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.move(Direction.Left)]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; SuggestionsManager.prototype.moveRight = function () { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.move(Direction.Right)]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; SuggestionsManager.prototype.selectAndReturnKeyboardFocusedElement = function () { var selected = this.keyboardFocusedElement; if (selected) { Dom_1.$$(selected).trigger('keyboardSelect'); // By definition, once an element has been "selected" with the keyboard, // it is not longer "active" since the event has been processed. this.keyboardFocusedElement = null; this.inputManager.blur(); } return selected; }; SuggestionsManager.prototype.clearKeyboardFocusedElement = function () { this.keyboardFocusedElement = null; }; SuggestionsManager.prototype.receiveSuggestions = function (suggestions) { return __awaiter(this, void 0, void 0, function () { var _a, results, status; return __generator(this, function (_b) { switch (_b.label) { case 0: return [4 /*yield*/, this.suggestionsProcessor.processQueries(suggestions)]; case 1: _a = _b.sent(), results = _a.results, status = _a.status; if (status === QueryProcessor_1.ProcessingStatus.Overriden) { return [2 /*return*/, []]; } this.updateSuggestions(results); return [2 /*return*/, results]; } }); }); }; SuggestionsManager.prototype.clearSuggestions = function () { this.updateSuggestions([]); }; SuggestionsManager.prototype.updateSuggestions = function (suggestions) { var _this = this; this.suggestionsListbox.empty(); this.inputManager.activeDescendant = null; this.currentSuggestions = suggestions; Dom_1.$$(this.element).toggleClass('magic-box-hasSuggestion', this.hasSuggestions); this.inputManager.expanded = this.hasSuggestions; this.resultPreviewsManager.displaySearchResultPreviewsForSuggestion(null); if (!this.hasSuggestions) { this.appendEmptySuggestionOption(); Dom_1.$$(this.root).trigger(OmniboxEvents_1.OmniboxEvents.querySuggestLoseFocus); return; } suggestions .sort(function (a, b) { return (b.index || 0) - (a.index || 0); }) .forEach(function (suggestion) { var dom = suggestion.dom ? _this.modifyDomFromExistingSuggestion(suggestion.dom) : _this.createDomFromSuggestion(suggestion); dom.setAttribute('id', "magic-box-suggestion-" + underscore_1.indexOf(suggestions, suggestion)); dom.setAttribute('role', 'option'); dom.setAttribute('aria-selected', 'false'); dom.setAttribute('aria-label', dom.text()); dom['suggestion'] = suggestion; _this.suggestionsListbox.append(dom.el); }); var numberOfSuggestions = this.suggestionsListbox.findAll("." + this.options.suggestionClass).length; Dom_1.$$(this.root).trigger(OmniboxEvents_1.OmniboxEvents.querySuggestRendered, { numberOfSuggestions: numberOfSuggestions }); }; Object.defineProperty(SuggestionsManager.prototype, "selectedSuggestion", { get: function () { if (this.htmlElementIsSuggestion(this.keyboardFocusedElement)) { return this.returnMoved(this.keyboardFocusedElement); } return null; }, enumerable: true, configurable: true }); SuggestionsManager.prototype.processKeyboardSelection = function (suggestion) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: this.addSelectedStatus(suggestion); this.keyboardFocusedElement = suggestion; return [4 /*yield*/, this.updateSelectedSuggestion(this.focusedSuggestion)]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; SuggestionsManager.prototype.processKeyboardPreviewSelection = function (preview) { this.addSelectedStatus(preview); this.keyboardFocusedElement = preview; }; SuggestionsManager.prototype.processMouseSelection = function (suggestion) { this.addSelectedStatus(suggestion); this.updateSelectedSuggestion(this.focusedSuggestion); this.keyboardFocusedElement = null; }; SuggestionsManager.prototype.buildSuggestionsContainer = function () { return Dom_1.$$('div', { className: this.suggestionListboxClassName, id: this.suggestionListboxID, role: 'listbox', ariaLabel: 'Search Suggestions' }); }; SuggestionsManager.prototype.createDomFromSuggestion = function (suggestion) { var _this = this; var dom = Dom_1.$$('div', { className: "magic-box-suggestion " + this.options.suggestionClass }); suggestion.dom = dom.el; dom.on('click', function () { _this.selectSuggestion(suggestion); }); dom.on('keyboardSelect', function () { _this.selectSuggestion(suggestion); }); if (suggestion.html) { dom.el.innerHTML = suggestion.html; return dom; } if (suggestion.text) { dom.text(suggestion.text); return dom; } if (suggestion.separator) { dom.addClass('magic-box-suggestion-seperator'); var suggestionLabel = Dom_1.$$('div', { className: 'magic-box-suggestion-seperator-label' }, suggestion.separator); dom.append(suggestionLabel.el); return dom; } return dom; }; SuggestionsManager.prototype.selectSuggestion = function (suggestion) { suggestion.onSelect(); Dom_1.$$(this.root).trigger(OmniboxEvents_1.OmniboxEvents.querySuggestSelection, { suggestion: suggestion.text }); }; SuggestionsManager.prototype.appendEmptySuggestionOption = function () { // Accessibility tools reports that a listbox element must always have at least one child with an option // Even if there is no suggestions to show. this.suggestionsListbox.append(Dom_1.$$('div', { role: 'option' }).el); }; SuggestionsManager.prototype.modifyDomFromExistingSuggestion = function (dom) { // this need to be done if the selection is in cache and the dom is set in the suggestion this.removeSelectedStatus(dom); var found = dom.classList.contains(this.options.suggestionClass) ? dom : Dom_1.$$(dom).find('.' + this.options.suggestionClass); this.removeSelectedStatus(found); return Dom_1.$$(dom); }; SuggestionsManager.prototype.move = function (direction) { return __awaiter(this, void 0, void 0, function () { var firstPreview; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!this.resultPreviewsManager.focusedPreviewElement) return [3 /*break*/, 2]; return [4 /*yield*/, this.moveWithinPreview(direction)]; case 1: _a.sent(); return [2 /*return*/]; case 2: if (direction === Direction.Right || direction === Direction.Left) { firstPreview = this.resultPreviewsManager.previewElements[0]; if (firstPreview) { this.processKeyboardPreviewSelection(firstPreview); return [2 /*return*/]; } } return [4 /*yield*/, this.moveWithinSuggestion(direction)]; case 3: _a.sent(); return [2 /*return*/]; } }); }); }; SuggestionsManager.prototype.moveWithinSuggestion = function (direction) { return __awaiter(this, void 0, void 0, function () { var currentlySelected, selectables, currentIndex, index; return __generator(this, function (_a) { switch (_a.label) { case 0: currentlySelected = Dom_1.$$(this.element).find("." + this.options.selectedClass); selectables = Dom_1.$$(this.element).findAll("." + this.options.suggestionClass); currentIndex = underscore_1.indexOf(selectables, currentlySelected); index = direction === Direction.Up ? currentIndex - 1 : currentIndex + 1; index = (index + selectables.length) % selectables.length; return [4 /*yield*/, this.selectQuerySuggest(selectables[index])]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; SuggestionsManager.prototype.selectQuerySuggest = function (suggestion) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: if (!suggestion) return [3 /*break*/, 2]; return [4 /*yield*/, this.processKeyboardSelection(suggestion)]; case 1: _a.sent(); return [3 /*break*/, 3]; case 2: this.keyboardFocusedElement = null; this.inputManager.input.removeAttribute('aria-activedescendant'); _a.label = 3; case 3: return [2 /*return*/, suggestion]; } }); }); }; SuggestionsManager.prototype.moveWithinPreview = function (direction) { return __awaiter(this, void 0, void 0, function () { var newFocusedPreview; return __generator(this, function (_a) { switch (_a.label) { case 0: newFocusedPreview = this.resultPreviewsManager.getElementInDirection(direction); if (!!newFocusedPreview) return [3 /*break*/, 2]; return [4 /*yield*/, this.selectQuerySuggest(this.resultPreviewsManager.previewsOwner.dom)]; case 1: _a.sent(); return [2 /*return*/]; case 2: this.processKeyboardPreviewSelection(newFocusedPreview); return [2 /*return*/]; } }); }); }; SuggestionsManager.prototype.returnMoved = function (selected) { if (selected) { if (selected['suggestion']) { return selected['suggestion']; } if (selected['no-text-suggestion']) { return null; } if (selected instanceof HTMLElement) { return { text: Dom_1.$$(selected).text() }; } } return null; }; SuggestionsManager.prototype.addSelectedStatus = function (element) { var selected = this.element.getElementsByClassName(this.options.selectedClass); for (var i = 0; i < selected.length; i++) { var elem = selected.item(i); this.removeSelectedStatus(elem); } Dom_1.$$(element).addClass(this.options.selectedClass); this.updateAreaSelectedIfDefined(element, 'true'); }; SuggestionsManager.prototype.updateSelectedSuggestion = function (suggestion) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: Dom_1.$$(this.root).trigger(OmniboxEvents_1.OmniboxEvents.querySuggestGetFocus, { suggestion: suggestion.text }); return [4 /*yield*/, this.resultPreviewsManager.displaySearchResultPreviewsForSuggestion(suggestion)]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; SuggestionsManager.prototype.removeSelectedStatus = function (suggestion) { Dom_1.$$(suggestion).removeClass(this.options.selectedClass); this.updateAreaSelectedIfDefined(suggestion, 'false'); }; SuggestionsManager.prototype.updateAreaSelectedIfDefined = function (element, value) { if (Dom_1.$$(element).getAttribute('aria-selected')) { this.inputManager.activeDescendant = element; Dom_1.$$(element).setAttribute('aria-selected', value); } }; SuggestionsManager.prototype.addAccessibilityProperties = function () { this.addAccessibilityPropertiesForMagicBox(); this.addAccessibilityPropertiesForInput(); }; SuggestionsManager.prototype.addAccessibilityPropertiesForMagicBox = function () { var magicBox = Dom_1.$$(this.magicBoxContainer); magicBox.setAttribute('role', 'search'); magicBox.setAttribute('aria-haspopup', 'listbox'); }; SuggestionsManager.prototype.addAccessibilityPropertiesForInput = function () { var input = Dom_1.$$(this.inputManager.input); this.inputManager.activeDescendant = null; this.inputManager.expanded = false; input.setAttribute('aria-owns', this.suggestionListboxID); input.setAttribute('aria-controls', this.suggestionListboxID); }; SuggestionsManager.prototype.htmlElementIsSuggestion = function (selected) { var omniboxSelectables = Dom_1.$$(this.element).findAll("." + this.options.suggestionClass); return underscore_1.indexOf(omniboxSelectables, selected) > -1; }; return SuggestionsManager; }()); exports.SuggestionsManager = SuggestionsManager; /***/ }), /***/ 222: /***/ (function(module, exports, __webpack_require__) { "use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulf