|
|
@@ -0,0 +1,138 @@ |
|
|
(function (window) { |
|
|
'use strict' |
|
|
|
|
|
let options = { |
|
|
searchInput: null, |
|
|
resultsContainer: null, |
|
|
json: [], |
|
|
success: Function.prototype, |
|
|
searchResultTemplate: '<li><a href="{url}" title="{desc}">{title}</a></li>', |
|
|
templateMiddleware: Function.prototype, |
|
|
sortMiddleware: function () { |
|
|
return 0 |
|
|
}, |
|
|
noResultsText: 'No results found', |
|
|
limit: 10, |
|
|
fuzzy: false, |
|
|
debounceTime: null, |
|
|
exclude: [], |
|
|
onSearch: Function.prototype |
|
|
} |
|
|
|
|
|
let debounceTimerHandle |
|
|
const debounce = function (func, delayMillis) { |
|
|
if (delayMillis) { |
|
|
clearTimeout(debounceTimerHandle) |
|
|
debounceTimerHandle = setTimeout(func, delayMillis) |
|
|
} else { |
|
|
func.call() |
|
|
} |
|
|
} |
|
|
|
|
|
const requiredOptions = ['searchInput', 'resultsContainer', 'json'] |
|
|
|
|
|
const templater = require('./Templater') |
|
|
const repository = require('./Repository') |
|
|
const jsonLoader = require('./JSONLoader') |
|
|
const optionsValidator = require('./OptionsValidator')({ |
|
|
required: requiredOptions |
|
|
}) |
|
|
const utils = require('./utils') |
|
|
|
|
|
window.SimpleJekyllSearch = function (_options) { |
|
|
const errors = optionsValidator.validate(_options) |
|
|
if (errors.length > 0) { |
|
|
throwError('You must specify the following required options: ' + requiredOptions) |
|
|
} |
|
|
|
|
|
options = utils.merge(options, _options) |
|
|
|
|
|
templater.setOptions({ |
|
|
template: options.searchResultTemplate, |
|
|
middleware: options.templateMiddleware |
|
|
}) |
|
|
|
|
|
repository.setOptions({ |
|
|
fuzzy: options.fuzzy, |
|
|
limit: options.limit, |
|
|
sort: options.sortMiddleware, |
|
|
exclude: options.exclude |
|
|
}) |
|
|
|
|
|
if (utils.isJSON(options.json)) { |
|
|
initWithJSON(options.json) |
|
|
} else { |
|
|
initWithURL(options.json) |
|
|
} |
|
|
|
|
|
const rv = { |
|
|
search: search |
|
|
} |
|
|
|
|
|
typeof options.success === 'function' && options.success.call(rv) |
|
|
return rv |
|
|
} |
|
|
|
|
|
function initWithJSON (json) { |
|
|
repository.put(json) |
|
|
registerInput() |
|
|
} |
|
|
|
|
|
function initWithURL (url) { |
|
|
jsonLoader.load(url, function (err, json) { |
|
|
if (err) { |
|
|
throwError('failed to get JSON (' + url + ')') |
|
|
} |
|
|
initWithJSON(json) |
|
|
}) |
|
|
} |
|
|
|
|
|
function emptyResultsContainer () { |
|
|
options.resultsContainer.innerHTML = '' |
|
|
} |
|
|
|
|
|
function appendToResultsContainer (text) { |
|
|
options.resultsContainer.innerHTML += text |
|
|
} |
|
|
|
|
|
function registerInput () { |
|
|
options.searchInput.addEventListener('input', function (e) { |
|
|
if (isWhitelistedKey(e.which)) { |
|
|
emptyResultsContainer() |
|
|
debounce(function () { search(e.target.value) }, options.debounceTime) |
|
|
} |
|
|
}) |
|
|
} |
|
|
|
|
|
function search (query) { |
|
|
if (isValidQuery(query)) { |
|
|
emptyResultsContainer() |
|
|
render(repository.search(query), query) |
|
|
|
|
|
typeof options.onSearch === 'function' && options.onSearch.call() |
|
|
} |
|
|
} |
|
|
|
|
|
function render (results, query) { |
|
|
const len = results.length |
|
|
if (len === 0) { |
|
|
return appendToResultsContainer(options.noResultsText) |
|
|
} |
|
|
for (let i = 0; i < len; i++) { |
|
|
results[i].query = query |
|
|
appendToResultsContainer(templater.compile(results[i])) |
|
|
} |
|
|
} |
|
|
|
|
|
function isValidQuery (query) { |
|
|
return query && query.length > 0 |
|
|
} |
|
|
|
|
|
function isWhitelistedKey (key) { |
|
|
return [13, 16, 20, 37, 38, 39, 40, 91].indexOf(key) === -1 |
|
|
} |
|
|
|
|
|
function throwError (message) { |
|
|
throw new Error('SimpleJekyllSearch --- ' + message) |
|
|
} |
|
|
})(window) |