Mercurial > repos > saskia-hiltemann > ireport
comparison DataTables-1.9.4/media/src/core/core.filter.js @ 0:ac5f9272033b draft
first upload
author | saskia-hiltemann |
---|---|
date | Tue, 01 Jul 2014 11:42:23 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:ac5f9272033b |
---|---|
1 | |
2 | |
3 /** | |
4 * Generate the node required for filtering text | |
5 * @returns {node} Filter control element | |
6 * @param {object} oSettings dataTables settings object | |
7 * @memberof DataTable#oApi | |
8 */ | |
9 function _fnFeatureHtmlFilter ( oSettings ) | |
10 { | |
11 var oPreviousSearch = oSettings.oPreviousSearch; | |
12 | |
13 var sSearchStr = oSettings.oLanguage.sSearch; | |
14 sSearchStr = (sSearchStr.indexOf('_INPUT_') !== -1) ? | |
15 sSearchStr.replace('_INPUT_', '<input type="text" />') : | |
16 sSearchStr==="" ? '<input type="text" />' : sSearchStr+' <input type="text" />'; | |
17 | |
18 var nFilter = document.createElement( 'div' ); | |
19 nFilter.className = oSettings.oClasses.sFilter; | |
20 nFilter.innerHTML = '<label>'+sSearchStr+'</label>'; | |
21 if ( !oSettings.aanFeatures.f ) | |
22 { | |
23 nFilter.id = oSettings.sTableId+'_filter'; | |
24 } | |
25 | |
26 var jqFilter = $('input[type="text"]', nFilter); | |
27 | |
28 // Store a reference to the input element, so other input elements could be | |
29 // added to the filter wrapper if needed (submit button for example) | |
30 nFilter._DT_Input = jqFilter[0]; | |
31 | |
32 jqFilter.val( oPreviousSearch.sSearch.replace('"','"') ); | |
33 jqFilter.bind( 'keyup.DT', function(e) { | |
34 /* Update all other filter input elements for the new display */ | |
35 var n = oSettings.aanFeatures.f; | |
36 var val = this.value==="" ? "" : this.value; // mental IE8 fix :-( | |
37 | |
38 for ( var i=0, iLen=n.length ; i<iLen ; i++ ) | |
39 { | |
40 if ( n[i] != $(this).parents('div.dataTables_filter')[0] ) | |
41 { | |
42 $(n[i]._DT_Input).val( val ); | |
43 } | |
44 } | |
45 | |
46 /* Now do the filter */ | |
47 if ( val != oPreviousSearch.sSearch ) | |
48 { | |
49 _fnFilterComplete( oSettings, { | |
50 "sSearch": val, | |
51 "bRegex": oPreviousSearch.bRegex, | |
52 "bSmart": oPreviousSearch.bSmart , | |
53 "bCaseInsensitive": oPreviousSearch.bCaseInsensitive | |
54 } ); | |
55 } | |
56 } ); | |
57 | |
58 jqFilter | |
59 .attr('aria-controls', oSettings.sTableId) | |
60 .bind( 'keypress.DT', function(e) { | |
61 /* Prevent form submission */ | |
62 if ( e.keyCode == 13 ) | |
63 { | |
64 return false; | |
65 } | |
66 } | |
67 ); | |
68 | |
69 return nFilter; | |
70 } | |
71 | |
72 | |
73 /** | |
74 * Filter the table using both the global filter and column based filtering | |
75 * @param {object} oSettings dataTables settings object | |
76 * @param {object} oSearch search information | |
77 * @param {int} [iForce] force a research of the master array (1) or not (undefined or 0) | |
78 * @memberof DataTable#oApi | |
79 */ | |
80 function _fnFilterComplete ( oSettings, oInput, iForce ) | |
81 { | |
82 var oPrevSearch = oSettings.oPreviousSearch; | |
83 var aoPrevSearch = oSettings.aoPreSearchCols; | |
84 var fnSaveFilter = function ( oFilter ) { | |
85 /* Save the filtering values */ | |
86 oPrevSearch.sSearch = oFilter.sSearch; | |
87 oPrevSearch.bRegex = oFilter.bRegex; | |
88 oPrevSearch.bSmart = oFilter.bSmart; | |
89 oPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive; | |
90 }; | |
91 | |
92 /* In server-side processing all filtering is done by the server, so no point hanging around here */ | |
93 if ( !oSettings.oFeatures.bServerSide ) | |
94 { | |
95 /* Global filter */ | |
96 _fnFilter( oSettings, oInput.sSearch, iForce, oInput.bRegex, oInput.bSmart, oInput.bCaseInsensitive ); | |
97 fnSaveFilter( oInput ); | |
98 | |
99 /* Now do the individual column filter */ | |
100 for ( var i=0 ; i<oSettings.aoPreSearchCols.length ; i++ ) | |
101 { | |
102 _fnFilterColumn( oSettings, aoPrevSearch[i].sSearch, i, aoPrevSearch[i].bRegex, | |
103 aoPrevSearch[i].bSmart, aoPrevSearch[i].bCaseInsensitive ); | |
104 } | |
105 | |
106 /* Custom filtering */ | |
107 _fnFilterCustom( oSettings ); | |
108 } | |
109 else | |
110 { | |
111 fnSaveFilter( oInput ); | |
112 } | |
113 | |
114 /* Tell the draw function we have been filtering */ | |
115 oSettings.bFiltered = true; | |
116 $(oSettings.oInstance).trigger('filter', oSettings); | |
117 | |
118 /* Redraw the table */ | |
119 oSettings._iDisplayStart = 0; | |
120 _fnCalculateEnd( oSettings ); | |
121 _fnDraw( oSettings ); | |
122 | |
123 /* Rebuild search array 'offline' */ | |
124 _fnBuildSearchArray( oSettings, 0 ); | |
125 } | |
126 | |
127 | |
128 /** | |
129 * Apply custom filtering functions | |
130 * @param {object} oSettings dataTables settings object | |
131 * @memberof DataTable#oApi | |
132 */ | |
133 function _fnFilterCustom( oSettings ) | |
134 { | |
135 var afnFilters = DataTable.ext.afnFiltering; | |
136 var aiFilterColumns = _fnGetColumns( oSettings, 'bSearchable' ); | |
137 | |
138 for ( var i=0, iLen=afnFilters.length ; i<iLen ; i++ ) | |
139 { | |
140 var iCorrector = 0; | |
141 for ( var j=0, jLen=oSettings.aiDisplay.length ; j<jLen ; j++ ) | |
142 { | |
143 var iDisIndex = oSettings.aiDisplay[j-iCorrector]; | |
144 var bTest = afnFilters[i]( | |
145 oSettings, | |
146 _fnGetRowData( oSettings, iDisIndex, 'filter', aiFilterColumns ), | |
147 iDisIndex | |
148 ); | |
149 | |
150 /* Check if we should use this row based on the filtering function */ | |
151 if ( !bTest ) | |
152 { | |
153 oSettings.aiDisplay.splice( j-iCorrector, 1 ); | |
154 iCorrector++; | |
155 } | |
156 } | |
157 } | |
158 } | |
159 | |
160 | |
161 /** | |
162 * Filter the table on a per-column basis | |
163 * @param {object} oSettings dataTables settings object | |
164 * @param {string} sInput string to filter on | |
165 * @param {int} iColumn column to filter | |
166 * @param {bool} bRegex treat search string as a regular expression or not | |
167 * @param {bool} bSmart use smart filtering or not | |
168 * @param {bool} bCaseInsensitive Do case insenstive matching or not | |
169 * @memberof DataTable#oApi | |
170 */ | |
171 function _fnFilterColumn ( oSettings, sInput, iColumn, bRegex, bSmart, bCaseInsensitive ) | |
172 { | |
173 if ( sInput === "" ) | |
174 { | |
175 return; | |
176 } | |
177 | |
178 var iIndexCorrector = 0; | |
179 var rpSearch = _fnFilterCreateSearch( sInput, bRegex, bSmart, bCaseInsensitive ); | |
180 | |
181 for ( var i=oSettings.aiDisplay.length-1 ; i>=0 ; i-- ) | |
182 { | |
183 var sData = _fnDataToSearch( _fnGetCellData( oSettings, oSettings.aiDisplay[i], iColumn, 'filter' ), | |
184 oSettings.aoColumns[iColumn].sType ); | |
185 if ( ! rpSearch.test( sData ) ) | |
186 { | |
187 oSettings.aiDisplay.splice( i, 1 ); | |
188 iIndexCorrector++; | |
189 } | |
190 } | |
191 } | |
192 | |
193 | |
194 /** | |
195 * Filter the data table based on user input and draw the table | |
196 * @param {object} oSettings dataTables settings object | |
197 * @param {string} sInput string to filter on | |
198 * @param {int} iForce optional - force a research of the master array (1) or not (undefined or 0) | |
199 * @param {bool} bRegex treat as a regular expression or not | |
200 * @param {bool} bSmart perform smart filtering or not | |
201 * @param {bool} bCaseInsensitive Do case insenstive matching or not | |
202 * @memberof DataTable#oApi | |
203 */ | |
204 function _fnFilter( oSettings, sInput, iForce, bRegex, bSmart, bCaseInsensitive ) | |
205 { | |
206 var i; | |
207 var rpSearch = _fnFilterCreateSearch( sInput, bRegex, bSmart, bCaseInsensitive ); | |
208 var oPrevSearch = oSettings.oPreviousSearch; | |
209 | |
210 /* Check if we are forcing or not - optional parameter */ | |
211 if ( !iForce ) | |
212 { | |
213 iForce = 0; | |
214 } | |
215 | |
216 /* Need to take account of custom filtering functions - always filter */ | |
217 if ( DataTable.ext.afnFiltering.length !== 0 ) | |
218 { | |
219 iForce = 1; | |
220 } | |
221 | |
222 /* | |
223 * If the input is blank - we want the full data set | |
224 */ | |
225 if ( sInput.length <= 0 ) | |
226 { | |
227 oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length); | |
228 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); | |
229 } | |
230 else | |
231 { | |
232 /* | |
233 * We are starting a new search or the new search string is smaller | |
234 * then the old one (i.e. delete). Search from the master array | |
235 */ | |
236 if ( oSettings.aiDisplay.length == oSettings.aiDisplayMaster.length || | |
237 oPrevSearch.sSearch.length > sInput.length || iForce == 1 || | |
238 sInput.indexOf(oPrevSearch.sSearch) !== 0 ) | |
239 { | |
240 /* Nuke the old display array - we are going to rebuild it */ | |
241 oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length); | |
242 | |
243 /* Force a rebuild of the search array */ | |
244 _fnBuildSearchArray( oSettings, 1 ); | |
245 | |
246 /* Search through all records to populate the search array | |
247 * The the oSettings.aiDisplayMaster and asDataSearch arrays have 1 to 1 | |
248 * mapping | |
249 */ | |
250 for ( i=0 ; i<oSettings.aiDisplayMaster.length ; i++ ) | |
251 { | |
252 if ( rpSearch.test(oSettings.asDataSearch[i]) ) | |
253 { | |
254 oSettings.aiDisplay.push( oSettings.aiDisplayMaster[i] ); | |
255 } | |
256 } | |
257 } | |
258 else | |
259 { | |
260 /* Using old search array - refine it - do it this way for speed | |
261 * Don't have to search the whole master array again | |
262 */ | |
263 var iIndexCorrector = 0; | |
264 | |
265 /* Search the current results */ | |
266 for ( i=0 ; i<oSettings.asDataSearch.length ; i++ ) | |
267 { | |
268 if ( ! rpSearch.test(oSettings.asDataSearch[i]) ) | |
269 { | |
270 oSettings.aiDisplay.splice( i-iIndexCorrector, 1 ); | |
271 iIndexCorrector++; | |
272 } | |
273 } | |
274 } | |
275 } | |
276 } | |
277 | |
278 | |
279 /** | |
280 * Create an array which can be quickly search through | |
281 * @param {object} oSettings dataTables settings object | |
282 * @param {int} iMaster use the master data array - optional | |
283 * @memberof DataTable#oApi | |
284 */ | |
285 function _fnBuildSearchArray ( oSettings, iMaster ) | |
286 { | |
287 if ( !oSettings.oFeatures.bServerSide ) | |
288 { | |
289 /* Clear out the old data */ | |
290 oSettings.asDataSearch = []; | |
291 | |
292 var aiFilterColumns = _fnGetColumns( oSettings, 'bSearchable' ); | |
293 var aiIndex = (iMaster===1) ? | |
294 oSettings.aiDisplayMaster : | |
295 oSettings.aiDisplay; | |
296 | |
297 for ( var i=0, iLen=aiIndex.length ; i<iLen ; i++ ) | |
298 { | |
299 oSettings.asDataSearch[i] = _fnBuildSearchRow( | |
300 oSettings, | |
301 _fnGetRowData( oSettings, aiIndex[i], 'filter', aiFilterColumns ) | |
302 ); | |
303 } | |
304 } | |
305 } | |
306 | |
307 | |
308 /** | |
309 * Create a searchable string from a single data row | |
310 * @param {object} oSettings dataTables settings object | |
311 * @param {array} aData Row data array to use for the data to search | |
312 * @memberof DataTable#oApi | |
313 */ | |
314 function _fnBuildSearchRow( oSettings, aData ) | |
315 { | |
316 var sSearch = aData.join(' '); | |
317 | |
318 /* If it looks like there is an HTML entity in the string, attempt to decode it */ | |
319 if ( sSearch.indexOf('&') !== -1 ) | |
320 { | |
321 sSearch = $('<div>').html(sSearch).text(); | |
322 } | |
323 | |
324 // Strip newline characters | |
325 return sSearch.replace( /[\n\r]/g, " " ); | |
326 } | |
327 | |
328 /** | |
329 * Build a regular expression object suitable for searching a table | |
330 * @param {string} sSearch string to search for | |
331 * @param {bool} bRegex treat as a regular expression or not | |
332 * @param {bool} bSmart perform smart filtering or not | |
333 * @param {bool} bCaseInsensitive Do case insensitive matching or not | |
334 * @returns {RegExp} constructed object | |
335 * @memberof DataTable#oApi | |
336 */ | |
337 function _fnFilterCreateSearch( sSearch, bRegex, bSmart, bCaseInsensitive ) | |
338 { | |
339 var asSearch, sRegExpString; | |
340 | |
341 if ( bSmart ) | |
342 { | |
343 /* Generate the regular expression to use. Something along the lines of: | |
344 * ^(?=.*?\bone\b)(?=.*?\btwo\b)(?=.*?\bthree\b).*$ | |
345 */ | |
346 asSearch = bRegex ? sSearch.split( ' ' ) : _fnEscapeRegex( sSearch ).split( ' ' ); | |
347 sRegExpString = '^(?=.*?'+asSearch.join( ')(?=.*?' )+').*$'; | |
348 return new RegExp( sRegExpString, bCaseInsensitive ? "i" : "" ); | |
349 } | |
350 else | |
351 { | |
352 sSearch = bRegex ? sSearch : _fnEscapeRegex( sSearch ); | |
353 return new RegExp( sSearch, bCaseInsensitive ? "i" : "" ); | |
354 } | |
355 } | |
356 | |
357 | |
358 /** | |
359 * Convert raw data into something that the user can search on | |
360 * @param {string} sData data to be modified | |
361 * @param {string} sType data type | |
362 * @returns {string} search string | |
363 * @memberof DataTable#oApi | |
364 */ | |
365 function _fnDataToSearch ( sData, sType ) | |
366 { | |
367 if ( typeof DataTable.ext.ofnSearch[sType] === "function" ) | |
368 { | |
369 return DataTable.ext.ofnSearch[sType]( sData ); | |
370 } | |
371 else if ( sData === null ) | |
372 { | |
373 return ''; | |
374 } | |
375 else if ( sType == "html" ) | |
376 { | |
377 return sData.replace(/[\r\n]/g," ").replace( /<.*?>/g, "" ); | |
378 } | |
379 else if ( typeof sData === "string" ) | |
380 { | |
381 return sData.replace(/[\r\n]/g," "); | |
382 } | |
383 return sData; | |
384 } | |
385 | |
386 | |
387 /** | |
388 * scape a string such that it can be used in a regular expression | |
389 * @param {string} sVal string to escape | |
390 * @returns {string} escaped string | |
391 * @memberof DataTable#oApi | |
392 */ | |
393 function _fnEscapeRegex ( sVal ) | |
394 { | |
395 var acEscape = [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ]; | |
396 var reReplace = new RegExp( '(\\' + acEscape.join('|\\') + ')', 'g' ); | |
397 return sVal.replace(reReplace, '\\$1'); | |
398 } | |
399 |