Skip to content

Commit

Permalink
fix: Allow searching for non-alphanumeric characters
Browse files Browse the repository at this point in the history
Fixes ietf-tools#3675

@rjsparks, this avoids us needing to fork list.js. If you prefer a fork, let me
know.
  • Loading branch information
larseggert committed Jul 24, 2022
1 parent de4f1fe commit 3b39840
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 3 deletions.
9 changes: 6 additions & 3 deletions ietf/static/js/list.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
137 changes: 137 additions & 0 deletions ietf/static/js/listjs-search.js
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit 3b39840

Please sign in to comment.