mirror of
https://github.com/DataTables/DataTables.git
synced 2025-02-19 17:54:14 +01:00
Performance: Re-written the sorting implementation so it doesn't use either eval() of $.inArray(). inArray was found to have a serious negative effect on IE which counts operations to give the 'script is running slow' error message - the new implemention uses a value / key reverse mapping to make the look up as fast as a single object parameter retreival. Furthermore, I've done some optimisation and feature completeness work on the non-eval version of the sorting (previously the AIR sorting method) and I've found this to be at least as good (faster in some cases) than the old eval() sort, so this is now the default and only sorting method DataTables provides - 2922
This commit is contained in:
parent
86bbbbfee0
commit
65b7c9b1e6
150
media/js/jquery.dataTables.js
vendored
150
media/js/jquery.dataTables.js
vendored
@ -4328,12 +4328,14 @@
|
||||
*/
|
||||
function _fnSort ( oSettings, bApplyClasses )
|
||||
{
|
||||
var aaSort = [];
|
||||
var oSort = _oExt.oSort;
|
||||
var aoData = oSettings.aoData;
|
||||
var iDataSort;
|
||||
var iDataType;
|
||||
var i, j, jLen;
|
||||
var
|
||||
iDataSort, iDataType,
|
||||
i, iLen, j, jLen,
|
||||
aaSort = [],
|
||||
aiOrig = [],
|
||||
oSort = _oExt.oSort,
|
||||
aoData = oSettings.aoData,
|
||||
aoColumns = oSettings.aoColumns;
|
||||
|
||||
/* No sorting required if server-side or no sorting array */
|
||||
if ( !oSettings.oFeatures.bServerSide &&
|
||||
@ -4366,109 +4368,51 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* DataTables offers two different methods for doing the 2D array sorting over multiple
|
||||
* columns. The first is to construct a function dynamically, and then evaluate and run
|
||||
* the function, while the second has no need for evalulation, but is a little bit slower.
|
||||
* This is used for environments which do not allow eval() for code execuation such as AIR
|
||||
/* Create a value - key array of the current row positions such that we can use their
|
||||
* current position during the sort, if values match, in order to perform stable sorting
|
||||
*/
|
||||
if ( !window.runtime )
|
||||
for ( i=0, iLen=oSettings.aiDisplayMaster.length ; i<iLen ; i++ )
|
||||
{
|
||||
/* Dynamically created sorting function. Based on the information that we have, we can
|
||||
* create a sorting function as if it were specifically written for this sort. Here we
|
||||
* want to build a function something like (for two column sorting):
|
||||
* fnLocalSorting = function(a,b){
|
||||
* var iTest;
|
||||
* iTest = oSort['string-asc']('data11', 'data12');
|
||||
* if (iTest === 0)
|
||||
* iTest = oSort['numeric-desc']('data21', 'data22');
|
||||
* if (iTest === 0)
|
||||
* return oSort['numeric-desc'](1,2);
|
||||
* return iTest;
|
||||
* }
|
||||
* So basically we have a test for each column, and if that column matches, test the
|
||||
* next one. If all columns match, then we use a numeric sort on the position the two
|
||||
* row have in the original data array in order to provide a stable sort. In order to
|
||||
* get the position for the numeric stablisation, we need to take a clone of the current
|
||||
* display array and then get the position of the sorting value from that during the
|
||||
* sort.
|
||||
*
|
||||
* Note that for use with the Closure compiler, we need to be very careful how we deal
|
||||
* with this eval. Closure will rename all of our local variables, resutling in breakage
|
||||
* if the variables in the eval don't also reflect this. For this reason, we need to use
|
||||
* 'this' to store the variables we need in the eval, so we can control them. A little
|
||||
* nasty, but well worth it for using Closure.
|
||||
*/
|
||||
this.ClosureDataTables = {
|
||||
"fn": function(){},
|
||||
"data": aoData,
|
||||
"sort": _oExt.oSort,
|
||||
"master": oSettings.aiDisplayMaster.slice()
|
||||
};
|
||||
var sDynamicSort = "this.ClosureDataTables.fn = function(a,b){"+
|
||||
"var iTest, oSort=this.ClosureDataTables.sort, "+
|
||||
"aoData=this.ClosureDataTables.data, "+
|
||||
"aiOrig=this.ClosureDataTables.master;";
|
||||
|
||||
for ( i=0 ; i<aaSort.length-1 ; i++ )
|
||||
aiOrig[ oSettings.aiDisplayMaster[i] ] = i;
|
||||
}
|
||||
|
||||
/* Do the sort - here we want multi-column sorting based on a given data source (column)
|
||||
* and sorting function (from oSort) in a certain direction. It's reasonably complex to
|
||||
* follow on it's own, but this is what we want (example two column sorting):
|
||||
* fnLocalSorting = function(a,b){
|
||||
* var iTest;
|
||||
* iTest = oSort['string-asc']('data11', 'data12');
|
||||
* if (iTest !== 0)
|
||||
* return iTest;
|
||||
* iTest = oSort['numeric-desc']('data21', 'data22');
|
||||
* if (iTest !== 0)
|
||||
* return iTest;
|
||||
* return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
|
||||
* }
|
||||
* Basically we have a test for each sorting column, if the data in that column is equal,
|
||||
* test the next column. If all columns match, then we use a numeric sort on the row
|
||||
* positions in the original data array to provide a stable sort.
|
||||
*/
|
||||
var iSortLen = aaSort.length;
|
||||
oSettings.aiDisplayMaster.sort( function ( a, b ) {
|
||||
var iTest;
|
||||
for ( i=0 ; i<iSortLen ; i++ )
|
||||
{
|
||||
iDataSort = oSettings.aoColumns[ aaSort[i][0] ].iDataSort;
|
||||
iDataType = oSettings.aoColumns[ iDataSort ].sType;
|
||||
sDynamicSort += "iTest = oSort['"+iDataType+"-"+aaSort[i][1]+"']"+
|
||||
"( aoData[a]._aData["+iDataSort+"], aoData[b]._aData["+iDataSort+"] ); if ( iTest === 0 )";
|
||||
}
|
||||
|
||||
if ( aaSort.length > 0 )
|
||||
{
|
||||
iDataSort = oSettings.aoColumns[ aaSort[aaSort.length-1][0] ].iDataSort;
|
||||
iDataType = oSettings.aoColumns[ iDataSort ].sType;
|
||||
sDynamicSort += "iTest = oSort['"+iDataType+"-"+aaSort[aaSort.length-1][1]+"']"+
|
||||
"( aoData[a]._aData["+iDataSort+"], aoData[b]._aData["+iDataSort+"] );"+
|
||||
"if (iTest===0) "+
|
||||
"return oSort['numeric-asc'](jQuery.inArray(a,aiOrig), jQuery.inArray(b,aiOrig)); "+
|
||||
"return iTest;}";
|
||||
iDataSort = aoColumns[ aaSort[i][0] ].iDataSort;
|
||||
iDataType = aoColumns[ iDataSort ].sType;
|
||||
iTest = oSort[ iDataType+"-"+aaSort[i][1] ](
|
||||
aoData[a]._aData[iDataSort],
|
||||
aoData[b]._aData[iDataSort]
|
||||
);
|
||||
|
||||
/* The eval has to be done to a variable for IE */
|
||||
eval( sDynamicSort );
|
||||
oSettings.aiDisplayMaster.sort( this.ClosureDataTables.fn );
|
||||
}
|
||||
this.ClosureDataTables = undefined;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Non-eval() sorting (AIR and other environments which doesn't allow code in eval()
|
||||
* Note that for reasonable sized data sets this method is around 1.5 times slower than
|
||||
* the eval above (hence why it is not used all the time). Oddly enough, it is ever so
|
||||
* slightly faster for very small sets (presumably the eval has overhead).
|
||||
* Single column (1083 records) - eval: 32mS AIR: 38mS
|
||||
* Two columns (1083 records) - eval: 55mS AIR: 66mS
|
||||
*/
|
||||
|
||||
/* Build a cached array so the sort doesn't have to process this stuff on every call */
|
||||
var aAirSort = [];
|
||||
var iLen = aaSort.length;
|
||||
for ( i=0 ; i<iLen ; i++ )
|
||||
{
|
||||
iDataSort = oSettings.aoColumns[ aaSort[i][0] ].iDataSort;
|
||||
aAirSort.push( [
|
||||
iDataSort,
|
||||
oSettings.aoColumns[ iDataSort ].sType+'-'+aaSort[i][1]
|
||||
] );
|
||||
}
|
||||
|
||||
oSettings.aiDisplayMaster.sort( function (a,b) {
|
||||
var iTest;
|
||||
for ( var i=0 ; i<iLen ; i++ )
|
||||
if ( iTest !== 0 )
|
||||
{
|
||||
iTest = oSort[ aAirSort[i][1] ]( aoData[a]._aData[aAirSort[i][0]], aoData[b]._aData[aAirSort[i][0]] );
|
||||
if ( iTest !== 0 )
|
||||
{
|
||||
return iTest;
|
||||
}
|
||||
return iTest;
|
||||
}
|
||||
return 0;
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
|
||||
} );
|
||||
}
|
||||
|
||||
/* Alter the sorting classes to take account of the changes */
|
||||
|
@ -23,21 +23,27 @@
|
||||
<script type="text/javascript" language="javascript" src="../../js/jquery.dataTables.js"></script>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
$(document).ready(function() {
|
||||
var oTable = $('#example').dataTable();
|
||||
var iStart = new Date().getTime();
|
||||
|
||||
if ( typeof console != 'undefined' ) {
|
||||
console.profile();
|
||||
}
|
||||
for ( var i=0 ; i<1 ; i++ )
|
||||
{
|
||||
var oTable = $('#example').dataTable({"bDestroy": true});
|
||||
}
|
||||
if ( typeof console != 'undefined' ) {
|
||||
console.profileEnd();
|
||||
}
|
||||
//if ( typeof console != 'undefined' ) {
|
||||
// console.profile();
|
||||
//}
|
||||
//for ( var i=0 ; i<1 ; i++ )
|
||||
//{
|
||||
// var oTable = $('#example').dataTable({"bDestroy": true});
|
||||
//}
|
||||
//if ( typeof console != 'undefined' ) {
|
||||
// console.profileEnd();
|
||||
//}
|
||||
|
||||
oTable.fnSort( [[ 1, 'asc' ]] );
|
||||
oTable.fnSort( [[ 2, 'asc' ]] );
|
||||
oTable.fnSort( [[ 1, 'asc' ]] );
|
||||
oTable.fnSort( [[ 2, 'asc' ]] );
|
||||
|
||||
var iEnd = new Date().getTime();
|
||||
document.getElementById('output').innerHTML = "Test took "+(iEnd-iStart)+"mS";
|
||||
document.getElementById('output').innerHTML = "Test took "+(iEnd-iStart)+" mS";
|
||||
} );
|
||||
</script>
|
||||
</head>
|
||||
@ -84,7 +90,6 @@
|
||||
echo '<td>'.$aRow['country'].'</td>';
|
||||
echo '<td>'.$aRow['zip2'].'</td>';
|
||||
echo '</tr>';
|
||||
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
|
Loading…
x
Reference in New Issue
Block a user