import qs from 'qs';
const findstr = window.findstr || {};
const {
buildSearchQuery,
getFacetDistribution,
itemsToFields,
parseFilters,
parseSort,
Handlebars,
} = window.findstr.helpers;
function isMultiselectField(field) {
return !(field.options && field.options.multiselect !== true);
}
function renderCheckboxField(fieldItem, field, choices) {
const checkboxTemplate = Handlebars.compile(
fieldItem.querySelector('.findstrCheckboxFieldTemplate').innerHTML
);
const container = fieldItem.querySelector('.findstrFieldContainer');
container.innerHTML = checkboxTemplate({
/**
* Filter the choices for the checkbox field
*
* @hook findstrCheckboxFieldChoices
*
* @param {Object} choices - The choices object
* @param {Object} field - The field object
*
* @return {Object} The choices object
*/
choices: findstr.hooks.applyFilters(
'findstrCheckboxFieldChoices',
choices,
field
),
field,
});
}
function optionCount(field, option, group, facets) {
if (facets) {
return facets[field.source_name][option] || 0;
}
if (disableCountUpdate(field)) {
return findstr.facets[field.source_name][option] || 0;
}
return showCount(field)
? findstr.groups[group]?.facets[field.source_name]?.[option] || 0
: findstr.facets[field.source_name][option] || 0;
}
function showCount(field) {
return field.options?.showCount;
}
function disableCountUpdate(field) {
return field.options?.disableCountUpdate;
}
function updateElementCount(element, count, field) {
const parent = element.parentElement;
if (0 === count && !element.checked) {
element.disabled = true;
parent.classList.add('disabled');
} else {
element.disabled = false;
parent.classList.remove('disabled');
}
if (!disableCountUpdate(field)) {
const countElement = parent.querySelector('.filter-count');
if (countElement) {
countElement.innerHTML = count;
}
}
}
document.addEventListener(
'findstrLoaded',
function (e) {
/**
* Build checkbox field with values on first search (warmup)
*/
findstr.hooks.addAction(
'searchResultsFirstLoad',
'findstr-search',
() => {
const urlSearchParams = qs.parse(window.location.search, {
ignoreQueryPrefix: true,
arrayFormat: 'brackets',
});
const checkboxFields = document.querySelectorAll(
'.findstr-field.findstr-field-checkbox'
);
checkboxFields.forEach((fieldItem) => {
const field = JSON.parse(fieldItem.dataset.field);
const facets = findstr.facets[field.source_name] || {};
const choices = {};
const selected =
urlSearchParams?.findstr?.filter?.[field.source_name]
?.value || [];
Object.keys(facets).forEach((key) => {
choices[key] = {
value: key,
/**
* Filter the field value label
*
* @hook findstrFieldValueLabel
*
* @param {string} label - The label
* @param {string} key - The key
* @param {Object} field - The field object
* @param {Array} selected - The selected values
*
* @return {string} The label
*/
label: findstr.hooks.applyFilters(
'findstrFieldValueLabel',
key,
field,
selected
),
count: 0,
checked: selected.includes(key),
};
});
renderCheckboxField(fieldItem, field, choices);
const items = fieldItem.querySelectorAll('[data-findstr]');
itemsToFields(items, findstr);
});
}
);
findstr.hooks.addAction(
'findstrInit',
'findstr-search-checkbox',
() => {
//manage checkbox default values
Object.keys(findstr.groups).forEach((group) => {
const checkboxFields = document.querySelectorAll(
`.findstr-field.findstr-field-checkbox[data-group="${group}"]`
);
checkboxFields.forEach((checkboxField) => {
const field = JSON.parse(checkboxField.dataset.field);
if (showCount(field)) {
const items = checkboxField.querySelectorAll(
'[data-findstr][type="checkbox"]'
);
items.forEach((item) => {
if (field.type === 'checkbox') {
if (findstr.groups[group].facets) {
const count = optionCount(
field,
item.value,
group
);
updateElementCount(item, count, field);
}
}
});
/**
* manage facets count updates
*/
findstr.hooks.addAction(
'beforeSearch',
'findstr-search',
(
currentGroup,
parameters,
currentField,
currentElement,
index
) => {
const newQuery = buildSearchQuery(
group,
checkboxField
);
const query = {
...newQuery,
q: newQuery.q,
facets: ['*'],
limit: 1,
};
const queryClone = JSON.parse(
JSON.stringify(query)
); //clone query;
//remove the filter for the current field
delete query.filter.clauses[
field.source_name
];
if (
field.options?.multiselect &&
field.options?.multiselectLogic === 'OR'
) {
//update count, remove filters
queryClone.filter = parseFilters(
query.filter
);
queryClone.sort = parseSort(query.sort);
} else {
//update count, with filters
queryClone.filter = parseFilters(
newQuery.filter
);
queryClone.sort = parseSort(
newQuery.sort
);
}
getFacetDistribution(queryClone).then(
(facets) => {
Object.keys(
findstr.facets[
field.source_name
]
).forEach((choice) => {
const count = optionCount(
field,
choice,
group,
facets
);
const item =
document.querySelector(
`[data-findstr-group="${group}"][data-findstr-id="${field.id}"][value="${choice}"]`
);
updateElementCount(
item,
count,
field
);
});
}
);
}
);
}
});
});
}
);
/**
* Build search query based on select value
*/
findstr.hooks.addFilter(
'findstrBuildSearchQuery',
'findstr-search',
(query, items, group, target) => {
//loop through all the fields
for (const [filter_name, inputs] of Object.entries(items)) {
//for each field, get the field object
const field = JSON.parse(
document.querySelector(
`[data-id="${inputs[0].dataset.findstrId}"]`
).dataset.field
);
//if the field is a checkbox
if (field.type === 'checkbox') {
const values = [];
inputs.forEach((input) => {
//if the checkbox is checked and has a value
if (input.checked && '' !== input.value) {
values.push(input.value);
if (!isMultiselectField(field)) {
query.filter.clauses[input.name] = {
value: input.value,
};
}
}
});
//default case, single select
if (values[0]) {
query.filter.clauses[filter_name] = {
value: values[0],
};
}
//handle multiselect fields
if (isMultiselectField(field)) {
if (values.length > 0) {
query.filter.clauses[filter_name].value =
values;
//if the field has AND/OR logic
if (field.options.multiselectLogic === 'OR') {
query.filter.clauses[filter_name].compare =
'IN';
} else {
query.filter.clauses[filter_name].compare =
'=';
}
}
}
}
}
return query;
}
);
/**
* Handle "radio mode" for checkbox fields
* Uncheck other checkboxes in the same group
*/
findstr.hooks.addAction(
'findstrOnFilterEvents',
'findstr-search',
(target, item, group, field) => {
if (
field.type === 'checkbox' &&
(!field.options || field.options.multiselect !== true)
) {
if (item.checked) {
const inputs = document.querySelectorAll(
`[data-findstr-group="${group}"][data-findstr-id="${field.id}"]:checked`
);
inputs.forEach((input) => {
if (input !== target) {
input.checked = false;
}
});
} else {
//if the checkbox is unchecked, re-check the first one ("all")
document.getElementById(
`findstr-all-${field.id}-${group}`
).checked = true;
}
}
}
);
/**
* Reset Filters
*/
findstr.hooks.addAction(
'resetFilters',
'checkboxField',
(group, query) => {
findstr.groups[group].items.forEach((item) => {
if ('checkbox' === item.field.type) {
const targetValue =
query.filter.clauses[item.field.source_name]
?.value || '';
if (targetValue === item.value) {
item.checked = true;
} else {
item.checked = false;
}
}
});
}
);
/**
* Excluded filters
*/
findstr.hooks.addFilter(
'findstrCheckboxFieldChoices',
'checkboxField',
function (choices, field) {
if (field.options?.excludeOptions) {
field.options.excludeOptions.forEach((excluded) => {
delete choices[excluded];
});
}
return choices;
}
);
},
false
);