/*! DataTables 1.10.20
2008-2019 SpryMedia Ltd - datatables.net/license
* @summary DataTables
* @description Paginate, search and order HTML tables
* @version 1.10.20
* @file jquery.dataTables.js
* @author SpryMedia Ltd
* @contact www.datatables.net
* @copyright Copyright 2008-2019 SpryMedia Ltd.
* This source file is free software, available under the following license:
* MIT license - http://datatables.net/license
* This source file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
* 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,_fnInvalidate,_fnGetRowElements,_fnCreateTr,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAddOptionsHtml,_fnDetectHeader,_fnGetUniqueThs,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterCo
(function( factory ) {
"use strict";
if ( typeof define === 'function' && define.amd ) {
// AMD
define( ['jquery'], function ( $ ) {
return factory( $, window, document );
else if ( typeof exports === 'object' ) {
// CommonJS
module.exports = function (root, $) {
if ( ! root ) {
// CommonJS environments without a window global must pass a
// root. This will give an error otherwise
root = window;
if ( ! $ ) {
$ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window
require('jquery') :
require('jquery')( root );
return factory( $, root, root.document );
else {
// Browser
factory( jQuery, window, document );
(function( $, window, document, undefined ) {
"use strict";
* DataTables is a plug-in for the jQuery Javascript library. It is a highly
* flexible tool, based upon the foundations of progressive enhancement,
* which will add advanced interaction controls to any HTML table. For a
* full list of features please refer to
* [DataTables.net](href="http://datatables.net).
* Note that the `DataTable` object is not a global variable but is aliased
* to `jQuery.fn.DataTable` and `jQuery.fn.dataTable` through which it may
* be accessed.
* @class
* @param {object} [init={}] Configuration object for DataTables. Options
* are defined by {@link DataTable.defaults}
* @requires jQuery 1.7+
* @example
* // Basic initialisation
* $(document).ready( function {
* $('#example').dataTable();
* } );
* @example
* // Initialisation with configuration options - in this case, disable
* // pagination and sorting.
* $(document).ready( function {
* $('#example').dataTable( {
* "paginate": false,
* "sort": false
* } );
* } );
var DataTable = function ( options )
* Perform a jQuery selector action on the table's TR elements (from the tbody) and
* return the resulting jQuery object.
* @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
* @param {object} [oOpts] Optional parameters for modifying the rows to be included
* @param {string} [oOpts.filter=none] Select TR elements that meet the current filter
* criterion ("applied") or all TR elements (i.e. no filter).
* @param {string} [oOpts.order=current] Order of the TR elements in the processed array.
* Can be either 'current', whereby the current sorting of the table is used, or
* 'original' whereby the original order the data was read into the table is used.
* @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
* ("current") or not ("all"). If 'current' is given, then order is assumed to be
* 'current' and filter is 'applied', regardless of what they might be given as.
* @returns {object} jQuery object, filtered by the given selector.
* @dtopt API
* @deprecated Since v1.10
* @example
* $(document).ready(function() {
* var oTable = $('#example').dataTable();
* // Highlight every second row
* oTable.$('tr:odd').css('backgroundColor', 'blue');
* } );
* @example
* $(document).ready(function() {
* var oTable = $('#example').dataTable();
* // Filter to rows with 'Webkit' in them, add a background colour and then
* // remove the filter, thus highlighting the 'Webkit' rows only.
* oTable.fnFilter('Webkit');
* oTable.$('tr', {"search": "applied"}).css('backgroundColor', 'blue');
* oTable.fnFilter('');
* } );
this.$ = function ( sSelector, oOpts )
return this.api(true).$( sSelector, oOpts );
* Almost identical to $ in operation, but in this case returns the data for the matched
* rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes
* rather than any descendants, so the data can be obtained for the row/cell. If matching
* rows are found, the data returned is the original data array/object that was used to
* create the row (or a generated array if from a DOM source).
* This method is often useful in-combination with $ where both functions are given the
* same parameters and the array indexes will match identically.
* @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
* @param {object} [oOpts] Optional parameters for modifying the rows to be included
* @param {string} [oOpts.filter=none] Select elements that meet the current filter
* criterion ("applied") or all elements (i.e. no filter).
* @param {string} [oOpts.order=current] Order of the data in the processed array.
* Can be either 'current', whereby the current sorting of the table is used, or
* 'original' whereby the original order the data was read into the table is used.
* @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
* ("current") or not ("all"). If 'current' is given, then order is assumed to be
* 'current' and filter is 'applied', regardless of what they might be given as.
* @returns {array} Data for the matched elements. If any elements, as a result of the
* selector, were not TR, TD or TH elements in the DataTable, they will have a null
* entry in the array.
* @dtopt API
* @deprecated Since v1.10
* @example
* $(document).ready(function() {
* var oTable = $('#example').dataTable();
* // Get the data from the first row in the table
* var data = oTable._('tr:first');
* // Do something useful with the data
* alert( "First cell is: "+data[0] );
* } );
* @example
* $(document).ready(function() {
* var oTable = $('#example').dataTable();
* // Filter to 'Webkit' and get all data for
* oTable.fnFilter('Webkit');
* var data = oTable._('tr', {"search": "applied"});
* // Do something with the data
* alert( data.length+" rows matched the search" );
* } );
this._ = function ( sSelector, oOpts )
return this.api(true).rows( sSelector, oOpts ).data();
* Create a DataTables Api instance, with the currently selected tables for
* the Api's context.
* @param {boolean} [traditional=false] Set the API instance's context to be
* only the table referred to by the `DataTable.ext.iApiIndex` option, as was
* used in the API presented by DataTables 1.9- (i.e. the traditional mode),
* or if all tables captured in the jQuery object should be used.
* @return {DataTables.Api}
this.api = function ( traditional )
return traditional ?
new _Api(
_fnSettingsFromNode( this[ _ext.iApiIndex ] )
new _Api( this );
* Add a single new row or multiple rows of data to the table. Please note
* that this is suitable for client-side processing only - if you are using
* server-side processing (i.e. "bServerSide": true), then to add data, you
* must add it to the data source, i.e. the server-side, through an Ajax call.
* @param {array|object} data The data to be added to the table. This can be:
* <ul>
* <li>1D array of data - add a single row with the data provided</li>
* <li>2D array of arrays - add multiple rows in a single call</li>
* <li>object - data object when using <i>mData</i></li>
* <li>array of objects - multiple data objects when using <i>mData</i></li>
* </ul>
* @param {bool} [redraw=true] redraw the table or not
* @returns {array} An array of integers, representing the list of indexes in
* <i>aoData</i> ({@link DataTable.models.oSettings}) that have been added to
* the table.
* @dtopt API
* @deprecated Since v1.10
* @example
* // Global var for counter
* var giCount = 2;
* $(document).ready(function() {
* $('#example').dataTable();
* } );
* function fnClickAddRow() {
* $('#example').dataTable().fnAddData( [
* giCount+".1",
* giCount+".2",
* giCount+".3",
* giCount+".4" ]
* );
* giCount++;
* }
this.fnAddData = function( data, redraw )
var api = this.api( true );
/* Check if we want to add multiple rows or not */
var rows = $.isArray(data) && ( $.isArray(data[0]) || $.isPlainObject(data[0]) ) ?
api.rows.add( data ) :
api.row.add( data );
if ( redraw === undefined || redraw ) {
api.draw();
return rows.flatten().toArray();
* This function will make DataTables recalculate the column sizes, based on the data
* contained in the table and the sizes applied to the columns (in the DOM, CSS or
* through the sWidth parameter). This can be useful when the width of the table's
* parent element changes (for example a window resize).
* @param {boolean} [bRedraw=true] Redraw the table or not, you will typically want to
* @dtopt API
* @deprecated Since v1.10
* @example
* $(document).ready(function() {
* var oTable = $('#example').dataTable( {
* "sScrollY": "200px",
* "bPaginate": false
* } );
* $(window).on('resize', function () {
* oTable.fnAdjustColumnSizing();
* } );
* } );
this.fnAdjustColumnSizing = function ( bRedraw )
var api = this.api( true ).columns.adjust();
var settings = api.settings()[0];
var scroll = settings.oScroll;
if ( bRedraw === undefined || bRedraw ) {
api.draw( false );
else if ( scroll.sX !== "" || scroll.sY !== "" ) {
/* If not redrawing, but scrolling, we want to apply the new column sizes anyway */
_fnScrollDraw( settings );
* Quickly and simply clear a table
* @param {bool} [bRedraw=true] redraw the table or not
* @dtopt API
* @deprecated Since v1.10
* @example
* $(document).ready(function() {
* var oTable = $('#example').dataTable();
* // Immediately 'nuke' the current rows (perhaps waiting for an Ajax callback...)
* oTable.fnClearTable();
* } );
this.fnClearTable = function( bRedraw )
var api = this.api( true ).clear();
if ( bRedraw === undefined || bRedraw ) {
api.draw();
* The exact opposite of 'opening' a row, this function will close any rows which
* are currently 'open'.
* @param {node} nTr the table row to 'close'
* @returns {int} 0 on success, or 1 if failed (can't find the row)
* @dtopt API
* @deprecated Since v1.10
* @example
* $(document).ready(function() {
* var oTable;
* // 'open' an information row when a row is clicked on
* $('#example tbody tr').click( function () {
* if ( oTable.fnIsOpen(this) ) {
* oTable.fnClose( this );
* } else {
* oTable.fnOpen( this, "Temporary row opened", "info_row" );
* }
* } );
* oTable = $('#example').dataTable();
* } );
this.fnClose = function( nTr )
this.api( true ).row( nTr ).child.hide();
* Remove a row for the table
* @param {mixed} target The index of the row from aoData to be deleted, or
* the TR element you want to delete
* @param {function|null} [callBack] Callback function
* @param {bool} [redraw=true] Redraw the table or not
* @returns {array} The row that was deleted
* @dtopt API
* @deprecated Since v1.10
* @example
* $(document).ready(function() {
* var oTable = $('#example').dataTable();
* // Immediately remove the first row
* oTable.fnDeleteRow( 0 );
* } );
this.fnDeleteRow = function( target, callback, redraw )
var api = this.api( true );
var rows = api.rows( target );
var settings = rows.settings()[0];
var data = settings.aoData[ rows[0][0] ];
rows.remove();
if ( callback ) {
callback.call( this, settings, data );
if ( redraw === undefined || redraw ) {
api.draw();
return data;
* Restore the table to it's original state in the DOM by removing all of DataTables
* enhancements, alterations to the DOM structure of the table and event listeners.
* @param {boolean} [remove=false] Completely remove the table from the DOM
* @dtopt API
* @deprecated Since v1.10
* @example
* $(document).ready(function() {
* // This example is fairly pointless in reality, but shows how fnDestroy can be used
* var oTable = $('#example').dataTable();
* oTable.fnDestroy();
* } );
this.fnDestroy = function ( remove )
this.api( true ).destroy( remove );
* Redraw the table
* @param {bool} [complete=true] Re-filter and resort (if enabled) the table before the draw.
* @dtopt API
* @deprecated Since v1.10
* @example
* $(document).ready(function() {
* var oTable = $('#example').dataTable();
* // Re-draw the table - you wouldn't want to do it here, but it's an example :-)
* oTable.fnDraw();
* } );
this.fnDraw = function( complete )
// Note that this isn't an exact match to the old call to _fnDraw - it takes
// into account the new data, but can hold position.
this.api( true ).draw( complete );
* Filter the input based on data
* @param {string} sInput String to filter the table on
* @param {int|null} [iColumn] Column to limit filtering to
* @param {bool} [bRegex=false] Treat as regular expression or not
* @param {bool} [bSmart=true] Perform smart filtering or not
* @param {bool} [bShowGlobal=true] Show the input global filter in it's input box(es)
* @param {bool} [bCaseInsensitive=true] Do case-insensitive matching (true) or not (false)
* @dtopt API
* @deprecated Since v1.10
* @example
* $(document).ready(function() {
* var oTable = $('#example').dataTable();
* // Sometime later - filter...
* oTable.fnFilter( 'test string' );
* } );
this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive )
var api = this.api( true );
if ( iColumn === null || iColumn === undefined ) {
api.search( sInput, bRegex, bSmart, bCaseInsensitive );
else {
api.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive );
api.draw();
* Get the data for the whole table, an individual row or an individual cell based on the
* provided parameters.
* @param {int|node} [src] A TR row node, TD/TH cell node or an integer. If given as
* a TR node then the data source for the whole row will be returned. If given as a
* TD/TH cell node then iCol will be automatically calculated and the data for the
* cell returned. If given as an integer, then this is treated as the aoData internal
* data index for the row (see fnGetPosition) and the data for that row used.
* @param {int} [col] Optional column index that you want the data of.
* @returns {array|object|string} If mRow is undefined, then the data for all rows is
* returned. If mRow is defined, just data for that row, and is iCol is
* defined, only data for the designated cell is returned.
* @dtopt API
* @deprecated Since v1.10
* @example
* // Row data
* $(document).ready(function() {
* oTable = $('#example').dataTable();
* oTable.$('tr').click( function () {
* var data = oTable.fnGetData( this );
* // ... do something with the array / object of data for the row
* } );
* } );
* @example
* // Individual cell data
* $(document).ready(function() {
* oTable = $('#example').dataTable();
* oTable.$('td').click( function () {
* var sData = oTable.fnGetData( this );
* alert( 'The cell clicked on had the value of '+sData );
* } );
* } );
this.fnGetData = function( src, col )
var api = this.api( true );
if ( src !== undefined ) {
var type = src.nodeName ? src.nodeName.toLowerCase() : '';
return col !== undefined || type == 'td' || type == 'th' ?
api.cell( src, col ).data() :
api.row( src ).data() || null;
return api.data().toArray();
* Get an array of the TR nodes that are used in the table's body. Note that you will
* typically want to use the '$' API method in preference to this as it is more
* flexible.
* @param {int} [iRow] Optional row index for the TR element you want
* @returns {array|node} If iRow is undefined, returns an array of all TR elements
* in the table's body, or iRow is defined, just the TR element requested.
* @dtopt API
* @deprecated Since v1.10
* @example
* $(document).ready(function() {
* var oTable = $('#example').dataTable();
* // Get the nodes from the table
* var nNodes = oTable.fnGetNodes( );
* } );
this.fnGetNodes = function( iRow )
var api = this.api( true );
return iRow !== undefined ?
api.row( iRow ).node() :
api.rows().nodes().flatten().toArray();
* Get the array indexes of a particular cell from it's DOM element
* and column index including hidden columns
* @param {node} node this can either be a TR, TD or TH in the table's body
* @returns {int} If nNode is given as a TR, then a single index is returned, or
* if given as a cell, an array of [row index, column index (visible),
* column index (all)] is given.
* @dtopt API
* @deprecated Since v1.10
* @example
* $(document).ready(function() {
* $('#example tbody td').click( function () {
* // Get the position of the current data from the node
* var aPos = oTable.fnGetPosition( this );
* // Get the data array for this row
* var aData = oTable.fnGetData( aPos[0] );
* // Update the data array and return the value
* aData[ aPos[1] ] = 'clicked';
* this.innerHTML = 'clicked';
* } );
* // Init DataTables
* oTable = $('#example').dataTable();
* } );
this.fnGetPosition = function( node )
var api = this.api( true );
var nodeName = node.nodeName.toUpperCase();
if ( nodeName == 'TR' ) {
return api.row( node ).index();
else if ( nodeName == 'TD' || nodeName == 'TH' ) {
var cell = api.cell( node ).index();
return [
cell.row,
cell.columnVisible,
cell.column
return null;
* Check to see if a row is 'open' or not.
* @param {node} nTr the table row to check
* @returns {boolean} true if the row is currently open, false otherwise
* @dtopt API
* @deprecated Since v1.10
* @example
* $(document).ready(function() {
* var oTable;
* // 'open' an information row when a row is clicked on
* $('#example tbody tr').click( function () {
* if ( oTable.fnIsOpen(this) ) {
* oTable.fnClose( this );
* } else {
* oTable.fnOpen( this, "Temporary row opened", "info_row" );
* }
* } );
* oTable = $('#example').dataTable();
* } );
this.fnIsOpen = function( nTr )
return this.api( true ).row( nTr ).child.isShown();
* This function will place a new row directly after a row which is currently
* on display on the page, with the HTML contents that is passed into the
* function. This can be used, for example, to ask for confirmation that a
* particular record should be deleted.
* @param {node} nTr The table row to 'open'
* @param {string|node|jQuery} mHtml The HTML to put into the row
* @param {string} sClass Class to give the new TD cell
* @returns {node} The row opened. Note that if the table row passed in as the
* first parameter, is not found in the table, this method will silently
* return.
* @dtopt API
* @deprecated Since v1.10
* @example
* $(document).ready(function() {
* var oTable;
* // 'open' an information row when a row is clicked on
* $('#example tbody tr').click( function () {
* if ( oTable.fnIsOpen(this) ) {
* oTable.fnClose( this );
* } else {
* oTable.fnOpen( this, "Temporary row opened", "info_row" );
* }
* } );
* oTable = $('#example').dataTable();
* } );
this.fnOpen = function( nTr, mHtml, sClass )
return this.api( true )
.row( nTr )
.child( mHtml, sClass )
.show()
.child()[0];
* Change the pagination - provides the internal logic for pagination in a simple API
* function. With this function you can have a DataTables table go to the next,
* previous, first or last pages.
* @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last"
* or page number to jump to (integer), note that page 0 is the first page.
* @param {bool} [bRedraw=true] Redraw the table or not
* @dtopt API
* @deprecated Since v1.10
* @example
* $(document).ready(function() {
* var oTable = $('#example').dataTable();
* oTable.fnPageChange( 'next' );
* } );
this.fnPageChange = function ( mAction, bRedraw )
var api = this.api( true ).page( mAction );
if ( bRedraw === undefined || bRedraw ) {
api.draw(false);
* Show a particular column
* @param {int} iCol The column whose display should be changed
* @param {bool} bShow Show (true) or hide (false) the column
* @param {bool} [bRedraw=true] Redraw the table or not
* @dtopt API
* @deprecated Since v1.10
* @example
* $(document).ready(function() {
* var oTable = $('#example').dataTable();
* // Hide the second column after initialisation
* oTable.fnSetColumnVis( 1, false );
* } );
this.fnSetColumnVis = function ( iCol, bShow, bRedraw )
var api = this.api( true ).column( iCol ).visible( bShow );
if ( bRedraw === undefined || bRedraw ) {
api.columns.adjust().draw();
* Get the settings for a particular table for external manipulation
* @returns {object} DataTables settings object. See
* {@link DataTable.models.oSettings}
* @dtopt API
* @deprecated Since v1.10
* @example
* $(document).ready(function() {
* var oTable = $('#example').dataTable();
* var oSettings = oTable.fnSettings();
* // Show an example parameter from the settings
* alert( oSettings._iDisplayStart );
* } );
this.fnSettings = function()
return _fnSettingsFromNode( this[_ext.iApiIndex] );
* Sort the table by a particular column
* @param {int} iCol the data index to sort on. Note that this will not match the
* 'display index' if you have hidden data entries
* @dtopt API
* @deprecated Since v1.10
* @example
* $(document).ready(function() {
* var oTable = $('#example').dataTable();
* // Sort immediately with columns 0 and 1
* oTable.fnSort( [ [0,'asc'], [1,'asc'] ] );
* } );
this.fnSort = function( aaSort )
this.api( true ).order( aaSort ).draw();
* Attach a sort listener to an element for a given column
* @param {node} nNode the element to attach the sort listener to
* @param {int} iColumn the column that a click on this node will sort on
* @param {function} [fnCallback] callback function when sort is run
* @dtopt API
* @deprecated Since v1.10
* @example
* $(document).ready(function() {
* var oTable = $('#example').dataTable();
* // Sort on column 1, when 'sorter' is clicked on
* oTable.fnSortListener( document.getElementById('sorter'), 1 );
* } );
this.fnSortListener = function( nNode, iColumn, fnCallback )
this.api( true ).order.listener( nNode, iColumn, fnCallback );
* Update a table cell or row - this method will accept either a single value to
* update the cell with, an array of values with one element for each column or
* an object in the same format as the original data source. The function is
* self-referencing in order to make the multi column updates easier.
* @param {object|array|string} mData Data to update the cell/row with
* @param {node|int} mRow TR element you want to update or the aoData index
* @param {int} [iColumn] The column to update, give as null or undefined to
* update a whole row.
* @param {bool} [bRedraw=true] Redraw the table or not
* @param {bool} [bAction=true] Perform pre-draw actions or not
* @returns {int} 0 on success, 1 on error
* @dtopt API
* @deprecated Since v1.10
* @example
* $(document).ready(function() {
* var oTable = $('#example').dataTable();
* oTable.fnUpdate( 'Example update', 0, 0 ); // Single cell
* oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], $('tbody tr')[0] ); // Row
* } );
this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction )
var api = this.api( true );
if ( iColumn === undefined || iColumn === null ) {
api.row( mRow ).data( mData );
else {
api.cell( mRow, iColumn ).data( mData );
if ( bAction === undefined || bAction ) {
api.columns.adjust();
if ( bRedraw === undefined || bRedraw ) {
api.draw();
return 0;
* Provide a common method for plug-ins to check the version of DataTables being used, in order
* to ensure compatibility.
* @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note that the
* formats "X" and "X.Y" are also acceptable.
* @returns {boolean} true if this version of DataTables is greater or equal to the required
* version, or false if this version of DataTales is not suitable
* @method
* @dtopt API
* @deprecated Since v1.10
* @example
* $(document).ready(function() {
* var oTable = $('#example').dataTable();
* alert( oTable.fnVersionCheck( '1.9.0' ) );
* } );
this.fnVersionCheck = _ext.fnVersionCheck;
var _that = this;
var emptyInit = options === undefined;
var len = this.length;
if ( emptyInit ) {
options = {};
this.oApi = this.internal = _ext.internal;
// Extend with old style plug-in API methods
for ( var fn in DataTable.ext.internal ) {
if ( fn ) {
this[fn] = _fnExternApiFunc(fn);
this.each(function() {
// For each initialisation we want to give it a clean initialisation
// object that can be bashed around
var o = {};
var oInit = len > 1 ? // optimisation for single table case
_fnExtend( o, options, true ) :
options;
/*global oInit,_that,emptyInit*/
var i=0, iLen, j, jLen, k, kLen;
var sId = this.getAttribute( 'id' );
var bInitHandedOff = false;
var defaults = DataTable.defaults;
var $this = $(this);
/* Sanity check */
if ( this.nodeName.toLowerCase() != 'table' )
_fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 );
return;
/* Backwards compatibility for the defaults */
_fnCompatOpts( defaults );
_fnCompatCols( defaults.column );
/* Convert the camel-case defaults to Hungarian */
_fnCamelToHungarian( defaults, defaults, true );
_fnCamelToHungarian( defaults.column, defaults.column, true );
/* Setting up the initialisation object */
_fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ), true );
/* Check to see if we are re-initialising a table */
var allSettings = DataTable.settings;
for ( i=0, iLen=allSettings.length ; i<iLen ; i++ )
var s = allSettings[i];
/* Base check on table node */
s.nTable == this ||
(s.nTHead && s.nTHead.parentNode == this) ||
(s.nTFoot && s.nTFoot.parentNode == this)
var bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve;
var bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy;
if ( emptyInit || bRetrieve )
return s.oInstance;
else if ( bDestroy )
s.oInstance.fnDestroy();
break;
_fnLog( s, 0, 'Cannot reinitialise DataTable', 3 );
return;
/* If the element we are initialising has the same ID as a table which was previously
* initialised, but the table nodes don't match (from before) then we destroy the old
* instance by simply deleting it. This is under the assumption that the table has been
* destroyed by other methods. Anyone using non-id selectors will need to do this manually
if ( s.sTableId == this.id )
allSettings.splice( i, 1 );
break;
/* Ensure the table has an ID - required for accessibility */
if ( sId === null || sId === "" )
sId = "DataTables_Table_"+(DataTable.ext._unique++);
this.id = sId;
/* Create the settings object for this table and set some of the default parameters */
var oSettings = $.extend( true, {}, DataTable.models.oSettings, {
"sDestroyWidth": $this[0].style.width,
"sInstance": sId,
"sTableId": sId
oSettings.nTable = this;
oSettings.oApi = _that.internal;
oSettings.oInit = oInit;
allSettings.push( oSettings );
// Need to add the instance after the instance after the settings object has been added
// to the settings array, so we can self reference the table instance if more than one
oSettings.oInstance = (_that.length===1) ? _that : $this.dataTable();
// Backwards compatibility, before we apply all the defaults
_fnCompatOpts( oInit );
_fnLanguageCompat( oInit.oLanguage );
// If the length menu is given, but the init display length is not, use the length menu
if ( oInit.aLengthMenu && ! oInit.iDisplayLength )
oInit.iDisplayLength = $.isArray( oInit.aLengthMenu[0] ) ?
oInit.aLengthMenu[0][0] : oInit.aLengthMenu[0];
// Apply the defaults and init options to make a single init object will all
// options defined from defaults and instance options.
oInit = _fnExtend( $.extend( true, {}, defaults ), oInit );
// Map the initialisation options onto the settings object
_fnMap( oSettings.oFeatures, oInit, [
"bPaginate",
"bLengthChange",
"bFilter",
"bSort",
"bSortMulti",
"bInfo",
"bProcessing",
"bAutoWidth",
"bSortClasses",
"bServerSide",
"bDeferRender"
_fnMap( oSettings, oInit, [
"asStripeClasses",
"ajax",
"fnServerData",
"fnFormatNumber",
"sServerMethod",
"aaSorting",
"aaSortingFixed",
"aLengthMenu",
"sPaginationType",
"sAjaxSource",
"sAjaxDataProp",
"iStateDuration",
"sDom",
"bSortCellsTop",
"iTabIndex",
"fnStateLoadCallback",
"fnStateSaveCallback",
"renderer",
"searchDelay",
"rowId",
[ "iCookieDuration", "iStateDuration" ], // backwards compat
[ "oSearch", "oPreviousSearch" ],
[ "aoSearchCols", "aoPreSearchCols" ],
[ "iDisplayLength", "_iDisplayLength" ]
_fnMap( oSettings.oScroll, oInit, [
[ "sScrollX", "sX" ],
[ "sScrollXInner", "sXInner" ],
[ "sScrollY", "sY" ],
[ "bScrollCollapse", "bCollapse" ]
_fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" );
/* Callback functions which are array driven */
_fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback, 'user' );
_fnCallbackReg( oSettings, 'aoServerParams', oInit.fnServerParams, 'user' );
_fnCallbackReg( oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams, 'user' );
_fnCallbackReg( oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams, 'user' );
_fnCallbackReg( oSettings, 'aoStateLoaded', oInit.fnStateLoaded, 'user' );
_fnCallbackReg( oSettings, 'aoRowCallback', oInit.fnRowCallback, 'user' );
_fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow, 'user' );
_fnCallbackReg( oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback, 'user' );
_fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback, 'user' );
_fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete, 'user' );
_fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback, 'user' );
oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId );
/* Browser support detection */
_fnBrowserDetect( oSettings );
var oClasses = oSettings.oClasses;
$.extend( oClasses, DataTable.ext.classes, oInit.oClasses );
$this.addClass( oClasses.sTable );
if ( oSettings.iInitDisplayStart === undefined )
/* Display start point, taking into account the save saving */
oSettings.iInitDisplayStart = oInit.iDisplayStart;
oSettings._iDisplayStart = oInit.iDisplayStart;
if ( oInit.iDeferLoading !== null )
oSettings.bDeferLoading = true;
var tmp = $.isArray( oInit.iDeferLoading );
oSettings._iRecordsDisplay = tmp ? oInit.iDeferLoading[0] : oInit.iDeferLoading;
oSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1] : oInit.iDeferLoading;
/* Language definitions */
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
$.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;
* Stripes
if ( oInit.asStripeClasses === null )
oSettings.asStripeClasses =[
oClasses.sStripeOdd,
oClasses.sStripeEven
/* Remove row stripe classes if they are already on the table row */
var stripeClasses = oSettings.asStripeClasses;
var rowOne = $this.children('tbody').find('tr').eq(0);
if ( $.inArray( true, $.map( stripeClasses, function(el, i) {
return rowOne.hasClass(el);
} ) ) !== -1 ) {
$('tbody tr', this).removeClass( stripeClasses.join(' ') );
oSettings.asDestroyStripes = stripeClasses.slice();
* Columns
* See if we should load columns automatically or use defined ones
var anThs = [];
var aoColumnsInit;
var nThead = this.getElementsByTagName('thead');
if ( nThead.length !== 0 )
_fnDetectHeader( oSettings.aoHeader, nThead[0] );
anThs = _fnGetUniqueThs( oSettings );
/* If not given a column array, generate one with nulls */
if ( oInit.aoColumns === null )
aoColumnsInit = [];
for ( i=0, iLen=anThs.length ; i<iLen ; i++ )
aoColumnsInit.push( null );
aoColumnsInit = oInit.aoColumns;
/* Add the columns */
for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
_fnAddColumn( oSettings, anThs ? anThs[i] : null );
/* Apply the column definitions */
_fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, aoColumnsInit, function (iCol, oDef) {
_fnColumnOptions( oSettings, iCol, oDef );
/* HTML5 attribute detection - build an mData object automatically if the
* attributes are found
if ( rowOne.length ) {
var a = function ( cell, name ) {
return cell.getAttribute( 'data-'+name ) !== null ? name : null;
$( rowOne[0] ).children('th, td').each( function (i, cell) {
var col = oSettings.aoColumns[i];
if ( col.mData === i ) {
var sort = a( cell, 'sort' ) || a( cell, 'order' );
var filter = a( cell, 'filter' ) || a( cell, 'search' );
if ( sort !== null || filter !== null ) {
col.mData = {
_: i+'.display',
sort: sort !== null ? i+'.@data-'+sort : undefined,
type: sort !== null ? i+'.@data-'+sort : undefined,
filter: filter !== null ? i+'.@data-'+filter : undefined
_fnColumnOptions( oSettings, i );
var features = oSettings.oFeatures;
var loadedInit = function () {
* Sorting
* @todo For modularisation (1.11) this needs to do into a sort start up handler
// If aaSorting is not defined, then we use the first indicator in asSorting
// in case that has been altered, so the default sort reflects that option
if ( oInit.aaSorting === undefined ) {
var sorting = oSettings.aaSorting;
for ( i=0, iLen=sorting.length ; i<iLen ; i++ ) {
sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0];
/* Do a first pass on the sorting classes (allows any size changes to be taken into
* account, and also will apply sorting disabled classes if disabled
_fnSortingClasses( oSettings );
if ( features.bSort ) {
_fnCallbackReg( oSettings, 'aoDrawCallback', function () {
if ( oSettings.bSorted ) {
var aSort = _fnSortFlatten( oSettings );
var sortedColumns = {};
$.each( aSort, function (i, val) {
sortedColumns[ val.src ] = val.dir;
_fnCallbackFire( oSettings, null, 'order', [oSettings, aSort, sortedColumns] );
_fnSortAria( oSettings );
_fnCallbackReg( oSettings, 'aoDrawCallback', function () {
if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) {
_fnSortingClasses( oSettings );
}, 'sc' );
* Final init
* Cache the header, body and footer as required, creating them if needed
// Work around for Webkit bug 83867 - store the caption-side before removing from doc
var captions = $this.children('caption').each( function () {
this._captionSide = $(this).css('caption-side');
var thead = $this.children('thead');
if ( thead.length === 0 ) {
thead = $('<thead/>').appendTo($this);
oSettings.nTHead = thead[0];
var tbody = $this.children('tbody');
if ( tbody.length === 0 ) {
tbody = $('<tbody/>').appendTo($this);
oSettings.nTBody = tbody[0];
var tfoot = $this.children('tfoot');
if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") ) {
// If we are a scrolling table, and no footer has been given, then we need to create
// a tfoot element for the caption element to be appended to
tfoot = $('<tfoot/>').appendTo($this);
if ( tfoot.length === 0 || tfoot.children().length === 0 ) {
$this.addClass( oClasses.sNoFooter );
else if ( tfoot.length > 0 ) {
oSettings.nTFoot = tfoot[0];
_fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot );
/* Check if there is data passing into the constructor */
if ( oInit.aaData ) {
for ( i=0 ; i<oInit.aaData.length ; i++ ) {
_fnAddData( oSettings, oInit.aaData[ i ] );
else if ( oSettings.bDeferLoading || _fnDataSource( oSettings ) == 'dom' ) {
/* Grab the data from the page - only do this when deferred loading or no Ajax
* source since there is no point in reading the DOM data if we are then going
* to replace it with Ajax data
_fnAddTr( oSettings, $(oSettings.nTBody).children('tr') );
/* Copy the data index array */
oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
/* Initialisation complete - table can be drawn */
oSettings.bInitialised = true;
/* Check if we need to initialise the table (it might not have been handed off to the
* language processor)
if ( bInitHandedOff === false ) {
_fnInitialise( oSettings );
/* Must be done after everything which can be overridden by the state saving! */
if ( oInit.bStateSave )
features.bStateSave = true;
_fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState, 'state_save' );
_fnLoadState( oSettings, oInit, loadedInit );
else {
loadedInit();
_that = null;
return this;
* It is useful to have variables which are scoped locally so only the
* DataTables functions can access them and they don't leak into global space.
* At the same time these functions are often useful over multiple files in the
* core and API, so we list, or at least document, all variables which are used
* by DataTables as private variables here. This also ensures that there is no
* clashing of variable names and that they can easily referenced for reuse.
// Defined else where
// _selector_run
// _selector_opts
// _selector_first
// _selector_row_indexes
var _ext; // DataTable.ext
var _Api; // DataTable.Api
var _api_register; // DataTable.Api.register
var _api_registerPlural; // DataTable.Api.registerPlural
var _re_dic = {};
var _re_new_lines = /[\r\n\u2028]/g;
var _re_html = /<.*?>/g;
// This is not strict ISO8601 - Date.parse() is quite lax, although
// implementations differ between browsers.
var _re_date = /^\d{2,4}[\.\/\-]\d{1,2}[\.\/\-]\d{1,2}([T ]{1}\d{1,2}[:\.]\d{2}([\.:]\d{2})?)?$/;
// Escape regular expression special characters
var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' );
// http://en.wikipedia.org/wiki/Foreign_exchange_market
// - \u20BD - Russian ruble.
// - \u20a9 - South Korean Won
// - \u20BA - Turkish Lira
// - \u20B9 - Indian Rupee
// - R - Brazil (R$) and South Africa
// - fr - Swiss Franc
// - kr - Swedish krona, Norwegian krone and Danish krone
// - \u2009 is thin space and \u202F is narrow no-break space, both used in many
- Bitcoin
- Ethereum
// standards as thousands separators.
var _re_formatted_numeric = /[',$
%\u2009\u202F\u20BD\u20a9\u20BArfk
var _empty = function ( d ) {
return !d || d === true || d === '-' ? true : false;
var _intVal = function ( s ) {
var integer = parseInt( s, 10 );
return !isNaN(integer) && isFinite(s) ? integer : null;
// Convert from a formatted number with characters other than `.` as the
// decimal place, to a Javascript number
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' && decimalPoint !== '.' ?
num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) :
var _isNumber = function ( d, decimalPoint, formatted ) {
var strType = typeof d === 'string';
// If empty return immediately so there must be a number if it is a
// formatted string (this stops the string "k", or "kr", etc being detected
// as a formatted number for currency
if ( _empty( d ) ) {
return true;
if ( decimalPoint && strType ) {
d = _numToDecimal( d, decimalPoint );
if ( formatted && strType ) {
d = d.replace( _re_formatted_numeric, '' );
return !isNaN( parseFloat(d) ) && isFinite( d );
// A string without HTML in it can be considered to be HTML still
var _isHtml = function ( d ) {
return _empty( d ) || typeof d === 'string';
var _htmlNumeric = function ( d, decimalPoint, formatted ) {
if ( _empty( d ) ) {
return true;
var html = _isHtml( d );
return ! html ?
null :
_isNumber( _stripHtml( d ), decimalPoint, formatted ) ?
true :
var _pluck = function ( a, prop, prop2 ) {
var out = [];
var i=0, ien=a.length;
// Could have the test in the loop for slightly smaller code, but speed
// is essential here
if ( prop2 !== undefined ) {
for ( ; i<ien ; i++ ) {
if ( a[i] && a[i][ prop ] ) {
out.push( a[i][ prop ][ prop2 ] );
else {
for ( ; i<ien ; i++ ) {
if ( a[i] ) {
out.push( a[i][ prop ] );
return out;
// Basically the same as _pluck, but rather than looping over `a` we use `order`
// as the indexes to pick from `a`
var _pluck_order = function ( a, order, prop, prop2 )
var out = [];
var i=0, ien=order.length;
// Could have the test in the loop for slightly smaller code, but speed
// is essential here
if ( prop2 !== undefined ) {
for ( ; i<ien ; i++ ) {
if ( a[ order[i] ][ prop ] ) {
out.push( a[ order[i] ][ prop ][ prop2 ] );
else {
for ( ; i<ien ; i++ ) {
out.push( a[ order[i] ][ prop ] );
return out;
var _range = function ( len, start )
var out = [];
var end;
if ( start === undefined ) {
start = 0;
end = len;
else {
end = start;
start = len;
for ( var i=start ; i<end ; i++ ) {
out.push( i );
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, '' );
* Determine if all values in the array are unique. This means we can short
* cut the _unique method at the cost of a single loop. A sorted array is used
* to easily check the values.
* @param {array} src Source array
* @return {boolean} true if all unique, false otherwise
* @ignore
var _areAllUnique = function ( src ) {
if ( src.length < 2 ) {
return true;
var sorted = src.slice().sort();
var last = sorted[0];
for ( var i=1, ien=sorted.length ; i<ien ; i++ ) {
if ( sorted[i] === last ) {
return false;
last = sorted[i];
return true;
* Find the unique elements in a source array.
* @param {array} src Source array
* @return {array} Array of unique items
* @ignore
var _unique = function ( src )
if ( _areAllUnique( src ) ) {
return src.slice();
// A faster unique method is to use object keys to identify used values,
// but this doesn't work with arrays or objects, which we must also
// consider. See jsperf.com/compare-array-unique-versions/4 for more
// information.
out = [],
i, ien=src.length,
j, k=0;
again: for ( i=0 ; i<ien ; i++ ) {
val = src[i];
for ( j=0 ; j<k ; j++ ) {
if ( out[j] === val ) {
continue again;
out.push( val );
return out;
* 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: function ( fn, freq ) {
frequency = freq !== undefined ? freq : 200,
timer;
return function () {
that = this,
now = +new Date(),
args = arguments;
if ( last && now < last + frequency ) {
clearTimeout( timer );
timer = setTimeout( function () {
last = undefined;
fn.apply( that, args );
}, frequency );
else {
last = now;
fn.apply( that, args );
* Escape a string such that it can be used in a regular expression
* @param {string} val string to escape
* @returns {string} escaped string
escapeRegex: function ( val ) {
return val.replace( _re_escape_regex, '\\$1' );
* Create a mapping object that allows camel case parameters to be looked up
* for their Hungarian counterparts. The mapping is stored in a private
* parameter called `_hungarianMap` which can be accessed on the source object.
* @param {object} o
* @memberof DataTable#oApi
function _fnHungarianMap ( o )
hungarian = 'a aa ai ao as b fn i m o s ',
match,
newKey,
map = {};
$.each( o, function (key, val) {
match = key.match(/^([^A-Z]+?)([A-Z])/);
if ( match && hungarian.indexOf(match[1]+' ') !== -1 )
newKey = key.replace( match[0], match[2].toLowerCase() );
map[ newKey ] = key;
if ( match[1] === 'o' )
_fnHungarianMap( o[key] );
o._hungarianMap = map;
* Convert from camel case parameters to Hungarian, based on a Hungarian map
* created by _fnHungarianMap.
* @param {object} src The model object which holds all parameters that can be
* mapped.
* @param {object} user The object to convert from camel case to Hungarian.
* @param {boolean} force When set to `true`, properties which already have a
* Hungarian value in the `user` object will be overwritten. Otherwise they
* won't be.
* @memberof DataTable#oApi
function _fnCamelToHungarian ( src, user, force )
if ( ! src._hungarianMap ) {
_fnHungarianMap( src );
var hungarianKey;
$.each( user, function (key, val) {
hungarianKey = src._hungarianMap[ key ];
if ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) )
// For objects, we need to buzz down into the object to copy parameters
if ( hungarianKey.charAt(0) === 'o' )
// Copy the camelCase options over to the hungarian
if ( ! user[ hungarianKey ] ) {
user[ hungarianKey ] = {};
$.extend( true, user[hungarianKey], user[key] );
_fnCamelToHungarian( src[hungarianKey], user[hungarianKey], force );
else {
user[hungarianKey] = user[ key ];
* Language compatibility - when certain options are given, and others aren't, we
* need to duplicate the values over, in order to provide backwards compatibility
* with older language files.
* @param {object} oSettings dataTables settings object
* @memberof DataTable#oApi
function _fnLanguageCompat( lang )
// Note the use of the Hungarian notation for the parameters in this method as
// this is called after the mapping of camelCase to Hungarian
var defaults = DataTable.defaults.oLanguage;
// Default mapping
var defaultDecimal = defaults.sDecimal;
if ( defaultDecimal ) {
_addNumericSort( defaultDecimal );
if ( lang ) {
var zeroRecords = lang.sZeroRecords;
// Backwards compatibility - if there is no sEmptyTable given, then use the same as
// sZeroRecords - assuming that is given.
if ( ! lang.sEmptyTable && zeroRecords &&
defaults.sEmptyTable === "No data available in table" )
_fnMap( lang, lang, 'sZeroRecords', 'sEmptyTable' );
// Likewise with loading records
if ( ! lang.sLoadingRecords && zeroRecords &&
defaults.sLoadingRecords === "Loading..." )
_fnMap( lang, lang, 'sZeroRecords', 'sLoadingRecords' );
// Old parameter name of the thousands separator mapped onto the new
if ( lang.sInfoThousands ) {
lang.sThousands = lang.sInfoThousands;
var decimal = lang.sDecimal;
if ( decimal && defaultDecimal !== decimal ) {
_addNumericSort( decimal );
* Map one parameter onto another
* @param {object} o Object to map
* @param {*} knew The new parameter name
* @param {*} old The old parameter name
var _fnCompatMap = function ( o, knew, old ) {
if ( o[ knew ] !== undefined ) {
o[ old ] = o[ knew ];
* Provide backwards compatibility for the main DT options. Note that the new
* options are mapped onto the old parameters, so this is an external interface
* change only.
* @param {object} init Object to map
function _fnCompatOpts ( init )
_fnCompatMap( init, 'ordering', 'bSort' );
_fnCompatMap( init, 'orderMulti', 'bSortMulti' );
_fnCompatMap( init, 'orderClasses', 'bSortClasses' );
_fnCompatMap( init, 'orderCellsTop', 'bSortCellsTop' );
_fnCompatMap( init, 'order', 'aaSorting' );
_fnCompatMap( init, 'orderFixed', 'aaSortingFixed' );
_fnCompatMap( init, 'paging', 'bPaginate' );
_fnCompatMap( init, 'pagingType', 'sPaginationType' );
_fnCompatMap( init, 'pageLength', 'iDisplayLength' );
_fnCompatMap( init, 'searching', 'bFilter' );
// Boolean initialisation of x-scrolling
if ( typeof init.sScrollX === 'boolean' ) {
init.sScrollX = init.sScrollX ? '100%' : '';
if ( typeof init.scrollX === 'boolean' ) {
init.scrollX = init.scrollX ? '100%' : '';
// 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
* are mapped onto the old parameters, so this is an external interface change
* only.
* @param {object} init Object to map
function _fnCompatCols ( init )
_fnCompatMap( init, 'orderable', 'bSortable' );
_fnCompatMap( init, 'orderData', 'aDataSort' );
_fnCompatMap( init, 'orderSequence', 'asSorting' );
_fnCompatMap( init, 'orderDataType', 'sortDataType' );
// orderData can be given as an integer
var dataSort = init.aDataSort;
if ( typeof dataSort === 'number' && ! $.isArray( dataSort ) ) {
init.aDataSort = [ dataSort ];
* Browser feature detection for capabilities, quirks
* @param {object} settings dataTables settings object
* @memberof DataTable#oApi
function _fnBrowserDetect( settings )
// We don't need to do this every time DataTables is constructed, the values
// calculated are specific to the browser and OS configuration which we
// don't expect to change between initialisations
if ( ! DataTable.__browser ) {
var browser = {};
DataTable.__browser = browser;
// Scrolling feature / quirks detection
var n = $('<div/>')
.css( {
position: 'fixed',
top: 0,
left: $(window).scrollLeft()*-1, // allow for scrolling
height: 1,
width: 1,
overflow: 'hidden'
.append(
$('<div/>')
.css( {
position: 'absolute',
top: 1,
left: 1,
width: 100,
overflow: 'scroll'
.append(
$('<div/>')
.css( {
width: '100%',
height: 10
.appendTo( 'body' );
var outer = n.children();
var inner = outer.children();
// Numbers below, in order, are:
// inner.offsetWidth, inner.clientWidth, outer.offsetWidth, outer.clientWidth
// IE6 XP: 100 100 100 83
// IE7 Vista: 100 100 100 83
// IE 8+ Windows: 83 83 100 83
// Evergreen Windows: 83 83 100 83
// Evergreen Mac with scrollbars: 85 85 100 85
// Evergreen Mac without scrollbars: 100 100 100 100
// Get scrollbar width
browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth;
// IE6/7 will oversize a width 100% element inside a scrolling element, to
// include the width of the scrollbar, while other browsers ensure the inner
// element is contained without forcing scrolling
browser.bScrollOversize = inner[0].offsetWidth === 100 && outer[0].clientWidth !== 100;
// In rtl text layout, some browsers (most, but not all) will place the
// scrollbar on the left, rather than the right.
browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1;
// IE8- don't provide height and width for getBoundingClientRect
browser.bBounding = n[0].getBoundingClientRect().width ? true : false;
n.remove();
$.extend( settings.oBrowser, DataTable.__browser );
settings.oScroll.iBarWidth = DataTable.__browser.barWidth;
* Array.prototype reduce[Right] method, used for browsers which don't support
* JS 1.6. Done this way to reduce code size, since we iterate either way
* @param {object} settings dataTables settings object
* @memberof DataTable#oApi
function _fnReduce ( that, fn, init, start, end, inc )
i = start,
value,
isSet = false;
if ( init !== undefined ) {
value = init;
isSet = true;
while ( i !== end ) {
if ( ! that.hasOwnProperty(i) ) {
continue;
value = isSet ?
fn( value, that[i], i, that ) :
that[i];
isSet = true;
i += inc;
return value;
* Add a column to the list used for the table with default values
* @param {object} oSettings dataTables settings object
* @param {node} nTh The th element for this column
* @memberof DataTable#oApi
function _fnAddColumn( oSettings, nTh )
// Add column to aoColumns array
var oDefaults = DataTable.defaults.column;
var iCol = oSettings.aoColumns.length;
var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, {
"nTh": nTh ? nTh : document.createElement('th'),
"sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '',
"aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol],
"mData": oDefaults.mData ? oDefaults.mData : iCol,
idx: iCol
oSettings.aoColumns.push( oCol );
// Add search object for column specific search. Note that the `searchCols[ iCol ]`
// passed into extend can be undefined. This allows the user to give a default
// with only some of the parameters defined, and also not give a default
var searchCols = oSettings.aoPreSearchCols;
searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] );
// Use the default column options function to initialise classes etc
_fnColumnOptions( oSettings, iCol, $(nTh).data() );
* Apply options for a column
* @param {object} oSettings dataTables settings object
* @param {int} iCol column index to consider
* @param {object} oOptions object with sType, bVisible and bSearchable etc
* @memberof DataTable#oApi
function _fnColumnOptions( oSettings, iCol, oOptions )
var oCol = oSettings.aoColumns[ iCol ];
var oClasses = oSettings.oClasses;
var th = $(oCol.nTh);
// Try to get width information from the DOM. We can't get it from CSS
// as we'd need to parse the CSS stylesheet. `width` option can override
if ( ! oCol.sWidthOrig ) {
// Width attribute
oCol.sWidthOrig = th.attr('width') || null;
// Style attribute
var t = (th.attr('style') || '').match(/width:\s*(\d+[pxem%]+)/);
if ( t ) {
oCol.sWidthOrig = t[1];
/* User specified column options */
if ( oOptions !== undefined && oOptions !== null )
// Backwards compatibility
_fnCompatCols( oOptions );
// Map camel case parameters to their Hungarian counterparts
_fnCamelToHungarian( DataTable.defaults.column, oOptions, true );
/* Backwards compatibility for mDataProp */
if ( oOptions.mDataProp !== undefined && !oOptions.mData )
oOptions.mData = oOptions.mDataProp;
if ( oOptions.sType )
oCol._sManualType = oOptions.sType;
// `class` is a reserved word in Javascript, so we need to provide
// the ability to use a valid name for the camel case input
if ( oOptions.className && ! oOptions.sClass )
oOptions.sClass = oOptions.className;
if ( oOptions.sClass ) {
th.addClass( oOptions.sClass );
$.extend( oCol, oOptions );
_fnMap( oCol, oOptions, "sWidth", "sWidthOrig" );
/* iDataSort to be applied (backwards compatibility), but aDataSort will take
* priority if defined
if ( oOptions.iDataSort !== undefined )
oCol.aDataSort = [ oOptions.iDataSort ];
_fnMap( oCol, oOptions, "aDataSort" );
/* Cache the data get and set functions for speed */
var mDataSrc = oCol.mData;
var mData = _fnGetObjectDataFn( mDataSrc );
var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null;
var attrTest = function( src ) {
return typeof src === 'string' && src.indexOf('@') !== -1;
oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && (
attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter)
oCol._setter = null;
oCol.fnGetData = function (rowData, type, meta) {
var innerData = mData( rowData, type, undefined, meta );
return mRender && type ?
mRender( innerData, type, rowData, meta ) :
innerData;
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
/* Check that the class assignment is correct for sorting */
var bAsc = $.inArray('asc', oCol.asSorting) !== -1;
var bDesc = $.inArray('desc', oCol.asSorting) !== -1;
if ( !oCol.bSortable || (!bAsc && !bDesc) )
oCol.sSortingClass = oClasses.sSortableNone;
oCol.sSortingClassJUI = "";
else if ( bAsc && !bDesc )
oCol.sSortingClass = oClasses.sSortableAsc;
oCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed;
else if ( !bAsc && bDesc )
oCol.sSortingClass = oClasses.sSortableDesc;
oCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed;
oCol.sSortingClass = oClasses.sSortable;
oCol.sSortingClassJUI = oClasses.sSortJUI;
* Adjust the table column widths for new data. Note: you would probably want to
* do a redraw after calling this function!
* @param {object} settings dataTables settings object
* @memberof DataTable#oApi
function _fnAdjustColumnSizing ( settings )
/* Not interested in doing column width calculation if auto-width is disabled */
if ( settings.oFeatures.bAutoWidth !== false )
var columns = settings.aoColumns;
_fnCalculateColumnWidths( settings );
for ( var i=0 , iLen=columns.length ; i<iLen ; i++ )
columns[i].nTh.style.width = columns[i].sWidth;
var scroll = settings.oScroll;
if ( scroll.sY !== '' || scroll.sX !== '')
_fnScrollDraw( settings );
_fnCallbackFire( settings, null, 'column-sizing', [settings] );
* Covert the index of a visible column to the index in the data array (take account
* of hidden columns)
* @param {object} oSettings dataTables settings object
* @param {int} iMatch Visible column index to lookup
* @returns {int} i the data index
* @memberof DataTable#oApi
function _fnVisibleToColumnIndex( oSettings, iMatch )
var aiVis = _fnGetColumns( oSettings, 'bVisible' );
return typeof aiVis[iMatch] === 'number' ?
aiVis[iMatch] :
* Covert the index of an index in the data array and convert it to the visible
* column index (take account of hidden columns)
* @param {int} iMatch Column index to lookup
* @param {object} oSettings dataTables settings object
* @returns {int} i the data index
* @memberof DataTable#oApi
function _fnColumnIndexToVisible( oSettings, iMatch )
var aiVis = _fnGetColumns( oSettings, 'bVisible' );
var iPos = $.inArray( iMatch, aiVis );
return iPos !== -1 ? iPos : null;
* Get the number of visible columns
* @param {object} oSettings dataTables settings object
* @returns {int} i the number of visible columns
* @memberof DataTable#oApi
function _fnVisbleColumns( oSettings )
var vis = 0;
// No reduce in IE8, use a loop for now
$.each( oSettings.aoColumns, function ( i, col ) {
if ( col.bVisible && $(col.nTh).css('display') !== 'none' ) {
vis++;
return vis;
* Get an array of column indexes that match a given property
* @param {object} oSettings dataTables settings object
* @param {string} sParam Parameter in aoColumns to look for - typically
* bVisible or bSearchable
* @returns {array} Array of indexes with matched properties
* @memberof DataTable#oApi
function _fnGetColumns( oSettings, sParam )
var a = [];
$.map( oSettings.aoColumns, function(val, i) {
if ( val[sParam] ) {
a.push( i );
return a;
* Calculate the 'type' of a column
* @param {object} settings dataTables settings object
* @memberof DataTable#oApi
function _fnColumnTypes ( settings )
var columns = settings.aoColumns;
var data = settings.aoData;
var types = DataTable.ext.type.detect;
var i, ien, j, jen, k, ken;
var col, cell, detectedType, cache;
// For each column, spin over the
for ( i=0, ien=columns.length ; i<ien ; i++ ) {
col = columns[i];
cache = [];
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], settings );
// 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 ) {
col.sType = detectedType;
break;
// Fall back - if no type was detected, always use string
if ( ! col.sType ) {
col.sType = 'string';
* Take the column definitions and static columns arrays and calculate how
* they relate to column indexes. The callback function will then apply the
* definition found for a column to a suitable configuration object.
* @param {object} oSettings dataTables settings object
* @param {array} aoColDefs The aoColumnDefs array that is to be applied
* @param {array} aoCols The aoColumns array that defines columns individually
* @param {function} fn Callback function - takes two parameters, the calculated
* column index and the definition for that column.
* @memberof DataTable#oApi
function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, fn )
var i, iLen, j, jLen, k, kLen, def;
var columns = oSettings.aoColumns;
// Column definitions with aTargets
if ( aoColDefs )
/* Loop over the definitions array - loop in reverse so first instance has priority */
for ( i=aoColDefs.length-1 ; i>=0 ; i-- )
def = aoColDefs[i];
/* Each definition can target multiple columns, as it is an array */
var aTargets = def.targets !== undefined ?
def.targets :
def.aTargets;
if ( ! $.isArray( aTargets ) )
aTargets = [ aTargets ];
for ( j=0, jLen=aTargets.length ; j<jLen ; j++ )
if ( typeof aTargets[j] === 'number' && aTargets[j] >= 0 )
/* Add columns that we don't yet know about */
while( columns.length <= aTargets[j] )
_fnAddColumn( oSettings );
/* Integer, basic index */
fn( aTargets[j], def );
else if ( typeof aTargets[j] === 'number' && aTargets[j] < 0 )
/* Negative integer, right to left column counting */
fn( columns.length+aTargets[j], def );
else if ( typeof aTargets[j] === 'string' )
/* Class name matching on TH element */
for ( k=0, kLen=columns.length ; k<kLen ; k++ )
if ( aTargets[j] == "_all" ||
$(columns[k].nTh).hasClass( aTargets[j] ) )
fn( k, def );
// Statically defined columns array
if ( aoCols )
for ( i=0, iLen=aoCols.length ; i<iLen ; i++ )
fn( i, aoCols[i] );
* Add a data array to the table, creating DOM node etc. This is the parallel to
* _fnGatherData, but for adding rows from a Javascript source, rather than a
* DOM source.
* @param {object} oSettings dataTables settings object
* @param {array} aData data array to be added
* @param {node} [nTr] TR element to add to the table - optional. If not given,
* DataTables will create a row automatically
* @param {array} [anTds] Array of TD|TH elements for the row - must be given
* if nTr is.
* @returns {int} >=0 if successful (index of new aoData entry), -1 if failed
* @memberof DataTable#oApi
function _fnAddData ( oSettings, aDataIn, nTr, anTds )
/* Create the object for storing information about this new row */
var iRow = oSettings.aoData.length;
var oData = $.extend( true, {}, DataTable.models.oRow, {
src: nTr ? 'dom' : 'data',
idx: iRow
oData._aData = aDataIn;
oSettings.aoData.push( oData );
/* Create the cells */
var nTd, sThisType;
var columns = oSettings.aoColumns;
// Invalidate the column types as the new data needs to be revalidated
for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
columns[i].sType = null;
/* Add to the display array */
oSettings.aiDisplayMaster.push( iRow );
var id = oSettings.rowIdFn( aDataIn );
if ( id !== undefined ) {
oSettings.aIds[ id ] = oData;
/* Create the DOM information, or register it if already present */
if ( nTr || ! oSettings.oFeatures.bDeferRender )
_fnCreateTr( oSettings, iRow, nTr, anTds );
return iRow;
* Add one or more TR elements to the table. Generally we'd expect to
* use this for reading data from a DOM sourced table, but it could be
* used for an TR element. Note that if a TR is given, it is used (i.e.
* it is not cloned).
* @param {object} settings dataTables settings object
* @param {array|node|jQuery} trs The TR element(s) to add to the table
* @returns {array} Array of indexes for the added rows
* @memberof DataTable#oApi
function _fnAddTr( settings, trs )
var row;
// Allow an individual node to be passed in
if ( ! (trs instanceof $) ) {
trs = $(trs);
return trs.map( function (i, el) {
row = _fnGetRowElements( settings, el );
return _fnAddData( settings, row.data, el, row.cells );
* Take a TR element and convert it to an index in aoData
* @param {object} oSettings dataTables settings object
* @param {node} n the TR element to find
* @returns {int} index if the node is found, null if not
* @memberof DataTable#oApi
function _fnNodeToDataIndex( oSettings, n )
return (n._DT_RowIndex!==undefined) ? n._DT_RowIndex : null;
* Take a TD element and convert it into a column data index (not the visible index)
* @param {object} oSettings dataTables settings object
* @param {int} iRow The row number the TD/TH can be found in
* @param {node} n The TD/TH element to find
* @returns {int} index if the node is found, -1 if not
* @memberof DataTable#oApi
function _fnNodeToColumnIndex( oSettings, iRow, n )
return $.inArray( n, oSettings.aoData[ iRow ].anCells );
* Get the data for a given cell from the internal cache, taking into account data mapping
* @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( settings, rowIdx, colIdx, type )
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 ( cellData === undefined ) {
if ( settings.iDrawError != draw && defaultContent === null ) {
_fnLog( settings, 0, "Requested unknown parameter "+
(typeof col.mData=='function' ? '{function}' : "'"+col.mData+"'")+
" for row "+rowIdx+", column "+colIdx, 4 );
settings.iDrawError = draw;
return defaultContent;
// When the data source is null and a specific data type is requested (i.e.
// not the original data), we can use default column data
if ( (cellData === rowData || cellData === null) && defaultContent !== null && type !== undefined ) {
cellData = defaultContent;
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 ( cellData === null && type == 'display' ) {
return '';
return cellData;
* Set the value for a specific cell, into the internal data cache
* @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( settings, rowIdx, colIdx, val )
var col = settings.aoColumns[colIdx];
var rowData = settings.aoData[rowIdx]._aData;
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 = /\[.*?\]$/;
var __reFn = /\(\)$/;
* Split string on periods, taking into account escaped periods
* @param {string} str String to split
* @return {array} Split string
function _fnSplitObjNotation( str )
return $.map( str.match(/(\\.|[^\.])+/g) || [''], function ( s ) {
return s.replace(/\\\./g, '.');
* Return a function that can be used to get data from a source object, taking
* into account the ability to use nested objects as a source
* @param {string|int|function} mSource The data source for the object
* @returns {function} Data get function
* @memberof DataTable#oApi
function _fnGetObjectDataFn( mSource )
if ( $.isPlainObject( mSource ) )
/* Build an object of get functions, and wrap them in a single call */
var o = {};
$.each( mSource, function (key, val) {
if ( val ) {
o[key] = _fnGetObjectDataFn( val );
return function (data, type, row, meta) {
var t = o[type] || o._;
return t !== undefined ?
t(data, type, row, meta) :
else if ( mSource === null )
/* Give an empty string for rendering / sorting etc */
return function (data) { // type, row and meta also passed, but not used
return data;
else if ( typeof mSource === 'function' )
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) )
/* If there is a . in the source string then the data source is in a
* nested object so we loop over the data for each level to get the next
* level down. On each loop we test for undefined, and if found immediately
* return. This allows entire objects to be missing and sDefaultContent to
* be used if defined, rather than throwing an error
var fetchData = function (data, type, src) {
var arrayNotation, funcNotation, out, innerSrc;
if ( src !== "" )
var a = _fnSplitObjNotation( src );
for ( var i=0, iLen=a.length ; i<iLen ; i++ )
// Check if we are dealing with special notation
arrayNotation = a[i].match(__reArray);
funcNotation = a[i].match(__reFn);
if ( arrayNotation )
// Array notation
a[i] = a[i].replace(__reArray, '');
// Condition allows simply [] to be passed in
if ( a[i] !== "" ) {
data = data[ a[i] ];
out = [];
// Get the remainder of the nested object to get
a.splice( 0, i+1 );
innerSrc = a.join('.');
// Traverse each entry in the array getting the properties requested
if ( $.isArray( data ) ) {
for ( var j=0, jLen=data.length ; j<jLen ; j++ ) {
out.push( fetchData( data[j], type, innerSrc ) );
// If a string is given in between the array notation indicators, that
// is used to join the strings together, otherwise an array is returned
var join = arrayNotation[0].substring(1, arrayNotation[0].length-1);
data = (join==="") ? out : out.join(join);
// The inner call to fetchData has already traversed through the remainder
// of the source requested, so we exit from the loop
break;
else if ( funcNotation )
// Function call
a[i] = a[i].replace(__reFn, '');
data = data[ a[i] ]();
continue;
if ( data === null || data[ a[i] ] === undefined )
return undefined;
data = data[ a[i] ];
return data;
return function (data, type) { // row and meta also passed, but not used
return fetchData( data, type, mSource );
/* Array or flat object mapping */
return function (data, type) { // row and meta also passed, but not used
return data[mSource];
* Return a function that can be used to set data from a source object, taking
* into account the ability to use nested objects as a source
* @param {string|int|function} mSource The data source for the object
* @returns {function} Data set function
* @memberof DataTable#oApi
function _fnSetObjectDataFn( mSource )
if ( $.isPlainObject( mSource ) )
/* Unlike get, only the underscore (global) option is used for for
* setting data since we don't know the type here. This is why an object
* option is not documented for `mData` (which is read/write), but it is
* for `mRender` which is read only.
return _fnSetObjectDataFn( mSource._ );
else if ( mSource === null )
/* Nothing to do when the data source is null */
return function () {};
else if ( typeof mSource === 'function' )
return function (data, val, meta) {
mSource( data, 'set', val, meta );
else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) )
/* Like the get, we need to get data from a nested object */
var setData = function (data, val, src) {
var a = _fnSplitObjNotation( src ), b;
var aLast = a[a.length-1];
var arrayNotation, funcNotation, o, innerSrc;
for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ )
// Check if we are dealing with an array notation request
arrayNotation = a[i].match(__reArray);
funcNotation = a[i].match(__reFn);
if ( arrayNotation )
a[i] = a[i].replace(__reArray, '');
data[ a[i] ] = [];
// Get the remainder of the nested object to set so we can recurse
b = a.slice();
b.splice( 0, i+1 );
innerSrc = b.join('.');
// Traverse each entry in the array setting the properties requested
if ( $.isArray( val ) )
for ( var j=0, jLen=val.length ; j<jLen ; j++ )
o = {};
setData( o, val[j], innerSrc );
data[ a[i] ].push( o );
// We've been asked to save data to an array, but it
// isn't array data to be saved. Best that can be done
// is to just save the value.
data[ a[i] ] = val;
// The inner call to setData has already traversed through the remainder
// of the source and has set the data, thus we can exit here
return;
else if ( funcNotation )
// Function call
a[i] = a[i].replace(__reFn, '');
data = data[ a[i] ]( val );
// If the nested object doesn't currently exist - since we are
// trying to set the value - create it
if ( data[ a[i] ] === null || data[ a[i] ] === undefined )
data[ a[i] ] = {};
data = data[ a[i] ];
// Last item in the input - i.e, the actual set
if ( aLast.match(__reFn ) )
// Function call
data = data[ aLast.replace(__reFn, '') ]( val );
// If array notation is used, we just want to strip it and use the property name
// 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) { // meta is also passed in, but not used
return setData( data, val, mSource );
/* Array or flat object mapping */
return function (data, val) { // meta is also passed in, but not used
data[mSource] = val;
* Return an array with the full table data
* @param {object} oSettings dataTables settings object
* @returns array {array} aData Master data array
* @memberof DataTable#oApi
function _fnGetDataMaster ( settings )
return _pluck( settings.aoData, '_aData' );
* Nuke the table
* @param {object} oSettings dataTables settings object
* @memberof DataTable#oApi
function _fnClearTable( settings )
settings.aoData.length = 0;
settings.aiDisplayMaster.length = 0;
settings.aiDisplay.length = 0;
settings.aIds = {};
* Take an array of integers (index array) and remove a target integer (value - not
* the key!)
* @param {array} a Index array to target
* @param {int} iTarget value to find
* @memberof DataTable#oApi
function _fnDeleteIndex( a, iTarget, splice )
var iTargetIndex = -1;
for ( var i=0, iLen=a.length ; i<iLen ; i++ )
if ( a[i] == iTarget )
iTargetIndex = i;
else if ( a[i] > iTarget )
a[i]--;
if ( iTargetIndex != -1 && splice === undefined )
a.splice( iTargetIndex, 1 );
* 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 {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 _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, colIdx, colIdx === undefined ? undefined : row._aData
.data;
else {
// Reading from data object, update the DOM
var cells = row.anCells;
if ( cells ) {
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 ( 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( settings, 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, colIdx, d )
tds = [],
td = row.firstChild,
name, col, o, i=0, contents,
columns = settings.aoColumns,
objectRead = settings._rowReadObject;
// Allow the data object to be passed in, or construct
d = d !== undefined ?
objectRead ?
var attr = function ( str, td ) {
if ( typeof str === 'string' ) {
var idx = str.indexOf('@');
if ( idx !== -1 ) {
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 ) {
if ( colIdx === undefined || colIdx === i ) {
col = columns[i];
contents = $.trim(cell.innerHTML);
if ( col && col._bAttrSrc ) {
var setter = _fnSetObjectDataFn( col.mData._ );
setter( d, contents );
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;
if ( td ) {
// `tr` element was passed in
while ( td ) {
name = td.nodeName.toUpperCase();
if ( name == "TD" || name == "TH" ) {
cellProcess( td );
tds.push( td );
td = td.nextSibling;
else {
// Existing row object passed in
tds = row.anCells;
for ( var j=0, jen=tds.length ; j<jen ; j++ ) {
cellProcess( tds[j] );
// Read the ID from the DOM if present
var rowNode = row.firstChild ? row : row.nTr;
if ( rowNode ) {
var id = rowNode.getAttribute( 'id' );
if ( id ) {
_fnSetObjectDataFn( settings.rowId )( d, id );
return {
data: d,
cells: tds
* Create a new TR element (and it's TD children) for a row
* @param {object} oSettings dataTables settings object
* @param {int} iRow Row to consider
* @param {node} [nTrIn] TR element to add to the table - optional. If not given,
* DataTables will create a row automatically
* @param {array} [anTds] Array of TD|TH elements for the row - must be given
* if nTr is.
* @memberof DataTable#oApi
function _fnCreateTr ( oSettings, iRow, nTrIn, anTds )
row = oSettings.aoData[iRow],
rowData = row._aData,
cells = [],
nTr, nTd, oCol,
i, iLen, create;
if ( row.nTr === null )
nTr = nTrIn || document.createElement('tr');
row.nTr = nTr;
row.anCells = cells;
/* Use a private property on the node to allow reserve mapping from the node
* to the aoData array for fast look up
nTr._DT_RowIndex = iRow;
/* Special parameters can be given by the data source to be used on the row */
_fnRowAttributes( oSettings, row );
/* Process each column */
for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
oCol = oSettings.aoColumns[i];
create = nTrIn ? false : true;
nTd = create ? document.createElement( oCol.sCellType ) : anTds[i];
nTd._DT_CellIndex = {
row: iRow,
column: i
cells.push( nTd );
// Need to create the HTML if new, or if a rendering function is defined
if ( create || ((!nTrIn || oCol.mRender || oCol.mData !== i) &&
(!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+'.display')
nTd.innerHTML = _fnGetCellData( oSettings, iRow, i, 'display' );
/* Add user defined class */
if ( oCol.sClass )
nTd.className += ' '+oCol.sClass;
// Visibility - add or remove as required
if ( oCol.bVisible && ! nTrIn )
nTr.appendChild( nTd );
else if ( ! oCol.bVisible && nTrIn )
nTd.parentNode.removeChild( nTd );
if ( oCol.fnCreatedCell )
oCol.fnCreatedCell.call( oSettings.oInstance,
nTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i
_fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [nTr, rowData, iRow, cells] );
// Remove once webkit bug 131819 and Chromium bug 365619 have been resolved
// and deployed
row.nTr.setAttribute( 'role', 'row' );
* Add attributes to a row based on the special `DT_*` parameters in a data
* source object.
* @param {object} settings DataTables settings object
* @param {object} DataTables row object for the row to be modified
* @memberof DataTable#oApi
function _fnRowAttributes( settings, row )
var tr = row.nTr;
var data = row._aData;
if ( tr ) {
var id = settings.rowIdFn( data );
if ( id ) {
tr.id = id;
if ( data.DT_RowClass ) {
// Remove any classes added by DT_RowClass before
var a = data.DT_RowClass.split(' ');
row.__rowc = row.__rowc ?
_unique( row.__rowc.concat( a ) ) :
.removeClass( row.__rowc.join(' ') )
.addClass( data.DT_RowClass );
if ( data.DT_RowAttr ) {
$(tr).attr( data.DT_RowAttr );
if ( data.DT_RowData ) {
$(tr).data( data.DT_RowData );
* Create the HTML header for the table
* @param {object} oSettings dataTables settings object
* @memberof DataTable#oApi
function _fnBuildHead( oSettings )
var i, ien, cell, row, column;
var thead = oSettings.nTHead;
var tfoot = oSettings.nTFoot;
var createHeader = $('th, td', thead).length === 0;
var classes = oSettings.oClasses;
var columns = oSettings.aoColumns;
if ( createHeader ) {
row = $('<tr/>').appendTo( thead );
for ( i=0, ien=columns.length ; i<ien ; i++ ) {
column = columns[i];
cell = $( column.nTh ).addClass( column.sClass );
if ( createHeader ) {
cell.appendTo( row );
// 1.11 move into sorting
if ( oSettings.oFeatures.bSort ) {
cell.addClass( column.sSortingClass );
if ( column.bSortable !== false ) {
.attr( 'tabindex', oSettings.iTabIndex )
.attr( 'aria-controls', oSettings.sTableId );
_fnSortAttachListener( oSettings, column.nTh, i );
if ( column.sTitle != cell[0].innerHTML ) {
cell.html( column.sTitle );
_fnRenderer( oSettings, 'header' )(
oSettings, cell, column, classes
if ( createHeader ) {
_fnDetectHeader( oSettings.aoHeader, thead );
/* ARIA role for the rows */
$(thead).find('>tr').attr('role', 'row');
/* Deal with the footer - add classes if required */
$(thead).find('>tr>th, >tr>td').addClass( classes.sHeaderTH );