Skip to content

Commit 28b8ed0

Browse files
authored
fix: Allow searching for non-alphanumeric characters (ietf-tools#4266)
* fix: Allow searching for non-alphanumeric characters Fixes ietf-tools#3675 @rjsparks, this avoids us needing to fork list.js. If you prefer a fork, let me know. * Add a TODO
1 parent aea533e commit 28b8ed0

2 files changed

Lines changed: 144 additions & 3 deletions

File tree

ietf/static/js/list.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -197,14 +197,18 @@ $(document)
197197
hook = parent[0];
198198
}
199199

200-
list_instance.push(
201-
new List(hook, pagination ? {
200+
let newlist = new List(hook, pagination ? {
202201
valueNames: fields,
203202
pagination: pagination,
204203
page: items_per_page
205204
} : {
206205
valueNames: fields
207-
}));
206+
});
207+
// override search module with a patched version
208+
// see https://github.com/javve/list.js/issues/699
209+
// TODO: check if this is still needed if list.js ever sees an update
210+
newlist.search = require("./listjs-search")(newlist);
211+
list_instance.push(newlist);
208212
});
209213

210214
if (enable_search) {

ietf/static/js/listjs-search.js

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
module.exports = function (list) {
2+
var item, text, columns, searchString, customSearch
3+
4+
var prepare = {
5+
resetList: function () {
6+
list.i = 1
7+
list.templater.clear()
8+
customSearch = undefined
9+
},
10+
setOptions: function (args) {
11+
if (args.length == 2 && args[1] instanceof Array) {
12+
columns = args[1]
13+
} else if (args.length == 2 && typeof args[1] == 'function') {
14+
columns = undefined
15+
customSearch = args[1]
16+
} else if (args.length == 3) {
17+
columns = args[1]
18+
customSearch = args[2]
19+
} else {
20+
columns = undefined
21+
}
22+
},
23+
setColumns: function () {
24+
if (list.items.length === 0) return
25+
if (columns === undefined) {
26+
columns = list.searchColumns === undefined ? prepare.toArray(list.items[0].values()) : list.searchColumns
27+
}
28+
},
29+
setSearchString: function (s) {
30+
s = list.utils.toString(s).toLowerCase()
31+
// see https://github.com/javve/list.js/issues/699
32+
// s = s.replace(/[-[\]{}()*+?.,\\^$|#]/g, '\\$&') // Escape regular expression characters
33+
searchString = s
34+
},
35+
toArray: function (values) {
36+
var tmpColumn = []
37+
for (var name in values) {
38+
tmpColumn.push(name)
39+
}
40+
return tmpColumn
41+
},
42+
}
43+
var search = {
44+
list: function () {
45+
// Extract quoted phrases "word1 word2" from original searchString
46+
// searchString is converted to lowercase by List.js
47+
var words = [],
48+
phrase,
49+
ss = searchString
50+
while ((phrase = ss.match(/"([^"]+)"/)) !== null) {
51+
words.push(phrase[1])
52+
ss = ss.substring(0, phrase.index) + ss.substring(phrase.index + phrase[0].length)
53+
}
54+
// Get remaining space-separated words (if any)
55+
ss = ss.trim()
56+
if (ss.length) words = words.concat(ss.split(/\s+/))
57+
for (var k = 0, kl = list.items.length; k < kl; k++) {
58+
var item = list.items[k]
59+
item.found = false
60+
if (!words.length) continue
61+
for (var i = 0, il = words.length; i < il; i++) {
62+
var word_found = false
63+
for (var j = 0, jl = columns.length; j < jl; j++) {
64+
var values = item.values(),
65+
column = columns[j]
66+
if (values.hasOwnProperty(column) && values[column] !== undefined && values[column] !== null) {
67+
var text = typeof values[column] !== 'string' ? values[column].toString() : values[column]
68+
if (text.toLowerCase().indexOf(words[i]) !== -1) {
69+
// word found, so no need to check it against any other columns
70+
word_found = true
71+
break
72+
}
73+
}
74+
}
75+
// this word not found? no need to check any other words, the item cannot match
76+
if (!word_found) break
77+
}
78+
item.found = word_found
79+
}
80+
},
81+
// Removed search.item() and search.values()
82+
reset: function () {
83+
list.reset.search()
84+
list.searched = false
85+
},
86+
}
87+
88+
var searchMethod = function (str) {
89+
list.trigger('searchStart')
90+
91+
prepare.resetList()
92+
prepare.setSearchString(str)
93+
prepare.setOptions(arguments) // str, cols|searchFunction, searchFunction
94+
prepare.setColumns()
95+
96+
if (searchString === '') {
97+
search.reset()
98+
} else {
99+
list.searched = true
100+
if (customSearch) {
101+
customSearch(searchString, columns)
102+
} else {
103+
search.list()
104+
}
105+
}
106+
107+
list.update()
108+
list.trigger('searchComplete')
109+
return list.visibleItems
110+
}
111+
112+
list.handlers.searchStart = list.handlers.searchStart || []
113+
list.handlers.searchComplete = list.handlers.searchComplete || []
114+
115+
list.utils.events.bind(
116+
list.utils.getByClass(list.listContainer, list.searchClass),
117+
'keyup',
118+
list.utils.events.debounce(function (e) {
119+
var target = e.target || e.srcElement, // IE have srcElement
120+
alreadyCleared = target.value === '' && !list.searched
121+
if (!alreadyCleared) {
122+
// If oninput already have resetted the list, do nothing
123+
searchMethod(target.value)
124+
}
125+
}, list.searchDelay)
126+
)
127+
128+
// Used to detect click on HTML5 clear button
129+
list.utils.events.bind(list.utils.getByClass(list.listContainer, list.searchClass), 'input', function (e) {
130+
var target = e.target || e.srcElement
131+
if (target.value === '') {
132+
searchMethod('')
133+
}
134+
})
135+
136+
return searchMethod
137+
}

0 commit comments

Comments
 (0)