diff --git a/ietf/static/js/list.js b/ietf/static/js/list.js index 8e22cfd3ba..7fe325bfd3 100644 --- a/ietf/static/js/list.js +++ b/ietf/static/js/list.js @@ -197,14 +197,17 @@ $(document) hook = parent[0]; } - list_instance.push( - new List(hook, pagination ? { + let newlist = new List(hook, pagination ? { valueNames: fields, pagination: pagination, page: items_per_page } : { valueNames: fields - })); + }); + // override search module with a patched version + // see https://github.com/javve/list.js/issues/699 + newlist.search = require("./listjs-search")(newlist); + list_instance.push(newlist); }); if (enable_search) { diff --git a/ietf/static/js/listjs-search.js b/ietf/static/js/listjs-search.js new file mode 100644 index 0000000000..da921b25ed --- /dev/null +++ b/ietf/static/js/listjs-search.js @@ -0,0 +1,137 @@ +module.exports = function (list) { + var item, text, columns, searchString, customSearch + + var prepare = { + resetList: function () { + list.i = 1 + list.templater.clear() + customSearch = undefined + }, + setOptions: function (args) { + if (args.length == 2 && args[1] instanceof Array) { + columns = args[1] + } else if (args.length == 2 && typeof args[1] == 'function') { + columns = undefined + customSearch = args[1] + } else if (args.length == 3) { + columns = args[1] + customSearch = args[2] + } else { + columns = undefined + } + }, + setColumns: function () { + if (list.items.length === 0) return + if (columns === undefined) { + columns = list.searchColumns === undefined ? prepare.toArray(list.items[0].values()) : list.searchColumns + } + }, + setSearchString: function (s) { + s = list.utils.toString(s).toLowerCase() + // see https://github.com/javve/list.js/issues/699 + // s = s.replace(/[-[\]{}()*+?.,\\^$|#]/g, '\\$&') // Escape regular expression characters + searchString = s + }, + toArray: function (values) { + var tmpColumn = [] + for (var name in values) { + tmpColumn.push(name) + } + return tmpColumn + }, + } + var search = { + list: function () { + // Extract quoted phrases "word1 word2" from original searchString + // searchString is converted to lowercase by List.js + var words = [], + phrase, + ss = searchString + while ((phrase = ss.match(/"([^"]+)"/)) !== null) { + words.push(phrase[1]) + ss = ss.substring(0, phrase.index) + ss.substring(phrase.index + phrase[0].length) + } + // Get remaining space-separated words (if any) + ss = ss.trim() + if (ss.length) words = words.concat(ss.split(/\s+/)) + for (var k = 0, kl = list.items.length; k < kl; k++) { + var item = list.items[k] + item.found = false + if (!words.length) continue + for (var i = 0, il = words.length; i < il; i++) { + var word_found = false + for (var j = 0, jl = columns.length; j < jl; j++) { + var values = item.values(), + column = columns[j] + if (values.hasOwnProperty(column) && values[column] !== undefined && values[column] !== null) { + var text = typeof values[column] !== 'string' ? values[column].toString() : values[column] + if (text.toLowerCase().indexOf(words[i]) !== -1) { + // word found, so no need to check it against any other columns + word_found = true + break + } + } + } + // this word not found? no need to check any other words, the item cannot match + if (!word_found) break + } + item.found = word_found + } + }, + // Removed search.item() and search.values() + reset: function () { + list.reset.search() + list.searched = false + }, + } + + var searchMethod = function (str) { + list.trigger('searchStart') + + prepare.resetList() + prepare.setSearchString(str) + prepare.setOptions(arguments) // str, cols|searchFunction, searchFunction + prepare.setColumns() + + if (searchString === '') { + search.reset() + } else { + list.searched = true + if (customSearch) { + customSearch(searchString, columns) + } else { + search.list() + } + } + + list.update() + list.trigger('searchComplete') + return list.visibleItems + } + + list.handlers.searchStart = list.handlers.searchStart || [] + list.handlers.searchComplete = list.handlers.searchComplete || [] + + list.utils.events.bind( + list.utils.getByClass(list.listContainer, list.searchClass), + 'keyup', + list.utils.events.debounce(function (e) { + var target = e.target || e.srcElement, // IE have srcElement + alreadyCleared = target.value === '' && !list.searched + if (!alreadyCleared) { + // If oninput already have resetted the list, do nothing + searchMethod(target.value) + } + }, list.searchDelay) + ) + + // Used to detect click on HTML5 clear button + list.utils.events.bind(list.utils.getByClass(list.listContainer, list.searchClass), 'input', function (e) { + var target = e.target || e.srcElement + if (target.value === '') { + searchMethod('') + } + }) + + return searchMethod +}