react-instantsearch
Version:
⚡ Lightning-fast search for React, by Algolia
1,271 lines (1,171 loc) • 1.12 MB
JavaScript
/*! React InstantSearch 7.26.0 | © Algolia, Inc. and contributors; MIT License | https://github.com/algolia/instantsearch */
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react')) :
typeof define === 'function' && define.amd ? define(['exports', 'react'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ReactInstantSearch = {}, global.React));
})(this, (function (exports, React) { 'use strict';
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
var version$2 = '7.26.0';
function getDefaultExportFromCjs (x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
}
var _object_spread$1 = {};
var _define_property$1 = {};
var hasRequired_define_property;
function require_define_property () {
if (hasRequired_define_property) return _define_property$1;
hasRequired_define_property = 1;
function _define_property(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true });
} else obj[key] = value;
return obj;
}
_define_property$1._ = _define_property;
return _define_property$1;
}
var hasRequired_object_spread;
function require_object_spread () {
if (hasRequired_object_spread) return _object_spread$1;
hasRequired_object_spread = 1;
var _define_property = /*@__PURE__*/ require_define_property();
function _object_spread(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
var ownKeys = Object.keys(source);
if (typeof Object.getOwnPropertySymbols === "function") {
ownKeys = ownKeys.concat(
Object.getOwnPropertySymbols(source).filter(function(sym) {
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
})
);
}
ownKeys.forEach(function(key) {
_define_property._(target, key, source[key]);
});
}
return target;
}
_object_spread$1._ = _object_spread;
return _object_spread$1;
}
var _object_spreadExports = /*@__PURE__*/ require_object_spread();
var _define_propertyExports = /*@__PURE__*/ require_define_property();
var _object_spread_props$1 = {};
var hasRequired_object_spread_props;
function require_object_spread_props () {
if (hasRequired_object_spread_props) return _object_spread_props$1;
hasRequired_object_spread_props = 1;
function ownKeys(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
keys.push.apply(keys, symbols);
}
return keys;
}
function _object_spread_props(target, source) {
source = source != null ? source : {};
if (Object.getOwnPropertyDescriptors) Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
else {
ownKeys(Object(source)).forEach(function(key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
return target;
}
_object_spread_props$1._ = _object_spread_props;
return _object_spread_props$1;
}
var _object_spread_propsExports = /*@__PURE__*/ require_object_spread_props();
var events;
var hasRequiredEvents;
function requireEvents () {
if (hasRequiredEvents) return events;
hasRequiredEvents = 1;
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
function EventEmitter() {
this._events = this._events || {};
this._maxListeners = this._maxListeners || undefined;
}
events = EventEmitter;
// Backwards-compat with node 0.10.x
// EventEmitter.EventEmitter = EventEmitter;
EventEmitter.prototype._events = undefined;
EventEmitter.prototype._maxListeners = undefined;
// By default EventEmitters will print a warning if more than 10 listeners are
// added to it. This is a useful default which helps finding memory leaks.
EventEmitter.defaultMaxListeners = 10;
// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
EventEmitter.prototype.setMaxListeners = function(n) {
if (!isNumber(n) || n < 0 || isNaN(n))
throw TypeError('n must be a positive number');
this._maxListeners = n;
return this;
};
EventEmitter.prototype.emit = function(type) {
var er, handler, len, args, i, listeners;
if (!this._events)
this._events = {};
// If there is no 'error' event listener then throw.
if (type === 'error') {
if (!this._events.error ||
(isObject(this._events.error) && !this._events.error.length)) {
er = arguments[1];
if (er instanceof Error) {
throw er; // Unhandled 'error' event
} else {
// At least give some kind of context to the user
var err = new Error('Uncaught, unspecified "error" event. (' + er + ')');
err.context = er;
throw err;
}
}
}
handler = this._events[type];
if (isUndefined(handler))
return false;
if (isFunction(handler)) {
switch (arguments.length) {
// fast cases
case 1:
handler.call(this);
break;
case 2:
handler.call(this, arguments[1]);
break;
case 3:
handler.call(this, arguments[1], arguments[2]);
break;
// slower
default:
args = Array.prototype.slice.call(arguments, 1);
handler.apply(this, args);
}
} else if (isObject(handler)) {
args = Array.prototype.slice.call(arguments, 1);
listeners = handler.slice();
len = listeners.length;
for (i = 0; i < len; i++)
listeners[i].apply(this, args);
}
return true;
};
EventEmitter.prototype.addListener = function(type, listener) {
var m;
if (!isFunction(listener))
throw TypeError('listener must be a function');
if (!this._events)
this._events = {};
// To avoid recursion in the case that type === "newListener"! Before
// adding it to the listeners, first emit "newListener".
if (this._events.newListener)
this.emit('newListener', type,
isFunction(listener.listener) ?
listener.listener : listener);
if (!this._events[type])
// Optimize the case of one listener. Don't need the extra array object.
this._events[type] = listener;
else if (isObject(this._events[type]))
// If we've already got an array, just append.
this._events[type].push(listener);
else
// Adding the second element, need to change to array.
this._events[type] = [this._events[type], listener];
// Check for listener leak
if (isObject(this._events[type]) && !this._events[type].warned) {
if (!isUndefined(this._maxListeners)) {
m = this._maxListeners;
} else {
m = EventEmitter.defaultMaxListeners;
}
if (m && m > 0 && this._events[type].length > m) {
this._events[type].warned = true;
console.error('(node) warning: possible EventEmitter memory ' +
'leak detected. %d listeners added. ' +
'Use emitter.setMaxListeners() to increase limit.',
this._events[type].length);
if (typeof console.trace === 'function') {
// not supported in IE 10
console.trace();
}
}
}
return this;
};
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
EventEmitter.prototype.once = function(type, listener) {
if (!isFunction(listener))
throw TypeError('listener must be a function');
var fired = false;
function g() {
this.removeListener(type, g);
if (!fired) {
fired = true;
listener.apply(this, arguments);
}
}
g.listener = listener;
this.on(type, g);
return this;
};
// emits a 'removeListener' event iff the listener was removed
EventEmitter.prototype.removeListener = function(type, listener) {
var list, position, length, i;
if (!isFunction(listener))
throw TypeError('listener must be a function');
if (!this._events || !this._events[type])
return this;
list = this._events[type];
length = list.length;
position = -1;
if (list === listener ||
(isFunction(list.listener) && list.listener === listener)) {
delete this._events[type];
if (this._events.removeListener)
this.emit('removeListener', type, listener);
} else if (isObject(list)) {
for (i = length; i-- > 0;) {
if (list[i] === listener ||
(list[i].listener && list[i].listener === listener)) {
position = i;
break;
}
}
if (position < 0)
return this;
if (list.length === 1) {
list.length = 0;
delete this._events[type];
} else {
list.splice(position, 1);
}
if (this._events.removeListener)
this.emit('removeListener', type, listener);
}
return this;
};
EventEmitter.prototype.removeAllListeners = function(type) {
var key, listeners;
if (!this._events)
return this;
// not listening for removeListener, no need to emit
if (!this._events.removeListener) {
if (arguments.length === 0)
this._events = {};
else if (this._events[type])
delete this._events[type];
return this;
}
// emit removeListener for all listeners on all events
if (arguments.length === 0) {
for (key in this._events) {
if (key === 'removeListener') continue;
this.removeAllListeners(key);
}
this.removeAllListeners('removeListener');
this._events = {};
return this;
}
listeners = this._events[type];
if (isFunction(listeners)) {
this.removeListener(type, listeners);
} else if (listeners) {
// LIFO order
while (listeners.length)
this.removeListener(type, listeners[listeners.length - 1]);
}
delete this._events[type];
return this;
};
EventEmitter.prototype.listeners = function(type) {
var ret;
if (!this._events || !this._events[type])
ret = [];
else if (isFunction(this._events[type]))
ret = [this._events[type]];
else
ret = this._events[type].slice();
return ret;
};
EventEmitter.prototype.listenerCount = function(type) {
if (this._events) {
var evlistener = this._events[type];
if (isFunction(evlistener))
return 1;
else if (evlistener)
return evlistener.length;
}
return 0;
};
EventEmitter.listenerCount = function(emitter, type) {
return emitter.listenerCount(type);
};
function isFunction(arg) {
return typeof arg === 'function';
}
function isNumber(arg) {
return typeof arg === 'number';
}
function isObject(arg) {
return typeof arg === 'object' && arg !== null;
}
function isUndefined(arg) {
return arg === void 0;
}
return events;
}
var inherits_1;
var hasRequiredInherits;
function requireInherits() {
if (hasRequiredInherits) return inherits_1;
hasRequiredInherits = 1;
function inherits(ctor, superCtor) {
ctor.prototype = Object.create(superCtor.prototype, {
constructor: {
value: ctor,
enumerable: false,
writable: true,
configurable: true
}
});
}
inherits_1 = inherits;
return inherits_1;
}
var DerivedHelper_1;
var hasRequiredDerivedHelper;
function requireDerivedHelper() {
if (hasRequiredDerivedHelper) return DerivedHelper_1;
hasRequiredDerivedHelper = 1;
var EventEmitter = requireEvents();
var inherits = requireInherits();
/**
* A DerivedHelper is a way to create sub requests to
* Algolia from a main helper.
* @class
* @classdesc The DerivedHelper provides an event based interface for search callbacks:
* - search: when a search is triggered using the `search()` method.
* - result: when the response is retrieved from Algolia and is processed.
* This event contains a {@link SearchResults} object and the
* {@link SearchParameters} corresponding to this answer.
* @param {AlgoliaSearchHelper} mainHelper the main helper
* @param {function} fn the function to create the derived state for search
* @param {function} recommendFn the function to create the derived state for recommendations
*/ function DerivedHelper(mainHelper, fn, recommendFn) {
this.main = mainHelper;
this.fn = fn;
this.recommendFn = recommendFn;
this.lastResults = null;
this.lastRecommendResults = null;
}
inherits(DerivedHelper, EventEmitter);
/**
* Detach this helper from the main helper
* @return {undefined}
* @throws Error if the derived helper is already detached
*/ DerivedHelper.prototype.detach = function() {
this.removeAllListeners();
this.main.detachDerivedHelper(this);
};
DerivedHelper.prototype.getModifiedState = function(parameters) {
return this.fn(parameters);
};
DerivedHelper.prototype.getModifiedRecommendState = function(parameters) {
return this.recommendFn(parameters);
};
DerivedHelper_1 = DerivedHelper;
return DerivedHelper_1;
}
var escapeFacetValue_1;
var hasRequiredEscapeFacetValue;
function requireEscapeFacetValue() {
if (hasRequiredEscapeFacetValue) return escapeFacetValue_1;
hasRequiredEscapeFacetValue = 1;
/**
* Replaces a leading - with \-
* @private
* @param {any} value the facet value to replace
* @returns {any} the escaped facet value or the value if it was not a string
*/ function escapeFacetValue(value) {
if (typeof value !== 'string') return value;
return String(value).replace(/^-/, '\\-');
}
/**
* Replaces a leading \- with -
* @private
* @param {any} value the escaped facet value
* @returns {any} the unescaped facet value or the value if it was not a string
*/ function unescapeFacetValue(value) {
if (typeof value !== 'string') return value;
return value.replace(/^\\-/, '-');
}
escapeFacetValue_1 = {
escapeFacetValue: escapeFacetValue,
unescapeFacetValue: unescapeFacetValue
};
return escapeFacetValue_1;
}
function _type_of$1(obj) {
"@swc/helpers - typeof";
return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
}
var merge_1;
var hasRequiredMerge;
function requireMerge() {
if (hasRequiredMerge) return merge_1;
hasRequiredMerge = 1;
function clone(value) {
if ((typeof value === "undefined" ? "undefined" : _type_of$1(value)) === 'object' && value !== null) {
return _merge(Array.isArray(value) ? [] : {}, value);
}
return value;
}
function isObjectOrArrayOrFunction(value) {
return typeof value === 'function' || Array.isArray(value) || Object.prototype.toString.call(value) === '[object Object]';
}
function _merge(target, source) {
if (target === source) {
return target;
}
// eslint-disable-next-line no-restricted-syntax
for(var key in source){
if (!Object.prototype.hasOwnProperty.call(source, key) || key === '__proto__' || key === 'constructor') {
continue;
}
var sourceVal = source[key];
var targetVal = target[key];
if (typeof targetVal !== 'undefined' && typeof sourceVal === 'undefined') {
continue;
}
if (isObjectOrArrayOrFunction(targetVal) && isObjectOrArrayOrFunction(sourceVal)) {
target[key] = _merge(targetVal, sourceVal);
} else {
target[key] = clone(sourceVal);
}
}
return target;
}
/**
* This method is like Object.assign, but recursively merges own and inherited
* enumerable keyed properties of source objects into the destination object.
*
* NOTE: this behaves like lodash/merge, but:
* - does mutate functions if they are a source
* - treats non-plain objects as plain
* - does not work for circular objects
* - treats sparse arrays as sparse
* - does not convert Array-like objects (Arguments, NodeLists, etc.) to arrays
*
* @param {Object} target The destination object.
* @param {...Object} [sources] The source objects.
* @returns {Object} Returns `object`.
*/ function merge(target) {
if (!isObjectOrArrayOrFunction(target)) {
target = {};
}
for(var i = 1, l = arguments.length; i < l; i++){
var source = arguments[i];
if (isObjectOrArrayOrFunction(source)) {
_merge(target, source);
}
}
return target;
}
merge_1 = merge;
return merge_1;
}
var objectHasKeys_1;
var hasRequiredObjectHasKeys;
function requireObjectHasKeys() {
if (hasRequiredObjectHasKeys) return objectHasKeys_1;
hasRequiredObjectHasKeys = 1;
function objectHasKeys(obj) {
return obj && Object.keys(obj).length > 0;
}
objectHasKeys_1 = objectHasKeys;
return objectHasKeys_1;
}
var omit;
var hasRequiredOmit;
function requireOmit() {
if (hasRequiredOmit) return omit;
hasRequiredOmit = 1;
// https://github.com/babel/babel/blob/3aaafae053fa75febb3aa45d45b6f00646e30ba4/packages/babel-helpers/src/helpers.js#L604-L620
function _objectWithoutPropertiesLoose(source, excluded) {
if (source === null) return {};
var target = {};
var sourceKeys = Object.keys(source);
var key;
var i;
for(i = 0; i < sourceKeys.length; i++){
key = sourceKeys[i];
// eslint-disable-next-line no-continue
if (excluded.indexOf(key) >= 0) continue;
target[key] = source[key];
}
return target;
}
omit = _objectWithoutPropertiesLoose;
return omit;
}
var RecommendParameters_1;
var hasRequiredRecommendParameters;
function requireRecommendParameters() {
if (hasRequiredRecommendParameters) return RecommendParameters_1;
hasRequiredRecommendParameters = 1;
/**
* RecommendParameters is the data structure that contains all the information
* usable for getting recommendations from the Algolia API. It doesn't do the
* search itself, nor does it contains logic about the parameters.
* It is an immutable object, therefore it has been created in a way that each
* changes does not change the object itself but returns a copy with the
* modification.
* This object should probably not be instantiated outside of the helper. It
* will be provided when needed.
* @constructor
* @classdesc contains all the parameters for recommendations
* @param {RecommendParametersOptions} opts the options to create the object
*/ function RecommendParameters(opts) {
opts = opts || {};
this.params = opts.params || [];
}
RecommendParameters.prototype = {
constructor: RecommendParameters,
addParams: function addParams(params) {
var newParams = this.params.slice();
newParams.push(params);
return new RecommendParameters({
params: newParams
});
},
removeParams: function removeParams(id) {
return new RecommendParameters({
params: this.params.filter(function(param) {
return param.$$id !== id;
})
});
},
addFrequentlyBoughtTogether: function addFrequentlyBoughtTogether(params) {
return this.addParams(Object.assign({}, params, {
model: 'bought-together'
}));
},
addRelatedProducts: function addRelatedProducts(params) {
return this.addParams(Object.assign({}, params, {
model: 'related-products'
}));
},
addTrendingItems: function addTrendingItems(params) {
return this.addParams(Object.assign({}, params, {
model: 'trending-items'
}));
},
addTrendingFacets: function addTrendingFacets(params) {
return this.addParams(Object.assign({}, params, {
model: 'trending-facets'
}));
},
addLookingSimilar: function addLookingSimilar(params) {
return this.addParams(Object.assign({}, params, {
model: 'looking-similar'
}));
},
_buildQueries: function _buildQueries(indexName, cache) {
return this.params.filter(function(params) {
return cache[params.$$id] === undefined;
}).map(function(params) {
var query = Object.assign({}, params, {
indexName: indexName,
// @TODO: remove this if it ever gets handled by the API
threshold: params.threshold || 0
});
delete query.$$id;
return query;
});
}
};
RecommendParameters_1 = RecommendParameters;
return RecommendParameters_1;
}
var RecommendResults_1;
var hasRequiredRecommendResults;
function requireRecommendResults() {
if (hasRequiredRecommendResults) return RecommendResults_1;
hasRequiredRecommendResults = 1;
/**
* Constructor for SearchResults
* @class
* @classdesc SearchResults contains the results of a query to Algolia using the
* {@link AlgoliaSearchHelper}.
* @param {RecommendParameters} state state that led to the response
* @param {Record<string,RecommendResultItem>} results the results from algolia client
**/ function RecommendResults(state, results) {
this._state = state;
this._rawResults = {};
// eslint-disable-next-line consistent-this
var self = this;
state.params.forEach(function(param) {
var id = param.$$id;
self[id] = results[id];
self._rawResults[id] = results[id];
});
}
RecommendResults.prototype = {
constructor: RecommendResults
};
RecommendResults_1 = RecommendResults;
return RecommendResults_1;
}
var requestBuilder_1;
var hasRequiredRequestBuilder;
function requireRequestBuilder() {
if (hasRequiredRequestBuilder) return requestBuilder_1;
hasRequiredRequestBuilder = 1;
var merge = requireMerge();
function sortObject(obj) {
return Object.keys(obj).sort().reduce(function(acc, curr) {
acc[curr] = obj[curr];
return acc;
}, {});
}
var requestBuilder = {
/**
* Get all the queries to send to the client, those queries can used directly
* with the Algolia client.
* @private
* @param {string} index The name of the index
* @param {SearchParameters} state The state from which to get the queries
* @return {object[]} The queries
*/ _getQueries: function getQueries(index, state) {
var queries = [];
// One query for the hits
queries.push({
indexName: index,
params: requestBuilder._getHitsSearchParams(state)
});
// One for each disjunctive facets
state.getRefinedDisjunctiveFacets().forEach(function(refinedFacet) {
queries.push({
indexName: index,
params: requestBuilder._getDisjunctiveFacetSearchParams(state, refinedFacet)
});
});
// More to get the parent levels of the hierarchical facets when refined
state.getRefinedHierarchicalFacets().forEach(function(refinedFacet) {
var hierarchicalFacet = state.getHierarchicalFacetByName(refinedFacet);
var currentRefinement = state.getHierarchicalRefinement(refinedFacet);
var separator = state._getHierarchicalFacetSeparator(hierarchicalFacet);
// If we are deeper than level 0 (starting from `beer > IPA`)
// we want to get all parent values
if (currentRefinement.length > 0 && currentRefinement[0].split(separator).length > 1) {
// We generate a map of the filters we will use for our facet values queries
var filtersMap = currentRefinement[0].split(separator).slice(0, -1).reduce(function createFiltersMap(map, segment, level) {
return map.concat({
attribute: hierarchicalFacet.attributes[level],
value: level === 0 ? segment : [
map[map.length - 1].value,
segment
].join(separator)
});
}, []);
filtersMap.forEach(function(filter, level) {
var params = requestBuilder._getDisjunctiveFacetSearchParams(state, filter.attribute, level === 0);
// Keep facet filters unrelated to current hierarchical attributes
function hasHierarchicalFacetFilter(value) {
return hierarchicalFacet.attributes.some(function(attribute) {
return attribute === value.split(':')[0];
});
}
var filteredFacetFilters = (params.facetFilters || []).reduce(function(acc, facetFilter) {
if (Array.isArray(facetFilter)) {
var filtered = facetFilter.filter(function(filterValue) {
return !hasHierarchicalFacetFilter(filterValue);
});
if (filtered.length > 0) {
acc.push(filtered);
}
}
if (typeof facetFilter === 'string' && !hasHierarchicalFacetFilter(facetFilter)) {
acc.push(facetFilter);
}
return acc;
}, []);
var parent = filtersMap[level - 1];
if (level > 0) {
params.facetFilters = filteredFacetFilters.concat(parent.attribute + ':' + parent.value);
} else if (filteredFacetFilters.length > 0) {
params.facetFilters = filteredFacetFilters;
} else {
delete params.facetFilters;
}
queries.push({
indexName: index,
params: params
});
});
}
});
return queries;
},
/**
* Get all the queries to send to the client, those queries can used directly
* with the Algolia client.
* @private
* @param {SearchParameters} state The state from which to get the queries
* @return {object[]} The queries
*/ _getCompositionQueries: function getQueries(state) {
return [
{
compositionID: state.index,
requestBody: {
params: requestBuilder._getCompositionHitsSearchParams(state)
}
}
];
},
/**
* Build search parameters used to fetch hits
* @private
* @param {SearchParameters} state The state from which to get the queries
* @return {object.<string, any>} The search parameters for hits
*/ _getHitsSearchParams: function _getHitsSearchParams(state) {
var facets = state.facets.concat(state.disjunctiveFacets).concat(requestBuilder._getHitsHierarchicalFacetsAttributes(state)).sort();
var facetFilters = requestBuilder._getFacetFilters(state);
var numericFilters = requestBuilder._getNumericFilters(state);
var tagFilters = requestBuilder._getTagFilters(state);
var additionalParams = {};
if (facets.length > 0) {
additionalParams.facets = facets.indexOf('*') > -1 ? [
'*'
] : facets;
}
if (tagFilters.length > 0) {
additionalParams.tagFilters = tagFilters;
}
if (facetFilters.length > 0) {
additionalParams.facetFilters = facetFilters;
}
if (numericFilters.length > 0) {
additionalParams.numericFilters = numericFilters;
}
return sortObject(merge({}, state.getQueryParams(), additionalParams));
},
/**
* Build search parameters used to fetch hits
* @private
* @param {SearchParameters} state The state from which to get the queries
* @return {object.<string, any>} The search parameters for hits
*/ _getCompositionHitsSearchParams: function _getCompositionHitsSearchParams(state) {
var facets = state.facets.concat(state.disjunctiveFacets.map(function(value) {
if (state.disjunctiveFacetsRefinements && state.disjunctiveFacetsRefinements[value] && state.disjunctiveFacetsRefinements[value].length > 0) {
// only tag a disjunctiveFacet as disjunctive if it has a value selected
// this helps avoid hitting the limit of 20 disjunctive facets in the Composition API
return 'disjunctive(' + value + ')';
}
return value;
})).concat(requestBuilder._getHitsHierarchicalFacetsAttributes(state)).sort();
var facetFilters = requestBuilder._getFacetFilters(state);
var numericFilters = requestBuilder._getNumericFilters(state);
var tagFilters = requestBuilder._getTagFilters(state);
var additionalParams = {};
if (facets.length > 0) {
additionalParams.facets = facets.indexOf('*') > -1 ? [
'*'
] : facets;
}
if (tagFilters.length > 0) {
additionalParams.tagFilters = tagFilters;
}
if (facetFilters.length > 0) {
additionalParams.facetFilters = facetFilters;
}
if (numericFilters.length > 0) {
additionalParams.numericFilters = numericFilters;
}
var params = state.getQueryParams();
delete params.highlightPreTag;
delete params.highlightPostTag;
// not a valid search parameter, it is handled in _getCompositionQueries
delete params.index;
return sortObject(merge({}, params, additionalParams));
},
/**
* Build search parameters used to fetch a disjunctive facet
* @private
* @param {SearchParameters} state The state from which to get the queries
* @param {string} facet the associated facet name
* @param {boolean} hierarchicalRootLevel ?? FIXME
* @return {object} The search parameters for a disjunctive facet
*/ _getDisjunctiveFacetSearchParams: function _getDisjunctiveFacetSearchParams(state, facet, hierarchicalRootLevel) {
var facetFilters = requestBuilder._getFacetFilters(state, facet, hierarchicalRootLevel);
var numericFilters = requestBuilder._getNumericFilters(state, facet);
var tagFilters = requestBuilder._getTagFilters(state);
var additionalParams = {
hitsPerPage: 0,
page: 0,
analytics: false,
clickAnalytics: false
};
if (tagFilters.length > 0) {
additionalParams.tagFilters = tagFilters;
}
var hierarchicalFacet = state.getHierarchicalFacetByName(facet);
if (hierarchicalFacet) {
additionalParams.facets = requestBuilder._getDisjunctiveHierarchicalFacetAttribute(state, hierarchicalFacet, hierarchicalRootLevel);
} else {
additionalParams.facets = facet;
}
if (numericFilters.length > 0) {
additionalParams.numericFilters = numericFilters;
}
if (facetFilters.length > 0) {
additionalParams.facetFilters = facetFilters;
}
return sortObject(merge({}, state.getQueryParams(), additionalParams));
},
/**
* Return the numeric filters in an algolia request fashion
* @private
* @param {SearchParameters} state the state from which to get the filters
* @param {string} [facetName] the name of the attribute for which the filters should be excluded
* @return {string[]} the numeric filters in the algolia format
*/ _getNumericFilters: function _getNumericFilters(state, facetName) {
if (state.numericFilters) {
return state.numericFilters;
}
var numericFilters = [];
Object.keys(state.numericRefinements).forEach(function(attribute) {
var operators = state.numericRefinements[attribute] || {};
Object.keys(operators).forEach(function(operator) {
var values = operators[operator] || [];
if (facetName !== attribute) {
values.forEach(function(value) {
if (Array.isArray(value)) {
var vs = value.map(function(v) {
return attribute + operator + v;
});
numericFilters.push(vs);
} else {
numericFilters.push(attribute + operator + value);
}
});
}
});
});
return numericFilters;
},
/**
* Return the tags filters depending on which format is used, either tagFilters or tagRefinements
* @private
* @param {SearchParameters} state the state from which to get the filters
* @return {string} Tag filters in a single string
*/ _getTagFilters: function _getTagFilters(state) {
if (state.tagFilters) {
return state.tagFilters;
}
return state.tagRefinements.join(',');
},
/**
* Build facetFilters parameter based on current refinements. The array returned
* contains strings representing the facet filters in the algolia format.
* @private
* @param {SearchParameters} state The state from which to get the queries
* @param {string} [facet] if set, the current disjunctive facet
* @param {boolean} [hierarchicalRootLevel] ?? FIXME
* @return {array.<string>} The facet filters in the algolia format
*/ _getFacetFilters: function _getFacetFilters(state, facet, hierarchicalRootLevel) {
var facetFilters = [];
var facetsRefinements = state.facetsRefinements || {};
Object.keys(facetsRefinements).sort().forEach(function(facetName) {
var facetValues = facetsRefinements[facetName] || [];
facetValues.slice().sort().forEach(function(facetValue) {
facetFilters.push(facetName + ':' + facetValue);
});
});
var facetsExcludes = state.facetsExcludes || {};
Object.keys(facetsExcludes).sort().forEach(function(facetName) {
var facetValues = facetsExcludes[facetName] || [];
facetValues.sort().forEach(function(facetValue) {
facetFilters.push(facetName + ':-' + facetValue);
});
});
var disjunctiveFacetsRefinements = state.disjunctiveFacetsRefinements || {};
Object.keys(disjunctiveFacetsRefinements).sort().forEach(function(facetName) {
var facetValues = disjunctiveFacetsRefinements[facetName] || [];
if (facetName === facet || !facetValues || facetValues.length === 0) {
return;
}
var orFilters = [];
facetValues.slice().sort().forEach(function(facetValue) {
orFilters.push(facetName + ':' + facetValue);
});
facetFilters.push(orFilters);
});
var hierarchicalFacetsRefinements = state.hierarchicalFacetsRefinements || {};
Object.keys(hierarchicalFacetsRefinements).sort().forEach(function(facetName) {
var facetValues = hierarchicalFacetsRefinements[facetName] || [];
var facetValue = facetValues[0];
if (facetValue === undefined) {
return;
}
var hierarchicalFacet = state.getHierarchicalFacetByName(facetName);
var separator = state._getHierarchicalFacetSeparator(hierarchicalFacet);
var rootPath = state._getHierarchicalRootPath(hierarchicalFacet);
var attributeToRefine;
var attributesIndex;
// we ask for parent facet values only when the `facet` is the current hierarchical facet
if (facet === facetName) {
// if we are at the root level already, no need to ask for facet values, we get them from
// the hits query
if (facetValue.indexOf(separator) === -1 || !rootPath && hierarchicalRootLevel === true || rootPath && rootPath.split(separator).length === facetValue.split(separator).length) {
return;
}
if (!rootPath) {
attributesIndex = facetValue.split(separator).length - 2;
facetValue = facetValue.slice(0, facetValue.lastIndexOf(separator));
} else {
attributesIndex = rootPath.split(separator).length - 1;
facetValue = rootPath;
}
attributeToRefine = hierarchicalFacet.attributes[attributesIndex];
} else {
attributesIndex = facetValue.split(separator).length - 1;
attributeToRefine = hierarchicalFacet.attributes[attributesIndex];
}
if (attributeToRefine) {
facetFilters.push([
attributeToRefine + ':' + facetValue
]);
}
});
return facetFilters;
},
_getHitsHierarchicalFacetsAttributes: function _getHitsHierarchicalFacetsAttributes(state) {
var out = [];
return state.hierarchicalFacets.reduce(// ask for as much levels as there's hierarchical refinements
function getHitsAttributesForHierarchicalFacet(allAttributes, hierarchicalFacet) {
var hierarchicalRefinement = state.getHierarchicalRefinement(hierarchicalFacet.name)[0];
// if no refinement, ask for root level
if (!hierarchicalRefinement) {
allAttributes.push(hierarchicalFacet.attributes[0]);
return allAttributes;
}
var separator = state._getHierarchicalFacetSeparator(hierarchicalFacet);
var level = hierarchicalRefinement.split(separator).length;
var newAttributes = hierarchicalFacet.attributes.slice(0, level + 1);
return allAttributes.concat(newAttributes);
}, out);
},
_getDisjunctiveHierarchicalFacetAttribute: function _getDisjunctiveHierarchicalFacetAttribute(state, hierarchicalFacet, rootLevel) {
var separator = state._getHierarchicalFacetSeparator(hierarchicalFacet);
if (rootLevel === true) {
var rootPath = state._getHierarchicalRootPath(hierarchicalFacet);
var attributeIndex = 0;
if (rootPath) {
attributeIndex = rootPath.split(separator).length;
}
return [
hierarchicalFacet.attributes[attributeIndex]
];
}
var hierarchicalRefinement = state.getHierarchicalRefinement(hierarchicalFacet.name)[0] || '';
// if refinement is 'beers > IPA > Flying dog',
// then we want `facets: ['beers > IPA']` as disjunctive facet (parent level values)
var parentLevel = hierarchicalRefinement.split(separator).length - 1;
return hierarchicalFacet.attributes.slice(0, parentLevel + 1);
},
getSearchForFacetQuery: function getSearchForFacetQuery(facetName, query, maxFacetHits, state) {
var stateForSearchForFacetValues = state.isDisjunctiveFacet(facetName) ? state.clearRefinements(facetName) : state;
var searchForFacetSearchParameters = {
facetQuery: query,
facetName: facetName
};
if (typeof maxFacetHits === 'number') {
searchForFacetSearchParameters.maxFacetHits = maxFacetHits;
}
return sortObject(merge({}, requestBuilder._getHitsSearchParams(stateForSearchForFacetValues), searchForFacetSearchParameters));
}
};
requestBuilder_1 = requestBuilder;
return requestBuilder_1;
}
function _instanceof$1(left, right) {
if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
return !!right[Symbol.hasInstance](left);
} else return left instanceof right;
}
var defaultsPure;
var hasRequiredDefaultsPure;
function requireDefaultsPure() {
if (hasRequiredDefaultsPure) return defaultsPure;
hasRequiredDefaultsPure = 1;
// NOTE: this behaves like lodash/defaults, but doesn't mutate the target
// it also preserve keys order
defaultsPure = function defaultsPure() {
var sources = Array.prototype.slice.call(arguments);
return sources.reduceRight(function(acc, source) {
Object.keys(Object(source)).forEach(function(key) {
if (source[key] === undefined) {
return;
}
if (acc[key] !== undefined) {
// remove if already added, so that we can add it in correct order
delete acc[key];
}
acc[key] = source[key];
});
return acc;
}, {});
};
return defaultsPure;
}
var find$2;
var hasRequiredFind;
function requireFind() {
if (hasRequiredFind) return find$2;
hasRequiredFind = 1;
// @MAJOR can be replaced by native Array#find when we change support
find$2 = function find(array, comparator) {
if (!Array.isArray(array)) {
return undefined;
}
for(var i = 0; i < array.length; i++){
if (comparator(array[i])) {
return array[i];
}
}
return undefined;
};
return find$2;
}
var intersection_1;
var hasRequiredIntersection;
function requireIntersection() {
if (hasRequiredIntersection) return intersection_1;
hasRequiredIntersection = 1;
function intersection(arr1, arr2) {
return arr1.filter(function(value, index) {
return arr2.indexOf(value) > -1 && arr1.indexOf(value) === index /* skips duplicates */ ;
});
}
intersection_1 = intersection;
return intersection_1;
}
var valToNumber_1;
var hasRequiredValToNumber;
function requireValToNumber() {
if (hasRequiredValToNumber) return valToNumber_1;
hasRequiredValToNumber = 1;
function valToNumber(v) {
if (typeof v === 'number') {
return v;
} else if (typeof v === 'string') {
return parseFloat(v);
} else if (Array.isArray(v)) {
return v.map(valToNumber);
}
throw new Error('The value should be a number, a parsable string or an array of those.');
}
valToNumber_1 = valToNumber;
return valToNumber_1;
}
var isValidUserToken;
var hasRequiredIsValidUserToken;
function requireIsValidUserToken() {
if (hasRequiredIsValidUserToken) return isValidUserToken;
hasRequiredIsValidUserToken = 1;
isValidUserToken = function isValidUserToken(userToken) {
if (userToken === null) {
return false;
}
return /^[a-zA-Z0-9_-]{1,64}$/.test(userToken);
};
return isValidUserToken;
}
var RefinementList$2;
var hasRequiredRefinementList;
function requireRefinementList() {
if (hasRequiredRefinementList) return RefinementList$2;
hasRequiredRefinementList = 1;
/**
* Functions to manipulate refinement lists
*
* The RefinementList is not formally defined through a prototype but is based
* on a specific structure.
*
* @module SearchParameters.refinementList
*
* @typedef {string[]} SearchParameters.refinementList.Refinements
* @typedef {Object.<string, SearchParameters.refinementList.Refinements>} SearchParameters.refinementList.RefinementList
*/ var defaultsPure = requireDefaultsPure();
var objectHasKeys = requireObjectHasKeys();
var omit = requireOmit();
var lib = {
/**
* Adds a refinement to a RefinementList
* @param {RefinementList} refinementList the initial list
* @param {string} attribute the attribute to refine
* @param {string} value the value of the refinement, if the value is not a string it will be converted
* @return {RefinementList} a new and updated refinement list
*/ addRefinement: function addRefinement(refinementList, attribute, value) {
if (lib.isRefined(refinementList, attribute, value)) {
return refinementList;
}
var valueAsString = '' + value;
var facetRefinement = !refinementList[attribute] ? [
valueAsString
] : refinementList[attribute].concat(valueAsString);
var mod = {};
mod[attribute] = facetRefinement;
return defaultsPure(mod, refinementList);
},
/**
* Removes refinement(s) for an attribute:
* - if the value is specified removes the refinement for the value on the attribute
* - if no value is specified removes all the refinements for this attribute
* @param {RefinementList} refinementList the initial list
* @param {string} attribute the attribute to refine
* @param {string} [value] the value of the refinement
* @return {RefinementList} a new and updated refinement lst
*/ removeRefinement: function removeRefinement(refinementList, attribute, value) {
if (value === undefined) {
// we use the "filter" form of clearRefinement, since it leaves empty values as-is
// the form with a string will remove the attribute completely
return lib.clearRefinement(ref