client/lanes/components/grid/vendor/jquery.dataTables.js in lanes-0.1.2 vs client/lanes/components/grid/vendor/jquery.dataTables.js in lanes-0.1.5
- old
+ new
@@ -1,13 +1,13 @@
-/*! DataTables 1.10.0
+/*! DataTables 1.10.4
* ©2008-2014 SpryMedia Ltd - datatables.net/license
*/
/**
* @summary DataTables
* @description Paginate, search and order HTML tables
- * @version 1.10.0
+ * @version 1.10.4
* @file jquery.dataTables.js
* @author SpryMedia Ltd (www.sprymedia.co.uk)
* @contact www.sprymedia.co.uk/contact
* @copyright Copyright 2008-2014 SpryMedia Ltd.
*
@@ -20,11 +20,11 @@
*
* For details please refer to: http://www.datatables.net
*/
/*jslint evil: true, undef: true, browser: true */
-/*globals $,require,jQuery,define,_selector_run,_selector_opts,_selector_first,_selector_row_indexes,_ext,_Api,_api_register,_api_registerPlural,_re_new_lines,_re_html,_re_formatted_numeric,_re_escape_regex,_empty,_intVal,_numToDecimal,_isNumber,_isHtml,_htmlNumeric,_pluck,_pluck_order,_range,_stripHtml,_unique,_fnBuildAjax,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnAjaxDataSrc,_fnAddColumn,_fnColumnOptions,_fnAdjustColumnSizing,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnVisbleColumns,_fnGetColumns,_fnColumnTypes,_fnApplyColumnDefs,_fnHungarianMap,_fnCamelToHungarian,_fnLanguageCompat,_fnBrowserDetect,_fnAddData,_fnAddTr,_fnNodeToDataIndex,_fnNodeToColumnIndex,_fnGetCellData,_fnSetCellData,_fnSplitObjNotation,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnGetDataMaster,_fnClearTable,_fnDeleteIndex,_fnInvalidateRow,_fnGetRowElements,_fnCreateTr,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAddOptionsHtml,_fnDetectHeader,_fnGetUniqueThs,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnFilterCreateSearch,_fnEscapeRegex,_fnFilterData,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnInfoMacros,_fnInitialise,_fnInitComplete,_fnLengthChange,_fnFeatureHtmlLength,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnFeatureHtmlTable,_fnScrollDraw,_fnApplyToChildren,_fnCalculateColumnWidths,_fnThrottle,_fnConvertToWidth,_fnScrollingWidthAdjust,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnScrollBarWidth,_fnSortFlatten,_fnSort,_fnSortAria,_fnSortListener,_fnSortAttachListener,_fnSortingClasses,_fnSortData,_fnSaveState,_fnLoadState,_fnSettingsFromNode,_fnLog,_fnMap,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnLengthOverflow,_fnRenderer,_fnDataSource,_fnRowAttributes*/
+/*globals $,require,jQuery,define,_selector_run,_selector_opts,_selector_first,_selector_row_indexes,_ext,_Api,_api_register,_api_registerPlural,_re_new_lines,_re_html,_re_formatted_numeric,_re_escape_regex,_empty,_intVal,_numToDecimal,_isNumber,_isHtml,_htmlNumeric,_pluck,_pluck_order,_range,_stripHtml,_unique,_fnBuildAjax,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnAjaxDataSrc,_fnAddColumn,_fnColumnOptions,_fnAdjustColumnSizing,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnVisbleColumns,_fnGetColumns,_fnColumnTypes,_fnApplyColumnDefs,_fnHungarianMap,_fnCamelToHungarian,_fnLanguageCompat,_fnBrowserDetect,_fnAddData,_fnAddTr,_fnNodeToDataIndex,_fnNodeToColumnIndex,_fnGetCellData,_fnSetCellData,_fnSplitObjNotation,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnGetDataMaster,_fnClearTable,_fnDeleteIndex,_fnInvalidate,_fnGetRowElements,_fnCreateTr,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAddOptionsHtml,_fnDetectHeader,_fnGetUniqueThs,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnFilterCreateSearch,_fnEscapeRegex,_fnFilterData,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnInfoMacros,_fnInitialise,_fnInitComplete,_fnLengthChange,_fnFeatureHtmlLength,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnFeatureHtmlTable,_fnScrollDraw,_fnApplyToChildren,_fnCalculateColumnWidths,_fnThrottle,_fnConvertToWidth,_fnScrollingWidthAdjust,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnScrollBarWidth,_fnSortFlatten,_fnSort,_fnSortAria,_fnSortListener,_fnSortAttachListener,_fnSortingClasses,_fnSortData,_fnSaveState,_fnLoadState,_fnSettingsFromNode,_fnLog,_fnMap,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnLengthOverflow,_fnRenderer,_fnDataSource,_fnRowAttributes*/
(/** @lends <global> */function( window, document, undefined ) {
(function( factory ) {
"use strict";
@@ -36,11 +36,10 @@
else if ( typeof exports === 'object' ) {
// Node/CommonJS
factory( require( 'jquery' ) );
}
else if ( jQuery && !jQuery.fn.dataTable ) {
-
// Define using browser globals otherwise
// Prevent multiple instantiations if the script is loaded twice
factory( jQuery );
}
}
@@ -104,22 +103,23 @@
var _api_registerPlural; // DataTable.Api.registerPlural
var _re_dic = {};
var _re_new_lines = /[\r\n]/g;
var _re_html = /<.*?>/g;
- var _re_date_start = /^[\d\+\-a-zA-Z]/;
+ var _re_date_start = /^[\w\+\-]/;
+ var _re_date_end = /[\w\+\-]$/;
// Escape regular expression special characters
var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' );
// U+2009 is thin space and U+202F is narrow no-break space, both used in many
// standards as thousands separators
var _re_formatted_numeric = /[',$£€¥%\u2009\u202F]/g;
var _empty = function ( d ) {
- return !d || d === '-' ? true : false;
+ return !d || d === true || d === '-' ? true : false;
};
var _intVal = function ( s ) {
var integer = parseInt( s, 10 );
@@ -131,11 +131,11 @@
var _numToDecimal = function ( num, decimalPoint ) {
// Cache created regular expressions for speed as this function is called often
if ( ! _re_dic[ decimalPoint ] ) {
_re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' );
}
- return typeof num === 'string' ?
+ return typeof num === 'string' && decimalPoint !== '.' ?
num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) :
num;
};
@@ -148,17 +148,17 @@
if ( formatted && strType ) {
d = d.replace( _re_formatted_numeric, '' );
}
- return !d || d==='-' || (!isNaN( parseFloat(d) ) && isFinite( d ));
+ return _empty( d ) || (!isNaN( parseFloat(d) ) && isFinite( d ));
};
// A string without HTML in it can be considered to be HTML still
var _isHtml = function ( d ) {
- return !d || typeof d === 'string';
+ return _empty( d ) || typeof d === 'string';
};
var _htmlNumeric = function ( d, decimalPoint, formatted ) {
if ( _empty( d ) ) {
@@ -208,11 +208,13 @@
// Could have the test in the loop for slightly smaller code, but speed
// is essential here
if ( prop2 !== undefined ) {
for ( ; i<ien ; i++ ) {
- out.push( a[ order[i] ][ prop ][ prop2 ] );
+ if ( a[ order[i] ][ prop ] ) {
+ out.push( a[ order[i] ][ prop ][ prop2 ] );
+ }
}
}
else {
for ( ; i<ien ; i++ ) {
out.push( a[ order[i] ][ prop ] );
@@ -243,10 +245,24 @@
return out;
};
+ var _removeEmpty = function ( a )
+ {
+ var out = [];
+
+ for ( var i=0, ien=a.length ; i<ien ; i++ ) {
+ if ( a[i] ) { // careful - will remove all falsy values!
+ out.push( a[i] );
+ }
+ }
+
+ return out;
+ };
+
+
var _stripHtml = function ( d ) {
return d.replace( _re_html, '' );
};
@@ -308,11 +324,10 @@
if ( match && hungarian.indexOf(match[1]+' ') !== -1 )
{
newKey = key.replace( match[0], match[2].toLowerCase() );
map[ newKey ] = key;
- //console.log( key, match );
if ( match[1] === 'o' )
{
_fnHungarianMap( o[key] );
}
}
@@ -434,10 +449,22 @@
_fnCompatMap( init, 'orderFixed', 'aaSortingFixed' );
_fnCompatMap( init, 'paging', 'bPaginate' );
_fnCompatMap( init, 'pagingType', 'sPaginationType' );
_fnCompatMap( init, 'pageLength', 'iDisplayLength' );
_fnCompatMap( init, 'searching', 'bFilter' );
+
+ // Column search objects are in an array, so it needs to be converted
+ // element by element
+ var searchCols = init.aoSearchCols;
+
+ if ( searchCols ) {
+ for ( var i=0, ien=searchCols.length ; i<ien ; i++ ) {
+ if ( searchCols[i] ) {
+ _fnCamelToHungarian( DataTable.models.oSearch, searchCols[i] );
+ }
+ }
+ }
}
/**
* Provide backwards compatibility for column options. Note that the new options
@@ -590,11 +617,11 @@
if ( ! oCol.sWidthOrig ) {
// Width attribute
oCol.sWidthOrig = th.attr('width') || null;
// Style attribute
- var t = (th.attr('style') || '').match(/width:\s*(\d+[pxem%])/);
+ var t = (th.attr('style') || '').match(/width:\s*(\d+[pxem%]+)/);
if ( t ) {
oCol.sWidthOrig = t[1];
}
}
@@ -648,21 +675,27 @@
};
oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && (
attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter)
);
- oCol.fnGetData = function (oData, sSpecific) {
- var innerData = mData( oData, sSpecific );
+ oCol.fnGetData = function (rowData, type, meta) {
+ var innerData = mData( rowData, type, undefined, meta );
- if ( oCol.mRender && (sSpecific && sSpecific !== '') )
- {
- return mRender( innerData, sSpecific, oData );
- }
- return innerData;
+ return mRender && type ?
+ mRender( innerData, type, rowData, meta ) :
+ innerData;
};
- oCol.fnSetData = _fnSetObjectDataFn( mDataSrc );
+ oCol.fnSetData = function ( rowData, val, meta ) {
+ return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta );
+ };
+ // Indicate if DataTables should read DOM data as an object or array
+ // Used in _fnGetRowElements
+ if ( typeof mDataSrc !== 'number' ) {
+ oSettings._rowReadObject = true;
+ }
+
/* Feature sorting overrides column specific when off */
if ( !oSettings.oFeatures.bSort )
{
oCol.bSortable = false;
th.addClass( oClasses.sSortableNone ); // Have to add class here as order event isn't called
@@ -823,17 +856,24 @@
cache[k] = _fnGetCellData( settings, k, i, 'type' );
}
detectedType = types[j]( cache[k], settings );
- // Doesn't match, so break early, since this type can't
- // apply to this column. Also, HTML is a special case since
- // it is so similar to `string`. Just a single match is
- // needed for a column to be html type
- if ( ! detectedType || detectedType === 'html' ) {
+ // If null, then this type can't apply to this column, so
+ // rather than testing all cells, break out. There is an
+ // exception for the last type which is `html`. We need to
+ // scan all rows since it is possible to mix string and HTML
+ // types
+ if ( ! detectedType && j !== types.length-1 ) {
break;
}
+
+ // Only a single match is needed for html type since it is
+ // bottom of the pile and very similar to string
+ if ( detectedType === 'html' ) {
+ break;
+ }
}
// Type is valid for all data points in the column - use this
// type
if ( detectedType ) {
@@ -968,12 +1008,12 @@
}
/* Add to the display array */
oSettings.aiDisplayMaster.push( iRow );
- /* Create the DOM information */
- if ( !oSettings.oFeatures.bDeferRender )
+ /* Create the DOM information, or register it if already present */
+ if ( nTr || ! oSettings.oFeatures.bDeferRender )
{
_fnCreateTr( oSettings, iRow, nTr, anTds );
}
return iRow;
@@ -1033,68 +1073,74 @@
}
/**
* Get the data for a given cell from the internal cache, taking into account data mapping
- * @param {object} oSettings dataTables settings object
- * @param {int} iRow aoData row id
- * @param {int} iCol Column index
- * @param {string} sSpecific data get type ('display', 'type' 'filter' 'sort')
+ * @param {object} settings dataTables settings object
+ * @param {int} rowIdx aoData row id
+ * @param {int} colIdx Column index
+ * @param {string} type data get type ('display', 'type' 'filter' 'sort')
* @returns {*} Cell data
* @memberof DataTable#oApi
*/
- function _fnGetCellData( oSettings, iRow, iCol, sSpecific )
+ function _fnGetCellData( settings, rowIdx, colIdx, type )
{
- var oCol = oSettings.aoColumns[iCol];
- var oData = oSettings.aoData[iRow]._aData;
- var sData = oCol.fnGetData( oData, sSpecific );
+ var draw = settings.iDraw;
+ var col = settings.aoColumns[colIdx];
+ var rowData = settings.aoData[rowIdx]._aData;
+ var defaultContent = col.sDefaultContent;
+ var cellData = col.fnGetData( rowData, type, {
+ settings: settings,
+ row: rowIdx,
+ col: colIdx
+ } );
- if ( sData === undefined )
- {
- if ( oSettings.iDrawError != oSettings.iDraw && oCol.sDefaultContent === null )
- {
- _fnLog( oSettings, 0, "Requested unknown parameter "+
- (typeof oCol.mData=='function' ? '{function}' : "'"+oCol.mData+"'")+
- " for row "+iRow, 4 );
- oSettings.iDrawError = oSettings.iDraw;
+ if ( cellData === undefined ) {
+ if ( settings.iDrawError != draw && defaultContent === null ) {
+ _fnLog( settings, 0, "Requested unknown parameter "+
+ (typeof col.mData=='function' ? '{function}' : "'"+col.mData+"'")+
+ " for row "+rowIdx, 4 );
+ settings.iDrawError = draw;
}
- return oCol.sDefaultContent;
+ return defaultContent;
}
/* When the data source is null, we can use default column data */
- if ( (sData === oData || sData === null) && oCol.sDefaultContent !== null )
- {
- sData = oCol.sDefaultContent;
+ if ( (cellData === rowData || cellData === null) && defaultContent !== null ) {
+ cellData = defaultContent;
}
- else if ( typeof sData === 'function' )
- {
- // If the data source is a function, then we run it and use the return
- return sData();
+ else if ( typeof cellData === 'function' ) {
+ // If the data source is a function, then we run it and use the return,
+ // executing in the scope of the data object (for instances)
+ return cellData.call( rowData );
}
- if ( sData === null && sSpecific == 'display' )
- {
+ if ( cellData === null && type == 'display' ) {
return '';
}
- return sData;
+ return cellData;
}
/**
* Set the value for a specific cell, into the internal data cache
- * @param {object} oSettings dataTables settings object
- * @param {int} iRow aoData row id
- * @param {int} iCol Column index
+ * @param {object} settings dataTables settings object
+ * @param {int} rowIdx aoData row id
+ * @param {int} colIdx Column index
* @param {*} val Value to set
* @memberof DataTable#oApi
*/
- function _fnSetCellData( oSettings, iRow, iCol, val )
+ function _fnSetCellData( settings, rowIdx, colIdx, val )
{
- var oCol = oSettings.aoColumns[iCol];
- var oData = oSettings.aoData[iRow]._aData;
+ var col = settings.aoColumns[colIdx];
+ var rowData = settings.aoData[rowIdx]._aData;
- oCol.fnSetData( oData, val );
+ col.fnSetData( rowData, val, {
+ settings: settings,
+ row: rowIdx,
+ col: colIdx
+ } );
}
// Private variable that is used to match action syntax in the data property object
var __reArray = /\[.*?\]$/;
@@ -1106,11 +1152,11 @@
* @return {array} Split string
*/
function _fnSplitObjNotation( str )
{
return $.map( str.match(/(\\.|[^\.])+/g), function ( s ) {
- return s.replace('\\.', '.');
+ return s.replace(/\\./g, '.');
} );
}
/**
@@ -1130,28 +1176,28 @@
if ( val ) {
o[key] = _fnGetObjectDataFn( val );
}
} );
- return function (data, type, extra) {
+ return function (data, type, row, meta) {
var t = o[type] || o._;
return t !== undefined ?
- t(data, type, extra) :
+ t(data, type, row, meta) :
data;
};
}
else if ( mSource === null )
{
/* Give an empty string for rendering / sorting etc */
- return function (data, type) {
+ return function (data) { // type, row and meta also passed, but not used
return data;
};
}
else if ( typeof mSource === 'function' )
{
- return function (data, type, extra) {
- return mSource( data, type, extra );
+ return function (data, type, row, meta) {
+ return mSource( data, type, row, meta );
};
}
else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) )
{
@@ -1220,18 +1266,18 @@
}
return data;
};
- return function (data, type) {
+ return function (data, type) { // row and meta also passed, but not used
return fetchData( data, type, mSource );
};
}
else
{
/* Array or flat object mapping */
- return function (data, type) {
+ return function (data, type) { // row and meta also passed, but not used
return data[mSource];
};
}
}
@@ -1255,16 +1301,16 @@
return _fnSetObjectDataFn( mSource._ );
}
else if ( mSource === null )
{
/* Nothing to do when the data source is null */
- return function (data, val) {};
+ return function () {};
}
else if ( typeof mSource === 'function' )
{
- return function (data, val) {
- mSource( data, 'set', val );
+ return function (data, val, meta) {
+ mSource( data, 'set', val, meta );
};
}
else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) )
{
@@ -1330,18 +1376,18 @@
// and assign the value. If it isn't used, then we get the result we want anyway
data[ aLast.replace(__reArray, '') ] = val;
}
};
- return function (data, val) {
+ return function (data, val) { // meta is also passed in, but not used
return setData( data, val, mSource );
};
}
else
{
/* Array or flat object mapping */
- return function (data, val) {
+ return function (data, val) { // meta is also passed in, but not used
data[mSource] = val;
};
}
}
@@ -1404,121 +1450,164 @@
/**
* Mark cached data as invalid such that a re-read of the data will occur when
* the cached data is next requested. Also update from the data source object.
*
* @param {object} settings DataTables settings object
- * @param {int} rowIdx Row index to invalidate
+ * @param {int} rowIdx Row index to invalidate
+ * @param {string} [src] Source to invalidate from: undefined, 'auto', 'dom'
+ * or 'data'
+ * @param {int} [colIdx] Column index to invalidate. If undefined the whole
+ * row will be invalidated
* @memberof DataTable#oApi
*
* @todo For the modularisation of v1.11 this will need to become a callback, so
* 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
*/
- function _fnInvalidateRow( settings, rowIdx, src, column )
+ function _fnInvalidate( settings, rowIdx, src, colIdx )
{
var row = settings.aoData[ rowIdx ];
var i, ien;
+ var cellWrite = function ( cell, col ) {
+ // This is very frustrating, but in IE if you just write directly
+ // to innerHTML, and elements that are overwritten are GC'ed,
+ // even if there is a reference to them elsewhere
+ while ( cell.childNodes.length ) {
+ cell.removeChild( cell.firstChild );
+ }
+ cell.innerHTML = _fnGetCellData( settings, rowIdx, col, 'display' );
+ };
+
// Are we reading last data from DOM or the data object?
if ( src === 'dom' || ((! src || src === 'auto') && row.src === 'dom') ) {
// Read the data from the DOM
- row._aData = _fnGetRowElements( settings, row ).data;
+ row._aData = _fnGetRowElements(
+ settings, row, colIdx, colIdx === undefined ? undefined : row._aData
+ )
+ .data;
}
else {
// Reading from data object, update the DOM
var cells = row.anCells;
if ( cells ) {
- for ( i=0, ien=cells.length ; i<ien ; i++ ) {
- cells[i].innerHTML = _fnGetCellData( settings, rowIdx, i, 'display' );
+ if ( colIdx !== undefined ) {
+ cellWrite( cells[colIdx], colIdx );
}
+ else {
+ for ( i=0, ien=cells.length ; i<ien ; i++ ) {
+ cellWrite( cells[i], i );
+ }
+ }
}
}
+ // For both row and cell invalidation, the cached data for sorting and
+ // filtering is nulled out
row._aSortData = 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;
+ if ( colIdx !== undefined ) {
+ cols[ colIdx ].sType = null;
}
else {
for ( i=0, ien=cols.length ; i<ien ; i++ ) {
cols[i].sType = null;
}
- }
- // Update DataTables special `DT_*` attributes for the row
- _fnRowAttributes( row );
+ // Update DataTables special `DT_*` attributes for the row
+ _fnRowAttributes( row );
+ }
}
/**
* Build a data source object from an HTML row, reading the contents of the
* cells that are in the row.
*
* @param {object} settings DataTables settings object
* @param {node|object} TR element from which to read data or existing row
* object from which to re-read the data from the cells
+ * @param {int} [colIdx] Optional column index
+ * @param {array|object} [d] Data source object. If `colIdx` is given then this
+ * parameter should also be given and will be used to write the data into.
+ * Only the column in question will be written
* @returns {object} Object with two parameters: `data` the data read, in
* document order, and `cells` and array of nodes (they can be useful to the
* caller, so rather than needing a second traversal to get them, just return
* them from here).
* @memberof DataTable#oApi
*/
- function _fnGetRowElements( settings, row )
+ function _fnGetRowElements( settings, row, colIdx, d )
{
var
- d = [],
tds = [],
td = row.firstChild,
name, col, o, i=0, contents,
- columns = settings.aoColumns;
+ columns = settings.aoColumns,
+ objectRead = settings._rowReadObject;
- var attr = function ( str, data, td ) {
+ // Allow the data object to be passed in, or construct
+ d = d || objectRead ? {} : [];
+
+ var attr = function ( str, td ) {
if ( typeof str === 'string' ) {
var idx = str.indexOf('@');
if ( idx !== -1 ) {
- var src = str.substring( idx+1 );
- o[ '@'+src ] = td.getAttribute( src );
+ var attr = str.substring( idx+1 );
+ var setter = _fnSetObjectDataFn( str );
+ setter( d, td.getAttribute( attr ) );
}
}
};
+ // Read data from a cell and store into the data object
var cellProcess = function ( cell ) {
- col = columns[i];
- contents = $.trim(cell.innerHTML);
+ if ( colIdx === undefined || colIdx === i ) {
+ col = columns[i];
+ contents = $.trim(cell.innerHTML);
- if ( col && col._bAttrSrc ) {
- o = {
- display: contents
- };
+ if ( col && col._bAttrSrc ) {
+ var setter = _fnSetObjectDataFn( col.mData._ );
+ setter( d, contents );
- attr( col.mData.sort, o, cell );
- attr( col.mData.type, o, cell );
- attr( col.mData.filter, o, cell );
-
- d.push( o );
+ attr( col.mData.sort, cell );
+ attr( col.mData.type, cell );
+ attr( col.mData.filter, cell );
+ }
+ else {
+ // Depending on the `data` option for the columns the data can
+ // be read to either an object or an array.
+ if ( objectRead ) {
+ if ( ! col._setter ) {
+ // Cache the setter function
+ col._setter = _fnSetObjectDataFn( col.mData );
+ }
+ col._setter( d, contents );
+ }
+ else {
+ d[i] = contents;
+ }
+ }
}
- else {
- d.push( contents );
- }
- tds.push( cell );
i++;
};
if ( td ) {
- // `tr` element passed in
+ // `tr` element was passed in
while ( td ) {
name = td.nodeName.toUpperCase();
if ( name == "TD" || name == "TH" ) {
cellProcess( td );
+ tds.push( td );
}
td = td.nextSibling;
}
}
@@ -1601,11 +1690,11 @@
}
if ( oCol.fnCreatedCell )
{
oCol.fnCreatedCell.call( oSettings.oInstance,
- nTd, _fnGetCellData( oSettings, iRow, i, 'display' ), rowData, iRow, i
+ nTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i
);
}
}
_fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [nTr, rowData, iRow] );
@@ -1920,11 +2009,13 @@
$(nRow).removeClass( aoData._sRowStripe ).addClass( sStripe );
aoData._sRowStripe = sStripe;
}
}
- /* Row callback functions - might want to manipulate the row */
+ // Row callback functions - might want to manipulate the row
+ // iRowCount and j are not currently documented. Are they at all
+ // useful?
_fnCallbackFire( oSettings, 'aoRowCallback', null,
[nRow, aoData._aData, iRowCount, j] );
anRows.push( nRow );
iRowCount++;
@@ -2001,11 +2092,17 @@
if ( holdPosition !== true ) {
settings._iDisplayStart = 0;
}
+ // Let any modules know about the draw hold position state (used by
+ // scrolling internally)
+ settings._drawHold = holdPosition;
+
_fnDraw( settings );
+
+ settings._drawHold = false;
}
/**
* Add the options to the page HTML for the table
@@ -2403,26 +2500,27 @@
}
/**
* Update the table using an Ajax call
- * @param {object} oSettings dataTables settings object
+ * @param {object} settings dataTables settings object
* @returns {boolean} Block the table drawing or not
* @memberof DataTable#oApi
*/
- function _fnAjaxUpdate( oSettings )
+ function _fnAjaxUpdate( settings )
{
- if ( oSettings.bAjaxDataGet )
- {
- oSettings.iDraw++;
- _fnProcessingDisplay( oSettings, true );
- var iColumns = oSettings.aoColumns.length;
- var aoData = _fnAjaxParameters( oSettings );
+ if ( settings.bAjaxDataGet ) {
+ settings.iDraw++;
+ _fnProcessingDisplay( settings, true );
- _fnBuildAjax( oSettings, aoData, function(json) {
- _fnAjaxUpdateDraw( oSettings, json );
- }, oSettings );
+ _fnBuildAjax(
+ settings,
+ _fnAjaxParameters( settings ),
+ function(json) {
+ _fnAjaxUpdateDraw( settings, json );
+ }
+ );
return false;
}
return true;
}
@@ -2552,15 +2650,14 @@
// v1.10 uses camelCase variables, while 1.9 uses Hungarian notation.
// Support both
var compat = function ( old, modern ) {
return json[old] !== undefined ? json[old] : json[modern];
};
- var data = _fnAjaxDataSrc( settings, json );
- var draw = compat( 'sEcho', 'draw' );
- var recordsTotal = compat( 'totaliTotalRecords', 'recordsTotal' );
- var rocordsFiltered = compat( 'iTotalDisplayRecords', 'recordsFiltered' );
+ var draw = compat( 'sEcho', 'draw' );
+ var recordsTotal = compat( 'iTotalRecords', 'recordsTotal' );
+ var recordsFiltered = compat( 'iTotalDisplayRecords', 'recordsFiltered' );
if ( draw ) {
// Protect against out of sequence returns
if ( draw*1 < settings.iDraw ) {
return;
@@ -2568,12 +2665,13 @@
settings.iDraw = draw * 1;
}
_fnClearTable( settings );
settings._iRecordsTotal = parseInt(recordsTotal, 10);
- settings._iRecordsDisplay = parseInt(rocordsFiltered, 10);
+ settings._iRecordsDisplay = parseInt(recordsFiltered, 10);
+ var data = _fnAjaxDataSrc( settings, json );
for ( var i=0, ien=data.length ; i<ien ; i++ ) {
_fnAddData( settings, data[i] );
}
settings.aiDisplay = settings.aiDisplayMaster.slice();
@@ -2623,15 +2721,16 @@
*/
function _fnFeatureHtmlFilter ( settings )
{
var classes = settings.oClasses;
var tableId = settings.sTableId;
+ var language = settings.oLanguage;
var previousSearch = settings.oPreviousSearch;
var features = settings.aanFeatures;
var input = '<input type="search" class="'+classes.sFilterInput+'"/>';
- var str = settings.oLanguage.sSearch;
+ var str = language.sSearch;
str = str.match(/_INPUT_/) ?
str.replace('_INPUT_', input) :
str+input;
var filter = $('<div/>', {
@@ -2657,16 +2756,24 @@
// Need to redraw, without resorting
settings._iDisplayStart = 0;
_fnDraw( settings );
}
};
+
+ var searchDelay = settings.searchDelay !== null ?
+ settings.searchDelay :
+ _fnDataSource( settings ) === 'ssp' ?
+ 400 :
+ 0;
+
var jqFilter = $('input', filter)
- .val( previousSearch.sSearch.replace('"','"') )
+ .val( previousSearch.sSearch )
+ .attr( 'placeholder', language.sSearchPlaceholder )
.bind(
'keyup.DT search.DT input.DT paste.DT cut.DT',
- _fnDataSource( settings ) === 'ssp' ?
- _fnThrottle( searchFn, 400 ):
+ searchDelay ?
+ _fnThrottle( searchFn, searchDelay ) :
searchFn
)
.bind( 'keypress.DT', function(e) {
/* Prevent form submission */
if ( e.keyCode == 13 ) {
@@ -2674,19 +2781,21 @@
}
} )
.attr('aria-controls', tableId);
// Update the input elements whenever the table is filtered
- $(settings.nTable).on( 'filter.DT', function () {
- // IE9 throws an 'unknown error' if document.activeElement is used
- // inside an iframe or frame...
- try {
- if ( jqFilter[0] !== document.activeElement ) {
- jqFilter.val( previousSearch.sSearch );
+ $(settings.nTable).on( 'search.dt.DT', function ( ev, s ) {
+ if ( settings === s ) {
+ // IE9 throws an 'unknown error' if document.activeElement is used
+ // inside an iframe or frame...
+ try {
+ if ( jqFilter[0] !== document.activeElement ) {
+ jqFilter.val( previousSearch.sSearch );
+ }
}
+ catch ( e ) {}
}
- catch ( e ) {}
} );
return filter[0];
}
@@ -2755,19 +2864,27 @@
{
var filters = DataTable.ext.search;
var displayRows = settings.aiDisplay;
var row, rowIdx;
- for ( var i=0, iLen=filters.length ; i<iLen ; i++ ) {
- for ( var j=displayRows.length-1 ; j>=0 ; j-- ) {
+ for ( var i=0, ien=filters.length ; i<ien ; i++ ) {
+ var rows = [];
+
+ // Loop over each row and see if it should be included
+ for ( var j=0, jen=displayRows.length ; j<jen ; j++ ) {
rowIdx = displayRows[ j ];
row = settings.aoData[ rowIdx ];
- if ( ! filters[i]( settings, row._aFilterData, rowIdx, row._aData ) ) {
- displayRows.splice( j, 1 );
+ if ( filters[i]( settings, row._aFilterData, rowIdx, row._aData, j ) ) {
+ rows.push( rowIdx );
}
}
+
+ // So the array reference doesn't break set the results into the
+ // existing array
+ displayRows.length = 0;
+ displayRows.push.apply( displayRows, rows );
}
}
/**
@@ -2875,24 +2992,27 @@
* generate:
*
* ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$
*/
var a = $.map( search.match( /"[^"]+"|[^ ]+/g ) || '', function ( word ) {
- return word.charAt(0) === '"' ?
- word.match( /^"(.*)"$/ )[1] :
- word;
+ if ( word.charAt(0) === '"' ) {
+ var m = word.match( /^"(.*)"$/ );
+ word = m ? m[1] : word;
+ }
+
+ return word.replace('"', '');
} );
search = '^(?=.*?'+a.join( ')(?=.*?' )+').*$';
}
return new RegExp( search, caseInsensitive ? 'i' : '' );
}
/**
- * scape a string such that it can be used in a regular expression
+ * Escape a string such that it can be used in a regular expression
* @param {string} sVal string to escape
* @returns {string} escaped string
* @memberof DataTable#oApi
*/
function _fnEscapeRegex ( sVal )
@@ -2924,15 +3044,23 @@
column = columns[j];
if ( column.bSearchable ) {
cellData = _fnGetCellData( settings, i, j, 'filter' );
- cellData = fomatters[ column.sType ] ?
- fomatters[ column.sType ]( cellData ) :
- cellData !== null ?
- cellData :
- '';
+ if ( fomatters[ column.sType ] ) {
+ cellData = fomatters[ column.sType ]( cellData );
+ }
+
+ // Search in DataTables 1.10 is string based. In 1.11 this
+ // should be altered to also allow strict type checking.
+ if ( cellData === null ) {
+ cellData = '';
+ }
+
+ if ( typeof cellData !== 'string' && cellData.toString ) {
+ cellData = cellData.toString();
+ }
}
else {
cellData = '';
}
@@ -2961,11 +3089,48 @@
}
return wasInvalidated;
}
+
/**
+ * Convert from the internal Hungarian notation to camelCase for external
+ * interaction
+ * @param {object} obj Object to convert
+ * @returns {object} Inverted object
+ * @memberof DataTable#oApi
+ */
+ function _fnSearchToCamel ( obj )
+ {
+ return {
+ search: obj.sSearch,
+ smart: obj.bSmart,
+ regex: obj.bRegex,
+ caseInsensitive: obj.bCaseInsensitive
+ };
+ }
+
+
+
+ /**
+ * Convert from camelCase notation to the internal Hungarian. We could use the
+ * Hungarian convert function here, but this is cleaner
+ * @param {object} obj Object to convert
+ * @returns {object} Inverted object
+ * @memberof DataTable#oApi
+ */
+ function _fnSearchToHung ( obj )
+ {
+ return {
+ sSearch: obj.search,
+ bSmart: obj.smart,
+ bRegex: obj.regex,
+ bCaseInsensitive: obj.caseInsensitive
+ };
+ }
+
+ /**
* Generate the node required for the info display
* @param {object} oSettings dataTables settings object
* @returns {node} Information element
* @memberof DataTable#oApi
*/
@@ -3204,27 +3369,28 @@
var div = $('<div><label/></div>').addClass( classes.sLength );
if ( ! settings.aanFeatures.l ) {
div[0].id = tableId+'_length';
}
- var a = settings.oLanguage.sLengthMenu.split(/(_MENU_)/);
- div.children().append( a.length > 1 ?
- [ a[0], select, a[2] ] :
- a[0]
+ div.children().append(
+ settings.oLanguage.sLengthMenu.replace( '_MENU_', select[0].outerHTML )
);
- // Can't use `select` variable, as user might provide their own select menu
+ // Can't use `select` variable as user might provide their own and the
+ // reference is broken by the use of outerHTML
$('select', div)
.val( settings._iDisplayLength )
.bind( 'change.DT', function(e) {
_fnLengthChange( settings, $(this).val() );
_fnDraw( settings );
} );
// Update node value whenever anything changes the table's length
$(settings.nTable).bind( 'length.dt.DT', function (e, s, len) {
- $('select', div).val( len );
+ if ( settings === s ) {
+ $('select', div).val( len );
+ }
} );
return div[0];
}
@@ -3482,16 +3648,16 @@
} )
.append(
headerClone
.removeAttr('id')
.css( 'margin-left', 0 )
+ .append( captionSide === 'top' ? caption : null )
.append(
table.children('thead')
)
)
)
- .append( captionSide === 'top' ? caption : null )
)
.append(
$(_div, { 'class': classes.sScrollBody } )
.css( {
overflow: 'auto',
@@ -3513,16 +3679,16 @@
$(_div, { 'class': classes.sScrollFootInner } )
.append(
footerClone
.removeAttr('id')
.css( 'margin-left', 0 )
+ .append( captionSide === 'bottom' ? caption : null )
.append(
table.children('tfoot')
)
)
)
- .append( captionSide === 'bottom' ? caption : null )
);
}
var children = scroller.children();
var scrollHead = children[0];
@@ -3842,12 +4008,13 @@
}
/* Adjust the position of the header in case we loose the y-scrollbar */
divBody.scroll();
- /* If sorting or filtering has occurred, jump the scrolling back to the top */
- if ( settings.bSorted || settings.bFiltered ) {
+ // If sorting or filtering has occurred, jump the scrolling back to the top
+ // only if we aren't holding the position
+ if ( (settings.bSorted || settings.bFiltered) && ! settings._drawHold ) {
divBodyEl.scrollTop = 0;
}
}
@@ -3943,11 +4110,12 @@
else
{
// Otherwise construct a single row table with the widest node in the
// data, assign any user defined widths, then insert it into the DOM and
// allow the browser to do all the hard work of calculating table widths
- var tmpTable = $( table.cloneNode( false ) )
+ var tmpTable = $(table).clone() // don't use cloneNode - IE8 will remove events on the main table
+ .empty()
.css( 'visibility', 'hidden' )
.removeAttr( 'id' )
.append( $(oSettings.nTHead).clone( false ) )
.append( $(oSettings.nTFoot).clone( false ) )
.append( $('<tbody><tr/></tbody>') );
@@ -4071,11 +4239,11 @@
* @returns {function} wrapped function
* @memberof DataTable#oApi
*/
function _fnThrottle( fn, freq ) {
var
- frequency = freq || 200,
+ frequency = freq !== undefined ? freq : 200,
last,
timer;
return function () {
var
@@ -4312,15 +4480,19 @@
for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )
{
iCol = aDataSort[k];
sType = aoColumns[ iCol ].sType || 'string';
+ if ( nestedSort[i]._idx === undefined ) {
+ nestedSort[i]._idx = $.inArray( nestedSort[i][1], aoColumns[iCol].asSorting );
+ }
+
aSort.push( {
src: srcCol,
col: iCol,
dir: nestedSort[i][1],
- index: nestedSort[i][2],
+ index: nestedSort[i]._idx,
type: sType,
formatter: DataTable.ext.type.order[ sType+"-pre" ]
} );
}
}
@@ -4519,30 +4691,44 @@
{
var col = settings.aoColumns[ colIdx ];
var sorting = settings.aaSorting;
var asSorting = col.asSorting;
var nextSortIdx;
- var next = function ( a ) {
+ var next = function ( a, overflow ) {
var idx = a._idx;
if ( idx === undefined ) {
idx = $.inArray( a[1], asSorting );
}
- return idx+1 >= asSorting.length ? 0 : idx+1;
+ return idx+1 < asSorting.length ?
+ idx+1 :
+ overflow ?
+ null :
+ 0;
};
+ // Convert to 2D array if needed
+ if ( typeof sorting[0] === 'number' ) {
+ sorting = settings.aaSorting = [ sorting ];
+ }
+
// If appending the sort then we are multi-column sorting
if ( append && settings.oFeatures.bSortMulti ) {
// Are we already doing some kind of sort on this column?
var sortIdx = $.inArray( colIdx, _pluck(sorting, '0') );
if ( sortIdx !== -1 ) {
// Yes, modify the sort
- nextSortIdx = next( sorting[sortIdx] );
+ nextSortIdx = next( sorting[sortIdx], true );
- sorting[sortIdx][1] = asSorting[ nextSortIdx ];
- sorting[sortIdx]._idx = nextSortIdx;
+ if ( nextSortIdx === null ) {
+ sorting.splice( sortIdx, 1 );
+ }
+ else {
+ sorting[sortIdx][1] = asSorting[ nextSortIdx ];
+ sorting[sortIdx]._idx = nextSortIdx;
+ }
}
else {
// No sort on this column yet
sorting.push( [ colIdx, asSorting[0], 0 ] );
sorting[sorting.length-1]._idx = 0;
@@ -4693,98 +4879,111 @@
/**
* Save the state of a table
* @param {object} oSettings dataTables settings object
* @memberof DataTable#oApi
*/
- function _fnSaveState ( oSettings )
+ function _fnSaveState ( settings )
{
- if ( !oSettings.oFeatures.bStateSave || oSettings.bDestroying )
+ if ( !settings.oFeatures.bStateSave || settings.bDestroying )
{
return;
}
/* Store the interesting variables */
- var i, iLen;
- var oState = {
- "iCreate": +new Date(),
- "iStart": oSettings._iDisplayStart,
- "iLength": oSettings._iDisplayLength,
- "aaSorting": $.extend( true, [], oSettings.aaSorting ),
- "oSearch": $.extend( true, {}, oSettings.oPreviousSearch ),
- "aoSearchCols": $.extend( true, [], oSettings.aoPreSearchCols ),
- "abVisCols": _pluck( oSettings.aoColumns, 'bVisible' )
+ var state = {
+ time: +new Date(),
+ start: settings._iDisplayStart,
+ length: settings._iDisplayLength,
+ order: $.extend( true, [], settings.aaSorting ),
+ search: _fnSearchToCamel( settings.oPreviousSearch ),
+ columns: $.map( settings.aoColumns, function ( col, i ) {
+ return {
+ visible: col.bVisible,
+ search: _fnSearchToCamel( settings.aoPreSearchCols[i] )
+ };
+ } )
};
- _fnCallbackFire( oSettings, "aoStateSaveParams", 'stateSaveParams', [oSettings, oState] );
+ _fnCallbackFire( settings, "aoStateSaveParams", 'stateSaveParams', [settings, state] );
- oSettings.fnStateSaveCallback.call( oSettings.oInstance, oSettings, oState );
+ settings.oSavedState = state;
+ settings.fnStateSaveCallback.call( settings.oInstance, settings, state );
}
/**
* Attempt to load a saved table state
* @param {object} oSettings dataTables settings object
* @param {object} oInit DataTables init object so we can override settings
* @memberof DataTable#oApi
*/
- function _fnLoadState ( oSettings, oInit )
+ function _fnLoadState ( settings, oInit )
{
var i, ien;
- var columns = oSettings.aoColumns;
+ var columns = settings.aoColumns;
- if ( ! oSettings.oFeatures.bStateSave ) {
+ if ( ! settings.oFeatures.bStateSave ) {
return;
}
- var oData = oSettings.fnStateLoadCallback.call( oSettings.oInstance, oSettings );
- if ( !oData ) {
+ var state = settings.fnStateLoadCallback.call( settings.oInstance, settings );
+ if ( ! state || ! state.time ) {
return;
}
/* Allow custom and plug-in manipulation functions to alter the saved data set and
* cancelling of loading by returning false
*/
- var abStateLoad = _fnCallbackFire( oSettings, 'aoStateLoadParams', 'stateLoadParams', [oSettings, oData] );
+ var abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, state] );
if ( $.inArray( false, abStateLoad ) !== -1 ) {
return;
}
/* Reject old data */
- var duration = oSettings.iStateDuration;
- if ( duration > 0 && oData.iCreate < +new Date() - (duration*1000) ) {
+ var duration = settings.iStateDuration;
+ if ( duration > 0 && state.time < +new Date() - (duration*1000) ) {
return;
}
// Number of columns have changed - all bets are off, no restore of settings
- if ( columns.length !== oData.aoSearchCols.length ) {
+ if ( columns.length !== state.columns.length ) {
return;
}
- /* Store the saved state so it might be accessed at any time */
- oSettings.oLoadedState = $.extend( true, {}, oData );
+ // Store the saved state so it might be accessed at any time
+ settings.oLoadedState = $.extend( true, {}, state );
- /* Restore key features */
- oSettings._iDisplayStart = oData.iStart;
- oSettings.iInitDisplayStart = oData.iStart;
- oSettings._iDisplayLength = oData.iLength;
- oSettings.aaSorting = $.map( oData.aaSorting, function ( col, i ) {
- return col[0] >= columns.length ?
+ // Restore key features - todo - for 1.11 this needs to be done by
+ // subscribed events
+ settings._iDisplayStart = state.start;
+ settings.iInitDisplayStart = state.start;
+ settings._iDisplayLength = state.length;
+ settings.aaSorting = [];
+
+ // Order
+ $.each( state.order, function ( i, col ) {
+ settings.aaSorting.push( col[0] >= columns.length ?
[ 0, col[1] ] :
- col;
+ col
+ );
} );
- /* Search filtering */
- $.extend( oSettings.oPreviousSearch, oData.oSearch );
- $.extend( true, oSettings.aoPreSearchCols, oData.aoSearchCols );
+ // Search
+ $.extend( settings.oPreviousSearch, _fnSearchToHung( state.search ) );
- /* Column visibility state */
- var visColumns = oData.abVisCols;
- for ( i=0, ien=visColumns.length ; i<ien ; i++ ) {
- columns[i].bVisible = visColumns[i];
+ // Columns
+ for ( i=0, ien=state.columns.length ; i<ien ; i++ ) {
+ var col = state.columns[i];
+
+ // Visibility
+ columns[i].bVisible = col.visible;
+
+ // Search
+ $.extend( settings.aoPreSearchCols[i], _fnSearchToHung( col.search ) );
}
- _fnCallbackFire( oSettings, 'aoStateLoaded', 'stateLoaded', [oSettings, oData] );
+ _fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, state] );
}
/**
* Return the settings object for a particular table
@@ -4978,22 +5177,22 @@
* trigger is fired
* @param {array} args Array of arguments to pass to the callback function /
* trigger
* @memberof DataTable#oApi
*/
- function _fnCallbackFire( settings, callbackArr, event, args )
+ function _fnCallbackFire( settings, callbackArr, e, args )
{
var ret = [];
if ( callbackArr ) {
ret = $.map( settings[callbackArr].slice().reverse(), function (val, i) {
return val.fn.apply( settings.oInstance, args );
} );
}
- if ( event !== null ) {
- $(settings.nTable).trigger( event+'.dt', args );
+ if ( e !== null ) {
+ $(settings.nTable).trigger( e+'.dt', args );
}
return ret;
}
@@ -5004,15 +5203,18 @@
start = settings._iDisplayStart,
end = settings.fnDisplayEnd(),
len = settings._iDisplayLength;
/* If we have space to show extra rows (backing up from the end point - then do so */
- if ( end === settings.fnRecordsDisplay() )
+ if ( start >= end )
{
start = end - len;
}
+ // Keep the start record on the current page
+ start -= (start % len);
+
if ( len === -1 || start < 0 )
{
start = 0;
}
@@ -5980,10 +6182,11 @@
"bSortCellsTop",
"iTabIndex",
"fnStateLoadCallback",
"fnStateSaveCallback",
"renderer",
+ "searchDelay",
[ "iCookieDuration", "iStateDuration" ], // backwards compat
[ "oSearch", "oPreviousSearch" ],
[ "aoSearchCols", "aoPreSearchCols" ],
[ "iDisplayLength", "_iDisplayLength" ],
[ "bJQueryUI", "bJUI" ]
@@ -6061,31 +6264,36 @@
oSettings._iRecordsDisplay = tmp ? oInit.iDeferLoading[0] : oInit.iDeferLoading;
oSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1] : oInit.iDeferLoading;
}
/* Language definitions */
- if ( oInit.oLanguage.sUrl !== "" )
+ var oLanguage = oSettings.oLanguage;
+ $.extend( true, oLanguage, oInit.oLanguage );
+
+ if ( oLanguage.sUrl !== "" )
{
/* Get the language definitions from a file - because this Ajax call makes the language
* get async to the remainder of this function we use bInitHandedOff to indicate that
* _fnInitialise will be fired by the returned Ajax handler, rather than the constructor
*/
- oSettings.oLanguage.sUrl = oInit.oLanguage.sUrl;
- $.getJSON( oSettings.oLanguage.sUrl, null, function( json ) {
- _fnLanguageCompat( json );
- _fnCamelToHungarian( defaults.oLanguage, json );
- $.extend( true, oSettings.oLanguage, oInit.oLanguage, json );
- _fnInitialise( oSettings );
+ $.ajax( {
+ dataType: 'json',
+ url: oLanguage.sUrl,
+ success: function ( json ) {
+ _fnLanguageCompat( json );
+ _fnCamelToHungarian( defaults.oLanguage, json );
+ $.extend( true, oLanguage, json );
+ _fnInitialise( oSettings );
+ },
+ error: function () {
+ // Error occurred loading language file, continue on as best we can
+ _fnInitialise( oSettings );
+ }
} );
bInitHandedOff = true;
}
- else
- {
- $.extend( true, oSettings.oLanguage, oInit.oLanguage );
- }
-
/*
* Stripes
*/
if ( oInit.asStripeClasses === null )
{
@@ -6469,11 +6677,11 @@
*
* @example
* // Initialisation as a constructor
* var api = new $.fn.DataTable.Api( 'table.dataTable' );
*/
- DataTable.Api = _Api = function ( context, data )
+ _Api = function ( context, data )
{
if ( ! this instanceof _Api ) {
throw 'DT API must be constructed as a new object';
// or should it do the 'new' for the caller?
// return new _Api.apply( this, arguments );
@@ -6512,10 +6720,11 @@
};
_Api.extend( this, this, __apiStruct );
};
+ DataTable.Api = _Api;
_Api.prototype = /** @lends DataTables.Api */{
/**
* Return a new Api instance, comprised of the data held in the current
* instance, join with the other array(s) and/or value(s).
@@ -6534,21 +6743,13 @@
context: [], // array of table settings objects
each: function ( fn )
{
- if ( __arrayProto.forEach ) {
- // Where possible, use the built-in forEach
- __arrayProto.forEach.call( this, fn, this );
+ for ( var i=0, ien=this.length ; i<ien; i++ ) {
+ fn.call( this, this[i], i, this );
}
- else {
- // Compatibility for browsers without EMCA-252-5 (JS 1.6)
- for ( var i=0, ien=this.length ; i<ien; i++ ) {
- // In strict mode the execution scope is the passed value
- fn.call( this, this[i], i, this );
- }
- }
return this;
},
@@ -6600,37 +6801,40 @@
}
}
return -1;
},
- // Internal only at the moment - relax?
- iterator: function ( flatten, type, fn ) {
+ // Note that `alwaysNew` is internal - use iteratorNew externally
+ iterator: function ( flatten, type, fn, alwaysNew ) {
var
a = [], ret,
i, ien, j, jen,
context = this.context,
rows, items, item,
selector = this.selector;
// Argument shifting
if ( typeof flatten === 'string' ) {
+ alwaysNew = fn;
fn = type;
type = flatten;
flatten = false;
}
for ( i=0, ien=context.length ; i<ien ; i++ ) {
+ var apiInst = new _Api( context[i] );
+
if ( type === 'table' ) {
- ret = fn( context[i], i );
+ ret = fn.call( apiInst, context[i], i );
if ( ret !== undefined ) {
a.push( ret );
}
}
else if ( type === 'columns' || type === 'rows' ) {
// this has same length as context - one entry for each table
- ret = fn( context[i], this[i], i );
+ ret = fn.call( apiInst, context[i], this[i], i );
if ( ret !== undefined ) {
a.push( ret );
}
}
@@ -6645,24 +6849,24 @@
for ( j=0, jen=items.length ; j<jen ; j++ ) {
item = items[j];
if ( type === 'cell' ) {
- ret = fn( context[i], item.row, item.column, i, j );
+ ret = fn.call( apiInst, context[i], item.row, item.column, i, j );
}
else {
- ret = fn( context[i], item, i, j, rows );
+ ret = fn.call( apiInst, context[i], item, i, j, rows );
}
if ( ret !== undefined ) {
a.push( ret );
}
}
}
}
- if ( a.length ) {
+ if ( a.length || alwaysNew ) {
var api = new _Api( context, flatten ? a.concat.apply( [], a ) : a );
var apiSelector = api.selector;
apiSelector.rows = selector.rows;
apiSelector.cols = selector.cols;
apiSelector.opts = selector.opts;
@@ -6779,11 +6983,11 @@
var
i, ien,
j, jen,
struct, inner,
- methodScoping = function ( fn, struc ) {
+ methodScoping = function ( scope, fn, struc ) {
return function () {
var ret = fn.apply( scope, arguments );
// Method extension
_Api.extend( ret, ret, struc.methodExt );
@@ -6794,11 +6998,11 @@
for ( i=0, ien=ext.length ; i<ien ; i++ ) {
struct = ext[i];
// Value
obj[ struct.name ] = typeof struct.val === 'function' ?
- methodScoping( struct.val, struct ) :
+ methodScoping( scope, struct.val, struct ) :
$.isPlainObject( struct.val ) ?
{} :
struct.val;
obj[ struct.name ].__dt_wrapper = true;
@@ -6890,15 +7094,10 @@
struct = method ?
src.methodExt :
src.propExt;
}
}
-
- // Rebuild the API with the new construct
- if ( _Api.ready ) {
- DataTable.api.build();
- }
};
_Api.registerPlural = _api_registerPlural = function ( pluralName, singularName, val ) {
_Api.register( pluralName, val );
@@ -6990,36 +7189,43 @@
_api_registerPlural( 'tables().nodes()', 'table().node()' , function () {
return this.iterator( 'table', function ( ctx ) {
return ctx.nTable;
- } );
+ }, 1 );
} );
_api_registerPlural( 'tables().body()', 'table().body()' , function () {
return this.iterator( 'table', function ( ctx ) {
return ctx.nTBody;
- } );
+ }, 1 );
} );
_api_registerPlural( 'tables().header()', 'table().header()' , function () {
return this.iterator( 'table', function ( ctx ) {
return ctx.nTHead;
- } );
+ }, 1 );
} );
_api_registerPlural( 'tables().footer()', 'table().footer()' , function () {
return this.iterator( 'table', function ( ctx ) {
return ctx.nTFoot;
- } );
+ }, 1 );
} );
+ _api_registerPlural( 'tables().containers()', 'table().container()' , function () {
+ return this.iterator( 'table', function ( ctx ) {
+ return ctx.nTableWrapper;
+ }, 1 );
+ } );
+
+
/**
* Redraw the tables in the current context.
*
* @param {boolean} [reset=true] Reset (default) or hold the current paging
* position. A full re-sort and re-filter is performed when this method is
@@ -7283,15 +7489,16 @@
var _selector_run = function ( selector, select )
{
var
out = [], res,
- a, i, ien, j, jen;
+ a, i, ien, j, jen,
+ selectorType = typeof selector;
// Can't just check for isArray here, as an API or jQuery instance might be
// given with their array like look
- if ( ! selector || typeof selector === 'string' || selector.length === undefined ) {
+ if ( ! selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined ) {
selector = [ selector ];
}
for ( i=0, ien=selector.length ; i<ien ; i++ ) {
a = selector[i] && selector[i].split ?
@@ -7397,11 +7604,11 @@
}
else { // applied | removed
tmp = $.inArray( i, displayFiltered );
if ((tmp === -1 && search == 'removed') ||
- (tmp === 1 && search == 'applied') )
+ (tmp >= 0 && search == 'applied') )
{
a.push( i );
}
}
}
@@ -7425,10 +7632,11 @@
var __row_selector = function ( settings, selector, opts )
{
return _selector_run( selector, function ( sel ) {
var selInt = _intVal( sel );
+ var i, ien;
// Short cut - selector is a number and no options provided (default is
// all records, so no need to check if the index is in there, since it
// must be - dev error if the index doesn't exist).
if ( selInt !== null && ! opts ) {
@@ -7444,21 +7652,28 @@
else if ( ! sel ) {
// Selector - none
return rows;
}
- // Get nodes in the order from the `rows` array (can't use `pluck`) @todo - use pluck_order
- var nodes = [];
- for ( var i=0, ien=rows.length ; i<ien ; i++ ) {
- nodes.push( settings.aoData[ rows[i] ].nTr );
+ // Selector - function
+ if ( typeof sel === 'function' ) {
+ return $.map( rows, function (idx) {
+ var row = settings.aoData[ idx ];
+ return sel( idx, row._aData, row.nTr ) ? idx : null;
+ } );
}
+ // Get nodes in the order from the `rows` array with null values removed
+ var nodes = _removeEmpty(
+ _pluck_order( settings.aoData, rows, 'nTr' )
+ );
+
+ // Selector - node
if ( sel.nodeName ) {
- // Selector - node
if ( $.inArray( sel, nodes ) !== -1 ) {
- return [ sel._DT_RowIndex ];// sel is a TR node that is in the table
- // and DataTables adds a prop for fast lookup
+ return [ sel._DT_RowIndex ]; // sel is a TR node that is in the table
+ // and DataTables adds a prop for fast lookup
}
}
// Selector - jQuery selector string, array of nodes or jQuery object/
// As jQuery's .filter() allows jQuery objects to be passed in filter,
@@ -7488,11 +7703,11 @@
opts = _selector_opts( opts );
var inst = this.iterator( 'table', function ( settings ) {
return __row_selector( settings, selector, opts );
- } );
+ }, 1 );
// Want argument shifting here and in __row_selector?
inst.selector.rows = selector;
inst.selector.opts = opts;
@@ -7501,36 +7716,36 @@
_api_register( 'rows().nodes()', function () {
return this.iterator( 'row', function ( settings, row ) {
return settings.aoData[ row ].nTr || undefined;
- } );
+ }, 1 );
} );
_api_register( 'rows().data()', function () {
return this.iterator( true, 'rows', function ( settings, rows ) {
return _pluck_order( settings.aoData, rows, '_aData' );
- } );
+ }, 1 );
} );
_api_registerPlural( 'rows().cache()', 'row().cache()', function ( type ) {
return this.iterator( 'row', function ( settings, row ) {
var r = settings.aoData[ row ];
return type === 'search' ? r._aFilterData : r._aSortData;
- } );
+ }, 1 );
} );
_api_registerPlural( 'rows().invalidate()', 'row().invalidate()', function ( src ) {
return this.iterator( 'row', function ( settings, row ) {
- _fnInvalidateRow( settings, row, src );
+ _fnInvalidate( settings, row, src );
} );
} );
_api_registerPlural( 'rows().indexes()', 'row().index()', function () {
return this.iterator( 'row', function ( settings, row ) {
return row;
- } );
+ }, 1 );
} );
_api_registerPlural( 'rows().remove()', 'row().remove()', function () {
var that = this;
@@ -7575,11 +7790,11 @@
out.push( _fnAddData( settings, row ) );
}
}
return out;
- } );
+ }, 1 );
// Return an Api.rows() extended instance, so rows().nodes() etc can be used
var modRows = this.rows( -1 );
modRows.pop();
modRows.push.apply( modRows, newRows.toArray() );
@@ -7611,11 +7826,11 @@
// Set
ctx[0].aoData[ this[0] ]._aData = data;
// Automatically invalidate
- _fnInvalidateRow( ctx[0], this[0], 'data' );
+ _fnInvalidate( ctx[0], this[0], 'data' );
return this;
} );
@@ -7658,11 +7873,11 @@
if ( r.nodeName && r.nodeName.toLowerCase() === 'tr' ) {
rows.push( r );
}
else {
// Otherwise create a row with a wrapper
- var created = $('<tr><td/></tr>');
+ var created = $('<tr><td/></tr>').addClass( k );
$('td', created)
.addClass( k )
.html( r )
[0].colSpan = _fnVisbleColumns( ctx );
@@ -7690,110 +7905,174 @@
row._details.insertAfter( row.nTr );
}
};
- var __details_display = function ( show ) {
- var ctx = this.context;
+ var __details_remove = function ( api, idx )
+ {
+ var ctx = api.context;
- if ( ctx.length && this.length ) {
- var row = ctx[0].aoData[ this[0] ];
+ if ( ctx.length ) {
+ var row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ];
if ( row._details ) {
+ row._details.remove();
+
+ row._detailsShow = undefined;
+ row._details = undefined;
+ }
+ }
+ };
+
+
+ var __details_display = function ( api, show ) {
+ var ctx = api.context;
+
+ if ( ctx.length && api.length ) {
+ var row = ctx[0].aoData[ api[0] ];
+
+ if ( row._details ) {
row._detailsShow = show;
+
if ( show ) {
row._details.insertAfter( row.nTr );
}
else {
- row._details.remove();
+ row._details.detach();
}
__details_events( ctx[0] );
}
}
-
- return this;
};
var __details_events = function ( settings )
{
var api = new _Api( settings );
var namespace = '.dt.DT_details';
var drawEvent = 'draw'+namespace;
var colvisEvent = 'column-visibility'+namespace;
+ var destroyEvent = 'destroy'+namespace;
+ var data = settings.aoData;
- api.off( drawEvent +' '+ colvisEvent );
+ api.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent );
- if ( _pluck( settings.aoData, '_details' ).length > 0 ) {
+ if ( _pluck( data, '_details' ).length > 0 ) {
// On each draw, insert the required elements into the document
- api.on( drawEvent, function () {
+ api.on( drawEvent, function ( e, ctx ) {
+ if ( settings !== ctx ) {
+ return;
+ }
+
api.rows( {page:'current'} ).eq(0).each( function (idx) {
// Internal data grab
- var row = settings.aoData[ idx ];
+ var row = data[ idx ];
if ( row._detailsShow ) {
row._details.insertAfter( row.nTr );
}
} );
} );
// Column visibility change - update the colspan
- api.on( colvisEvent, function ( e, settings, idx, vis ) {
+ api.on( colvisEvent, function ( e, ctx, idx, vis ) {
+ if ( settings !== ctx ) {
+ return;
+ }
+
// Update the colspan for the details rows (note, only if it already has
// a colspan)
- var row, visible = _fnVisbleColumns( settings );
+ var row, visible = _fnVisbleColumns( ctx );
- for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
- row = settings.aoData[i];
+ for ( var i=0, ien=data.length ; i<ien ; i++ ) {
+ row = data[i];
if ( row._details ) {
row._details.children('td[colspan]').attr('colspan', visible );
}
}
} );
+
+ // Table destroyed - nuke any child rows
+ api.on( destroyEvent, function ( e, ctx ) {
+ if ( settings !== ctx ) {
+ return;
+ }
+
+ for ( var i=0, ien=data.length ; i<ien ; i++ ) {
+ if ( data[i]._details ) {
+ __details_remove( api, i );
+ }
+ }
+ } );
}
};
+ // Strings for the method names to help minification
+ var _emp = '';
+ var _child_obj = _emp+'row().child';
+ var _child_mth = _child_obj+'()';
+
// data can be:
// tr
// string
// jQuery or array of any of the above
- _api_register( 'row().child()', function ( data, klass ) {
+ _api_register( _child_mth, function ( data, klass ) {
var ctx = this.context;
if ( data === undefined ) {
// get
return ctx.length && this.length ?
ctx[0].aoData[ this[0] ]._details :
undefined;
}
+ else if ( data === true ) {
+ // show
+ this.child.show();
+ }
+ else if ( data === false ) {
+ // remove
+ __details_remove( this );
+ }
else if ( ctx.length && this.length ) {
// set
__details_add( ctx[0], ctx[0].aoData[ this[0] ], data, klass );
}
return this;
} );
+
_api_register( [
- 'row().child.show()',
- 'row().child().show()'
- ], function () {
- __details_display.call( this, true );
+ _child_obj+'.show()',
+ _child_mth+'.show()' // only when `child()` was called with parameters (without
+ ], function ( show ) { // it returns an object and this method is not executed)
+ __details_display( this, true );
return this;
} );
+
_api_register( [
- 'row().child.hide()',
- 'row().child().hide()'
- ], function () {
- __details_display.call( this, false );
+ _child_obj+'.hide()',
+ _child_mth+'.hide()' // only when `child()` was called with parameters (without
+ ], function () { // it returns an object and this method is not executed)
+ __details_display( this, false );
return this;
} );
- _api_register( 'row().child.isShown()', function () {
+
+ _api_register( [
+ _child_obj+'.remove()',
+ _child_mth+'.remove()' // only when `child()` was called with parameters (without
+ ], function () { // it returns an object and this method is not executed)
+ __details_remove( this );
+ return this;
+ } );
+
+
+ _api_register( _child_obj+'.isShown()', function () {
var ctx = this.context;
if ( ctx.length && this.length ) {
// _detailsShown as false or undefined will fall through to return false
return ctx[0].aoData[ this[0] ]._detailsShow || false;
@@ -7815,79 +8094,102 @@
*/
// can be an array of these items, comma separated list, or an array of comma
// separated lists
- var __re_column_selector = /^(.*):(name|visIdx|visible)$/;
+ var __re_column_selector = /^(.+):(name|visIdx|visible)$/;
+
+ // r1 and r2 are redundant - but it means that the parameters match for the
+ // iterator callback in columns().data()
+ var __columnData = function ( settings, column, r1, r2, rows ) {
+ var a = [];
+ for ( var row=0, ien=rows.length ; row<ien ; row++ ) {
+ a.push( _fnGetCellData( settings, rows[row], column ) );
+ }
+ return a;
+ };
+
+
var __column_selector = function ( settings, selector, opts )
{
var
columns = settings.aoColumns,
names = _pluck( columns, 'sName' ),
nodes = _pluck( columns, 'nTh' );
return _selector_run( selector, function ( s ) {
var selInt = _intVal( s );
+ // Selector - all
if ( s === '' ) {
- // All columns
return _range( columns.length );
}
- else if ( selInt !== null ) {
- // Integer selector
+
+ // Selector - index
+ if ( selInt !== null ) {
return [ selInt >= 0 ?
selInt : // Count from left
columns.length + selInt // Count from right (+ because its a negative value)
];
}
- else {
- var match = typeof s === 'string' ?
- s.match( __re_column_selector ) :
- '';
- if ( match ) {
- switch( match[2] ) {
- case 'visIdx':
- case 'visible':
- var idx = parseInt( match[1], 10 );
- // Visible index given, convert to column index
- if ( idx < 0 ) {
- // Counting from the right
- var visColumns = $.map( columns, function (col,i) {
- return col.bVisible ? i : null;
- } );
- return [ visColumns[ visColumns.length + idx ] ];
- }
- // Counting from the left
- return [ _fnVisibleToColumnIndex( settings, idx ) ];
+ // Selector = function
+ if ( typeof s === 'function' ) {
+ var rows = _selector_row_indexes( settings, opts );
- case 'name':
- // match by name. `names` is column index complete and in order
- return $.map( names, function (name, i) {
- return name === match[1] ? i : null;
+ return $.map( columns, function (col, idx) {
+ return s(
+ idx,
+ __columnData( settings, idx, 0, 0, rows ),
+ nodes[ idx ]
+ ) ? idx : null;
+ } );
+ }
+
+ // jQuery or string selector
+ var match = typeof s === 'string' ?
+ s.match( __re_column_selector ) :
+ '';
+
+ if ( match ) {
+ switch( match[2] ) {
+ case 'visIdx':
+ case 'visible':
+ var idx = parseInt( match[1], 10 );
+ // Visible index given, convert to column index
+ if ( idx < 0 ) {
+ // Counting from the right
+ var visColumns = $.map( columns, function (col,i) {
+ return col.bVisible ? i : null;
} );
- }
+ return [ visColumns[ visColumns.length + idx ] ];
+ }
+ // Counting from the left
+ return [ _fnVisibleToColumnIndex( settings, idx ) ];
+
+ case 'name':
+ // match by name. `names` is column index complete and in order
+ return $.map( names, function (name, i) {
+ return name === match[1] ? i : null;
+ } );
}
- else {
- // jQuery selector on the TH elements for the columns
- return $( nodes )
- .filter( s )
- .map( function () {
- return $.inArray( this, nodes ); // `nodes` is column index complete and in order
- } )
- .toArray();
- }
}
+ else {
+ // jQuery selector on the TH elements for the columns
+ return $( nodes )
+ .filter( s )
+ .map( function () {
+ return $.inArray( this, nodes ); // `nodes` is column index complete and in order
+ } )
+ .toArray();
+ }
} );
};
-
-
-
- var __setColumnVis = function ( settings, column, vis ) {
+ var __setColumnVis = function ( settings, column, vis, recalc ) {
var
cols = settings.aoColumns,
col = cols[ column ],
data = settings.aoData,
row, cells, i, ien, tr;
@@ -7919,29 +8221,25 @@
}
}
else {
// Remove column
$( _pluck( settings.aoData, 'anCells', column ) ).detach();
-
- col.bVisible = false;
- _fnDrawHead( settings, settings.aoHeader );
- _fnDrawHead( settings, settings.aoFooter );
-
- _fnSaveState( settings );
}
// Common actions
col.bVisible = vis;
_fnDrawHead( settings, settings.aoHeader );
_fnDrawHead( settings, settings.aoFooter );
- // Automatically adjust column sizing
- _fnAdjustColumnSizing( settings );
+ if ( recalc === undefined || recalc ) {
+ // Automatically adjust column sizing
+ _fnAdjustColumnSizing( settings );
- // Realign columns for scrolling
- if ( settings.oScroll.sX || settings.oScroll.sY ) {
- _fnScrollDraw( settings );
+ // Realign columns for scrolling
+ if ( settings.oScroll.sX || settings.oScroll.sY ) {
+ _fnScrollDraw( settings );
+ }
}
_fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis] );
_fnSaveState( settings );
@@ -7963,11 +8261,11 @@
opts = _selector_opts( opts );
var inst = this.iterator( 'table', function ( settings ) {
return __column_selector( settings, selector, opts );
- } );
+ }, 1 );
// Want argument shifting here and in _row_selector?
inst.selector.cols = selector;
inst.selector.opts = opts;
@@ -7979,71 +8277,73 @@
*
*/
_api_registerPlural( 'columns().header()', 'column().header()', function ( selector, opts ) {
return this.iterator( 'column', function ( settings, column ) {
return settings.aoColumns[column].nTh;
- } );
+ }, 1 );
} );
/**
*
*/
_api_registerPlural( 'columns().footer()', 'column().footer()', function ( selector, opts ) {
return this.iterator( 'column', function ( settings, column ) {
return settings.aoColumns[column].nTf;
- } );
+ }, 1 );
} );
/**
*
*/
_api_registerPlural( 'columns().data()', 'column().data()', function () {
- return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
- var a = [];
- for ( var row=0, ien=rows.length ; row<ien ; row++ ) {
- a.push( _fnGetCellData( settings, rows[row], column, '' ) );
- }
- return a;
- } );
+ return this.iterator( 'column-rows', __columnData, 1 );
} );
+ _api_registerPlural( 'columns().dataSrc()', 'column().dataSrc()', function () {
+ return this.iterator( 'column', function ( settings, column ) {
+ return settings.aoColumns[column].mData;
+ }, 1 );
+ } );
+
+
_api_registerPlural( 'columns().cache()', 'column().cache()', function ( type ) {
return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
return _pluck_order( settings.aoData, rows,
type === 'search' ? '_aFilterData' : '_aSortData', column
);
- } );
+ }, 1 );
} );
_api_registerPlural( 'columns().nodes()', 'column().nodes()', function () {
return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
return _pluck_order( settings.aoData, rows, 'anCells', column ) ;
- } );
+ }, 1 );
} );
- _api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis ) {
+ _api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) {
return this.iterator( 'column', function ( settings, column ) {
- return vis === undefined ?
- settings.aoColumns[ column ].bVisible :
- __setColumnVis( settings, column, vis );
+ if ( vis === undefined ) {
+ return settings.aoColumns[ column ].bVisible;
+ } // else
+ __setColumnVis( settings, column, vis, calc );
} );
} );
_api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) {
return this.iterator( 'column', function ( settings, column ) {
return type === 'visible' ?
_fnColumnIndexToVisible( settings, column ) :
column;
- } );
+ }, 1 );
} );
// _api_register( 'columns().show()', function () {
// var selector = this.selector;
@@ -8059,11 +8359,11 @@
_api_register( 'columns.adjust()', function () {
return this.iterator( 'table', function ( settings ) {
_fnAdjustColumnSizing( settings );
- } );
+ }, 1 );
} );
// Convert from one column index type, to another type
_api_register( 'column.index()', function ( type, idx ) {
@@ -8089,39 +8389,56 @@
var __cell_selector = function ( settings, selector, opts )
{
var data = settings.aoData;
var rows = _selector_row_indexes( settings, opts );
- var cells = _pluck_order( data, rows, 'anCells' );
+ var cells = _removeEmpty( _pluck_order( data, rows, 'anCells' ) );
var allCells = $( [].concat.apply([], cells) );
var row;
var columns = settings.aoColumns.length;
- var a, i, ien, j;
+ var a, i, ien, j, o, host;
return _selector_run( selector, function ( s ) {
- if ( ! s ) {
- // All cells
+ var fnSelector = typeof s === 'function';
+
+ if ( s === null || s === undefined || fnSelector ) {
+ // All cells and function selectors
a = [];
for ( i=0, ien=rows.length ; i<ien ; i++ ) {
row = rows[i];
for ( j=0 ; j<columns ; j++ ) {
- a.push( {
+ o = {
row: row,
column: j
- } );
+ };
+
+ if ( fnSelector ) {
+ // Selector - function
+ host = settings.aoData[ row ];
+
+ if ( s( o, _fnGetCellData(settings, row, j), host.anCells[j] ) ) {
+ a.push( o );
+ }
+ }
+ else {
+ // Selector - all
+ a.push( o );
+ }
}
}
return a;
}
- else if ( $.isPlainObject( s ) ) {
+
+ // Selector - index
+ if ( $.isPlainObject( s ) ) {
return [s];
}
- // jQuery filtered cells
+ // Selector - jQuery filtered cells
return allCells
.filter( s )
.map( function (i, el) {
row = el.parentNode._DT_RowIndex;
@@ -8138,12 +8455,12 @@
_api_register( 'cells()', function ( rowSelector, columnSelector, opts ) {
// Argument shifting
if ( $.isPlainObject( rowSelector ) ) {
- // If passing in a cell index
- if ( rowSelector.row ) {
+ // Indexes
+ if ( typeof rowSelector.row !== undefined ) {
opts = columnSelector;
columnSelector = null;
}
else {
opts = rowSelector;
@@ -8178,11 +8495,11 @@
} );
}
}
return a;
- } );
+ }, 1 );
$.extend( cells.selector, {
cols: columnSelector,
rows: rowSelector,
opts: opts
@@ -8192,65 +8509,65 @@
} );
_api_registerPlural( 'cells().nodes()', 'cell().node()', function () {
return this.iterator( 'cell', function ( settings, row, column ) {
- return settings.aoData[ row ].anCells[ column ];
- } );
+ var cells = settings.aoData[ row ].anCells;
+ return cells ?
+ cells[ column ] :
+ undefined;
+ }, 1 );
} );
_api_register( 'cells().data()', function () {
return this.iterator( 'cell', function ( settings, row, column ) {
return _fnGetCellData( settings, row, column );
- } );
+ }, 1 );
} );
_api_registerPlural( 'cells().cache()', 'cell().cache()', function ( type ) {
type = type === 'search' ? '_aFilterData' : '_aSortData';
return this.iterator( 'cell', function ( settings, row, column ) {
return settings.aoData[ row ][ type ][ column ];
- } );
+ }, 1 );
} );
+ _api_registerPlural( 'cells().render()', 'cell().render()', function ( type ) {
+ return this.iterator( 'cell', function ( settings, row, column ) {
+ return _fnGetCellData( settings, row, column, type );
+ }, 1 );
+ } );
+
+
_api_registerPlural( 'cells().indexes()', 'cell().index()', function () {
return this.iterator( 'cell', function ( settings, row, column ) {
return {
row: row,
column: column,
columnVisible: _fnColumnIndexToVisible( settings, column )
};
- } );
+ }, 1 );
} );
- _api_register( [
- 'cells().invalidate()',
- 'cell().invalidate()'
- ], function ( src ) {
- var selector = this.selector;
-
- // Use the rows method of the instance to perform the invalidation, rather
- // than doing it here. This avoids needing to handle duplicate rows from
- // the cells.
- this.rows( selector.rows, selector.opts ).invalidate( src );
-
- return this;
+ _api_registerPlural( 'cells().invalidate()', 'cell().invalidate()', function ( src ) {
+ return this.iterator( 'cell', function ( settings, row, column ) {
+ _fnInvalidate( settings, row, src, column );
+ } );
} );
-
_api_register( 'cell()', function ( rowSelector, columnSelector, opts ) {
return _selector_first( this.cells( rowSelector, columnSelector, opts ) );
} );
-
_api_register( 'cell().data()', function ( data ) {
var ctx = this.context;
var cell = this[0];
if ( data === undefined ) {
@@ -8260,11 +8577,11 @@
undefined;
}
// Set
_fnSetCellData( ctx[0], cell[0].row, cell[0].column, data );
- _fnInvalidateRow( ctx[0], cell[0].row, 'data', cell[0].column );
+ _fnInvalidate( ctx[0], cell[0].row, 'data', cell[0].column );
return this;
} );
@@ -8385,40 +8702,73 @@
} ), 1 );
} );
} );
- _api_register( [
+ _api_registerPlural(
'columns().search()',
- 'column().search()'
- ], function ( input, regex, smart, caseInsen ) {
- return this.iterator( 'column', function ( settings, column ) {
- var preSearch = settings.aoPreSearchCols;
+ 'column().search()',
+ function ( input, regex, smart, caseInsen ) {
+ return this.iterator( 'column', function ( settings, column ) {
+ var preSearch = settings.aoPreSearchCols;
- if ( input === undefined ) {
- // get
- return preSearch[ column ].sSearch;
- }
+ if ( input === undefined ) {
+ // get
+ return preSearch[ column ].sSearch;
+ }
- // set
- if ( ! settings.oFeatures.bFilter ) {
- return;
- }
+ // set
+ if ( ! settings.oFeatures.bFilter ) {
+ return;
+ }
- $.extend( preSearch[ column ], {
- "sSearch": input+"",
- "bRegex": regex === null ? false : regex,
- "bSmart": smart === null ? true : smart,
- "bCaseInsensitive": caseInsen === null ? true : caseInsen
+ $.extend( preSearch[ column ], {
+ "sSearch": input+"",
+ "bRegex": regex === null ? false : regex,
+ "bSmart": smart === null ? true : smart,
+ "bCaseInsensitive": caseInsen === null ? true : caseInsen
+ } );
+
+ _fnFilterComplete( settings, settings.oPreviousSearch, 1 );
} );
+ }
+ );
- _fnFilterComplete( settings, settings.oPreviousSearch, 1 );
+ /*
+ * State API methods
+ */
+
+ _api_register( 'state()', function () {
+ return this.context.length ?
+ this.context[0].oSavedState :
+ null;
+ } );
+
+
+ _api_register( 'state.clear()', function () {
+ return this.iterator( 'table', function ( settings ) {
+ // Save an empty object
+ settings.fnStateSaveCallback.call( settings.oInstance, settings, {} );
} );
} );
+ _api_register( 'state.loaded()', function () {
+ return this.context.length ?
+ this.context[0].oLoadedState :
+ null;
+ } );
+
+ _api_register( 'state.save()', function () {
+ return this.iterator( 'table', function ( settings ) {
+ _fnSaveState( settings );
+ } );
+ } );
+
+
+
/**
* Provide a common method for plug-ins to check the version of DataTables being
* used, in order to ensure compatibility.
*
* @param {string} version Version string to check for, in the format "X.Y.Z".
@@ -8501,19 +8851,51 @@
* $(table).DataTable().columns.adjust();
* } );
*/
DataTable.tables = DataTable.fnTables = function ( visible )
{
- return jQuery.map( DataTable.settings, function (o) {
+ return $.map( DataTable.settings, function (o) {
if ( !visible || (visible && $(o.nTable).is(':visible')) ) {
return o.nTable;
}
} );
};
/**
+ * DataTables utility methods
+ *
+ * This namespace provides helper methods that DataTables uses internally to
+ * create a DataTable, but which are not exclusively used only for DataTables.
+ * These methods can be used by extension authors to save the duplication of
+ * code.
+ *
+ * @namespace
+ */
+ DataTable.util = {
+ /**
+ * Throttle the calls to a function. Arguments and context are maintained
+ * for the throttled function.
+ *
+ * @param {function} fn Function to be called
+ * @param {integer} freq Call frequency in mS
+ * @return {function} Wrapped function
+ */
+ throttle: _fnThrottle,
+
+
+ /**
+ * Escape a string such that it can be used in a regular expression
+ *
+ * @param {string} sVal string to escape
+ * @returns {string} escaped string
+ */
+ escapeRegex: _fnEscapeRegex
+ };
+
+
+ /**
* Convert from camel case parameters to Hungarian notation. This is made public
* for the extensions to provide the same ability as DataTables core to accept
* either the 1.9 style Hungarian notation, or the 1.10+ style camelCase
* parameters.
*
@@ -8547,11 +8929,11 @@
$.each( [ 'on', 'one', 'off' ], function (i, key) {
_api_register( key+'()', function ( /* event, handler */ ) {
var args = Array.prototype.slice.call(arguments);
// Add the `dt` namespace automatically if it isn't already present
- if ( args[0].indexOf( '.dt' ) === -1 ) {
+ if ( ! args[0].match(/\.dt\b/) ) {
args[0] += '.dt';
}
var inst = $( this.tables().nodes() );
inst[key].apply( inst, args );
@@ -8688,11 +9070,11 @@
* only for non-release builds. See http://semver.org/ for more information.
* @member
* @type string
* @default Version number
*/
- DataTable.version = "1.10.0";
+ DataTable.version = "1.10.4";
/**
* Private data store, containing all of the settings objects that are
* created for the tables on a given page.
*
@@ -10952,10 +11334,21 @@
*/
"sSearch": "Search:",
/**
+ * Assign a `placeholder` attribute to the search `input` element
+ * @type string
+ * @default
+ *
+ * @dtopt Language
+ * @name DataTable.defaults.language.searchPlaceholder
+ */
+ "sSearchPlaceholder": "",
+
+
+ /**
* All of the language information can be stored in a file on the
* server-side, which DataTables will look up if this parameter is passed.
* It must store the URL of the language file, which is in a JSON format,
* and the object has the same properties as the oLanguage object in the
* initialiser object (i.e. the above parameters). Please refer to one of
@@ -11118,10 +11511,30 @@
*/
"sDom": "lfrtip",
/**
+ * Search delay option. This will throttle full table searches that use the
+ * DataTables provided search input element (it does not effect calls to
+ * `dt-api search()`, providing a delay before the search is made.
+ * @type integer
+ * @default 0
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.searchDelay
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "searchDelay": 200
+ * } );
+ * } )
+ */
+ "searchDelay": null,
+
+
+ /**
* DataTables features four different built-in options for the buttons to
* display for pagination control:
*
* * `simple` - 'Previous' and 'Next' buttons only
* * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers
@@ -12634,10 +13047,17 @@
* @default null
*/
"sDom": null,
/**
+ * Search delay (in mS)
+ * @type integer
+ * @default null
+ */
+ "searchDelay": null,
+
+ /**
* Which type of pagination should be used.
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
* @type string
* @default two_button
@@ -12680,10 +13100,17 @@
* @default []
*/
"aoStateLoad": [],
/**
+ * State that was saved. Useful for back reference
+ * @type object
+ * @default null
+ */
+ "oSavedState": null,
+
+ /**
* State that was loaded. Useful for back reference
* @type object
* @default null
*/
"oLoadedState": null,
@@ -13666,10 +14093,11 @@
// For testing and plug-ins to use
_numbers: _numbers,
numbers_length: 7
} );
+
$.extend( true, DataTable.ext.renderer, {
pageButton: {
_: function ( settings, host, idx, buttons, page, pages ) {
var classes = settings.oClasses;
var lang = settings.oLanguage.oPaginate;
@@ -13750,29 +14178,120 @@
}
}
}
};
- // Because this approach is destroying and recreating the paging
- // elements, focus is lost on the select button which is bad for
- // accessibility. So we want to restore focus once the draw has
- // completed
- var activeEl = $(document.activeElement).data('dt-idx');
+ // IE9 throws an 'unknown error' if document.activeElement is used
+ // inside an iframe or frame. Try / catch the error. Not good for
+ // accessibility, but neither are frames.
+ try {
+ // Because this approach is destroying and recreating the paging
+ // elements, focus is lost on the select button which is bad for
+ // accessibility. So we want to restore focus once the draw has
+ // completed
+ var activeEl = $(document.activeElement).data('dt-idx');
- attach( $(host).empty(), buttons );
+ attach( $(host).empty(), buttons );
- if ( activeEl !== null ) {
- $(host).find( '[data-dt-idx='+activeEl+']' ).focus();
+ if ( activeEl !== null ) {
+ $(host).find( '[data-dt-idx='+activeEl+']' ).focus();
+ }
}
+ catch (e) {}
}
}
} );
+ // Built in type detection. See model.ext.aTypes for information about
+ // what is required from this methods.
+ $.extend( DataTable.ext.type.detect, [
+ // Plain numbers - first since V8 detects some plain numbers as dates
+ // e.g. Date.parse('55') (but not all, e.g. Date.parse('22')...).
+ function ( d, settings )
+ {
+ var decimal = settings.oLanguage.sDecimal;
+ return _isNumber( d, decimal ) ? 'num'+decimal : null;
+ },
+
+ // Dates (only those recognised by the browser's Date.parse)
+ function ( d, settings )
+ {
+ // V8 will remove any unknown characters at the start and end of the
+ // expression, leading to false matches such as `$245.12` or `10%` being
+ // a valid date. See forum thread 18941 for detail.
+ if ( d && !(d instanceof Date) && ( ! _re_date_start.test(d) || ! _re_date_end.test(d) ) ) {
+ return null;
+ }
+ var parsed = Date.parse(d);
+ return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null;
+ },
+
+ // Formatted numbers
+ function ( d, settings )
+ {
+ var decimal = settings.oLanguage.sDecimal;
+ return _isNumber( d, decimal, true ) ? 'num-fmt'+decimal : null;
+ },
+
+ // HTML numeric
+ function ( d, settings )
+ {
+ var decimal = settings.oLanguage.sDecimal;
+ return _htmlNumeric( d, decimal ) ? 'html-num'+decimal : null;
+ },
+
+ // HTML numeric, formatted
+ function ( d, settings )
+ {
+ var decimal = settings.oLanguage.sDecimal;
+ return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt'+decimal : null;
+ },
+
+ // HTML (this is strict checking - there must be html)
+ function ( d, settings )
+ {
+ return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ?
+ 'html' : null;
+ }
+ ] );
+
+
+
+ // Filter formatting functions. See model.ext.ofnSearch for information about
+ // what is required from these methods.
+ //
+ // Note that additional search methods are added for the html numbers and
+ // html formatted numbers by `_addNumericSort()` when we know what the decimal
+ // place is
+
+
+ $.extend( DataTable.ext.type.search, {
+ html: function ( data ) {
+ return _empty(data) ?
+ data :
+ typeof data === 'string' ?
+ data
+ .replace( _re_new_lines, " " )
+ .replace( _re_html, "" ) :
+ '';
+ },
+
+ string: function ( data ) {
+ return _empty(data) ?
+ data :
+ typeof data === 'string' ?
+ data.replace( _re_new_lines, " " ) :
+ data;
+ }
+ } );
+
+
+
var __numericReplace = function ( d, decimalPlace, re1, re2 ) {
- if ( !d || d === '-' ) {
+ if ( d !== 0 && (!d || d === '-') ) {
return -Infinity;
}
// If a decimal place other than `.` is used, it needs to be given to the
// function so we can detect it and replace with a `.` which is the only
@@ -13793,12 +14312,12 @@
return d * 1;
};
- // Add the numeric 'deformatting' functions for sorting. This is done in a
- // function to provide an easy ability for the language options to add
+ // Add the numeric 'deformatting' functions for sorting and search. This is done
+ // in a function to provide an easy ability for the language options to add
// additional methods if a non-period decimal place is used.
function _addNumericSort ( decimalPlace ) {
$.each(
{
// Plain numbers
@@ -13820,11 +14339,17 @@
"html-num-fmt": function ( d ) {
return __numericReplace( d, decimalPlace, _re_html, _re_formatted_numeric );
}
},
function ( key, fn ) {
+ // Add the ordering method
_ext.type.order[ key+decimalPlace+'-pre' ] = fn;
+
+ // For HTML types add a search formatter that will strip the HTML
+ if ( key.match(/^html\-/) ) {
+ _ext.type.search[ key+decimalPlace ] = _ext.type.search.html;
+ }
}
);
}
@@ -13835,24 +14360,28 @@
return Date.parse( d ) || 0;
},
// html
"html-pre": function ( a ) {
- return ! a ?
+ return _empty(a) ?
'' :
a.replace ?
a.replace( /<.*?>/g, "" ).toLowerCase() :
a+'';
},
// string
"string-pre": function ( a ) {
- return typeof a === 'string' ?
- a.toLowerCase() :
- ! a || ! a.toString ?
- '' :
- a.toString();
+ // This is a little complex, but faster than always calling toString,
+ // http://jsperf.com/tostring-v-check
+ return _empty(a) ?
+ '' :
+ typeof a === 'string' ?
+ a.toLowerCase() :
+ ! a.toString ?
+ '' :
+ a.toString();
},
// string-asc and -desc are retained only for compatibility with the old
// sort methods
"string-asc": function ( x, y ) {
@@ -13867,100 +14396,23 @@
// Numeric sorting types - order doesn't matter here
_addNumericSort( '' );
- // Built in type detection. See model.ext.aTypes for information about
- // what is required from this methods.
- $.extend( DataTable.ext.type.detect, [
- // Plain numbers - first since V8 detects some plain numbers as dates
- // e.g. Date.parse('55') (but not all, e.g. Date.parse('22')...).
- function ( d, settings )
- {
- var decimal = settings.oLanguage.sDecimal;
- return _isNumber( d, decimal ) ? 'num'+decimal : null;
- },
-
- // Dates (only those recognised by the browser's Date.parse)
- function ( d, settings )
- {
- // V8 will remove any unknown characters at the start of the expression,
- // leading to false matches such as `$245.12` being a valid date. See
- // forum thread 18941 for detail.
- if ( d && ! _re_date_start.test(d) ) {
- return null;
- }
- var parsed = Date.parse(d);
- return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null;
- },
-
- // Formatted numbers
- function ( d, settings )
- {
- var decimal = settings.oLanguage.sDecimal;
- return _isNumber( d, decimal, true ) ? 'num-fmt'+decimal : null;
- },
-
- // HTML numeric
- function ( d, settings )
- {
- var decimal = settings.oLanguage.sDecimal;
- return _htmlNumeric( d, decimal ) ? 'html-num'+decimal : null;
- },
-
- // HTML numeric, formatted
- function ( d, settings )
- {
- var decimal = settings.oLanguage.sDecimal;
- return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt'+decimal : null;
- },
-
- // HTML (this is strict checking - there must be html)
- function ( d, settings )
- {
- return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ?
- 'html' : null;
- }
- ] );
-
-
-
- // Filter formatting functions. See model.ext.ofnSearch for information about
- // what is required from these methods.
-
-
- $.extend( DataTable.ext.type.search, {
- html: function ( data ) {
- return _empty(data) ?
- '' :
- typeof data === 'string' ?
- data
- .replace( _re_new_lines, " " )
- .replace( _re_html, "" ) :
- '';
- },
-
- string: function ( data ) {
- return _empty(data) ?
- '' :
- typeof data === 'string' ?
- data.replace( _re_new_lines, " " ) :
- data;
- }
- } );
-
-
-
$.extend( true, DataTable.ext.renderer, {
header: {
_: function ( settings, cell, column, classes ) {
// No additional mark-up required
// Attach a sort listener to update on sort - note that using the
// `DT` namespace will allow the event to be removed automatically
// on destroy, while the `dt` namespaced event is the one we are
// listening for
- $(settings.nTable).on( 'order.dt.DT', function ( e, settings, sorting, columns ) {
+ $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
+ if ( settings !== ctx ) { // need to check this this is the host
+ return; // table, not a nested one
+ }
+
var colIdx = column.idx;
cell
.removeClass(
column.sSortingClass +' '+
@@ -13974,22 +14426,26 @@
);
} );
},
jqueryui: function ( settings, cell, column, classes ) {
- var colIdx = column.idx;
-
$('<div/>')
.addClass( classes.sSortJUIWrapper )
.append( cell.contents() )
.append( $('<span/>')
.addClass( classes.sSortIcon+' '+column.sSortingClassJUI )
)
.appendTo( cell );
// Attach a sort listener to update on sort
- $(settings.nTable).on( 'order.dt.DT', function ( e, settings, sorting, columns ) {
+ $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
+ if ( settings !== ctx ) {
+ return;
+ }
+
+ var colIdx = column.idx;
+
cell
.removeClass( classes.sSortAsc +" "+classes.sSortDesc )
.addClass( columns[ colIdx ] == 'asc' ?
classes.sSortAsc : columns[ colIdx ] == 'desc' ?
classes.sSortDesc :
@@ -14047,17 +14503,19 @@
*/
DataTable.render = {
number: function ( thousands, decimal, precision, prefix ) {
return {
display: function ( d ) {
- d = parseFloat( d );
+ var negative = d < 0 ? '-' : '';
+ d = Math.abs( parseFloat( d ) );
+
var intPart = parseInt( d, 10 );
var floatPart = precision ?
- (decimal+(d - intPart).toFixed( precision )).substring( 2 ):
+ decimal+(d - intPart).toFixed( precision ).substring( 2 ):
'';
- return (prefix||'') +
+ return negative + (prefix||'') +
intPart.toString().replace(
/\B(?=(\d{3})+(?!\d))/g, thousands
) +
floatPart;
}
@@ -14126,10 +14584,10 @@
_fnGetObjectDataFn: _fnGetObjectDataFn,
_fnSetObjectDataFn: _fnSetObjectDataFn,
_fnGetDataMaster: _fnGetDataMaster,
_fnClearTable: _fnClearTable,
_fnDeleteIndex: _fnDeleteIndex,
- _fnInvalidateRow: _fnInvalidateRow,
+ _fnInvalidate: _fnInvalidate,
_fnGetRowElements: _fnGetRowElements,
_fnCreateTr: _fnCreateTr,
_fnBuildHead: _fnBuildHead,
_fnDrawHead: _fnDrawHead,
_fnDraw: _fnDraw,