UNPKG

@sajari/sdk-react

Version:
1,360 lines (1,327 loc) 142 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var Cookies = _interopDefault(require('js-cookie')); var sdkJs = require('@sajari/sdk-js'); var React = require('react'); var React__default = _interopDefault(React); var emotionTheming = require('emotion-theming'); var reactAriaLive = require('react-aria-live'); var memoize = _interopDefault(require('fast-memoize')); var Downshift = _interopDefault(require('downshift')); var i18next = _interopDefault(require('i18next')); var LngDetector = _interopDefault(require('i18next-browser-languagedetector')); var core = require('@emotion/core'); var classnames = _interopDefault(require('classnames')); var styled = _interopDefault(require('@emotion/styled')); var chroma = _interopDefault(require('chroma-js')); var ReactSelect = _interopDefault(require('react-select')); var rcSlider = require('rc-slider'); var Listener = /** @class */ (function () { /** * Constructs a listener object. */ function Listener() { this.listeners = []; } /** * Adds a callback to the listener. * Returns a function that will unregister the callback from the listener when called. * @param callback The callback to be registered. * @return The unregister function to remove the callback from the listener. */ Listener.prototype.listen = function (callback) { var _this = this; this.listeners.push(callback); return function () { return _this.unlisten(callback); }; }; /** * Removes a callback from the listener. */ Listener.prototype.unlisten = function (callback) { var index = this.listeners.indexOf(callback); if (index >= 0) { this.listeners.splice(index, 1); } }; /** * Notify takes a function and calls it for every listener. * The listener is supplied as the first argument to the function. * @param {function(callback: Function)} fn Function to call each of the callbacks in the listener with. */ Listener.prototype.notify = function (fn) { this.listeners.forEach(function (l) { try { fn(l); } catch (e) { // tslint:disable-next-line no-console if (console && console.error) { // tslint:disable-next-line no-console console.error(e); } } }); }; return Listener; }()); var EVENT_SEARCH_SENT = "search-sent"; var EVENT_RESPONSE_UPDATED = "response-updated"; var EVENT_RESULT_CLICKED = "result-clicked"; var EVENT_VALUES_UPDATED = "values-changed"; var EVENT_TRACKING_RESET = "tracking-reset"; var EVENT_ANALYTICS_PAGE_CLOSED = "page-close-analytics"; var EVENT_ANALYTICS_BODY_RESET = "body-reset-analytics"; var EVENT_ANALYTICS_RESULT_CLICKED = "result-clicked-analytics"; var EVENT_SELECTION_UPDATED = "selection-updated"; var EVENT_OPTIONS_UPDATED = "options-updated"; var events = [ EVENT_ANALYTICS_PAGE_CLOSED, EVENT_ANALYTICS_BODY_RESET, EVENT_ANALYTICS_RESULT_CLICKED ]; /** * Analytics is an adaptor which listens for events on Pipeline and * Tracking and re-emits them as analytics-based events. */ var Analytics = /** @class */ (function () { /** * Constructs an analytics object that operates on the specified pipeline. */ function Analytics(pipeline, tracking) { var _a; var _this = this; /** * Runs before the page is closed/navigated away from. Can trigger a ga onPageClose call. */ this.beforeunload = function () { if (_this.enabled && _this.body) { _this.listeners.get(EVENT_ANALYTICS_PAGE_CLOSED).notify(function (callback) { callback(_this.body); }); _this.enabled = false; // TODO(tbillington): unload -> disable!! } }; /** * Resets the currently held parameters. Can trigger a ga onBodyReset call. */ this.resetBody = function () { if (_this.enabled) { _this.listeners.get(EVENT_ANALYTICS_BODY_RESET).notify(function (callback) { callback(_this.body); }); _this.longestNonAutocompletedBody = ""; _this.longestAutocompletedBody = ""; _this.enabled = false; } }; /** * Runs when the response has been updated. Updates the currently held search parameters. */ this.responseUpdated = function (response) { if (response.isEmpty() || response.isError()) { return; } _this.enabled = true; var originalBody = response.getQueryValues().get(_this.bodyLabel) || ""; var responseBody = response.getValues().get(_this.bodyAutocompletedLabel) || originalBody; _this.body = responseBody; // Here we check the lengths of the non-autocompleted bodies. // We do this because while the user is backspacing their query // the new autocompleted body may be longer than their actual input, // but we want to record their input rather than a completion resulting // from them removing chars. if (originalBody.length >= _this.longestNonAutocompletedBody.length) { _this.longestNonAutocompletedBody = originalBody; _this.longestAutocompletedBody = responseBody; } }; /** * Runs when a result has been clicked. Can trigger a ga onResultClicked call. */ this.resultClicked = function () { if (_this.enabled && _this.body) { _this.listeners.get(EVENT_ANALYTICS_RESULT_CLICKED).notify(function (callback) { callback(_this.body); }); _this.longestNonAutocompletedBody = ""; _this.longestAutocompletedBody = ""; _this.enabled = false; } }; this.enabled = false; this.body = ""; this.pipeline = pipeline; this.tracking = tracking; this.listeners = new Map(Object.entries((_a = {}, _a[EVENT_ANALYTICS_PAGE_CLOSED] = new Listener(), _a[EVENT_ANALYTICS_BODY_RESET] = new Listener(), _a[EVENT_ANALYTICS_RESULT_CLICKED] = new Listener(), _a))); // longest values are for sending the users last intended query on reset this.longestNonAutocompletedBody = ""; this.longestAutocompletedBody = ""; // default to working with website pipeline values this.bodyLabel = "q"; this.bodyAutocompletedLabel = "q"; window.addEventListener("beforeunload", this.beforeunload); this.pipeline.listen(EVENT_RESPONSE_UPDATED, this.responseUpdated); this.pipeline.listen(EVENT_RESULT_CLICKED, this.resultClicked); this.tracking.listen(EVENT_TRACKING_RESET, this.resetBody); } /** * Register a listener for a specific event. * @param event Event to listen for * @param callback Callback to run when the event happens. * @return The unregister function to remove the callback from the listener. */ Analytics.prototype.listen = function (event, callback) { if (events.indexOf(event) === -1) { throw new Error("unknown event type \"" + event + "\""); } return this.listeners.get(event).listen(callback); }; return Analytics; }()); var GoogleAnalyticsObjects; (function (GoogleAnalyticsObjects) { GoogleAnalyticsObjects["UniversalAnalytics"] = "_ua"; GoogleAnalyticsObjects["AnalyticsJS"] = "ga"; GoogleAnalyticsObjects["GTag"] = "gtag"; })(GoogleAnalyticsObjects || (GoogleAnalyticsObjects = {})); var GoogleAnalytics = /** @class */ (function () { /** * Constructs a GoogleAnalytics object. * @param {Analytics} analytics The analytics object to attach to. * @param {string} [id=undefined] The name of the ga global object. Defaults to "ga" or "_ua" if one isn't supplied. * @param {string} [param="q"] The URL parameter to use to indicate a search. Default to "q". */ function GoogleAnalytics(analytics, id, param) { var _this = this; if (param === void 0) { param = "q"; } this.unregisterFunctions = []; /** * Stops this object listening for events. */ this.detatch = function () { return _this.unregisterFunctions.forEach(function (fn) { return fn(); }); }; /** * Callback for when the body has been reset. Calls sendGAPageView. */ this.onBodyReset = function (body) { return _this.sendGAPageView(body); }; /** * Callback for when a result has been clicked. Calls sendGAPageView. */ this.onResultClicked = function (body) { return _this.sendGAPageView(body); }; /** * Callback for when the page has been closed. Calls sendGAPageView. */ this.onPageClose = function (body) { return _this.sendGAPageView(body); }; this.unregisterFunctions.push(analytics.listen(EVENT_ANALYTICS_PAGE_CLOSED, this.onPageClose)); this.unregisterFunctions.push(analytics.listen(EVENT_ANALYTICS_BODY_RESET, this.onBodyReset)); this.unregisterFunctions.push(analytics.listen(EVENT_ANALYTICS_RESULT_CLICKED, this.onResultClicked)); if (id !== undefined) { this.id = id; } else if (isFunction(window[GoogleAnalyticsObjects.AnalyticsJS])) { this.id = GoogleAnalyticsObjects.AnalyticsJS; } else if (isFunction(window[GoogleAnalyticsObjects.UniversalAnalytics])) { this.id = GoogleAnalyticsObjects.UniversalAnalytics; } else if (isFunction(window[GoogleAnalyticsObjects.GTag])) { this.id = GoogleAnalyticsObjects.GTag; } else { this.id = null; } this.param = param; } /** * Sends a page view event if ga is found on the page and we're not in dev mode. */ GoogleAnalytics.prototype.sendGAPageView = function (body) { var _a; // @ts-ignore: window is an object if (this.id && isFunction(window[this.id])) { // Merge the body in with the existing query params in the url var pageAddress = url.augmentUri( // Take only the portion of the url following the domain location.href.substring(location.origin.length), (_a = {}, _a[this.param] = body, _a)); if (this.id === GoogleAnalyticsObjects.GTag) { window[this.id]("event", "page_view", { page_location: pageAddress }); } else { window[this.id]("send", "pageview", pageAddress); } } }; return GoogleAnalytics; }()); var url = { /** * Convert a query string in to an object */ decodeUriArgs: function (queryStr) { var args = {}; var a = queryStr.split("&"); for (var i in a) { if (a.hasOwnProperty(i)) { var b = a[i].split("="); args[decodeURIComponent(b[0])] = decodeURIComponent(b[1]); } } return args; }, /** * Convert an arguments object in to a query string */ encodeUriArgs: function (args) { var queryParts = []; Object.keys(args).forEach(function (key) { return queryParts.push(encodeURIComponent(key) + "=" + encodeURIComponent(args[key])); }); return queryParts.join("&"); }, /** * Merges query strings or objects into a single query string. Accepts a variable number of query string/objects * to merge. The latter overrides the former. */ mergeQueryStr: function (first) { var _this = this; var rest = []; for (var _i = 1; _i < arguments.length; _i++) { rest[_i - 1] = arguments[_i]; } var args = typeof first === "string" ? this.decodeUriArgs(first) : first; rest.forEach(function (arg) { var next = typeof arg === "string" ? _this.decodeUriArgs(arg) : arg; Object.keys(next).forEach(function (prop) { return (args[prop] = next[prop]); }); }); return this.encodeUriArgs(args); }, /** * Takes an existing URL and merges additional data into the query string */ augmentUri: function (uri, args) { var m = /^([^?]+)\?(.+)+$/.exec(uri); if (m) { return m[1] + "?" + this.mergeQueryStr(m[2], args); } else { return uri + "?" + this.encodeUriArgs(args); } }, /** * Get a parameter from the URL */ getURLParameter: function (name) { var value = new RegExp("[?|&]" + name + "=" + "([^&;]+?)(&|#|;|$)").exec(location.search) || [undefined, ""]; return (decodeURIComponent(value[1].replace(/\+/g, "%20")) || null); } }; var isFunction = function (x) { return typeof x === "function"; }; var DebugAnalytics = /** @class */ (function () { /** * * @param {Analytics} analytics Analytics adaptor to listen for events on. */ function DebugAnalytics(analytics) { this.pageClosed = function (bodyToSend) { // tslint:disable-next-line no-console console.log("DebugAnalytics: pageClosed, body:", bodyToSend); }; this.bodyReset = function (bodyToSend) { // tslint:disable-next-line no-console console.log("DebugAnalytics: bodyReset, body:", bodyToSend); }; this.resultClicked = function (bodyToSend) { // tslint:disable-next-line no-console console.log("DebugAnalytics: resultClicked, body:", bodyToSend); }; analytics.listen(EVENT_ANALYTICS_PAGE_CLOSED, this.pageClosed); analytics.listen(EVENT_ANALYTICS_BODY_RESET, this.bodyReset); analytics.listen(EVENT_ANALYTICS_RESULT_CLICKED, this.resultClicked); } return DebugAnalytics; }()); var Response = /** @class */ (function () { /** * Constructs a Response object. * @param error * @param queryValues * @param response * @param values */ function Response(error, queryValues, response, values) { this.error = error; this.queryValues = queryValues; this.response = response; this.values = values; } /** * Is this response empty? */ Response.prototype.isEmpty = function () { return (this.error === null && this.response === undefined && this.values === undefined && this.queryValues === undefined); }; /** * Is this response an error? */ Response.prototype.isError = function () { return this.error !== null; }; /** * The error associated with this response. */ Response.prototype.getError = function () { return this.error; }; /** * Return the query values used in the search which created this response. */ Response.prototype.getQueryValues = function () { return this.queryValues; }; /** * Returns the response, which includes results and aggregates etc. */ Response.prototype.getResponse = function () { return this.response; }; /** * Return the pipeline values returned by the search. */ Response.prototype.getValues = function () { return this.values; }; /** * Return results from the response. */ Response.prototype.getResults = function () { return this.response !== undefined ? this.response.get("results") : undefined; }; /** * Return the total number of results. */ Response.prototype.getTotalResults = function () { return this.response !== undefined ? this.response.get("totalResults") : undefined; }; /** * Return time from the response. */ Response.prototype.getTime = function () { return this.response !== undefined ? this.response.get("time") : undefined; }; /** * Return the aggregates in the response. */ Response.prototype.getAggregates = function () { if (this.response === undefined) { return undefined; } var aggregates = this.response.get("aggregates"); if (aggregates === undefined) { return undefined; } return aggregates; }; /** * Return the aggregateFilters in the response. */ Response.prototype.getAggregateFilters = function () { if (this.response === undefined) { return undefined; } var aggregates = this.response.get("aggregateFilters"); if (aggregates === undefined) { return undefined; } return aggregates; }; return Response; }()); var events$1 = [EVENT_TRACKING_RESET]; var Tracking = /** @class */ (function () { function Tracking() { var _a; this.clientTracking = null; this.listeners = new Map(Object.entries((_a = {}, _a[EVENT_TRACKING_RESET] = new Listener(), _a))); } /** * Register a listener for a specific event. * @param event Event to listen for * @param callback Callback to run when the event happens. * @return The unregister function to remove the callback from the listener. */ Tracking.prototype.listen = function (event, callback) { if (events$1.indexOf(event) === -1) { throw new Error("unknown event type \"" + event + "\""); } return this.listeners.get(event).listen(callback); }; /** * Emits a tracking reset event to the tracking reset event listener. * @private */ Tracking.prototype._emitTrackingReset = function (values) { this.listeners.get(EVENT_TRACKING_RESET).notify(function (listener) { listener(values); }); }; /** * Reset the tracking. * @param values Key-value pair parameters to use in the pipeline. */ Tracking.prototype.reset = function (values) { throw new Error("method 'reset' unimplemented"); }; /** * Tracking returns the tracking data to be attached to the pipeline request. * @param values Key-value pair parameters to use in the pipeline. * @return Tracking values to be used in the search request. */ Tracking.prototype.next = function (values) { throw new Error("method 'next' unimplemented"); }; return Tracking; }()); /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = function(d, b) { 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 extendStatics(d, b); }; function __extends(d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function() { __assign = Object.assign || function __assign(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; }; return __assign.apply(this, arguments); }; function __rest(s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; } function __makeTemplateObject(cooked, raw) { if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } return cooked; } // @ts-ignore: module missing defintion file var getTrackingData = function () { var data = {}; var ga = Cookies.get("_ga"); if (ga) { data.ga = ga; } var sjID = Cookies.get("sjID"); if (sjID) { data.sjID = sjID; } return data; }; var ClickTracking = /** @class */ (function (_super) { __extends(ClickTracking, _super); /** * Construct a ClickTracking instance. * * @param field Field to use for click token generation. * @param qParam Value to use for full-text query param. */ function ClickTracking(field, qParam) { if (field === void 0) { field = "url"; } if (qParam === void 0) { qParam = "q"; } var _this = _super.call(this) || this; _this.field = field; _this.qParam = qParam; _this.clientTracking = new sdkJs.InteractiveSession(qParam, new sdkJs.DefaultSession(sdkJs.TrackingType.Click, field, getTrackingData())); _this.prevQ = ""; return _this; } /** * Reset the tracking. * @param values Key-value pair parameters to use in the pipeline. */ ClickTracking.prototype.reset = function (values) { this.clientTracking.reset(); if (values !== undefined) { this._emitTrackingReset(values); } }; /** * Construct a tracking session to be used in a search. * * @param values Key-value pair parameters to use in the pipeline. */ ClickTracking.prototype.next = function (values) { if (this.clientTracking === null) { throw new Error("clientTracking is null"); } return this.clientTracking.next(values); }; return ClickTracking; }(Tracking)); var NoTracking = /** @class */ (function (_super) { __extends(NoTracking, _super); /** * Construct a NoTracking instance. */ function NoTracking() { var _this = _super.call(this) || this; _this.clientTracking = new sdkJs.DefaultSession(sdkJs.TrackingType.None, "_id", getTrackingData()); return _this; } /** * Reset the tracking. * @param values Key-value pair parameters to use in the pipeline. */ NoTracking.prototype.reset = function (values) { this.clientTracking.reset(); if (values !== undefined) { this._emitTrackingReset(values); } }; /** * Construct a tracking session to be used in a search. */ NoTracking.prototype.next = function (values) { if (this.clientTracking === null) { throw new Error("clientTracking is null"); } return this.clientTracking.next(values); }; return NoTracking; }(Tracking)); var PosNegTracking = /** @class */ (function (_super) { __extends(PosNegTracking, _super); /** * Construct a PosNegTracking instance. */ function PosNegTracking(field) { var _this = _super.call(this) || this; _this.clientTracking = new sdkJs.DefaultSession(sdkJs.TrackingType.PosNeg, field, getTrackingData()); return _this; } /** * Reset the tracking. * @param values Key-value pair parameters to use in the pipeline. */ PosNegTracking.prototype.reset = function (values) { this.clientTracking.reset(); if (values !== undefined) { this._emitTrackingReset(values); } }; /** * Construct a tracking session to be used in a search. * @param values Key-value pair parameters to use in the pipeline. */ PosNegTracking.prototype.next = function (values) { if (this.clientTracking === null) { throw new Error("clientTracking is null"); } return this.clientTracking.next(values); }; return PosNegTracking; }(Tracking)); var events$2 = [ EVENT_SEARCH_SENT, EVENT_RESPONSE_UPDATED, EVENT_RESULT_CLICKED ]; var Pipeline = /** @class */ (function () { /** * Constructs a Pipeline object. * @param config Project, Collection config * @param name Name of the pipeline. * @param [tracking=ClickTracking()] Default tracking to use in searches. * @param [analyticsAdapters=GoogleAnalytics] */ function Pipeline(config, name, tracking, analyticsAdapters) { var _this = this; if (tracking === void 0) { tracking = new NoTracking(); } if (analyticsAdapters === void 0) { analyticsAdapters = [GoogleAnalytics]; } this.response = new Response(null); var project = config.project, collection = config.collection, endpoint = config.endpoint; this.config = config; var opts = endpoint !== undefined ? [sdkJs.withEndpoint(endpoint)] : []; var p = { name: undefined, version: undefined }; if (typeof name === "string") { p.name = name; } else if ("name" in name) { p.name = name.name; p.version = name.version; } this.pipeline = new sdkJs.Client(project, collection, opts).pipeline(p.name, p.version); this.name = p.name; this.version = p.version; this.tracking = tracking; this.listeners = new Map([ [EVENT_SEARCH_SENT, new Listener()], [EVENT_RESPONSE_UPDATED, new Listener()], [EVENT_RESULT_CLICKED, new Listener()] ]); this.searchCount = 0; this.response = new Response(null); this.analytics = new Analytics(this, this.tracking); analyticsAdapters.forEach(function (Adapter) { // tslint:disable-next-line new Adapter(_this.analytics); }); } /** * Register a listener for a specific event. * @param event Event to listen for * @param callback Callback to run when the event happens. * @return The unregister function to remove the callback from the listener. */ Pipeline.prototype.listen = function (event, callback) { if (events$2.indexOf(event) === -1) { throw new Error("unknown event type \"" + event + "\""); } return this.listeners.get(event).listen(callback); }; /** * Emits a search event to the search event listener. * @private */ Pipeline.prototype._emitSearchSent = function (values) { this.listeners.get(EVENT_SEARCH_SENT).notify(function (listener) { listener(values); }); }; /** * Emits a results event to the results event listener. * @private */ Pipeline.prototype._emitResponseUpdated = function (response) { this.listeners.get(EVENT_RESPONSE_UPDATED).notify(function (listener) { listener(response); }); }; /** * Emits a result clicked event to the results clicked event listeners. * @param value Value to send to the listeners. */ Pipeline.prototype.emitResultClicked = function (value) { this.listeners.get(EVENT_RESULT_CLICKED).notify(function (listener) { listener(value); }); }; /** * Perform a search. * @param values Key-value parameters to pass to the pipeline. */ Pipeline.prototype.search = function (values) { var _this = this; this.searchCount++; var currentSearch = this.searchCount; this.pipeline.search(values, this.tracking, function (error, response, responseValues) { if (responseValues === void 0) { responseValues = {}; } if (currentSearch < _this.searchCount) { return; } _this.response = new Response(error ? error : null, new Map(Object.entries(values)), response !== undefined ? new Map(Object.entries(response)) : undefined, new Map(Object.entries(responseValues))); // tslint:disable-next-line no-console if (error && console && console.error) { // tslint:disable-next-line no-console console.error(error); } _this._emitResponseUpdated(_this.response); }); this._emitSearchSent(values); }; /** * Clears the error, response, and response values from this object. * @param values Key-value pair parameters. */ Pipeline.prototype.clearResponse = function (values) { this.tracking.next(values); this.searchCount++; this.response = new Response(null); this._emitResponseUpdated(this.response); }; /** * The current response. */ Pipeline.prototype.getResponse = function () { return this.response; }; /** * The analytics adaptor connected to this pipeline. */ Pipeline.prototype.getAnalytics = function () { return this.analytics; }; return Pipeline; }()); var Values = /** @class */ (function () { /** * Constructor for Values object. * @param values Initial values. */ function Values(values) { if (values === void 0) { values = {}; } this.listeners = new Map([[EVENT_VALUES_UPDATED, new Listener()]]); this.values = new Map(Object.entries(values)); } /** * Register a listener for a specific event. * @param event Event to listen for * @param callback Callback to run when the event happens. */ Values.prototype.listen = function (event, callback) { if (event !== EVENT_VALUES_UPDATED) { throw new Error("unknown event type \"" + event + "\""); } return this.listeners.get(event).listen(callback); }; /** * Merge values into the value map. * * Set a value to undefined to remove it. */ Values.prototype.set = function (values) { this._set(values); this._emitUpdated(values); }; /** * get returns the current values. */ Values.prototype.get = function () { var values = {}; this.values.forEach(function (value, key) { if (typeof value === "function") { values[key] = value(); } else if (Array.isArray(value)) { values[key] = value.join(","); } else { values[key] = String(value); } }); return values; }; /** * Emits an event to notify listener that the values have been updated. * * @private */ Values.prototype._emitUpdated = function (changes) { var _this = this; this.listeners.get(EVENT_VALUES_UPDATED).notify(function (listener) { return listener(changes, function (values) { return _this._set(values); }); }); }; /** * Sets values without triggering an event, internal use only. */ Values.prototype._set = function (values) { var _this = this; Object.keys(values).forEach(function (key) { if (values[key] === undefined) { _this.values.delete(key); } else { _this.values.set(key, values[key]); } }); }; return Values; }()); var events$3 = [EVENT_SELECTION_UPDATED, EVENT_OPTIONS_UPDATED]; /** * Filter is a helper class for building filters from UI components. */ var Filter = /** @class */ (function () { /** * Constructs an instance of Filter. * * @example * const filter = new Filter({}); */ function Filter(options, // Dictionary of name -> filter pairs initial, // List of initially selected items multi, // Multiple selections allowed? joinOperator // Join operator used if multi = true ) { var _a; if (initial === void 0) { initial = []; } if (multi === void 0) { multi = false; } if (joinOperator === void 0) { joinOperator = "OR"; } if (typeof initial === "string") { initial = [initial]; } /** @private */ this.current = initial; /** @private */ this.options = options; /** @private */ this.multi = multi; /** @private */ this.joinOperator = joinOperator; /** @private */ this.listeners = (_a = {}, _a[EVENT_SELECTION_UPDATED] = new Listener(), _a[EVENT_OPTIONS_UPDATED] = new Listener(), _a); } /** * Register a listener for a specific event. */ Filter.prototype.listen = function (event, callback) { if (events$3.indexOf(event) === -1) { throw new Error("unknown event type \"" + event + "\""); } return this.listeners[event].listen(callback); }; /** * Set the state of the filter. */ Filter.prototype.set = function (name, on) { if (this.multi) { this._setMulti(name, on); return; } // if on add to current if (on) { this.current = [name]; } else { // clear current this.current = []; } this._emitSelectionUpdated(); }; /** * returns whether the filter is set or not. */ Filter.prototype.isSet = function (name) { return this.current.indexOf(name) !== -1; }; /** * Merge options into the filter options. * * Set an option to null to remove it. */ Filter.prototype.setOptions = function (options) { var _this = this; Object.keys(options).forEach(function (k) { if (options[k] === null) { delete _this.options[k]; _this.current = _this.current.filter(function (n) { return n !== k; }); } else { _this.options[k] = options[k]; } }); this._emitOptionsUpdated(); }; /** * Get all the options defined in this filter. */ Filter.prototype.getOptions = function () { var fields = []; for (var _i = 0; _i < arguments.length; _i++) { fields[_i] = arguments[_i]; } return this.options; }; /** * Get the current selection in this filter. */ Filter.prototype.get = function () { var fields = []; for (var _i = 0; _i < arguments.length; _i++) { fields[_i] = arguments[_i]; } return this.current; }; /** * Builds up the filter string from the current filter and it's children. */ Filter.prototype.filter = function () { var _this = this; var filters = this.current .map(function (c) { var f = _this.options[c]; if (typeof f === "function") { f = f(); } if (f !== "") { f = "(" + f + ")"; } return f; }) .filter(Boolean); switch (filters.length) { case 0: return ""; case 1: return filters[0]; default: return filters.join(" " + this.joinOperator + " "); } }; /** * Emits a selection updated event to the selection updated listener. * @private */ Filter.prototype._emitSelectionUpdated = function () { this.listeners[EVENT_SELECTION_UPDATED].notify(function (listener) { listener(); }); }; /** * Emits an options updated event to the options updated listener. * @private */ Filter.prototype._emitOptionsUpdated = function () { this.listeners[EVENT_OPTIONS_UPDATED].notify(function (listener) { listener(); }); }; /** @private */ Filter.prototype._setMulti = function (name, on) { if (on && this.current.indexOf(name) === -1) { this.current = this.current.concat(name); } else { this.current = this.current.filter(function (n) { return n !== name; }); } this._emitSelectionUpdated(); }; return Filter; }()); /** * CombineFilters is a helper for combining multiple Filter instances * into one. * * Whenever any of the combined filters are updated, the events are * propagated up to the returned "root" filter. * * @param filters Array of filters to combine. * @param [operator="AND"] Operator to apply between them ("AND" | "OR"). * @return The resulting Filter. */ var CombineFilters = function (filters, operator) { if (operator === void 0) { operator = "AND"; } var opts = {}; var count = 1; var on = []; filters.forEach(function (f) { opts["" + count] = function () { return f.filter(); }; on = on.concat(["" + count]); count++; }); var combFilter = new Filter(opts, on, true, operator); filters.forEach(function (f) { f.listen(EVENT_SELECTION_UPDATED, function () { // @ts-ignore combFilter._emitSelectionUpdated(); }); f.listen(EVENT_OPTIONS_UPDATED, function () { // @ts-ignore combFilter._emitOptionsUpdated(); }); }); return combFilter; }; var CountAggregateFilter = /** @class */ (function (_super) { __extends(CountAggregateFilter, _super); function CountAggregateFilter(field, pipeline, values, multi, type, listField) { if (multi === void 0) { multi = false; } if (type === void 0) { type = "~"; } if (listField === void 0) { listField = false; } var _this = _super.call(this, {}, [], multi) || this; _this._field = ""; _this._counts = []; _this._field = field; _this._addCountToValues(values); pipeline.listen(EVENT_RESPONSE_UPDATED, function (response) { var current = _this.get(); var aggregates = response.getAggregates(); _this._counts = _this._getCounts(aggregates); _this._clearFilterOptions(current); _this.setOptions(_this._genFilterOptions(current, type, listField)); }); return _this; } CountAggregateFilter.prototype.getCounts = function () { return this._counts; }; CountAggregateFilter.prototype.reset = function () { var _this = this; this.get().forEach(function (item) { _this.set(item, false); }); this._counts = []; }; CountAggregateFilter.prototype._addCountToValues = function (values) { var count = values.get().count; var fields = typeof count === "string" ? count.split(",") : []; if (!fields.includes(this._field)) { fields.push(this._field); values.set({ count: fields.join(",") }); } }; CountAggregateFilter.prototype._getCounts = function (aggregates) { if (!aggregates || !aggregates["count." + this._field]) { return []; } var counts = aggregates["count." + this._field]; return Object.keys(counts) .map(function (key) { return { name: key, count: counts[key] }; }) .filter(function (x) { return x.name !== ""; }) .sort(function (_a, _b) { var a = _a.count; var b = _b.count; return b - a; }); }; CountAggregateFilter.prototype._genFilterOptions = function (current, type, listField) { var _this = this; if (type === void 0) { type = "~"; } if (listField === void 0) { listField = false; } return this._counts.reduce(function (opts, _a) { var name = _a.name; if (!current.includes(name)) { opts[name] = filterOptValue(name, _this._field, type, listField); } return opts; }, {}); }; CountAggregateFilter.prototype._clearFilterOptions = function (current) { var clear = Object.keys(this.getOptions()).reduce(function (opts, key) { if (!current.includes(key)) { opts[key] = null; } return opts; }, {}); this.setOptions(clear); }; return CountAggregateFilter; }(Filter)); function filterOptValue(name, field, type, listField) { if (type === void 0) { type = "~"; } if (listField === void 0) { listField = false; } var item = "\"" + name + "\""; if (listField) { item = "[\"" + name + "\"]"; } return "" + field + type + item; } var RangeFilter = /** @class */ (function (_super) { __extends(RangeFilter, _super); function RangeFilter(field, limit) { var _this = _super.call(this, {}, []) || this; _this._field = ""; _this._range = [0, 0]; _this._limit = [0, 0]; // [min, max] _this._current = ""; _this._filter = ""; _this.range = function () { return _this._range; }; _this.filter = function () { return _this._filter; }; _this.limit = function () { return _this._limit; }; _this.get = function () { return (_this._current === "" ? [] : [_this._current]); }; // @ts-ignore: override method _this.set = function (from, to) { _this._range = [from, to]; _this._filter = getFilterQuery(_this._range, _this._limit, _this._field); _this._emitSelectionUpdated(); }; _this.reset = function () { _this._range = _this._limit.map(function (r) { return r; }); if (_this._filter !== "") { _this.clear(); } }; _this.clear = function () { _this._filter = ""; _this._emitSelectionUpdated(); }; _this._field = field; _this._limit = limit; _this._range = limit; return _this; } RangeFilter.prototype.getRange = function () { return this._range; }; return RangeFilter; }(Filter)); function getFilterQuery(range, limit, field) { if (range[1] === limit[1] && range[0] === limit[0]) { return ""; } return "(" + field + " >= " + range[0] + " AND " + field + " <= " + range[1] + ")"; } var RangeAggregateFilter = /** @class */ (function (_super) { __extends(RangeAggregateFilter, _super); function RangeAggregateFilter(field, pipeline, values) { var _this = _super.call(this, field, [0, 0]) || this; _this._prevInput = ""; _this._count = ""; _this._limitChangeListeners = []; _this._field = field; _this._addMinMaxToValues(values); pipeline.listen(EVENT_SEARCH_SENT, function (sent) { var q = sent.q, count = sent.count; if (_this._prevInput !== q || (_this._count === "" && count !== "")) { var removeListener_1 = pipeline.listen(EVENT_RESPONSE_UPDATED, function (response) { var aggregates = response.getAggregates(); var bounce = _this._getLimit(aggregates); var range = _this._calculateRange(bounce); _this._limit = bounce; _this._range = range; _this._fireLimitChangeEvent({ bounce: bounce, range: range }); removeListener_1(); }); _this._prevInput = q; _this._count = count; } }); return _this; } RangeAggregateFilter.prototype.addLimitChangeListener = function (listener) { this._limitChangeListeners.push(listener); }; RangeAggregateFilter.prototype.removeLimitChangeListener = function (listener) { this._limitChangeListeners = this._limitChangeListeners.filter(function (item) { return item !== listener; }); }; RangeAggregateFilter.prototype._calculateRange = function (bounce) { var range = this._range.map(function (r) { return r; }); if (this._range[0] < bounce[0]) { range[0] = bounce[0]; } if (this._range[1] > bounce[1] || this._range[1] <= range[0]) { range[1] = bounce[1]; } return range; }; RangeAggregateFilter.prototype._fireLimitChangeEvent = function (_a) { var bounce = _a.bounce, range = _a.range; this._limitChangeListeners.forEach(function (func) { return func({ bounce: bounce, range: range }); }); }; RangeAggregateFilter.prototype._getLimit = function (aggregates) { if (isEmpty(aggregates, this._field)) { return this._limit.map(function (r) { return r; }); } var min = aggregates["min." + this._field] || 0; var max = aggregates["max." + this._field] || 0; return [min, max]; }; RangeAggregateFilter.prototype._addMinMaxToValues = function (values) { var _a = values.get(), min = _a.min, max = _a.max; var minFields = typeof min === "string" ? min.split(",") : []; if (!minFields.includes(this._field)) { minFields.push(this._field); values.set({ min: minFields.join(",") }); } var maxFields = typeof max === "string" ? max.split(",") : []; if (!maxFields.includes(this._field)) { maxFields.push(this._field); values.set({ max: maxFields.join(",") }); } }; return RangeAggregateFilter; }(RangeFilter)); var isEmpty = function (aggregates, field) { return !aggregates && (!aggregates["max." + field] || !aggregates["min." + field]); }; var PipelineContext = React.createContext({}); var Consumer = PipelineContext.Consumer; var defaultConfig = { qParam: "q", qOverrideParam: "q.override", qSuggestionsParam: "q.suggestions", maxSuggestions: 5, resultsPerPageParam: "resultsPerPage", pageParam: "page" }; var defaultState = { response: null, query: "", completion: "", suggestions: [], config: defaultConfig }; var Provider = /** @class */ (function (_super) { __extends(Provider, _super); function Provider() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.state = { search: defaultState, instant: defaultState }; _this.unregisterFunctions = []; _this.getContext = function (state) { return (__assign(__assign({}, state), { search: __assign(__assign({}, state.search), { search: _this.search("search"), clear: _this.clear("search") }), instant: __assign(__assign({}, state.instant), { search: _this.search("instant"), clear: _this.clear("instant") }), resultClicked: _this.handleResultClicked, paginate: _this.handlePaginate })); }; _this.search = function (key) { return debounce(function (query, override) { var _a; if (override === void 0) { override = false; } var _b = _this.props[key] || _this.instant, pipeline = _b.pipeline, values = _b.values; var config = _this.state[key].config; var text = (_a = {}, _a[config.qParam] = query, _a[config.qOverrideParam] = undefined, _a); if (override) { text[config.qOverrideParam] = "true"; } values.set(text); if (text[config.qParam]) { pipeline.search(values.get()); }