‘;
resetResultsScroll();
setStatus(‘Type at least ‘ + minChars + ‘ characters to search content.’);
};
var renderLoading = function () {
results.innerHTML = ‘
Searching content…
Looking through FLEXIcontent items.
‘;
resetResultsScroll();
setStatus(‘Searching FLEXIcontent items…’);
};
var renderEmpty = function (query) {
results.innerHTML = ‘
‘ + escapeHtml(emptyMessage) + ‘
Try a more specific title, keyword, category term, or subject.
‘;
resetResultsScroll();
setStatus(‘No matching content items were found for “‘ + query + ‘”.’);
};
var renderUnavailable = function () {
results.innerHTML = ‘
Search unavailable
The live content search could not load results just now. Press Enter to open the full search page.
‘;
resetResultsScroll();
setStatus(‘Live search is temporarily unavailable.’);
};
var updateClearButton = function () {
clearButton.hidden = !input.value.trim();
};
var normalizeUrl = function (value) {
try {
return new URL(value, window.location.origin).toString();
} catch (error) {
return ‘#’;
}
};
var textFrom = function (node, selector) {
var selected = node.querySelector(selector);
return selected ? selected.textContent.replace(/s+/g, ‘ ‘).trim() : ”;
};
var buildSummary = function (item) {
var summary = textFrom(item, ‘.fc_search_result_text’);
return summary.length > 240 ? summary.slice(0, 237) + ‘…’ : summary;
};
var parseResults = function (html, query) {
var doc = new DOMParser().parseFromString(html, ‘text/html’);
var items = Array.prototype.slice.call(doc.querySelectorAll(‘.fc_search_result, fieldset[id^=”searchlist_item_”]’)).slice(0, limit);
return items.map(function (item) {
var link = item.querySelector(‘.fc_search_result_title a, h2 a, h3 a, a’);
var title = link ? link.textContent.replace(/^s*d+.s*/, ”).replace(/s+/g, ‘ ‘).trim() : ”;
var href = link ? normalizeUrl(link.getAttribute(‘href’) || link.href) : buildSearchUrl(query, false);
var category = textFrom(item, ‘.fc_search_result_category’);
var date = textFrom(item, ‘.fc_search_result_date’);
var summary = buildSummary(item);
return title ? {
title: title,
href: href,
category: category,
date: date,
summary: summary
} : null;
}).filter(Boolean);
};
var renderResults = function (items, query) {
if (!items.length) {
renderEmpty(query);
return;
}
results.innerHTML = ‘
var meta = [item.category, item.date].filter(Boolean).join(‘ · ‘);
var relevance = item.relevance || scoreResult(item, query);
return ‘
‘
‘
‘
‘ + highlightText(item.title, query) + ‘
‘ +
‘‘ + relevance + ‘% match‘ +
‘
‘ +
(item.summary ? ‘
Matched in page text ‘ + highlightText(item.summary, query) + ‘
‘ : ”) +
(meta ? ‘
‘ : ”) +
‘
‘ +
‘
‘;
}).join(”) + ‘
‘;
resetResultsScroll();
var noun = items.length === 1 ? ‘item’ : ‘items’;
setStatus(‘Showing ‘ + items.length + ‘ content ‘ + noun + ‘ for “‘ + query + ‘”.’);
};
var scoreResult = function (item, query) {
var terms = String(query || ”).toLowerCase().split(/s+/).filter(function (term) {
return term.length > 1;
});
var title = String(item.title || ”).toLowerCase();
var summary = String(item.summary || ”).toLowerCase();
var meta = String([item.category, item.date].filter(Boolean).join(‘ ‘) || ”).toLowerCase();
var score = 25;
terms.forEach(function (term) {
if (title.indexOf(term) >= 0) {
score += 22;
}
if (summary.indexOf(term) >= 0) {
score += 10;
}
if (meta.indexOf(term) >= 0) {
score += 7;
}
});
return Math.max(35, Math.min(100, score));
};
var describeResults = function (total, shown, from, to, query) {
if (!shown) {
return ‘No matching content items were found for “‘ + query + ‘”.’;
}
if (from > 0 && to >= from) {
return ‘Showing ‘ + from + ‘-‘ + to + ‘ of ‘ + total + ‘ content items for “‘ + query + ‘”.’;
}
var noun = shown === 1 ? ‘item’ : ‘items’;
return ‘Showing ‘ + shown + ‘ content ‘ + noun + ‘ for “‘ + query + ‘”.’;
};
var renderEndpointFragment = function (html, query) {
results.innerHTML = html;
resetResultsScroll();
var fragment = results.querySelector(‘[data-flexcontentsearch-fragment]’);
if (!fragment) {
renderUnavailable();
return;
}
var total = parseInt(fragment.getAttribute(‘data-total’) || ‘0’, 10);
var rendered = results.querySelectorAll(‘.g-flexcontentsearch__result’).length;
var shown = rendered || parseInt(fragment.getAttribute(‘data-shown’) || String(total), 10);
var from = parseInt(fragment.getAttribute(‘data-from’) || ‘0’, 10);
var to = parseInt(fragment.getAttribute(‘data-to’) || ‘0’, 10);
setStatus(describeResults(total, shown, from, to, query));
};
var fetchResults = function (query, page) {
page = Math.max(1, parseInt(page || 1, 10));
setQueryUrl(query);
var activeFilters = getFilters();
var cacheKey = [‘v10’, query.toLowerCase(), limit, contenttypes, page, (activeFilters.types || []).join(‘,’), (activeFilters.categories || []).join(‘,’), activeFilters.order, activeFilters.phrase].join(‘|’);
if (endpoint && resultCache.has(cacheKey)) {
manuallyClosed = false;
openOverlay();
renderEndpointFragment(resultCache.get(cacheKey), query);
return;
}
if (controller) {
controller.abort();
}
manuallyClosed = false;
controller = window.AbortController ? new AbortController() : null;
renderLoading();
openOverlay();
fetch(endpoint ? buildEndpointUrl(query, page) : buildSearchUrl(query, true), {
credentials: ‘same-origin’,
signal: controller ? controller.signal : undefined
})
.then(function (response) {
return response.text();
})
.then(function (html) {
if (endpoint) {
resultCache.set(cacheKey, html);
renderEndpointFragment(html, query);
return;
}
renderResults(parseResults(html, query), query);
})
.catch(function (error) {
if (error && error.name === ‘AbortError’) {
return;
}
renderUnavailable();
});
};
var queueSearch = function () {
var query = input.value.trim();
updateClearButton();
manuallyClosed = false;
if (debounceId) {
window.clearTimeout(debounceId);
}
if (query.length < minChars) {
if (controller) {
controller.abort();
}
renderIdle();
return;
}
debounceId = window.setTimeout(function () {
fetchResults(query, 1);
}, 120);
};
triggerButton.addEventListener(‘click’, function () {
manuallyClosed = false;
pushSearchState();
openOverlay();
if (input.value.trim().length >= minChars) {
queueSearch();
}
});
input.addEventListener(‘input’, queueSearch);
input.addEventListener(‘keydown’, function (event) {
if (event.key === ‘Enter’ && input.value.trim()) {
event.preventDefault();
window.location.href = buildSearchUrl(input.value.trim(), false);
}
});
results.addEventListener(‘click’, function (event) {
var pageButton = event.target.closest(‘[data-flexcontentsearch-page]’);
if (!pageButton || pageButton.disabled) {
return;
}
event.preventDefault();
fetchResults(input.value.trim(), pageButton.getAttribute(‘data-flexcontentsearch-page’));
});
clearButton.addEventListener(‘click’, function () {
input.value = ”;
updateClearButton();
if (controller) {
controller.abort();
}
manuallyClosed = false;
setQueryUrl(”);
renderIdle();
input.focus();
});
closeButton.addEventListener(‘click’, function () {
dismissSearch();
});
backdrop.addEventListener(‘click’, function () {
dismissSearch();
});
document.addEventListener(‘keydown’, function (event) {
if (event.key === ‘Escape’ && !overlayShell.hidden) {
dismissSearch();
}
});
window.addEventListener(‘resize’, function () {
if (!overlayShell.hidden) {
positionOverlay();
}
});
// Sync the overlay to the URL on Back/Forward within the document
// and when the page is restored from the back/forward cache.
window.addEventListener(‘popstate’, applyUrlState);
window.addEventListener(‘pageshow’, function (event) {
if (event.persisted) {
applyUrlState();
}
});
renderIdle();
updateClearButton();
// Initial sync on DOMContentLoaded so the footer (which is parsed
// after this in-header script) exists before we pin it.
if (document.readyState === ‘loading’) {
document.addEventListener(‘DOMContentLoaded’, applyUrlState);
} else {
applyUrlState();
}
})();