Add datatables-1.9.4 and jquery-1.10.2 libraries
[proteocache.git] / webapp / resources / datatables-1.9.4 / media / src / core / core.filter.js
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