//= require lunr.min //= require _vendor/jquery.mark.js (function ($, Modules) { 'use strict' Modules.Search = function Search () { var s = this var $html = $('html') var $searchForm var $searchLabel var $searchInput var $searchResults var $searchResultsTitle var $searchResultsWrapper var $searchResultsClose var results var query var queryTimer var maxSearchEntries = 20 this.start = function start ($element) { $searchForm = $element.find('form') $searchInput = $element.find('#search') $searchLabel = $element.find('.search__label') $searchResultsWrapper = $element.find('.search-results') $searchResults = $searchResultsWrapper.find('.search-results__content') $searchResultsTitle = $searchResultsWrapper.find('.search-results__title') $searchResultsClose = $searchResultsWrapper.find('.search-results__close') s.downloadSearchIndex() attach() changeSearchLabel() } this.downloadSearchIndex = function downloadSearchIndex () { updateTitle('Loading search index') $.ajax({ url: '/search.json', cache: true, method: 'GET', success: function (data) { s.lunrData = data s.lunrIndex = lunr.Index.load(s.lunrData.index) replaceStopWordFilter() $(document).trigger('lunrIndexLoaded') } }) } function attach () { // Search functionality on search text input $searchInput.on('input', function (e) { e.preventDefault() query = $(this).val() s.search(query, function (r) { results = r renderResults(query) updateTitle() }) if (window.ga) { window.clearTimeout(queryTimer) queryTimer = window.setTimeout(sendQueryToAnalytics, 1000) } }) // Set focus on the first search result instead of submiting the search // form to Google $searchForm.on('submit', function (e) { e.preventDefault() showResults() $searchResults.find('.search-result__title a').first().focus() }) // Closing the search results, move focus back to the search input $searchResultsClose.on('click', function (e) { e.preventDefault() $searchInput.focus() hideResults() }) // Attach analytics events to search result clicks if (window.ga) { $searchResults.on('click', '.search-result__title a', function () { var href = $(this).attr('href') ga('send', { hitType: 'event', eventCategory: 'Search result', eventAction: 'click', eventLabel: href, transport: 'beacon' }) }) } // When selecting navigation link, close the search results. $('#toc').on('click', 'a', function (e) { hideResults() }) } function changeSearchLabel () { $searchLabel.text('Search') } function getResults (query) { var results = [] s.lunrIndex.search(query).forEach(function (item, index) { if (index < maxSearchEntries) { results.push(s.lunrData.docs[item.ref]) } }) return results } this.search = function search (query, callback) { if (query === '') { hideResults() return } showResults() // The index has not been downloaded yet, exit early and wait. if (!s.lunrIndex) { $(document).on('lunrIndexLoaded', function () { s.search(query, callback) }) return } callback(getResults(query)) } function renderResults (query) { var output = '' if (results.length === 0) { output += '

Nothing found

' } output += '' $searchResults.html(output) } this.processContent = function processContent (content, query) { var output content = '
' + content + '
' content = $(content).mark(query) // Split content by sentence. var sentences = content.html().replace(/(\.+|:|!|\?|\r|\n)("*|'*|\)*|}*|]*)/gm, '|').split('|') // Select the first five sentences that contain a var selectedSentences = [] for (var i = 0; i < sentences.length; i++) { if (selectedSentences.length === 5) { break } var sentence = sentences[i].trim() var containsMark = sentence.includes('mark>') if (containsMark && (selectedSentences.indexOf(sentence) === -1)) { selectedSentences.push(sentence) } } if (selectedSentences.length > 0) { output = ' … ' + selectedSentences.join(' … ') + ' … ' } return output } // Default text is to display the number of search results function updateTitle (text) { if (typeof text === 'undefined') { var count = results.length var resultsText = count + ' results' } $searchResultsTitle.text(text || resultsText) } function showResults () { $searchResultsWrapper.addClass('is-open') .attr('aria-hidden', 'false') $html.addClass('has-search-results-open') } function hideResults () { $searchResultsWrapper.removeClass('is-open') .attr('aria-hidden', 'true') $html.removeClass('has-search-results-open') } function sendQueryToAnalytics () { if (query === '') { return } var stripped = window.stripPIIFromString(query) ga('send', { hitType: 'event', eventCategory: 'Search query', eventAction: 'type', eventLabel: stripped, transport: 'beacon' }) } function replaceStopWordFilter () { // Replace the default stopWordFilter as it excludes useful words like // 'get' // See: https://lunrjs.com/docs/stop_word_filter.js.html#line43 s.lunrIndex.pipeline.remove(lunr.stopWordFilter) s.lunrIndex.pipeline.add(s.govukStopWorldFilter) } this.govukStopWorldFilter = lunr.generateStopWordFilter([ 'a', 'able', 'about', 'across', 'after', 'all', 'almost', 'also', 'am', 'among', 'an', 'and', 'any', 'are', 'as', 'at', 'be', 'because', 'been', 'but', 'by', 'can', 'cannot', 'could', 'dear', 'did', 'do', 'does', 'either', 'else', 'ever', 'every', 'for', 'from', 'got', 'had', 'has', 'have', 'he', 'her', 'hers', 'him', 'his', 'how', 'however', 'i', 'if', 'in', 'into', 'is', 'it', 'its', 'just', 'least', 'let', 'like', 'likely', 'may', 'me', 'might', 'most', 'must', 'my', 'neither', 'no', 'nor', 'not', 'of', 'off', 'often', 'on', 'only', 'or', 'other', 'our', 'own', 'rather', 'said', 'say', 'says', 'she', 'should', 'since', 'so', 'some', 'than', 'that', 'the', 'their', 'them', 'then', 'there', 'these', 'they', 'this', 'tis', 'to', 'too', 'twas', 'us', 'wants', 'was', 'we', 'were', 'what', 'when', 'where', 'which', 'while', 'who', 'whom', 'why', 'will', 'with', 'would', 'yet', 'you', 'your' ]) } // Polyfill includes if (!String.prototype.includes) { String.prototype.includes = function (search, start) { // eslint-disable-line no-extend-native 'use strict' if (typeof start !== 'number') { start = 0 } if (start + search.length > this.length) { return false } else { return this.indexOf(search, start) !== -1 } } } })(jQuery, window.GOVUK.Modules)