mirror of
https://github.com/DataTables/DataTables.git
synced 2024-12-01 13:24:10 +01:00
Internal: Refactoring the sort functions for invalidation
- invalidation is going to play an important part in the new API, with the ability to invalid the cached data for sorting, filtering, display etc so we need to be able tos upport this in the core. - In fairness the sorting didn't actually need invalidation because it would always get data on every sort, which is bad for performance - proper invalidation and caching will resolve this. Which is what has been implemented here. - I had expected (hoped) to be able to save a bit of space in the refactor, but only aorund 100 bytes (compressed) saved, which will probably be lost when the invalidation is fully implemented in the API. Still, better performance, tidier code, and no extra space... Fix: Sorting classes now show multi column sorting when a column is defined to sort over multiple columns (sortData). Previously it would only show the first column.
This commit is contained in:
parent
67d19ac9e4
commit
4913226456
@ -45,8 +45,14 @@ _api.register( 'row().data()', function ( data ) {
|
|||||||
// return undefined;
|
// return undefined;
|
||||||
|
|
||||||
// @todo - Set operator
|
// @todo - Set operator
|
||||||
|
ctx[0].aoData[ this[0] ]._aData = data;
|
||||||
|
|
||||||
|
// Invalidate the row
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
// Should row object have an invalidated flag? Scan the array before doing
|
||||||
|
// anything with the data? or better to update at the invalidation point
|
||||||
|
|
||||||
|
|
||||||
_api.register( 'row().index()', function () {
|
_api.register( 'row().index()', function () {
|
||||||
return this.length ? this[0] : undefined;
|
return this.length ? this[0] : undefined;
|
||||||
|
@ -1,3 +1,37 @@
|
|||||||
|
|
||||||
|
|
||||||
|
function _fnSortFlatten ( settings )
|
||||||
|
{
|
||||||
|
var
|
||||||
|
i, iLen, k, kLen,
|
||||||
|
aSort = [],
|
||||||
|
aiOrig = [],
|
||||||
|
aoColumns = settings.aoColumns,
|
||||||
|
aDataSort, iCol, sType,
|
||||||
|
nestedSort = settings.aaSortingFixed.concat( settings.aaSorting );
|
||||||
|
|
||||||
|
for ( i=0 ; i<nestedSort.length ; i++ )
|
||||||
|
{
|
||||||
|
aDataSort = aoColumns[ nestedSort[i][0] ].aDataSort;
|
||||||
|
|
||||||
|
for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )
|
||||||
|
{
|
||||||
|
iCol = aDataSort[k];
|
||||||
|
sType = aoColumns[ iCol ].sType || 'string';
|
||||||
|
|
||||||
|
aSort.push( {
|
||||||
|
col: iCol,
|
||||||
|
dir: nestedSort[i][1],
|
||||||
|
index: nestedSort[i][2],
|
||||||
|
type: sType,
|
||||||
|
formatter: DataTable.ext.oSort[ sType+"-pre" ]
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return aSort;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change the order of the table
|
* Change the order of the table
|
||||||
* @param {object} oSettings dataTables settings object
|
* @param {object} oSettings dataTables settings object
|
||||||
@ -8,107 +42,42 @@
|
|||||||
function _fnSort ( oSettings, bApplyClasses )
|
function _fnSort ( oSettings, bApplyClasses )
|
||||||
{
|
{
|
||||||
var
|
var
|
||||||
i, iLen, j, jLen, k, kLen,
|
i, ien, iLen, j, jLen, k, kLen,
|
||||||
sDataType, nTh,
|
sDataType, nTh,
|
||||||
aSort = [],
|
aSort = [],
|
||||||
aiOrig = [],
|
aiOrig = [],
|
||||||
oExtSort = DataTable.ext.oSort,
|
|
||||||
aoData = oSettings.aoData,
|
aoData = oSettings.aoData,
|
||||||
aoColumns = oSettings.aoColumns,
|
aoColumns = oSettings.aoColumns,
|
||||||
oAria = oSettings.oLanguage.oAria,
|
oAria = oSettings.oLanguage.oAria,
|
||||||
fnFormatter, aDataSort, data, iCol, sType, oSort,
|
aDataSort, data, iCol, sType, oSort,
|
||||||
iFormatters = 0,
|
formatters = 0,
|
||||||
aaNestedSort = ( oSettings.aaSortingFixed !== null ) ?
|
nestedSort = oSettings.aaSortingFixed.concat( oSettings.aaSorting ),
|
||||||
oSettings.aaSortingFixed.concat( oSettings.aaSorting ) :
|
sortCol,
|
||||||
oSettings.aaSorting.slice();
|
displayMaster = oSettings.aiDisplayMaster;
|
||||||
|
|
||||||
/* Flatten the aDataSort inner arrays into a single array, otherwise we have nested
|
aDataSort = _fnSortFlatten( oSettings );
|
||||||
* loops in multiple locations
|
|
||||||
*/
|
|
||||||
for ( i=0 ; i<aaNestedSort.length ; i++ )
|
|
||||||
{
|
|
||||||
aDataSort = aoColumns[ aaNestedSort[i][0] ].aDataSort;
|
|
||||||
|
|
||||||
for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )
|
for ( i=0, ien=aDataSort.length ; i<ien ; i++ ) {
|
||||||
{
|
sortCol = aDataSort[i];
|
||||||
iCol = aDataSort[k];
|
|
||||||
sType = aoColumns[ iCol ].sType || 'string';
|
|
||||||
fnFormatter = oExtSort[ sType+"-pre" ];
|
|
||||||
|
|
||||||
aSort.push( {
|
// Track if we can use the fast sort algorithm
|
||||||
col: iCol,
|
if ( sortCol.formatter ) {
|
||||||
dir: aaNestedSort[i][1],
|
formatters++;
|
||||||
index: aaNestedSort[i][2],
|
|
||||||
type: sType,
|
|
||||||
format: fnFormatter
|
|
||||||
} );
|
|
||||||
|
|
||||||
// Track if we can use the formatter method
|
|
||||||
if ( fnFormatter )
|
|
||||||
{
|
|
||||||
iFormatters++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load the data needed for the sort, for each cell
|
||||||
|
_fnSortColumn( oSettings, sortCol.col );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* No sorting required if server-side or no sorting array */
|
/* No sorting required if server-side or no sorting array */
|
||||||
if ( !oSettings.oFeatures.bServerSide && aSort.length !== 0 )
|
if ( !oSettings.oFeatures.bServerSide && aSort.length !== 0 )
|
||||||
{
|
{
|
||||||
/* If there is a sorting data type, and a function belonging to it, then we need to
|
// Create a value - key array of the current row positions such that we can use their
|
||||||
* get the data from the developer's function and apply it for this column
|
// current position during the sort, if values match, in order to perform stable sorting
|
||||||
*/
|
for ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) {
|
||||||
for ( i=0 ; i<aSort.length ; i++ )
|
aiOrig[ displayMaster[i] ] = i;
|
||||||
{
|
|
||||||
var iColumn = aSort[i].col;
|
|
||||||
var iVisColumn = _fnColumnIndexToVisible( oSettings, iColumn );
|
|
||||||
|
|
||||||
sDataType = oSettings.aoColumns[ iColumn ].sSortDataType;
|
|
||||||
if ( DataTable.ext.afnSortData[sDataType] )
|
|
||||||
{
|
|
||||||
var aData = DataTable.ext.afnSortData[sDataType].call(
|
|
||||||
oSettings.oInstance, oSettings, iColumn, iVisColumn
|
|
||||||
);
|
|
||||||
if ( aData.length === aoData.length )
|
|
||||||
{
|
|
||||||
for ( j=0, jLen=aoData.length ; j<jLen ; j++ )
|
|
||||||
{
|
|
||||||
_fnSetCellData( oSettings, j, iColumn, aData[j] );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_fnLog( oSettings, 0, "Returned data sort array (col "+iColumn+") is the wrong length" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* 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
|
|
||||||
*/
|
|
||||||
for ( i=0, iLen=oSettings.aiDisplayMaster.length ; i<iLen ; i++ )
|
|
||||||
{
|
|
||||||
aiOrig[ oSettings.aiDisplayMaster[i] ] = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Build an internal data array which is specific to the sort, so we can get and prep
|
|
||||||
* the data to be sorted only once, rather than needing to do it every time the sorting
|
|
||||||
* function runs. This make the sorting function a very simple comparison
|
|
||||||
*/
|
|
||||||
for ( j=0 ; j<aSort.length ; j++ )
|
|
||||||
{
|
|
||||||
oSort = aSort[j];
|
|
||||||
|
|
||||||
for ( i=0, iLen=aoData.length ; i<iLen ; i++ )
|
|
||||||
{
|
|
||||||
data = _fnGetCellData( oSettings, i, oSort.col, 'sort' );
|
|
||||||
|
|
||||||
aoData[i]._aSortData[ oSort.col ] = oSort.format ?
|
|
||||||
oSort.format( data ) :
|
|
||||||
data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Do the sort - here we want multi-column sorting based on a given data source (column)
|
/* 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
|
* 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):
|
* follow on it's own, but this is what we want (example two column sorting):
|
||||||
@ -130,29 +99,27 @@ function _fnSort ( oSettings, bApplyClasses )
|
|||||||
* 15% faster, so the second is only maintained for backwards compatibility with sorting
|
* 15% faster, so the second is only maintained for backwards compatibility with sorting
|
||||||
* methods which do not have a pre-sort formatting function.
|
* methods which do not have a pre-sort formatting function.
|
||||||
*/
|
*/
|
||||||
if ( iFormatters === aSort.length ) {
|
if ( formatters === aSort.length ) {
|
||||||
// All sort types have formatting functions
|
// All sort types have formatting functions
|
||||||
oSettings.aiDisplayMaster.sort( function ( a, b ) {
|
displayMaster.sort( function ( a, b ) {
|
||||||
var
|
var
|
||||||
x, y, k, test, sort,
|
x, y, k, test, sort,
|
||||||
len=aSort.length,
|
len=aSort.length,
|
||||||
dataA = aoData[a]._aSortData,
|
dataA = aoData[a]._aSortData,
|
||||||
dataB = aoData[b]._aSortData;
|
dataB = aoData[b]._aSortData;
|
||||||
|
|
||||||
for ( k=0 ; k<len ; k++ )
|
for ( k=0 ; k<len ; k++ ) {
|
||||||
{
|
|
||||||
sort = aSort[k];
|
sort = aSort[k];
|
||||||
|
|
||||||
x = dataA[ sort.col ];
|
x = dataA[ sort.col ];
|
||||||
y = dataB[ sort.col ];
|
y = dataB[ sort.col ];
|
||||||
|
|
||||||
test = x<y ? -1 : x>y ? 1 : 0;
|
test = x<y ? -1 : x>y ? 1 : 0;
|
||||||
if ( test !== 0 )
|
if ( test !== 0 ) {
|
||||||
{
|
|
||||||
return sort.dir === 'asc' ? test : -test;
|
return sort.dir === 'asc' ? test : -test;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
x = aiOrig[a];
|
x = aiOrig[a];
|
||||||
y = aiOrig[b];
|
y = aiOrig[b];
|
||||||
return x<y ? -1 : x>y ? 1 : 0;
|
return x<y ? -1 : x>y ? 1 : 0;
|
||||||
@ -162,34 +129,32 @@ function _fnSort ( oSettings, bApplyClasses )
|
|||||||
// Depreciated - remove in 1.11 (providing a plug-in option)
|
// Depreciated - remove in 1.11 (providing a plug-in option)
|
||||||
// Not all sort types have formatting methods, so we have to call their sorting
|
// Not all sort types have formatting methods, so we have to call their sorting
|
||||||
// methods.
|
// methods.
|
||||||
oSettings.aiDisplayMaster.sort( function ( a, b ) {
|
displayMaster.sort( function ( a, b ) {
|
||||||
var
|
var
|
||||||
x, y, k, l, test, sort,
|
x, y, k, l, test, sort,
|
||||||
len=aSort.length,
|
len=aSort.length,
|
||||||
dataA = aoData[a]._aSortData,
|
dataA = aoData[a]._aSortData,
|
||||||
dataB = aoData[b]._aSortData;
|
dataB = aoData[b]._aSortData;
|
||||||
|
|
||||||
for ( k=0 ; k<len ; k++ )
|
for ( k=0 ; k<len ; k++ ) {
|
||||||
{
|
|
||||||
sort = aSort[k];
|
sort = aSort[k];
|
||||||
|
|
||||||
x = dataA[ sort.col ];
|
x = dataA[ sort.col ];
|
||||||
y = dataB[ sort.col ];
|
y = dataB[ sort.col ];
|
||||||
|
|
||||||
test = oExtSort[ sort.type+"-"+sort.dir ]( x, y );
|
test = oExtSort[ sort.type+"-"+sort.dir ]( x, y );
|
||||||
if ( test !== 0 )
|
if ( test !== 0 ) {
|
||||||
{
|
|
||||||
return test;
|
return test;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
x = aiOrig[a];
|
x = aiOrig[a];
|
||||||
y = aiOrig[b];
|
y = aiOrig[b];
|
||||||
return x<y ? -1 : x>y ? 1 : 0;
|
return x<y ? -1 : x>y ? 1 : 0;
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Alter the sorting classes to take account of the changes */
|
/* Alter the sorting classes to take account of the changes */
|
||||||
if ( (bApplyClasses === undefined || bApplyClasses) && !oSettings.oFeatures.bDeferRender )
|
if ( (bApplyClasses === undefined || bApplyClasses) && !oSettings.oFeatures.bDeferRender )
|
||||||
{
|
{
|
||||||
@ -202,14 +167,14 @@ function _fnSort ( oSettings, bApplyClasses )
|
|||||||
nTh = aoColumns[i].nTh;
|
nTh = aoColumns[i].nTh;
|
||||||
nTh.removeAttribute('aria-sort');
|
nTh.removeAttribute('aria-sort');
|
||||||
nTh.removeAttribute('aria-label');
|
nTh.removeAttribute('aria-label');
|
||||||
|
|
||||||
/* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */
|
/* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */
|
||||||
if ( aoColumns[i].bSortable )
|
if ( aoColumns[i].bSortable )
|
||||||
{
|
{
|
||||||
if ( aSort.length > 0 && aSort[0].col == i )
|
if ( aSort.length > 0 && aSort[0].col == i )
|
||||||
{
|
{
|
||||||
nTh.setAttribute('aria-sort', aSort[0].dir=="asc" ? "ascending" : "descending" );
|
nTh.setAttribute('aria-sort', aSort[0].dir=="asc" ? "ascending" : "descending" );
|
||||||
|
|
||||||
var nextSort = (aoColumns[i].asSorting[ aSort[0].index+1 ]) ?
|
var nextSort = (aoColumns[i].asSorting[ aSort[0].index+1 ]) ?
|
||||||
aoColumns[i].asSorting[ aSort[0].index+1 ] : aoColumns[i].asSorting[0];
|
aoColumns[i].asSorting[ aSort[0].index+1 ] : aoColumns[i].asSorting[0];
|
||||||
nTh.setAttribute('aria-label', sTitle+
|
nTh.setAttribute('aria-label', sTitle+
|
||||||
@ -226,7 +191,7 @@ function _fnSort ( oSettings, bApplyClasses )
|
|||||||
nTh.setAttribute('aria-label', sTitle);
|
nTh.setAttribute('aria-label', sTitle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tell the draw function that we have sorted the data */
|
/* Tell the draw function that we have sorted the data */
|
||||||
oSettings.bSorted = true;
|
oSettings.bSorted = true;
|
||||||
$(oSettings.oInstance).trigger('sort', oSettings);
|
$(oSettings.oInstance).trigger('sort', oSettings);
|
||||||
@ -374,14 +339,7 @@ function _fnSortingClasses( oSettings )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( oSettings.aaSortingFixed !== null )
|
aaSort = _fnSortFlatten( oSettings );
|
||||||
{
|
|
||||||
aaSort = oSettings.aaSortingFixed.concat( oSettings.aaSorting );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
aaSort = oSettings.aaSorting.slice();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Apply the required classes to the header */
|
/* Apply the required classes to the header */
|
||||||
for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
|
for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
|
||||||
@ -392,9 +350,9 @@ function _fnSortingClasses( oSettings )
|
|||||||
iFound = -1;
|
iFound = -1;
|
||||||
for ( j=0 ; j<aaSort.length ; j++ )
|
for ( j=0 ; j<aaSort.length ; j++ )
|
||||||
{
|
{
|
||||||
if ( aaSort[j][0] == i )
|
if ( aaSort[j].col == i )
|
||||||
{
|
{
|
||||||
sClass = ( aaSort[j][1] == "asc" ) ?
|
sClass = ( aaSort[j].dir == "asc" ) ?
|
||||||
oClasses.sSortAsc : oClasses.sSortDesc;
|
oClasses.sSortAsc : oClasses.sSortDesc;
|
||||||
iFound = j;
|
iFound = j;
|
||||||
break;
|
break;
|
||||||
@ -414,7 +372,7 @@ function _fnSortingClasses( oSettings )
|
|||||||
{
|
{
|
||||||
sSpanClass = oSettings.aoColumns[i].sSortingClassJUI;
|
sSpanClass = oSettings.aoColumns[i].sSortingClassJUI;
|
||||||
}
|
}
|
||||||
else if ( aaSort[iFound][1] == "asc" )
|
else if ( aaSort[iFound].dir == "asc" )
|
||||||
{
|
{
|
||||||
sSpanClass = oClasses.sSortJUIAsc;
|
sSpanClass = oClasses.sSortJUIAsc;
|
||||||
}
|
}
|
||||||
@ -506,3 +464,48 @@ function _fnSortingClasses( oSettings )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Get the data to sort a column, be it from cache, fresh (populating the
|
||||||
|
// cache), or from a sort formatter
|
||||||
|
function _fnSortColumn( settings, idx )
|
||||||
|
{
|
||||||
|
// Custom sorting function - provided by the sort data type
|
||||||
|
var column = settings.aoColumns[ idx ];
|
||||||
|
var customSort = DataTable.ext.afnSortData[ column.sSortDataType ];
|
||||||
|
var customData;
|
||||||
|
|
||||||
|
if ( customSort ) {
|
||||||
|
customData = customSort.call( settings.oInstance, settings, idx,
|
||||||
|
_fnColumnIndexToVisible( settings, idx )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use / populate cache
|
||||||
|
var row, cellData;
|
||||||
|
var formatter = DataTable.ext.oSort[ column.sType+"-pre" ];
|
||||||
|
|
||||||
|
for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
|
||||||
|
row = settings.aoData[i];
|
||||||
|
|
||||||
|
if ( ! row._aSortData ) {
|
||||||
|
row._aSortData = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! row._aSortData[idx] || customSort ) {
|
||||||
|
cellData = customSort ?
|
||||||
|
customData : // If there was a custom sort function, use data from there
|
||||||
|
_fnGetCellData( settings, i, idx, 'sort' );
|
||||||
|
|
||||||
|
row._aSortData[ idx ] = formatter ?
|
||||||
|
formatter( cellData ) :
|
||||||
|
cellData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function _fnSortInvalidate( settings, row )
|
||||||
|
{
|
||||||
|
row._aSortData = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -133,7 +133,7 @@ DataTable.defaults = {
|
|||||||
* } );
|
* } );
|
||||||
* } )
|
* } )
|
||||||
*/
|
*/
|
||||||
"aaSortingFixed": null,
|
"aaSortingFixed": [],
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -43,10 +43,10 @@ DataTable.models.oRow = {
|
|||||||
* per sort. This array should not be read from or written to by anything
|
* per sort. This array should not be read from or written to by anything
|
||||||
* other than the master sorting methods.
|
* other than the master sorting methods.
|
||||||
* @type array
|
* @type array
|
||||||
* @default []
|
* @default null
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
"_aSortData": [],
|
"_aSortData": null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cache of the class name that DataTables has applied to the row, so we
|
* Cache of the class name that DataTables has applied to the row, so we
|
||||||
|
@ -360,10 +360,10 @@ DataTable.models.oSettings = {
|
|||||||
* aaSorting).
|
* aaSorting).
|
||||||
* Note that this parameter will be set by the initialisation routine. To
|
* Note that this parameter will be set by the initialisation routine. To
|
||||||
* set a default use {@link DataTable.defaults}.
|
* set a default use {@link DataTable.defaults}.
|
||||||
* @type array|null
|
* @type array
|
||||||
* @default null
|
* @default []
|
||||||
*/
|
*/
|
||||||
"aaSortingFixed": null,
|
"aaSortingFixed": [],
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Classes to use for the striping of a table.
|
* Classes to use for the striping of a table.
|
||||||
|
Loading…
Reference in New Issue
Block a user