1
0
mirror of https://github.com/DataTables/DataTables.git synced 2024-11-29 11: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:
Allan Jardine 2013-05-16 17:16:04 +01:00
parent 67d19ac9e4
commit 4913226456
5 changed files with 127 additions and 118 deletions

View File

@ -45,8 +45,14 @@ _api.register( 'row().data()', function ( data ) {
// return undefined;
// @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 () {
return this.length ? this[0] : undefined;

View File

@ -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
* @param {object} oSettings dataTables settings object
@ -8,107 +42,42 @@
function _fnSort ( oSettings, bApplyClasses )
{
var
i, iLen, j, jLen, k, kLen,
i, ien, iLen, j, jLen, k, kLen,
sDataType, nTh,
aSort = [],
aiOrig = [],
oExtSort = DataTable.ext.oSort,
aoData = oSettings.aoData,
aoColumns = oSettings.aoColumns,
oAria = oSettings.oLanguage.oAria,
fnFormatter, aDataSort, data, iCol, sType, oSort,
iFormatters = 0,
aaNestedSort = ( oSettings.aaSortingFixed !== null ) ?
oSettings.aaSortingFixed.concat( oSettings.aaSorting ) :
oSettings.aaSorting.slice();
aDataSort, data, iCol, sType, oSort,
formatters = 0,
nestedSort = oSettings.aaSortingFixed.concat( oSettings.aaSorting ),
sortCol,
displayMaster = oSettings.aiDisplayMaster;
/* Flatten the aDataSort inner arrays into a single array, otherwise we have nested
* loops in multiple locations
*/
for ( i=0 ; i<aaNestedSort.length ; i++ )
{
aDataSort = aoColumns[ aaNestedSort[i][0] ].aDataSort;
aDataSort = _fnSortFlatten( oSettings );
for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )
{
iCol = aDataSort[k];
sType = aoColumns[ iCol ].sType || 'string';
fnFormatter = oExtSort[ sType+"-pre" ];
for ( i=0, ien=aDataSort.length ; i<ien ; i++ ) {
sortCol = aDataSort[i];
aSort.push( {
col: iCol,
dir: aaNestedSort[i][1],
index: aaNestedSort[i][2],
type: sType,
format: fnFormatter
} );
// Track if we can use the formatter method
if ( fnFormatter )
{
iFormatters++;
}
// Track if we can use the fast sort algorithm
if ( sortCol.formatter ) {
formatters++;
}
// Load the data needed for the sort, for each cell
_fnSortColumn( oSettings, sortCol.col );
}
/* No sorting required if server-side or no sorting array */
if ( !oSettings.oFeatures.bServerSide && aSort.length !== 0 )
{
/* If there is a sorting data type, and a function belonging to it, then we need to
* get the data from the developer's function and apply it for this column
*/
for ( i=0 ; i<aSort.length ; 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=displayMaster.length ; i<iLen ; i++ ) {
aiOrig[ displayMaster[i] ] = i;
}
/* 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)
* 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):
@ -130,29 +99,27 @@ function _fnSort ( oSettings, bApplyClasses )
* 15% faster, so the second is only maintained for backwards compatibility with sorting
* methods which do not have a pre-sort formatting function.
*/
if ( iFormatters === aSort.length ) {
if ( formatters === aSort.length ) {
// All sort types have formatting functions
oSettings.aiDisplayMaster.sort( function ( a, b ) {
displayMaster.sort( function ( a, b ) {
var
x, y, k, test, sort,
len=aSort.length,
dataA = aoData[a]._aSortData,
dataB = aoData[b]._aSortData;
for ( k=0 ; k<len ; k++ )
{
for ( k=0 ; k<len ; k++ ) {
sort = aSort[k];
x = dataA[ sort.col ];
y = dataB[ sort.col ];
test = x<y ? -1 : x>y ? 1 : 0;
if ( test !== 0 )
{
if ( test !== 0 ) {
return sort.dir === 'asc' ? test : -test;
}
}
x = aiOrig[a];
y = aiOrig[b];
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)
// Not all sort types have formatting methods, so we have to call their sorting
// methods.
oSettings.aiDisplayMaster.sort( function ( a, b ) {
displayMaster.sort( function ( a, b ) {
var
x, y, k, l, test, sort,
len=aSort.length,
dataA = aoData[a]._aSortData,
dataB = aoData[b]._aSortData;
for ( k=0 ; k<len ; k++ )
{
for ( k=0 ; k<len ; k++ ) {
sort = aSort[k];
x = dataA[ sort.col ];
y = dataB[ sort.col ];
test = oExtSort[ sort.type+"-"+sort.dir ]( x, y );
if ( test !== 0 )
{
if ( test !== 0 ) {
return test;
}
}
x = aiOrig[a];
y = aiOrig[b];
return x<y ? -1 : x>y ? 1 : 0;
} );
}
}
/* Alter the sorting classes to take account of the changes */
if ( (bApplyClasses === undefined || bApplyClasses) && !oSettings.oFeatures.bDeferRender )
{
@ -202,14 +167,14 @@ function _fnSort ( oSettings, bApplyClasses )
nTh = aoColumns[i].nTh;
nTh.removeAttribute('aria-sort');
nTh.removeAttribute('aria-label');
/* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */
if ( aoColumns[i].bSortable )
{
if ( aSort.length > 0 && aSort[0].col == i )
{
nTh.setAttribute('aria-sort', aSort[0].dir=="asc" ? "ascending" : "descending" );
var nextSort = (aoColumns[i].asSorting[ aSort[0].index+1 ]) ?
aoColumns[i].asSorting[ aSort[0].index+1 ] : aoColumns[i].asSorting[0];
nTh.setAttribute('aria-label', sTitle+
@ -226,7 +191,7 @@ function _fnSort ( oSettings, bApplyClasses )
nTh.setAttribute('aria-label', sTitle);
}
}
/* Tell the draw function that we have sorted the data */
oSettings.bSorted = true;
$(oSettings.oInstance).trigger('sort', oSettings);
@ -374,14 +339,7 @@ function _fnSortingClasses( oSettings )
}
}
if ( oSettings.aaSortingFixed !== null )
{
aaSort = oSettings.aaSortingFixed.concat( oSettings.aaSorting );
}
else
{
aaSort = oSettings.aaSorting.slice();
}
aaSort = _fnSortFlatten( oSettings );
/* Apply the required classes to the header */
for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
@ -392,9 +350,9 @@ function _fnSortingClasses( oSettings )
iFound = -1;
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;
iFound = j;
break;
@ -414,7 +372,7 @@ function _fnSortingClasses( oSettings )
{
sSpanClass = oSettings.aoColumns[i].sSortingClassJUI;
}
else if ( aaSort[iFound][1] == "asc" )
else if ( aaSort[iFound].dir == "asc" )
{
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;
}

View File

@ -133,7 +133,7 @@ DataTable.defaults = {
* } );
* } )
*/
"aaSortingFixed": null,
"aaSortingFixed": [],
/**

View File

@ -43,10 +43,10 @@ DataTable.models.oRow = {
* per sort. This array should not be read from or written to by anything
* other than the master sorting methods.
* @type array
* @default []
* @default null
* @private
*/
"_aSortData": [],
"_aSortData": null,
/**
* Cache of the class name that DataTables has applied to the row, so we

View File

@ -360,10 +360,10 @@ DataTable.models.oSettings = {
* aaSorting).
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
* @type array|null
* @default null
* @type array
* @default []
*/
"aaSortingFixed": null,
"aaSortingFixed": [],
/**
* Classes to use for the striping of a table.