mirror of
https://github.com/DataTables/DataTables.git
synced 2025-03-15 16:29:16 +01:00
Update: Rewrite of column type detection
- Automatic column type detection was a real weak point of v1.9- - it did basically work, but if you then updated a row that didn't match the current data type it would always end up as a string. A good example of this is the ambiguous date "06-06-13" (is it dd-mm-yy or mm-dd-yy?). If it was detected as dd-mm-yy and then you add '05-20-13' to the column (or update an exisiting cell), the type would not match the exisiting value that thus failover to a string. - Type detection is now more rigorous, but still optimised (since it has the potential to take up a significant amount of time). When a row is added or updated, or a cell is updated, the exisiting type is removed from the target column(s) and then, before sorting or filtering, the _fnColumnTypes function checks to see if any column needs to be type detected and do so if needed. This approach allows multiple rows to be added (for example) before the draw is performed and the type actually needs to be calculated. - In future I'd like to have a 'data-ready' type event which will tell DataTables, and any of its components that something wants to work with the data in the table and it should prep the data. The counterpart would be a 'data-invalid' flag which would be set on update, add etc so it knows when an update is needed.
This commit is contained in:
parent
d9ce185f35
commit
16dea34d8c
@ -1 +1 @@
|
|||||||
b4fbf198ab4d7ed709fd4c35dd3201f877408361
|
01c0282a6f2b9b9ff6db0b05bffda9c30ef93d07
|
||||||
|
153
media/js/jquery.dataTables.js
vendored
153
media/js/jquery.dataTables.js
vendored
@ -281,11 +281,7 @@
|
|||||||
oOptions.mData = oOptions.mDataProp;
|
oOptions.mData = oOptions.mDataProp;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( oOptions.sType !== undefined )
|
oCol._sManualType = oOptions.sType;
|
||||||
{
|
|
||||||
oCol.sType = oOptions.sType;
|
|
||||||
oCol._bAutoType = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// `class` is a reserved word in Javascript, so we need to provide
|
// `class` is a reserved word in Javascript, so we need to provide
|
||||||
// the ability to use a valid name for the camel case input
|
// the ability to use a valid name for the camel case input
|
||||||
@ -448,30 +444,58 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
function _fnColumnTypes ( settings )
|
||||||
* Get the sort type based on an input string
|
|
||||||
* @param {string} sData data we wish to know the type of
|
|
||||||
* @returns {string} type (defaults to 'string' if no type can be detected)
|
|
||||||
* @memberof DataTable#oApi
|
|
||||||
*/
|
|
||||||
function _fnDetectType( sData )
|
|
||||||
{
|
{
|
||||||
var aTypes = DataTable.ext.type.detect;
|
var columns = settings.aoColumns;
|
||||||
var iLen = aTypes.length;
|
var data = settings.aoData;
|
||||||
|
var types = DataTable.ext.type.detect;
|
||||||
|
var i, ien, j, jen, k, ken;
|
||||||
|
var col, cell, detectedType, cache;
|
||||||
|
|
||||||
for ( var i=0 ; i<iLen ; i++ )
|
// For each column, spin over the
|
||||||
{
|
for ( i=0, ien=columns.length ; i<ien ; i++ ) {
|
||||||
var sType = aTypes[i]( sData );
|
col = columns[i];
|
||||||
if ( sType !== null )
|
cache = [];
|
||||||
{
|
|
||||||
return sType;
|
if ( ! col.sType && col._sManualType ) {
|
||||||
|
col.sType = col._sManualType;
|
||||||
|
}
|
||||||
|
else if ( ! col.sType ) {
|
||||||
|
for ( j=0, jen=types.length ; j<jen ; j++ ) {
|
||||||
|
for ( k=0, ken=data.length ; k<ken ; k++ ) {
|
||||||
|
// Use a cache array so we only need to get the type data
|
||||||
|
// from the formatter once (when using multiple detectors)
|
||||||
|
if ( cache[k] === undefined ) {
|
||||||
|
cache[k] = _fnGetCellData( settings, k, i, 'type' );
|
||||||
|
}
|
||||||
|
|
||||||
|
detectedType = types[j]( cache[k] );
|
||||||
|
|
||||||
|
// Doesn't match, so break early, since this type can't
|
||||||
|
// apply to this column
|
||||||
|
if ( ! detectedType ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type is valid for all data points in the column - use this
|
||||||
|
// type
|
||||||
|
if ( detectedType ) {
|
||||||
|
col.sType = detectedType;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back - if no type was detected, always use string
|
||||||
|
if ( ! col.sType ) {
|
||||||
|
col.sType = 'string';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'string';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the column ordering that DataTables expects
|
* Get the column ordering that DataTables expects
|
||||||
* @param {object} oSettings dataTables settings object
|
* @param {object} oSettings dataTables settings object
|
||||||
@ -585,8 +609,6 @@
|
|||||||
*/
|
*/
|
||||||
function _fnAddData ( oSettings, aDataIn, nTr, anTds )
|
function _fnAddData ( oSettings, aDataIn, nTr, anTds )
|
||||||
{
|
{
|
||||||
var oCol;
|
|
||||||
|
|
||||||
/* Create the object for storing information about this new row */
|
/* Create the object for storing information about this new row */
|
||||||
var iRow = oSettings.aoData.length;
|
var iRow = oSettings.aoData.length;
|
||||||
var oData = $.extend( true, {}, DataTable.models.oRow, {
|
var oData = $.extend( true, {}, DataTable.models.oRow, {
|
||||||
@ -598,31 +620,11 @@
|
|||||||
|
|
||||||
/* Create the cells */
|
/* Create the cells */
|
||||||
var nTd, sThisType;
|
var nTd, sThisType;
|
||||||
for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
|
var columns = oSettings.aoColumns;
|
||||||
|
for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
|
||||||
{
|
{
|
||||||
oCol = oSettings.aoColumns[i];
|
|
||||||
|
|
||||||
_fnSetCellData( oSettings, iRow, i, _fnGetCellData( oSettings, iRow, i ) );
|
_fnSetCellData( oSettings, iRow, i, _fnGetCellData( oSettings, iRow, i ) );
|
||||||
|
columns[i].sType = null;
|
||||||
/* See if we should auto-detect the column type */
|
|
||||||
if ( oCol._bAutoType && oCol.sType != 'string' )
|
|
||||||
{
|
|
||||||
/* Attempt to auto detect the type - same as _fnGatherData() */
|
|
||||||
var sVarType = _fnGetCellData( oSettings, iRow, i, 'type' );
|
|
||||||
if ( sVarType !== null && sVarType !== '' )
|
|
||||||
{
|
|
||||||
sThisType = _fnDetectType( sVarType );
|
|
||||||
if ( oCol.sType === null )
|
|
||||||
{
|
|
||||||
oCol.sType = sThisType;
|
|
||||||
}
|
|
||||||
else if ( oCol.sType != sThisType && oCol.sType != "html" )
|
|
||||||
{
|
|
||||||
/* String is always the 'fallback' option */
|
|
||||||
oCol.sType = 'string';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add to the display array */
|
/* Add to the display array */
|
||||||
@ -1085,9 +1087,10 @@
|
|||||||
* the sort and filter methods can subscribe to it. That will required
|
* the sort and filter methods can subscribe to it. That will required
|
||||||
* initialisation options for sorting, which is why it is not already baked in
|
* initialisation options for sorting, which is why it is not already baked in
|
||||||
*/
|
*/
|
||||||
function _fnInvalidateRow( settings, rowIdx, src )
|
function _fnInvalidateRow( settings, rowIdx, src, column )
|
||||||
{
|
{
|
||||||
var row = settings.aoData[ rowIdx ];
|
var row = settings.aoData[ rowIdx ];
|
||||||
|
var i, ien;
|
||||||
|
|
||||||
// Are we reading last data from DOM or the data object?
|
// Are we reading last data from DOM or the data object?
|
||||||
if ( src === 'dom' || (! src && row.src === 'dom') ) {
|
if ( src === 'dom' || (! src && row.src === 'dom') ) {
|
||||||
@ -1098,13 +1101,25 @@
|
|||||||
// Reading from data object, update the DOM
|
// Reading from data object, update the DOM
|
||||||
var cells = row.anCells;
|
var cells = row.anCells;
|
||||||
|
|
||||||
for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
|
for ( i=0, ien=cells.length ; i<ien ; i++ ) {
|
||||||
cells[i].innerHTML = _fnGetCellData( settings, rowIdx, i, 'display' );
|
cells[i].innerHTML = _fnGetCellData( settings, rowIdx, i, 'display' );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
row._aSortData = null;
|
row._aSortData = null;
|
||||||
row._aFilterData = null;
|
row._aFilterData = null;
|
||||||
|
|
||||||
|
// Invalidate the type for a specific column (if given) or all columns since
|
||||||
|
// the data might have changed
|
||||||
|
var cols = settings.aoColumns;
|
||||||
|
if ( column !== undefined ) {
|
||||||
|
cols[ column ].sType = null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for ( i=0, ien=cols.length ; i<ien ; i++ ) {
|
||||||
|
cols[i].sType = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2263,6 +2278,10 @@
|
|||||||
oPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive;
|
oPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Resolve any column types that are unknown due to addition or invalidation
|
||||||
|
// @todo As per sort - can this be moved into an event handler?
|
||||||
|
_fnColumnTypes( oSettings );
|
||||||
|
|
||||||
/* In server-side processing all filtering is done by the server, so no point hanging around here */
|
/* In server-side processing all filtering is done by the server, so no point hanging around here */
|
||||||
if ( !oSettings.oFeatures.bServerSide )
|
if ( !oSettings.oFeatures.bServerSide )
|
||||||
{
|
{
|
||||||
@ -3901,7 +3920,6 @@
|
|||||||
var
|
var
|
||||||
i, ien, iLen, j, jLen, k, kLen,
|
i, ien, iLen, j, jLen, k, kLen,
|
||||||
sDataType, nTh,
|
sDataType, nTh,
|
||||||
aSort = [],
|
|
||||||
aiOrig = [],
|
aiOrig = [],
|
||||||
oExtSort = DataTable.ext.type.sort,
|
oExtSort = DataTable.ext.type.sort,
|
||||||
aoData = oSettings.aoData,
|
aoData = oSettings.aoData,
|
||||||
@ -3910,9 +3928,13 @@
|
|||||||
formatters = 0,
|
formatters = 0,
|
||||||
nestedSort = oSettings.aaSortingFixed.concat( oSettings.aaSorting ),
|
nestedSort = oSettings.aaSortingFixed.concat( oSettings.aaSorting ),
|
||||||
sortCol,
|
sortCol,
|
||||||
displayMaster = oSettings.aiDisplayMaster;
|
displayMaster = oSettings.aiDisplayMaster,
|
||||||
|
aSort = _fnSortFlatten( oSettings );
|
||||||
|
|
||||||
aSort = _fnSortFlatten( oSettings );
|
// Resolve any column types that are unknown due to addition or invalidation
|
||||||
|
// @todo Can this be moved into a 'data-ready' handler which is called when
|
||||||
|
// data is going to be used in the table?
|
||||||
|
_fnColumnTypes( oSettings );
|
||||||
|
|
||||||
for ( i=0, ien=aSort.length ; i<ien ; i++ ) {
|
for ( i=0, ien=aSort.length ; i<ien ; i++ ) {
|
||||||
sortCol = aSort[i];
|
sortCol = aSort[i];
|
||||||
@ -5408,7 +5430,6 @@
|
|||||||
"_fnGetWidestNode": _fnGetWidestNode,
|
"_fnGetWidestNode": _fnGetWidestNode,
|
||||||
"_fnGetMaxLenString": _fnGetMaxLenString,
|
"_fnGetMaxLenString": _fnGetMaxLenString,
|
||||||
"_fnStringToCss": _fnStringToCss,
|
"_fnStringToCss": _fnStringToCss,
|
||||||
"_fnDetectType": _fnDetectType,
|
|
||||||
"_fnSettingsFromNode": _fnSettingsFromNode,
|
"_fnSettingsFromNode": _fnSettingsFromNode,
|
||||||
"_fnGetDataMaster": _fnGetDataMaster,
|
"_fnGetDataMaster": _fnGetDataMaster,
|
||||||
"_fnEscapeRegex": _fnEscapeRegex,
|
"_fnEscapeRegex": _fnEscapeRegex,
|
||||||
@ -7982,7 +8003,7 @@
|
|||||||
|
|
||||||
// Set
|
// Set
|
||||||
_fnSetCellData( ctx[0], cell[0].row, cell[0].column, data );
|
_fnSetCellData( ctx[0], cell[0].row, cell[0].column, data );
|
||||||
_fnInvalidateRow( ctx[0], cell[0].row, 'data' );
|
_fnInvalidateRow( ctx[0], cell[0].row, 'data', cell[0].column );
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
} );
|
} );
|
||||||
@ -8613,13 +8634,13 @@
|
|||||||
"bVisible": null,
|
"bVisible": null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag to indicate to the type detection method if the automatic type
|
* Store for manual type assignment using the `column.type` option. This
|
||||||
* detection should be used, or if a column type (sType) has been specified
|
* is held in store so we can manipulate the column's `sType` property.
|
||||||
* @type boolean
|
* @type string
|
||||||
* @default true
|
* @default null
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
"_bAutoType": true,
|
"_sManualType": null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag to indicate if HTML5 data attributes should be used as the data
|
* Flag to indicate if HTML5 data attributes should be used as the data
|
||||||
@ -13534,7 +13555,8 @@
|
|||||||
return a.toLowerCase();
|
return a.toLowerCase();
|
||||||
},
|
},
|
||||||
|
|
||||||
// string-asc and -desc are retained only for compatibility with
|
// string-asc and -desc are retained only for compatibility with the old
|
||||||
|
// sort methods
|
||||||
"string-asc": function ( x, y )
|
"string-asc": function ( x, y )
|
||||||
{
|
{
|
||||||
return ((x < y) ? -1 : ((x > y) ? 1 : 0));
|
return ((x < y) ? -1 : ((x > y) ? 1 : 0));
|
||||||
@ -13606,6 +13628,17 @@
|
|||||||
}
|
}
|
||||||
] );
|
] );
|
||||||
|
|
||||||
|
// date
|
||||||
|
// numeric (inc. formatted)
|
||||||
|
// html numbers (inc. formatted)
|
||||||
|
// html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
add sort types (currency, html numbers, formatted numbers, formatted html numbers)
|
||||||
|
currency is just formatted numbers
|
||||||
|
|
||||||
|
|
||||||
// Filter formatting functions. See model.ext.ofnSearch for information about
|
// Filter formatting functions. See model.ext.ofnSearch for information about
|
||||||
|
Loading…
x
Reference in New Issue
Block a user