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('"','&quot;') );
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