coveo-search-ui
Version:
Coveo JavaScript Search Framework
1,029 lines (1,011 loc) • 223 kB
JavaScript
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)