mirror of
https://github.com/rancher/rancher-docs.git
synced 2026-09-26 21:18:04 +00:00
6787 lines
230 KiB
JavaScript
6787 lines
230 KiB
JavaScript
(self["webpackChunkrancher_docs"] = self["webpackChunkrancher_docs"] || []).push([[97920],{
|
|
|
|
/***/ 17331:
|
|
/***/ ((module) => {
|
|
|
|
// 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;
|
|
}
|
|
module.exports = 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;
|
|
}
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 8131:
|
|
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
|
|
var AlgoliaSearchHelper = __webpack_require__(49374);
|
|
var SearchParameters = __webpack_require__(17775);
|
|
var SearchResults = __webpack_require__(23076);
|
|
|
|
/**
|
|
* The algoliasearchHelper module is the function that will let its
|
|
* contains everything needed to use the Algoliasearch
|
|
* Helper. It is a also a function that instanciate the helper.
|
|
* To use the helper, you also need the Algolia JS client v3.
|
|
* @example
|
|
* //using the UMD build
|
|
* var client = algoliasearch('latency', '6be0576ff61c053d5f9a3225e2a90f76');
|
|
* var helper = algoliasearchHelper(client, 'bestbuy', {
|
|
* facets: ['shipping'],
|
|
* disjunctiveFacets: ['category']
|
|
* });
|
|
* helper.on('result', function(event) {
|
|
* console.log(event.results);
|
|
* });
|
|
* helper
|
|
* .toggleFacetRefinement('category', 'Movies & TV Shows')
|
|
* .toggleFacetRefinement('shipping', 'Free shipping')
|
|
* .search();
|
|
* @example
|
|
* // The helper is an event emitter using the node API
|
|
* helper.on('result', updateTheResults);
|
|
* helper.once('result', updateTheResults);
|
|
* helper.removeListener('result', updateTheResults);
|
|
* helper.removeAllListeners('result');
|
|
* @module algoliasearchHelper
|
|
* @param {AlgoliaSearch} client an AlgoliaSearch client
|
|
* @param {string} index the name of the index to query
|
|
* @param {SearchParameters|object} opts an object defining the initial config of the search. It doesn't have to be a {SearchParameters}, just an object containing the properties you need from it.
|
|
* @return {AlgoliaSearchHelper} The helper instance
|
|
*/
|
|
function algoliasearchHelper(client, index, opts) {
|
|
return new AlgoliaSearchHelper(client, index, opts);
|
|
}
|
|
|
|
/**
|
|
* The version currently used
|
|
* @member module:algoliasearchHelper.version
|
|
* @type {number}
|
|
*/
|
|
algoliasearchHelper.version = __webpack_require__(24336);
|
|
|
|
/**
|
|
* Constructor for the Helper.
|
|
* @member module:algoliasearchHelper.AlgoliaSearchHelper
|
|
* @type {AlgoliaSearchHelper}
|
|
*/
|
|
algoliasearchHelper.AlgoliaSearchHelper = AlgoliaSearchHelper;
|
|
|
|
/**
|
|
* Constructor for the object containing all the parameters of the search.
|
|
* @member module:algoliasearchHelper.SearchParameters
|
|
* @type {SearchParameters}
|
|
*/
|
|
algoliasearchHelper.SearchParameters = SearchParameters;
|
|
|
|
/**
|
|
* Constructor for the object containing the results of the search.
|
|
* @member module:algoliasearchHelper.SearchResults
|
|
* @type {SearchResults}
|
|
*/
|
|
algoliasearchHelper.SearchResults = SearchResults;
|
|
|
|
module.exports = algoliasearchHelper;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 68078:
|
|
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
|
|
var EventEmitter = __webpack_require__(17331);
|
|
|
|
var inherits = __webpack_require__(14853);
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
function DerivedHelper(mainHelper, fn) {
|
|
this.main = mainHelper;
|
|
this.fn = fn;
|
|
this.lastResults = 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);
|
|
};
|
|
|
|
module.exports = DerivedHelper;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 82437:
|
|
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
|
|
/**
|
|
* 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 = __webpack_require__(52344);
|
|
var objectHasKeys = __webpack_require__(90116);
|
|
var omit = __webpack_require__(49803);
|
|
|
|
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(refinementList, function (v, f) {
|
|
return attribute === f;
|
|
});
|
|
}
|
|
|
|
var valueAsString = '' + value;
|
|
|
|
return lib.clearRefinement(refinementList, function (v, f) {
|
|
return attribute === f && valueAsString === v;
|
|
});
|
|
},
|
|
/**
|
|
* Toggles the refinement value for an 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 list
|
|
*/
|
|
toggleRefinement: function toggleRefinement(
|
|
refinementList,
|
|
attribute,
|
|
value
|
|
) {
|
|
if (value === undefined)
|
|
throw new Error('toggleRefinement should be used with a value');
|
|
|
|
if (lib.isRefined(refinementList, attribute, value)) {
|
|
return lib.removeRefinement(refinementList, attribute, value);
|
|
}
|
|
|
|
return lib.addRefinement(refinementList, attribute, value);
|
|
},
|
|
/**
|
|
* Clear all or parts of a RefinementList. Depending on the arguments, three
|
|
* kinds of behavior can happen:
|
|
* - if no attribute is provided: clears the whole list
|
|
* - if an attribute is provided as a string: clears the list for the specific attribute
|
|
* - if an attribute is provided as a function: discards the elements for which the function returns true
|
|
* @param {RefinementList} refinementList the initial list
|
|
* @param {string} [attribute] the attribute or function to discard
|
|
* @param {string} [refinementType] optional parameter to give more context to the attribute function
|
|
* @return {RefinementList} a new and updated refinement list
|
|
*/
|
|
clearRefinement: function clearRefinement(
|
|
refinementList,
|
|
attribute,
|
|
refinementType
|
|
) {
|
|
if (attribute === undefined) {
|
|
// return the same object if the list is already empty
|
|
// this is mainly for tests, as it doesn't have much impact on performance
|
|
if (!objectHasKeys(refinementList)) {
|
|
return refinementList;
|
|
}
|
|
return {};
|
|
} else if (typeof attribute === 'string') {
|
|
return omit(refinementList, [attribute]);
|
|
} else if (typeof attribute === 'function') {
|
|
var hasChanged = false;
|
|
|
|
var newRefinementList = Object.keys(refinementList).reduce(function (
|
|
memo,
|
|
key
|
|
) {
|
|
var values = refinementList[key] || [];
|
|
var facetList = values.filter(function (value) {
|
|
return !attribute(value, key, refinementType);
|
|
});
|
|
|
|
if (facetList.length !== values.length) {
|
|
hasChanged = true;
|
|
}
|
|
|
|
memo[key] = facetList;
|
|
|
|
return memo;
|
|
},
|
|
{});
|
|
|
|
if (hasChanged) return newRefinementList;
|
|
return refinementList;
|
|
}
|
|
|
|
// We return nothing if the attribute is not undefined, a string or a function,
|
|
// as it is not a valid value for a refinement
|
|
return undefined;
|
|
},
|
|
/**
|
|
* Test if the refinement value is used for the attribute. If no refinement value
|
|
* is provided, test if the refinementList contains any refinement for the
|
|
* given attribute.
|
|
* @param {RefinementList} refinementList the list of refinement
|
|
* @param {string} attribute name of the attribute
|
|
* @param {string} [refinementValue] value of the filter/refinement
|
|
* @return {boolean} true if the attribute is refined, false otherwise
|
|
*/
|
|
isRefined: function isRefined(refinementList, attribute, refinementValue) {
|
|
var containsRefinements =
|
|
Boolean(refinementList[attribute]) &&
|
|
refinementList[attribute].length > 0;
|
|
|
|
if (refinementValue === undefined || !containsRefinements) {
|
|
return containsRefinements;
|
|
}
|
|
|
|
var refinementValueAsString = '' + refinementValue;
|
|
|
|
return refinementList[attribute].indexOf(refinementValueAsString) !== -1;
|
|
},
|
|
};
|
|
|
|
module.exports = lib;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 17775:
|
|
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
|
|
var defaultsPure = __webpack_require__(52344);
|
|
var find = __webpack_require__(7888);
|
|
var intersection = __webpack_require__(22686);
|
|
var merge = __webpack_require__(60185);
|
|
var objectHasKeys = __webpack_require__(90116);
|
|
var omit = __webpack_require__(49803);
|
|
var valToNumber = __webpack_require__(28023);
|
|
var isValidUserToken = __webpack_require__(46801);
|
|
|
|
var RefinementList = __webpack_require__(82437);
|
|
|
|
/**
|
|
* isEqual, but only for numeric refinement values, possible values:
|
|
* - 5
|
|
* - [5]
|
|
* - [[5]]
|
|
* - [[5,5],[4]]
|
|
* @param {any} a numeric refinement value
|
|
* @param {any} b numeric refinement value
|
|
* @return {boolean} true if the values are equal
|
|
*/
|
|
function isEqualNumericRefinement(a, b) {
|
|
if (Array.isArray(a) && Array.isArray(b)) {
|
|
return (
|
|
a.length === b.length &&
|
|
a.every(function (el, i) {
|
|
return isEqualNumericRefinement(b[i], el);
|
|
})
|
|
);
|
|
}
|
|
return a === b;
|
|
}
|
|
|
|
/**
|
|
* like _.find but using deep equality to be able to use it
|
|
* to find arrays.
|
|
* @private
|
|
* @param {any[]} array array to search into (elements are base or array of base)
|
|
* @param {any} searchedValue the value we're looking for (base or array of base)
|
|
* @return {any} the searched value or undefined
|
|
*/
|
|
function findArray(array, searchedValue) {
|
|
return find(array, function (currentValue) {
|
|
return isEqualNumericRefinement(currentValue, searchedValue);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* The facet list is the structure used to store the list of values used to
|
|
* filter a single attribute.
|
|
* @typedef {string[]} SearchParameters.FacetList
|
|
*/
|
|
|
|
/**
|
|
* Structure to store numeric filters with the operator as the key. The supported operators
|
|
* are `=`, `>`, `<`, `>=`, `<=` and `!=`.
|
|
* @typedef {Object.<string, Array.<number|number[]>>} SearchParameters.OperatorList
|
|
*/
|
|
|
|
/**
|
|
* SearchParameters is the data structure that contains all the information
|
|
* usable for making a search to 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. This object is documented for reference as you'll
|
|
* get it from events generated by the {@link AlgoliaSearchHelper}.
|
|
* If need be, instantiate the Helper from the factory function {@link SearchParameters.make}
|
|
* @constructor
|
|
* @classdesc contains all the parameters of a search
|
|
* @param {object|SearchParameters} newParameters existing parameters or partial object
|
|
* for the properties of a new SearchParameters
|
|
* @see SearchParameters.make
|
|
* @example <caption>SearchParameters of the first query in
|
|
* <a href="http://demos.algolia.com/instant-search-demo/">the instant search demo</a></caption>
|
|
{
|
|
"query": "",
|
|
"disjunctiveFacets": [
|
|
"customerReviewCount",
|
|
"category",
|
|
"salePrice_range",
|
|
"manufacturer"
|
|
],
|
|
"maxValuesPerFacet": 30,
|
|
"page": 0,
|
|
"hitsPerPage": 10,
|
|
"facets": [
|
|
"type",
|
|
"shipping"
|
|
]
|
|
}
|
|
*/
|
|
function SearchParameters(newParameters) {
|
|
var params = newParameters
|
|
? SearchParameters._parseNumbers(newParameters)
|
|
: {};
|
|
|
|
if (params.userToken !== undefined && !isValidUserToken(params.userToken)) {
|
|
// eslint-disable-next-line no-console
|
|
console.warn(
|
|
'[algoliasearch-helper] The `userToken` parameter is invalid. This can lead to wrong analytics.\n - Format: [a-zA-Z0-9_-]{1,64}'
|
|
);
|
|
}
|
|
/**
|
|
* This attribute contains the list of all the conjunctive facets
|
|
* used. This list will be added to requested facets in the
|
|
* [facets attribute](https://www.algolia.com/doc/rest-api/search#param-facets) sent to algolia.
|
|
* @member {string[]}
|
|
*/
|
|
this.facets = params.facets || [];
|
|
/**
|
|
* This attribute contains the list of all the disjunctive facets
|
|
* used. This list will be added to requested facets in the
|
|
* [facets attribute](https://www.algolia.com/doc/rest-api/search#param-facets) sent to algolia.
|
|
* @member {string[]}
|
|
*/
|
|
this.disjunctiveFacets = params.disjunctiveFacets || [];
|
|
/**
|
|
* This attribute contains the list of all the hierarchical facets
|
|
* used. This list will be added to requested facets in the
|
|
* [facets attribute](https://www.algolia.com/doc/rest-api/search#param-facets) sent to algolia.
|
|
* Hierarchical facets are a sub type of disjunctive facets that
|
|
* let you filter faceted attributes hierarchically.
|
|
* @member {string[]|object[]}
|
|
*/
|
|
this.hierarchicalFacets = params.hierarchicalFacets || [];
|
|
|
|
// Refinements
|
|
/**
|
|
* This attribute contains all the filters that need to be
|
|
* applied on the conjunctive facets. Each facet must be properly
|
|
* defined in the `facets` attribute.
|
|
*
|
|
* The key is the name of the facet, and the `FacetList` contains all
|
|
* filters selected for the associated facet name.
|
|
*
|
|
* When querying algolia, the values stored in this attribute will
|
|
* be translated into the `facetFilters` attribute.
|
|
* @member {Object.<string, SearchParameters.FacetList>}
|
|
*/
|
|
this.facetsRefinements = params.facetsRefinements || {};
|
|
/**
|
|
* This attribute contains all the filters that need to be
|
|
* excluded from the conjunctive facets. Each facet must be properly
|
|
* defined in the `facets` attribute.
|
|
*
|
|
* The key is the name of the facet, and the `FacetList` contains all
|
|
* filters excluded for the associated facet name.
|
|
*
|
|
* When querying algolia, the values stored in this attribute will
|
|
* be translated into the `facetFilters` attribute.
|
|
* @member {Object.<string, SearchParameters.FacetList>}
|
|
*/
|
|
this.facetsExcludes = params.facetsExcludes || {};
|
|
/**
|
|
* This attribute contains all the filters that need to be
|
|
* applied on the disjunctive facets. Each facet must be properly
|
|
* defined in the `disjunctiveFacets` attribute.
|
|
*
|
|
* The key is the name of the facet, and the `FacetList` contains all
|
|
* filters selected for the associated facet name.
|
|
*
|
|
* When querying algolia, the values stored in this attribute will
|
|
* be translated into the `facetFilters` attribute.
|
|
* @member {Object.<string, SearchParameters.FacetList>}
|
|
*/
|
|
this.disjunctiveFacetsRefinements = params.disjunctiveFacetsRefinements || {};
|
|
/**
|
|
* This attribute contains all the filters that need to be
|
|
* applied on the numeric attributes.
|
|
*
|
|
* The key is the name of the attribute, and the value is the
|
|
* filters to apply to this attribute.
|
|
*
|
|
* When querying algolia, the values stored in this attribute will
|
|
* be translated into the `numericFilters` attribute.
|
|
* @member {Object.<string, SearchParameters.OperatorList>}
|
|
*/
|
|
this.numericRefinements = params.numericRefinements || {};
|
|
/**
|
|
* This attribute contains all the tags used to refine the query.
|
|
*
|
|
* When querying algolia, the values stored in this attribute will
|
|
* be translated into the `tagFilters` attribute.
|
|
* @member {string[]}
|
|
*/
|
|
this.tagRefinements = params.tagRefinements || [];
|
|
/**
|
|
* This attribute contains all the filters that need to be
|
|
* applied on the hierarchical facets. Each facet must be properly
|
|
* defined in the `hierarchicalFacets` attribute.
|
|
*
|
|
* The key is the name of the facet, and the `FacetList` contains all
|
|
* filters selected for the associated facet name. The FacetList values
|
|
* are structured as a string that contain the values for each level
|
|
* separated by the configured separator.
|
|
*
|
|
* When querying algolia, the values stored in this attribute will
|
|
* be translated into the `facetFilters` attribute.
|
|
* @member {Object.<string, SearchParameters.FacetList>}
|
|
*/
|
|
this.hierarchicalFacetsRefinements =
|
|
params.hierarchicalFacetsRefinements || {};
|
|
|
|
// eslint-disable-next-line consistent-this
|
|
var self = this;
|
|
Object.keys(params).forEach(function (paramName) {
|
|
var isKeyKnown = SearchParameters.PARAMETERS.indexOf(paramName) !== -1;
|
|
var isValueDefined = params[paramName] !== undefined;
|
|
|
|
if (!isKeyKnown && isValueDefined) {
|
|
self[paramName] = params[paramName];
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* List all the properties in SearchParameters and therefore all the known Algolia properties
|
|
* This doesn't contain any beta/hidden features.
|
|
* @private
|
|
*/
|
|
SearchParameters.PARAMETERS = Object.keys(new SearchParameters());
|
|
|
|
/**
|
|
* @private
|
|
* @param {object} partialState full or part of a state
|
|
* @return {object} a new object with the number keys as number
|
|
*/
|
|
SearchParameters._parseNumbers = function (partialState) {
|
|
// Do not parse numbers again in SearchParameters, they ought to be parsed already
|
|
if (partialState instanceof SearchParameters) return partialState;
|
|
|
|
var numbers = {};
|
|
|
|
var numberKeys = [
|
|
'aroundPrecision',
|
|
'aroundRadius',
|
|
'getRankingInfo',
|
|
'minWordSizefor2Typos',
|
|
'minWordSizefor1Typo',
|
|
'page',
|
|
'maxValuesPerFacet',
|
|
'distinct',
|
|
'minimumAroundRadius',
|
|
'hitsPerPage',
|
|
'minProximity',
|
|
];
|
|
|
|
numberKeys.forEach(function (k) {
|
|
var value = partialState[k];
|
|
if (typeof value === 'string') {
|
|
var parsedValue = parseFloat(value);
|
|
// global isNaN is ok to use here, value is only number or NaN
|
|
numbers[k] = isNaN(parsedValue) ? value : parsedValue;
|
|
}
|
|
});
|
|
|
|
// there's two formats of insideBoundingBox, we need to parse
|
|
// the one which is an array of float geo rectangles
|
|
if (Array.isArray(partialState.insideBoundingBox)) {
|
|
numbers.insideBoundingBox = partialState.insideBoundingBox.map(function (
|
|
geoRect
|
|
) {
|
|
if (Array.isArray(geoRect)) {
|
|
return geoRect.map(function (value) {
|
|
return parseFloat(value);
|
|
});
|
|
}
|
|
return geoRect;
|
|
});
|
|
}
|
|
|
|
if (partialState.numericRefinements) {
|
|
var numericRefinements = {};
|
|
Object.keys(partialState.numericRefinements).forEach(function (attribute) {
|
|
var operators = partialState.numericRefinements[attribute] || {};
|
|
numericRefinements[attribute] = {};
|
|
Object.keys(operators).forEach(function (operator) {
|
|
var values = operators[operator];
|
|
var parsedValues = values.map(function (v) {
|
|
if (Array.isArray(v)) {
|
|
return v.map(function (vPrime) {
|
|
if (typeof vPrime === 'string') {
|
|
return parseFloat(vPrime);
|
|
}
|
|
return vPrime;
|
|
});
|
|
} else if (typeof v === 'string') {
|
|
return parseFloat(v);
|
|
}
|
|
return v;
|
|
});
|
|
numericRefinements[attribute][operator] = parsedValues;
|
|
});
|
|
});
|
|
numbers.numericRefinements = numericRefinements;
|
|
}
|
|
|
|
return merge({}, partialState, numbers);
|
|
};
|
|
|
|
/**
|
|
* Factory for SearchParameters
|
|
* @param {object|SearchParameters} newParameters existing parameters or partial
|
|
* object for the properties of a new SearchParameters
|
|
* @return {SearchParameters} frozen instance of SearchParameters
|
|
*/
|
|
SearchParameters.make = function makeSearchParameters(newParameters) {
|
|
var instance = new SearchParameters(newParameters);
|
|
|
|
var hierarchicalFacets = newParameters.hierarchicalFacets || [];
|
|
hierarchicalFacets.forEach(function (facet) {
|
|
if (facet.rootPath) {
|
|
var currentRefinement = instance.getHierarchicalRefinement(facet.name);
|
|
|
|
if (
|
|
currentRefinement.length > 0 &&
|
|
currentRefinement[0].indexOf(facet.rootPath) !== 0
|
|
) {
|
|
instance = instance.clearRefinements(facet.name);
|
|
}
|
|
|
|
// get it again in case it has been cleared
|
|
currentRefinement = instance.getHierarchicalRefinement(facet.name);
|
|
if (currentRefinement.length === 0) {
|
|
instance = instance.toggleHierarchicalFacetRefinement(
|
|
facet.name,
|
|
facet.rootPath
|
|
);
|
|
}
|
|
}
|
|
});
|
|
|
|
return instance;
|
|
};
|
|
|
|
/**
|
|
* Validates the new parameters based on the previous state
|
|
* @param {SearchParameters} currentState the current state
|
|
* @param {object|SearchParameters} parameters the new parameters to set
|
|
* @return {Error|null} Error if the modification is invalid, null otherwise
|
|
*/
|
|
SearchParameters.validate = function (currentState, parameters) {
|
|
var params = parameters || {};
|
|
|
|
if (
|
|
currentState.tagFilters &&
|
|
params.tagRefinements &&
|
|
params.tagRefinements.length > 0
|
|
) {
|
|
return new Error(
|
|
'[Tags] Cannot switch from the managed tag API to the advanced API. It is probably ' +
|
|
'an error, if it is really what you want, you should first clear the tags with clearTags method.'
|
|
);
|
|
}
|
|
|
|
if (currentState.tagRefinements.length > 0 && params.tagFilters) {
|
|
return new Error(
|
|
'[Tags] Cannot switch from the advanced tag API to the managed API. It is probably ' +
|
|
'an error, if it is not, you should first clear the tags with clearTags method.'
|
|
);
|
|
}
|
|
|
|
if (
|
|
currentState.numericFilters &&
|
|
params.numericRefinements &&
|
|
objectHasKeys(params.numericRefinements)
|
|
) {
|
|
return new Error(
|
|
"[Numeric filters] Can't switch from the advanced to the managed API. It" +
|
|
' is probably an error, if this is really what you want, you have to first' +
|
|
' clear the numeric filters.'
|
|
);
|
|
}
|
|
|
|
if (objectHasKeys(currentState.numericRefinements) && params.numericFilters) {
|
|
return new Error(
|
|
"[Numeric filters] Can't switch from the managed API to the advanced. It" +
|
|
' is probably an error, if this is really what you want, you have to first' +
|
|
' clear the numeric filters.'
|
|
);
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
SearchParameters.prototype = {
|
|
constructor: SearchParameters,
|
|
|
|
/**
|
|
* Remove all refinements (disjunctive + conjunctive + excludes + numeric filters)
|
|
* @method
|
|
* @param {undefined|string|SearchParameters.clearCallback} [attribute] optional string or function
|
|
* - If not given, means to clear all the filters.
|
|
* - If `string`, means to clear all refinements for the `attribute` named filter.
|
|
* - If `function`, means to clear all the refinements that return truthy values.
|
|
* @return {SearchParameters} new instance with filters cleared
|
|
*/
|
|
clearRefinements: function clearRefinements(attribute) {
|
|
var patch = {
|
|
numericRefinements: this._clearNumericRefinements(attribute),
|
|
facetsRefinements: RefinementList.clearRefinement(
|
|
this.facetsRefinements,
|
|
attribute,
|
|
'conjunctiveFacet'
|
|
),
|
|
facetsExcludes: RefinementList.clearRefinement(
|
|
this.facetsExcludes,
|
|
attribute,
|
|
'exclude'
|
|
),
|
|
disjunctiveFacetsRefinements: RefinementList.clearRefinement(
|
|
this.disjunctiveFacetsRefinements,
|
|
attribute,
|
|
'disjunctiveFacet'
|
|
),
|
|
hierarchicalFacetsRefinements: RefinementList.clearRefinement(
|
|
this.hierarchicalFacetsRefinements,
|
|
attribute,
|
|
'hierarchicalFacet'
|
|
),
|
|
};
|
|
if (
|
|
patch.numericRefinements === this.numericRefinements &&
|
|
patch.facetsRefinements === this.facetsRefinements &&
|
|
patch.facetsExcludes === this.facetsExcludes &&
|
|
patch.disjunctiveFacetsRefinements ===
|
|
this.disjunctiveFacetsRefinements &&
|
|
patch.hierarchicalFacetsRefinements === this.hierarchicalFacetsRefinements
|
|
) {
|
|
return this;
|
|
}
|
|
return this.setQueryParameters(patch);
|
|
},
|
|
/**
|
|
* Remove all the refined tags from the SearchParameters
|
|
* @method
|
|
* @return {SearchParameters} new instance with tags cleared
|
|
*/
|
|
clearTags: function clearTags() {
|
|
if (this.tagFilters === undefined && this.tagRefinements.length === 0)
|
|
return this;
|
|
|
|
return this.setQueryParameters({
|
|
tagFilters: undefined,
|
|
tagRefinements: [],
|
|
});
|
|
},
|
|
/**
|
|
* Set the index.
|
|
* @method
|
|
* @param {string} index the index name
|
|
* @return {SearchParameters} new instance
|
|
*/
|
|
setIndex: function setIndex(index) {
|
|
if (index === this.index) return this;
|
|
|
|
return this.setQueryParameters({
|
|
index: index,
|
|
});
|
|
},
|
|
/**
|
|
* Query setter
|
|
* @method
|
|
* @param {string} newQuery value for the new query
|
|
* @return {SearchParameters} new instance
|
|
*/
|
|
setQuery: function setQuery(newQuery) {
|
|
if (newQuery === this.query) return this;
|
|
|
|
return this.setQueryParameters({
|
|
query: newQuery,
|
|
});
|
|
},
|
|
/**
|
|
* Page setter
|
|
* @method
|
|
* @param {number} newPage new page number
|
|
* @return {SearchParameters} new instance
|
|
*/
|
|
setPage: function setPage(newPage) {
|
|
if (newPage === this.page) return this;
|
|
|
|
return this.setQueryParameters({
|
|
page: newPage,
|
|
});
|
|
},
|
|
/**
|
|
* Facets setter
|
|
* The facets are the simple facets, used for conjunctive (and) faceting.
|
|
* @method
|
|
* @param {string[]} facets all the attributes of the algolia records used for conjunctive faceting
|
|
* @return {SearchParameters} new instance
|
|
*/
|
|
setFacets: function setFacets(facets) {
|
|
return this.setQueryParameters({
|
|
facets: facets,
|
|
});
|
|
},
|
|
/**
|
|
* Disjunctive facets setter
|
|
* Change the list of disjunctive (or) facets the helper chan handle.
|
|
* @method
|
|
* @param {string[]} facets all the attributes of the algolia records used for disjunctive faceting
|
|
* @return {SearchParameters} new instance
|
|
*/
|
|
setDisjunctiveFacets: function setDisjunctiveFacets(facets) {
|
|
return this.setQueryParameters({
|
|
disjunctiveFacets: facets,
|
|
});
|
|
},
|
|
/**
|
|
* HitsPerPage setter
|
|
* Hits per page represents the number of hits retrieved for this query
|
|
* @method
|
|
* @param {number} n number of hits retrieved per page of results
|
|
* @return {SearchParameters} new instance
|
|
*/
|
|
setHitsPerPage: function setHitsPerPage(n) {
|
|
if (this.hitsPerPage === n) return this;
|
|
|
|
return this.setQueryParameters({
|
|
hitsPerPage: n,
|
|
});
|
|
},
|
|
/**
|
|
* typoTolerance setter
|
|
* Set the value of typoTolerance
|
|
* @method
|
|
* @param {string} typoTolerance new value of typoTolerance ("true", "false", "min" or "strict")
|
|
* @return {SearchParameters} new instance
|
|
*/
|
|
setTypoTolerance: function setTypoTolerance(typoTolerance) {
|
|
if (this.typoTolerance === typoTolerance) return this;
|
|
|
|
return this.setQueryParameters({
|
|
typoTolerance: typoTolerance,
|
|
});
|
|
},
|
|
/**
|
|
* Add a numeric filter for a given attribute
|
|
* When value is an array, they are combined with OR
|
|
* When value is a single value, it will combined with AND
|
|
* @method
|
|
* @param {string} attribute attribute to set the filter on
|
|
* @param {string} operator operator of the filter (possible values: =, >, >=, <, <=, !=)
|
|
* @param {number | number[]} value value of the filter
|
|
* @return {SearchParameters} new instance
|
|
* @example
|
|
* // for price = 50 or 40
|
|
* state.addNumericRefinement('price', '=', [50, 40]);
|
|
* @example
|
|
* // for size = 38 and 40
|
|
* state.addNumericRefinement('size', '=', 38);
|
|
* state.addNumericRefinement('size', '=', 40);
|
|
*/
|
|
addNumericRefinement: function (attribute, operator, value) {
|
|
var val = valToNumber(value);
|
|
|
|
if (this.isNumericRefined(attribute, operator, val)) return this;
|
|
|
|
var mod = merge({}, this.numericRefinements);
|
|
|
|
mod[attribute] = merge({}, mod[attribute]);
|
|
|
|
if (mod[attribute][operator]) {
|
|
// Array copy
|
|
mod[attribute][operator] = mod[attribute][operator].slice();
|
|
// Add the element. Concat can't be used here because value can be an array.
|
|
mod[attribute][operator].push(val);
|
|
} else {
|
|
mod[attribute][operator] = [val];
|
|
}
|
|
|
|
return this.setQueryParameters({
|
|
numericRefinements: mod,
|
|
});
|
|
},
|
|
/**
|
|
* Get the list of conjunctive refinements for a single facet
|
|
* @param {string} facetName name of the attribute used for faceting
|
|
* @return {string[]} list of refinements
|
|
*/
|
|
getConjunctiveRefinements: function (facetName) {
|
|
if (!this.isConjunctiveFacet(facetName)) {
|
|
return [];
|
|
}
|
|
return this.facetsRefinements[facetName] || [];
|
|
},
|
|
/**
|
|
* Get the list of disjunctive refinements for a single facet
|
|
* @param {string} facetName name of the attribute used for faceting
|
|
* @return {string[]} list of refinements
|
|
*/
|
|
getDisjunctiveRefinements: function (facetName) {
|
|
if (!this.isDisjunctiveFacet(facetName)) {
|
|
return [];
|
|
}
|
|
return this.disjunctiveFacetsRefinements[facetName] || [];
|
|
},
|
|
/**
|
|
* Get the list of hierarchical refinements for a single facet
|
|
* @param {string} facetName name of the attribute used for faceting
|
|
* @return {string[]} list of refinements
|
|
*/
|
|
getHierarchicalRefinement: function (facetName) {
|
|
// we send an array but we currently do not support multiple
|
|
// hierarchicalRefinements for a hierarchicalFacet
|
|
return this.hierarchicalFacetsRefinements[facetName] || [];
|
|
},
|
|
/**
|
|
* Get the list of exclude refinements for a single facet
|
|
* @param {string} facetName name of the attribute used for faceting
|
|
* @return {string[]} list of refinements
|
|
*/
|
|
getExcludeRefinements: function (facetName) {
|
|
if (!this.isConjunctiveFacet(facetName)) {
|
|
return [];
|
|
}
|
|
return this.facetsExcludes[facetName] || [];
|
|
},
|
|
|
|
/**
|
|
* Remove all the numeric filter for a given (attribute, operator)
|
|
* @method
|
|
* @param {string} attribute attribute to set the filter on
|
|
* @param {string} [operator] operator of the filter (possible values: =, >, >=, <, <=, !=)
|
|
* @param {number} [number] the value to be removed
|
|
* @return {SearchParameters} new instance
|
|
*/
|
|
removeNumericRefinement: function (attribute, operator, number) {
|
|
var paramValue = number;
|
|
if (paramValue !== undefined) {
|
|
if (!this.isNumericRefined(attribute, operator, paramValue)) {
|
|
return this;
|
|
}
|
|
return this.setQueryParameters({
|
|
numericRefinements: this._clearNumericRefinements(function (
|
|
value,
|
|
key
|
|
) {
|
|
return (
|
|
key === attribute &&
|
|
value.op === operator &&
|
|
isEqualNumericRefinement(value.val, valToNumber(paramValue))
|
|
);
|
|
}),
|
|
});
|
|
} else if (operator !== undefined) {
|
|
if (!this.isNumericRefined(attribute, operator)) return this;
|
|
return this.setQueryParameters({
|
|
numericRefinements: this._clearNumericRefinements(function (
|
|
value,
|
|
key
|
|
) {
|
|
return key === attribute && value.op === operator;
|
|
}),
|
|
});
|
|
}
|
|
|
|
if (!this.isNumericRefined(attribute)) return this;
|
|
return this.setQueryParameters({
|
|
numericRefinements: this._clearNumericRefinements(function (value, key) {
|
|
return key === attribute;
|
|
}),
|
|
});
|
|
},
|
|
/**
|
|
* Get the list of numeric refinements for a single facet
|
|
* @param {string} facetName name of the attribute used for faceting
|
|
* @return {SearchParameters.OperatorList} list of refinements
|
|
*/
|
|
getNumericRefinements: function (facetName) {
|
|
return this.numericRefinements[facetName] || {};
|
|
},
|
|
/**
|
|
* Return the current refinement for the (attribute, operator)
|
|
* @param {string} attribute attribute in the record
|
|
* @param {string} operator operator applied on the refined values
|
|
* @return {Array.<number|number[]>} refined values
|
|
*/
|
|
getNumericRefinement: function (attribute, operator) {
|
|
return (
|
|
this.numericRefinements[attribute] &&
|
|
this.numericRefinements[attribute][operator]
|
|
);
|
|
},
|
|
/**
|
|
* Clear numeric filters.
|
|
* @method
|
|
* @private
|
|
* @param {string|SearchParameters.clearCallback} [attribute] optional string or function
|
|
* - If not given, means to clear all the filters.
|
|
* - If `string`, means to clear all refinements for the `attribute` named filter.
|
|
* - If `function`, means to clear all the refinements that return truthy values.
|
|
* @return {Object.<string, OperatorList>} new numeric refinements
|
|
*/
|
|
_clearNumericRefinements: function _clearNumericRefinements(attribute) {
|
|
if (attribute === undefined) {
|
|
if (!objectHasKeys(this.numericRefinements)) {
|
|
return this.numericRefinements;
|
|
}
|
|
return {};
|
|
} else if (typeof attribute === 'string') {
|
|
return omit(this.numericRefinements, [attribute]);
|
|
} else if (typeof attribute === 'function') {
|
|
var hasChanged = false;
|
|
var numericRefinements = this.numericRefinements;
|
|
var newNumericRefinements = Object.keys(numericRefinements).reduce(
|
|
function (memo, key) {
|
|
var operators = numericRefinements[key];
|
|
var operatorList = {};
|
|
|
|
operators = operators || {};
|
|
Object.keys(operators).forEach(function (operator) {
|
|
var values = operators[operator] || [];
|
|
var outValues = [];
|
|
values.forEach(function (value) {
|
|
var predicateResult = attribute(
|
|
{ val: value, op: operator },
|
|
key,
|
|
'numeric'
|
|
);
|
|
if (!predicateResult) outValues.push(value);
|
|
});
|
|
if (outValues.length !== values.length) {
|
|
hasChanged = true;
|
|
}
|
|
operatorList[operator] = outValues;
|
|
});
|
|
|
|
memo[key] = operatorList;
|
|
|
|
return memo;
|
|
},
|
|
{}
|
|
);
|
|
|
|
if (hasChanged) return newNumericRefinements;
|
|
return this.numericRefinements;
|
|
}
|
|
|
|
// We return nothing if the attribute is not undefined, a string or a function,
|
|
// as it is not a valid value for a refinement
|
|
return undefined;
|
|
},
|
|
/**
|
|
* Add a facet to the facets attribute of the helper configuration, if it
|
|
* isn't already present.
|
|
* @method
|
|
* @param {string} facet facet name to add
|
|
* @return {SearchParameters} new instance
|
|
*/
|
|
addFacet: function addFacet(facet) {
|
|
if (this.isConjunctiveFacet(facet)) {
|
|
return this;
|
|
}
|
|
|
|
return this.setQueryParameters({
|
|
facets: this.facets.concat([facet]),
|
|
});
|
|
},
|
|
/**
|
|
* Add a disjunctive facet to the disjunctiveFacets attribute of the helper
|
|
* configuration, if it isn't already present.
|
|
* @method
|
|
* @param {string} facet disjunctive facet name to add
|
|
* @return {SearchParameters} new instance
|
|
*/
|
|
addDisjunctiveFacet: function addDisjunctiveFacet(facet) {
|
|
if (this.isDisjunctiveFacet(facet)) {
|
|
return this;
|
|
}
|
|
|
|
return this.setQueryParameters({
|
|
disjunctiveFacets: this.disjunctiveFacets.concat([facet]),
|
|
});
|
|
},
|
|
/**
|
|
* Add a hierarchical facet to the hierarchicalFacets attribute of the helper
|
|
* configuration.
|
|
* @method
|
|
* @param {object} hierarchicalFacet hierarchical facet to add
|
|
* @return {SearchParameters} new instance
|
|
* @throws will throw an error if a hierarchical facet with the same name was already declared
|
|
*/
|
|
addHierarchicalFacet: function addHierarchicalFacet(hierarchicalFacet) {
|
|
if (this.isHierarchicalFacet(hierarchicalFacet.name)) {
|
|
throw new Error(
|
|
'Cannot declare two hierarchical facets with the same name: `' +
|
|
hierarchicalFacet.name +
|
|
'`'
|
|
);
|
|
}
|
|
|
|
return this.setQueryParameters({
|
|
hierarchicalFacets: this.hierarchicalFacets.concat([hierarchicalFacet]),
|
|
});
|
|
},
|
|
/**
|
|
* Add a refinement on a "normal" facet
|
|
* @method
|
|
* @param {string} facet attribute to apply the faceting on
|
|
* @param {string} value value of the attribute (will be converted to string)
|
|
* @return {SearchParameters} new instance
|
|
*/
|
|
addFacetRefinement: function addFacetRefinement(facet, value) {
|
|
if (!this.isConjunctiveFacet(facet)) {
|
|
throw new Error(
|
|
facet +
|
|
' is not defined in the facets attribute of the helper configuration'
|
|
);
|
|
}
|
|
if (RefinementList.isRefined(this.facetsRefinements, facet, value))
|
|
return this;
|
|
|
|
return this.setQueryParameters({
|
|
facetsRefinements: RefinementList.addRefinement(
|
|
this.facetsRefinements,
|
|
facet,
|
|
value
|
|
),
|
|
});
|
|
},
|
|
/**
|
|
* Exclude a value from a "normal" facet
|
|
* @method
|
|
* @param {string} facet attribute to apply the exclusion on
|
|
* @param {string} value value of the attribute (will be converted to string)
|
|
* @return {SearchParameters} new instance
|
|
*/
|
|
addExcludeRefinement: function addExcludeRefinement(facet, value) {
|
|
if (!this.isConjunctiveFacet(facet)) {
|
|
throw new Error(
|
|
facet +
|
|
' is not defined in the facets attribute of the helper configuration'
|
|
);
|
|
}
|
|
if (RefinementList.isRefined(this.facetsExcludes, facet, value))
|
|
return this;
|
|
|
|
return this.setQueryParameters({
|
|
facetsExcludes: RefinementList.addRefinement(
|
|
this.facetsExcludes,
|
|
facet,
|
|
value
|
|
),
|
|
});
|
|
},
|
|
/**
|
|
* Adds a refinement on a disjunctive facet.
|
|
* @method
|
|
* @param {string} facet attribute to apply the faceting on
|
|
* @param {string} value value of the attribute (will be converted to string)
|
|
* @return {SearchParameters} new instance
|
|
*/
|
|
addDisjunctiveFacetRefinement: function addDisjunctiveFacetRefinement(
|
|
facet,
|
|
value
|
|
) {
|
|
if (!this.isDisjunctiveFacet(facet)) {
|
|
throw new Error(
|
|
facet +
|
|
' is not defined in the disjunctiveFacets attribute of the helper configuration'
|
|
);
|
|
}
|
|
|
|
if (
|
|
RefinementList.isRefined(this.disjunctiveFacetsRefinements, facet, value)
|
|
)
|
|
return this;
|
|
|
|
return this.setQueryParameters({
|
|
disjunctiveFacetsRefinements: RefinementList.addRefinement(
|
|
this.disjunctiveFacetsRefinements,
|
|
facet,
|
|
value
|
|
),
|
|
});
|
|
},
|
|
/**
|
|
* addTagRefinement adds a tag to the list used to filter the results
|
|
* @param {string} tag tag to be added
|
|
* @return {SearchParameters} new instance
|
|
*/
|
|
addTagRefinement: function addTagRefinement(tag) {
|
|
if (this.isTagRefined(tag)) return this;
|
|
|
|
var modification = {
|
|
tagRefinements: this.tagRefinements.concat(tag),
|
|
};
|
|
|
|
return this.setQueryParameters(modification);
|
|
},
|
|
/**
|
|
* Remove a facet from the facets attribute of the helper configuration, if it
|
|
* is present.
|
|
* @method
|
|
* @param {string} facet facet name to remove
|
|
* @return {SearchParameters} new instance
|
|
*/
|
|
removeFacet: function removeFacet(facet) {
|
|
if (!this.isConjunctiveFacet(facet)) {
|
|
return this;
|
|
}
|
|
|
|
return this.clearRefinements(facet).setQueryParameters({
|
|
facets: this.facets.filter(function (f) {
|
|
return f !== facet;
|
|
}),
|
|
});
|
|
},
|
|
/**
|
|
* Remove a disjunctive facet from the disjunctiveFacets attribute of the
|
|
* helper configuration, if it is present.
|
|
* @method
|
|
* @param {string} facet disjunctive facet name to remove
|
|
* @return {SearchParameters} new instance
|
|
*/
|
|
removeDisjunctiveFacet: function removeDisjunctiveFacet(facet) {
|
|
if (!this.isDisjunctiveFacet(facet)) {
|
|
return this;
|
|
}
|
|
|
|
return this.clearRefinements(facet).setQueryParameters({
|
|
disjunctiveFacets: this.disjunctiveFacets.filter(function (f) {
|
|
return f !== facet;
|
|
}),
|
|
});
|
|
},
|
|
/**
|
|
* Remove a hierarchical facet from the hierarchicalFacets attribute of the
|
|
* helper configuration, if it is present.
|
|
* @method
|
|
* @param {string} facet hierarchical facet name to remove
|
|
* @return {SearchParameters} new instance
|
|
*/
|
|
removeHierarchicalFacet: function removeHierarchicalFacet(facet) {
|
|
if (!this.isHierarchicalFacet(facet)) {
|
|
return this;
|
|
}
|
|
|
|
return this.clearRefinements(facet).setQueryParameters({
|
|
hierarchicalFacets: this.hierarchicalFacets.filter(function (f) {
|
|
return f.name !== facet;
|
|
}),
|
|
});
|
|
},
|
|
/**
|
|
* Remove a refinement set on facet. If a value is provided, it will clear the
|
|
* refinement for the given value, otherwise it will clear all the refinement
|
|
* values for the faceted attribute.
|
|
* @method
|
|
* @param {string} facet name of the attribute used for faceting
|
|
* @param {string} [value] value used to filter
|
|
* @return {SearchParameters} new instance
|
|
*/
|
|
removeFacetRefinement: function removeFacetRefinement(facet, value) {
|
|
if (!this.isConjunctiveFacet(facet)) {
|
|
throw new Error(
|
|
facet +
|
|
' is not defined in the facets attribute of the helper configuration'
|
|
);
|
|
}
|
|
if (!RefinementList.isRefined(this.facetsRefinements, facet, value))
|
|
return this;
|
|
|
|
return this.setQueryParameters({
|
|
facetsRefinements: RefinementList.removeRefinement(
|
|
this.facetsRefinements,
|
|
facet,
|
|
value
|
|
),
|
|
});
|
|
},
|
|
/**
|
|
* Remove a negative refinement on a facet
|
|
* @method
|
|
* @param {string} facet name of the attribute used for faceting
|
|
* @param {string} value value used to filter
|
|
* @return {SearchParameters} new instance
|
|
*/
|
|
removeExcludeRefinement: function removeExcludeRefinement(facet, value) {
|
|
if (!this.isConjunctiveFacet(facet)) {
|
|
throw new Error(
|
|
facet +
|
|
' is not defined in the facets attribute of the helper configuration'
|
|
);
|
|
}
|
|
if (!RefinementList.isRefined(this.facetsExcludes, facet, value))
|
|
return this;
|
|
|
|
return this.setQueryParameters({
|
|
facetsExcludes: RefinementList.removeRefinement(
|
|
this.facetsExcludes,
|
|
facet,
|
|
value
|
|
),
|
|
});
|
|
},
|
|
/**
|
|
* Remove a refinement on a disjunctive facet
|
|
* @method
|
|
* @param {string} facet name of the attribute used for faceting
|
|
* @param {string} value value used to filter
|
|
* @return {SearchParameters} new instance
|
|
*/
|
|
removeDisjunctiveFacetRefinement: function removeDisjunctiveFacetRefinement(
|
|
facet,
|
|
value
|
|
) {
|
|
if (!this.isDisjunctiveFacet(facet)) {
|
|
throw new Error(
|
|
facet +
|
|
' is not defined in the disjunctiveFacets attribute of the helper configuration'
|
|
);
|
|
}
|
|
if (
|
|
!RefinementList.isRefined(this.disjunctiveFacetsRefinements, facet, value)
|
|
)
|
|
return this;
|
|
|
|
return this.setQueryParameters({
|
|
disjunctiveFacetsRefinements: RefinementList.removeRefinement(
|
|
this.disjunctiveFacetsRefinements,
|
|
facet,
|
|
value
|
|
),
|
|
});
|
|
},
|
|
/**
|
|
* Remove a tag from the list of tag refinements
|
|
* @method
|
|
* @param {string} tag the tag to remove
|
|
* @return {SearchParameters} new instance
|
|
*/
|
|
removeTagRefinement: function removeTagRefinement(tag) {
|
|
if (!this.isTagRefined(tag)) return this;
|
|
|
|
var modification = {
|
|
tagRefinements: this.tagRefinements.filter(function (t) {
|
|
return t !== tag;
|
|
}),
|
|
};
|
|
|
|
return this.setQueryParameters(modification);
|
|
},
|
|
/**
|
|
* Generic toggle refinement method to use with facet, disjunctive facets
|
|
* and hierarchical facets
|
|
* @param {string} facet the facet to refine
|
|
* @param {string} value the associated value
|
|
* @return {SearchParameters} new instance
|
|
* @throws will throw an error if the facet is not declared in the settings of the helper
|
|
* @deprecated since version 2.19.0, see {@link SearchParameters#toggleFacetRefinement}
|
|
*/
|
|
toggleRefinement: function toggleRefinement(facet, value) {
|
|
return this.toggleFacetRefinement(facet, value);
|
|
},
|
|
/**
|
|
* Generic toggle refinement method to use with facet, disjunctive facets
|
|
* and hierarchical facets
|
|
* @param {string} facet the facet to refine
|
|
* @param {string} value the associated value
|
|
* @return {SearchParameters} new instance
|
|
* @throws will throw an error if the facet is not declared in the settings of the helper
|
|
*/
|
|
toggleFacetRefinement: function toggleFacetRefinement(facet, value) {
|
|
if (this.isHierarchicalFacet(facet)) {
|
|
return this.toggleHierarchicalFacetRefinement(facet, value);
|
|
} else if (this.isConjunctiveFacet(facet)) {
|
|
return this.toggleConjunctiveFacetRefinement(facet, value);
|
|
} else if (this.isDisjunctiveFacet(facet)) {
|
|
return this.toggleDisjunctiveFacetRefinement(facet, value);
|
|
}
|
|
|
|
throw new Error(
|
|
'Cannot refine the undeclared facet ' +
|
|
facet +
|
|
'; it should be added to the helper options facets, disjunctiveFacets or hierarchicalFacets'
|
|
);
|
|
},
|
|
/**
|
|
* Switch the refinement applied over a facet/value
|
|
* @method
|
|
* @param {string} facet name of the attribute used for faceting
|
|
* @param {value} value value used for filtering
|
|
* @return {SearchParameters} new instance
|
|
*/
|
|
toggleConjunctiveFacetRefinement: function toggleConjunctiveFacetRefinement(
|
|
facet,
|
|
value
|
|
) {
|
|
if (!this.isConjunctiveFacet(facet)) {
|
|
throw new Error(
|
|
facet +
|
|
' is not defined in the facets attribute of the helper configuration'
|
|
);
|
|
}
|
|
|
|
return this.setQueryParameters({
|
|
facetsRefinements: RefinementList.toggleRefinement(
|
|
this.facetsRefinements,
|
|
facet,
|
|
value
|
|
),
|
|
});
|
|
},
|
|
/**
|
|
* Switch the refinement applied over a facet/value
|
|
* @method
|
|
* @param {string} facet name of the attribute used for faceting
|
|
* @param {value} value value used for filtering
|
|
* @return {SearchParameters} new instance
|
|
*/
|
|
toggleExcludeFacetRefinement: function toggleExcludeFacetRefinement(
|
|
facet,
|
|
value
|
|
) {
|
|
if (!this.isConjunctiveFacet(facet)) {
|
|
throw new Error(
|
|
facet +
|
|
' is not defined in the facets attribute of the helper configuration'
|
|
);
|
|
}
|
|
|
|
return this.setQueryParameters({
|
|
facetsExcludes: RefinementList.toggleRefinement(
|
|
this.facetsExcludes,
|
|
facet,
|
|
value
|
|
),
|
|
});
|
|
},
|
|
/**
|
|
* Switch the refinement applied over a facet/value
|
|
* @method
|
|
* @param {string} facet name of the attribute used for faceting
|
|
* @param {value} value value used for filtering
|
|
* @return {SearchParameters} new instance
|
|
*/
|
|
toggleDisjunctiveFacetRefinement: function toggleDisjunctiveFacetRefinement(
|
|
facet,
|
|
value
|
|
) {
|
|
if (!this.isDisjunctiveFacet(facet)) {
|
|
throw new Error(
|
|
facet +
|
|
' is not defined in the disjunctiveFacets attribute of the helper configuration'
|
|
);
|
|
}
|
|
|
|
return this.setQueryParameters({
|
|
disjunctiveFacetsRefinements: RefinementList.toggleRefinement(
|
|
this.disjunctiveFacetsRefinements,
|
|
facet,
|
|
value
|
|
),
|
|
});
|
|
},
|
|
/**
|
|
* Switch the refinement applied over a facet/value
|
|
* @method
|
|
* @param {string} facet name of the attribute used for faceting
|
|
* @param {value} value value used for filtering
|
|
* @return {SearchParameters} new instance
|
|
*/
|
|
toggleHierarchicalFacetRefinement: function toggleHierarchicalFacetRefinement(
|
|
facet,
|
|
value
|
|
) {
|
|
if (!this.isHierarchicalFacet(facet)) {
|
|
throw new Error(
|
|
facet +
|
|
' is not defined in the hierarchicalFacets attribute of the helper configuration'
|
|
);
|
|
}
|
|
|
|
var separator = this._getHierarchicalFacetSeparator(
|
|
this.getHierarchicalFacetByName(facet)
|
|
);
|
|
|
|
var mod = {};
|
|
|
|
var upOneOrMultipleLevel =
|
|
this.hierarchicalFacetsRefinements[facet] !== undefined &&
|
|
this.hierarchicalFacetsRefinements[facet].length > 0 &&
|
|
// remove current refinement:
|
|
// refinement was 'beer > IPA', call is toggleRefine('beer > IPA'), refinement should be `beer`
|
|
(this.hierarchicalFacetsRefinements[facet][0] === value ||
|
|
// remove a parent refinement of the current refinement:
|
|
// - refinement was 'beer > IPA > Flying dog'
|
|
// - call is toggleRefine('beer > IPA')
|
|
// - refinement should be `beer`
|
|
this.hierarchicalFacetsRefinements[facet][0].indexOf(
|
|
value + separator
|
|
) === 0);
|
|
|
|
if (upOneOrMultipleLevel) {
|
|
if (value.indexOf(separator) === -1) {
|
|
// go back to root level
|
|
mod[facet] = [];
|
|
} else {
|
|
mod[facet] = [value.slice(0, value.lastIndexOf(separator))];
|
|
}
|
|
} else {
|
|
mod[facet] = [value];
|
|
}
|
|
|
|
return this.setQueryParameters({
|
|
hierarchicalFacetsRefinements: defaultsPure(
|
|
{},
|
|
mod,
|
|
this.hierarchicalFacetsRefinements
|
|
),
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Adds a refinement on a hierarchical facet.
|
|
* @param {string} facet the facet name
|
|
* @param {string} path the hierarchical facet path
|
|
* @return {SearchParameter} the new state
|
|
* @throws Error if the facet is not defined or if the facet is refined
|
|
*/
|
|
addHierarchicalFacetRefinement: function (facet, path) {
|
|
if (this.isHierarchicalFacetRefined(facet)) {
|
|
throw new Error(facet + ' is already refined.');
|
|
}
|
|
if (!this.isHierarchicalFacet(facet)) {
|
|
throw new Error(
|
|
facet +
|
|
' is not defined in the hierarchicalFacets attribute of the helper configuration.'
|
|
);
|
|
}
|
|
var mod = {};
|
|
mod[facet] = [path];
|
|
return this.setQueryParameters({
|
|
hierarchicalFacetsRefinements: defaultsPure(
|
|
{},
|
|
mod,
|
|
this.hierarchicalFacetsRefinements
|
|
),
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Removes the refinement set on a hierarchical facet.
|
|
* @param {string} facet the facet name
|
|
* @return {SearchParameter} the new state
|
|
* @throws Error if the facet is not defined or if the facet is not refined
|
|
*/
|
|
removeHierarchicalFacetRefinement: function (facet) {
|
|
if (!this.isHierarchicalFacetRefined(facet)) {
|
|
return this;
|
|
}
|
|
var mod = {};
|
|
mod[facet] = [];
|
|
return this.setQueryParameters({
|
|
hierarchicalFacetsRefinements: defaultsPure(
|
|
{},
|
|
mod,
|
|
this.hierarchicalFacetsRefinements
|
|
),
|
|
});
|
|
},
|
|
/**
|
|
* Switch the tag refinement
|
|
* @method
|
|
* @param {string} tag the tag to remove or add
|
|
* @return {SearchParameters} new instance
|
|
*/
|
|
toggleTagRefinement: function toggleTagRefinement(tag) {
|
|
if (this.isTagRefined(tag)) {
|
|
return this.removeTagRefinement(tag);
|
|
}
|
|
|
|
return this.addTagRefinement(tag);
|
|
},
|
|
/**
|
|
* Test if the facet name is from one of the disjunctive facets
|
|
* @method
|
|
* @param {string} facet facet name to test
|
|
* @return {boolean} true if facet is a disjunctive facet
|
|
*/
|
|
isDisjunctiveFacet: function (facet) {
|
|
return this.disjunctiveFacets.indexOf(facet) > -1;
|
|
},
|
|
/**
|
|
* Test if the facet name is from one of the hierarchical facets
|
|
* @method
|
|
* @param {string} facetName facet name to test
|
|
* @return {boolean} true if facetName is a hierarchical facet
|
|
*/
|
|
isHierarchicalFacet: function (facetName) {
|
|
return this.getHierarchicalFacetByName(facetName) !== undefined;
|
|
},
|
|
/**
|
|
* Test if the facet name is from one of the conjunctive/normal facets
|
|
* @method
|
|
* @param {string} facet facet name to test
|
|
* @return {boolean} true if facet is a conjunctive facet
|
|
*/
|
|
isConjunctiveFacet: function (facet) {
|
|
return this.facets.indexOf(facet) > -1;
|
|
},
|
|
/**
|
|
* Returns true if the facet is refined, either for a specific value or in
|
|
* general.
|
|
* @method
|
|
* @param {string} facet name of the attribute for used for faceting
|
|
* @param {string} value, optional value. If passed will test that this value
|
|
* is filtering the given facet.
|
|
* @return {boolean} returns true if refined
|
|
*/
|
|
isFacetRefined: function isFacetRefined(facet, value) {
|
|
if (!this.isConjunctiveFacet(facet)) {
|
|
return false;
|
|
}
|
|
return RefinementList.isRefined(this.facetsRefinements, facet, value);
|
|
},
|
|
/**
|
|
* Returns true if the facet contains exclusions or if a specific value is
|
|
* excluded.
|
|
*
|
|
* @method
|
|
* @param {string} facet name of the attribute for used for faceting
|
|
* @param {string} [value] optional value. If passed will test that this value
|
|
* is filtering the given facet.
|
|
* @return {boolean} returns true if refined
|
|
*/
|
|
isExcludeRefined: function isExcludeRefined(facet, value) {
|
|
if (!this.isConjunctiveFacet(facet)) {
|
|
return false;
|
|
}
|
|
return RefinementList.isRefined(this.facetsExcludes, facet, value);
|
|
},
|
|
/**
|
|
* Returns true if the facet contains a refinement, or if a value passed is a
|
|
* refinement for the facet.
|
|
* @method
|
|
* @param {string} facet name of the attribute for used for faceting
|
|
* @param {string} value optional, will test if the value is used for refinement
|
|
* if there is one, otherwise will test if the facet contains any refinement
|
|
* @return {boolean} true if the facet is refined
|
|
*/
|
|
isDisjunctiveFacetRefined: function isDisjunctiveFacetRefined(facet, value) {
|
|
if (!this.isDisjunctiveFacet(facet)) {
|
|
return false;
|
|
}
|
|
return RefinementList.isRefined(
|
|
this.disjunctiveFacetsRefinements,
|
|
facet,
|
|
value
|
|
);
|
|
},
|
|
/**
|
|
* Returns true if the facet contains a refinement, or if a value passed is a
|
|
* refinement for the facet.
|
|
* @method
|
|
* @param {string} facet name of the attribute for used for faceting
|
|
* @param {string} value optional, will test if the value is used for refinement
|
|
* if there is one, otherwise will test if the facet contains any refinement
|
|
* @return {boolean} true if the facet is refined
|
|
*/
|
|
isHierarchicalFacetRefined: function isHierarchicalFacetRefined(
|
|
facet,
|
|
value
|
|
) {
|
|
if (!this.isHierarchicalFacet(facet)) {
|
|
return false;
|
|
}
|
|
|
|
var refinements = this.getHierarchicalRefinement(facet);
|
|
|
|
if (!value) {
|
|
return refinements.length > 0;
|
|
}
|
|
|
|
return refinements.indexOf(value) !== -1;
|
|
},
|
|
/**
|
|
* Test if the triple (attribute, operator, value) is already refined.
|
|
* If only the attribute and the operator are provided, it tests if the
|
|
* contains any refinement value.
|
|
* @method
|
|
* @param {string} attribute attribute for which the refinement is applied
|
|
* @param {string} [operator] operator of the refinement
|
|
* @param {string} [value] value of the refinement
|
|
* @return {boolean} true if it is refined
|
|
*/
|
|
isNumericRefined: function isNumericRefined(attribute, operator, value) {
|
|
if (value === undefined && operator === undefined) {
|
|
return Boolean(this.numericRefinements[attribute]);
|
|
}
|
|
|
|
var isOperatorDefined =
|
|
this.numericRefinements[attribute] &&
|
|
this.numericRefinements[attribute][operator] !== undefined;
|
|
|
|
if (value === undefined || !isOperatorDefined) {
|
|
return isOperatorDefined;
|
|
}
|
|
|
|
var parsedValue = valToNumber(value);
|
|
var isAttributeValueDefined =
|
|
findArray(this.numericRefinements[attribute][operator], parsedValue) !==
|
|
undefined;
|
|
|
|
return isOperatorDefined && isAttributeValueDefined;
|
|
},
|
|
/**
|
|
* Returns true if the tag refined, false otherwise
|
|
* @method
|
|
* @param {string} tag the tag to check
|
|
* @return {boolean} true if tag is refined
|
|
*/
|
|
isTagRefined: function isTagRefined(tag) {
|
|
return this.tagRefinements.indexOf(tag) !== -1;
|
|
},
|
|
/**
|
|
* Returns the list of all disjunctive facets refined
|
|
* @method
|
|
* @param {string} facet name of the attribute used for faceting
|
|
* @param {value} value value used for filtering
|
|
* @return {string[]} returns the list of refinements
|
|
*/
|
|
getRefinedDisjunctiveFacets: function getRefinedDisjunctiveFacets() {
|
|
// eslint-disable-next-line consistent-this
|
|
var self = this;
|
|
|
|
// attributes used for numeric filter can also be disjunctive
|
|
var disjunctiveNumericRefinedFacets = intersection(
|
|
Object.keys(this.numericRefinements).filter(function (facet) {
|
|
return Object.keys(self.numericRefinements[facet]).length > 0;
|
|
}),
|
|
this.disjunctiveFacets
|
|
);
|
|
|
|
return Object.keys(this.disjunctiveFacetsRefinements)
|
|
.filter(function (facet) {
|
|
return self.disjunctiveFacetsRefinements[facet].length > 0;
|
|
})
|
|
.concat(disjunctiveNumericRefinedFacets)
|
|
.concat(this.getRefinedHierarchicalFacets())
|
|
.sort();
|
|
},
|
|
/**
|
|
* Returns the list of all disjunctive facets refined
|
|
* @method
|
|
* @param {string} facet name of the attribute used for faceting
|
|
* @param {value} value value used for filtering
|
|
* @return {string[]} returns the list of refinements
|
|
*/
|
|
getRefinedHierarchicalFacets: function getRefinedHierarchicalFacets() {
|
|
// eslint-disable-next-line consistent-this
|
|
var self = this;
|
|
return intersection(
|
|
// enforce the order between the two arrays,
|
|
// so that refinement name index === hierarchical facet index
|
|
this.hierarchicalFacets.map(function (facet) {
|
|
return facet.name;
|
|
}),
|
|
Object.keys(this.hierarchicalFacetsRefinements).filter(function (facet) {
|
|
return self.hierarchicalFacetsRefinements[facet].length > 0;
|
|
})
|
|
).sort();
|
|
},
|
|
/**
|
|
* Returned the list of all disjunctive facets not refined
|
|
* @method
|
|
* @return {string[]} returns the list of facets that are not refined
|
|
*/
|
|
getUnrefinedDisjunctiveFacets: function () {
|
|
var refinedFacets = this.getRefinedDisjunctiveFacets();
|
|
|
|
return this.disjunctiveFacets.filter(function (f) {
|
|
return refinedFacets.indexOf(f) === -1;
|
|
});
|
|
},
|
|
|
|
managedParameters: [
|
|
'index',
|
|
|
|
'facets',
|
|
'disjunctiveFacets',
|
|
'facetsRefinements',
|
|
'hierarchicalFacets',
|
|
'facetsExcludes',
|
|
|
|
'disjunctiveFacetsRefinements',
|
|
'numericRefinements',
|
|
'tagRefinements',
|
|
'hierarchicalFacetsRefinements',
|
|
],
|
|
|
|
getQueryParams: function getQueryParams() {
|
|
var managedParameters = this.managedParameters;
|
|
|
|
var queryParams = {};
|
|
|
|
// eslint-disable-next-line consistent-this
|
|
var self = this;
|
|
Object.keys(this).forEach(function (paramName) {
|
|
var paramValue = self[paramName];
|
|
if (
|
|
managedParameters.indexOf(paramName) === -1 &&
|
|
paramValue !== undefined
|
|
) {
|
|
queryParams[paramName] = paramValue;
|
|
}
|
|
});
|
|
|
|
return queryParams;
|
|
},
|
|
/**
|
|
* Let the user set a specific value for a given parameter. Will return the
|
|
* same instance if the parameter is invalid or if the value is the same as the
|
|
* previous one.
|
|
* @method
|
|
* @param {string} parameter the parameter name
|
|
* @param {any} value the value to be set, must be compliant with the definition
|
|
* of the attribute on the object
|
|
* @return {SearchParameters} the updated state
|
|
*/
|
|
setQueryParameter: function setParameter(parameter, value) {
|
|
if (this[parameter] === value) return this;
|
|
|
|
var modification = {};
|
|
|
|
modification[parameter] = value;
|
|
|
|
return this.setQueryParameters(modification);
|
|
},
|
|
/**
|
|
* Let the user set any of the parameters with a plain object.
|
|
* @method
|
|
* @param {object} params all the keys and the values to be updated
|
|
* @return {SearchParameters} a new updated instance
|
|
*/
|
|
setQueryParameters: function setQueryParameters(params) {
|
|
if (!params) return this;
|
|
|
|
var error = SearchParameters.validate(this, params);
|
|
|
|
if (error) {
|
|
throw error;
|
|
}
|
|
|
|
// eslint-disable-next-line consistent-this
|
|
var self = this;
|
|
var nextWithNumbers = SearchParameters._parseNumbers(params);
|
|
var previousPlainObject = Object.keys(this).reduce(function (acc, key) {
|
|
acc[key] = self[key];
|
|
return acc;
|
|
}, {});
|
|
|
|
var nextPlainObject = Object.keys(nextWithNumbers).reduce(function (
|
|
previous,
|
|
key
|
|
) {
|
|
var isPreviousValueDefined = previous[key] !== undefined;
|
|
var isNextValueDefined = nextWithNumbers[key] !== undefined;
|
|
|
|
if (isPreviousValueDefined && !isNextValueDefined) {
|
|
return omit(previous, [key]);
|
|
}
|
|
|
|
if (isNextValueDefined) {
|
|
previous[key] = nextWithNumbers[key];
|
|
}
|
|
|
|
return previous;
|
|
},
|
|
previousPlainObject);
|
|
|
|
return new this.constructor(nextPlainObject);
|
|
},
|
|
|
|
/**
|
|
* Returns a new instance with the page reset. Two scenarios possible:
|
|
* the page is omitted -> return the given instance
|
|
* the page is set -> return a new instance with a page of 0
|
|
* @return {SearchParameters} a new updated instance
|
|
*/
|
|
resetPage: function () {
|
|
if (this.page === undefined) {
|
|
return this;
|
|
}
|
|
|
|
return this.setPage(0);
|
|
},
|
|
|
|
/**
|
|
* Helper function to get the hierarchicalFacet separator or the default one (`>`)
|
|
* @param {object} hierarchicalFacet the hierarchicalFacet object
|
|
* @return {string} returns the hierarchicalFacet.separator or `>` as default
|
|
*/
|
|
_getHierarchicalFacetSortBy: function (hierarchicalFacet) {
|
|
return hierarchicalFacet.sortBy || ['isRefined:desc', 'name:asc'];
|
|
},
|
|
|
|
/**
|
|
* Helper function to get the hierarchicalFacet separator or the default one (`>`)
|
|
* @private
|
|
* @param {object} hierarchicalFacet the hierarchicalFacet object
|
|
* @return {string} returns the hierarchicalFacet.separator or `>` as default
|
|
*/
|
|
_getHierarchicalFacetSeparator: function (hierarchicalFacet) {
|
|
return hierarchicalFacet.separator || ' > ';
|
|
},
|
|
|
|
/**
|
|
* Helper function to get the hierarchicalFacet prefix path or null
|
|
* @private
|
|
* @param {object} hierarchicalFacet the hierarchicalFacet object
|
|
* @return {string} returns the hierarchicalFacet.rootPath or null as default
|
|
*/
|
|
_getHierarchicalRootPath: function (hierarchicalFacet) {
|
|
return hierarchicalFacet.rootPath || null;
|
|
},
|
|
|
|
/**
|
|
* Helper function to check if we show the parent level of the hierarchicalFacet
|
|
* @private
|
|
* @param {object} hierarchicalFacet the hierarchicalFacet object
|
|
* @return {string} returns the hierarchicalFacet.showParentLevel or true as default
|
|
*/
|
|
_getHierarchicalShowParentLevel: function (hierarchicalFacet) {
|
|
if (typeof hierarchicalFacet.showParentLevel === 'boolean') {
|
|
return hierarchicalFacet.showParentLevel;
|
|
}
|
|
return true;
|
|
},
|
|
|
|
/**
|
|
* Helper function to get the hierarchicalFacet by it's name
|
|
* @param {string} hierarchicalFacetName the hierarchicalFacet name
|
|
* @return {object} a hierarchicalFacet
|
|
*/
|
|
getHierarchicalFacetByName: function (hierarchicalFacetName) {
|
|
return find(this.hierarchicalFacets, function (f) {
|
|
return f.name === hierarchicalFacetName;
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Get the current breadcrumb for a hierarchical facet, as an array
|
|
* @param {string} facetName Hierarchical facet name
|
|
* @return {array.<string>} the path as an array of string
|
|
*/
|
|
getHierarchicalFacetBreadcrumb: function (facetName) {
|
|
if (!this.isHierarchicalFacet(facetName)) {
|
|
return [];
|
|
}
|
|
|
|
var refinement = this.getHierarchicalRefinement(facetName)[0];
|
|
if (!refinement) return [];
|
|
|
|
var separator = this._getHierarchicalFacetSeparator(
|
|
this.getHierarchicalFacetByName(facetName)
|
|
);
|
|
var path = refinement.split(separator);
|
|
return path.map(function (part) {
|
|
return part.trim();
|
|
});
|
|
},
|
|
|
|
toString: function () {
|
|
return JSON.stringify(this, null, 2);
|
|
},
|
|
};
|
|
|
|
/**
|
|
* Callback used for clearRefinement method
|
|
* @callback SearchParameters.clearCallback
|
|
* @param {OperatorList|FacetList} value the value of the filter
|
|
* @param {string} key the current attribute name
|
|
* @param {string} type `numeric`, `disjunctiveFacet`, `conjunctiveFacet`, `hierarchicalFacet` or `exclude`
|
|
* depending on the type of facet
|
|
* @return {boolean} `true` if the element should be removed. `false` otherwise.
|
|
*/
|
|
module.exports = SearchParameters;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 10210:
|
|
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
|
|
module.exports = generateTrees;
|
|
|
|
var fv = __webpack_require__(94039);
|
|
var find = __webpack_require__(7888);
|
|
var prepareHierarchicalFacetSortBy = __webpack_require__(82293);
|
|
var orderBy = __webpack_require__(42148);
|
|
var escapeFacetValue = fv.escapeFacetValue;
|
|
var unescapeFacetValue = fv.unescapeFacetValue;
|
|
|
|
function generateTrees(state) {
|
|
return function generate(hierarchicalFacetResult, hierarchicalFacetIndex) {
|
|
var hierarchicalFacet = state.hierarchicalFacets[hierarchicalFacetIndex];
|
|
var hierarchicalFacetRefinement =
|
|
(state.hierarchicalFacetsRefinements[hierarchicalFacet.name] &&
|
|
state.hierarchicalFacetsRefinements[hierarchicalFacet.name][0]) ||
|
|
'';
|
|
var hierarchicalSeparator =
|
|
state._getHierarchicalFacetSeparator(hierarchicalFacet);
|
|
var hierarchicalRootPath =
|
|
state._getHierarchicalRootPath(hierarchicalFacet);
|
|
var hierarchicalShowParentLevel =
|
|
state._getHierarchicalShowParentLevel(hierarchicalFacet);
|
|
var sortBy = prepareHierarchicalFacetSortBy(
|
|
state._getHierarchicalFacetSortBy(hierarchicalFacet)
|
|
);
|
|
|
|
var rootExhaustive = hierarchicalFacetResult.every(function (facetResult) {
|
|
return facetResult.exhaustive;
|
|
});
|
|
|
|
var generateTreeFn = generateHierarchicalTree(
|
|
sortBy,
|
|
hierarchicalSeparator,
|
|
hierarchicalRootPath,
|
|
hierarchicalShowParentLevel,
|
|
hierarchicalFacetRefinement
|
|
);
|
|
|
|
var results = hierarchicalFacetResult;
|
|
|
|
if (hierarchicalRootPath) {
|
|
results = hierarchicalFacetResult.slice(
|
|
hierarchicalRootPath.split(hierarchicalSeparator).length
|
|
);
|
|
}
|
|
|
|
return results.reduce(generateTreeFn, {
|
|
name: state.hierarchicalFacets[hierarchicalFacetIndex].name,
|
|
count: null, // root level, no count
|
|
isRefined: true, // root level, always refined
|
|
path: null, // root level, no path
|
|
escapedValue: null,
|
|
exhaustive: rootExhaustive,
|
|
data: null,
|
|
});
|
|
};
|
|
}
|
|
|
|
function generateHierarchicalTree(
|
|
sortBy,
|
|
hierarchicalSeparator,
|
|
hierarchicalRootPath,
|
|
hierarchicalShowParentLevel,
|
|
currentRefinement
|
|
) {
|
|
return function generateTree(
|
|
hierarchicalTree,
|
|
hierarchicalFacetResult,
|
|
currentHierarchicalLevel
|
|
) {
|
|
var parent = hierarchicalTree;
|
|
|
|
if (currentHierarchicalLevel > 0) {
|
|
var level = 0;
|
|
|
|
parent = hierarchicalTree;
|
|
|
|
while (level < currentHierarchicalLevel) {
|
|
/**
|
|
* @type {object[]]} hierarchical data
|
|
*/
|
|
var data = parent && Array.isArray(parent.data) ? parent.data : [];
|
|
parent = find(data, function (subtree) {
|
|
return subtree.isRefined;
|
|
});
|
|
level++;
|
|
}
|
|
}
|
|
|
|
// we found a refined parent, let's add current level data under it
|
|
if (parent) {
|
|
// filter values in case an object has multiple categories:
|
|
// {
|
|
// categories: {
|
|
// level0: ['beers', 'bières'],
|
|
// level1: ['beers > IPA', 'bières > Belges']
|
|
// }
|
|
// }
|
|
//
|
|
// If parent refinement is `beers`, then we do not want to have `bières > Belges`
|
|
// showing up
|
|
|
|
var picked = Object.keys(hierarchicalFacetResult.data)
|
|
.map(function (facetValue) {
|
|
return [facetValue, hierarchicalFacetResult.data[facetValue]];
|
|
})
|
|
.filter(function (tuple) {
|
|
var facetValue = tuple[0];
|
|
return onlyMatchingTree(
|
|
facetValue,
|
|
parent.path || hierarchicalRootPath,
|
|
currentRefinement,
|
|
hierarchicalSeparator,
|
|
hierarchicalRootPath,
|
|
hierarchicalShowParentLevel
|
|
);
|
|
});
|
|
|
|
parent.data = orderBy(
|
|
picked.map(function (tuple) {
|
|
var facetValue = tuple[0];
|
|
var facetCount = tuple[1];
|
|
|
|
return format(
|
|
facetCount,
|
|
facetValue,
|
|
hierarchicalSeparator,
|
|
unescapeFacetValue(currentRefinement),
|
|
hierarchicalFacetResult.exhaustive
|
|
);
|
|
}),
|
|
sortBy[0],
|
|
sortBy[1]
|
|
);
|
|
}
|
|
|
|
return hierarchicalTree;
|
|
};
|
|
}
|
|
|
|
// eslint-disable-next-line max-params
|
|
function onlyMatchingTree(
|
|
facetValue,
|
|
parentPath,
|
|
currentRefinement,
|
|
hierarchicalSeparator,
|
|
hierarchicalRootPath,
|
|
hierarchicalShowParentLevel
|
|
) {
|
|
// we want the facetValue is a child of hierarchicalRootPath
|
|
if (
|
|
hierarchicalRootPath &&
|
|
(facetValue.indexOf(hierarchicalRootPath) !== 0 ||
|
|
hierarchicalRootPath === facetValue)
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
// we always want root levels (only when there is no prefix path)
|
|
return (
|
|
(!hierarchicalRootPath &&
|
|
facetValue.indexOf(hierarchicalSeparator) === -1) ||
|
|
// if there is a rootPath, being root level mean 1 level under rootPath
|
|
(hierarchicalRootPath &&
|
|
facetValue.split(hierarchicalSeparator).length -
|
|
hierarchicalRootPath.split(hierarchicalSeparator).length ===
|
|
1) ||
|
|
// if current refinement is a root level and current facetValue is a root level,
|
|
// keep the facetValue
|
|
(facetValue.indexOf(hierarchicalSeparator) === -1 &&
|
|
currentRefinement.indexOf(hierarchicalSeparator) === -1) ||
|
|
// currentRefinement is a child of the facet value
|
|
currentRefinement.indexOf(facetValue) === 0 ||
|
|
// facetValue is a child of the current parent, add it
|
|
(facetValue.indexOf(parentPath + hierarchicalSeparator) === 0 &&
|
|
(hierarchicalShowParentLevel ||
|
|
facetValue.indexOf(currentRefinement) === 0))
|
|
);
|
|
}
|
|
|
|
function format(
|
|
facetCount,
|
|
facetValue,
|
|
hierarchicalSeparator,
|
|
currentRefinement,
|
|
exhaustive
|
|
) {
|
|
var parts = facetValue.split(hierarchicalSeparator);
|
|
return {
|
|
name: parts[parts.length - 1].trim(),
|
|
path: facetValue,
|
|
escapedValue: escapeFacetValue(facetValue),
|
|
count: facetCount,
|
|
isRefined:
|
|
currentRefinement === facetValue ||
|
|
currentRefinement.indexOf(facetValue + hierarchicalSeparator) === 0,
|
|
exhaustive: exhaustive,
|
|
data: null,
|
|
};
|
|
}
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 23076:
|
|
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
|
|
var compact = __webpack_require__(74587);
|
|
var defaultsPure = __webpack_require__(52344);
|
|
var fv = __webpack_require__(94039);
|
|
var find = __webpack_require__(7888);
|
|
var findIndex = __webpack_require__(69725);
|
|
var formatSort = __webpack_require__(82293);
|
|
var merge = __webpack_require__(60185);
|
|
var orderBy = __webpack_require__(42148);
|
|
var escapeFacetValue = fv.escapeFacetValue;
|
|
var unescapeFacetValue = fv.unescapeFacetValue;
|
|
|
|
var generateHierarchicalTree = __webpack_require__(10210);
|
|
|
|
/**
|
|
* @typedef SearchResults.Facet
|
|
* @type {object}
|
|
* @property {string} name name of the attribute in the record
|
|
* @property {object} data the faceting data: value, number of entries
|
|
* @property {object} stats undefined unless facet_stats is retrieved from algolia
|
|
*/
|
|
|
|
/**
|
|
* @typedef SearchResults.HierarchicalFacet
|
|
* @type {object}
|
|
* @property {string} name name of the current value given the hierarchical level, trimmed.
|
|
* If root node, you get the facet name
|
|
* @property {number} count number of objects matching this hierarchical value
|
|
* @property {string} path the current hierarchical value full path
|
|
* @property {boolean} isRefined `true` if the current value was refined, `false` otherwise
|
|
* @property {HierarchicalFacet[]} data sub values for the current level
|
|
*/
|
|
|
|
/**
|
|
* @typedef SearchResults.FacetValue
|
|
* @type {object}
|
|
* @property {string} name the facet value itself
|
|
* @property {number} count times this facet appears in the results
|
|
* @property {boolean} isRefined is the facet currently selected
|
|
* @property {boolean} isExcluded is the facet currently excluded (only for conjunctive facets)
|
|
*/
|
|
|
|
/**
|
|
* @typedef Refinement
|
|
* @type {object}
|
|
* @property {string} type the type of filter used:
|
|
* `numeric`, `facet`, `exclude`, `disjunctive`, `hierarchical`
|
|
* @property {string} attributeName name of the attribute used for filtering
|
|
* @property {string} name the value of the filter
|
|
* @property {number} numericValue the value as a number. Only for numeric filters.
|
|
* @property {string} operator the operator used. Only for numeric filters.
|
|
* @property {number} count the number of computed hits for this filter. Only on facets.
|
|
* @property {boolean} exhaustive if the count is exhaustive
|
|
*/
|
|
|
|
/**
|
|
* Turn an array of attributes in an object of attributes with their position in the array as value
|
|
* @param {string[]} attributes the list of attributes in the record
|
|
* @return {object} the list of attributes indexed by attribute name
|
|
*/
|
|
function getIndices(attributes) {
|
|
var indices = {};
|
|
|
|
attributes.forEach(function (val, idx) {
|
|
indices[val] = idx;
|
|
});
|
|
|
|
return indices;
|
|
}
|
|
|
|
function assignFacetStats(dest, facetStats, key) {
|
|
if (facetStats && facetStats[key]) {
|
|
dest.stats = facetStats[key];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @typedef {Object} HierarchicalFacet
|
|
* @property {string} name
|
|
* @property {string[]} attributes
|
|
*/
|
|
|
|
/**
|
|
* @param {HierarchicalFacet[]} hierarchicalFacets All hierarchical facets
|
|
* @param {string} hierarchicalAttributeName The name of the hierarchical attribute
|
|
* @return {HierarchicalFacet} The hierarchical facet matching the attribute name
|
|
*/
|
|
function findMatchingHierarchicalFacetFromAttributeName(
|
|
hierarchicalFacets,
|
|
hierarchicalAttributeName
|
|
) {
|
|
return find(
|
|
hierarchicalFacets,
|
|
function facetKeyMatchesAttribute(hierarchicalFacet) {
|
|
var facetNames = hierarchicalFacet.attributes || [];
|
|
return facetNames.indexOf(hierarchicalAttributeName) > -1;
|
|
}
|
|
);
|
|
}
|
|
|
|
// eslint-disable-next-line valid-jsdoc
|
|
/**
|
|
* Constructor for SearchResults
|
|
* @class
|
|
* @classdesc SearchResults contains the results of a query to Algolia using the
|
|
* {@link AlgoliaSearchHelper}.
|
|
* @param {SearchParameters} state state that led to the response
|
|
* @param {array.<object>} results the results from algolia client
|
|
* @example <caption>SearchResults of the first query in
|
|
* <a href="http://demos.algolia.com/instant-search-demo">the instant search demo</a></caption>
|
|
{
|
|
"hitsPerPage": 10,
|
|
"processingTimeMS": 2,
|
|
"facets": [
|
|
{
|
|
"name": "type",
|
|
"data": {
|
|
"HardGood": 6627,
|
|
"BlackTie": 550,
|
|
"Music": 665,
|
|
"Software": 131,
|
|
"Game": 456,
|
|
"Movie": 1571
|
|
},
|
|
"exhaustive": false
|
|
},
|
|
{
|
|
"exhaustive": false,
|
|
"data": {
|
|
"Free shipping": 5507
|
|
},
|
|
"name": "shipping"
|
|
}
|
|
],
|
|
"hits": [
|
|
{
|
|
"thumbnailImage": "http://img.bbystatic.com/BestBuy_US/images/products/1688/1688832_54x108_s.gif",
|
|
"_highlightResult": {
|
|
"shortDescription": {
|
|
"matchLevel": "none",
|
|
"value": "Safeguard your PC, Mac, Android and iOS devices with comprehensive Internet protection",
|
|
"matchedWords": []
|
|
},
|
|
"category": {
|
|
"matchLevel": "none",
|
|
"value": "Computer Security Software",
|
|
"matchedWords": []
|
|
},
|
|
"manufacturer": {
|
|
"matchedWords": [],
|
|
"value": "Webroot",
|
|
"matchLevel": "none"
|
|
},
|
|
"name": {
|
|
"value": "Webroot SecureAnywhere Internet Security (3-Device) (1-Year Subscription) - Mac/Windows",
|
|
"matchedWords": [],
|
|
"matchLevel": "none"
|
|
}
|
|
},
|
|
"image": "http://img.bbystatic.com/BestBuy_US/images/products/1688/1688832_105x210_sc.jpg",
|
|
"shipping": "Free shipping",
|
|
"bestSellingRank": 4,
|
|
"shortDescription": "Safeguard your PC, Mac, Android and iOS devices with comprehensive Internet protection",
|
|
"url": "http://www.bestbuy.com/site/webroot-secureanywhere-internet-security-3-devi…d=1219060687969&skuId=1688832&cmp=RMX&ky=2d3GfEmNIzjA0vkzveHdZEBgpPCyMnLTJ",
|
|
"name": "Webroot SecureAnywhere Internet Security (3-Device) (1-Year Subscription) - Mac/Windows",
|
|
"category": "Computer Security Software",
|
|
"salePrice_range": "1 - 50",
|
|
"objectID": "1688832",
|
|
"type": "Software",
|
|
"customerReviewCount": 5980,
|
|
"salePrice": 49.99,
|
|
"manufacturer": "Webroot"
|
|
},
|
|
....
|
|
],
|
|
"nbHits": 10000,
|
|
"disjunctiveFacets": [
|
|
{
|
|
"exhaustive": false,
|
|
"data": {
|
|
"5": 183,
|
|
"12": 112,
|
|
"7": 149,
|
|
...
|
|
},
|
|
"name": "customerReviewCount",
|
|
"stats": {
|
|
"max": 7461,
|
|
"avg": 157.939,
|
|
"min": 1
|
|
}
|
|
},
|
|
{
|
|
"data": {
|
|
"Printer Ink": 142,
|
|
"Wireless Speakers": 60,
|
|
"Point & Shoot Cameras": 48,
|
|
...
|
|
},
|
|
"name": "category",
|
|
"exhaustive": false
|
|
},
|
|
{
|
|
"exhaustive": false,
|
|
"data": {
|
|
"> 5000": 2,
|
|
"1 - 50": 6524,
|
|
"501 - 2000": 566,
|
|
"201 - 500": 1501,
|
|
"101 - 200": 1360,
|
|
"2001 - 5000": 47
|
|
},
|
|
"name": "salePrice_range"
|
|
},
|
|
{
|
|
"data": {
|
|
"Dynex™": 202,
|
|
"Insignia™": 230,
|
|
"PNY": 72,
|
|
...
|
|
},
|
|
"name": "manufacturer",
|
|
"exhaustive": false
|
|
}
|
|
],
|
|
"query": "",
|
|
"nbPages": 100,
|
|
"page": 0,
|
|
"index": "bestbuy"
|
|
}
|
|
**/
|
|
function SearchResults(state, results, options) {
|
|
var mainSubResponse = results[0];
|
|
|
|
this._rawResults = results;
|
|
|
|
// eslint-disable-next-line consistent-this
|
|
var self = this;
|
|
|
|
// https://www.algolia.com/doc/api-reference/api-methods/search/#response
|
|
Object.keys(mainSubResponse).forEach(function (key) {
|
|
self[key] = mainSubResponse[key];
|
|
});
|
|
|
|
// Make every key of the result options reachable from the instance
|
|
Object.keys(options || {}).forEach(function (key) {
|
|
self[key] = options[key];
|
|
});
|
|
|
|
/**
|
|
* query used to generate the results
|
|
* @name query
|
|
* @member {string}
|
|
* @memberof SearchResults
|
|
* @instance
|
|
*/
|
|
/**
|
|
* The query as parsed by the engine given all the rules.
|
|
* @name parsedQuery
|
|
* @member {string}
|
|
* @memberof SearchResults
|
|
* @instance
|
|
*/
|
|
/**
|
|
* all the records that match the search parameters. Each record is
|
|
* augmented with a new attribute `_highlightResult`
|
|
* which is an object keyed by attribute and with the following properties:
|
|
* - `value` : the value of the facet highlighted (html)
|
|
* - `matchLevel`: `full`, `partial` or `none`, depending on how the query terms match
|
|
* @name hits
|
|
* @member {object[]}
|
|
* @memberof SearchResults
|
|
* @instance
|
|
*/
|
|
/**
|
|
* index where the results come from
|
|
* @name index
|
|
* @member {string}
|
|
* @memberof SearchResults
|
|
* @instance
|
|
*/
|
|
/**
|
|
* number of hits per page requested
|
|
* @name hitsPerPage
|
|
* @member {number}
|
|
* @memberof SearchResults
|
|
* @instance
|
|
*/
|
|
/**
|
|
* total number of hits of this query on the index
|
|
* @name nbHits
|
|
* @member {number}
|
|
* @memberof SearchResults
|
|
* @instance
|
|
*/
|
|
/**
|
|
* total number of pages with respect to the number of hits per page and the total number of hits
|
|
* @name nbPages
|
|
* @member {number}
|
|
* @memberof SearchResults
|
|
* @instance
|
|
*/
|
|
/**
|
|
* current page
|
|
* @name page
|
|
* @member {number}
|
|
* @memberof SearchResults
|
|
* @instance
|
|
*/
|
|
/**
|
|
* The position if the position was guessed by IP.
|
|
* @name aroundLatLng
|
|
* @member {string}
|
|
* @memberof SearchResults
|
|
* @instance
|
|
* @example "48.8637,2.3615",
|
|
*/
|
|
/**
|
|
* The radius computed by Algolia.
|
|
* @name automaticRadius
|
|
* @member {string}
|
|
* @memberof SearchResults
|
|
* @instance
|
|
* @example "126792922",
|
|
*/
|
|
/**
|
|
* String identifying the server used to serve this request.
|
|
*
|
|
* getRankingInfo needs to be set to `true` for this to be returned
|
|
*
|
|
* @name serverUsed
|
|
* @member {string}
|
|
* @memberof SearchResults
|
|
* @instance
|
|
* @example "c7-use-2.algolia.net",
|
|
*/
|
|
/**
|
|
* Boolean that indicates if the computation of the counts did time out.
|
|
* @deprecated
|
|
* @name timeoutCounts
|
|
* @member {boolean}
|
|
* @memberof SearchResults
|
|
* @instance
|
|
*/
|
|
/**
|
|
* Boolean that indicates if the computation of the hits did time out.
|
|
* @deprecated
|
|
* @name timeoutHits
|
|
* @member {boolean}
|
|
* @memberof SearchResults
|
|
* @instance
|
|
*/
|
|
/**
|
|
* True if the counts of the facets is exhaustive
|
|
* @name exhaustiveFacetsCount
|
|
* @member {boolean}
|
|
* @memberof SearchResults
|
|
* @instance
|
|
*/
|
|
/**
|
|
* True if the number of hits is exhaustive
|
|
* @name exhaustiveNbHits
|
|
* @member {boolean}
|
|
* @memberof SearchResults
|
|
* @instance
|
|
*/
|
|
/**
|
|
* Contains the userData if they are set by a [query rule](https://www.algolia.com/doc/guides/query-rules/query-rules-overview/).
|
|
* @name userData
|
|
* @member {object[]}
|
|
* @memberof SearchResults
|
|
* @instance
|
|
*/
|
|
/**
|
|
* queryID is the unique identifier of the query used to generate the current search results.
|
|
* This value is only available if the `clickAnalytics` search parameter is set to `true`.
|
|
* @name queryID
|
|
* @member {string}
|
|
* @memberof SearchResults
|
|
* @instance
|
|
*/
|
|
|
|
/**
|
|
* sum of the processing time of all the queries
|
|
* @name processingTimeMS
|
|
* @member {number}
|
|
* @memberof SearchResults
|
|
* @instance
|
|
*/
|
|
this.processingTimeMS = results.reduce(function (sum, result) {
|
|
return result.processingTimeMS === undefined
|
|
? sum
|
|
: sum + result.processingTimeMS;
|
|
}, 0);
|
|
|
|
/**
|
|
* disjunctive facets results
|
|
* @member {SearchResults.Facet[]}
|
|
*/
|
|
this.disjunctiveFacets = [];
|
|
/**
|
|
* disjunctive facets results
|
|
* @member {SearchResults.HierarchicalFacet[]}
|
|
*/
|
|
this.hierarchicalFacets = state.hierarchicalFacets.map(
|
|
function initFutureTree() {
|
|
return [];
|
|
}
|
|
);
|
|
/**
|
|
* other facets results
|
|
* @member {SearchResults.Facet[]}
|
|
*/
|
|
this.facets = [];
|
|
|
|
var disjunctiveFacets = state.getRefinedDisjunctiveFacets();
|
|
|
|
var facetsIndices = getIndices(state.facets);
|
|
var disjunctiveFacetsIndices = getIndices(state.disjunctiveFacets);
|
|
var nextDisjunctiveResult = 1;
|
|
|
|
// Since we send request only for disjunctive facets that have been refined,
|
|
// we get the facets information from the first, general, response.
|
|
|
|
var mainFacets = mainSubResponse.facets || {};
|
|
|
|
Object.keys(mainFacets).forEach(function (facetKey) {
|
|
var facetValueObject = mainFacets[facetKey];
|
|
|
|
var hierarchicalFacet = findMatchingHierarchicalFacetFromAttributeName(
|
|
state.hierarchicalFacets,
|
|
facetKey
|
|
);
|
|
|
|
if (hierarchicalFacet) {
|
|
// Place the hierarchicalFacet data at the correct index depending on
|
|
// the attributes order that was defined at the helper initialization
|
|
var facetIndex = hierarchicalFacet.attributes.indexOf(facetKey);
|
|
var idxAttributeName = findIndex(state.hierarchicalFacets, function (f) {
|
|
return f.name === hierarchicalFacet.name;
|
|
});
|
|
self.hierarchicalFacets[idxAttributeName][facetIndex] = {
|
|
attribute: facetKey,
|
|
data: facetValueObject,
|
|
exhaustive: mainSubResponse.exhaustiveFacetsCount,
|
|
};
|
|
} else {
|
|
var isFacetDisjunctive = state.disjunctiveFacets.indexOf(facetKey) !== -1;
|
|
var isFacetConjunctive = state.facets.indexOf(facetKey) !== -1;
|
|
var position;
|
|
|
|
if (isFacetDisjunctive) {
|
|
position = disjunctiveFacetsIndices[facetKey];
|
|
self.disjunctiveFacets[position] = {
|
|
name: facetKey,
|
|
data: facetValueObject,
|
|
exhaustive: mainSubResponse.exhaustiveFacetsCount,
|
|
};
|
|
assignFacetStats(
|
|
self.disjunctiveFacets[position],
|
|
mainSubResponse.facets_stats,
|
|
facetKey
|
|
);
|
|
}
|
|
if (isFacetConjunctive) {
|
|
position = facetsIndices[facetKey];
|
|
self.facets[position] = {
|
|
name: facetKey,
|
|
data: facetValueObject,
|
|
exhaustive: mainSubResponse.exhaustiveFacetsCount,
|
|
};
|
|
assignFacetStats(
|
|
self.facets[position],
|
|
mainSubResponse.facets_stats,
|
|
facetKey
|
|
);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Make sure we do not keep holes within the hierarchical facets
|
|
this.hierarchicalFacets = compact(this.hierarchicalFacets);
|
|
|
|
// aggregate the refined disjunctive facets
|
|
disjunctiveFacets.forEach(function (disjunctiveFacet) {
|
|
var result = results[nextDisjunctiveResult];
|
|
var facets = result && result.facets ? result.facets : {};
|
|
var hierarchicalFacet = state.getHierarchicalFacetByName(disjunctiveFacet);
|
|
|
|
// There should be only item in facets.
|
|
Object.keys(facets).forEach(function (dfacet) {
|
|
var facetResults = facets[dfacet];
|
|
|
|
var position;
|
|
|
|
if (hierarchicalFacet) {
|
|
position = findIndex(state.hierarchicalFacets, function (f) {
|
|
return f.name === hierarchicalFacet.name;
|
|
});
|
|
var attributeIndex = findIndex(
|
|
self.hierarchicalFacets[position],
|
|
function (f) {
|
|
return f.attribute === dfacet;
|
|
}
|
|
);
|
|
|
|
// previous refinements and no results so not able to find it
|
|
if (attributeIndex === -1) {
|
|
return;
|
|
}
|
|
|
|
self.hierarchicalFacets[position][attributeIndex].data = merge(
|
|
{},
|
|
self.hierarchicalFacets[position][attributeIndex].data,
|
|
facetResults
|
|
);
|
|
} else {
|
|
position = disjunctiveFacetsIndices[dfacet];
|
|
|
|
var dataFromMainRequest =
|
|
(mainSubResponse.facets && mainSubResponse.facets[dfacet]) || {};
|
|
|
|
self.disjunctiveFacets[position] = {
|
|
name: dfacet,
|
|
data: defaultsPure({}, facetResults, dataFromMainRequest),
|
|
exhaustive: result.exhaustiveFacetsCount,
|
|
};
|
|
assignFacetStats(
|
|
self.disjunctiveFacets[position],
|
|
result.facets_stats,
|
|
dfacet
|
|
);
|
|
|
|
if (state.disjunctiveFacetsRefinements[dfacet]) {
|
|
state.disjunctiveFacetsRefinements[dfacet].forEach(function (
|
|
refinementValue
|
|
) {
|
|
// add the disjunctive refinements if it is no more retrieved
|
|
if (
|
|
!self.disjunctiveFacets[position].data[refinementValue] &&
|
|
state.disjunctiveFacetsRefinements[dfacet].indexOf(
|
|
unescapeFacetValue(refinementValue)
|
|
) > -1
|
|
) {
|
|
self.disjunctiveFacets[position].data[refinementValue] = 0;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
});
|
|
nextDisjunctiveResult++;
|
|
});
|
|
|
|
// if we have some parent level values for hierarchical facets, merge them
|
|
state.getRefinedHierarchicalFacets().forEach(function (refinedFacet) {
|
|
var hierarchicalFacet = state.getHierarchicalFacetByName(refinedFacet);
|
|
var separator = state._getHierarchicalFacetSeparator(hierarchicalFacet);
|
|
|
|
var currentRefinement = state.getHierarchicalRefinement(refinedFacet);
|
|
// if we are already at a root refinement (or no refinement at all), there is no
|
|
// root level values request
|
|
if (
|
|
currentRefinement.length === 0 ||
|
|
currentRefinement[0].split(separator).length < 2
|
|
) {
|
|
return;
|
|
}
|
|
|
|
results.slice(nextDisjunctiveResult).forEach(function (result) {
|
|
var facets = result && result.facets ? result.facets : {};
|
|
|
|
Object.keys(facets).forEach(function (dfacet) {
|
|
var facetResults = facets[dfacet];
|
|
var position = findIndex(state.hierarchicalFacets, function (f) {
|
|
return f.name === hierarchicalFacet.name;
|
|
});
|
|
var attributeIndex = findIndex(
|
|
self.hierarchicalFacets[position],
|
|
function (f) {
|
|
return f.attribute === dfacet;
|
|
}
|
|
);
|
|
|
|
// previous refinements and no results so not able to find it
|
|
if (attributeIndex === -1) {
|
|
return;
|
|
}
|
|
|
|
// when we always get root levels, if the hits refinement is `beers > IPA` (count: 5),
|
|
// then the disjunctive values will be `beers` (count: 100),
|
|
// but we do not want to display
|
|
// | beers (100)
|
|
// > IPA (5)
|
|
// We want
|
|
// | beers (5)
|
|
// > IPA (5)
|
|
var defaultData = {};
|
|
|
|
if (currentRefinement.length > 0) {
|
|
var root = currentRefinement[0].split(separator)[0];
|
|
defaultData[root] =
|
|
self.hierarchicalFacets[position][attributeIndex].data[root];
|
|
}
|
|
|
|
self.hierarchicalFacets[position][attributeIndex].data = defaultsPure(
|
|
defaultData,
|
|
facetResults,
|
|
self.hierarchicalFacets[position][attributeIndex].data
|
|
);
|
|
});
|
|
|
|
nextDisjunctiveResult++;
|
|
});
|
|
});
|
|
|
|
// add the excludes
|
|
Object.keys(state.facetsExcludes).forEach(function (facetName) {
|
|
var excludes = state.facetsExcludes[facetName];
|
|
var position = facetsIndices[facetName];
|
|
|
|
self.facets[position] = {
|
|
name: facetName,
|
|
data: mainFacets[facetName],
|
|
exhaustive: mainSubResponse.exhaustiveFacetsCount,
|
|
};
|
|
excludes.forEach(function (facetValue) {
|
|
self.facets[position] = self.facets[position] || { name: facetName };
|
|
self.facets[position].data = self.facets[position].data || {};
|
|
self.facets[position].data[facetValue] = 0;
|
|
});
|
|
});
|
|
|
|
/**
|
|
* @type {Array}
|
|
*/
|
|
this.hierarchicalFacets = this.hierarchicalFacets.map(
|
|
generateHierarchicalTree(state)
|
|
);
|
|
|
|
/**
|
|
* @type {Array}
|
|
*/
|
|
this.facets = compact(this.facets);
|
|
/**
|
|
* @type {Array}
|
|
*/
|
|
this.disjunctiveFacets = compact(this.disjunctiveFacets);
|
|
|
|
this._state = state;
|
|
}
|
|
|
|
/**
|
|
* Get a facet object with its name
|
|
* @deprecated
|
|
* @param {string} name name of the faceted attribute
|
|
* @return {SearchResults.Facet} the facet object
|
|
*/
|
|
SearchResults.prototype.getFacetByName = function (name) {
|
|
function predicate(facet) {
|
|
return facet.name === name;
|
|
}
|
|
|
|
return (
|
|
find(this.facets, predicate) ||
|
|
find(this.disjunctiveFacets, predicate) ||
|
|
find(this.hierarchicalFacets, predicate)
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Get the facet values of a specified attribute from a SearchResults object.
|
|
* @private
|
|
* @param {SearchResults} results the search results to search in
|
|
* @param {string} attribute name of the faceted attribute to search for
|
|
* @return {array|object} facet values. For the hierarchical facets it is an object.
|
|
*/
|
|
function extractNormalizedFacetValues(results, attribute) {
|
|
function predicate(facet) {
|
|
return facet.name === attribute;
|
|
}
|
|
|
|
if (results._state.isConjunctiveFacet(attribute)) {
|
|
var facet = find(results.facets, predicate);
|
|
if (!facet) return [];
|
|
|
|
return Object.keys(facet.data).map(function (name) {
|
|
var value = escapeFacetValue(name);
|
|
return {
|
|
name: name,
|
|
escapedValue: value,
|
|
count: facet.data[name],
|
|
isRefined: results._state.isFacetRefined(attribute, value),
|
|
isExcluded: results._state.isExcludeRefined(attribute, name),
|
|
};
|
|
});
|
|
} else if (results._state.isDisjunctiveFacet(attribute)) {
|
|
var disjunctiveFacet = find(results.disjunctiveFacets, predicate);
|
|
if (!disjunctiveFacet) return [];
|
|
|
|
return Object.keys(disjunctiveFacet.data).map(function (name) {
|
|
var value = escapeFacetValue(name);
|
|
return {
|
|
name: name,
|
|
escapedValue: value,
|
|
count: disjunctiveFacet.data[name],
|
|
isRefined: results._state.isDisjunctiveFacetRefined(attribute, value),
|
|
};
|
|
});
|
|
} else if (results._state.isHierarchicalFacet(attribute)) {
|
|
var hierarchicalFacetValues = find(results.hierarchicalFacets, predicate);
|
|
if (!hierarchicalFacetValues) return hierarchicalFacetValues;
|
|
|
|
var hierarchicalFacet =
|
|
results._state.getHierarchicalFacetByName(attribute);
|
|
var separator =
|
|
results._state._getHierarchicalFacetSeparator(hierarchicalFacet);
|
|
var currentRefinement = unescapeFacetValue(
|
|
results._state.getHierarchicalRefinement(attribute)[0] || ''
|
|
);
|
|
|
|
if (currentRefinement.indexOf(hierarchicalFacet.rootPath) === 0) {
|
|
currentRefinement = currentRefinement.replace(
|
|
hierarchicalFacet.rootPath + separator,
|
|
''
|
|
);
|
|
}
|
|
|
|
var currentRefinementSplit = currentRefinement.split(separator);
|
|
currentRefinementSplit.unshift(attribute);
|
|
|
|
setIsRefined(hierarchicalFacetValues, currentRefinementSplit, 0);
|
|
|
|
return hierarchicalFacetValues;
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
/**
|
|
* Set the isRefined of a hierarchical facet result based on the current state.
|
|
* @param {SearchResults.HierarchicalFacet} item Hierarchical facet to fix
|
|
* @param {string[]} currentRefinement array of parts of the current hierarchical refinement
|
|
* @param {number} depth recursion depth in the currentRefinement
|
|
* @return {undefined} function mutates the item
|
|
*/
|
|
function setIsRefined(item, currentRefinement, depth) {
|
|
item.isRefined = item.name === currentRefinement[depth];
|
|
if (item.data) {
|
|
item.data.forEach(function (child) {
|
|
setIsRefined(child, currentRefinement, depth + 1);
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sort nodes of a hierarchical or disjunctive facet results
|
|
* @private
|
|
* @param {function} sortFn sort function to apply
|
|
* @param {HierarchicalFacet|Array} node node upon which we want to apply the sort
|
|
* @param {string[]} names attribute names
|
|
* @param {number} [level=0] current index in the names array
|
|
* @return {HierarchicalFacet|Array} sorted node
|
|
*/
|
|
function recSort(sortFn, node, names, level) {
|
|
level = level || 0;
|
|
|
|
if (Array.isArray(node)) {
|
|
return sortFn(node, names[level]);
|
|
}
|
|
|
|
if (!node.data || node.data.length === 0) {
|
|
return node;
|
|
}
|
|
|
|
var children = node.data.map(function (childNode) {
|
|
return recSort(sortFn, childNode, names, level + 1);
|
|
});
|
|
var sortedChildren = sortFn(children, names[level]);
|
|
var newNode = defaultsPure({ data: sortedChildren }, node);
|
|
return newNode;
|
|
}
|
|
|
|
SearchResults.DEFAULT_SORT = ['isRefined:desc', 'count:desc', 'name:asc'];
|
|
|
|
function vanillaSortFn(order, data) {
|
|
return data.sort(order);
|
|
}
|
|
|
|
/**
|
|
* @typedef FacetOrdering
|
|
* @type {Object}
|
|
* @property {string[]} [order]
|
|
* @property {'count' | 'alpha' | 'hidden'} [sortRemainingBy]
|
|
*/
|
|
|
|
/**
|
|
* Sorts facet arrays via their facet ordering
|
|
* @param {Array} facetValues the values
|
|
* @param {FacetOrdering} facetOrdering the ordering
|
|
* @returns {Array} the sorted facet values
|
|
*/
|
|
function sortViaFacetOrdering(facetValues, facetOrdering) {
|
|
var orderedFacets = [];
|
|
var remainingFacets = [];
|
|
|
|
var order = facetOrdering.order || [];
|
|
/**
|
|
* an object with the keys being the values in order, the values their index:
|
|
* ['one', 'two'] -> { one: 0, two: 1 }
|
|
*/
|
|
var reverseOrder = order.reduce(function (acc, name, i) {
|
|
acc[name] = i;
|
|
return acc;
|
|
}, {});
|
|
|
|
facetValues.forEach(function (item) {
|
|
// hierarchical facets get sorted using their raw name
|
|
var name = item.path || item.name;
|
|
if (reverseOrder[name] !== undefined) {
|
|
orderedFacets[reverseOrder[name]] = item;
|
|
} else {
|
|
remainingFacets.push(item);
|
|
}
|
|
});
|
|
|
|
orderedFacets = orderedFacets.filter(function (facet) {
|
|
return facet;
|
|
});
|
|
|
|
var sortRemainingBy = facetOrdering.sortRemainingBy;
|
|
var ordering;
|
|
if (sortRemainingBy === 'hidden') {
|
|
return orderedFacets;
|
|
} else if (sortRemainingBy === 'alpha') {
|
|
ordering = [
|
|
['path', 'name'],
|
|
['asc', 'asc'],
|
|
];
|
|
} else {
|
|
ordering = [['count'], ['desc']];
|
|
}
|
|
|
|
return orderedFacets.concat(
|
|
orderBy(remainingFacets, ordering[0], ordering[1])
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param {SearchResults} results the search results class
|
|
* @param {string} attribute the attribute to retrieve ordering of
|
|
* @returns {FacetOrdering | undefined} the facet ordering
|
|
*/
|
|
function getFacetOrdering(results, attribute) {
|
|
return (
|
|
results.renderingContent &&
|
|
results.renderingContent.facetOrdering &&
|
|
results.renderingContent.facetOrdering.values &&
|
|
results.renderingContent.facetOrdering.values[attribute]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get a the list of values for a given facet attribute. Those values are sorted
|
|
* refinement first, descending count (bigger value on top), and name ascending
|
|
* (alphabetical order). The sort formula can overridden using either string based
|
|
* predicates or a function.
|
|
*
|
|
* This method will return all the values returned by the Algolia engine plus all
|
|
* the values already refined. This means that it can happen that the
|
|
* `maxValuesPerFacet` [configuration](https://www.algolia.com/doc/rest-api/search#param-maxValuesPerFacet)
|
|
* might not be respected if you have facet values that are already refined.
|
|
* @param {string} attribute attribute name
|
|
* @param {object} opts configuration options.
|
|
* @param {boolean} [opts.facetOrdering]
|
|
* Force the use of facetOrdering from the result if a sortBy is present. If
|
|
* sortBy isn't present, facetOrdering will be used automatically.
|
|
* @param {Array.<string> | function} opts.sortBy
|
|
* When using strings, it consists of
|
|
* the name of the [FacetValue](#SearchResults.FacetValue) or the
|
|
* [HierarchicalFacet](#SearchResults.HierarchicalFacet) attributes with the
|
|
* order (`asc` or `desc`). For example to order the value by count, the
|
|
* argument would be `['count:asc']`.
|
|
*
|
|
* If only the attribute name is specified, the ordering defaults to the one
|
|
* specified in the default value for this attribute.
|
|
*
|
|
* When not specified, the order is
|
|
* ascending. This parameter can also be a function which takes two facet
|
|
* values and should return a number, 0 if equal, 1 if the first argument is
|
|
* bigger or -1 otherwise.
|
|
*
|
|
* The default value for this attribute `['isRefined:desc', 'count:desc', 'name:asc']`
|
|
* @return {FacetValue[]|HierarchicalFacet|undefined} depending on the type of facet of
|
|
* the attribute requested (hierarchical, disjunctive or conjunctive)
|
|
* @example
|
|
* helper.on('result', function(event){
|
|
* //get values ordered only by name ascending using the string predicate
|
|
* event.results.getFacetValues('city', {sortBy: ['name:asc']});
|
|
* //get values ordered only by count ascending using a function
|
|
* event.results.getFacetValues('city', {
|
|
* // this is equivalent to ['count:asc']
|
|
* sortBy: function(a, b) {
|
|
* if (a.count === b.count) return 0;
|
|
* if (a.count > b.count) return 1;
|
|
* if (b.count > a.count) return -1;
|
|
* }
|
|
* });
|
|
* });
|
|
*/
|
|
SearchResults.prototype.getFacetValues = function (attribute, opts) {
|
|
var facetValues = extractNormalizedFacetValues(this, attribute);
|
|
if (!facetValues) {
|
|
return undefined;
|
|
}
|
|
|
|
var options = defaultsPure({}, opts, {
|
|
sortBy: SearchResults.DEFAULT_SORT,
|
|
// if no sortBy is given, attempt to sort based on facetOrdering
|
|
// if it is given, we still allow to sort via facet ordering first
|
|
facetOrdering: !(opts && opts.sortBy),
|
|
});
|
|
|
|
// eslint-disable-next-line consistent-this
|
|
var results = this;
|
|
var attributes;
|
|
if (Array.isArray(facetValues)) {
|
|
attributes = [attribute];
|
|
} else {
|
|
var config = results._state.getHierarchicalFacetByName(facetValues.name);
|
|
attributes = config.attributes;
|
|
}
|
|
|
|
return recSort(
|
|
function (data, facetName) {
|
|
if (options.facetOrdering) {
|
|
var facetOrdering = getFacetOrdering(results, facetName);
|
|
if (facetOrdering) {
|
|
return sortViaFacetOrdering(data, facetOrdering);
|
|
}
|
|
}
|
|
|
|
if (Array.isArray(options.sortBy)) {
|
|
var order = formatSort(options.sortBy, SearchResults.DEFAULT_SORT);
|
|
return orderBy(data, order[0], order[1]);
|
|
} else if (typeof options.sortBy === 'function') {
|
|
return vanillaSortFn(options.sortBy, data);
|
|
}
|
|
throw new Error(
|
|
'options.sortBy is optional but if defined it must be ' +
|
|
'either an array of string (predicates) or a sorting function'
|
|
);
|
|
},
|
|
facetValues,
|
|
attributes
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Returns the facet stats if attribute is defined and the facet contains some.
|
|
* Otherwise returns undefined.
|
|
* @param {string} attribute name of the faceted attribute
|
|
* @return {object} The stats of the facet
|
|
*/
|
|
SearchResults.prototype.getFacetStats = function (attribute) {
|
|
if (this._state.isConjunctiveFacet(attribute)) {
|
|
return getFacetStatsIfAvailable(this.facets, attribute);
|
|
} else if (this._state.isDisjunctiveFacet(attribute)) {
|
|
return getFacetStatsIfAvailable(this.disjunctiveFacets, attribute);
|
|
}
|
|
|
|
return undefined;
|
|
};
|
|
|
|
/**
|
|
* @typedef {Object} FacetListItem
|
|
* @property {string} name
|
|
*/
|
|
|
|
/**
|
|
* @param {FacetListItem[]} facetList (has more items, but enough for here)
|
|
* @param {string} facetName The attribute to look for
|
|
* @return {object|undefined} The stats of the facet
|
|
*/
|
|
function getFacetStatsIfAvailable(facetList, facetName) {
|
|
var data = find(facetList, function (facet) {
|
|
return facet.name === facetName;
|
|
});
|
|
return data && data.stats;
|
|
}
|
|
|
|
/**
|
|
* Returns all refinements for all filters + tags. It also provides
|
|
* additional information: count and exhaustiveness for each filter.
|
|
*
|
|
* See the [refinement type](#Refinement) for an exhaustive view of the available
|
|
* data.
|
|
*
|
|
* Note that for a numeric refinement, results are grouped per operator, this
|
|
* means that it will return responses for operators which are empty.
|
|
*
|
|
* @return {Array.<Refinement>} all the refinements
|
|
*/
|
|
SearchResults.prototype.getRefinements = function () {
|
|
var state = this._state;
|
|
// eslint-disable-next-line consistent-this
|
|
var results = this;
|
|
var res = [];
|
|
|
|
Object.keys(state.facetsRefinements).forEach(function (attributeName) {
|
|
state.facetsRefinements[attributeName].forEach(function (name) {
|
|
res.push(
|
|
getRefinement(state, 'facet', attributeName, name, results.facets)
|
|
);
|
|
});
|
|
});
|
|
|
|
Object.keys(state.facetsExcludes).forEach(function (attributeName) {
|
|
state.facetsExcludes[attributeName].forEach(function (name) {
|
|
res.push(
|
|
getRefinement(state, 'exclude', attributeName, name, results.facets)
|
|
);
|
|
});
|
|
});
|
|
|
|
Object.keys(state.disjunctiveFacetsRefinements).forEach(function (
|
|
attributeName
|
|
) {
|
|
state.disjunctiveFacetsRefinements[attributeName].forEach(function (name) {
|
|
res.push(
|
|
getRefinement(
|
|
state,
|
|
'disjunctive',
|
|
attributeName,
|
|
name,
|
|
results.disjunctiveFacets
|
|
)
|
|
);
|
|
});
|
|
});
|
|
|
|
Object.keys(state.hierarchicalFacetsRefinements).forEach(function (
|
|
attributeName
|
|
) {
|
|
state.hierarchicalFacetsRefinements[attributeName].forEach(function (name) {
|
|
res.push(
|
|
getHierarchicalRefinement(
|
|
state,
|
|
attributeName,
|
|
name,
|
|
results.hierarchicalFacets
|
|
)
|
|
);
|
|
});
|
|
});
|
|
|
|
Object.keys(state.numericRefinements).forEach(function (attributeName) {
|
|
var operators = state.numericRefinements[attributeName];
|
|
Object.keys(operators).forEach(function (operator) {
|
|
operators[operator].forEach(function (value) {
|
|
res.push({
|
|
type: 'numeric',
|
|
attributeName: attributeName,
|
|
name: value,
|
|
numericValue: value,
|
|
operator: operator,
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
state.tagRefinements.forEach(function (name) {
|
|
res.push({ type: 'tag', attributeName: '_tags', name: name });
|
|
});
|
|
|
|
return res;
|
|
};
|
|
|
|
/**
|
|
* @typedef {Object} Facet
|
|
* @property {string} name
|
|
* @property {Object} data
|
|
* @property {boolean} exhaustive
|
|
*/
|
|
|
|
/**
|
|
* @param {SearchParameters} state the current state
|
|
* @param {string} type the type of the refinement
|
|
* @param {string} attributeName The attribute of the facet
|
|
* @param {*} name The name of the facet
|
|
* @param {Facet[]} resultsFacets facets from the results
|
|
* @return {Refinement} the refinement
|
|
*/
|
|
function getRefinement(state, type, attributeName, name, resultsFacets) {
|
|
var facet = find(resultsFacets, function (f) {
|
|
return f.name === attributeName;
|
|
});
|
|
var count = facet && facet.data && facet.data[name] ? facet.data[name] : 0;
|
|
var exhaustive = (facet && facet.exhaustive) || false;
|
|
|
|
return {
|
|
type: type,
|
|
attributeName: attributeName,
|
|
name: name,
|
|
count: count,
|
|
exhaustive: exhaustive,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @param {SearchParameters} state the current state
|
|
* @param {string} attributeName the attribute of the hierarchical facet
|
|
* @param {string} name the name of the facet
|
|
* @param {Facet[]} resultsFacets facets from the results
|
|
* @return {HierarchicalFacet} the hierarchical facet
|
|
*/
|
|
function getHierarchicalRefinement(state, attributeName, name, resultsFacets) {
|
|
var facetDeclaration = state.getHierarchicalFacetByName(attributeName);
|
|
var separator = state._getHierarchicalFacetSeparator(facetDeclaration);
|
|
var split = name.split(separator);
|
|
var rootFacet = find(resultsFacets, function (facet) {
|
|
return facet.name === attributeName;
|
|
});
|
|
|
|
var facet = split.reduce(function (intermediateFacet, part) {
|
|
var newFacet =
|
|
intermediateFacet &&
|
|
find(intermediateFacet.data, function (f) {
|
|
return f.name === part;
|
|
});
|
|
return newFacet !== undefined ? newFacet : intermediateFacet;
|
|
}, rootFacet);
|
|
|
|
var count = (facet && facet.count) || 0;
|
|
var exhaustive = (facet && facet.exhaustive) || false;
|
|
var path = (facet && facet.path) || '';
|
|
|
|
return {
|
|
type: 'hierarchical',
|
|
attributeName: attributeName,
|
|
name: path,
|
|
count: count,
|
|
exhaustive: exhaustive,
|
|
};
|
|
}
|
|
|
|
module.exports = SearchResults;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 49374:
|
|
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
|
|
var EventEmitter = __webpack_require__(17331);
|
|
|
|
var DerivedHelper = __webpack_require__(68078);
|
|
var escapeFacetValue = (__webpack_require__(94039).escapeFacetValue);
|
|
var inherits = __webpack_require__(14853);
|
|
var merge = __webpack_require__(60185);
|
|
var objectHasKeys = __webpack_require__(90116);
|
|
var omit = __webpack_require__(49803);
|
|
var requestBuilder = __webpack_require__(96394);
|
|
var SearchParameters = __webpack_require__(17775);
|
|
var SearchResults = __webpack_require__(23076);
|
|
var version = __webpack_require__(24336);
|
|
|
|
/**
|
|
* Event triggered when a parameter is set or updated
|
|
* @event AlgoliaSearchHelper#event:change
|
|
* @property {object} event
|
|
* @property {SearchParameters} event.state the current parameters with the latest changes applied
|
|
* @property {SearchResults} event.results the previous results received from Algolia. `null` before the first request
|
|
* @example
|
|
* helper.on('change', function(event) {
|
|
* console.log('The parameters have changed');
|
|
* });
|
|
*/
|
|
|
|
/**
|
|
* Event triggered when a main search is sent to Algolia
|
|
* @event AlgoliaSearchHelper#event:search
|
|
* @property {object} event
|
|
* @property {SearchParameters} event.state the parameters used for this search
|
|
* @property {SearchResults} event.results the results from the previous search. `null` if it is the first search.
|
|
* @example
|
|
* helper.on('search', function(event) {
|
|
* console.log('Search sent');
|
|
* });
|
|
*/
|
|
|
|
/**
|
|
* Event triggered when a search using `searchForFacetValues` is sent to Algolia
|
|
* @event AlgoliaSearchHelper#event:searchForFacetValues
|
|
* @property {object} event
|
|
* @property {SearchParameters} event.state the parameters used for this search it is the first search.
|
|
* @property {string} event.facet the facet searched into
|
|
* @property {string} event.query the query used to search in the facets
|
|
* @example
|
|
* helper.on('searchForFacetValues', function(event) {
|
|
* console.log('searchForFacetValues sent');
|
|
* });
|
|
*/
|
|
|
|
/**
|
|
* Event triggered when a search using `searchOnce` is sent to Algolia
|
|
* @event AlgoliaSearchHelper#event:searchOnce
|
|
* @property {object} event
|
|
* @property {SearchParameters} event.state the parameters used for this search it is the first search.
|
|
* @example
|
|
* helper.on('searchOnce', function(event) {
|
|
* console.log('searchOnce sent');
|
|
* });
|
|
*/
|
|
|
|
/**
|
|
* Event triggered when the results are retrieved from Algolia
|
|
* @event AlgoliaSearchHelper#event:result
|
|
* @property {object} event
|
|
* @property {SearchResults} event.results the results received from Algolia
|
|
* @property {SearchParameters} event.state the parameters used to query Algolia. Those might be different from the one in the helper instance (for example if the network is unreliable).
|
|
* @example
|
|
* helper.on('result', function(event) {
|
|
* console.log('Search results received');
|
|
* });
|
|
*/
|
|
|
|
/**
|
|
* Event triggered when Algolia sends back an error. For example, if an unknown parameter is
|
|
* used, the error can be caught using this event.
|
|
* @event AlgoliaSearchHelper#event:error
|
|
* @property {object} event
|
|
* @property {Error} event.error the error returned by the Algolia.
|
|
* @example
|
|
* helper.on('error', function(event) {
|
|
* console.log('Houston we got a problem.');
|
|
* });
|
|
*/
|
|
|
|
/**
|
|
* Event triggered when the queue of queries have been depleted (with any result or outdated queries)
|
|
* @event AlgoliaSearchHelper#event:searchQueueEmpty
|
|
* @example
|
|
* helper.on('searchQueueEmpty', function() {
|
|
* console.log('No more search pending');
|
|
* // This is received before the result event if we're not expecting new results
|
|
* });
|
|
*
|
|
* helper.search();
|
|
*/
|
|
|
|
/**
|
|
* Initialize a new AlgoliaSearchHelper
|
|
* @class
|
|
* @classdesc The AlgoliaSearchHelper is a class that ease the management of the
|
|
* search. It provides an event based interface for search callbacks:
|
|
* - change: when the internal search state is changed.
|
|
* This event contains a {@link SearchParameters} object and the
|
|
* {@link SearchResults} of the last result if any.
|
|
* - 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.
|
|
* - error: when the response is an error. This event contains the error returned by the server.
|
|
* @param {AlgoliaSearch} client an AlgoliaSearch client
|
|
* @param {string} index the index name to query
|
|
* @param {SearchParameters | object} options an object defining the initial
|
|
* config of the search. It doesn't have to be a {SearchParameters},
|
|
* just an object containing the properties you need from it.
|
|
*/
|
|
function AlgoliaSearchHelper(client, index, options) {
|
|
if (typeof client.addAlgoliaAgent === 'function') {
|
|
client.addAlgoliaAgent('JS Helper (' + version + ')');
|
|
}
|
|
|
|
this.setClient(client);
|
|
var opts = options || {};
|
|
opts.index = index;
|
|
this.state = SearchParameters.make(opts);
|
|
this.lastResults = null;
|
|
this._queryId = 0;
|
|
this._lastQueryIdReceived = -1;
|
|
this.derivedHelpers = [];
|
|
this._currentNbQueries = 0;
|
|
}
|
|
|
|
inherits(AlgoliaSearchHelper, EventEmitter);
|
|
|
|
/**
|
|
* Start the search with the parameters set in the state. When the
|
|
* method is called, it triggers a `search` event. The results will
|
|
* be available through the `result` event. If an error occurs, an
|
|
* `error` will be fired instead.
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
* @fires search
|
|
* @fires result
|
|
* @fires error
|
|
* @chainable
|
|
*/
|
|
AlgoliaSearchHelper.prototype.search = function () {
|
|
this._search({ onlyWithDerivedHelpers: false });
|
|
return this;
|
|
};
|
|
|
|
AlgoliaSearchHelper.prototype.searchOnlyWithDerivedHelpers = function () {
|
|
this._search({ onlyWithDerivedHelpers: true });
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Gets the search query parameters that would be sent to the Algolia Client
|
|
* for the hits
|
|
* @return {object} Query Parameters
|
|
*/
|
|
AlgoliaSearchHelper.prototype.getQuery = function () {
|
|
var state = this.state;
|
|
return requestBuilder._getHitsSearchParams(state);
|
|
};
|
|
|
|
/**
|
|
* Start a search using a modified version of the current state. This method does
|
|
* not trigger the helper lifecycle and does not modify the state kept internally
|
|
* by the helper. This second aspect means that the next search call will be the
|
|
* same as a search call before calling searchOnce.
|
|
* @param {object} options can contain all the parameters that can be set to SearchParameters
|
|
* plus the index
|
|
* @param {function} [cb] optional callback executed when the response from the
|
|
* server is back.
|
|
* @return {promise|undefined} if a callback is passed the method returns undefined
|
|
* otherwise it returns a promise containing an object with two keys :
|
|
* - content with a SearchResults
|
|
* - state with the state used for the query as a SearchParameters
|
|
* @example
|
|
* // Changing the number of records returned per page to 1
|
|
* // This example uses the callback API
|
|
* var state = helper.searchOnce({hitsPerPage: 1},
|
|
* function(error, content, state) {
|
|
* // if an error occurred it will be passed in error, otherwise its value is null
|
|
* // content contains the results formatted as a SearchResults
|
|
* // state is the instance of SearchParameters used for this search
|
|
* });
|
|
* @example
|
|
* // Changing the number of records returned per page to 1
|
|
* // This example uses the promise API
|
|
* var state1 = helper.searchOnce({hitsPerPage: 1})
|
|
* .then(promiseHandler);
|
|
*
|
|
* function promiseHandler(res) {
|
|
* // res contains
|
|
* // {
|
|
* // content : SearchResults
|
|
* // state : SearchParameters (the one used for this specific search)
|
|
* // }
|
|
* }
|
|
*/
|
|
AlgoliaSearchHelper.prototype.searchOnce = function (options, cb) {
|
|
var tempState = !options
|
|
? this.state
|
|
: this.state.setQueryParameters(options);
|
|
var queries = requestBuilder._getQueries(tempState.index, tempState);
|
|
// eslint-disable-next-line consistent-this
|
|
var self = this;
|
|
|
|
this._currentNbQueries++;
|
|
|
|
this.emit('searchOnce', {
|
|
state: tempState,
|
|
});
|
|
|
|
if (cb) {
|
|
this.client
|
|
.search(queries)
|
|
.then(function (content) {
|
|
self._currentNbQueries--;
|
|
if (self._currentNbQueries === 0) {
|
|
self.emit('searchQueueEmpty');
|
|
}
|
|
|
|
cb(null, new SearchResults(tempState, content.results), tempState);
|
|
})
|
|
.catch(function (err) {
|
|
self._currentNbQueries--;
|
|
if (self._currentNbQueries === 0) {
|
|
self.emit('searchQueueEmpty');
|
|
}
|
|
|
|
cb(err, null, tempState);
|
|
});
|
|
|
|
return undefined;
|
|
}
|
|
|
|
return this.client.search(queries).then(
|
|
function (content) {
|
|
self._currentNbQueries--;
|
|
if (self._currentNbQueries === 0) self.emit('searchQueueEmpty');
|
|
return {
|
|
content: new SearchResults(tempState, content.results),
|
|
state: tempState,
|
|
_originalResponse: content,
|
|
};
|
|
},
|
|
function (e) {
|
|
self._currentNbQueries--;
|
|
if (self._currentNbQueries === 0) self.emit('searchQueueEmpty');
|
|
throw e;
|
|
}
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Start the search for answers with the parameters set in the state.
|
|
* This method returns a promise.
|
|
* @param {Object} options - the options for answers API call
|
|
* @param {string[]} options.attributesForPrediction - Attributes to use for predictions. If empty, `searchableAttributes` is used instead.
|
|
* @param {string[]} options.queryLanguages - The languages in the query. Currently only supports ['en'].
|
|
* @param {number} options.nbHits - Maximum number of answers to retrieve from the Answers Engine. Cannot be greater than 1000.
|
|
*
|
|
* @return {promise} the answer results
|
|
* @deprecated answers is deprecated and will be replaced with new initiatives
|
|
*/
|
|
AlgoliaSearchHelper.prototype.findAnswers = function (options) {
|
|
// eslint-disable-next-line no-console
|
|
console.warn('[algoliasearch-helper] answers is no longer supported');
|
|
var state = this.state;
|
|
var derivedHelper = this.derivedHelpers[0];
|
|
if (!derivedHelper) {
|
|
return Promise.resolve([]);
|
|
}
|
|
var derivedState = derivedHelper.getModifiedState(state);
|
|
var data = merge(
|
|
{
|
|
attributesForPrediction: options.attributesForPrediction,
|
|
nbHits: options.nbHits,
|
|
},
|
|
{
|
|
params: omit(requestBuilder._getHitsSearchParams(derivedState), [
|
|
'attributesToSnippet',
|
|
'hitsPerPage',
|
|
'restrictSearchableAttributes',
|
|
'snippetEllipsisText',
|
|
]),
|
|
}
|
|
);
|
|
|
|
var errorMessage =
|
|
'search for answers was called, but this client does not have a function client.initIndex(index).findAnswers';
|
|
if (typeof this.client.initIndex !== 'function') {
|
|
throw new Error(errorMessage);
|
|
}
|
|
var index = this.client.initIndex(derivedState.index);
|
|
if (typeof index.findAnswers !== 'function') {
|
|
throw new Error(errorMessage);
|
|
}
|
|
return index.findAnswers(derivedState.query, options.queryLanguages, data);
|
|
};
|
|
|
|
/**
|
|
* Structure of each result when using
|
|
* [`searchForFacetValues()`](reference.html#AlgoliaSearchHelper#searchForFacetValues)
|
|
* @typedef FacetSearchHit
|
|
* @type {object}
|
|
* @property {string} value the facet value
|
|
* @property {string} highlighted the facet value highlighted with the query string
|
|
* @property {number} count number of occurrence of this facet value
|
|
* @property {boolean} isRefined true if the value is already refined
|
|
*/
|
|
|
|
/**
|
|
* Structure of the data resolved by the
|
|
* [`searchForFacetValues()`](reference.html#AlgoliaSearchHelper#searchForFacetValues)
|
|
* promise.
|
|
* @typedef FacetSearchResult
|
|
* @type {object}
|
|
* @property {FacetSearchHit} facetHits the results for this search for facet values
|
|
* @property {number} processingTimeMS time taken by the query inside the engine
|
|
*/
|
|
|
|
/**
|
|
* Search for facet values based on an query and the name of a faceted attribute. This
|
|
* triggers a search and will return a promise. On top of using the query, it also sends
|
|
* the parameters from the state so that the search is narrowed down to only the possible values.
|
|
*
|
|
* See the description of [FacetSearchResult](reference.html#FacetSearchResult)
|
|
* @param {string} facet the name of the faceted attribute
|
|
* @param {string} query the string query for the search
|
|
* @param {number} [maxFacetHits] the maximum number values returned. Should be > 0 and <= 100
|
|
* @param {object} [userState] the set of custom parameters to use on top of the current state. Setting a property to `undefined` removes
|
|
* it in the generated query.
|
|
* @return {promise.<FacetSearchResult>} the results of the search
|
|
*/
|
|
AlgoliaSearchHelper.prototype.searchForFacetValues = function (
|
|
facet,
|
|
query,
|
|
maxFacetHits,
|
|
userState
|
|
) {
|
|
var clientHasSFFV = typeof this.client.searchForFacetValues === 'function';
|
|
var clientHasInitIndex = typeof this.client.initIndex === 'function';
|
|
if (
|
|
!clientHasSFFV &&
|
|
!clientHasInitIndex &&
|
|
typeof this.client.search !== 'function'
|
|
) {
|
|
throw new Error(
|
|
'search for facet values (searchable) was called, but this client does not have a function client.searchForFacetValues or client.initIndex(index).searchForFacetValues'
|
|
);
|
|
}
|
|
|
|
var state = this.state.setQueryParameters(userState || {});
|
|
var isDisjunctive = state.isDisjunctiveFacet(facet);
|
|
var algoliaQuery = requestBuilder.getSearchForFacetQuery(
|
|
facet,
|
|
query,
|
|
maxFacetHits,
|
|
state
|
|
);
|
|
|
|
this._currentNbQueries++;
|
|
// eslint-disable-next-line consistent-this
|
|
var self = this;
|
|
var searchForFacetValuesPromise;
|
|
// newer algoliasearch ^3.27.1 - ~4.0.0
|
|
if (clientHasSFFV) {
|
|
searchForFacetValuesPromise = this.client.searchForFacetValues([
|
|
{ indexName: state.index, params: algoliaQuery },
|
|
]);
|
|
// algoliasearch < 3.27.1
|
|
} else if (clientHasInitIndex) {
|
|
searchForFacetValuesPromise = this.client
|
|
.initIndex(state.index)
|
|
.searchForFacetValues(algoliaQuery);
|
|
// algoliasearch ~5.0.0
|
|
} else {
|
|
// @MAJOR only use client.search
|
|
delete algoliaQuery.facetName;
|
|
searchForFacetValuesPromise = this.client
|
|
.search([
|
|
{
|
|
type: 'facet',
|
|
facet: facet,
|
|
indexName: state.index,
|
|
params: algoliaQuery,
|
|
},
|
|
])
|
|
.then(function processResponse(response) {
|
|
return response.results[0];
|
|
});
|
|
}
|
|
|
|
this.emit('searchForFacetValues', {
|
|
state: state,
|
|
facet: facet,
|
|
query: query,
|
|
});
|
|
|
|
return searchForFacetValuesPromise.then(
|
|
function addIsRefined(content) {
|
|
self._currentNbQueries--;
|
|
if (self._currentNbQueries === 0) self.emit('searchQueueEmpty');
|
|
|
|
content = Array.isArray(content) ? content[0] : content;
|
|
|
|
content.facetHits.forEach(function (f) {
|
|
f.escapedValue = escapeFacetValue(f.value);
|
|
f.isRefined = isDisjunctive
|
|
? state.isDisjunctiveFacetRefined(facet, f.escapedValue)
|
|
: state.isFacetRefined(facet, f.escapedValue);
|
|
});
|
|
|
|
return content;
|
|
},
|
|
function (e) {
|
|
self._currentNbQueries--;
|
|
if (self._currentNbQueries === 0) self.emit('searchQueueEmpty');
|
|
throw e;
|
|
}
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Sets the text query used for the search.
|
|
*
|
|
* This method resets the current page to 0.
|
|
* @param {string} q the user query
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
* @fires change
|
|
* @chainable
|
|
*/
|
|
AlgoliaSearchHelper.prototype.setQuery = function (q) {
|
|
this._change({
|
|
state: this.state.resetPage().setQuery(q),
|
|
isPageReset: true,
|
|
});
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Remove all the types of refinements except tags. A string can be provided to remove
|
|
* only the refinements of a specific attribute. For more advanced use case, you can
|
|
* provide a function instead. This function should follow the
|
|
* [clearCallback definition](#SearchParameters.clearCallback).
|
|
*
|
|
* This method resets the current page to 0.
|
|
* @param {string} [name] optional name of the facet / attribute on which we want to remove all refinements
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
* @fires change
|
|
* @chainable
|
|
* @example
|
|
* // Removing all the refinements
|
|
* helper.clearRefinements().search();
|
|
* @example
|
|
* // Removing all the filters on a the category attribute.
|
|
* helper.clearRefinements('category').search();
|
|
* @example
|
|
* // Removing only the exclude filters on the category facet.
|
|
* helper.clearRefinements(function(value, attribute, type) {
|
|
* return type === 'exclude' && attribute === 'category';
|
|
* }).search();
|
|
*/
|
|
AlgoliaSearchHelper.prototype.clearRefinements = function (name) {
|
|
this._change({
|
|
state: this.state.resetPage().clearRefinements(name),
|
|
isPageReset: true,
|
|
});
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Remove all the tag filters.
|
|
*
|
|
* This method resets the current page to 0.
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
* @fires change
|
|
* @chainable
|
|
*/
|
|
AlgoliaSearchHelper.prototype.clearTags = function () {
|
|
this._change({
|
|
state: this.state.resetPage().clearTags(),
|
|
isPageReset: true,
|
|
});
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Adds a disjunctive filter to a faceted attribute with the `value` provided. If the
|
|
* filter is already set, it doesn't change the filters.
|
|
*
|
|
* This method resets the current page to 0.
|
|
* @param {string} facet the facet to refine
|
|
* @param {string} value the associated value (will be converted to string)
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
* @fires change
|
|
* @chainable
|
|
*/
|
|
AlgoliaSearchHelper.prototype.addDisjunctiveFacetRefinement = function (
|
|
facet,
|
|
value
|
|
) {
|
|
this._change({
|
|
state: this.state.resetPage().addDisjunctiveFacetRefinement(facet, value),
|
|
isPageReset: true,
|
|
});
|
|
|
|
return this;
|
|
};
|
|
|
|
// eslint-disable-next-line valid-jsdoc
|
|
/**
|
|
* @deprecated since version 2.4.0, see {@link AlgoliaSearchHelper#addDisjunctiveFacetRefinement}
|
|
*/
|
|
AlgoliaSearchHelper.prototype.addDisjunctiveRefine = function () {
|
|
return this.addDisjunctiveFacetRefinement.apply(this, arguments);
|
|
};
|
|
|
|
/**
|
|
* Adds a refinement on a hierarchical facet. It will throw
|
|
* an exception if the facet is not defined or if the facet
|
|
* is already refined.
|
|
*
|
|
* This method resets the current page to 0.
|
|
* @param {string} facet the facet name
|
|
* @param {string} path the hierarchical facet path
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
* @throws Error if the facet is not defined or if the facet is refined
|
|
* @chainable
|
|
* @fires change
|
|
*/
|
|
AlgoliaSearchHelper.prototype.addHierarchicalFacetRefinement = function (
|
|
facet,
|
|
path
|
|
) {
|
|
this._change({
|
|
state: this.state.resetPage().addHierarchicalFacetRefinement(facet, path),
|
|
isPageReset: true,
|
|
});
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Adds a an numeric filter to an attribute with the `operator` and `value` provided. If the
|
|
* filter is already set, it doesn't change the filters.
|
|
*
|
|
* This method resets the current page to 0.
|
|
* @param {string} attribute the attribute on which the numeric filter applies
|
|
* @param {string} operator the operator of the filter
|
|
* @param {number} value the value of the filter
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
* @fires change
|
|
* @chainable
|
|
*/
|
|
AlgoliaSearchHelper.prototype.addNumericRefinement = function (
|
|
attribute,
|
|
operator,
|
|
value
|
|
) {
|
|
this._change({
|
|
state: this.state
|
|
.resetPage()
|
|
.addNumericRefinement(attribute, operator, value),
|
|
isPageReset: true,
|
|
});
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Adds a filter to a faceted attribute with the `value` provided. If the
|
|
* filter is already set, it doesn't change the filters.
|
|
*
|
|
* This method resets the current page to 0.
|
|
* @param {string} facet the facet to refine
|
|
* @param {string} value the associated value (will be converted to string)
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
* @fires change
|
|
* @chainable
|
|
*/
|
|
AlgoliaSearchHelper.prototype.addFacetRefinement = function (facet, value) {
|
|
this._change({
|
|
state: this.state.resetPage().addFacetRefinement(facet, value),
|
|
isPageReset: true,
|
|
});
|
|
|
|
return this;
|
|
};
|
|
|
|
// eslint-disable-next-line valid-jsdoc
|
|
/**
|
|
* @deprecated since version 2.4.0, see {@link AlgoliaSearchHelper#addFacetRefinement}
|
|
*/
|
|
AlgoliaSearchHelper.prototype.addRefine = function () {
|
|
return this.addFacetRefinement.apply(this, arguments);
|
|
};
|
|
|
|
/**
|
|
* Adds a an exclusion filter to a faceted attribute with the `value` provided. If the
|
|
* filter is already set, it doesn't change the filters.
|
|
*
|
|
* This method resets the current page to 0.
|
|
* @param {string} facet the facet to refine
|
|
* @param {string} value the associated value (will be converted to string)
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
* @fires change
|
|
* @chainable
|
|
*/
|
|
AlgoliaSearchHelper.prototype.addFacetExclusion = function (facet, value) {
|
|
this._change({
|
|
state: this.state.resetPage().addExcludeRefinement(facet, value),
|
|
isPageReset: true,
|
|
});
|
|
|
|
return this;
|
|
};
|
|
|
|
// eslint-disable-next-line valid-jsdoc
|
|
/**
|
|
* @deprecated since version 2.4.0, see {@link AlgoliaSearchHelper#addFacetExclusion}
|
|
*/
|
|
AlgoliaSearchHelper.prototype.addExclude = function () {
|
|
return this.addFacetExclusion.apply(this, arguments);
|
|
};
|
|
|
|
/**
|
|
* Adds a tag filter with the `tag` provided. If the
|
|
* filter is already set, it doesn't change the filters.
|
|
*
|
|
* This method resets the current page to 0.
|
|
* @param {string} tag the tag to add to the filter
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
* @fires change
|
|
* @chainable
|
|
*/
|
|
AlgoliaSearchHelper.prototype.addTag = function (tag) {
|
|
this._change({
|
|
state: this.state.resetPage().addTagRefinement(tag),
|
|
isPageReset: true,
|
|
});
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Removes an numeric filter to an attribute with the `operator` and `value` provided. If the
|
|
* filter is not set, it doesn't change the filters.
|
|
*
|
|
* Some parameters are optional, triggering different behavior:
|
|
* - if the value is not provided, then all the numeric value will be removed for the
|
|
* specified attribute/operator couple.
|
|
* - if the operator is not provided either, then all the numeric filter on this attribute
|
|
* will be removed.
|
|
*
|
|
* This method resets the current page to 0.
|
|
* @param {string} attribute the attribute on which the numeric filter applies
|
|
* @param {string} [operator] the operator of the filter
|
|
* @param {number} [value] the value of the filter
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
* @fires change
|
|
* @chainable
|
|
*/
|
|
AlgoliaSearchHelper.prototype.removeNumericRefinement = function (
|
|
attribute,
|
|
operator,
|
|
value
|
|
) {
|
|
this._change({
|
|
state: this.state
|
|
.resetPage()
|
|
.removeNumericRefinement(attribute, operator, value),
|
|
isPageReset: true,
|
|
});
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Removes a disjunctive filter to a faceted attribute with the `value` provided. If the
|
|
* filter is not set, it doesn't change the filters.
|
|
*
|
|
* If the value is omitted, then this method will remove all the filters for the
|
|
* attribute.
|
|
*
|
|
* This method resets the current page to 0.
|
|
* @param {string} facet the facet to refine
|
|
* @param {string} [value] the associated value
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
* @fires change
|
|
* @chainable
|
|
*/
|
|
AlgoliaSearchHelper.prototype.removeDisjunctiveFacetRefinement = function (
|
|
facet,
|
|
value
|
|
) {
|
|
this._change({
|
|
state: this.state
|
|
.resetPage()
|
|
.removeDisjunctiveFacetRefinement(facet, value),
|
|
isPageReset: true,
|
|
});
|
|
|
|
return this;
|
|
};
|
|
|
|
// eslint-disable-next-line valid-jsdoc
|
|
/**
|
|
* @deprecated since version 2.4.0, see {@link AlgoliaSearchHelper#removeDisjunctiveFacetRefinement}
|
|
*/
|
|
AlgoliaSearchHelper.prototype.removeDisjunctiveRefine = function () {
|
|
return this.removeDisjunctiveFacetRefinement.apply(this, arguments);
|
|
};
|
|
|
|
/**
|
|
* Removes the refinement set on a hierarchical facet.
|
|
* @param {string} facet the facet name
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
* @throws Error if the facet is not defined or if the facet is not refined
|
|
* @fires change
|
|
* @chainable
|
|
*/
|
|
AlgoliaSearchHelper.prototype.removeHierarchicalFacetRefinement = function (
|
|
facet
|
|
) {
|
|
this._change({
|
|
state: this.state.resetPage().removeHierarchicalFacetRefinement(facet),
|
|
isPageReset: true,
|
|
});
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Removes a filter to a faceted attribute with the `value` provided. If the
|
|
* filter is not set, it doesn't change the filters.
|
|
*
|
|
* If the value is omitted, then this method will remove all the filters for the
|
|
* attribute.
|
|
*
|
|
* This method resets the current page to 0.
|
|
* @param {string} facet the facet to refine
|
|
* @param {string} [value] the associated value
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
* @fires change
|
|
* @chainable
|
|
*/
|
|
AlgoliaSearchHelper.prototype.removeFacetRefinement = function (facet, value) {
|
|
this._change({
|
|
state: this.state.resetPage().removeFacetRefinement(facet, value),
|
|
isPageReset: true,
|
|
});
|
|
|
|
return this;
|
|
};
|
|
|
|
// eslint-disable-next-line valid-jsdoc
|
|
/**
|
|
* @deprecated since version 2.4.0, see {@link AlgoliaSearchHelper#removeFacetRefinement}
|
|
*/
|
|
AlgoliaSearchHelper.prototype.removeRefine = function () {
|
|
return this.removeFacetRefinement.apply(this, arguments);
|
|
};
|
|
|
|
/**
|
|
* Removes an exclusion filter to a faceted attribute with the `value` provided. If the
|
|
* filter is not set, it doesn't change the filters.
|
|
*
|
|
* If the value is omitted, then this method will remove all the filters for the
|
|
* attribute.
|
|
*
|
|
* This method resets the current page to 0.
|
|
* @param {string} facet the facet to refine
|
|
* @param {string} [value] the associated value
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
* @fires change
|
|
* @chainable
|
|
*/
|
|
AlgoliaSearchHelper.prototype.removeFacetExclusion = function (facet, value) {
|
|
this._change({
|
|
state: this.state.resetPage().removeExcludeRefinement(facet, value),
|
|
isPageReset: true,
|
|
});
|
|
|
|
return this;
|
|
};
|
|
|
|
// eslint-disable-next-line valid-jsdoc
|
|
/**
|
|
* @deprecated since version 2.4.0, see {@link AlgoliaSearchHelper#removeFacetExclusion}
|
|
*/
|
|
AlgoliaSearchHelper.prototype.removeExclude = function () {
|
|
return this.removeFacetExclusion.apply(this, arguments);
|
|
};
|
|
|
|
/**
|
|
* Removes a tag filter with the `tag` provided. If the
|
|
* filter is not set, it doesn't change the filters.
|
|
*
|
|
* This method resets the current page to 0.
|
|
* @param {string} tag tag to remove from the filter
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
* @fires change
|
|
* @chainable
|
|
*/
|
|
AlgoliaSearchHelper.prototype.removeTag = function (tag) {
|
|
this._change({
|
|
state: this.state.resetPage().removeTagRefinement(tag),
|
|
isPageReset: true,
|
|
});
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Adds or removes an exclusion filter to a faceted attribute with the `value` provided. If
|
|
* the value is set then it removes it, otherwise it adds the filter.
|
|
*
|
|
* This method resets the current page to 0.
|
|
* @param {string} facet the facet to refine
|
|
* @param {string} value the associated value
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
* @fires change
|
|
* @chainable
|
|
*/
|
|
AlgoliaSearchHelper.prototype.toggleFacetExclusion = function (facet, value) {
|
|
this._change({
|
|
state: this.state.resetPage().toggleExcludeFacetRefinement(facet, value),
|
|
isPageReset: true,
|
|
});
|
|
|
|
return this;
|
|
};
|
|
|
|
// eslint-disable-next-line valid-jsdoc
|
|
/**
|
|
* @deprecated since version 2.4.0, see {@link AlgoliaSearchHelper#toggleFacetExclusion}
|
|
*/
|
|
AlgoliaSearchHelper.prototype.toggleExclude = function () {
|
|
return this.toggleFacetExclusion.apply(this, arguments);
|
|
};
|
|
|
|
/**
|
|
* Adds or removes a filter to a faceted attribute with the `value` provided. If
|
|
* the value is set then it removes it, otherwise it adds the filter.
|
|
*
|
|
* This method can be used for conjunctive, disjunctive and hierarchical filters.
|
|
*
|
|
* This method resets the current page to 0.
|
|
* @param {string} facet the facet to refine
|
|
* @param {string} value the associated value
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
* @throws Error will throw an error if the facet is not declared in the settings of the helper
|
|
* @fires change
|
|
* @chainable
|
|
* @deprecated since version 2.19.0, see {@link AlgoliaSearchHelper#toggleFacetRefinement}
|
|
*/
|
|
AlgoliaSearchHelper.prototype.toggleRefinement = function (facet, value) {
|
|
return this.toggleFacetRefinement(facet, value);
|
|
};
|
|
|
|
/**
|
|
* Adds or removes a filter to a faceted attribute with the `value` provided. If
|
|
* the value is set then it removes it, otherwise it adds the filter.
|
|
*
|
|
* This method can be used for conjunctive, disjunctive and hierarchical filters.
|
|
*
|
|
* This method resets the current page to 0.
|
|
* @param {string} facet the facet to refine
|
|
* @param {string} value the associated value
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
* @throws Error will throw an error if the facet is not declared in the settings of the helper
|
|
* @fires change
|
|
* @chainable
|
|
*/
|
|
AlgoliaSearchHelper.prototype.toggleFacetRefinement = function (facet, value) {
|
|
this._change({
|
|
state: this.state.resetPage().toggleFacetRefinement(facet, value),
|
|
isPageReset: true,
|
|
});
|
|
|
|
return this;
|
|
};
|
|
|
|
// eslint-disable-next-line valid-jsdoc
|
|
/**
|
|
* @deprecated since version 2.4.0, see {@link AlgoliaSearchHelper#toggleFacetRefinement}
|
|
*/
|
|
AlgoliaSearchHelper.prototype.toggleRefine = function () {
|
|
return this.toggleFacetRefinement.apply(this, arguments);
|
|
};
|
|
|
|
/**
|
|
* Adds or removes a tag filter with the `value` provided. If
|
|
* the value is set then it removes it, otherwise it adds the filter.
|
|
*
|
|
* This method resets the current page to 0.
|
|
* @param {string} tag tag to remove or add
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
* @fires change
|
|
* @chainable
|
|
*/
|
|
AlgoliaSearchHelper.prototype.toggleTag = function (tag) {
|
|
this._change({
|
|
state: this.state.resetPage().toggleTagRefinement(tag),
|
|
isPageReset: true,
|
|
});
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Increments the page number by one.
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
* @fires change
|
|
* @chainable
|
|
* @example
|
|
* helper.setPage(0).nextPage().getPage();
|
|
* // returns 1
|
|
*/
|
|
AlgoliaSearchHelper.prototype.nextPage = function () {
|
|
var page = this.state.page || 0;
|
|
return this.setPage(page + 1);
|
|
};
|
|
|
|
/**
|
|
* Decrements the page number by one.
|
|
* @fires change
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
* @chainable
|
|
* @example
|
|
* helper.setPage(1).previousPage().getPage();
|
|
* // returns 0
|
|
*/
|
|
AlgoliaSearchHelper.prototype.previousPage = function () {
|
|
var page = this.state.page || 0;
|
|
return this.setPage(page - 1);
|
|
};
|
|
|
|
/**
|
|
* @private
|
|
* @param {number} page The page number
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
* @chainable
|
|
* @fires change
|
|
*/
|
|
function setCurrentPage(page) {
|
|
if (page < 0) throw new Error('Page requested below 0.');
|
|
|
|
this._change({
|
|
state: this.state.setPage(page),
|
|
isPageReset: false,
|
|
});
|
|
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Change the current page
|
|
* @deprecated
|
|
* @param {number} page The page number
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
* @fires change
|
|
* @chainable
|
|
*/
|
|
AlgoliaSearchHelper.prototype.setCurrentPage = setCurrentPage;
|
|
|
|
/**
|
|
* Updates the current page.
|
|
* @function
|
|
* @param {number} page The page number
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
* @fires change
|
|
* @chainable
|
|
*/
|
|
AlgoliaSearchHelper.prototype.setPage = setCurrentPage;
|
|
|
|
/**
|
|
* Updates the name of the index that will be targeted by the query.
|
|
*
|
|
* This method resets the current page to 0.
|
|
* @param {string} name the index name
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
* @fires change
|
|
* @chainable
|
|
*/
|
|
AlgoliaSearchHelper.prototype.setIndex = function (name) {
|
|
this._change({
|
|
state: this.state.resetPage().setIndex(name),
|
|
isPageReset: true,
|
|
});
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Update a parameter of the search. This method reset the page
|
|
*
|
|
* The complete list of parameters is available on the
|
|
* [Algolia website](https://www.algolia.com/doc/rest#query-an-index).
|
|
* The most commonly used parameters have their own [shortcuts](#query-parameters-shortcuts)
|
|
* or benefit from higher-level APIs (all the kind of filters and facets have their own API)
|
|
*
|
|
* This method resets the current page to 0.
|
|
* @param {string} parameter name of the parameter to update
|
|
* @param {any} value new value of the parameter
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
* @fires change
|
|
* @chainable
|
|
* @example
|
|
* helper.setQueryParameter('hitsPerPage', 20).search();
|
|
*/
|
|
AlgoliaSearchHelper.prototype.setQueryParameter = function (parameter, value) {
|
|
this._change({
|
|
state: this.state.resetPage().setQueryParameter(parameter, value),
|
|
isPageReset: true,
|
|
});
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Set the whole state (warning: will erase previous state)
|
|
* @param {SearchParameters} newState the whole new state
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
* @fires change
|
|
* @chainable
|
|
*/
|
|
AlgoliaSearchHelper.prototype.setState = function (newState) {
|
|
this._change({
|
|
state: SearchParameters.make(newState),
|
|
isPageReset: false,
|
|
});
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Override the current state without triggering a change event.
|
|
* Do not use this method unless you know what you are doing. (see the example
|
|
* for a legit use case)
|
|
* @param {SearchParameters} newState the whole new state
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
* @example
|
|
* helper.on('change', function(state){
|
|
* // In this function you might want to find a way to store the state in the url/history
|
|
* updateYourURL(state)
|
|
* })
|
|
* window.onpopstate = function(event){
|
|
* // This is naive though as you should check if the state is really defined etc.
|
|
* helper.overrideStateWithoutTriggeringChangeEvent(event.state).search()
|
|
* }
|
|
* @chainable
|
|
*/
|
|
AlgoliaSearchHelper.prototype.overrideStateWithoutTriggeringChangeEvent =
|
|
function (newState) {
|
|
this.state = new SearchParameters(newState);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Check if an attribute has any numeric, conjunctive, disjunctive or hierarchical filters.
|
|
* @param {string} attribute the name of the attribute
|
|
* @return {boolean} true if the attribute is filtered by at least one value
|
|
* @example
|
|
* // hasRefinements works with numeric, conjunctive, disjunctive and hierarchical filters
|
|
* helper.hasRefinements('price'); // false
|
|
* helper.addNumericRefinement('price', '>', 100);
|
|
* helper.hasRefinements('price'); // true
|
|
*
|
|
* helper.hasRefinements('color'); // false
|
|
* helper.addFacetRefinement('color', 'blue');
|
|
* helper.hasRefinements('color'); // true
|
|
*
|
|
* helper.hasRefinements('material'); // false
|
|
* helper.addDisjunctiveFacetRefinement('material', 'plastic');
|
|
* helper.hasRefinements('material'); // true
|
|
*
|
|
* helper.hasRefinements('categories'); // false
|
|
* helper.toggleFacetRefinement('categories', 'kitchen > knife');
|
|
* helper.hasRefinements('categories'); // true
|
|
*
|
|
*/
|
|
AlgoliaSearchHelper.prototype.hasRefinements = function (attribute) {
|
|
if (objectHasKeys(this.state.getNumericRefinements(attribute))) {
|
|
return true;
|
|
} else if (this.state.isConjunctiveFacet(attribute)) {
|
|
return this.state.isFacetRefined(attribute);
|
|
} else if (this.state.isDisjunctiveFacet(attribute)) {
|
|
return this.state.isDisjunctiveFacetRefined(attribute);
|
|
} else if (this.state.isHierarchicalFacet(attribute)) {
|
|
return this.state.isHierarchicalFacetRefined(attribute);
|
|
}
|
|
|
|
// there's currently no way to know that the user did call `addNumericRefinement` at some point
|
|
// thus we cannot distinguish if there once was a numeric refinement that was cleared
|
|
// so we will return false in every other situations to be consistent
|
|
// while what we should do here is throw because we did not find the attribute in any type
|
|
// of refinement
|
|
return false;
|
|
};
|
|
|
|
/**
|
|
* Check if a value is excluded for a specific faceted attribute. If the value
|
|
* is omitted then the function checks if there is any excluding refinements.
|
|
*
|
|
* @param {string} facet name of the attribute for used for faceting
|
|
* @param {string} [value] optional value. If passed will test that this value
|
|
* is filtering the given facet.
|
|
* @return {boolean} true if refined
|
|
* @example
|
|
* helper.isExcludeRefined('color'); // false
|
|
* helper.isExcludeRefined('color', 'blue') // false
|
|
* helper.isExcludeRefined('color', 'red') // false
|
|
*
|
|
* helper.addFacetExclusion('color', 'red');
|
|
*
|
|
* helper.isExcludeRefined('color'); // true
|
|
* helper.isExcludeRefined('color', 'blue') // false
|
|
* helper.isExcludeRefined('color', 'red') // true
|
|
*/
|
|
AlgoliaSearchHelper.prototype.isExcluded = function (facet, value) {
|
|
return this.state.isExcludeRefined(facet, value);
|
|
};
|
|
|
|
// eslint-disable-next-line valid-jsdoc
|
|
/**
|
|
* @deprecated since 2.4.0, see {@link AlgoliaSearchHelper#hasRefinements}
|
|
*/
|
|
AlgoliaSearchHelper.prototype.isDisjunctiveRefined = function (facet, value) {
|
|
return this.state.isDisjunctiveFacetRefined(facet, value);
|
|
};
|
|
|
|
/**
|
|
* Check if the string is a currently filtering tag.
|
|
* @param {string} tag tag to check
|
|
* @return {boolean} true if the tag is currently refined
|
|
*/
|
|
AlgoliaSearchHelper.prototype.hasTag = function (tag) {
|
|
return this.state.isTagRefined(tag);
|
|
};
|
|
|
|
// eslint-disable-next-line valid-jsdoc
|
|
/**
|
|
* @deprecated since 2.4.0, see {@link AlgoliaSearchHelper#hasTag}
|
|
*/
|
|
AlgoliaSearchHelper.prototype.isTagRefined = function () {
|
|
return this.hasTagRefinements.apply(this, arguments);
|
|
};
|
|
|
|
/**
|
|
* Get the name of the currently used index.
|
|
* @return {string} name of the index
|
|
* @example
|
|
* helper.setIndex('highestPrice_products').getIndex();
|
|
* // returns 'highestPrice_products'
|
|
*/
|
|
AlgoliaSearchHelper.prototype.getIndex = function () {
|
|
return this.state.index;
|
|
};
|
|
|
|
function getCurrentPage() {
|
|
return this.state.page;
|
|
}
|
|
|
|
/**
|
|
* Get the currently selected page
|
|
* @deprecated
|
|
* @return {number} the current page
|
|
*/
|
|
AlgoliaSearchHelper.prototype.getCurrentPage = getCurrentPage;
|
|
/**
|
|
* Get the currently selected page
|
|
* @function
|
|
* @return {number} the current page
|
|
*/
|
|
AlgoliaSearchHelper.prototype.getPage = getCurrentPage;
|
|
|
|
/**
|
|
* Get all the tags currently set to filters the results.
|
|
*
|
|
* @return {string[]} The list of tags currently set.
|
|
*/
|
|
AlgoliaSearchHelper.prototype.getTags = function () {
|
|
return this.state.tagRefinements;
|
|
};
|
|
|
|
/**
|
|
* Get the list of refinements for a given attribute. This method works with
|
|
* conjunctive, disjunctive, excluding and numerical filters.
|
|
*
|
|
* See also SearchResults#getRefinements
|
|
*
|
|
* @param {string} facetName attribute name used for faceting
|
|
* @return {Array.<FacetRefinement|NumericRefinement>} All Refinement are objects that contain a value, and
|
|
* a type. Numeric also contains an operator.
|
|
* @example
|
|
* helper.addNumericRefinement('price', '>', 100);
|
|
* helper.getRefinements('price');
|
|
* // [
|
|
* // {
|
|
* // "value": [
|
|
* // 100
|
|
* // ],
|
|
* // "operator": ">",
|
|
* // "type": "numeric"
|
|
* // }
|
|
* // ]
|
|
* @example
|
|
* helper.addFacetRefinement('color', 'blue');
|
|
* helper.addFacetExclusion('color', 'red');
|
|
* helper.getRefinements('color');
|
|
* // [
|
|
* // {
|
|
* // "value": "blue",
|
|
* // "type": "conjunctive"
|
|
* // },
|
|
* // {
|
|
* // "value": "red",
|
|
* // "type": "exclude"
|
|
* // }
|
|
* // ]
|
|
* @example
|
|
* helper.addDisjunctiveFacetRefinement('material', 'plastic');
|
|
* // [
|
|
* // {
|
|
* // "value": "plastic",
|
|
* // "type": "disjunctive"
|
|
* // }
|
|
* // ]
|
|
*/
|
|
AlgoliaSearchHelper.prototype.getRefinements = function (facetName) {
|
|
var refinements = [];
|
|
|
|
if (this.state.isConjunctiveFacet(facetName)) {
|
|
var conjRefinements = this.state.getConjunctiveRefinements(facetName);
|
|
|
|
conjRefinements.forEach(function (r) {
|
|
refinements.push({
|
|
value: r,
|
|
type: 'conjunctive',
|
|
});
|
|
});
|
|
|
|
var excludeRefinements = this.state.getExcludeRefinements(facetName);
|
|
|
|
excludeRefinements.forEach(function (r) {
|
|
refinements.push({
|
|
value: r,
|
|
type: 'exclude',
|
|
});
|
|
});
|
|
} else if (this.state.isDisjunctiveFacet(facetName)) {
|
|
var disjunctiveRefinements =
|
|
this.state.getDisjunctiveRefinements(facetName);
|
|
|
|
disjunctiveRefinements.forEach(function (r) {
|
|
refinements.push({
|
|
value: r,
|
|
type: 'disjunctive',
|
|
});
|
|
});
|
|
}
|
|
|
|
var numericRefinements = this.state.getNumericRefinements(facetName);
|
|
|
|
Object.keys(numericRefinements).forEach(function (operator) {
|
|
var value = numericRefinements[operator];
|
|
|
|
refinements.push({
|
|
value: value,
|
|
operator: operator,
|
|
type: 'numeric',
|
|
});
|
|
});
|
|
|
|
return refinements;
|
|
};
|
|
|
|
/**
|
|
* Return the current refinement for the (attribute, operator)
|
|
* @param {string} attribute attribute in the record
|
|
* @param {string} operator operator applied on the refined values
|
|
* @return {Array.<number|number[]>} refined values
|
|
*/
|
|
AlgoliaSearchHelper.prototype.getNumericRefinement = function (
|
|
attribute,
|
|
operator
|
|
) {
|
|
return this.state.getNumericRefinement(attribute, operator);
|
|
};
|
|
|
|
/**
|
|
* Get the current breadcrumb for a hierarchical facet, as an array
|
|
* @param {string} facetName Hierarchical facet name
|
|
* @return {array.<string>} the path as an array of string
|
|
*/
|
|
AlgoliaSearchHelper.prototype.getHierarchicalFacetBreadcrumb = function (
|
|
facetName
|
|
) {
|
|
return this.state.getHierarchicalFacetBreadcrumb(facetName);
|
|
};
|
|
|
|
// /////////// PRIVATE
|
|
|
|
/**
|
|
* Perform the underlying queries
|
|
* @private
|
|
* @param {object} options options for the query
|
|
* @param {boolean} [options.onlyWithDerivedHelpers=false] if true, only the derived helpers will be queried
|
|
* @return {undefined} does not return anything
|
|
* @fires search
|
|
* @fires result
|
|
* @fires error
|
|
*/
|
|
AlgoliaSearchHelper.prototype._search = function (options) {
|
|
var state = this.state;
|
|
var states = [];
|
|
var mainQueries = [];
|
|
|
|
if (!options.onlyWithDerivedHelpers) {
|
|
mainQueries = requestBuilder._getQueries(state.index, state);
|
|
|
|
states.push({
|
|
state: state,
|
|
queriesCount: mainQueries.length,
|
|
helper: this,
|
|
});
|
|
|
|
this.emit('search', {
|
|
state: state,
|
|
results: this.lastResults,
|
|
});
|
|
}
|
|
|
|
var derivedQueries = this.derivedHelpers.map(function (derivedHelper) {
|
|
var derivedState = derivedHelper.getModifiedState(state);
|
|
var derivedStateQueries = derivedState.index
|
|
? requestBuilder._getQueries(derivedState.index, derivedState)
|
|
: [];
|
|
|
|
states.push({
|
|
state: derivedState,
|
|
queriesCount: derivedStateQueries.length,
|
|
helper: derivedHelper,
|
|
});
|
|
|
|
derivedHelper.emit('search', {
|
|
state: derivedState,
|
|
results: derivedHelper.lastResults,
|
|
});
|
|
|
|
return derivedStateQueries;
|
|
});
|
|
|
|
var queries = Array.prototype.concat.apply(mainQueries, derivedQueries);
|
|
|
|
var queryId = this._queryId++;
|
|
this._currentNbQueries++;
|
|
|
|
if (!queries.length) {
|
|
return Promise.resolve({ results: [] }).then(
|
|
this._dispatchAlgoliaResponse.bind(this, states, queryId)
|
|
);
|
|
}
|
|
|
|
try {
|
|
this.client
|
|
.search(queries)
|
|
.then(this._dispatchAlgoliaResponse.bind(this, states, queryId))
|
|
.catch(this._dispatchAlgoliaError.bind(this, queryId));
|
|
} catch (error) {
|
|
// If we reach this part, we're in an internal error state
|
|
this.emit('error', {
|
|
error: error,
|
|
});
|
|
}
|
|
|
|
return undefined;
|
|
};
|
|
|
|
/**
|
|
* Transform the responses as sent by the server and transform them into a user
|
|
* usable object that merge the results of all the batch requests. It will dispatch
|
|
* over the different helper + derived helpers (when there are some).
|
|
* @private
|
|
* @param {array.<{SearchParameters, AlgoliaQueries, AlgoliaSearchHelper}>} states state used to generate the request
|
|
* @param {number} queryId id of the current request
|
|
* @param {object} content content of the response
|
|
* @return {undefined}
|
|
*/
|
|
AlgoliaSearchHelper.prototype._dispatchAlgoliaResponse = function (
|
|
states,
|
|
queryId,
|
|
content
|
|
) {
|
|
// @TODO remove the number of outdated queries discarded instead of just one
|
|
|
|
if (queryId < this._lastQueryIdReceived) {
|
|
// Outdated answer
|
|
return;
|
|
}
|
|
|
|
this._currentNbQueries -= queryId - this._lastQueryIdReceived;
|
|
this._lastQueryIdReceived = queryId;
|
|
|
|
if (this._currentNbQueries === 0) this.emit('searchQueueEmpty');
|
|
|
|
var results = content.results.slice();
|
|
|
|
states.forEach(function (s) {
|
|
var state = s.state;
|
|
var queriesCount = s.queriesCount;
|
|
var helper = s.helper;
|
|
var specificResults = results.splice(0, queriesCount);
|
|
|
|
if (!state.index) {
|
|
helper.emit('result', {
|
|
results: null,
|
|
state: state,
|
|
});
|
|
return;
|
|
}
|
|
|
|
helper.lastResults = new SearchResults(state, specificResults);
|
|
|
|
helper.emit('result', {
|
|
results: helper.lastResults,
|
|
state: state,
|
|
});
|
|
});
|
|
};
|
|
|
|
AlgoliaSearchHelper.prototype._dispatchAlgoliaError = function (
|
|
queryId,
|
|
error
|
|
) {
|
|
if (queryId < this._lastQueryIdReceived) {
|
|
// Outdated answer
|
|
return;
|
|
}
|
|
|
|
this._currentNbQueries -= queryId - this._lastQueryIdReceived;
|
|
this._lastQueryIdReceived = queryId;
|
|
|
|
this.emit('error', {
|
|
error: error,
|
|
});
|
|
|
|
if (this._currentNbQueries === 0) this.emit('searchQueueEmpty');
|
|
};
|
|
|
|
AlgoliaSearchHelper.prototype.containsRefinement = function (
|
|
query,
|
|
facetFilters,
|
|
numericFilters,
|
|
tagFilters
|
|
) {
|
|
return (
|
|
query ||
|
|
facetFilters.length !== 0 ||
|
|
numericFilters.length !== 0 ||
|
|
tagFilters.length !== 0
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Test if there are some disjunctive refinements on the facet
|
|
* @private
|
|
* @param {string} facet the attribute to test
|
|
* @return {boolean} true if there are refinements on this attribute
|
|
*/
|
|
AlgoliaSearchHelper.prototype._hasDisjunctiveRefinements = function (facet) {
|
|
return (
|
|
this.state.disjunctiveRefinements[facet] &&
|
|
this.state.disjunctiveRefinements[facet].length > 0
|
|
);
|
|
};
|
|
|
|
AlgoliaSearchHelper.prototype._change = function (event) {
|
|
var state = event.state;
|
|
var isPageReset = event.isPageReset;
|
|
|
|
if (state !== this.state) {
|
|
this.state = state;
|
|
|
|
this.emit('change', {
|
|
state: this.state,
|
|
results: this.lastResults,
|
|
isPageReset: isPageReset,
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Clears the cache of the underlying Algolia client.
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
*/
|
|
AlgoliaSearchHelper.prototype.clearCache = function () {
|
|
if (this.client.clearCache) this.client.clearCache();
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Updates the internal client instance. If the reference of the clients
|
|
* are equal then no update is actually done.
|
|
* @param {AlgoliaSearch} newClient an AlgoliaSearch client
|
|
* @return {AlgoliaSearchHelper} Method is chainable, it returns itself
|
|
*/
|
|
AlgoliaSearchHelper.prototype.setClient = function (newClient) {
|
|
if (this.client === newClient) return this;
|
|
|
|
if (typeof newClient.addAlgoliaAgent === 'function') {
|
|
newClient.addAlgoliaAgent('JS Helper (' + version + ')');
|
|
}
|
|
this.client = newClient;
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Gets the instance of the currently used client.
|
|
* @return {AlgoliaSearch} the currently used client
|
|
*/
|
|
AlgoliaSearchHelper.prototype.getClient = function () {
|
|
return this.client;
|
|
};
|
|
|
|
/**
|
|
* Creates an derived instance of the Helper. A derived helper
|
|
* is a way to request other indices synchronised with the lifecycle
|
|
* of the main Helper. This mechanism uses the multiqueries feature
|
|
* of Algolia to aggregate all the requests in a single network call.
|
|
*
|
|
* This method takes a function that is used to create a new SearchParameter
|
|
* that will be used to create requests to Algolia. Those new requests
|
|
* are created just before the `search` event. The signature of the function
|
|
* is `SearchParameters -> SearchParameters`.
|
|
*
|
|
* This method returns a new DerivedHelper which is an EventEmitter
|
|
* that fires the same `search`, `result` and `error` events. Those
|
|
* events, however, will receive data specific to this DerivedHelper
|
|
* and the SearchParameters that is returned by the call of the
|
|
* parameter function.
|
|
* @param {function} fn SearchParameters -> SearchParameters
|
|
* @return {DerivedHelper} a new DerivedHelper
|
|
*/
|
|
AlgoliaSearchHelper.prototype.derive = function (fn) {
|
|
var derivedHelper = new DerivedHelper(this, fn);
|
|
this.derivedHelpers.push(derivedHelper);
|
|
return derivedHelper;
|
|
};
|
|
|
|
/**
|
|
* This method detaches a derived Helper from the main one. Prefer using the one from the
|
|
* derived helper itself, to remove the event listeners too.
|
|
* @private
|
|
* @param {DerivedHelper} derivedHelper the derived helper to detach
|
|
* @return {undefined} nothing is returned
|
|
* @throws Error
|
|
*/
|
|
AlgoliaSearchHelper.prototype.detachDerivedHelper = function (derivedHelper) {
|
|
var pos = this.derivedHelpers.indexOf(derivedHelper);
|
|
if (pos === -1) throw new Error('Derived helper already detached');
|
|
this.derivedHelpers.splice(pos, 1);
|
|
};
|
|
|
|
/**
|
|
* This method returns true if there is currently at least one on-going search.
|
|
* @return {boolean} true if there is a search pending
|
|
*/
|
|
AlgoliaSearchHelper.prototype.hasPendingRequests = function () {
|
|
return this._currentNbQueries > 0;
|
|
};
|
|
|
|
/**
|
|
* @typedef AlgoliaSearchHelper.NumericRefinement
|
|
* @type {object}
|
|
* @property {number[]} value the numbers that are used for filtering this attribute with
|
|
* the operator specified.
|
|
* @property {string} operator the faceting data: value, number of entries
|
|
* @property {string} type will be 'numeric'
|
|
*/
|
|
|
|
/**
|
|
* @typedef AlgoliaSearchHelper.FacetRefinement
|
|
* @type {object}
|
|
* @property {string} value the string use to filter the attribute
|
|
* @property {string} type the type of filter: 'conjunctive', 'disjunctive', 'exclude'
|
|
*/
|
|
|
|
module.exports = AlgoliaSearchHelper;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 74587:
|
|
/***/ ((module) => {
|
|
|
|
"use strict";
|
|
|
|
|
|
module.exports = function compact(array) {
|
|
if (!Array.isArray(array)) {
|
|
return [];
|
|
}
|
|
|
|
return array.filter(Boolean);
|
|
};
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 52344:
|
|
/***/ ((module) => {
|
|
|
|
"use strict";
|
|
|
|
|
|
// NOTE: this behaves like lodash/defaults, but doesn't mutate the target
|
|
// it also preserve keys order
|
|
module.exports = 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;
|
|
}, {});
|
|
};
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 94039:
|
|
/***/ ((module) => {
|
|
|
|
"use strict";
|
|
|
|
|
|
/**
|
|
* 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(/^\\-/, '-');
|
|
}
|
|
|
|
module.exports = {
|
|
escapeFacetValue: escapeFacetValue,
|
|
unescapeFacetValue: unescapeFacetValue,
|
|
};
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 7888:
|
|
/***/ ((module) => {
|
|
|
|
"use strict";
|
|
|
|
|
|
// @MAJOR can be replaced by native Array#find when we change support
|
|
module.exports = 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;
|
|
};
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 69725:
|
|
/***/ ((module) => {
|
|
|
|
"use strict";
|
|
|
|
|
|
// @MAJOR can be replaced by native Array#findIndex when we change support
|
|
module.exports = function find(array, comparator) {
|
|
if (!Array.isArray(array)) {
|
|
return -1;
|
|
}
|
|
|
|
for (var i = 0; i < array.length; i++) {
|
|
if (comparator(array[i])) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
};
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 82293:
|
|
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
|
|
var find = __webpack_require__(7888);
|
|
|
|
/**
|
|
* Transform sort format from user friendly notation to lodash format
|
|
* @param {string[]} sortBy array of predicate of the form "attribute:order"
|
|
* @param {string[]} [defaults] array of predicate of the form "attribute:order"
|
|
* @return {array.<string[]>} array containing 2 elements : attributes, orders
|
|
*/
|
|
module.exports = function formatSort(sortBy, defaults) {
|
|
var defaultInstructions = (defaults || []).map(function (sort) {
|
|
return sort.split(':');
|
|
});
|
|
|
|
return sortBy.reduce(
|
|
function preparePredicate(out, sort) {
|
|
var sortInstruction = sort.split(':');
|
|
|
|
var matchingDefault = find(
|
|
defaultInstructions,
|
|
function (defaultInstruction) {
|
|
return defaultInstruction[0] === sortInstruction[0];
|
|
}
|
|
);
|
|
|
|
if (sortInstruction.length > 1 || !matchingDefault) {
|
|
out[0].push(sortInstruction[0]);
|
|
out[1].push(sortInstruction[1]);
|
|
return out;
|
|
}
|
|
|
|
out[0].push(matchingDefault[0]);
|
|
out[1].push(matchingDefault[1]);
|
|
return out;
|
|
},
|
|
[[], []]
|
|
);
|
|
};
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 14853:
|
|
/***/ ((module) => {
|
|
|
|
"use strict";
|
|
|
|
|
|
function inherits(ctor, superCtor) {
|
|
ctor.prototype = Object.create(superCtor.prototype, {
|
|
constructor: {
|
|
value: ctor,
|
|
enumerable: false,
|
|
writable: true,
|
|
configurable: true,
|
|
},
|
|
});
|
|
}
|
|
|
|
module.exports = inherits;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 22686:
|
|
/***/ ((module) => {
|
|
|
|
"use strict";
|
|
|
|
|
|
function intersection(arr1, arr2) {
|
|
return arr1.filter(function (value, index) {
|
|
return (
|
|
arr2.indexOf(value) > -1 &&
|
|
arr1.indexOf(value) === index /* skips duplicates */
|
|
);
|
|
});
|
|
}
|
|
|
|
module.exports = intersection;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 60185:
|
|
/***/ ((module) => {
|
|
|
|
"use strict";
|
|
|
|
|
|
function clone(value) {
|
|
if (typeof 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'
|
|
) {
|
|
// eslint-disable-next-line no-continue
|
|
continue;
|
|
}
|
|
|
|
var sourceVal = source[key];
|
|
var targetVal = target[key];
|
|
|
|
if (typeof targetVal !== 'undefined' && typeof sourceVal === 'undefined') {
|
|
// eslint-disable-next-line no-continue
|
|
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;
|
|
}
|
|
|
|
module.exports = merge;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 90116:
|
|
/***/ ((module) => {
|
|
|
|
"use strict";
|
|
|
|
|
|
function objectHasKeys(obj) {
|
|
return obj && Object.keys(obj).length > 0;
|
|
}
|
|
|
|
module.exports = objectHasKeys;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 49803:
|
|
/***/ ((module) => {
|
|
|
|
"use strict";
|
|
|
|
|
|
// 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;
|
|
}
|
|
|
|
module.exports = _objectWithoutPropertiesLoose;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 42148:
|
|
/***/ ((module) => {
|
|
|
|
"use strict";
|
|
|
|
|
|
function compareAscending(value, other) {
|
|
if (value !== other) {
|
|
var valIsDefined = value !== undefined;
|
|
var valIsNull = value === null;
|
|
|
|
var othIsDefined = other !== undefined;
|
|
var othIsNull = other === null;
|
|
|
|
if (
|
|
(!othIsNull && value > other) ||
|
|
(valIsNull && othIsDefined) ||
|
|
!valIsDefined
|
|
) {
|
|
return 1;
|
|
}
|
|
if (
|
|
(!valIsNull && value < other) ||
|
|
(othIsNull && valIsDefined) ||
|
|
!othIsDefined
|
|
) {
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @param {Array<object>} collection object with keys in attributes
|
|
* @param {Array<string>} iteratees attributes
|
|
* @param {Array<string>} orders asc | desc
|
|
* @return {Array<object>} sorted collection
|
|
*/
|
|
function orderBy(collection, iteratees, orders) {
|
|
if (!Array.isArray(collection)) {
|
|
return [];
|
|
}
|
|
|
|
if (!Array.isArray(orders)) {
|
|
orders = [];
|
|
}
|
|
|
|
var result = collection.map(function (value, index) {
|
|
return {
|
|
criteria: iteratees.map(function (iteratee) {
|
|
return value[iteratee];
|
|
}),
|
|
index: index,
|
|
value: value,
|
|
};
|
|
});
|
|
|
|
result.sort(function comparer(object, other) {
|
|
var index = -1;
|
|
|
|
while (++index < object.criteria.length) {
|
|
var res = compareAscending(object.criteria[index], other.criteria[index]);
|
|
if (res) {
|
|
if (index >= orders.length) {
|
|
return res;
|
|
}
|
|
if (orders[index] === 'desc') {
|
|
return -res;
|
|
}
|
|
return res;
|
|
}
|
|
}
|
|
|
|
// This ensures a stable sort in V8 and other engines.
|
|
// See https://bugs.chromium.org/p/v8/issues/detail?id=90 for more details.
|
|
return object.index - other.index;
|
|
});
|
|
|
|
return result.map(function (res) {
|
|
return res.value;
|
|
});
|
|
}
|
|
|
|
module.exports = orderBy;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 28023:
|
|
/***/ ((module) => {
|
|
|
|
"use strict";
|
|
|
|
|
|
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.'
|
|
);
|
|
}
|
|
|
|
module.exports = valToNumber;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 96394:
|
|
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
|
|
var merge = __webpack_require__(60185);
|
|
|
|
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 {
|
|
params.facetFilters =
|
|
filteredFacetFilters.length > 0
|
|
? filteredFacetFilters
|
|
: undefined;
|
|
}
|
|
|
|
queries.push({ indexName: index, params: params });
|
|
});
|
|
}
|
|
});
|
|
|
|
return queries;
|
|
},
|
|
|
|
/**
|
|
* 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 (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 = {
|
|
facets: facets.indexOf('*') > -1 ? ['*'] : facets,
|
|
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 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 (
|
|
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 (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 (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 (state, facet, hierarchicalRootLevel) {
|
|
var facetFilters = [];
|
|
|
|
var facetsRefinements = state.facetsRefinements || {};
|
|
Object.keys(facetsRefinements)
|
|
.sort()
|
|
.forEach(function (facetName) {
|
|
var facetValues = facetsRefinements[facetName] || [];
|
|
facetValues.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.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 (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 (
|
|
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 (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
|
|
)
|
|
);
|
|
},
|
|
};
|
|
|
|
module.exports = requestBuilder;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 46801:
|
|
/***/ ((module) => {
|
|
|
|
"use strict";
|
|
|
|
|
|
module.exports = function isValidUserToken(userToken) {
|
|
if (userToken === null) {
|
|
return false;
|
|
}
|
|
return /^[a-zA-Z0-9_-]{1,64}$/.test(userToken);
|
|
};
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 24336:
|
|
/***/ ((module) => {
|
|
|
|
"use strict";
|
|
|
|
|
|
module.exports = '3.14.2';
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 70290:
|
|
/***/ (function(module) {
|
|
|
|
/*! algoliasearch-lite.umd.js | 4.20.0 | © Algolia, inc. | https://github.com/algolia/algoliasearch-client-javascript */
|
|
!function(e,t){ true?module.exports=t():0}(this,(function(){"use strict";function e(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function t(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function r(r){for(var n=1;n<arguments.length;n++){var o=null!=arguments[n]?arguments[n]:{};n%2?t(Object(o),!0).forEach((function(t){e(r,t,o[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(r,Object.getOwnPropertyDescriptors(o)):t(Object(o)).forEach((function(e){Object.defineProperty(r,e,Object.getOwnPropertyDescriptor(o,e))}))}return r}function n(e,t){if(null==e)return{};var r,n,o=function(e,t){if(null==e)return{};var r,n,o={},a=Object.keys(e);for(n=0;n<a.length;n++)r=a[n],t.indexOf(r)>=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n<a.length;n++)r=a[n],t.indexOf(r)>=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}function o(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){if(!(Symbol.iterator in Object(e)||"[object Arguments]"===Object.prototype.toString.call(e)))return;var r=[],n=!0,o=!1,a=void 0;try{for(var u,i=e[Symbol.iterator]();!(n=(u=i.next()).done)&&(r.push(u.value),!t||r.length!==t);n=!0);}catch(e){o=!0,a=e}finally{try{n||null==i.return||i.return()}finally{if(o)throw a}}return r}(e,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}()}function a(e){return function(e){if(Array.isArray(e)){for(var t=0,r=new Array(e.length);t<e.length;t++)r[t]=e[t];return r}}(e)||function(e){if(Symbol.iterator in Object(e)||"[object Arguments]"===Object.prototype.toString.call(e))return Array.from(e)}(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance")}()}function u(e){var t,r="algoliasearch-client-js-".concat(e.key),n=function(){return void 0===t&&(t=e.localStorage||window.localStorage),t},a=function(){return JSON.parse(n().getItem(r)||"{}")},u=function(e){n().setItem(r,JSON.stringify(e))},i=function(){var t=e.timeToLive?1e3*e.timeToLive:null,r=a(),n=Object.fromEntries(Object.entries(r).filter((function(e){return void 0!==o(e,2)[1].timestamp})));if(u(n),t){var i=Object.fromEntries(Object.entries(n).filter((function(e){var r=o(e,2)[1],n=(new Date).getTime();return!(r.timestamp+t<n)})));u(i)}};return{get:function(e,t){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{miss:function(){return Promise.resolve()}};return Promise.resolve().then((function(){i();var t=JSON.stringify(e);return a()[t]})).then((function(e){return Promise.all([e?e.value:t(),void 0!==e])})).then((function(e){var t=o(e,2),n=t[0],a=t[1];return Promise.all([n,a||r.miss(n)])})).then((function(e){return o(e,1)[0]}))},set:function(e,t){return Promise.resolve().then((function(){var o=a();return o[JSON.stringify(e)]={timestamp:(new Date).getTime(),value:t},n().setItem(r,JSON.stringify(o)),t}))},delete:function(e){return Promise.resolve().then((function(){var t=a();delete t[JSON.stringify(e)],n().setItem(r,JSON.stringify(t))}))},clear:function(){return Promise.resolve().then((function(){n().removeItem(r)}))}}}function i(e){var t=a(e.caches),r=t.shift();return void 0===r?{get:function(e,t){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{miss:function(){return Promise.resolve()}},n=t();return n.then((function(e){return Promise.all([e,r.miss(e)])})).then((function(e){return o(e,1)[0]}))},set:function(e,t){return Promise.resolve(t)},delete:function(e){return Promise.resolve()},clear:function(){return Promise.resolve()}}:{get:function(e,n){var o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{miss:function(){return Promise.resolve()}};return r.get(e,n,o).catch((function(){return i({caches:t}).get(e,n,o)}))},set:function(e,n){return r.set(e,n).catch((function(){return i({caches:t}).set(e,n)}))},delete:function(e){return r.delete(e).catch((function(){return i({caches:t}).delete(e)}))},clear:function(){return r.clear().catch((function(){return i({caches:t}).clear()}))}}}function s(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{serializable:!0},t={};return{get:function(r,n){var o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{miss:function(){return Promise.resolve()}},a=JSON.stringify(r);if(a in t)return Promise.resolve(e.serializable?JSON.parse(t[a]):t[a]);var u=n(),i=o&&o.miss||function(){return Promise.resolve()};return u.then((function(e){return i(e)})).then((function(){return u}))},set:function(r,n){return t[JSON.stringify(r)]=e.serializable?JSON.stringify(n):n,Promise.resolve(n)},delete:function(e){return delete t[JSON.stringify(e)],Promise.resolve()},clear:function(){return t={},Promise.resolve()}}}function c(e){for(var t=e.length-1;t>0;t--){var r=Math.floor(Math.random()*(t+1)),n=e[t];e[t]=e[r],e[r]=n}return e}function l(e,t){return t?(Object.keys(t).forEach((function(r){e[r]=t[r](e)})),e):e}function f(e){for(var t=arguments.length,r=new Array(t>1?t-1:0),n=1;n<t;n++)r[n-1]=arguments[n];var o=0;return e.replace(/%s/g,(function(){return encodeURIComponent(r[o++])}))}var m={WithinQueryParameters:0,WithinHeaders:1};function h(e,t){var r=e||{},n=r.data||{};return Object.keys(r).forEach((function(e){-1===["timeout","headers","queryParameters","data","cacheable"].indexOf(e)&&(n[e]=r[e])})),{data:Object.entries(n).length>0?n:void 0,timeout:r.timeout||t,headers:r.headers||{},queryParameters:r.queryParameters||{},cacheable:r.cacheable}}var d={Read:1,Write:2,Any:3},p=1,v=2,g=3;function y(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:p;return r(r({},e),{},{status:t,lastUpdate:Date.now()})}function b(e){return"string"==typeof e?{protocol:"https",url:e,accept:d.Any}:{protocol:e.protocol||"https",url:e.url,accept:e.accept||d.Any}}var O="GET",P="POST";function q(e,t){return Promise.all(t.map((function(t){return e.get(t,(function(){return Promise.resolve(y(t))}))}))).then((function(e){var r=e.filter((function(e){return function(e){return e.status===p||Date.now()-e.lastUpdate>12e4}(e)})),n=e.filter((function(e){return function(e){return e.status===g&&Date.now()-e.lastUpdate<=12e4}(e)})),o=[].concat(a(r),a(n));return{getTimeout:function(e,t){return(0===n.length&&0===e?1:n.length+3+e)*t},statelessHosts:o.length>0?o.map((function(e){return b(e)})):t}}))}function j(e,t,n,o){var u=[],i=function(e,t){if(e.method===O||void 0===e.data&&void 0===t.data)return;var n=Array.isArray(e.data)?e.data:r(r({},e.data),t.data);return JSON.stringify(n)}(n,o),s=function(e,t){var n=r(r({},e.headers),t.headers),o={};return Object.keys(n).forEach((function(e){var t=n[e];o[e.toLowerCase()]=t})),o}(e,o),c=n.method,l=n.method!==O?{}:r(r({},n.data),o.data),f=r(r(r({"x-algolia-agent":e.userAgent.value},e.queryParameters),l),o.queryParameters),m=0,h=function t(r,a){var l=r.pop();if(void 0===l)throw{name:"RetryError",message:"Unreachable hosts - your application id may be incorrect. If the error persists, contact support@algolia.com.",transporterStackTrace:A(u)};var h={data:i,headers:s,method:c,url:S(l,n.path,f),connectTimeout:a(m,e.timeouts.connect),responseTimeout:a(m,o.timeout)},d=function(e){var t={request:h,response:e,host:l,triesLeft:r.length};return u.push(t),t},p={onSuccess:function(e){return function(e){try{return JSON.parse(e.content)}catch(t){throw function(e,t){return{name:"DeserializationError",message:e,response:t}}(t.message,e)}}(e)},onRetry:function(n){var o=d(n);return n.isTimedOut&&m++,Promise.all([e.logger.info("Retryable failure",N(o)),e.hostsCache.set(l,y(l,n.isTimedOut?g:v))]).then((function(){return t(r,a)}))},onFail:function(e){throw d(e),function(e,t){var r=e.content,n=e.status,o=r;try{o=JSON.parse(r).message}catch(e){}return function(e,t,r){return{name:"ApiError",message:e,status:t,transporterStackTrace:r}}(o,n,t)}(e,A(u))}};return e.requester.send(h).then((function(e){return function(e,t){return function(e){var t=e.status;return e.isTimedOut||function(e){var t=e.isTimedOut,r=e.status;return!t&&0==~~r}(e)||2!=~~(t/100)&&4!=~~(t/100)}(e)?t.onRetry(e):2==~~(e.status/100)?t.onSuccess(e):t.onFail(e)}(e,p)}))};return q(e.hostsCache,t).then((function(e){return h(a(e.statelessHosts).reverse(),e.getTimeout)}))}function w(e){var t={value:"Algolia for JavaScript (".concat(e,")"),add:function(e){var r="; ".concat(e.segment).concat(void 0!==e.version?" (".concat(e.version,")"):"");return-1===t.value.indexOf(r)&&(t.value="".concat(t.value).concat(r)),t}};return t}function S(e,t,r){var n=T(r),o="".concat(e.protocol,"://").concat(e.url,"/").concat("/"===t.charAt(0)?t.substr(1):t);return n.length&&(o+="?".concat(n)),o}function T(e){return Object.keys(e).map((function(t){return f("%s=%s",t,(r=e[t],"[object Object]"===Object.prototype.toString.call(r)||"[object Array]"===Object.prototype.toString.call(r)?JSON.stringify(e[t]):e[t]));var r})).join("&")}function A(e){return e.map((function(e){return N(e)}))}function N(e){var t=e.request.headers["x-algolia-api-key"]?{"x-algolia-api-key":"*****"}:{};return r(r({},e),{},{request:r(r({},e.request),{},{headers:r(r({},e.request.headers),t)})})}var x=function(e){var t=e.appId,n=function(e,t,r){var n={"x-algolia-api-key":r,"x-algolia-application-id":t};return{headers:function(){return e===m.WithinHeaders?n:{}},queryParameters:function(){return e===m.WithinQueryParameters?n:{}}}}(void 0!==e.authMode?e.authMode:m.WithinHeaders,t,e.apiKey),a=function(e){var t=e.hostsCache,r=e.logger,n=e.requester,a=e.requestsCache,u=e.responsesCache,i=e.timeouts,s=e.userAgent,c=e.hosts,l=e.queryParameters,f={hostsCache:t,logger:r,requester:n,requestsCache:a,responsesCache:u,timeouts:i,userAgent:s,headers:e.headers,queryParameters:l,hosts:c.map((function(e){return b(e)})),read:function(e,t){var r=h(t,f.timeouts.read),n=function(){return j(f,f.hosts.filter((function(e){return 0!=(e.accept&d.Read)})),e,r)};if(!0!==(void 0!==r.cacheable?r.cacheable:e.cacheable))return n();var a={request:e,mappedRequestOptions:r,transporter:{queryParameters:f.queryParameters,headers:f.headers}};return f.responsesCache.get(a,(function(){return f.requestsCache.get(a,(function(){return f.requestsCache.set(a,n()).then((function(e){return Promise.all([f.requestsCache.delete(a),e])}),(function(e){return Promise.all([f.requestsCache.delete(a),Promise.reject(e)])})).then((function(e){var t=o(e,2);t[0];return t[1]}))}))}),{miss:function(e){return f.responsesCache.set(a,e)}})},write:function(e,t){return j(f,f.hosts.filter((function(e){return 0!=(e.accept&d.Write)})),e,h(t,f.timeouts.write))}};return f}(r(r({hosts:[{url:"".concat(t,"-dsn.algolia.net"),accept:d.Read},{url:"".concat(t,".algolia.net"),accept:d.Write}].concat(c([{url:"".concat(t,"-1.algolianet.com")},{url:"".concat(t,"-2.algolianet.com")},{url:"".concat(t,"-3.algolianet.com")}]))},e),{},{headers:r(r(r({},n.headers()),{"content-type":"application/x-www-form-urlencoded"}),e.headers),queryParameters:r(r({},n.queryParameters()),e.queryParameters)}));return l({transporter:a,appId:t,addAlgoliaAgent:function(e,t){a.userAgent.add({segment:e,version:t})},clearCache:function(){return Promise.all([a.requestsCache.clear(),a.responsesCache.clear()]).then((function(){}))}},e.methods)},C=function(e){return function(t,r){return t.method===O?e.transporter.read(t,r):e.transporter.write(t,r)}},E=function(e){return function(t){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n={transporter:e.transporter,appId:e.appId,indexName:t};return l(n,r.methods)}},J=function(e){return function(t,n){var o=t.map((function(e){return r(r({},e),{},{params:T(e.params||{})})}));return e.transporter.read({method:P,path:"1/indexes/*/queries",data:{requests:o},cacheable:!0},n)}},k=function(e){return function(t,o){return Promise.all(t.map((function(t){var a=t.params,u=a.facetName,i=a.facetQuery,s=n(a,["facetName","facetQuery"]);return E(e)(t.indexName,{methods:{searchForFacetValues:R}}).searchForFacetValues(u,i,r(r({},o),s))})))}},I=function(e){return function(t,r,n){return e.transporter.read({method:P,path:f("1/answers/%s/prediction",e.indexName),data:{query:t,queryLanguages:r},cacheable:!0},n)}},F=function(e){return function(t,r){return e.transporter.read({method:P,path:f("1/indexes/%s/query",e.indexName),data:{query:t},cacheable:!0},r)}},R=function(e){return function(t,r,n){return e.transporter.read({method:P,path:f("1/indexes/%s/facets/%s/query",e.indexName,t),data:{facetQuery:r},cacheable:!0},n)}},D=1,W=2,H=3;function Q(e,t,n){var o,a={appId:e,apiKey:t,timeouts:{connect:1,read:2,write:30},requester:{send:function(e){return new Promise((function(t){var r=new XMLHttpRequest;r.open(e.method,e.url,!0),Object.keys(e.headers).forEach((function(t){return r.setRequestHeader(t,e.headers[t])}));var n,o=function(e,n){return setTimeout((function(){r.abort(),t({status:0,content:n,isTimedOut:!0})}),1e3*e)},a=o(e.connectTimeout,"Connection timeout");r.onreadystatechange=function(){r.readyState>r.OPENED&&void 0===n&&(clearTimeout(a),n=o(e.responseTimeout,"Socket timeout"))},r.onerror=function(){0===r.status&&(clearTimeout(a),clearTimeout(n),t({content:r.responseText||"Network request failed",status:r.status,isTimedOut:!1}))},r.onload=function(){clearTimeout(a),clearTimeout(n),t({content:r.responseText,status:r.status,isTimedOut:!1})},r.send(e.data)}))}},logger:(o=H,{debug:function(e,t){return D>=o&&console.debug(e,t),Promise.resolve()},info:function(e,t){return W>=o&&console.info(e,t),Promise.resolve()},error:function(e,t){return console.error(e,t),Promise.resolve()}}),responsesCache:s(),requestsCache:s({serializable:!1}),hostsCache:i({caches:[u({key:"".concat("4.20.0","-").concat(e)}),s()]}),userAgent:w("4.20.0").add({segment:"Browser",version:"lite"}),authMode:m.WithinQueryParameters};return x(r(r(r({},a),n),{},{methods:{search:J,searchForFacetValues:k,multipleQueries:J,multipleSearchForFacetValues:k,customRequest:C,initIndex:function(e){return function(t){return E(e)(t,{methods:{search:F,searchForFacetValues:R,findAnswers:I}})}}}}))}return Q.version="4.20.0",Q}));
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 40304:
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
// ESM COMPAT FLAG
|
|
__webpack_require__.r(__webpack_exports__);
|
|
|
|
// EXPORTS
|
|
__webpack_require__.d(__webpack_exports__, {
|
|
"default": () => (/* binding */ SearchPage)
|
|
});
|
|
|
|
// EXTERNAL MODULE: ./node_modules/react/index.js
|
|
var react = __webpack_require__(67294);
|
|
// EXTERNAL MODULE: ./node_modules/clsx/dist/clsx.m.js
|
|
var clsx_m = __webpack_require__(86010);
|
|
// EXTERNAL MODULE: ./node_modules/algoliasearch-helper/index.js
|
|
var algoliasearch_helper = __webpack_require__(8131);
|
|
var algoliasearch_helper_default = /*#__PURE__*/__webpack_require__.n(algoliasearch_helper);
|
|
// EXTERNAL MODULE: ./node_modules/algoliasearch/dist/algoliasearch-lite.umd.js
|
|
var algoliasearch_lite_umd = __webpack_require__(70290);
|
|
var algoliasearch_lite_umd_default = /*#__PURE__*/__webpack_require__.n(algoliasearch_lite_umd);
|
|
// EXTERNAL MODULE: ./node_modules/@docusaurus/core/lib/client/exports/ExecutionEnvironment.js
|
|
var ExecutionEnvironment = __webpack_require__(58087);
|
|
// EXTERNAL MODULE: ./node_modules/@docusaurus/core/lib/client/exports/Head.js
|
|
var Head = __webpack_require__(71098);
|
|
// EXTERNAL MODULE: ./node_modules/@docusaurus/core/lib/client/exports/Link.js + 1 modules
|
|
var Link = __webpack_require__(31984);
|
|
// EXTERNAL MODULE: ./node_modules/@docusaurus/plugin-content-docs/lib/client/index.js + 1 modules
|
|
var client = __webpack_require__(88249);
|
|
// EXTERNAL MODULE: ./node_modules/@docusaurus/core/lib/client/exports/useDocusaurusContext.js
|
|
var useDocusaurusContext = __webpack_require__(8241);
|
|
;// CONCATENATED MODULE: ./node_modules/@docusaurus/theme-common/lib/utils/usePluralForm.js
|
|
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
// We want to ensurer a stable plural form order in all cases
|
|
// It is more convenient and natural to handle "small values" first
|
|
// See https://twitter.com/sebastienlorber/status/1366820663261077510
|
|
const OrderedPluralForms = [
|
|
'zero',
|
|
'one',
|
|
'two',
|
|
'few',
|
|
'many',
|
|
'other'
|
|
];
|
|
function sortPluralForms(pluralForms) {
|
|
return OrderedPluralForms.filter((pf)=>pluralForms.includes(pf));
|
|
}
|
|
// Hardcoded english/fallback implementation
|
|
const EnglishPluralForms = {
|
|
locale: 'en',
|
|
pluralForms: sortPluralForms([
|
|
'one',
|
|
'other'
|
|
]),
|
|
select: (count)=>count === 1 ? 'one' : 'other'
|
|
};
|
|
function createLocalePluralForms(locale) {
|
|
const pluralRules = new Intl.PluralRules(locale);
|
|
return {
|
|
locale,
|
|
pluralForms: sortPluralForms(pluralRules.resolvedOptions().pluralCategories),
|
|
select: (count)=>pluralRules.select(count)
|
|
};
|
|
}
|
|
/**
|
|
* Poor man's `PluralSelector` implementation, using an English fallback. We
|
|
* want a lightweight, future-proof and good-enough solution. We don't want a
|
|
* perfect and heavy solution.
|
|
*
|
|
* Docusaurus classic theme has only 2 deeply nested labels requiring complex
|
|
* plural rules. We don't want to use `Intl` + `PluralRules` polyfills + full
|
|
* ICU syntax (react-intl) just for that.
|
|
*
|
|
* Notes:
|
|
* - 2021: 92+% Browsers support `Intl.PluralRules`, and support will increase
|
|
* in the future
|
|
* - NodeJS >= 13 has full ICU support by default
|
|
* - In case of "mismatch" between SSR and Browser ICU support, React keeps
|
|
* working!
|
|
*/ function useLocalePluralForms() {
|
|
const { i18n: { currentLocale } } = (0,useDocusaurusContext/* default */.Z)();
|
|
return (0,react.useMemo)(()=>{
|
|
try {
|
|
return createLocalePluralForms(currentLocale);
|
|
} catch (err) {
|
|
console.error(`Failed to use Intl.PluralRules for locale "${currentLocale}".
|
|
Docusaurus will fallback to the default (English) implementation.
|
|
Error: ${err.message}
|
|
`);
|
|
return EnglishPluralForms;
|
|
}
|
|
}, [
|
|
currentLocale
|
|
]);
|
|
}
|
|
function selectPluralMessage(pluralMessages, count, localePluralForms) {
|
|
const separator = '|';
|
|
const parts = pluralMessages.split(separator);
|
|
if (parts.length === 1) {
|
|
return parts[0];
|
|
}
|
|
if (parts.length > localePluralForms.pluralForms.length) {
|
|
console.error(`For locale=${localePluralForms.locale}, a maximum of ${localePluralForms.pluralForms.length} plural forms are expected (${localePluralForms.pluralForms.join(',')}), but the message contains ${parts.length}: ${pluralMessages}`);
|
|
}
|
|
const pluralForm = localePluralForms.select(count);
|
|
const pluralFormIndex = localePluralForms.pluralForms.indexOf(pluralForm);
|
|
// In case of not enough plural form messages, we take the last one (other)
|
|
// instead of returning undefined
|
|
return parts[Math.min(pluralFormIndex, parts.length - 1)];
|
|
}
|
|
/**
|
|
* Reads the current locale and returns an interface very similar to
|
|
* `Intl.PluralRules`.
|
|
*/ function usePluralForm() {
|
|
const localePluralForm = useLocalePluralForms();
|
|
return {
|
|
selectMessage: (count, pluralMessages)=>selectPluralMessage(pluralMessages, count, localePluralForm)
|
|
};
|
|
} //# sourceMappingURL=usePluralForm.js.map
|
|
|
|
// EXTERNAL MODULE: ./node_modules/@docusaurus/theme-common/lib/hooks/useSearchPage.js
|
|
var useSearchPage = __webpack_require__(80958);
|
|
// EXTERNAL MODULE: ./node_modules/@docusaurus/theme-common/lib/utils/reactUtils.js
|
|
var reactUtils = __webpack_require__(37806);
|
|
// EXTERNAL MODULE: ./node_modules/@docusaurus/theme-common/lib/utils/metadataUtils.js + 1 modules
|
|
var metadataUtils = __webpack_require__(89712);
|
|
// EXTERNAL MODULE: ./node_modules/@docusaurus/theme-common/lib/utils/generalUtils.js
|
|
var generalUtils = __webpack_require__(54995);
|
|
// EXTERNAL MODULE: ./node_modules/@docusaurus/core/lib/client/exports/Translate.js + 1 modules
|
|
var Translate = __webpack_require__(92210);
|
|
// EXTERNAL MODULE: ./node_modules/@docusaurus/theme-search-algolia/lib/client/useAlgoliaThemeConfig.js
|
|
var useAlgoliaThemeConfig = __webpack_require__(49704);
|
|
// EXTERNAL MODULE: ./node_modules/@docusaurus/theme-search-algolia/lib/client/useSearchResultUrlProcessor.js
|
|
var useSearchResultUrlProcessor = __webpack_require__(30888);
|
|
// EXTERNAL MODULE: ./node_modules/@docusaurus/theme-classic/lib/theme/Layout/index.js + 71 modules
|
|
var Layout = __webpack_require__(33737);
|
|
;// CONCATENATED MODULE: ./node_modules/@docusaurus/theme-search-algolia/lib/theme/SearchPage/styles.module.css
|
|
// extracted by mini-css-extract-plugin
|
|
/* harmony default export */ const styles_module = ({"searchQueryInput":"searchQueryInput_u2C7","searchVersionInput":"searchVersionInput_m0Ui","searchResultsColumn":"searchResultsColumn_JPFH","algoliaLogo":"algoliaLogo_rT1R","algoliaLogoPathFill":"algoliaLogoPathFill_WdUC","searchResultItem":"searchResultItem_Tv2o","searchResultItemHeading":"searchResultItemHeading_KbCB","searchResultItemPath":"searchResultItemPath_lhe1","searchResultItemSummary":"searchResultItemSummary_AEaO","searchQueryColumn":"searchQueryColumn_RTkw","searchVersionColumn":"searchVersionColumn_ypXd","searchLogoColumn":"searchLogoColumn_rJIA","loadingSpinner":"loadingSpinner_XVxU","loading-spin":"loading-spin_vzvp","loader":"loader_vvXV"});
|
|
;// CONCATENATED MODULE: ./node_modules/@docusaurus/theme-search-algolia/lib/theme/SearchPage/index.js
|
|
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/ /* eslint-disable jsx-a11y/no-autofocus */ 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;
|
|
}
|
|
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;
|
|
}
|
|
function ownKeys(object, enumerableOnly) {
|
|
var keys = Object.keys(object);
|
|
if (Object.getOwnPropertySymbols) {
|
|
var symbols = Object.getOwnPropertySymbols(object);
|
|
if (enumerableOnly) {
|
|
symbols = symbols.filter(function(sym) {
|
|
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
|
|
});
|
|
}
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Very simple pluralization: probably good enough for now
|
|
function useDocumentsFoundPlural() {
|
|
const { selectMessage } = usePluralForm();
|
|
return (count)=>selectMessage(count, (0,Translate/* translate */.I)({
|
|
id: 'theme.SearchPage.documentsFound.plurals',
|
|
description: 'Pluralized label for "{count} documents found". Use as much plural forms (separated by "|") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)',
|
|
message: 'One document found|{count} documents found'
|
|
}, {
|
|
count
|
|
}));
|
|
}
|
|
function useDocsSearchVersionsHelpers() {
|
|
const allDocsData = (0,client/* useAllDocsData */._r)();
|
|
// State of the version select menus / algolia facet filters
|
|
// docsPluginId -> versionName map
|
|
const [searchVersions, setSearchVersions] = (0,react.useState)(()=>Object.entries(allDocsData).reduce((acc, [pluginId, pluginData])=>_object_spread_props(_object_spread({}, acc), {
|
|
[pluginId]: pluginData.versions[0].name
|
|
}), {}));
|
|
// Set the value of a single select menu
|
|
const setSearchVersion = (pluginId, searchVersion)=>setSearchVersions((s)=>_object_spread_props(_object_spread({}, s), {
|
|
[pluginId]: searchVersion
|
|
}));
|
|
const versioningEnabled = Object.values(allDocsData).some((docsData)=>docsData.versions.length > 1);
|
|
return {
|
|
allDocsData,
|
|
versioningEnabled,
|
|
searchVersions,
|
|
setSearchVersion
|
|
};
|
|
}
|
|
// We want to display one select per versioned docs plugin instance
|
|
function SearchVersionSelectList({ docsSearchVersionsHelpers }) {
|
|
const versionedPluginEntries = Object.entries(docsSearchVersionsHelpers.allDocsData)// Do not show a version select for unversioned docs plugin instances
|
|
.filter(([, docsData])=>docsData.versions.length > 1);
|
|
return /*#__PURE__*/ react.createElement("div", {
|
|
className: (0,clsx_m/* default */.Z)('col', 'col--3', 'padding-left--none', styles_module.searchVersionColumn)
|
|
}, versionedPluginEntries.map(([pluginId, docsData])=>{
|
|
const labelPrefix = versionedPluginEntries.length > 1 ? `${pluginId}: ` : '';
|
|
return /*#__PURE__*/ react.createElement("select", {
|
|
key: pluginId,
|
|
onChange: (e)=>docsSearchVersionsHelpers.setSearchVersion(pluginId, e.target.value),
|
|
defaultValue: docsSearchVersionsHelpers.searchVersions[pluginId],
|
|
className: styles_module.searchVersionInput
|
|
}, docsData.versions.map((version, i)=>/*#__PURE__*/ react.createElement("option", {
|
|
key: i,
|
|
label: `${labelPrefix}${version.label}`,
|
|
value: version.name
|
|
})));
|
|
}));
|
|
}
|
|
function SearchPageContent() {
|
|
const { i18n: { currentLocale } } = (0,useDocusaurusContext/* default */.Z)();
|
|
const { algolia: { appId, apiKey, indexName } } = (0,useAlgoliaThemeConfig/* useAlgoliaThemeConfig */.L)();
|
|
const processSearchResultUrl = (0,useSearchResultUrlProcessor/* useSearchResultUrlProcessor */.l)();
|
|
const documentsFoundPlural = useDocumentsFoundPlural();
|
|
const docsSearchVersionsHelpers = useDocsSearchVersionsHelpers();
|
|
const [searchQuery, setSearchQuery] = (0,useSearchPage/* useSearchQueryString */.K)();
|
|
const initialSearchResultState = {
|
|
items: [],
|
|
query: null,
|
|
totalResults: null,
|
|
totalPages: null,
|
|
lastPage: null,
|
|
hasMore: null,
|
|
loading: null
|
|
};
|
|
const [searchResultState, searchResultStateDispatcher] = (0,react.useReducer)((prevState, data)=>{
|
|
switch(data.type){
|
|
case 'reset':
|
|
{
|
|
return initialSearchResultState;
|
|
}
|
|
case 'loading':
|
|
{
|
|
return _object_spread_props(_object_spread({}, prevState), {
|
|
loading: true
|
|
});
|
|
}
|
|
case 'update':
|
|
{
|
|
if (searchQuery !== data.value.query) {
|
|
return prevState;
|
|
}
|
|
return _object_spread_props(_object_spread({}, data.value), {
|
|
items: data.value.lastPage === 0 ? data.value.items : prevState.items.concat(data.value.items)
|
|
});
|
|
}
|
|
case 'advance':
|
|
{
|
|
const hasMore = prevState.totalPages > prevState.lastPage + 1;
|
|
return _object_spread_props(_object_spread({}, prevState), {
|
|
lastPage: hasMore ? prevState.lastPage + 1 : prevState.lastPage,
|
|
hasMore
|
|
});
|
|
}
|
|
default:
|
|
return prevState;
|
|
}
|
|
}, initialSearchResultState);
|
|
const algoliaClient = algoliasearch_lite_umd_default()(appId, apiKey);
|
|
const algoliaHelper = algoliasearch_helper_default()(algoliaClient, indexName, {
|
|
hitsPerPage: 15,
|
|
advancedSyntax: true,
|
|
disjunctiveFacets: [
|
|
'language',
|
|
'docusaurus_tag'
|
|
]
|
|
});
|
|
algoliaHelper.on('result', ({ results: { query, hits, page, nbHits, nbPages } })=>{
|
|
if (query === '' || !Array.isArray(hits)) {
|
|
searchResultStateDispatcher({
|
|
type: 'reset'
|
|
});
|
|
return;
|
|
}
|
|
const sanitizeValue = (value)=>value.replace(/algolia-docsearch-suggestion--highlight/g, 'search-result-match');
|
|
const items = hits.map(({ url, _highlightResult: { hierarchy }, _snippetResult: snippet = {} })=>{
|
|
const titles = Object.keys(hierarchy).map((key)=>sanitizeValue(hierarchy[key].value));
|
|
return {
|
|
title: titles.pop(),
|
|
url: processSearchResultUrl(url),
|
|
summary: snippet.content ? `${sanitizeValue(snippet.content.value)}...` : '',
|
|
breadcrumbs: titles
|
|
};
|
|
});
|
|
searchResultStateDispatcher({
|
|
type: 'update',
|
|
value: {
|
|
items,
|
|
query,
|
|
totalResults: nbHits,
|
|
totalPages: nbPages,
|
|
lastPage: page,
|
|
hasMore: nbPages > page + 1,
|
|
loading: false
|
|
}
|
|
});
|
|
});
|
|
const [loaderRef, setLoaderRef] = (0,react.useState)(null);
|
|
const prevY = (0,react.useRef)(0);
|
|
const observer = (0,react.useRef)(ExecutionEnvironment["default"].canUseIntersectionObserver && new IntersectionObserver((entries)=>{
|
|
const { isIntersecting, boundingClientRect: { y: currentY } } = entries[0];
|
|
if (isIntersecting && prevY.current > currentY) {
|
|
searchResultStateDispatcher({
|
|
type: 'advance'
|
|
});
|
|
}
|
|
prevY.current = currentY;
|
|
}, {
|
|
threshold: 1
|
|
}));
|
|
const getTitle = ()=>searchQuery ? (0,Translate/* translate */.I)({
|
|
id: 'theme.SearchPage.existingResultsTitle',
|
|
message: 'Search results for "{query}"',
|
|
description: 'The search page title for non-empty query'
|
|
}, {
|
|
query: searchQuery
|
|
}) : (0,Translate/* translate */.I)({
|
|
id: 'theme.SearchPage.emptyResultsTitle',
|
|
message: 'Search the documentation',
|
|
description: 'The search page title for empty query'
|
|
});
|
|
const makeSearch = (0,reactUtils/* useEvent */.zX)((page = 0)=>{
|
|
algoliaHelper.addDisjunctiveFacetRefinement('docusaurus_tag', 'default');
|
|
algoliaHelper.addDisjunctiveFacetRefinement('language', currentLocale);
|
|
Object.entries(docsSearchVersionsHelpers.searchVersions).forEach(([pluginId, searchVersion])=>{
|
|
algoliaHelper.addDisjunctiveFacetRefinement('docusaurus_tag', `docs-${pluginId}-${searchVersion}`);
|
|
});
|
|
algoliaHelper.setQuery(searchQuery).setPage(page).search();
|
|
});
|
|
(0,react.useEffect)(()=>{
|
|
if (!loaderRef) {
|
|
return undefined;
|
|
}
|
|
const currentObserver = observer.current;
|
|
if (currentObserver) {
|
|
currentObserver.observe(loaderRef);
|
|
return ()=>currentObserver.unobserve(loaderRef);
|
|
}
|
|
return ()=>true;
|
|
}, [
|
|
loaderRef
|
|
]);
|
|
(0,react.useEffect)(()=>{
|
|
searchResultStateDispatcher({
|
|
type: 'reset'
|
|
});
|
|
if (searchQuery) {
|
|
searchResultStateDispatcher({
|
|
type: 'loading'
|
|
});
|
|
setTimeout(()=>{
|
|
makeSearch();
|
|
}, 300);
|
|
}
|
|
}, [
|
|
searchQuery,
|
|
docsSearchVersionsHelpers.searchVersions,
|
|
makeSearch
|
|
]);
|
|
(0,react.useEffect)(()=>{
|
|
if (!searchResultState.lastPage || searchResultState.lastPage === 0) {
|
|
return;
|
|
}
|
|
makeSearch(searchResultState.lastPage);
|
|
}, [
|
|
makeSearch,
|
|
searchResultState.lastPage
|
|
]);
|
|
return /*#__PURE__*/ react.createElement(Layout/* default */.Z, null, /*#__PURE__*/ react.createElement(Head/* default */.Z, null, /*#__PURE__*/ react.createElement("title", null, (0,generalUtils/* useTitleFormatter */.p)(getTitle())), /*#__PURE__*/ react.createElement("meta", {
|
|
property: "robots",
|
|
content: "noindex, follow"
|
|
})), /*#__PURE__*/ react.createElement("div", {
|
|
className: "container margin-vert--lg"
|
|
}, /*#__PURE__*/ react.createElement("h1", null, getTitle()), /*#__PURE__*/ react.createElement("form", {
|
|
className: "row",
|
|
onSubmit: (e)=>e.preventDefault()
|
|
}, /*#__PURE__*/ react.createElement("div", {
|
|
className: (0,clsx_m/* default */.Z)('col', styles_module.searchQueryColumn, {
|
|
'col--9': docsSearchVersionsHelpers.versioningEnabled,
|
|
'col--12': !docsSearchVersionsHelpers.versioningEnabled
|
|
})
|
|
}, /*#__PURE__*/ react.createElement("input", {
|
|
type: "search",
|
|
name: "q",
|
|
className: styles_module.searchQueryInput,
|
|
placeholder: (0,Translate/* translate */.I)({
|
|
id: 'theme.SearchPage.inputPlaceholder',
|
|
message: 'Type your search here',
|
|
description: 'The placeholder for search page input'
|
|
}),
|
|
"aria-label": (0,Translate/* translate */.I)({
|
|
id: 'theme.SearchPage.inputLabel',
|
|
message: 'Search',
|
|
description: 'The ARIA label for search page input'
|
|
}),
|
|
onChange: (e)=>setSearchQuery(e.target.value),
|
|
value: searchQuery,
|
|
autoComplete: "off",
|
|
autoFocus: true
|
|
})), docsSearchVersionsHelpers.versioningEnabled && /*#__PURE__*/ react.createElement(SearchVersionSelectList, {
|
|
docsSearchVersionsHelpers: docsSearchVersionsHelpers
|
|
})), /*#__PURE__*/ react.createElement("div", {
|
|
className: "row"
|
|
}, /*#__PURE__*/ react.createElement("div", {
|
|
className: (0,clsx_m/* default */.Z)('col', 'col--8', styles_module.searchResultsColumn)
|
|
}, !!searchResultState.totalResults && documentsFoundPlural(searchResultState.totalResults)), /*#__PURE__*/ react.createElement("div", {
|
|
className: (0,clsx_m/* default */.Z)('col', 'col--4', 'text--right', styles_module.searchLogoColumn)
|
|
}, /*#__PURE__*/ react.createElement("a", {
|
|
target: "_blank",
|
|
rel: "noopener noreferrer",
|
|
href: "https://www.algolia.com/",
|
|
"aria-label": (0,Translate/* translate */.I)({
|
|
id: 'theme.SearchPage.algoliaLabel',
|
|
message: 'Search by Algolia',
|
|
description: 'The ARIA label for Algolia mention'
|
|
})
|
|
}, /*#__PURE__*/ react.createElement("svg", {
|
|
viewBox: "0 0 168 24",
|
|
className: styles_module.algoliaLogo
|
|
}, /*#__PURE__*/ react.createElement("g", {
|
|
fill: "none"
|
|
}, /*#__PURE__*/ react.createElement("path", {
|
|
className: styles_module.algoliaLogoPathFill,
|
|
d: "M120.925 18.804c-4.386.02-4.386-3.54-4.386-4.106l-.007-13.336 2.675-.424v13.254c0 .322 0 2.358 1.718 2.364v2.248zm-10.846-2.18c.821 0 1.43-.047 1.855-.129v-2.719a6.334 6.334 0 0 0-1.574-.199 5.7 5.7 0 0 0-.897.069 2.699 2.699 0 0 0-.814.24c-.24.116-.439.28-.582.491-.15.212-.219.335-.219.656 0 .628.219.991.616 1.23s.938.362 1.615.362zm-.233-9.7c.883 0 1.629.109 2.231.328.602.218 1.088.525 1.444.915.363.396.609.922.76 1.483.157.56.232 1.175.232 1.85v6.874a32.5 32.5 0 0 1-1.868.314c-.834.123-1.772.185-2.813.185-.69 0-1.327-.069-1.895-.198a4.001 4.001 0 0 1-1.471-.636 3.085 3.085 0 0 1-.951-1.134c-.226-.465-.343-1.12-.343-1.803 0-.656.13-1.073.384-1.525a3.24 3.24 0 0 1 1.047-1.106c.445-.287.95-.492 1.532-.615a8.8 8.8 0 0 1 1.82-.185 8.404 8.404 0 0 1 1.972.24v-.438c0-.307-.035-.6-.11-.874a1.88 1.88 0 0 0-.384-.73 1.784 1.784 0 0 0-.724-.493 3.164 3.164 0 0 0-1.143-.205c-.616 0-1.177.075-1.69.164a7.735 7.735 0 0 0-1.26.307l-.321-2.192c.335-.117.834-.233 1.478-.349a10.98 10.98 0 0 1 2.073-.178zm52.842 9.626c.822 0 1.43-.048 1.854-.13V13.7a6.347 6.347 0 0 0-1.574-.199c-.294 0-.595.021-.896.069a2.7 2.7 0 0 0-.814.24 1.46 1.46 0 0 0-.582.491c-.15.212-.218.335-.218.656 0 .628.218.991.615 1.23.404.245.938.362 1.615.362zm-.226-9.694c.883 0 1.629.108 2.231.327.602.219 1.088.526 1.444.915.355.39.609.923.759 1.483a6.8 6.8 0 0 1 .233 1.852v6.873c-.41.088-1.034.19-1.868.314-.834.123-1.772.184-2.813.184-.69 0-1.327-.068-1.895-.198a4.001 4.001 0 0 1-1.471-.635 3.085 3.085 0 0 1-.951-1.134c-.226-.465-.343-1.12-.343-1.804 0-.656.13-1.073.384-1.524.26-.45.608-.82 1.047-1.107.445-.286.95-.491 1.532-.614a8.803 8.803 0 0 1 2.751-.13c.329.034.671.096 1.04.185v-.437a3.3 3.3 0 0 0-.109-.875 1.873 1.873 0 0 0-.384-.731 1.784 1.784 0 0 0-.724-.492 3.165 3.165 0 0 0-1.143-.205c-.616 0-1.177.075-1.69.164a7.75 7.75 0 0 0-1.26.307l-.321-2.193c.335-.116.834-.232 1.478-.348a11.633 11.633 0 0 1 2.073-.177zm-8.034-1.271a1.626 1.626 0 0 1-1.628-1.62c0-.895.725-1.62 1.628-1.62.904 0 1.63.725 1.63 1.62 0 .895-.733 1.62-1.63 1.62zm1.348 13.22h-2.689V7.27l2.69-.423v11.956zm-4.714 0c-4.386.02-4.386-3.54-4.386-4.107l-.008-13.336 2.676-.424v13.254c0 .322 0 2.358 1.718 2.364v2.248zm-8.698-5.903c0-1.156-.253-2.119-.746-2.788-.493-.677-1.183-1.01-2.067-1.01-.882 0-1.574.333-2.065 1.01-.493.676-.733 1.632-.733 2.788 0 1.168.246 1.953.74 2.63.492.683 1.183 1.018 2.066 1.018.882 0 1.574-.342 2.067-1.019.492-.683.738-1.46.738-2.63zm2.737-.007c0 .902-.13 1.584-.397 2.33a5.52 5.52 0 0 1-1.128 1.906 4.986 4.986 0 0 1-1.752 1.223c-.685.286-1.739.45-2.265.45-.528-.006-1.574-.157-2.252-.45a5.096 5.096 0 0 1-1.744-1.223c-.487-.527-.863-1.162-1.137-1.906a6.345 6.345 0 0 1-.41-2.33c0-.902.123-1.77.397-2.508a5.554 5.554 0 0 1 1.15-1.892 5.133 5.133 0 0 1 1.75-1.216c.679-.287 1.425-.423 2.232-.423.808 0 1.553.142 2.237.423a4.88 4.88 0 0 1 1.753 1.216 5.644 5.644 0 0 1 1.135 1.892c.287.738.431 1.606.431 2.508zm-20.138 0c0 1.12.246 2.363.738 2.882.493.52 1.13.78 1.91.78.424 0 .828-.062 1.204-.178.377-.116.677-.253.917-.417V9.33a10.476 10.476 0 0 0-1.766-.226c-.971-.028-1.71.37-2.23 1.004-.513.636-.773 1.75-.773 2.788zm7.438 5.274c0 1.824-.466 3.156-1.404 4.004-.936.846-2.367 1.27-4.296 1.27-.705 0-2.17-.137-3.34-.396l.431-2.118c.98.205 2.272.26 2.95.26 1.074 0 1.84-.219 2.299-.656.459-.437.684-1.086.684-1.948v-.437a8.07 8.07 0 0 1-1.047.397c-.43.13-.93.198-1.492.198-.739 0-1.41-.116-2.018-.349a4.206 4.206 0 0 1-1.567-1.025c-.431-.45-.774-1.017-1.013-1.694-.24-.677-.363-1.885-.363-2.773 0-.834.13-1.88.384-2.577.26-.696.629-1.298 1.129-1.796.493-.498 1.095-.881 1.8-1.162a6.605 6.605 0 0 1 2.428-.457c.87 0 1.67.109 2.45.24.78.129 1.444.265 1.985.415V18.17zM6.972 6.677v1.627c-.712-.446-1.52-.67-2.425-.67-.585 0-1.045.13-1.38.391a1.24 1.24 0 0 0-.502 1.03c0 .425.164.765.494 1.02.33.256.835.532 1.516.83.447.192.795.356 1.045.495.25.138.537.332.862.582.324.25.563.548.718.894.154.345.23.741.23 1.188 0 .947-.334 1.691-1.004 2.234-.67.542-1.537.814-2.601.814-1.18 0-2.16-.229-2.936-.686v-1.708c.84.628 1.814.942 2.92.942.585 0 1.048-.136 1.388-.407.34-.271.51-.646.51-1.125 0-.287-.1-.55-.302-.79-.203-.24-.42-.42-.655-.542-.234-.123-.585-.29-1.053-.503a61.27 61.27 0 0 1-.582-.271 13.67 13.67 0 0 1-.55-.287 4.275 4.275 0 0 1-.567-.351 6.92 6.92 0 0 1-.455-.4c-.18-.17-.31-.34-.39-.51-.08-.17-.155-.37-.224-.598a2.553 2.553 0 0 1-.104-.742c0-.915.333-1.638.998-2.17.664-.532 1.523-.798 2.576-.798.968 0 1.793.17 2.473.51zm7.468 5.696v-.287c-.022-.607-.187-1.088-.495-1.444-.309-.357-.75-.535-1.324-.535-.532 0-.99.194-1.373.583-.382.388-.622.949-.717 1.683h3.909zm1.005 2.792v1.404c-.596.34-1.383.51-2.362.51-1.255 0-2.255-.377-3-1.132-.744-.755-1.116-1.744-1.116-2.968 0-1.297.34-2.316 1.021-3.055.68-.74 1.548-1.11 2.6-1.11 1.033 0 1.852.323 2.458.966.606.644.91 1.572.91 2.784 0 .33-.033.676-.096 1.038h-5.314c.107.702.405 1.239.894 1.611.49.372 1.106.558 1.85.558.862 0 1.58-.202 2.155-.606zm6.605-1.77h-1.212c-.596 0-1.045.116-1.349.35-.303.234-.454.532-.454.894 0 .372.117.664.35.877.235.213.575.32 1.022.32.51 0 .912-.142 1.204-.424.293-.281.44-.651.44-1.108v-.91zm-4.068-2.554V9.325c.627-.361 1.457-.542 2.489-.542 2.116 0 3.175 1.026 3.175 3.08V17h-1.548v-.957c-.415.68-1.143 1.02-2.186 1.02-.766 0-1.38-.22-1.843-.661-.462-.442-.694-1.003-.694-1.684 0-.776.293-1.38.878-1.81.585-.431 1.404-.647 2.457-.647h1.34V11.8c0-.554-.133-.971-.399-1.253-.266-.282-.707-.423-1.324-.423a4.07 4.07 0 0 0-2.345.718zm9.333-1.93v1.42c.394-1 1.101-1.5 2.123-1.5.148 0 .313.016.494.048v1.531a1.885 1.885 0 0 0-.75-.143c-.542 0-.989.24-1.34.718-.351.479-.527 1.048-.527 1.707V17h-1.563V8.91h1.563zm5.01 4.084c.022.82.272 1.492.75 2.019.479.526 1.15.79 2.01.79.639 0 1.235-.176 1.788-.527v1.404c-.521.319-1.186.479-1.995.479-1.265 0-2.276-.4-3.031-1.197-.755-.798-1.133-1.792-1.133-2.984 0-1.16.38-2.151 1.14-2.975.761-.825 1.79-1.237 3.088-1.237.702 0 1.346.149 1.93.447v1.436a3.242 3.242 0 0 0-1.77-.495c-.84 0-1.513.266-2.019.798-.505.532-.758 1.213-.758 2.042zM40.24 5.72v4.579c.458-1 1.293-1.5 2.505-1.5.787 0 1.42.245 1.899.734.479.49.718 1.17.718 2.042V17h-1.564v-5.106c0-.553-.14-.98-.422-1.284-.282-.303-.652-.455-1.11-.455-.531 0-1.002.202-1.411.606-.41.405-.615 1.022-.615 1.851V17h-1.563V5.72h1.563zm14.966 10.02c.596 0 1.096-.253 1.5-.758.404-.506.606-1.157.606-1.955 0-.915-.202-1.62-.606-2.114-.404-.495-.92-.742-1.548-.742-.553 0-1.05.224-1.491.67-.442.447-.662 1.133-.662 2.058 0 .958.212 1.67.638 2.138.425.469.946.703 1.563.703zM53.004 5.72v4.42c.574-.894 1.388-1.341 2.44-1.341 1.022 0 1.857.383 2.506 1.149.649.766.973 1.781.973 3.047 0 1.138-.309 2.109-.925 2.912-.617.803-1.463 1.205-2.537 1.205-1.075 0-1.894-.447-2.457-1.34V17h-1.58V5.72h1.58zm9.908 11.104l-3.223-7.913h1.739l1.005 2.632 1.26 3.415c.096-.32.48-1.458 1.15-3.415l.909-2.632h1.66l-2.92 7.866c-.777 2.074-1.963 3.11-3.559 3.11a2.92 2.92 0 0 1-.734-.079v-1.34c.17.042.351.064.543.064 1.032 0 1.755-.57 2.17-1.708z"
|
|
}), /*#__PURE__*/ react.createElement("path", {
|
|
fill: "#5468FF",
|
|
d: "M78.988.938h16.594a2.968 2.968 0 0 1 2.966 2.966V20.5a2.967 2.967 0 0 1-2.966 2.964H78.988a2.967 2.967 0 0 1-2.966-2.964V3.897A2.961 2.961 0 0 1 78.988.938z"
|
|
}), /*#__PURE__*/ react.createElement("path", {
|
|
fill: "white",
|
|
d: "M89.632 5.967v-.772a.978.978 0 0 0-.978-.977h-2.28a.978.978 0 0 0-.978.977v.793c0 .088.082.15.171.13a7.127 7.127 0 0 1 1.984-.28c.65 0 1.295.088 1.917.259.082.02.164-.04.164-.13m-6.248 1.01l-.39-.389a.977.977 0 0 0-1.382 0l-.465.465a.973.973 0 0 0 0 1.38l.383.383c.062.061.15.047.205-.014.226-.307.472-.601.746-.874.281-.28.568-.526.883-.751.068-.042.075-.137.02-.2m4.16 2.453v3.341c0 .096.104.165.192.117l2.97-1.537c.068-.034.089-.117.055-.184a3.695 3.695 0 0 0-3.08-1.866c-.068 0-.136.054-.136.13m0 8.048a4.489 4.489 0 0 1-4.49-4.482 4.488 4.488 0 0 1 4.49-4.482 4.488 4.488 0 0 1 4.489 4.482 4.484 4.484 0 0 1-4.49 4.482m0-10.85a6.363 6.363 0 1 0 0 12.729 6.37 6.37 0 0 0 6.372-6.368 6.358 6.358 0 0 0-6.371-6.36"
|
|
})))))), searchResultState.items.length > 0 ? /*#__PURE__*/ react.createElement("main", null, searchResultState.items.map(({ title, url, summary, breadcrumbs }, i)=>/*#__PURE__*/ react.createElement("article", {
|
|
key: i,
|
|
className: styles_module.searchResultItem
|
|
}, /*#__PURE__*/ react.createElement("h2", {
|
|
className: styles_module.searchResultItemHeading
|
|
}, /*#__PURE__*/ react.createElement(Link/* default */.Z, {
|
|
to: url,
|
|
dangerouslySetInnerHTML: {
|
|
__html: title
|
|
}
|
|
})), breadcrumbs.length > 0 && /*#__PURE__*/ react.createElement("nav", {
|
|
"aria-label": "breadcrumbs"
|
|
}, /*#__PURE__*/ react.createElement("ul", {
|
|
className: (0,clsx_m/* default */.Z)('breadcrumbs', styles_module.searchResultItemPath)
|
|
}, breadcrumbs.map((html, index)=>/*#__PURE__*/ react.createElement("li", {
|
|
key: index,
|
|
className: "breadcrumbs__item",
|
|
// Developer provided the HTML, so assume it's safe.
|
|
// eslint-disable-next-line react/no-danger
|
|
dangerouslySetInnerHTML: {
|
|
__html: html
|
|
}
|
|
})))), summary && /*#__PURE__*/ react.createElement("p", {
|
|
className: styles_module.searchResultItemSummary,
|
|
// Developer provided the HTML, so assume it's safe.
|
|
// eslint-disable-next-line react/no-danger
|
|
dangerouslySetInnerHTML: {
|
|
__html: summary
|
|
}
|
|
})))) : [
|
|
searchQuery && !searchResultState.loading && /*#__PURE__*/ react.createElement("p", {
|
|
key: "no-results"
|
|
}, /*#__PURE__*/ react.createElement(Translate/* default */.Z, {
|
|
id: "theme.SearchPage.noResultsText",
|
|
description: "The paragraph for empty search result"
|
|
}, "No results were found")),
|
|
!!searchResultState.loading && /*#__PURE__*/ react.createElement("div", {
|
|
key: "spinner",
|
|
className: styles_module.loadingSpinner
|
|
})
|
|
], searchResultState.hasMore && /*#__PURE__*/ react.createElement("div", {
|
|
className: styles_module.loader,
|
|
ref: setLoaderRef
|
|
}, /*#__PURE__*/ react.createElement(Translate/* default */.Z, {
|
|
id: "theme.SearchPage.fetchingNewResults",
|
|
description: "The paragraph for fetching new search results"
|
|
}, "Fetching new results..."))));
|
|
}
|
|
function SearchPage() {
|
|
return /*#__PURE__*/ react.createElement(metadataUtils/* HtmlClassNameProvider */.FG, {
|
|
className: "search-page-wrapper"
|
|
}, /*#__PURE__*/ react.createElement(SearchPageContent, null));
|
|
}
|
|
|
|
|
|
/***/ })
|
|
|
|
}]); |