Add datatables-1.9.4 and jquery-1.10.2 libraries
[proteocache.git] / webapp / resources / datatables-1.9.4 / media / src / core / core.sort.js
1 /**
2  * Change the order of the table
3  *  @param {object} oSettings dataTables settings object
4  *  @param {bool} bApplyClasses optional - should we apply classes or not
5  *  @memberof DataTable#oApi
6  */
7 function _fnSort ( oSettings, bApplyClasses )
8 {
9         var
10                 i, iLen, j, jLen, k, kLen,
11                 sDataType, nTh,
12                 aaSort = [],
13                 aiOrig = [],
14                 oSort = DataTable.ext.oSort,
15                 aoData = oSettings.aoData,
16                 aoColumns = oSettings.aoColumns,
17                 oAria = oSettings.oLanguage.oAria;
18         
19         /* No sorting required if server-side or no sorting array */
20         if ( !oSettings.oFeatures.bServerSide && 
21                 (oSettings.aaSorting.length !== 0 || oSettings.aaSortingFixed !== null) )
22         {
23                 aaSort = ( oSettings.aaSortingFixed !== null ) ?
24                         oSettings.aaSortingFixed.concat( oSettings.aaSorting ) :
25                         oSettings.aaSorting.slice();
26                 
27                 /* If there is a sorting data type, and a function belonging to it, then we need to
28                  * get the data from the developer's function and apply it for this column
29                  */
30                 for ( i=0 ; i<aaSort.length ; i++ )
31                 {
32                         var iColumn = aaSort[i][0];
33                         var iVisColumn = _fnColumnIndexToVisible( oSettings, iColumn );
34                         sDataType = oSettings.aoColumns[ iColumn ].sSortDataType;
35                         if ( DataTable.ext.afnSortData[sDataType] )
36                         {
37                                 var aData = DataTable.ext.afnSortData[sDataType].call( 
38                                         oSettings.oInstance, oSettings, iColumn, iVisColumn
39                                 );
40                                 if ( aData.length === aoData.length )
41                                 {
42                                         for ( j=0, jLen=aoData.length ; j<jLen ; j++ )
43                                         {
44                                                 _fnSetCellData( oSettings, j, iColumn, aData[j] );
45                                         }
46                                 }
47                                 else
48                                 {
49                                         _fnLog( oSettings, 0, "Returned data sort array (col "+iColumn+") is the wrong length" );
50                                 }
51                         }
52                 }
53                 
54                 /* Create a value - key array of the current row positions such that we can use their
55                  * current position during the sort, if values match, in order to perform stable sorting
56                  */
57                 for ( i=0, iLen=oSettings.aiDisplayMaster.length ; i<iLen ; i++ )
58                 {
59                         aiOrig[ oSettings.aiDisplayMaster[i] ] = i;
60                 }
61
62                 /* Build an internal data array which is specific to the sort, so we can get and prep
63                  * the data to be sorted only once, rather than needing to do it every time the sorting
64                  * function runs. This make the sorting function a very simple comparison
65                  */
66                 var iSortLen = aaSort.length;
67                 var fnSortFormat, aDataSort;
68                 for ( i=0, iLen=aoData.length ; i<iLen ; i++ )
69                 {
70                         for ( j=0 ; j<iSortLen ; j++ )
71                         {
72                                 aDataSort = aoColumns[ aaSort[j][0] ].aDataSort;
73
74                                 for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )
75                                 {
76                                         sDataType = aoColumns[ aDataSort[k] ].sType;
77                                         fnSortFormat = oSort[ (sDataType ? sDataType : 'string')+"-pre" ];
78                                         
79                                         aoData[i]._aSortData[ aDataSort[k] ] = fnSortFormat ?
80                                                 fnSortFormat( _fnGetCellData( oSettings, i, aDataSort[k], 'sort' ) ) :
81                                                 _fnGetCellData( oSettings, i, aDataSort[k], 'sort' );
82                                 }
83                         }
84                 }
85                 
86                 /* Do the sort - here we want multi-column sorting based on a given data source (column)
87                  * and sorting function (from oSort) in a certain direction. It's reasonably complex to
88                  * follow on it's own, but this is what we want (example two column sorting):
89                  *  fnLocalSorting = function(a,b){
90                  *      var iTest;
91                  *      iTest = oSort['string-asc']('data11', 'data12');
92                  *      if (iTest !== 0)
93                  *              return iTest;
94                  *    iTest = oSort['numeric-desc']('data21', 'data22');
95                  *    if (iTest !== 0)
96                  *              return iTest;
97                  *      return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
98                  *  }
99                  * Basically we have a test for each sorting column, if the data in that column is equal,
100                  * test the next column. If all columns match, then we use a numeric sort on the row 
101                  * positions in the original data array to provide a stable sort.
102                  */
103                 oSettings.aiDisplayMaster.sort( function ( a, b ) {
104                         var k, l, lLen, iTest, aDataSort, sDataType;
105                         for ( k=0 ; k<iSortLen ; k++ )
106                         {
107                                 aDataSort = aoColumns[ aaSort[k][0] ].aDataSort;
108
109                                 for ( l=0, lLen=aDataSort.length ; l<lLen ; l++ )
110                                 {
111                                         sDataType = aoColumns[ aDataSort[l] ].sType;
112                                         
113                                         iTest = oSort[ (sDataType ? sDataType : 'string')+"-"+aaSort[k][1] ](
114                                                 aoData[a]._aSortData[ aDataSort[l] ],
115                                                 aoData[b]._aSortData[ aDataSort[l] ]
116                                         );
117                                 
118                                         if ( iTest !== 0 )
119                                         {
120                                                 return iTest;
121                                         }
122                                 }
123                         }
124                         
125                         return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
126                 } );
127         }
128         
129         /* Alter the sorting classes to take account of the changes */
130         if ( (bApplyClasses === undefined || bApplyClasses) && !oSettings.oFeatures.bDeferRender )
131         {
132                 _fnSortingClasses( oSettings );
133         }
134
135         for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
136         {
137                 var sTitle = aoColumns[i].sTitle.replace( /<.*?>/g, "" );
138                 nTh = aoColumns[i].nTh;
139                 nTh.removeAttribute('aria-sort');
140                 nTh.removeAttribute('aria-label');
141                 
142                 /* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */
143                 if ( aoColumns[i].bSortable )
144                 {
145                         if ( aaSort.length > 0 && aaSort[0][0] == i )
146                         {
147                                 nTh.setAttribute('aria-sort', aaSort[0][1]=="asc" ? "ascending" : "descending" );
148                                 
149                                 var nextSort = (aoColumns[i].asSorting[ aaSort[0][2]+1 ]) ? 
150                                         aoColumns[i].asSorting[ aaSort[0][2]+1 ] : aoColumns[i].asSorting[0];
151                                 nTh.setAttribute('aria-label', sTitle+
152                                         (nextSort=="asc" ? oAria.sSortAscending : oAria.sSortDescending) );
153                         }
154                         else
155                         {
156                                 nTh.setAttribute('aria-label', sTitle+
157                                         (aoColumns[i].asSorting[0]=="asc" ? oAria.sSortAscending : oAria.sSortDescending) );
158                         }
159                 }
160                 else
161                 {
162                         nTh.setAttribute('aria-label', sTitle);
163                 }
164         }
165         
166         /* Tell the draw function that we have sorted the data */
167         oSettings.bSorted = true;
168         $(oSettings.oInstance).trigger('sort', oSettings);
169         
170         /* Copy the master data into the draw array and re-draw */
171         if ( oSettings.oFeatures.bFilter )
172         {
173                 /* _fnFilter() will redraw the table for us */
174                 _fnFilterComplete( oSettings, oSettings.oPreviousSearch, 1 );
175         }
176         else
177         {
178                 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
179                 oSettings._iDisplayStart = 0; /* reset display back to page 0 */
180                 _fnCalculateEnd( oSettings );
181                 _fnDraw( oSettings );
182         }
183 }
184
185
186 /**
187  * Attach a sort handler (click) to a node
188  *  @param {object} oSettings dataTables settings object
189  *  @param {node} nNode node to attach the handler to
190  *  @param {int} iDataIndex column sorting index
191  *  @param {function} [fnCallback] callback function
192  *  @memberof DataTable#oApi
193  */
194 function _fnSortAttachListener ( oSettings, nNode, iDataIndex, fnCallback )
195 {
196         _fnBindAction( nNode, {}, function (e) {
197                 /* If the column is not sortable - don't to anything */
198                 if ( oSettings.aoColumns[iDataIndex].bSortable === false )
199                 {
200                         return;
201                 }
202                 
203                 /*
204                  * This is a little bit odd I admit... I declare a temporary function inside the scope of
205                  * _fnBuildHead and the click handler in order that the code presented here can be used 
206                  * twice - once for when bProcessing is enabled, and another time for when it is 
207                  * disabled, as we need to perform slightly different actions.
208                  *   Basically the issue here is that the Javascript engine in modern browsers don't 
209                  * appear to allow the rendering engine to update the display while it is still executing
210                  * it's thread (well - it does but only after long intervals). This means that the 
211                  * 'processing' display doesn't appear for a table sort. To break the js thread up a bit
212                  * I force an execution break by using setTimeout - but this breaks the expected 
213                  * thread continuation for the end-developer's point of view (their code would execute
214                  * too early), so we only do it when we absolutely have to.
215                  */
216                 var fnInnerSorting = function () {
217                         var iColumn, iNextSort;
218                         
219                         /* If the shift key is pressed then we are multiple column sorting */
220                         if ( e.shiftKey )
221                         {
222                                 /* Are we already doing some kind of sort on this column? */
223                                 var bFound = false;
224                                 for ( var i=0 ; i<oSettings.aaSorting.length ; i++ )
225                                 {
226                                         if ( oSettings.aaSorting[i][0] == iDataIndex )
227                                         {
228                                                 bFound = true;
229                                                 iColumn = oSettings.aaSorting[i][0];
230                                                 iNextSort = oSettings.aaSorting[i][2]+1;
231                                                 
232                                                 if ( !oSettings.aoColumns[iColumn].asSorting[iNextSort] )
233                                                 {
234                                                         /* Reached the end of the sorting options, remove from multi-col sort */
235                                                         oSettings.aaSorting.splice( i, 1 );
236                                                 }
237                                                 else
238                                                 {
239                                                         /* Move onto next sorting direction */
240                                                         oSettings.aaSorting[i][1] = oSettings.aoColumns[iColumn].asSorting[iNextSort];
241                                                         oSettings.aaSorting[i][2] = iNextSort;
242                                                 }
243                                                 break;
244                                         }
245                                 }
246                                 
247                                 /* No sort yet - add it in */
248                                 if ( bFound === false )
249                                 {
250                                         oSettings.aaSorting.push( [ iDataIndex, 
251                                                 oSettings.aoColumns[iDataIndex].asSorting[0], 0 ] );
252                                 }
253                         }
254                         else
255                         {
256                                 /* If no shift key then single column sort */
257                                 if ( oSettings.aaSorting.length == 1 && oSettings.aaSorting[0][0] == iDataIndex )
258                                 {
259                                         iColumn = oSettings.aaSorting[0][0];
260                                         iNextSort = oSettings.aaSorting[0][2]+1;
261                                         if ( !oSettings.aoColumns[iColumn].asSorting[iNextSort] )
262                                         {
263                                                 iNextSort = 0;
264                                         }
265                                         oSettings.aaSorting[0][1] = oSettings.aoColumns[iColumn].asSorting[iNextSort];
266                                         oSettings.aaSorting[0][2] = iNextSort;
267                                 }
268                                 else
269                                 {
270                                         oSettings.aaSorting.splice( 0, oSettings.aaSorting.length );
271                                         oSettings.aaSorting.push( [ iDataIndex, 
272                                                 oSettings.aoColumns[iDataIndex].asSorting[0], 0 ] );
273                                 }
274                         }
275                         
276                         /* Run the sort */
277                         _fnSort( oSettings );
278                 }; /* /fnInnerSorting */
279                 
280                 if ( !oSettings.oFeatures.bProcessing )
281                 {
282                         fnInnerSorting();
283                 }
284                 else
285                 {
286                         _fnProcessingDisplay( oSettings, true );
287                         setTimeout( function() {
288                                 fnInnerSorting();
289                                 if ( !oSettings.oFeatures.bServerSide )
290                                 {
291                                         _fnProcessingDisplay( oSettings, false );
292                                 }
293                         }, 0 );
294                 }
295                 
296                 /* Call the user specified callback function - used for async user interaction */
297                 if ( typeof fnCallback == 'function' )
298                 {
299                         fnCallback( oSettings );
300                 }
301         } );
302 }
303
304
305 /**
306  * Set the sorting classes on the header, Note: it is safe to call this function 
307  * when bSort and bSortClasses are false
308  *  @param {object} oSettings dataTables settings object
309  *  @memberof DataTable#oApi
310  */
311 function _fnSortingClasses( oSettings )
312 {
313         var i, iLen, j, jLen, iFound;
314         var aaSort, sClass;
315         var iColumns = oSettings.aoColumns.length;
316         var oClasses = oSettings.oClasses;
317         
318         for ( i=0 ; i<iColumns ; i++ )
319         {
320                 if ( oSettings.aoColumns[i].bSortable )
321                 {
322                         $(oSettings.aoColumns[i].nTh).removeClass( oClasses.sSortAsc +" "+ oClasses.sSortDesc +
323                                 " "+ oSettings.aoColumns[i].sSortingClass );
324                 }
325         }
326         
327         if ( oSettings.aaSortingFixed !== null )
328         {
329                 aaSort = oSettings.aaSortingFixed.concat( oSettings.aaSorting );
330         }
331         else
332         {
333                 aaSort = oSettings.aaSorting.slice();
334         }
335         
336         /* Apply the required classes to the header */
337         for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
338         {
339                 if ( oSettings.aoColumns[i].bSortable )
340                 {
341                         sClass = oSettings.aoColumns[i].sSortingClass;
342                         iFound = -1;
343                         for ( j=0 ; j<aaSort.length ; j++ )
344                         {
345                                 if ( aaSort[j][0] == i )
346                                 {
347                                         sClass = ( aaSort[j][1] == "asc" ) ?
348                                                 oClasses.sSortAsc : oClasses.sSortDesc;
349                                         iFound = j;
350                                         break;
351                                 }
352                         }
353                         $(oSettings.aoColumns[i].nTh).addClass( sClass );
354                         
355                         if ( oSettings.bJUI )
356                         {
357                                 /* jQuery UI uses extra markup */
358                                 var jqSpan = $("span."+oClasses.sSortIcon,  oSettings.aoColumns[i].nTh);
359                                 jqSpan.removeClass(oClasses.sSortJUIAsc +" "+ oClasses.sSortJUIDesc +" "+ 
360                                         oClasses.sSortJUI +" "+ oClasses.sSortJUIAscAllowed +" "+ oClasses.sSortJUIDescAllowed );
361                                 
362                                 var sSpanClass;
363                                 if ( iFound == -1 )
364                                 {
365                                         sSpanClass = oSettings.aoColumns[i].sSortingClassJUI;
366                                 }
367                                 else if ( aaSort[iFound][1] == "asc" )
368                                 {
369                                         sSpanClass = oClasses.sSortJUIAsc;
370                                 }
371                                 else
372                                 {
373                                         sSpanClass = oClasses.sSortJUIDesc;
374                                 }
375                                 
376                                 jqSpan.addClass( sSpanClass );
377                         }
378                 }
379                 else
380                 {
381                         /* No sorting on this column, so add the base class. This will have been assigned by
382                          * _fnAddColumn
383                          */
384                         $(oSettings.aoColumns[i].nTh).addClass( oSettings.aoColumns[i].sSortingClass );
385                 }
386         }
387         
388         /* 
389          * Apply the required classes to the table body
390          * Note that this is given as a feature switch since it can significantly slow down a sort
391          * on large data sets (adding and removing of classes is always slow at the best of times..)
392          * Further to this, note that this code is admittedly fairly ugly. It could be made a lot 
393          * simpler using jQuery selectors and add/removeClass, but that is significantly slower
394          * (on the order of 5 times slower) - hence the direct DOM manipulation here.
395          * Note that for deferred drawing we do use jQuery - the reason being that taking the first
396          * row found to see if the whole column needs processed can miss classes since the first
397          * column might be new.
398          */
399         sClass = oClasses.sSortColumn;
400         
401         if ( oSettings.oFeatures.bSort && oSettings.oFeatures.bSortClasses )
402         {
403                 var nTds = _fnGetTdNodes( oSettings );
404                 
405                 /* Determine what the sorting class for each column should be */
406                 var iClass, iTargetCol;
407                 var asClasses = [];
408                 for (i = 0; i < iColumns; i++)
409                 {
410                         asClasses.push("");
411                 }
412                 for (i = 0, iClass = 1; i < aaSort.length; i++)
413                 {
414                         iTargetCol = parseInt( aaSort[i][0], 10 );
415                         asClasses[iTargetCol] = sClass + iClass;
416                         
417                         if ( iClass < 3 )
418                         {
419                                 iClass++;
420                         }
421                 }
422                 
423                 /* Make changes to the classes for each cell as needed */
424                 var reClass = new RegExp(sClass + "[123]");
425                 var sTmpClass, sCurrentClass, sNewClass;
426                 for ( i=0, iLen=nTds.length; i<iLen; i++ )
427                 {
428                         /* Determine which column we're looking at */
429                         iTargetCol = i % iColumns;
430                         
431                         /* What is the full list of classes now */
432                         sCurrentClass = nTds[i].className;
433                         /* What sorting class should be applied? */
434                         sNewClass = asClasses[iTargetCol];
435                         /* What would the new full list be if we did a replacement? */
436                         sTmpClass = sCurrentClass.replace(reClass, sNewClass);
437                         
438                         if ( sTmpClass != sCurrentClass )
439                         {
440                                 /* We changed something */
441                                 nTds[i].className = $.trim( sTmpClass );
442                         }
443                         else if ( sNewClass.length > 0 && sCurrentClass.indexOf(sNewClass) == -1 )
444                         {
445                                 /* We need to add a class */
446                                 nTds[i].className = sCurrentClass + " " + sNewClass;
447                         }
448                 }
449         }
450 }
451