datatables.net-feature-fuzzysearch 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Readme.md +24 -0
- package/dist/dataTables.fuzzySearch.css +0 -0
- package/dist/dataTables.fuzzySearch.d.ts +10 -0
- package/dist/dataTables.fuzzySearch.js +482 -0
- package/dist/dataTables.fuzzySearch.min.css +0 -0
- package/dist/dataTables.fuzzySearch.min.js +10 -0
- package/dist/dataTables.fuzzySearch.min.mjs +10 -0
- package/dist/dataTables.fuzzySearch.mjs +442 -0
- package/dist/search.d.ts +4 -0
- package/dist/search.js +157 -0
- package/dist/types.d.ts +36 -0
- package/package.json +33 -0
package/Readme.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
|
|
2
|
+
# DataTables input paging feature
|
|
3
|
+
|
|
4
|
+
The FuzzySearch plugin for DataTables which adds a _feature_ to DataTables for use in the `-init layout` option. The `fuzzySearch` feature adds a "fuzzy" option to the DataTables default search box, which the end user can activate.
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
To install with npm (substitute with your favorite package manager if you prefer):
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
npm install datatables.net datatables.net-feature-fuzzysearch
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Then in your Javascript file (assuming you are using ES Modules):
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
import DataTable from 'datatables.net';
|
|
19
|
+
import 'datatables.net-feature-fuzzysearch';
|
|
20
|
+
|
|
21
|
+
new DataTable('#example', {
|
|
22
|
+
fuzzySearch: true
|
|
23
|
+
});
|
|
24
|
+
```
|
|
File without changes
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Fuzzy Search for DataTables
|
|
3
|
+
* SpryMedia Ltd - datatables.net/license MIT license
|
|
4
|
+
*
|
|
5
|
+
* Damerau-Levenshtein function courtesy of https://github.com/tad-lispy/node-damerau-levenshtein
|
|
6
|
+
* BSD 2-Clause License
|
|
7
|
+
* Copyright (c) 2018, Tadeusz Łazurski
|
|
8
|
+
* All rights reserved.
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Fuzzy Search 3.0.0-beta.2 for DataTables
|
|
3
|
+
* SpryMedia Ltd - datatables.net/license MIT license
|
|
4
|
+
*
|
|
5
|
+
* Damerau-Levenshtein function courtesy of https://github.com/tad-lispy/node-damerau-levenshtein
|
|
6
|
+
* BSD 2-Clause License
|
|
7
|
+
* Copyright (c) 2018, Tadeusz Łazurski
|
|
8
|
+
* All rights reserved.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
(function(factory){
|
|
12
|
+
if (typeof define === 'function' && define.amd) {
|
|
13
|
+
// AMD
|
|
14
|
+
define(['datatables.net'], function (dt) {
|
|
15
|
+
return factory(window, document, dt);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
else if (typeof exports === 'object') {
|
|
19
|
+
// CommonJS
|
|
20
|
+
var cjsRequires = function (root) {
|
|
21
|
+
if (! root.DataTable) {
|
|
22
|
+
require('datatables.net')(root);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
if (typeof window === 'undefined') {
|
|
27
|
+
module.exports = function (root) {
|
|
28
|
+
if (! root) {
|
|
29
|
+
// CommonJS environments without a window global must pass a
|
|
30
|
+
// root. This will give an error otherwise
|
|
31
|
+
root = window;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
cjsRequires(root);
|
|
35
|
+
return factory(root, root.document, root.DataTable);
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
cjsRequires(window);
|
|
40
|
+
module.exports = factory(window, window.document, window.DataTable);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
// Browser
|
|
45
|
+
factory(window, document, window.DataTable);
|
|
46
|
+
}
|
|
47
|
+
}(function(window, document, DataTable) {
|
|
48
|
+
'use strict';
|
|
49
|
+
|
|
50
|
+
var Dom = DataTable.Dom;
|
|
51
|
+
var util = DataTable.util;
|
|
52
|
+
|
|
53
|
+
DataTable.ext.search.push(function (settings, data, dataIndex) {
|
|
54
|
+
let initial = settings.init.fuzzySearch;
|
|
55
|
+
let row = settings.data[dataIndex];
|
|
56
|
+
if (!initial) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
if (row) {
|
|
60
|
+
// If fuzzy searching has not been implemented then pass all rows for this function
|
|
61
|
+
if (row._fuzzySearch !== undefined) {
|
|
62
|
+
// Read score to set the cell content and sort data
|
|
63
|
+
var score = row._fuzzySearch.score;
|
|
64
|
+
if (util.is.plainObject(initial) &&
|
|
65
|
+
initial.rankColumn !== undefined) {
|
|
66
|
+
row.cells[initial.rankColumn].innerHTML = score;
|
|
67
|
+
// Remove '%' from the end of the score so can sort on a number
|
|
68
|
+
if (row.orderCache) {
|
|
69
|
+
row.orderCache[initial.rankColumn] = +score.substring(0, score.length - 1);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Return the value for the pass as decided by the fuzzySearch function
|
|
73
|
+
return row._fuzzySearch.pass;
|
|
74
|
+
}
|
|
75
|
+
else if (util.is.plainObject(initial) &&
|
|
76
|
+
initial.rankColumn !== undefined) {
|
|
77
|
+
row.cells[initial.rankColumn].innerHTML = '';
|
|
78
|
+
if (row.orderCache) {
|
|
79
|
+
row.orderCache[initial.rankColumn] = '';
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return true;
|
|
84
|
+
});
|
|
85
|
+
Dom.s(document).on('init.dt', function (e, settings) {
|
|
86
|
+
var api = new DataTable.Api(settings);
|
|
87
|
+
var initial = api.init();
|
|
88
|
+
var initialFuzzy = initial.fuzzySearch;
|
|
89
|
+
// If this is not set then fuzzy searching is not enabled on the table so return.
|
|
90
|
+
if (!initialFuzzy) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (typeof initialFuzzy === 'object' && initialFuzzy.columns) {
|
|
94
|
+
initialFuzzy.columns = api
|
|
95
|
+
.columns(initialFuzzy.columns)
|
|
96
|
+
.indexes()
|
|
97
|
+
.toArray();
|
|
98
|
+
}
|
|
99
|
+
var fromPlugin = false;
|
|
100
|
+
// Find the input element
|
|
101
|
+
var input = Dom.s(api.table().container()).find('div.dt-search input');
|
|
102
|
+
var fontBold = {
|
|
103
|
+
'font-weight': '600',
|
|
104
|
+
'background-color': 'rgba(255,255,255,0.1)'
|
|
105
|
+
};
|
|
106
|
+
var fontNormal = {
|
|
107
|
+
'font-weight': '500',
|
|
108
|
+
'background-color': 'transparent'
|
|
109
|
+
};
|
|
110
|
+
var toggleCSS = {
|
|
111
|
+
border: 'none',
|
|
112
|
+
background: 'none',
|
|
113
|
+
'font-size': '100%',
|
|
114
|
+
width: '50%',
|
|
115
|
+
display: 'inline-block',
|
|
116
|
+
color: 'white',
|
|
117
|
+
cursor: 'pointer',
|
|
118
|
+
padding: '0.5em'
|
|
119
|
+
};
|
|
120
|
+
// Only going to set the toggle if it is enabled
|
|
121
|
+
var toggle = Dom.c('button').classAdd('toggleSearch').text('Abc').css({
|
|
122
|
+
border: 'none',
|
|
123
|
+
background: 'none',
|
|
124
|
+
position: 'relative',
|
|
125
|
+
right: '33px',
|
|
126
|
+
top: '0px',
|
|
127
|
+
cursor: 'pointer',
|
|
128
|
+
color: '#3b5e99',
|
|
129
|
+
'margin-top': '1px'
|
|
130
|
+
});
|
|
131
|
+
var tooltip, exact, fuzzy, label;
|
|
132
|
+
if (initialFuzzy === true || initialFuzzy.toggleSmart) {
|
|
133
|
+
toggle.insertAfter(input);
|
|
134
|
+
exact = Dom.c('button')
|
|
135
|
+
.classAdd('toggleSearch')
|
|
136
|
+
.text('Exact')
|
|
137
|
+
.insertAfter(input)
|
|
138
|
+
.css(toggleCSS)
|
|
139
|
+
.css(fontBold)
|
|
140
|
+
.attr('highlighted', 'true');
|
|
141
|
+
fuzzy = Dom.c('button')
|
|
142
|
+
.classAdd('toggleSearch')
|
|
143
|
+
.text('Fuzzy')
|
|
144
|
+
.insertAfter(input)
|
|
145
|
+
.css(toggleCSS);
|
|
146
|
+
input.css({
|
|
147
|
+
'padding-right': '30px'
|
|
148
|
+
});
|
|
149
|
+
Dom.s(input.parent()).css('right', '-33px').css('position', 'relative');
|
|
150
|
+
label = Dom.c('div').text('Search Type').css({
|
|
151
|
+
'padding-bottom': '0.5em',
|
|
152
|
+
'font-size': '0.8em'
|
|
153
|
+
});
|
|
154
|
+
tooltip = Dom.c('div')
|
|
155
|
+
.classAdd('fuzzyToolTip')
|
|
156
|
+
.css({
|
|
157
|
+
position: 'absolute',
|
|
158
|
+
top: '2em',
|
|
159
|
+
background: 'white',
|
|
160
|
+
'border-radius': '4px',
|
|
161
|
+
'text-align': 'center',
|
|
162
|
+
padding: '0.5em',
|
|
163
|
+
'background-color': '#16232a',
|
|
164
|
+
'box-shadow': '4px 4px 4px rgba(0, 0, 0, 0.5)',
|
|
165
|
+
color: 'white',
|
|
166
|
+
transition: 'opacity 0.25s',
|
|
167
|
+
'z-index': '30001',
|
|
168
|
+
width: input.width('outer') - 3 + 'px'
|
|
169
|
+
})
|
|
170
|
+
.append(label)
|
|
171
|
+
.append(exact)
|
|
172
|
+
.append(fuzzy);
|
|
173
|
+
}
|
|
174
|
+
function toggleFuzzy(event) {
|
|
175
|
+
if (toggle.attr('blurred')) {
|
|
176
|
+
toggle.css({ filter: 'blur(0px)' }).attrRemove('blurred');
|
|
177
|
+
fuzzy.attrRemove('highlighted').css(fontNormal);
|
|
178
|
+
exact.attr('highlighted', true).css(fontBold);
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
toggle.css({ filter: 'blur(1px)' }).attr('blurred', true);
|
|
182
|
+
exact.attrRemove('highlighted').css(fontNormal);
|
|
183
|
+
fuzzy.attr('highlighted', true).css(fontBold);
|
|
184
|
+
}
|
|
185
|
+
// Whenever the search mode is changed we need to re-search
|
|
186
|
+
triggerSearchFunction(event);
|
|
187
|
+
}
|
|
188
|
+
// Highlights one of the buttons in the tooltip and un-highlights the other
|
|
189
|
+
function highlightButton(toHighlight, event) {
|
|
190
|
+
if (!toHighlight.attr('highlighted')) {
|
|
191
|
+
toggleFuzzy(event);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// Removes the tooltip element
|
|
195
|
+
function removeToolTip() {
|
|
196
|
+
tooltip.remove();
|
|
197
|
+
}
|
|
198
|
+
// Turn off the default datatables searching events
|
|
199
|
+
Dom.s(settings.table).off('search.dt.DT');
|
|
200
|
+
var fuzzySearchVal = '';
|
|
201
|
+
var searchVal = '';
|
|
202
|
+
// The function that we want to run on search
|
|
203
|
+
var triggerSearchFunction = function (event) {
|
|
204
|
+
// If the search is only to be triggered on return wait for that
|
|
205
|
+
if ((event.type === 'input' &&
|
|
206
|
+
(initial.search === undefined ||
|
|
207
|
+
!initial.search.return)) ||
|
|
208
|
+
event.key === 'Enter' ||
|
|
209
|
+
event.type === 'click') {
|
|
210
|
+
// If the toggle is set and isn't checkd then perform a normal search
|
|
211
|
+
if (toggle && !toggle.attr('blurred')) {
|
|
212
|
+
api.rows().iterator('row', function (settings, rowIdx) {
|
|
213
|
+
settings.data[rowIdx]._fuzzySearch = undefined;
|
|
214
|
+
}, false);
|
|
215
|
+
searchVal = input.val();
|
|
216
|
+
fuzzySearchVal = searchVal;
|
|
217
|
+
fromPlugin = true;
|
|
218
|
+
api.search(searchVal);
|
|
219
|
+
fromPlugin = false;
|
|
220
|
+
searchVal = '';
|
|
221
|
+
}
|
|
222
|
+
// Otherwise perform a fuzzy search
|
|
223
|
+
else {
|
|
224
|
+
// Get the value from the input element and convert to lower case
|
|
225
|
+
fuzzySearchVal = input.val();
|
|
226
|
+
searchVal = '';
|
|
227
|
+
if (fuzzySearchVal !== undefined &&
|
|
228
|
+
fuzzySearchVal.length !== 0) {
|
|
229
|
+
fuzzySearchVal = fuzzySearchVal.toLowerCase();
|
|
230
|
+
}
|
|
231
|
+
// For each row call the fuzzy search function to get result
|
|
232
|
+
api.rows().iterator('row', function (settings, rowIdx) {
|
|
233
|
+
var _a;
|
|
234
|
+
settings.data[rowIdx]._fuzzySearch = fuzzySearch(fuzzySearchVal, (_a = settings.data[rowIdx]) === null || _a === void 0 ? void 0 : _a.searchCellCache, initialFuzzy);
|
|
235
|
+
}, false);
|
|
236
|
+
fromPlugin = true;
|
|
237
|
+
// Empty the datatables search and replace it with our own
|
|
238
|
+
api.search('');
|
|
239
|
+
input.val(fuzzySearchVal);
|
|
240
|
+
fromPlugin = false;
|
|
241
|
+
}
|
|
242
|
+
fromPlugin = true;
|
|
243
|
+
api.draw();
|
|
244
|
+
fromPlugin = false;
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
DataTable.Api.register('search.fuzzy()', function (value) {
|
|
248
|
+
if (value === undefined) {
|
|
249
|
+
return fuzzySearchVal;
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
fuzzySearchVal = value.toLowerCase();
|
|
253
|
+
searchVal = api.search();
|
|
254
|
+
input.val(fuzzySearchVal);
|
|
255
|
+
// For each row call the fuzzy search function to get result
|
|
256
|
+
api.rows().iterator('row', function (settings, rowIdx) {
|
|
257
|
+
var _a;
|
|
258
|
+
settings.data[rowIdx]._fuzzySearch = fuzzySearch(fuzzySearchVal, (_a = settings.data[rowIdx]) === null || _a === void 0 ? void 0 : _a.searchCellCache, initialFuzzy);
|
|
259
|
+
}, false);
|
|
260
|
+
// triggerSearchFunction({key: 'Enter'});
|
|
261
|
+
return this;
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
input.off();
|
|
265
|
+
// Set listeners to occur on toggle and typing
|
|
266
|
+
if (toggle) {
|
|
267
|
+
// Actions for the toggle button
|
|
268
|
+
toggle
|
|
269
|
+
.on('click', toggleFuzzy)
|
|
270
|
+
.on('mouseenter', function () {
|
|
271
|
+
tooltip.insertAfter(toggle).on('mouseleave', removeToolTip);
|
|
272
|
+
tooltip.css('left', input.position().left + 3 + 'px');
|
|
273
|
+
exact.on('click', event => highlightButton(exact, event));
|
|
274
|
+
fuzzy.on('click', event => highlightButton(fuzzy, event));
|
|
275
|
+
})
|
|
276
|
+
.on('mouseleave', removeToolTip);
|
|
277
|
+
// Actions for the input element
|
|
278
|
+
input
|
|
279
|
+
.on('mouseenter', function () {
|
|
280
|
+
tooltip.insertAfter(toggle).on('mouseleave', removeToolTip);
|
|
281
|
+
tooltip.css('left', input.position().left + 3 + 'px');
|
|
282
|
+
exact.on('click', event => highlightButton(exact, event));
|
|
283
|
+
fuzzy.on('click', event => highlightButton(fuzzy, event));
|
|
284
|
+
})
|
|
285
|
+
.on('mouseleave', function () {
|
|
286
|
+
var inToolTip = false;
|
|
287
|
+
tooltip.on('mouseenter', () => (inToolTip = true));
|
|
288
|
+
toggle.on('mouseenter', () => (inToolTip = true));
|
|
289
|
+
setTimeout(function () {
|
|
290
|
+
if (!inToolTip) {
|
|
291
|
+
removeToolTip();
|
|
292
|
+
}
|
|
293
|
+
}, 250);
|
|
294
|
+
});
|
|
295
|
+
var state = api.state.loaded();
|
|
296
|
+
api.on('stateSaveParams', function (e, settings, data) {
|
|
297
|
+
data._fuzzySearch = {
|
|
298
|
+
active: toggle.attr('blurred'),
|
|
299
|
+
val: input.val()
|
|
300
|
+
};
|
|
301
|
+
});
|
|
302
|
+
if (state !== null && state._fuzzySearch !== undefined) {
|
|
303
|
+
input.val(state._fuzzySearch.val);
|
|
304
|
+
if (state._fuzzySearch.active === 'true' &&
|
|
305
|
+
state.start &&
|
|
306
|
+
state.length) {
|
|
307
|
+
toggle.trigger('click');
|
|
308
|
+
api.page(state.start / state.length).draw('page');
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
api.on('search', function () {
|
|
313
|
+
if (!fromPlugin) {
|
|
314
|
+
input.val(api.search() !== searchVal
|
|
315
|
+
? api.search()
|
|
316
|
+
: fuzzySearchVal);
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
// Always add this event no matter if toggling is enabled
|
|
320
|
+
input.on('input keydown', triggerSearchFunction);
|
|
321
|
+
});
|
|
322
|
+
function levenshtein(__this, that, limit) {
|
|
323
|
+
var thisLength = __this.length, thatLength = that.length, matrix = [];
|
|
324
|
+
// If the limit is not defined it will be calculate from this and that args.
|
|
325
|
+
limit = (limit || (thatLength > thisLength ? thatLength : thisLength)) + 1;
|
|
326
|
+
for (var i = 0; i < limit; i++) {
|
|
327
|
+
matrix[i] = [i];
|
|
328
|
+
matrix[i].length = limit;
|
|
329
|
+
}
|
|
330
|
+
for (i = 0; i < limit; i++) {
|
|
331
|
+
matrix[0][i] = i;
|
|
332
|
+
}
|
|
333
|
+
if (Math.abs(thisLength - thatLength) > (limit || 100)) {
|
|
334
|
+
return prepare(limit || 100);
|
|
335
|
+
}
|
|
336
|
+
if (thisLength === 0) {
|
|
337
|
+
return prepare(thatLength);
|
|
338
|
+
}
|
|
339
|
+
if (thatLength === 0) {
|
|
340
|
+
return prepare(thisLength);
|
|
341
|
+
}
|
|
342
|
+
// Calculate matrix.
|
|
343
|
+
var j, this_i, that_j, cost, min, t;
|
|
344
|
+
for (i = 1; i <= thisLength; ++i) {
|
|
345
|
+
this_i = __this[i - 1];
|
|
346
|
+
// Step 4
|
|
347
|
+
for (j = 1; j <= thatLength; ++j) {
|
|
348
|
+
// Check the jagged ld total so far
|
|
349
|
+
if (i === j && matrix[i][j] > 4)
|
|
350
|
+
return prepare(thisLength);
|
|
351
|
+
that_j = that[j - 1];
|
|
352
|
+
cost = this_i === that_j ? 0 : 1; // Step 5
|
|
353
|
+
// Calculate the minimum (much faster than Math.min(...)).
|
|
354
|
+
min = matrix[i - 1][j] + 1; // Devarion.
|
|
355
|
+
if ((t = matrix[i][j - 1] + 1) < min)
|
|
356
|
+
min = t; // Insertion.
|
|
357
|
+
if ((t = matrix[i - 1][j - 1] + cost) < min)
|
|
358
|
+
min = t; // Substitution.
|
|
359
|
+
// Update matrix.
|
|
360
|
+
matrix[i][j] =
|
|
361
|
+
i > 1 &&
|
|
362
|
+
j > 1 &&
|
|
363
|
+
this_i === that[j - 2] &&
|
|
364
|
+
__this[i - 2] === that_j &&
|
|
365
|
+
(t = matrix[i - 2][j - 2] + cost) < min
|
|
366
|
+
? t
|
|
367
|
+
: min; // Transposition.
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
return prepare(matrix[thisLength][thatLength]);
|
|
371
|
+
function prepare(steps) {
|
|
372
|
+
var length = Math.max(thisLength, thatLength);
|
|
373
|
+
var relative = length === 0 ? 0 : steps / length;
|
|
374
|
+
var similarity = 1 - relative;
|
|
375
|
+
return {
|
|
376
|
+
steps: steps,
|
|
377
|
+
relative: relative,
|
|
378
|
+
similarity: similarity
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
function fuzzySearch(searchVal, data, initial) {
|
|
383
|
+
var x, y, i;
|
|
384
|
+
// If no searchVal has been defined then return all rows.
|
|
385
|
+
if (searchVal === undefined || searchVal.length === 0) {
|
|
386
|
+
return {
|
|
387
|
+
pass: true,
|
|
388
|
+
score: ''
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
var columns = initial.columns !== undefined ? initial.columns : null;
|
|
392
|
+
var threshold = initial.threshold !== undefined ? initial.threshold : 0.5;
|
|
393
|
+
// Split the searchVal into individual words.
|
|
394
|
+
var splitSearch = searchVal.split(/ /g);
|
|
395
|
+
// Array to keep scores in
|
|
396
|
+
var highestCollated = [];
|
|
397
|
+
// Remove any empty words or spaces
|
|
398
|
+
for (x = 0; x < splitSearch.length; x++) {
|
|
399
|
+
if (splitSearch[x].length === 0 || splitSearch[x] === ' ') {
|
|
400
|
+
splitSearch.splice(x, 1);
|
|
401
|
+
x--;
|
|
402
|
+
}
|
|
403
|
+
// Aside - Add to the score collection if not done so yet for this search word
|
|
404
|
+
else if (highestCollated.length < splitSearch.length) {
|
|
405
|
+
highestCollated.push({ pass: false, score: 0 });
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
// Going to check each cell for potential matches
|
|
409
|
+
for (i = 0; i < data.length; i++) {
|
|
410
|
+
if (columns === null || columns.includes(i)) {
|
|
411
|
+
// Convert all data points to lower case fo insensitive sorting
|
|
412
|
+
data[i] = data[i].toLowerCase();
|
|
413
|
+
// Split the data into individual words
|
|
414
|
+
var splitData = data[i].split(/ /g);
|
|
415
|
+
// Remove any empty words or spaces
|
|
416
|
+
for (y = 0; y < splitData.length; y++) {
|
|
417
|
+
if (splitData[y].length === 0 || splitData[y] === ' ') {
|
|
418
|
+
splitData.splice(y, 1);
|
|
419
|
+
x--;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
// Check each search term word
|
|
423
|
+
for (x = 0; x < splitSearch.length; x++) {
|
|
424
|
+
// Reset highest score
|
|
425
|
+
var highest = {
|
|
426
|
+
pass: undefined,
|
|
427
|
+
score: 0
|
|
428
|
+
};
|
|
429
|
+
// Against each word in the cell
|
|
430
|
+
for (y = 0; y < splitData.length; y++) {
|
|
431
|
+
// If this search Term word is the beginning of the word in the cell we want to pass this word
|
|
432
|
+
if (splitData[y].indexOf(splitSearch[x]) === 0) {
|
|
433
|
+
var newScore = splitSearch[x].length / splitData[y].length;
|
|
434
|
+
highest = {
|
|
435
|
+
pass: true,
|
|
436
|
+
score: highest.score < newScore
|
|
437
|
+
? newScore
|
|
438
|
+
: highest.score
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
// Get the levenshtein similarity score for the two words
|
|
442
|
+
var steps = levenshtein(splitSearch[x], splitData[y]).similarity;
|
|
443
|
+
// If the levenshtein similarity score is better than a previous one for the search word then var's store it
|
|
444
|
+
if (steps > highest.score) {
|
|
445
|
+
highest.score = steps;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
// If this cell has a higher scoring word than previously found to the search term in the row, store it
|
|
449
|
+
if (highestCollated[x].score < highest.score || highest.pass) {
|
|
450
|
+
highestCollated[x] = {
|
|
451
|
+
pass: highest.pass || highestCollated[x].pass
|
|
452
|
+
? true
|
|
453
|
+
: highest.score > threshold,
|
|
454
|
+
score: highest.score
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
// Check that all of the search words have passed
|
|
461
|
+
for (i = 0; i < highestCollated.length; i++) {
|
|
462
|
+
if (!highestCollated[i].pass) {
|
|
463
|
+
return {
|
|
464
|
+
pass: false,
|
|
465
|
+
score: Math.round((highestCollated.reduce((a, b) => a + b.score, 0) /
|
|
466
|
+
highestCollated.length) *
|
|
467
|
+
100) + '%'
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
// If we get to here, all scores greater than 0.5 so display the row
|
|
472
|
+
return {
|
|
473
|
+
pass: true,
|
|
474
|
+
score: Math.round((highestCollated.reduce((a, b) => a + b.score, 0) /
|
|
475
|
+
highestCollated.length) *
|
|
476
|
+
100) + '%'
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
return DataTable;
|
|
482
|
+
}));
|
|
File without changes
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Fuzzy Search 3.0.0-beta.2 for DataTables
|
|
3
|
+
* SpryMedia Ltd - datatables.net/license MIT license
|
|
4
|
+
*
|
|
5
|
+
* Damerau-Levenshtein function courtesy of https://github.com/tad-lispy/node-damerau-levenshtein
|
|
6
|
+
* BSD 2-Clause License
|
|
7
|
+
* Copyright (c) 2018, Tadeusz Łazurski
|
|
8
|
+
* All rights reserved.
|
|
9
|
+
*/
|
|
10
|
+
(t=>{var r;"function"==typeof define&&define.amd?define(["datatables.net"],function(e){return t(window,document,e)}):"object"==typeof exports?(r=function(e){e.DataTable||require("datatables.net")(e)},"undefined"==typeof window?module.exports=function(e){return e=e||window,r(e),t(0,e.document,e.DataTable)}:(r(window),module.exports=t(window,window.document,window.DataTable))):t(window,document,window.DataTable)})(function(e,t,z){var x=z.Dom,n=z.util;function y(e,t,r){var o;if(void 0===e||0===e.length)return{pass:!0,score:""};for(var n=void 0!==r.columns?r.columns:null,a=void 0!==r.threshold?r.threshold:.5,s=e.split(/ /g),i=[],c=0;c<s.length;c++)0===s[c].length||" "===s[c]?(s.splice(c,1),c--):i.length<s.length&&i.push({pass:!1,score:0});for(o=0;o<t.length;o++)if(null===n||n.includes(o)){t[o]=t[o].toLowerCase();var l=t[o].split(/ /g);for(d=0;d<l.length;d++)0!==l[d].length&&" "!==l[d]||(l.splice(d,1),c--);for(c=0;c<s.length;c++){for(var u={pass:void 0,score:0},d=0;d<l.length;d++){0===l[d].indexOf(s[c])&&(h=s[c].length/l[d].length,u={pass:!0,score:u.score<h?h:u.score});var h=((e,t,r)=>{var o=e.length,n=t.length,a=[];r=(r||(o<n?n:o))+1;for(var s,i,c,l,u,d,h=0;h<r;h++)a[h]=[h],a[h].length=r;for(h=0;h<r;h++)a[0][h]=h;if(Math.abs(o-n)>(r||100))return f(r||100);if(0===o)return f(n);if(0===n)return f(o);for(h=1;h<=o;++h)for(i=e[h-1],s=1;s<=n;++s){if(h===s&&4<a[h][s])return f(o);c=t[s-1],u=a[h-1][s]+1,(d=a[h][s-1]+1)<u&&(u=d),(d=a[h-1][s-1]+(l=i===c?0:1))<u&&(u=d),a[h][s]=1<h&&1<s&&i===t[s-2]&&e[h-2]===c&&(d=a[h-2][s-2]+l)<u?d:u}return f(a[o][n]);function f(e){var t=Math.max(o,n),t=0===t?0:e/t;return{steps:e,relative:t,similarity:1-t}}})(s[c],l[d]).similarity;h>u.score&&(u.score=h)}(i[c].score<u.score||u.pass)&&(i[c]={pass:!(!u.pass&&!i[c].pass)||u.score>a,score:u.score})}}for(o=0;o<i.length;o++)if(!i[o].pass)return{pass:!1,score:Math.round(i.reduce((e,t)=>e+t.score,0)/i.length*100)+"%"};return{pass:!0,score:Math.round(i.reduce((e,t)=>e+t.score,0)/i.length*100)+"%"}}return z.ext.search.push(function(e,t,r){var o=e.init.fuzzySearch,e=e.data[r];if(o&&e){if(void 0!==e._fuzzySearch)return r=e._fuzzySearch.score,n.is.plainObject(o)&&void 0!==o.rankColumn&&(e.cells[o.rankColumn].innerHTML=r,e.orderCache)&&(e.orderCache[o.rankColumn]=+r.substring(0,r.length-1)),e._fuzzySearch.pass;n.is.plainObject(o)&&void 0!==o.rankColumn&&(e.cells[o.rankColumn].innerHTML="",e.orderCache)&&(e.orderCache[o.rankColumn]="")}return!0}),x.s(t).on("init.dt",function(e,t){var r,o,n,a,s,i,c,l,u,d,h,f,p=new z.Api(t),g=p.init(),v=g.fuzzySearch;function m(e){(s.attr("blurred")?(s.css({filter:"blur(0px)"}).attrRemove("blurred"),l.attrRemove("highlighted").css(a),c):(s.css({filter:"blur(1px)"}).attr("blurred",!0),c.attrRemove("highlighted").css(a),l)).attr("highlighted",!0).css(n),h(e)}function b(e,t){e.attr("highlighted")||m(t)}function w(){i.remove()}v&&("object"==typeof v&&v.columns&&(v.columns=p.columns(v.columns).indexes().toArray()),r=!1,o=x.s(p.table().container()).find("div.dt-search input"),n={"font-weight":"600","background-color":"rgba(255,255,255,0.1)"},a={"font-weight":"500","background-color":"transparent"},f={border:"none",background:"none","font-size":"100%",width:"50%",display:"inline-block",color:"white",cursor:"pointer",padding:"0.5em"},s=x.c("button").classAdd("toggleSearch").text("Abc").css({border:"none",background:"none",position:"relative",right:"33px",top:"0px",cursor:"pointer",color:"#3b5e99","margin-top":"1px"}),!0!==v&&!v.toggleSmart||(s.insertAfter(o),c=x.c("button").classAdd("toggleSearch").text("Exact").insertAfter(o).css(f).css(n).attr("highlighted","true"),l=x.c("button").classAdd("toggleSearch").text("Fuzzy").insertAfter(o).css(f),o.css({"padding-right":"30px"}),x.s(o.parent()).css("right","-33px").css("position","relative"),f=x.c("div").text("Search Type").css({"padding-bottom":"0.5em","font-size":"0.8em"}),i=x.c("div").classAdd("fuzzyToolTip").css({position:"absolute",top:"2em",background:"white","border-radius":"4px","text-align":"center",padding:"0.5em","background-color":"#16232a","box-shadow":"4px 4px 4px rgba(0, 0, 0, 0.5)",color:"white",transition:"opacity 0.25s","z-index":"30001",width:o.width("outer")-3+"px"}).append(f).append(c).append(l)),x.s(t.table).off("search.dt.DT"),d=u="",h=function(e){("input"!==e.type||void 0!==g.search&&g.search.return)&&"Enter"!==e.key&&"click"!==e.type||(s&&!s.attr("blurred")?(p.rows().iterator("row",function(e,t){e.data[t]._fuzzySearch=void 0},!1),d=o.val(),u=d,r=!0,p.search(d),r=!1,d=""):(u=o.val(),d="",void 0!==u&&0!==u.length&&(u=u.toLowerCase()),p.rows().iterator("row",function(e,t){e.data[t]._fuzzySearch=y(u,null==(e=e.data[t])?void 0:e.searchCellCache,v)},!1),r=!0,p.search(""),o.val(u),r=!1),r=!0,p.draw(),r=!1)},z.Api.register("search.fuzzy()",function(e){return void 0===e?u:(u=e.toLowerCase(),d=p.search(),o.val(u),p.rows().iterator("row",function(e,t){e.data[t]._fuzzySearch=y(u,null==(e=e.data[t])?void 0:e.searchCellCache,v)},!1),this)}),o.off(),s&&(s.on("click",m).on("mouseenter",function(){i.insertAfter(s).on("mouseleave",w),i.css("left",o.position().left+3+"px"),c.on("click",e=>b(c,e)),l.on("click",e=>b(l,e))}).on("mouseleave",w),o.on("mouseenter",function(){i.insertAfter(s).on("mouseleave",w),i.css("left",o.position().left+3+"px"),c.on("click",e=>b(c,e)),l.on("click",e=>b(l,e))}).on("mouseleave",function(){var e=!1;i.on("mouseenter",()=>e=!0),s.on("mouseenter",()=>e=!0),setTimeout(function(){e||w()},250)}),f=p.state.loaded(),p.on("stateSaveParams",function(e,t,r){r._fuzzySearch={active:s.attr("blurred"),val:o.val()}}),null!==f)&&void 0!==f._fuzzySearch&&(o.val(f._fuzzySearch.val),"true"===f._fuzzySearch.active)&&f.start&&f.length&&(s.trigger("click"),p.page(f.start/f.length).draw("page")),p.on("search",function(){r||o.val(p.search()!==d?p.search():u)}),o.on("input keydown",h))}),z});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Fuzzy Search 3.0.0-beta.2 for DataTables
|
|
3
|
+
* SpryMedia Ltd - datatables.net/license MIT license
|
|
4
|
+
*
|
|
5
|
+
* Damerau-Levenshtein function courtesy of https://github.com/tad-lispy/node-damerau-levenshtein
|
|
6
|
+
* BSD 2-Clause License
|
|
7
|
+
* Copyright (c) 2018, Tadeusz Łazurski
|
|
8
|
+
* All rights reserved.
|
|
9
|
+
*/
|
|
10
|
+
import DataTable,{Dom,util}from"datatables.net";function levenshtein(e,t,r){var o=e.length,n=t.length,a=[];r=(r||(o<n?n:o))+1;for(var s,i,c,l,u,h,d=0;d<r;d++)a[d]=[d],a[d].length=r;for(d=0;d<r;d++)a[0][d]=d;if(Math.abs(o-n)>(r||100))return f(r||100);if(0===o)return f(n);if(0===n)return f(o);for(d=1;d<=o;++d)for(i=e[d-1],s=1;s<=n;++s){if(d===s&&4<a[d][s])return f(o);c=t[s-1],u=a[d-1][s]+1,(h=a[d][s-1]+1)<u&&(u=h),(h=a[d-1][s-1]+(l=i===c?0:1))<u&&(u=h),a[d][s]=1<d&&1<s&&i===t[s-2]&&e[d-2]===c&&(h=a[d-2][s-2]+l)<u?h:u}return f(a[o][n]);function f(e){var t=Math.max(o,n),t=0===t?0:e/t;return{steps:e,relative:t,similarity:1-t}}}function fuzzySearch(e,t,r){var o;if(void 0===e||0===e.length)return{pass:!0,score:""};for(var n=void 0!==r.columns?r.columns:null,a=void 0!==r.threshold?r.threshold:.5,s=e.split(/ /g),i=[],c=0;c<s.length;c++)0===s[c].length||" "===s[c]?(s.splice(c,1),c--):i.length<s.length&&i.push({pass:!1,score:0});for(o=0;o<t.length;o++)if(null===n||n.includes(o)){t[o]=t[o].toLowerCase();var l=t[o].split(/ /g);for(h=0;h<l.length;h++)0!==l[h].length&&" "!==l[h]||(l.splice(h,1),c--);for(c=0;c<s.length;c++){for(var u={pass:void 0,score:0},h=0;h<l.length;h++){0===l[h].indexOf(s[c])&&(d=s[c].length/l[h].length,u={pass:!0,score:u.score<d?d:u.score});var d=levenshtein(s[c],l[h]).similarity;d>u.score&&(u.score=d)}(i[c].score<u.score||u.pass)&&(i[c]={pass:!(!u.pass&&!i[c].pass)||u.score>a,score:u.score})}}for(o=0;o<i.length;o++)if(!i[o].pass)return{pass:!1,score:Math.round(i.reduce((e,t)=>e+t.score,0)/i.length*100)+"%"};return{pass:!0,score:Math.round(i.reduce((e,t)=>e+t.score,0)/i.length*100)+"%"}}DataTable.ext.search.push(function(e,t,r){var o=e.init.fuzzySearch,e=e.data[r];if(o&&e){if(void 0!==e._fuzzySearch)return r=e._fuzzySearch.score,util.is.plainObject(o)&&void 0!==o.rankColumn&&(e.cells[o.rankColumn].innerHTML=r,e.orderCache)&&(e.orderCache[o.rankColumn]=+r.substring(0,r.length-1)),e._fuzzySearch.pass;util.is.plainObject(o)&&void 0!==o.rankColumn&&(e.cells[o.rankColumn].innerHTML="",e.orderCache)&&(e.orderCache[o.rankColumn]="")}return!0}),Dom.s(document).on("init.dt",function(e,t){var r,o,n,a,s,i,c,l,u,h,d,f,p=new DataTable.Api(t),g=p.init(),v=g.fuzzySearch;function m(e){(s.attr("blurred")?(s.css({filter:"blur(0px)"}).attrRemove("blurred"),l.attrRemove("highlighted").css(a),c):(s.css({filter:"blur(1px)"}).attr("blurred",!0),c.attrRemove("highlighted").css(a),l)).attr("highlighted",!0).css(n),d(e)}function b(e,t){e.attr("highlighted")||m(t)}function z(){i.remove()}v&&("object"==typeof v&&v.columns&&(v.columns=p.columns(v.columns).indexes().toArray()),r=!1,o=Dom.s(p.table().container()).find("div.dt-search input"),n={"font-weight":"600","background-color":"rgba(255,255,255,0.1)"},a={"font-weight":"500","background-color":"transparent"},f={border:"none",background:"none","font-size":"100%",width:"50%",display:"inline-block",color:"white",cursor:"pointer",padding:"0.5em"},s=Dom.c("button").classAdd("toggleSearch").text("Abc").css({border:"none",background:"none",position:"relative",right:"33px",top:"0px",cursor:"pointer",color:"#3b5e99","margin-top":"1px"}),!0!==v&&!v.toggleSmart||(s.insertAfter(o),c=Dom.c("button").classAdd("toggleSearch").text("Exact").insertAfter(o).css(f).css(n).attr("highlighted","true"),l=Dom.c("button").classAdd("toggleSearch").text("Fuzzy").insertAfter(o).css(f),o.css({"padding-right":"30px"}),Dom.s(o.parent()).css("right","-33px").css("position","relative"),f=Dom.c("div").text("Search Type").css({"padding-bottom":"0.5em","font-size":"0.8em"}),i=Dom.c("div").classAdd("fuzzyToolTip").css({position:"absolute",top:"2em",background:"white","border-radius":"4px","text-align":"center",padding:"0.5em","background-color":"#16232a","box-shadow":"4px 4px 4px rgba(0, 0, 0, 0.5)",color:"white",transition:"opacity 0.25s","z-index":"30001",width:o.width("outer")-3+"px"}).append(f).append(c).append(l)),Dom.s(t.table).off("search.dt.DT"),h=u="",d=function(e){("input"!==e.type||void 0!==g.search&&g.search.return)&&"Enter"!==e.key&&"click"!==e.type||(s&&!s.attr("blurred")?(p.rows().iterator("row",function(e,t){e.data[t]._fuzzySearch=void 0},!1),h=o.val(),u=h,r=!0,p.search(h),r=!1,h=""):(u=o.val(),h="",void 0!==u&&0!==u.length&&(u=u.toLowerCase()),p.rows().iterator("row",function(e,t){e.data[t]._fuzzySearch=fuzzySearch(u,null==(e=e.data[t])?void 0:e.searchCellCache,v)},!1),r=!0,p.search(""),o.val(u),r=!1),r=!0,p.draw(),r=!1)},DataTable.Api.register("search.fuzzy()",function(e){return void 0===e?u:(u=e.toLowerCase(),h=p.search(),o.val(u),p.rows().iterator("row",function(e,t){e.data[t]._fuzzySearch=fuzzySearch(u,null==(e=e.data[t])?void 0:e.searchCellCache,v)},!1),this)}),o.off(),s&&(s.on("click",m).on("mouseenter",function(){i.insertAfter(s).on("mouseleave",z),i.css("left",o.position().left+3+"px"),c.on("click",e=>b(c,e)),l.on("click",e=>b(l,e))}).on("mouseleave",z),o.on("mouseenter",function(){i.insertAfter(s).on("mouseleave",z),i.css("left",o.position().left+3+"px"),c.on("click",e=>b(c,e)),l.on("click",e=>b(l,e))}).on("mouseleave",function(){var e=!1;i.on("mouseenter",()=>e=!0),s.on("mouseenter",()=>e=!0),setTimeout(function(){e||z()},250)}),f=p.state.loaded(),p.on("stateSaveParams",function(e,t,r){r._fuzzySearch={active:s.attr("blurred"),val:o.val()}}),null!==f)&&void 0!==f._fuzzySearch&&(o.val(f._fuzzySearch.val),"true"===f._fuzzySearch.active)&&f.start&&f.length&&(s.trigger("click"),p.page(f.start/f.length).draw("page")),p.on("search",function(){r||o.val(p.search()!==h?p.search():u)}),o.on("input keydown",d))});export default DataTable;
|
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Fuzzy Search 3.0.0-beta.2 for DataTables
|
|
3
|
+
* SpryMedia Ltd - datatables.net/license MIT license
|
|
4
|
+
*
|
|
5
|
+
* Damerau-Levenshtein function courtesy of https://github.com/tad-lispy/node-damerau-levenshtein
|
|
6
|
+
* BSD 2-Clause License
|
|
7
|
+
* Copyright (c) 2018, Tadeusz Łazurski
|
|
8
|
+
* All rights reserved.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import DataTable, { Dom, util } from 'datatables.net';
|
|
12
|
+
|
|
13
|
+
DataTable.ext.search.push(function (settings, data, dataIndex) {
|
|
14
|
+
let initial = settings.init.fuzzySearch;
|
|
15
|
+
let row = settings.data[dataIndex];
|
|
16
|
+
if (!initial) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
if (row) {
|
|
20
|
+
// If fuzzy searching has not been implemented then pass all rows for this function
|
|
21
|
+
if (row._fuzzySearch !== undefined) {
|
|
22
|
+
// Read score to set the cell content and sort data
|
|
23
|
+
var score = row._fuzzySearch.score;
|
|
24
|
+
if (util.is.plainObject(initial) &&
|
|
25
|
+
initial.rankColumn !== undefined) {
|
|
26
|
+
row.cells[initial.rankColumn].innerHTML = score;
|
|
27
|
+
// Remove '%' from the end of the score so can sort on a number
|
|
28
|
+
if (row.orderCache) {
|
|
29
|
+
row.orderCache[initial.rankColumn] = +score.substring(0, score.length - 1);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// Return the value for the pass as decided by the fuzzySearch function
|
|
33
|
+
return row._fuzzySearch.pass;
|
|
34
|
+
}
|
|
35
|
+
else if (util.is.plainObject(initial) &&
|
|
36
|
+
initial.rankColumn !== undefined) {
|
|
37
|
+
row.cells[initial.rankColumn].innerHTML = '';
|
|
38
|
+
if (row.orderCache) {
|
|
39
|
+
row.orderCache[initial.rankColumn] = '';
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return true;
|
|
44
|
+
});
|
|
45
|
+
Dom.s(document).on('init.dt', function (e, settings) {
|
|
46
|
+
var api = new DataTable.Api(settings);
|
|
47
|
+
var initial = api.init();
|
|
48
|
+
var initialFuzzy = initial.fuzzySearch;
|
|
49
|
+
// If this is not set then fuzzy searching is not enabled on the table so return.
|
|
50
|
+
if (!initialFuzzy) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (typeof initialFuzzy === 'object' && initialFuzzy.columns) {
|
|
54
|
+
initialFuzzy.columns = api
|
|
55
|
+
.columns(initialFuzzy.columns)
|
|
56
|
+
.indexes()
|
|
57
|
+
.toArray();
|
|
58
|
+
}
|
|
59
|
+
var fromPlugin = false;
|
|
60
|
+
// Find the input element
|
|
61
|
+
var input = Dom.s(api.table().container()).find('div.dt-search input');
|
|
62
|
+
var fontBold = {
|
|
63
|
+
'font-weight': '600',
|
|
64
|
+
'background-color': 'rgba(255,255,255,0.1)'
|
|
65
|
+
};
|
|
66
|
+
var fontNormal = {
|
|
67
|
+
'font-weight': '500',
|
|
68
|
+
'background-color': 'transparent'
|
|
69
|
+
};
|
|
70
|
+
var toggleCSS = {
|
|
71
|
+
border: 'none',
|
|
72
|
+
background: 'none',
|
|
73
|
+
'font-size': '100%',
|
|
74
|
+
width: '50%',
|
|
75
|
+
display: 'inline-block',
|
|
76
|
+
color: 'white',
|
|
77
|
+
cursor: 'pointer',
|
|
78
|
+
padding: '0.5em'
|
|
79
|
+
};
|
|
80
|
+
// Only going to set the toggle if it is enabled
|
|
81
|
+
var toggle = Dom.c('button').classAdd('toggleSearch').text('Abc').css({
|
|
82
|
+
border: 'none',
|
|
83
|
+
background: 'none',
|
|
84
|
+
position: 'relative',
|
|
85
|
+
right: '33px',
|
|
86
|
+
top: '0px',
|
|
87
|
+
cursor: 'pointer',
|
|
88
|
+
color: '#3b5e99',
|
|
89
|
+
'margin-top': '1px'
|
|
90
|
+
});
|
|
91
|
+
var tooltip, exact, fuzzy, label;
|
|
92
|
+
if (initialFuzzy === true || initialFuzzy.toggleSmart) {
|
|
93
|
+
toggle.insertAfter(input);
|
|
94
|
+
exact = Dom.c('button')
|
|
95
|
+
.classAdd('toggleSearch')
|
|
96
|
+
.text('Exact')
|
|
97
|
+
.insertAfter(input)
|
|
98
|
+
.css(toggleCSS)
|
|
99
|
+
.css(fontBold)
|
|
100
|
+
.attr('highlighted', 'true');
|
|
101
|
+
fuzzy = Dom.c('button')
|
|
102
|
+
.classAdd('toggleSearch')
|
|
103
|
+
.text('Fuzzy')
|
|
104
|
+
.insertAfter(input)
|
|
105
|
+
.css(toggleCSS);
|
|
106
|
+
input.css({
|
|
107
|
+
'padding-right': '30px'
|
|
108
|
+
});
|
|
109
|
+
Dom.s(input.parent()).css('right', '-33px').css('position', 'relative');
|
|
110
|
+
label = Dom.c('div').text('Search Type').css({
|
|
111
|
+
'padding-bottom': '0.5em',
|
|
112
|
+
'font-size': '0.8em'
|
|
113
|
+
});
|
|
114
|
+
tooltip = Dom.c('div')
|
|
115
|
+
.classAdd('fuzzyToolTip')
|
|
116
|
+
.css({
|
|
117
|
+
position: 'absolute',
|
|
118
|
+
top: '2em',
|
|
119
|
+
background: 'white',
|
|
120
|
+
'border-radius': '4px',
|
|
121
|
+
'text-align': 'center',
|
|
122
|
+
padding: '0.5em',
|
|
123
|
+
'background-color': '#16232a',
|
|
124
|
+
'box-shadow': '4px 4px 4px rgba(0, 0, 0, 0.5)',
|
|
125
|
+
color: 'white',
|
|
126
|
+
transition: 'opacity 0.25s',
|
|
127
|
+
'z-index': '30001',
|
|
128
|
+
width: input.width('outer') - 3 + 'px'
|
|
129
|
+
})
|
|
130
|
+
.append(label)
|
|
131
|
+
.append(exact)
|
|
132
|
+
.append(fuzzy);
|
|
133
|
+
}
|
|
134
|
+
function toggleFuzzy(event) {
|
|
135
|
+
if (toggle.attr('blurred')) {
|
|
136
|
+
toggle.css({ filter: 'blur(0px)' }).attrRemove('blurred');
|
|
137
|
+
fuzzy.attrRemove('highlighted').css(fontNormal);
|
|
138
|
+
exact.attr('highlighted', true).css(fontBold);
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
toggle.css({ filter: 'blur(1px)' }).attr('blurred', true);
|
|
142
|
+
exact.attrRemove('highlighted').css(fontNormal);
|
|
143
|
+
fuzzy.attr('highlighted', true).css(fontBold);
|
|
144
|
+
}
|
|
145
|
+
// Whenever the search mode is changed we need to re-search
|
|
146
|
+
triggerSearchFunction(event);
|
|
147
|
+
}
|
|
148
|
+
// Highlights one of the buttons in the tooltip and un-highlights the other
|
|
149
|
+
function highlightButton(toHighlight, event) {
|
|
150
|
+
if (!toHighlight.attr('highlighted')) {
|
|
151
|
+
toggleFuzzy(event);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Removes the tooltip element
|
|
155
|
+
function removeToolTip() {
|
|
156
|
+
tooltip.remove();
|
|
157
|
+
}
|
|
158
|
+
// Turn off the default datatables searching events
|
|
159
|
+
Dom.s(settings.table).off('search.dt.DT');
|
|
160
|
+
var fuzzySearchVal = '';
|
|
161
|
+
var searchVal = '';
|
|
162
|
+
// The function that we want to run on search
|
|
163
|
+
var triggerSearchFunction = function (event) {
|
|
164
|
+
// If the search is only to be triggered on return wait for that
|
|
165
|
+
if ((event.type === 'input' &&
|
|
166
|
+
(initial.search === undefined ||
|
|
167
|
+
!initial.search.return)) ||
|
|
168
|
+
event.key === 'Enter' ||
|
|
169
|
+
event.type === 'click') {
|
|
170
|
+
// If the toggle is set and isn't checkd then perform a normal search
|
|
171
|
+
if (toggle && !toggle.attr('blurred')) {
|
|
172
|
+
api.rows().iterator('row', function (settings, rowIdx) {
|
|
173
|
+
settings.data[rowIdx]._fuzzySearch = undefined;
|
|
174
|
+
}, false);
|
|
175
|
+
searchVal = input.val();
|
|
176
|
+
fuzzySearchVal = searchVal;
|
|
177
|
+
fromPlugin = true;
|
|
178
|
+
api.search(searchVal);
|
|
179
|
+
fromPlugin = false;
|
|
180
|
+
searchVal = '';
|
|
181
|
+
}
|
|
182
|
+
// Otherwise perform a fuzzy search
|
|
183
|
+
else {
|
|
184
|
+
// Get the value from the input element and convert to lower case
|
|
185
|
+
fuzzySearchVal = input.val();
|
|
186
|
+
searchVal = '';
|
|
187
|
+
if (fuzzySearchVal !== undefined &&
|
|
188
|
+
fuzzySearchVal.length !== 0) {
|
|
189
|
+
fuzzySearchVal = fuzzySearchVal.toLowerCase();
|
|
190
|
+
}
|
|
191
|
+
// For each row call the fuzzy search function to get result
|
|
192
|
+
api.rows().iterator('row', function (settings, rowIdx) {
|
|
193
|
+
var _a;
|
|
194
|
+
settings.data[rowIdx]._fuzzySearch = fuzzySearch(fuzzySearchVal, (_a = settings.data[rowIdx]) === null || _a === void 0 ? void 0 : _a.searchCellCache, initialFuzzy);
|
|
195
|
+
}, false);
|
|
196
|
+
fromPlugin = true;
|
|
197
|
+
// Empty the datatables search and replace it with our own
|
|
198
|
+
api.search('');
|
|
199
|
+
input.val(fuzzySearchVal);
|
|
200
|
+
fromPlugin = false;
|
|
201
|
+
}
|
|
202
|
+
fromPlugin = true;
|
|
203
|
+
api.draw();
|
|
204
|
+
fromPlugin = false;
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
DataTable.Api.register('search.fuzzy()', function (value) {
|
|
208
|
+
if (value === undefined) {
|
|
209
|
+
return fuzzySearchVal;
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
fuzzySearchVal = value.toLowerCase();
|
|
213
|
+
searchVal = api.search();
|
|
214
|
+
input.val(fuzzySearchVal);
|
|
215
|
+
// For each row call the fuzzy search function to get result
|
|
216
|
+
api.rows().iterator('row', function (settings, rowIdx) {
|
|
217
|
+
var _a;
|
|
218
|
+
settings.data[rowIdx]._fuzzySearch = fuzzySearch(fuzzySearchVal, (_a = settings.data[rowIdx]) === null || _a === void 0 ? void 0 : _a.searchCellCache, initialFuzzy);
|
|
219
|
+
}, false);
|
|
220
|
+
// triggerSearchFunction({key: 'Enter'});
|
|
221
|
+
return this;
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
input.off();
|
|
225
|
+
// Set listeners to occur on toggle and typing
|
|
226
|
+
if (toggle) {
|
|
227
|
+
// Actions for the toggle button
|
|
228
|
+
toggle
|
|
229
|
+
.on('click', toggleFuzzy)
|
|
230
|
+
.on('mouseenter', function () {
|
|
231
|
+
tooltip.insertAfter(toggle).on('mouseleave', removeToolTip);
|
|
232
|
+
tooltip.css('left', input.position().left + 3 + 'px');
|
|
233
|
+
exact.on('click', event => highlightButton(exact, event));
|
|
234
|
+
fuzzy.on('click', event => highlightButton(fuzzy, event));
|
|
235
|
+
})
|
|
236
|
+
.on('mouseleave', removeToolTip);
|
|
237
|
+
// Actions for the input element
|
|
238
|
+
input
|
|
239
|
+
.on('mouseenter', function () {
|
|
240
|
+
tooltip.insertAfter(toggle).on('mouseleave', removeToolTip);
|
|
241
|
+
tooltip.css('left', input.position().left + 3 + 'px');
|
|
242
|
+
exact.on('click', event => highlightButton(exact, event));
|
|
243
|
+
fuzzy.on('click', event => highlightButton(fuzzy, event));
|
|
244
|
+
})
|
|
245
|
+
.on('mouseleave', function () {
|
|
246
|
+
var inToolTip = false;
|
|
247
|
+
tooltip.on('mouseenter', () => (inToolTip = true));
|
|
248
|
+
toggle.on('mouseenter', () => (inToolTip = true));
|
|
249
|
+
setTimeout(function () {
|
|
250
|
+
if (!inToolTip) {
|
|
251
|
+
removeToolTip();
|
|
252
|
+
}
|
|
253
|
+
}, 250);
|
|
254
|
+
});
|
|
255
|
+
var state = api.state.loaded();
|
|
256
|
+
api.on('stateSaveParams', function (e, settings, data) {
|
|
257
|
+
data._fuzzySearch = {
|
|
258
|
+
active: toggle.attr('blurred'),
|
|
259
|
+
val: input.val()
|
|
260
|
+
};
|
|
261
|
+
});
|
|
262
|
+
if (state !== null && state._fuzzySearch !== undefined) {
|
|
263
|
+
input.val(state._fuzzySearch.val);
|
|
264
|
+
if (state._fuzzySearch.active === 'true' &&
|
|
265
|
+
state.start &&
|
|
266
|
+
state.length) {
|
|
267
|
+
toggle.trigger('click');
|
|
268
|
+
api.page(state.start / state.length).draw('page');
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
api.on('search', function () {
|
|
273
|
+
if (!fromPlugin) {
|
|
274
|
+
input.val(api.search() !== searchVal
|
|
275
|
+
? api.search()
|
|
276
|
+
: fuzzySearchVal);
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
// Always add this event no matter if toggling is enabled
|
|
280
|
+
input.on('input keydown', triggerSearchFunction);
|
|
281
|
+
});
|
|
282
|
+
function levenshtein(__this, that, limit) {
|
|
283
|
+
var thisLength = __this.length, thatLength = that.length, matrix = [];
|
|
284
|
+
// If the limit is not defined it will be calculate from this and that args.
|
|
285
|
+
limit = (limit || (thatLength > thisLength ? thatLength : thisLength)) + 1;
|
|
286
|
+
for (var i = 0; i < limit; i++) {
|
|
287
|
+
matrix[i] = [i];
|
|
288
|
+
matrix[i].length = limit;
|
|
289
|
+
}
|
|
290
|
+
for (i = 0; i < limit; i++) {
|
|
291
|
+
matrix[0][i] = i;
|
|
292
|
+
}
|
|
293
|
+
if (Math.abs(thisLength - thatLength) > (limit || 100)) {
|
|
294
|
+
return prepare(limit || 100);
|
|
295
|
+
}
|
|
296
|
+
if (thisLength === 0) {
|
|
297
|
+
return prepare(thatLength);
|
|
298
|
+
}
|
|
299
|
+
if (thatLength === 0) {
|
|
300
|
+
return prepare(thisLength);
|
|
301
|
+
}
|
|
302
|
+
// Calculate matrix.
|
|
303
|
+
var j, this_i, that_j, cost, min, t;
|
|
304
|
+
for (i = 1; i <= thisLength; ++i) {
|
|
305
|
+
this_i = __this[i - 1];
|
|
306
|
+
// Step 4
|
|
307
|
+
for (j = 1; j <= thatLength; ++j) {
|
|
308
|
+
// Check the jagged ld total so far
|
|
309
|
+
if (i === j && matrix[i][j] > 4)
|
|
310
|
+
return prepare(thisLength);
|
|
311
|
+
that_j = that[j - 1];
|
|
312
|
+
cost = this_i === that_j ? 0 : 1; // Step 5
|
|
313
|
+
// Calculate the minimum (much faster than Math.min(...)).
|
|
314
|
+
min = matrix[i - 1][j] + 1; // Devarion.
|
|
315
|
+
if ((t = matrix[i][j - 1] + 1) < min)
|
|
316
|
+
min = t; // Insertion.
|
|
317
|
+
if ((t = matrix[i - 1][j - 1] + cost) < min)
|
|
318
|
+
min = t; // Substitution.
|
|
319
|
+
// Update matrix.
|
|
320
|
+
matrix[i][j] =
|
|
321
|
+
i > 1 &&
|
|
322
|
+
j > 1 &&
|
|
323
|
+
this_i === that[j - 2] &&
|
|
324
|
+
__this[i - 2] === that_j &&
|
|
325
|
+
(t = matrix[i - 2][j - 2] + cost) < min
|
|
326
|
+
? t
|
|
327
|
+
: min; // Transposition.
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return prepare(matrix[thisLength][thatLength]);
|
|
331
|
+
function prepare(steps) {
|
|
332
|
+
var length = Math.max(thisLength, thatLength);
|
|
333
|
+
var relative = length === 0 ? 0 : steps / length;
|
|
334
|
+
var similarity = 1 - relative;
|
|
335
|
+
return {
|
|
336
|
+
steps: steps,
|
|
337
|
+
relative: relative,
|
|
338
|
+
similarity: similarity
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
function fuzzySearch(searchVal, data, initial) {
|
|
343
|
+
var x, y, i;
|
|
344
|
+
// If no searchVal has been defined then return all rows.
|
|
345
|
+
if (searchVal === undefined || searchVal.length === 0) {
|
|
346
|
+
return {
|
|
347
|
+
pass: true,
|
|
348
|
+
score: ''
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
var columns = initial.columns !== undefined ? initial.columns : null;
|
|
352
|
+
var threshold = initial.threshold !== undefined ? initial.threshold : 0.5;
|
|
353
|
+
// Split the searchVal into individual words.
|
|
354
|
+
var splitSearch = searchVal.split(/ /g);
|
|
355
|
+
// Array to keep scores in
|
|
356
|
+
var highestCollated = [];
|
|
357
|
+
// Remove any empty words or spaces
|
|
358
|
+
for (x = 0; x < splitSearch.length; x++) {
|
|
359
|
+
if (splitSearch[x].length === 0 || splitSearch[x] === ' ') {
|
|
360
|
+
splitSearch.splice(x, 1);
|
|
361
|
+
x--;
|
|
362
|
+
}
|
|
363
|
+
// Aside - Add to the score collection if not done so yet for this search word
|
|
364
|
+
else if (highestCollated.length < splitSearch.length) {
|
|
365
|
+
highestCollated.push({ pass: false, score: 0 });
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
// Going to check each cell for potential matches
|
|
369
|
+
for (i = 0; i < data.length; i++) {
|
|
370
|
+
if (columns === null || columns.includes(i)) {
|
|
371
|
+
// Convert all data points to lower case fo insensitive sorting
|
|
372
|
+
data[i] = data[i].toLowerCase();
|
|
373
|
+
// Split the data into individual words
|
|
374
|
+
var splitData = data[i].split(/ /g);
|
|
375
|
+
// Remove any empty words or spaces
|
|
376
|
+
for (y = 0; y < splitData.length; y++) {
|
|
377
|
+
if (splitData[y].length === 0 || splitData[y] === ' ') {
|
|
378
|
+
splitData.splice(y, 1);
|
|
379
|
+
x--;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
// Check each search term word
|
|
383
|
+
for (x = 0; x < splitSearch.length; x++) {
|
|
384
|
+
// Reset highest score
|
|
385
|
+
var highest = {
|
|
386
|
+
pass: undefined,
|
|
387
|
+
score: 0
|
|
388
|
+
};
|
|
389
|
+
// Against each word in the cell
|
|
390
|
+
for (y = 0; y < splitData.length; y++) {
|
|
391
|
+
// If this search Term word is the beginning of the word in the cell we want to pass this word
|
|
392
|
+
if (splitData[y].indexOf(splitSearch[x]) === 0) {
|
|
393
|
+
var newScore = splitSearch[x].length / splitData[y].length;
|
|
394
|
+
highest = {
|
|
395
|
+
pass: true,
|
|
396
|
+
score: highest.score < newScore
|
|
397
|
+
? newScore
|
|
398
|
+
: highest.score
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
// Get the levenshtein similarity score for the two words
|
|
402
|
+
var steps = levenshtein(splitSearch[x], splitData[y]).similarity;
|
|
403
|
+
// If the levenshtein similarity score is better than a previous one for the search word then var's store it
|
|
404
|
+
if (steps > highest.score) {
|
|
405
|
+
highest.score = steps;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
// If this cell has a higher scoring word than previously found to the search term in the row, store it
|
|
409
|
+
if (highestCollated[x].score < highest.score || highest.pass) {
|
|
410
|
+
highestCollated[x] = {
|
|
411
|
+
pass: highest.pass || highestCollated[x].pass
|
|
412
|
+
? true
|
|
413
|
+
: highest.score > threshold,
|
|
414
|
+
score: highest.score
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
// Check that all of the search words have passed
|
|
421
|
+
for (i = 0; i < highestCollated.length; i++) {
|
|
422
|
+
if (!highestCollated[i].pass) {
|
|
423
|
+
return {
|
|
424
|
+
pass: false,
|
|
425
|
+
score: Math.round((highestCollated.reduce((a, b) => a + b.score, 0) /
|
|
426
|
+
highestCollated.length) *
|
|
427
|
+
100) + '%'
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
// If we get to here, all scores greater than 0.5 so display the row
|
|
432
|
+
return {
|
|
433
|
+
pass: true,
|
|
434
|
+
score: Math.round((highestCollated.reduce((a, b) => a + b.score, 0) /
|
|
435
|
+
highestCollated.length) *
|
|
436
|
+
100) + '%'
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
export default DataTable;
|
|
442
|
+
|
package/dist/search.d.ts
ADDED
package/dist/search.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
function levenshtein(__this, that, limit) {
|
|
2
|
+
var thisLength = __this.length, thatLength = that.length, matrix = [];
|
|
3
|
+
// If the limit is not defined it will be calculate from this and that args.
|
|
4
|
+
limit = (limit || (thatLength > thisLength ? thatLength : thisLength)) + 1;
|
|
5
|
+
for (var i = 0; i < limit; i++) {
|
|
6
|
+
matrix[i] = [i];
|
|
7
|
+
matrix[i].length = limit;
|
|
8
|
+
}
|
|
9
|
+
for (i = 0; i < limit; i++) {
|
|
10
|
+
matrix[0][i] = i;
|
|
11
|
+
}
|
|
12
|
+
if (Math.abs(thisLength - thatLength) > (limit || 100)) {
|
|
13
|
+
return prepare(limit || 100);
|
|
14
|
+
}
|
|
15
|
+
if (thisLength === 0) {
|
|
16
|
+
return prepare(thatLength);
|
|
17
|
+
}
|
|
18
|
+
if (thatLength === 0) {
|
|
19
|
+
return prepare(thisLength);
|
|
20
|
+
}
|
|
21
|
+
// Calculate matrix.
|
|
22
|
+
var j, this_i, that_j, cost, min, t;
|
|
23
|
+
for (i = 1; i <= thisLength; ++i) {
|
|
24
|
+
this_i = __this[i - 1];
|
|
25
|
+
// Step 4
|
|
26
|
+
for (j = 1; j <= thatLength; ++j) {
|
|
27
|
+
// Check the jagged ld total so far
|
|
28
|
+
if (i === j && matrix[i][j] > 4)
|
|
29
|
+
return prepare(thisLength);
|
|
30
|
+
that_j = that[j - 1];
|
|
31
|
+
cost = this_i === that_j ? 0 : 1; // Step 5
|
|
32
|
+
// Calculate the minimum (much faster than Math.min(...)).
|
|
33
|
+
min = matrix[i - 1][j] + 1; // Devarion.
|
|
34
|
+
if ((t = matrix[i][j - 1] + 1) < min)
|
|
35
|
+
min = t; // Insertion.
|
|
36
|
+
if ((t = matrix[i - 1][j - 1] + cost) < min)
|
|
37
|
+
min = t; // Substitution.
|
|
38
|
+
// Update matrix.
|
|
39
|
+
matrix[i][j] =
|
|
40
|
+
i > 1 &&
|
|
41
|
+
j > 1 &&
|
|
42
|
+
this_i === that[j - 2] &&
|
|
43
|
+
__this[i - 2] === that_j &&
|
|
44
|
+
(t = matrix[i - 2][j - 2] + cost) < min
|
|
45
|
+
? t
|
|
46
|
+
: min; // Transposition.
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return prepare(matrix[thisLength][thatLength]);
|
|
50
|
+
function prepare(steps) {
|
|
51
|
+
var length = Math.max(thisLength, thatLength);
|
|
52
|
+
var relative = length === 0 ? 0 : steps / length;
|
|
53
|
+
var similarity = 1 - relative;
|
|
54
|
+
return {
|
|
55
|
+
steps: steps,
|
|
56
|
+
relative: relative,
|
|
57
|
+
similarity: similarity
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export default function fuzzySearch(searchVal, data, initial) {
|
|
62
|
+
var x, y, i;
|
|
63
|
+
// If no searchVal has been defined then return all rows.
|
|
64
|
+
if (searchVal === undefined || searchVal.length === 0) {
|
|
65
|
+
return {
|
|
66
|
+
pass: true,
|
|
67
|
+
score: ''
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
var columns = initial.columns !== undefined ? initial.columns : null;
|
|
71
|
+
var threshold = initial.threshold !== undefined ? initial.threshold : 0.5;
|
|
72
|
+
// Split the searchVal into individual words.
|
|
73
|
+
var splitSearch = searchVal.split(/ /g);
|
|
74
|
+
// Array to keep scores in
|
|
75
|
+
var highestCollated = [];
|
|
76
|
+
// Remove any empty words or spaces
|
|
77
|
+
for (x = 0; x < splitSearch.length; x++) {
|
|
78
|
+
if (splitSearch[x].length === 0 || splitSearch[x] === ' ') {
|
|
79
|
+
splitSearch.splice(x, 1);
|
|
80
|
+
x--;
|
|
81
|
+
}
|
|
82
|
+
// Aside - Add to the score collection if not done so yet for this search word
|
|
83
|
+
else if (highestCollated.length < splitSearch.length) {
|
|
84
|
+
highestCollated.push({ pass: false, score: 0 });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Going to check each cell for potential matches
|
|
88
|
+
for (i = 0; i < data.length; i++) {
|
|
89
|
+
if (columns === null || columns.includes(i)) {
|
|
90
|
+
// Convert all data points to lower case fo insensitive sorting
|
|
91
|
+
data[i] = data[i].toLowerCase();
|
|
92
|
+
// Split the data into individual words
|
|
93
|
+
var splitData = data[i].split(/ /g);
|
|
94
|
+
// Remove any empty words or spaces
|
|
95
|
+
for (y = 0; y < splitData.length; y++) {
|
|
96
|
+
if (splitData[y].length === 0 || splitData[y] === ' ') {
|
|
97
|
+
splitData.splice(y, 1);
|
|
98
|
+
x--;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Check each search term word
|
|
102
|
+
for (x = 0; x < splitSearch.length; x++) {
|
|
103
|
+
// Reset highest score
|
|
104
|
+
var highest = {
|
|
105
|
+
pass: undefined,
|
|
106
|
+
score: 0
|
|
107
|
+
};
|
|
108
|
+
// Against each word in the cell
|
|
109
|
+
for (y = 0; y < splitData.length; y++) {
|
|
110
|
+
// If this search Term word is the beginning of the word in the cell we want to pass this word
|
|
111
|
+
if (splitData[y].indexOf(splitSearch[x]) === 0) {
|
|
112
|
+
var newScore = splitSearch[x].length / splitData[y].length;
|
|
113
|
+
highest = {
|
|
114
|
+
pass: true,
|
|
115
|
+
score: highest.score < newScore
|
|
116
|
+
? newScore
|
|
117
|
+
: highest.score
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
// Get the levenshtein similarity score for the two words
|
|
121
|
+
var steps = levenshtein(splitSearch[x], splitData[y]).similarity;
|
|
122
|
+
// If the levenshtein similarity score is better than a previous one for the search word then var's store it
|
|
123
|
+
if (steps > highest.score) {
|
|
124
|
+
highest.score = steps;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// If this cell has a higher scoring word than previously found to the search term in the row, store it
|
|
128
|
+
if (highestCollated[x].score < highest.score || highest.pass) {
|
|
129
|
+
highestCollated[x] = {
|
|
130
|
+
pass: highest.pass || highestCollated[x].pass
|
|
131
|
+
? true
|
|
132
|
+
: highest.score > threshold,
|
|
133
|
+
score: highest.score
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// Check that all of the search words have passed
|
|
140
|
+
for (i = 0; i < highestCollated.length; i++) {
|
|
141
|
+
if (!highestCollated[i].pass) {
|
|
142
|
+
return {
|
|
143
|
+
pass: false,
|
|
144
|
+
score: Math.round((highestCollated.reduce((a, b) => a + b.score, 0) /
|
|
145
|
+
highestCollated.length) *
|
|
146
|
+
100) + '%'
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// If we get to here, all scores greater than 0.5 so display the row
|
|
151
|
+
return {
|
|
152
|
+
pass: true,
|
|
153
|
+
score: Math.round((highestCollated.reduce((a, b) => a + b.score, 0) /
|
|
154
|
+
highestCollated.length) *
|
|
155
|
+
100) + '%'
|
|
156
|
+
};
|
|
157
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
|
|
2
|
+
import DataTables, { Api, ColumnSelector } from 'datatables.net';
|
|
3
|
+
|
|
4
|
+
export default DataTables;
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
8
|
+
* DataTables' types integration
|
|
9
|
+
*/
|
|
10
|
+
declare module 'datatables.net' {
|
|
11
|
+
interface Options {
|
|
12
|
+
fuzzySearch?:
|
|
13
|
+
| boolean
|
|
14
|
+
| {
|
|
15
|
+
columns?: ColumnSelector;
|
|
16
|
+
rankColumn?: number;
|
|
17
|
+
threshold?: number;
|
|
18
|
+
toggleSmart?: boolean;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface Row {
|
|
23
|
+
_fuzzySearch?: any;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface StateLoad {
|
|
27
|
+
_fuzzySearch: {
|
|
28
|
+
active: 'true' | 'false';
|
|
29
|
+
val: any;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface ApiSearch<T> {
|
|
34
|
+
fuzzy: (input: string) => Api;
|
|
35
|
+
}
|
|
36
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "datatables.net-feature-fuzzysearch",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "DataTables plugin: Fuzzy search",
|
|
5
|
+
"main": "dist/dataTables.fuzzySearch.js",
|
|
6
|
+
"module": "dist/dataTables.fuzzySearch.mjs",
|
|
7
|
+
"style": "dist/dataTables.fuzzySearch.css",
|
|
8
|
+
"types": "dist/types.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist/*.css",
|
|
11
|
+
"dist/*.js",
|
|
12
|
+
"dist/*.mjs",
|
|
13
|
+
"dist/*.d.ts"
|
|
14
|
+
],
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/DataTables/Plugins.git"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"DataTables",
|
|
21
|
+
"plugins",
|
|
22
|
+
"search"
|
|
23
|
+
],
|
|
24
|
+
"author": "SpryMedia Ltd",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://datatables.net/forums"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://datatables.net",
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"datatables.net": "^3.0.0-0"
|
|
32
|
+
}
|
|
33
|
+
}
|