27 (
function( factory ) {
30 if ( typeof define ===
'function' && define.amd ) {
32 define( [
'jquery'],
function ( $ ) {
33 return factory( $, window, document );
36 else if ( typeof exports ===
'object' ) {
38 module.exports =
function (root, $) {
46 $ = typeof window !==
'undefined' ?
48 require(
'jquery')( root );
51 return factory( $, root, root.document );
56 factory( jQuery, window, document );
59 (
function( $, window, document, undefined ) {
116 var _api_registerPlural;
119 var _re_new_lines = /[\r\n]/g;
120 var _re_html = /<.*?>/g;
121 var _re_date_start = /^[\w\+\-]/;
122 var _re_date_end = /[\w\+\-]$/;
125 var _re_escape_regex =
new RegExp(
'(\\' + [
'/',
'.',
'*',
'+',
'?',
'|',
'(',
')',
'[',
']',
'{',
'}',
'\\',
'$',
'^',
'-' ].join(
'|\\') +
')',
'g' );
137 var _re_formatted_numeric = /[
',$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfk]/gi;
140 var _empty = function ( d ) {
141 return !d || d === true || d === '-
' ? true : false;
145 var _intVal = function ( s ) {
146 var integer = parseInt( s, 10 );
147 return !isNaN(integer) && isFinite(s) ? integer : null;
150 // Convert from a formatted number with characters other than `.` as the
151 // decimal place, to a Javascript number
152 var _numToDecimal = function ( num, decimalPoint ) {
153 // Cache created regular expressions for speed as this function is called often
154 if ( ! _re_dic[ decimalPoint ] ) {
155 _re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g
' );
157 return typeof num === 'string' && decimalPoint !== '.
' ?
158 num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.
' ) :
163 var _isNumber = function ( d, decimalPoint, formatted ) {
164 var strType = typeof d === 'string';
166 // If empty return immediately so there must be a number if it is a
167 // formatted string (this stops the string "k", or "kr", etc being detected
168 // as a formatted number for currency
173 if ( decimalPoint && strType ) {
174 d = _numToDecimal( d, decimalPoint );
177 if ( formatted && strType ) {
178 d = d.replace( _re_formatted_numeric, '' );
181 return !isNaN( parseFloat(d) ) && isFinite( d );
185 // A string without HTML in it can be considered to be HTML still
186 var _isHtml = function ( d ) {
187 return _empty( d ) || typeof d === 'string';
191 var _htmlNumeric = function ( d, decimalPoint, formatted ) {
196 var html = _isHtml( d );
199 _isNumber( _stripHtml( d ), decimalPoint, formatted ) ?
205 var _pluck = function ( a, prop, prop2 ) {
207 var i=0, ien=a.length;
209 // Could have the test in the loop for slightly smaller code, but speed
211 if ( prop2 !== undefined ) {
212 for ( ; i<ien ; i++ ) {
213 if ( a[i] && a[i][ prop ] ) {
214 out.push( a[i][ prop ][ prop2 ] );
219 for ( ; i<ien ; i++ ) {
221 out.push( a[i][ prop ] );
230 // Basically the same as _pluck, but rather than looping over `a` we use `order`
231 // as the indexes to pick from `a`
232 var _pluck_order = function ( a, order, prop, prop2 )
235 var i=0, ien=order.length;
237 // Could have the test in the loop for slightly smaller code, but speed
239 if ( prop2 !== undefined ) {
240 for ( ; i<ien ; i++ ) {
241 if ( a[ order[i] ][ prop ] ) {
242 out.push( a[ order[i] ][ prop ][ prop2 ] );
247 for ( ; i<ien ; i++ ) {
248 out.push( a[ order[i] ][ prop ] );
256 var _range = function ( len, start )
261 if ( start === undefined ) {
270 for ( var i=start ; i<end ; i++ ) {
278 var _removeEmpty = function ( a )
282 for ( var i=0, ien=a.length ; i<ien ; i++ ) {
283 if ( a[i] ) { // careful - will remove all falsy values!
292 var _stripHtml = function ( d ) {
293 return d.replace( _re_html, '' );
304 var _unique = function ( src )
306 // A faster unique method is to use object keys to identify used values,
307 // but this doesn't work with arrays or objects, which we must also
316 again:
for ( i=0 ; i<ien ; i++ ) {
319 for ( j=0 ; j<k ; j++ ) {
320 if ( out[j] === val ) {
341 function _fnHungarianMap ( o )
344 hungarian =
'a aa ai ao as b fn i m o s ',
349 $.each( o,
function (key, val) {
350 match = key.match(/^([^A-Z]+?)([A-Z])/);
352 if ( match && hungarian.indexOf(match[1]+
' ') !== -1 )
354 newKey = key.replace( match[0], match[2].toLowerCase() );
357 if ( match[1] ===
'o' )
359 _fnHungarianMap( o[key] );
364 o._hungarianMap = map;
379 function _fnCamelToHungarian ( src, user, force )
381 if ( ! src._hungarianMap ) {
382 _fnHungarianMap( src );
387 $.each( user,
function (key, val) {
388 hungarianKey = src._hungarianMap[ key ];
390 if ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) )
393 if ( hungarianKey.charAt(0) ===
'o' )
396 if ( ! user[ hungarianKey ] ) {
397 user[ hungarianKey ] = {};
399 $.extend(
true, user[hungarianKey], user[key] );
401 _fnCamelToHungarian( src[hungarianKey], user[hungarianKey], force );
404 user[hungarianKey] = user[ key ];
418 function _fnLanguageCompat( lang )
420 var defaults = DataTable.defaults.oLanguage;
421 var zeroRecords = lang.sZeroRecords;
426 if ( ! lang.sEmptyTable && zeroRecords &&
427 defaults.sEmptyTable ===
"No data available in table" )
429 _fnMap( lang, lang,
'sZeroRecords',
'sEmptyTable' );
433 if ( ! lang.sLoadingRecords && zeroRecords &&
434 defaults.sLoadingRecords ===
"Loading..." )
436 _fnMap( lang, lang,
'sZeroRecords',
'sLoadingRecords' );
440 if ( lang.sInfoThousands ) {
441 lang.sThousands = lang.sInfoThousands;
444 var decimal = lang.sDecimal;
446 _addNumericSort( decimal );
457 var _fnCompatMap =
function ( o, knew, old ) {
458 if ( o[ knew ] !== undefined ) {
459 o[ old ] = o[ knew ];
470 function _fnCompatOpts ( init )
472 _fnCompatMap( init,
'ordering',
'bSort' );
473 _fnCompatMap( init,
'orderMulti',
'bSortMulti' );
474 _fnCompatMap( init,
'orderClasses',
'bSortClasses' );
475 _fnCompatMap( init,
'orderCellsTop',
'bSortCellsTop' );
476 _fnCompatMap( init,
'order',
'aaSorting' );
477 _fnCompatMap( init,
'orderFixed',
'aaSortingFixed' );
478 _fnCompatMap( init,
'paging',
'bPaginate' );
479 _fnCompatMap( init,
'pagingType',
'sPaginationType' );
480 _fnCompatMap( init,
'pageLength',
'iDisplayLength' );
481 _fnCompatMap( init,
'searching',
'bFilter' );
484 if ( typeof init.sScrollX ===
'boolean' ) {
485 init.sScrollX = init.sScrollX ?
'100%' :
'';
487 if ( typeof init.scrollX ===
'boolean' ) {
488 init.scrollX = init.scrollX ?
'100%' :
'';
493 var searchCols = init.aoSearchCols;
496 for ( var i=0, ien=searchCols.length ; i<ien ; i++ ) {
497 if ( searchCols[i] ) {
498 _fnCamelToHungarian( DataTable.models.oSearch, searchCols[i] );
511 function _fnCompatCols ( init )
513 _fnCompatMap( init,
'orderable',
'bSortable' );
514 _fnCompatMap( init,
'orderData',
'aDataSort' );
515 _fnCompatMap( init,
'orderSequence',
'asSorting' );
516 _fnCompatMap( init,
'orderDataType',
'sortDataType' );
519 var dataSort = init.aDataSort;
520 if ( dataSort && ! $.isArray( dataSort ) ) {
521 init.aDataSort = [ dataSort ];
531 function _fnBrowserDetect( settings )
536 if ( ! DataTable.__browser ) {
538 DataTable.__browser = browser;
553 position:
'absolute',
569 var outer = n.children();
570 var inner = outer.children();
583 browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth;
588 browser.bScrollOversize = inner[0].offsetWidth === 100 && outer[0].clientWidth !== 100;
592 browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1;
595 browser.bBounding = n[0].getBoundingClientRect().width ?
true :
false;
600 $.extend( settings.oBrowser, DataTable.__browser );
601 settings.oScroll.iBarWidth = DataTable.__browser.barWidth;
611 function _fnReduce ( that, fn, init, start, end, inc )
618 if ( init !== undefined ) {
623 while ( i !== end ) {
624 if ( ! that.hasOwnProperty(i) ) {
629 fn( value, that[i], i, that ) :
645 function _fnAddColumn( oSettings, nTh )
648 var oDefaults = DataTable.defaults.column;
649 var iCol = oSettings.aoColumns.length;
650 var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, {
651 "nTh": nTh ? nTh : document.createElement(
'th'),
652 "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML :
'',
653 "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol],
654 "mData": oDefaults.mData ? oDefaults.mData : iCol,
657 oSettings.aoColumns.push( oCol );
662 var searchCols = oSettings.aoPreSearchCols;
663 searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] );
666 _fnColumnOptions( oSettings, iCol, $(nTh).data() );
677 function _fnColumnOptions( oSettings, iCol, oOptions )
679 var oCol = oSettings.aoColumns[ iCol ];
680 var oClasses = oSettings.oClasses;
681 var th = $(oCol.nTh);
685 if ( ! oCol.sWidthOrig ) {
687 oCol.sWidthOrig = th.attr(
'width') || null;
690 var t = (th.attr(
'style') ||
'').match(/width:\s*(\d+[pxem%]+)/);
692 oCol.sWidthOrig = t[1];
697 if ( oOptions !== undefined && oOptions !== null )
700 _fnCompatCols( oOptions );
703 _fnCamelToHungarian( DataTable.defaults.column, oOptions );
706 if ( oOptions.mDataProp !== undefined && !oOptions.mData )
708 oOptions.mData = oOptions.mDataProp;
711 if ( oOptions.sType )
713 oCol._sManualType = oOptions.sType;
718 if ( oOptions.className && ! oOptions.sClass )
720 oOptions.sClass = oOptions.className;
723 $.extend( oCol, oOptions );
724 _fnMap( oCol, oOptions,
"sWidth",
"sWidthOrig" );
729 if ( oOptions.iDataSort !== undefined )
731 oCol.aDataSort = [ oOptions.iDataSort ];
733 _fnMap( oCol, oOptions,
"aDataSort" );
737 var mDataSrc = oCol.mData;
738 var mData = _fnGetObjectDataFn( mDataSrc );
739 var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null;
741 var attrTest =
function( src ) {
742 return typeof src ===
'string' && src.indexOf(
'@') !== -1;
744 oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && (
745 attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter)
749 oCol.fnGetData =
function (rowData, type, meta) {
750 var innerData = mData( rowData, type, undefined, meta );
752 return mRender && type ?
753 mRender( innerData, type, rowData, meta ) :
756 oCol.fnSetData =
function ( rowData, val, meta ) {
757 return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta );
762 if ( typeof mDataSrc !==
'number' ) {
763 oSettings._rowReadObject =
true;
767 if ( !oSettings.oFeatures.bSort )
769 oCol.bSortable =
false;
770 th.addClass( oClasses.sSortableNone );
774 var bAsc = $.inArray(
'asc', oCol.asSorting) !== -1;
775 var bDesc = $.inArray(
'desc', oCol.asSorting) !== -1;
776 if ( !oCol.bSortable || (!bAsc && !bDesc) )
778 oCol.sSortingClass = oClasses.sSortableNone;
779 oCol.sSortingClassJUI =
"";
781 else if ( bAsc && !bDesc )
783 oCol.sSortingClass = oClasses.sSortableAsc;
784 oCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed;
786 else if ( !bAsc && bDesc )
788 oCol.sSortingClass = oClasses.sSortableDesc;
789 oCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed;
793 oCol.sSortingClass = oClasses.sSortable;
794 oCol.sSortingClassJUI = oClasses.sSortJUI;
805 function _fnAdjustColumnSizing ( settings )
808 if ( settings.oFeatures.bAutoWidth !==
false )
810 var columns = settings.aoColumns;
812 _fnCalculateColumnWidths( settings );
813 for ( var i=0 , iLen=columns.length ; i<iLen ; i++ )
815 columns[i].nTh.style.width = columns[i].sWidth;
819 var scroll = settings.oScroll;
820 if ( scroll.sY !==
'' || scroll.sX !==
'')
822 _fnScrollDraw( settings );
825 _fnCallbackFire( settings, null,
'column-sizing', [settings] );
837 function _fnVisibleToColumnIndex( oSettings, iMatch )
839 var aiVis = _fnGetColumns( oSettings,
'bVisible' );
841 return typeof aiVis[iMatch] ===
'number' ?
855 function _fnColumnIndexToVisible( oSettings, iMatch )
857 var aiVis = _fnGetColumns( oSettings,
'bVisible' );
858 var iPos = $.inArray( iMatch, aiVis );
860 return iPos !== -1 ? iPos : null;
870 function _fnVisbleColumns( oSettings )
875 $.each( oSettings.aoColumns, function ( i, col ) {
876 if ( col.bVisible && $(col.nTh).css(
'display') !==
'none' ) {
893 function _fnGetColumns( oSettings, sParam )
897 $.map( oSettings.aoColumns,
function(val, i) {
912 function _fnColumnTypes ( settings )
914 var columns = settings.aoColumns;
915 var data = settings.aoData;
916 var types = DataTable.ext.type.detect;
917 var i, ien, j, jen, k, ken;
918 var col, cell, detectedType, cache;
921 for ( i=0, ien=columns.length ; i<ien ; i++ ) {
925 if ( ! col.sType && col._sManualType ) {
926 col.sType = col._sManualType;
928 else if ( ! col.sType ) {
929 for ( j=0, jen=types.length ; j<jen ; j++ ) {
930 for ( k=0, ken=data.length ; k<ken ; k++ ) {
933 if ( cache[k] === undefined ) {
934 cache[k] = _fnGetCellData( settings, k, i,
'type' );
937 detectedType = types[j]( cache[k], settings );
944 if ( ! detectedType && j !== types.length-1 ) {
950 if ( detectedType ===
'html' ) {
957 if ( detectedType ) {
958 col.sType = detectedType;
965 col.sType =
'string';
983 function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, fn )
985 var i, iLen, j, jLen, k, kLen, def;
986 var columns = oSettings.aoColumns;
992 for ( i=aoColDefs.length-1 ; i>=0 ; i-- )
997 var aTargets = def.targets !== undefined ?
1001 if ( ! $.isArray( aTargets ) )
1003 aTargets = [ aTargets ];
1006 for ( j=0, jLen=aTargets.length ; j<jLen ; j++ )
1008 if ( typeof aTargets[j] ===
'number' && aTargets[j] >= 0 )
1011 while( columns.length <= aTargets[j] )
1013 _fnAddColumn( oSettings );
1017 fn( aTargets[j], def );
1019 else if ( typeof aTargets[j] ===
'number' && aTargets[j] < 0 )
1022 fn( columns.length+aTargets[j], def );
1024 else if ( typeof aTargets[j] ===
'string' )
1027 for ( k=0, kLen=columns.length ; k<kLen ; k++ )
1029 if ( aTargets[j] ==
"_all" ||
1030 $(columns[k].nTh).hasClass( aTargets[j] ) )
1043 for ( i=0, iLen=aoCols.length ; i<iLen ; i++ )
1063 function _fnAddData ( oSettings, aDataIn, nTr, anTds )
1066 var iRow = oSettings.aoData.length;
1067 var oData = $.extend(
true, {}, DataTable.models.oRow, {
1068 src: nTr ?
'dom' :
'data',
1072 oData._aData = aDataIn;
1073 oSettings.aoData.push( oData );
1077 var columns = oSettings.aoColumns;
1080 for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
1082 columns[i].sType = null;
1086 oSettings.aiDisplayMaster.push( iRow );
1088 var
id = oSettings.rowIdFn( aDataIn );
1089 if (
id !== undefined ) {
1090 oSettings.aIds[ id ] = oData;
1094 if ( nTr || ! oSettings.oFeatures.bDeferRender )
1096 _fnCreateTr( oSettings, iRow, nTr, anTds );
1113 function _fnAddTr( settings, trs )
1118 if ( ! (trs instanceof $) ) {
1122 return trs.map(
function (i, el) {
1123 row = _fnGetRowElements( settings, el );
1124 return _fnAddData( settings, row.data, el, row.cells );
1136 function _fnNodeToDataIndex( oSettings, n )
1138 return (n._DT_RowIndex!==undefined) ? n._DT_RowIndex : null;
1150 function _fnNodeToColumnIndex( oSettings, iRow, n )
1152 return $.inArray( n, oSettings.aoData[ iRow ].anCells );
1165 function _fnGetCellData( settings, rowIdx, colIdx, type )
1167 var draw = settings.iDraw;
1168 var col = settings.aoColumns[colIdx];
1169 var rowData = settings.aoData[rowIdx]._aData;
1170 var defaultContent = col.sDefaultContent;
1171 var cellData = col.fnGetData( rowData, type, {
1177 if ( cellData === undefined ) {
1178 if ( settings.iDrawError != draw && defaultContent === null ) {
1179 _fnLog( settings, 0,
"Requested unknown parameter "+
1180 (typeof col.mData==
'function' ?
'{function}' :
"'"+col.mData+
"'")+
1181 " for row "+rowIdx+
", column "+colIdx, 4 );
1182 settings.iDrawError = draw;
1184 return defaultContent;
1189 if ( (cellData === rowData || cellData === null) && defaultContent !== null && type !== undefined ) {
1190 cellData = defaultContent;
1192 else if ( typeof cellData ===
'function' ) {
1195 return cellData.call( rowData );
1198 if ( cellData === null && type ==
'display' ) {
1213 function _fnSetCellData( settings, rowIdx, colIdx, val )
1215 var col = settings.aoColumns[colIdx];
1216 var rowData = settings.aoData[rowIdx]._aData;
1218 col.fnSetData( rowData, val, {
1227 var __reArray = /\[.*?\]$/;
1228 var __reFn = /\(\)$/;
1235 function _fnSplitObjNotation( str )
1237 return $.map( str.match(/(\\.|[^\.])+/g) || [
''],
function ( s ) {
1238 return s.replace(/\\./g,
'.');
1250 function _fnGetObjectDataFn( mSource )
1252 if ( $.isPlainObject( mSource ) )
1256 $.each( mSource,
function (key, val) {
1258 o[key] = _fnGetObjectDataFn( val );
1262 return function (data, type, row, meta) {
1263 var t = o[type] || o._;
1264 return t !== undefined ?
1265 t(data, type, row, meta) :
1269 else if ( mSource === null )
1272 return function (data) {
1276 else if ( typeof mSource ===
'function' )
1278 return function (data, type, row, meta) {
1279 return mSource( data, type, row, meta );
1282 else if ( typeof mSource ===
'string' && (mSource.indexOf(
'.') !== -1 ||
1283 mSource.indexOf(
'[') !== -1 || mSource.indexOf(
'(') !== -1) )
1291 var fetchData =
function (data, type, src) {
1292 var arrayNotation, funcNotation, out, innerSrc;
1296 var a = _fnSplitObjNotation( src );
1298 for ( var i=0, iLen=a.length ; i<iLen ; i++ )
1301 arrayNotation = a[i].match(__reArray);
1302 funcNotation = a[i].match(__reFn);
1304 if ( arrayNotation )
1307 a[i] = a[i].replace(__reArray,
'');
1310 if ( a[i] !==
"" ) {
1311 data = data[ a[i] ];
1317 innerSrc = a.join(
'.');
1320 if ( $.isArray( data ) ) {
1321 for ( var j=0, jLen=data.length ; j<jLen ; j++ ) {
1322 out.push( fetchData( data[j], type, innerSrc ) );
1328 var join = arrayNotation[0].substring(1, arrayNotation[0].length-1);
1329 data = (join===
"") ? out : out.join(join);
1335 else if ( funcNotation )
1338 a[i] = a[i].replace(__reFn,
'');
1339 data = data[ a[i] ]();
1343 if ( data === null || data[ a[i] ] === undefined )
1347 data = data[ a[i] ];
1354 return function (data, type) {
1355 return fetchData( data, type, mSource );
1361 return function (data, type) {
1362 return data[mSource];
1375 function _fnSetObjectDataFn( mSource )
1377 if ( $.isPlainObject( mSource ) )
1384 return _fnSetObjectDataFn( mSource._ );
1386 else if ( mSource === null )
1389 return function () {};
1391 else if ( typeof mSource ===
'function' )
1393 return function (data, val, meta) {
1394 mSource( data,
'set', val, meta );
1397 else if ( typeof mSource ===
'string' && (mSource.indexOf(
'.') !== -1 ||
1398 mSource.indexOf(
'[') !== -1 || mSource.indexOf(
'(') !== -1) )
1401 var setData =
function (data, val, src) {
1402 var a = _fnSplitObjNotation( src ), b;
1403 var aLast = a[a.length-1];
1404 var arrayNotation, funcNotation, o, innerSrc;
1406 for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ )
1409 arrayNotation = a[i].match(__reArray);
1410 funcNotation = a[i].match(__reFn);
1412 if ( arrayNotation )
1414 a[i] = a[i].replace(__reArray,
'');
1420 innerSrc = b.join(
'.');
1423 if ( $.isArray( val ) )
1425 for ( var j=0, jLen=val.length ; j<jLen ; j++ )
1428 setData( o, val[j], innerSrc );
1429 data[ a[i] ].push( o );
1444 else if ( funcNotation )
1447 a[i] = a[i].replace(__reFn,
'');
1448 data = data[ a[i] ]( val );
1453 if ( data[ a[i] ] === null || data[ a[i] ] === undefined )
1457 data = data[ a[i] ];
1461 if ( aLast.match(__reFn ) )
1464 data = data[ aLast.replace(__reFn,
'') ]( val );
1470 data[ aLast.replace(__reArray,
'') ] = val;
1474 return function (data, val) {
1475 return setData( data, val, mSource );
1481 return function (data, val) {
1482 data[mSource] = val;
1494 function _fnGetDataMaster ( settings )
1496 return _pluck( settings.aoData,
'_aData' );
1505 function _fnClearTable( settings )
1507 settings.aoData.length = 0;
1508 settings.aiDisplayMaster.length = 0;
1509 settings.aiDisplay.length = 0;
1521 function _fnDeleteIndex( a, iTarget, splice )
1523 var iTargetIndex = -1;
1525 for ( var i=0, iLen=a.length ; i<iLen ; i++ )
1527 if ( a[i] == iTarget )
1531 else if ( a[i] > iTarget )
1537 if ( iTargetIndex != -1 && splice === undefined )
1539 a.splice( iTargetIndex, 1 );
1560 function _fnInvalidate( settings, rowIdx, src, colIdx )
1562 var row = settings.aoData[ rowIdx ];
1564 var cellWrite =
function ( cell, col ) {
1568 while ( cell.childNodes.length ) {
1569 cell.removeChild( cell.firstChild );
1572 cell.innerHTML = _fnGetCellData( settings, rowIdx, col,
'display' );
1576 if ( src ===
'dom' || ((! src || src ===
'auto') && row.src ===
'dom') ) {
1578 row._aData = _fnGetRowElements(
1579 settings, row, colIdx, colIdx === undefined ? undefined : row._aData
1585 var cells = row.anCells;
1588 if ( colIdx !== undefined ) {
1589 cellWrite( cells[colIdx], colIdx );
1592 for ( i=0, ien=cells.length ; i<ien ; i++ ) {
1593 cellWrite( cells[i], i );
1601 row._aSortData = null;
1602 row._aFilterData = null;
1606 var cols = settings.aoColumns;
1607 if ( colIdx !== undefined ) {
1608 cols[ colIdx ].sType = null;
1611 for ( i=0, ien=cols.length ; i<ien ; i++ ) {
1612 cols[i].sType = null;
1616 _fnRowAttributes( settings, row );
1638 function _fnGetRowElements( settings, row, colIdx, d )
1642 td = row.firstChild,
1643 name, col, o, i=0, contents,
1644 columns = settings.aoColumns,
1645 objectRead = settings._rowReadObject;
1648 d = d !== undefined ?
1654 var attr =
function ( str, td ) {
1655 if ( typeof str ===
'string' ) {
1656 var idx = str.indexOf(
'@');
1659 var attr = str.substring( idx+1 );
1660 var setter = _fnSetObjectDataFn( str );
1661 setter( d, td.getAttribute( attr ) );
1667 var cellProcess =
function ( cell ) {
1668 if ( colIdx === undefined || colIdx === i ) {
1670 contents = $.trim(cell.innerHTML);
1672 if ( col && col._bAttrSrc ) {
1673 var setter = _fnSetObjectDataFn( col.mData._ );
1674 setter( d, contents );
1676 attr( col.mData.sort, cell );
1677 attr( col.mData.type, cell );
1678 attr( col.mData.filter, cell );
1684 if ( ! col._setter ) {
1686 col._setter = _fnSetObjectDataFn( col.mData );
1688 col._setter( d, contents );
1702 name = td.nodeName.toUpperCase();
1704 if ( name ==
"TD" || name ==
"TH" ) {
1709 td = td.nextSibling;
1716 for ( var j=0, jen=tds.length ; j<jen ; j++ ) {
1717 cellProcess( tds[j] );
1722 var rowNode = row.firstChild ? row : row.nTr;
1725 var
id = rowNode.getAttribute(
'id' );
1728 _fnSetObjectDataFn( settings.rowId )( d, id );
1747 function _fnCreateTr ( oSettings, iRow, nTrIn, anTds )
1750 row = oSettings.aoData[iRow],
1751 rowData = row._aData,
1756 if ( row.nTr === null )
1758 nTr = nTrIn || document.createElement(
'tr');
1761 row.anCells = cells;
1766 nTr._DT_RowIndex = iRow;
1769 _fnRowAttributes( oSettings, row );
1772 for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
1774 oCol = oSettings.aoColumns[i];
1776 nTd = nTrIn ? anTds[i] : document.createElement( oCol.sCellType );
1777 nTd._DT_CellIndex = {
1785 if ( (!nTrIn || oCol.mRender || oCol.mData !== i) &&
1786 (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+
'.display')
1788 nTd.innerHTML = _fnGetCellData( oSettings, iRow, i,
'display' );
1794 nTd.className +=
' '+oCol.sClass;
1798 if ( oCol.bVisible && ! nTrIn )
1800 nTr.appendChild( nTd );
1802 else if ( ! oCol.bVisible && nTrIn )
1804 nTd.parentNode.removeChild( nTd );
1807 if ( oCol.fnCreatedCell )
1809 oCol.fnCreatedCell.call( oSettings.oInstance,
1810 nTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i
1815 _fnCallbackFire( oSettings,
'aoRowCreatedCallback', null, [nTr, rowData, iRow] );
1820 row.nTr.setAttribute(
'role',
'row' );
1831 function _fnRowAttributes( settings, row )
1834 var data = row._aData;
1837 var
id = settings.rowIdFn( data );
1843 if ( data.DT_RowClass ) {
1845 var a = data.DT_RowClass.split(
' ');
1846 row.__rowc = row.__rowc ?
1847 _unique( row.__rowc.concat( a ) ) :
1851 .removeClass( row.__rowc.join(
' ') )
1852 .addClass( data.DT_RowClass );
1855 if ( data.DT_RowAttr ) {
1856 $(tr).attr( data.DT_RowAttr );
1859 if ( data.DT_RowData ) {
1860 $(tr).data( data.DT_RowData );
1871 function _fnBuildHead( oSettings )
1873 var i, ien, cell, row, column;
1874 var thead = oSettings.nTHead;
1875 var tfoot = oSettings.nTFoot;
1876 var createHeader = $(
'th, td', thead).length === 0;
1877 var classes = oSettings.oClasses;
1878 var columns = oSettings.aoColumns;
1880 if ( createHeader ) {
1881 row = $(
'<tr/>').appendTo( thead );
1884 for ( i=0, ien=columns.length ; i<ien ; i++ ) {
1885 column = columns[i];
1886 cell = $( column.nTh ).addClass( column.sClass );
1888 if ( createHeader ) {
1889 cell.appendTo( row );
1893 if ( oSettings.oFeatures.bSort ) {
1894 cell.addClass( column.sSortingClass );
1896 if ( column.bSortable !==
false ) {
1898 .attr(
'tabindex', oSettings.iTabIndex )
1899 .attr(
'aria-controls', oSettings.sTableId );
1901 _fnSortAttachListener( oSettings, column.nTh, i );
1905 if ( column.sTitle != cell[0].innerHTML ) {
1906 cell.html( column.sTitle );
1909 _fnRenderer( oSettings,
'header' )(
1910 oSettings, cell, column, classes
1914 if ( createHeader ) {
1915 _fnDetectHeader( oSettings.aoHeader, thead );
1919 $(thead).find(
'>tr').attr(
'role',
'row');
1922 $(thead).find(
'>tr>th, >tr>td').addClass( classes.sHeaderTH );
1923 $(tfoot).find(
'>tr>th, >tr>td').addClass( classes.sFooterTH );
1929 if ( tfoot !== null ) {
1930 var cells = oSettings.aoFooter[0];
1932 for ( i=0, ien=cells.length ; i<ien ; i++ ) {
1933 column = columns[i];
1934 column.nTf = cells[i].cell;
1936 if ( column.sClass ) {
1937 $(column.nTf).addClass( column.sClass );
1957 function _fnDrawHead( oSettings, aoSource, bIncludeHidden )
1959 var i, iLen, j, jLen, k, kLen, n, nLocalTr;
1962 var iColumns = oSettings.aoColumns.length;
1963 var iRowspan, iColspan;
1970 if ( bIncludeHidden === undefined )
1972 bIncludeHidden =
false;
1976 for ( i=0, iLen=aoSource.length ; i<iLen ; i++ )
1978 aoLocal[i] = aoSource[i].slice();
1979 aoLocal[i].nTr = aoSource[i].nTr;
1982 for ( j=iColumns-1 ; j>=0 ; j-- )
1984 if ( !oSettings.aoColumns[j].bVisible && !bIncludeHidden )
1986 aoLocal[i].splice( j, 1 );
1991 aApplied.push( [] );
1994 for ( i=0, iLen=aoLocal.length ; i<iLen ; i++ )
1996 nLocalTr = aoLocal[i].nTr;
2001 while( (n = nLocalTr.firstChild) )
2003 nLocalTr.removeChild( n );
2007 for ( j=0, jLen=aoLocal[i].length ; j<jLen ; j++ )
2015 if ( aApplied[i][j] === undefined )
2017 nLocalTr.appendChild( aoLocal[i][j].cell );
2021 while ( aoLocal[i+iRowspan] !== undefined &&
2022 aoLocal[i][j].cell == aoLocal[i+iRowspan][j].cell )
2024 aApplied[i+iRowspan][j] = 1;
2029 while ( aoLocal[i][j+iColspan] !== undefined &&
2030 aoLocal[i][j].cell == aoLocal[i][j+iColspan].cell )
2033 for ( k=0 ; k<iRowspan ; k++ )
2035 aApplied[i+k][j+iColspan] = 1;
2041 $(aoLocal[i][j].cell)
2042 .attr(
'rowspan', iRowspan)
2043 .attr(
'colspan', iColspan);
2055 function _fnDraw( oSettings )
2058 var aPreDraw = _fnCallbackFire( oSettings,
'aoPreDrawCallback',
'preDraw', [oSettings] );
2059 if ( $.inArray(
false, aPreDraw ) !== -1 )
2061 _fnProcessingDisplay( oSettings,
false );
2068 var asStripeClasses = oSettings.asStripeClasses;
2069 var iStripes = asStripeClasses.length;
2070 var iOpenRows = oSettings.aoOpenRows.length;
2071 var oLang = oSettings.oLanguage;
2072 var iInitDisplayStart = oSettings.iInitDisplayStart;
2073 var bServerSide = _fnDataSource( oSettings ) ==
'ssp';
2074 var aiDisplay = oSettings.aiDisplay;
2076 oSettings.bDrawing =
true;
2079 if ( iInitDisplayStart !== undefined && iInitDisplayStart !== -1 )
2081 oSettings._iDisplayStart = bServerSide ?
2083 iInitDisplayStart >= oSettings.fnRecordsDisplay() ?
2087 oSettings.iInitDisplayStart = -1;
2090 var iDisplayStart = oSettings._iDisplayStart;
2091 var iDisplayEnd = oSettings.fnDisplayEnd();
2094 if ( oSettings.bDeferLoading )
2096 oSettings.bDeferLoading =
false;
2098 _fnProcessingDisplay( oSettings,
false );
2100 else if ( !bServerSide )
2104 else if ( !oSettings.bDestroying && !_fnAjaxUpdate( oSettings ) )
2109 if ( aiDisplay.length !== 0 )
2111 var iStart = bServerSide ? 0 : iDisplayStart;
2112 var iEnd = bServerSide ? oSettings.aoData.length : iDisplayEnd;
2114 for ( var j=iStart ; j<iEnd ; j++ )
2116 var iDataIndex = aiDisplay[j];
2117 var aoData = oSettings.aoData[ iDataIndex ];
2118 if ( aoData.nTr === null )
2120 _fnCreateTr( oSettings, iDataIndex );
2123 var nRow = aoData.nTr;
2126 if ( iStripes !== 0 )
2128 var sStripe = asStripeClasses[ iRowCount % iStripes ];
2129 if ( aoData._sRowStripe != sStripe )
2131 $(nRow).removeClass( aoData._sRowStripe ).addClass( sStripe );
2132 aoData._sRowStripe = sStripe;
2139 _fnCallbackFire( oSettings,
'aoRowCallback', null,
2140 [nRow, aoData._aData, iRowCount, j] );
2142 anRows.push( nRow );
2149 var sZero = oLang.sZeroRecords;
2150 if ( oSettings.iDraw == 1 && _fnDataSource( oSettings ) ==
'ajax' )
2152 sZero = oLang.sLoadingRecords;
2154 else if ( oLang.sEmptyTable && oSettings.fnRecordsTotal() === 0 )
2156 sZero = oLang.sEmptyTable;
2159 anRows[ 0 ] = $(
'<tr/>', {
'class': iStripes ? asStripeClasses[0] :
'' } )
2160 .append( $(
'<td />', {
2162 'colSpan': _fnVisbleColumns( oSettings ),
2163 'class': oSettings.oClasses.sRowEmpty
2164 } ).html( sZero ) )[0];
2168 _fnCallbackFire( oSettings,
'aoHeaderCallback',
'header', [ $(oSettings.nTHead).children(
'tr')[0],
2169 _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
2171 _fnCallbackFire( oSettings,
'aoFooterCallback',
'footer', [ $(oSettings.nTFoot).children(
'tr')[0],
2172 _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
2174 var body = $(oSettings.nTBody);
2176 body.children().detach();
2177 body.append( $(anRows) );
2180 _fnCallbackFire( oSettings,
'aoDrawCallback',
'draw', [oSettings] );
2183 oSettings.bSorted =
false;
2184 oSettings.bFiltered =
false;
2185 oSettings.bDrawing =
false;
2196 function _fnReDraw( settings, holdPosition )
2199 features = settings.oFeatures,
2200 sort = features.bSort,
2201 filter = features.bFilter;
2204 _fnSort( settings );
2208 _fnFilterComplete( settings, settings.oPreviousSearch );
2212 settings.aiDisplay = settings.aiDisplayMaster.slice();
2215 if ( holdPosition !==
true ) {
2216 settings._iDisplayStart = 0;
2221 settings._drawHold = holdPosition;
2223 _fnDraw( settings );
2225 settings._drawHold =
false;
2234 function _fnAddOptionsHtml ( oSettings )
2236 var classes = oSettings.oClasses;
2237 var table = $(oSettings.nTable);
2238 var holding = $(
'<div/>').insertBefore( table );
2239 var features = oSettings.oFeatures;
2242 var insert = $(
'<div/>', {
2243 id: oSettings.sTableId+
'_wrapper',
2244 'class': classes.sWrapper + (oSettings.nTFoot ?
'' :
' '+classes.sNoFooter)
2247 oSettings.nHolding = holding[0];
2248 oSettings.nTableWrapper = insert[0];
2249 oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling;
2252 var aDom = oSettings.sDom.split(
'');
2253 var featureNode, cOption, nNewNode, cNext, sAttr, j;
2254 for ( var i=0 ; i<aDom.length ; i++ )
2259 if ( cOption ==
'<' )
2262 nNewNode = $(
'<div/>')[0];
2266 if ( cNext ==
"'" || cNext ==
'"' )
2270 while ( aDom[i+j] != cNext )
2279 sAttr = classes.sJUIHeader;
2281 else if ( sAttr ==
"F" )
2283 sAttr = classes.sJUIFooter;
2289 if ( sAttr.indexOf(
'.') != -1 )
2291 var aSplit = sAttr.split(
'.');
2292 nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1);
2293 nNewNode.className = aSplit[1];
2295 else if ( sAttr.charAt(0) ==
"#" )
2297 nNewNode.id = sAttr.substr(1, sAttr.length-1);
2301 nNewNode.className = sAttr;
2307 insert.append( nNewNode );
2308 insert = $(nNewNode);
2310 else if ( cOption ==
'>' )
2313 insert = insert.parent();
2316 else if ( cOption ==
'l' && features.bPaginate && features.bLengthChange )
2319 featureNode = _fnFeatureHtmlLength( oSettings );
2321 else if ( cOption ==
'f' && features.bFilter )
2324 featureNode = _fnFeatureHtmlFilter( oSettings );
2326 else if ( cOption ==
'r' && features.bProcessing )
2329 featureNode = _fnFeatureHtmlProcessing( oSettings );
2331 else if ( cOption ==
't' )
2334 featureNode = _fnFeatureHtmlTable( oSettings );
2336 else if ( cOption ==
'i' && features.bInfo )
2339 featureNode = _fnFeatureHtmlInfo( oSettings );
2341 else if ( cOption ==
'p' && features.bPaginate )
2344 featureNode = _fnFeatureHtmlPaginate( oSettings );
2346 else if ( DataTable.ext.feature.length !== 0 )
2349 var aoFeatures = DataTable.ext.feature;
2350 for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ )
2352 if ( cOption == aoFeatures[k].cFeature )
2354 featureNode = aoFeatures[k].fnInit( oSettings );
2363 var aanFeatures = oSettings.aanFeatures;
2365 if ( ! aanFeatures[cOption] )
2367 aanFeatures[cOption] = [];
2370 aanFeatures[cOption].push( featureNode );
2371 insert.append( featureNode );
2376 holding.replaceWith( insert );
2377 oSettings.nHolding = null;
2390 function _fnDetectHeader ( aLayout, nThead )
2392 var nTrs = $(nThead).children(
'tr');
2394 var i, k, l, iLen, jLen, iColShifted, iColumn, iColspan, iRowspan;
2396 var fnShiftCol =
function ( a, i, j ) {
2404 aLayout.splice( 0, aLayout.length );
2407 for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
2413 for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
2419 nCell = nTr.firstChild;
2421 if ( nCell.nodeName.toUpperCase() ==
"TD" ||
2422 nCell.nodeName.toUpperCase() ==
"TH" )
2425 iColspan = nCell.getAttribute(
'colspan') * 1;
2426 iRowspan = nCell.getAttribute(
'rowspan') * 1;
2427 iColspan = (!iColspan || iColspan===0 || iColspan===1) ? 1 : iColspan;
2428 iRowspan = (!iRowspan || iRowspan===0 || iRowspan===1) ? 1 : iRowspan;
2433 iColShifted = fnShiftCol( aLayout, i, iColumn );
2436 bUnique = iColspan === 1 ?
true :
false;
2439 for ( l=0 ; l<iColspan ; l++ )
2441 for ( k=0 ; k<iRowspan ; k++ )
2443 aLayout[i+k][iColShifted+l] = {
2447 aLayout[i+k].nTr = nTr;
2451 nCell = nCell.nextSibling;
2465 function _fnGetUniqueThs ( oSettings, nHeader, aLayout )
2470 aLayout = oSettings.aoHeader;
2474 _fnDetectHeader( aLayout, nHeader );
2478 for ( var i=0, iLen=aLayout.length ; i<iLen ; i++ )
2480 for ( var j=0, jLen=aLayout[i].length ; j<jLen ; j++ )
2482 if ( aLayout[i][j].unique &&
2483 (!aReturn[j] || !oSettings.bSortCellsTop) )
2485 aReturn[j] = aLayout[i][j].cell;
2502 function _fnBuildAjax( oSettings, data, fn )
2505 _fnCallbackFire( oSettings,
'aoServerParams',
'serverParams', [data] );
2509 if ( data && $.isArray(data) ) {
2511 var rbracket = /(.*?)\[\]$/;
2513 $.each( data,
function (key, val) {
2514 var match = val.name.match(rbracket);
2518 var name = match[0];
2520 if ( ! tmp[ name ] ) {
2523 tmp[ name ].push( val.value );
2526 tmp[val.name] = val.value;
2533 var ajax = oSettings.ajax;
2534 var instance = oSettings.oInstance;
2535 var callback =
function ( json ) {
2536 _fnCallbackFire( oSettings, null,
'xhr', [oSettings, json, oSettings.jqXHR] );
2540 if ( $.isPlainObject( ajax ) && ajax.data )
2542 ajaxData = ajax.data;
2544 var newData = $.isFunction( ajaxData ) ?
2545 ajaxData( data, oSettings ) :
2549 data = $.isFunction( ajaxData ) && newData ?
2551 $.extend(
true, data, newData );
2560 "success":
function (json) {
2561 var error = json.error || json.sError;
2563 _fnLog( oSettings, 0, error );
2566 oSettings.json = json;
2571 "type": oSettings.sServerMethod,
2572 "error":
function (xhr, error, thrown) {
2573 var ret = _fnCallbackFire( oSettings, null,
'xhr', [oSettings, null, oSettings.jqXHR] );
2575 if ( $.inArray(
true, ret ) === -1 ) {
2576 if ( error ==
"parsererror" ) {
2577 _fnLog( oSettings, 0,
'Invalid JSON response', 1 );
2579 else if ( xhr.readyState === 4 ) {
2580 _fnLog( oSettings, 0,
'Ajax error', 7 );
2584 _fnProcessingDisplay( oSettings,
false );
2589 oSettings.oAjaxData = data;
2592 _fnCallbackFire( oSettings, null,
'preXhr', [oSettings, data] );
2594 if ( oSettings.fnServerData )
2597 oSettings.fnServerData.call( instance,
2598 oSettings.sAjaxSource,
2599 $.map( data, function (val, key) {
2600 return { name: key, value: val };
2606 else if ( oSettings.sAjaxSource || typeof ajax ===
'string' )
2609 oSettings.jqXHR = $.ajax( $.extend( baseAjax, {
2610 url: ajax || oSettings.sAjaxSource
2613 else if ( $.isFunction( ajax ) )
2616 oSettings.jqXHR = ajax.call( instance, data, callback, oSettings );
2621 oSettings.jqXHR = $.ajax( $.extend( baseAjax, ajax ) );
2624 ajax.data = ajaxData;
2635 function _fnAjaxUpdate( settings )
2637 if ( settings.bAjaxDataGet ) {
2639 _fnProcessingDisplay( settings,
true );
2643 _fnAjaxParameters( settings ),
2645 _fnAjaxUpdateDraw( settings, json );
2666 function _fnAjaxParameters( settings )
2669 columns = settings.aoColumns,
2670 columnCount = columns.length,
2671 features = settings.oFeatures,
2672 preSearch = settings.oPreviousSearch,
2673 preColSearch = settings.aoPreSearchCols,
2674 i, data = [], dataProp, column, columnSearch,
2675 sort = _fnSortFlatten( settings ),
2676 displayStart = settings._iDisplayStart,
2677 displayLength = features.bPaginate !==
false ?
2678 settings._iDisplayLength :
2681 var param =
function ( name, value ) {
2682 data.push( {
'name': name,
'value': value } );
2686 param(
'sEcho', settings.iDraw );
2687 param(
'iColumns', columnCount );
2688 param(
'sColumns', _pluck( columns,
'sName' ).join(
',') );
2689 param(
'iDisplayStart', displayStart );
2690 param(
'iDisplayLength', displayLength );
2694 draw: settings.iDraw,
2697 start: displayStart,
2698 length: displayLength,
2700 value: preSearch.sSearch,
2701 regex: preSearch.bRegex
2705 for ( i=0 ; i<columnCount ; i++ ) {
2706 column = columns[i];
2707 columnSearch = preColSearch[i];
2708 dataProp = typeof column.mData==
"function" ?
'function' : column.mData ;
2713 searchable: column.bSearchable,
2714 orderable: column.bSortable,
2716 value: columnSearch.sSearch,
2717 regex: columnSearch.bRegex
2721 param(
"mDataProp_"+i, dataProp );
2723 if ( features.bFilter ) {
2724 param(
'sSearch_'+i, columnSearch.sSearch );
2725 param(
'bRegex_'+i, columnSearch.bRegex );
2726 param(
'bSearchable_'+i, column.bSearchable );
2729 if ( features.bSort ) {
2730 param(
'bSortable_'+i, column.bSortable );
2734 if ( features.bFilter ) {
2735 param(
'sSearch', preSearch.sSearch );
2736 param(
'bRegex', preSearch.bRegex );
2739 if ( features.bSort ) {
2740 $.each( sort,
function ( i, val ) {
2741 d.order.push( { column: val.col, dir: val.dir } );
2743 param(
'iSortCol_'+i, val.col );
2744 param(
'sSortDir_'+i, val.dir );
2747 param(
'iSortingCols', sort.length );
2752 var legacy = DataTable.ext.legacy.ajax;
2753 if ( legacy === null ) {
2754 return settings.sAjaxSource ? data : d;
2759 return legacy ? data : d;
2774 function _fnAjaxUpdateDraw ( settings, json )
2778 var compat =
function ( old, modern ) {
2779 return json[old] !== undefined ? json[old] : json[modern];
2782 var data = _fnAjaxDataSrc( settings, json );
2783 var draw = compat(
'sEcho',
'draw' );
2784 var recordsTotal = compat(
'iTotalRecords',
'recordsTotal' );
2785 var recordsFiltered = compat(
'iTotalDisplayRecords',
'recordsFiltered' );
2789 if ( draw*1 < settings.iDraw ) {
2792 settings.iDraw = draw * 1;
2795 _fnClearTable( settings );
2796 settings._iRecordsTotal = parseInt(recordsTotal, 10);
2797 settings._iRecordsDisplay = parseInt(recordsFiltered, 10);
2799 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
2800 _fnAddData( settings, data[i] );
2802 settings.aiDisplay = settings.aiDisplayMaster.slice();
2804 settings.bAjaxDataGet =
false;
2805 _fnDraw( settings );
2807 if ( ! settings._bInitComplete ) {
2808 _fnInitComplete( settings, json );
2811 settings.bAjaxDataGet =
true;
2812 _fnProcessingDisplay( settings,
false );
2824 function _fnAjaxDataSrc ( oSettings, json )
2826 var dataSrc = $.isPlainObject( oSettings.ajax ) && oSettings.ajax.dataSrc !== undefined ?
2827 oSettings.ajax.dataSrc :
2828 oSettings.sAjaxDataProp;
2832 if ( dataSrc ===
'data' ) {
2833 return json.aaData || json[dataSrc];
2836 return dataSrc !==
"" ?
2837 _fnGetObjectDataFn( dataSrc )( json ) :
2847 function _fnFeatureHtmlFilter ( settings )
2849 var classes = settings.oClasses;
2850 var tableId = settings.sTableId;
2851 var language = settings.oLanguage;
2852 var previousSearch = settings.oPreviousSearch;
2853 var features = settings.aanFeatures;
2854 var input =
'<input type="search" class="'+classes.sFilterInput+
'"/>';
2856 var str = language.sSearch;
2857 str = str.match(/_INPUT_/) ?
2858 str.replace(
'_INPUT_', input) :
2861 var filter = $(
'<div/>', {
2862 'id': ! features.f ? tableId+
'_filter' : null,
2863 'class': classes.sFilter
2865 .append( $(
'<label/>' ).append( str ) );
2867 var searchFn =
function() {
2870 var val = !this.value ?
"" : this.value;
2873 if ( val != previousSearch.sSearch ) {
2874 _fnFilterComplete( settings, {
2876 "bRegex": previousSearch.bRegex,
2877 "bSmart": previousSearch.bSmart ,
2878 "bCaseInsensitive": previousSearch.bCaseInsensitive
2882 settings._iDisplayStart = 0;
2883 _fnDraw( settings );
2887 var searchDelay = settings.searchDelay !== null ?
2888 settings.searchDelay :
2889 _fnDataSource( settings ) ===
'ssp' ?
2893 var jqFilter = $(
'input', filter)
2894 .val( previousSearch.sSearch )
2895 .attr(
'placeholder', language.sSearchPlaceholder )
2897 'keyup.DT search.DT input.DT paste.DT cut.DT',
2899 _fnThrottle( searchFn, searchDelay ) :
2902 .bind(
'keypress.DT',
function(e) {
2904 if ( e.keyCode == 13 ) {
2908 .attr(
'aria-controls', tableId);
2911 $(settings.nTable).on(
'search.dt.DT',
function ( ev, s ) {
2912 if ( settings === s ) {
2916 if ( jqFilter[0] !== document.activeElement ) {
2917 jqFilter.val( previousSearch.sSearch );
2935 function _fnFilterComplete ( oSettings, oInput, iForce )
2937 var oPrevSearch = oSettings.oPreviousSearch;
2938 var aoPrevSearch = oSettings.aoPreSearchCols;
2939 var fnSaveFilter =
function ( oFilter ) {
2941 oPrevSearch.sSearch = oFilter.sSearch;
2942 oPrevSearch.bRegex = oFilter.bRegex;
2943 oPrevSearch.bSmart = oFilter.bSmart;
2944 oPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive;
2946 var fnRegex =
function ( o ) {
2948 return o.bEscapeRegex !== undefined ? !o.bEscapeRegex : o.bRegex;
2953 _fnColumnTypes( oSettings );
2956 if ( _fnDataSource( oSettings ) !=
'ssp' )
2959 _fnFilter( oSettings, oInput.sSearch, iForce, fnRegex(oInput), oInput.bSmart, oInput.bCaseInsensitive );
2960 fnSaveFilter( oInput );
2963 for ( var i=0 ; i<aoPrevSearch.length ; i++ )
2965 _fnFilterColumn( oSettings, aoPrevSearch[i].sSearch, i, fnRegex(aoPrevSearch[i]),
2966 aoPrevSearch[i].bSmart, aoPrevSearch[i].bCaseInsensitive );
2970 _fnFilterCustom( oSettings );
2974 fnSaveFilter( oInput );
2978 oSettings.bFiltered =
true;
2979 _fnCallbackFire( oSettings, null,
'search', [oSettings] );
2988 function _fnFilterCustom( settings )
2990 var filters = DataTable.ext.search;
2991 var displayRows = settings.aiDisplay;
2994 for ( var i=0, ien=filters.length ; i<ien ; i++ ) {
2998 for ( var j=0, jen=displayRows.length ; j<jen ; j++ ) {
2999 rowIdx = displayRows[ j ];
3000 row = settings.aoData[ rowIdx ];
3002 if ( filters[i]( settings, row._aFilterData, rowIdx, row._aData, j ) ) {
3003 rows.push( rowIdx );
3009 displayRows.length = 0;
3010 $.merge( displayRows, rows );
3025 function _fnFilterColumn ( settings, searchStr, colIdx, regex, smart, caseInsensitive )
3027 if ( searchStr ===
'' ) {
3032 var display = settings.aiDisplay;
3033 var rpSearch = _fnFilterCreateSearch( searchStr, regex, smart, caseInsensitive );
3035 for ( var i=display.length-1 ; i>=0 ; i-- ) {
3036 data = settings.aoData[ display[i] ]._aFilterData[ colIdx ];
3038 if ( ! rpSearch.test( data ) ) {
3039 display.splice( i, 1 );
3055 function _fnFilter( settings, input, force, regex, smart, caseInsensitive )
3057 var rpSearch = _fnFilterCreateSearch( input, regex, smart, caseInsensitive );
3058 var prevSearch = settings.oPreviousSearch.sSearch;
3059 var displayMaster = settings.aiDisplayMaster;
3060 var display, invalidated, i;
3063 if ( DataTable.ext.search.length !== 0 ) {
3068 invalidated = _fnFilterData( settings );
3071 if ( input.length <= 0 ) {
3072 settings.aiDisplay = displayMaster.slice();
3078 prevSearch.length > input.length ||
3079 input.indexOf(prevSearch) !== 0 ||
3083 settings.aiDisplay = displayMaster.slice();
3087 display = settings.aiDisplay;
3089 for ( i=display.length-1 ; i>=0 ; i-- ) {
3090 if ( ! rpSearch.test( settings.aoData[ display[i] ]._sFilterRow ) ) {
3091 display.splice( i, 1 );
3107 function _fnFilterCreateSearch( search, regex, smart, caseInsensitive )
3111 _fnEscapeRegex( search );
3121 var a = $.map( search.match( /
"[^"]+
"|[^ ]+/g ) || [''], function ( word ) {
3122 if ( word.charAt(0) === '"' ) {
3123 var m = word.match( /^"(.*)"$/ );
3124 word = m ? m[1] : word;
3127 return word.replace('"', '');
3130 search = '^(?=.*?'+a.join( ')(?=.*?' )+').*$';
3133 return new RegExp( search, caseInsensitive ? 'i' : '' );
3143 function _fnEscapeRegex ( sVal )
3145 return sVal.replace( _re_escape_regex, '\\$1' );
3150 var __filter_div = $('<div>')[0];
3151 var __filter_div_textContent = __filter_div.textContent !== undefined;
3153 // Update the filtering data for each row if needed (by invalidation or first run)
3154 function _fnFilterData ( settings )
3156 var columns = settings.aoColumns;
3158 var i, j, ien, jen, filterData, cellData, row;
3159 var fomatters = DataTable.ext.type.search;
3160 var wasInvalidated = false;
3162 for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
3163 row = settings.aoData[i];
3165 if ( ! row._aFilterData ) {
3168 for ( j=0, jen=columns.length ; j<jen ; j++ ) {
3169 column = columns[j];
3171 if ( column.bSearchable ) {
3172 cellData = _fnGetCellData( settings, i, j, 'filter' );
3174 if ( fomatters[ column.sType ] ) {
3175 cellData = fomatters[ column.sType ]( cellData );
3178 // Search in DataTables 1.10 is string based. In 1.11 this
3179 // should be altered to also allow strict type checking.
3180 if ( cellData === null ) {
3184 if ( typeof cellData !== 'string' && cellData.toString ) {
3185 cellData = cellData.toString();
3192 // If it looks like there is an HTML entity in the string,
3193 // attempt to decode it so sorting works as expected. Note that
3194 // we could use a single line of jQuery to do this, but the DOM
3195 // method used here is much faster http://jsperf.com/html-decode
3196 if ( cellData.indexOf && cellData.indexOf('&') !== -1 ) {
3197 __filter_div.innerHTML = cellData;
3198 cellData = __filter_div_textContent ?
3199 __filter_div.textContent :
3200 __filter_div.innerText;
3203 if ( cellData.replace ) {
3204 cellData = cellData.replace(/[\r\n]/g, '');
3207 filterData.push( cellData );
3210 row._aFilterData = filterData;
3211 row._sFilterRow = filterData.join(' ');
3212 wasInvalidated = true;
3216 return wasInvalidated;
3227 function _fnSearchToCamel ( obj )
3230 search: obj.sSearch,
3233 caseInsensitive: obj.bCaseInsensitive
3246 function _fnSearchToHung ( obj )
3249 sSearch: obj.search,
3252 bCaseInsensitive: obj.caseInsensitive
3262 function _fnFeatureHtmlInfo ( settings )
3265 tid = settings.sTableId,
3266 nodes = settings.aanFeatures.i,
3268 'class': settings.oClasses.sInfo,
3269 'id': ! nodes ? tid+'_info' : null
3273 // Update display on each draw
3274 settings.aoDrawCallback.push( {
3275 "fn
": _fnUpdateInfo,
3276 "sName
": "information
"
3280 .attr( 'role', 'status' )
3281 .attr( 'aria-live', 'polite' );
3283 // Table is described by our info div
3284 $(settings.nTable).attr( 'aria-describedby', tid+'_info' );
3296 function _fnUpdateInfo ( settings )
3298 /* Show information about the table */
3299 var nodes = settings.aanFeatures.i;
3300 if ( nodes.length === 0 ) {
3305 lang = settings.oLanguage,
3306 start = settings._iDisplayStart+1,
3307 end = settings.fnDisplayEnd(),
3308 max = settings.fnRecordsTotal(),
3309 total = settings.fnRecordsDisplay(),
3314 if ( total !== max ) {
3315 /* Record set after filtering */
3316 out += ' ' + lang.sInfoFiltered;
3319 // Convert the macros
3320 out += lang.sInfoPostFix;
3321 out = _fnInfoMacros( settings, out );
3323 var callback = lang.fnInfoCallback;
3324 if ( callback !== null ) {
3325 out = callback.call( settings.oInstance,
3326 settings, start, end, max, total, out
3330 $(nodes).html( out );
3334 function _fnInfoMacros ( settings, str )
3336 // When infinite scrolling, we are always starting at 1. _iDisplayStart is used only
3339 formatter = settings.fnFormatNumber,
3340 start = settings._iDisplayStart+1,
3341 len = settings._iDisplayLength,
3342 vis = settings.fnRecordsDisplay(),
3346 replace(/_START_/g, formatter.call( settings, start ) ).
3347 replace(/_END_/g, formatter.call( settings, settings.fnDisplayEnd() ) ).
3348 replace(/_MAX_/g, formatter.call( settings, settings.fnRecordsTotal() ) ).
3349 replace(/_TOTAL_/g, formatter.call( settings, vis ) ).
3350 replace(/_PAGE_/g, formatter.call( settings, all ? 1 : Math.ceil( start / len ) ) ).
3351 replace(/_PAGES_/g, formatter.call( settings, all ? 1 : Math.ceil( vis / len ) ) );
3361 function _fnInitialise ( settings )
3363 var i, iLen, iAjaxStart=settings.iInitDisplayStart;
3364 var columns = settings.aoColumns, column;
3365 var features = settings.oFeatures;
3366 var deferLoading = settings.bDeferLoading; // value modified by the draw
3368 /* Ensure that the table data is fully initialised */
3369 if ( ! settings.bInitialised ) {
3370 setTimeout( function(){ _fnInitialise( settings ); }, 200 );
3374 /* Show the display HTML options */
3375 _fnAddOptionsHtml( settings );
3377 /* Build and draw the header / footer for the table */
3378 _fnBuildHead( settings );
3379 _fnDrawHead( settings, settings.aoHeader );
3380 _fnDrawHead( settings, settings.aoFooter );
3382 /* Okay to show that something is going on now */
3383 _fnProcessingDisplay( settings, true );
3385 /* Calculate sizes for columns */
3386 if ( features.bAutoWidth ) {
3387 _fnCalculateColumnWidths( settings );
3390 for ( i=0, iLen=columns.length ; i<iLen ; i++ ) {
3391 column = columns[i];
3393 if ( column.sWidth ) {
3394 column.nTh.style.width = _fnStringToCss( column.sWidth );
3398 _fnCallbackFire( settings, null, 'preInit', [settings] );
3400 // If there is default sorting required - let's do it. The sort function
3401 // will do the drawing for us. Otherwise we draw the table regardless of the
3402 // Ajax source - this allows the table to look initialised for Ajax sourcing
3403 // data (show 'loading' message possibly)
3404 _fnReDraw( settings );
3406 // Server-side processing init complete is done by _fnAjaxUpdateDraw
3407 var dataSrc = _fnDataSource( settings );
3408 if ( dataSrc != 'ssp' || deferLoading ) {
3409 // if there is an ajax source load the data
3410 if ( dataSrc == 'ajax' ) {
3411 _fnBuildAjax( settings, [], function(json) {
3412 var aData = _fnAjaxDataSrc( settings, json );
3414 // Got the data - add it to the table
3415 for ( i=0 ; i<aData.length ; i++ ) {
3416 _fnAddData( settings, aData[i] );
3419 // Reset the init display for cookie saving. We've already done
3420 // a filter, and therefore cleared it before. So we need to make
3421 // it appear 'fresh'
3422 settings.iInitDisplayStart = iAjaxStart;
3424 _fnReDraw( settings );
3426 _fnProcessingDisplay( settings, false );
3427 _fnInitComplete( settings, json );
3431 _fnProcessingDisplay( settings, false );
3432 _fnInitComplete( settings );
3445 function _fnInitComplete ( settings, json )
3447 settings._bInitComplete = true;
3449 // When data was added after the initialisation (data or Ajax) we need to
3450 // calculate the column sizing
3451 if ( json || settings.oInit.aaData ) {
3452 _fnAdjustColumnSizing( settings );
3455 _fnCallbackFire( settings, null, 'plugin-init', [settings, json] );
3456 _fnCallbackFire( settings, 'aoInitComplete', 'init', [settings, json] );
3460 function _fnLengthChange ( settings, val )
3462 var len = parseInt( val, 10 );
3463 settings._iDisplayLength = len;
3465 _fnLengthOverflow( settings );
3467 // Fire length change event
3468 _fnCallbackFire( settings, null, 'length', [settings, len] );
3478 function _fnFeatureHtmlLength ( settings )
3481 classes = settings.oClasses,
3482 tableId = settings.sTableId,
3483 menu = settings.aLengthMenu,
3484 d2 = $.isArray( menu[0] ),
3485 lengths = d2 ? menu[0] : menu,
3486 language = d2 ? menu[1] : menu;
3488 var select = $('<select/>', {
3489 'name': tableId+'_length',
3490 'aria-controls': tableId,
3491 'class': classes.sLengthSelect
3494 for ( var i=0, ien=lengths.length ; i<ien ; i++ ) {
3495 select[0][ i ] = new Option( language[i], lengths[i] );
3498 var div = $('<div><label/></div>').addClass( classes.sLength );
3499 if ( ! settings.aanFeatures.l ) {
3500 div[0].id = tableId+'_length';
3503 div.children().append(
3504 settings.oLanguage.sLengthMenu.replace( '_MENU_', select[0].outerHTML )
3507 // Can't use `select` variable as user might provide their own and the
3508 // reference is broken by the use of outerHTML
3510 .val( settings._iDisplayLength )
3511 .bind( 'change.DT', function(e) {
3512 _fnLengthChange( settings, $(this).val() );
3513 _fnDraw( settings );
3516 // Update node value whenever anything changes the table's length
3517 $(settings.nTable).bind( 'length.dt.DT', function (e, s, len) {
3518 if ( settings === s ) {
3519 $('select', div).val( len );
3528 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
3529 * Note that most of the paging logic is done in
3530 * DataTable.ext.pager
3539 function _fnFeatureHtmlPaginate ( settings )
3542 type = settings.sPaginationType,
3543 plugin = DataTable.ext.pager[ type ],
3544 modern = typeof plugin === 'function',
3545 redraw = function( settings ) {
3546 _fnDraw( settings );
3548 node = $('<div/>').addClass( settings.oClasses.sPaging + type )[0],
3549 features = settings.aanFeatures;
3552 plugin.fnInit( settings, node, redraw );
3555 /* Add a draw callback for the pagination on first instance, to update the paging display */
3558 node.id = settings.sTableId+'_paginate';
3560 settings.aoDrawCallback.push( {
3561 "fn
": function( settings ) {
3564 start = settings._iDisplayStart,
3565 len = settings._iDisplayLength,
3566 visRecords = settings.fnRecordsDisplay(),
3568 page = all ? 0 : Math.ceil( start / len ),
3569 pages = all ? 1 : Math.ceil( visRecords / len ),
3570 buttons = plugin(page, pages),
3573 for ( i=0, ien=features.p.length ; i<ien ; i++ ) {
3574 _fnRenderer( settings, 'pageButton' )(
3575 settings, features.p[i], i, buttons, page, pages
3580 plugin.fnUpdate( settings, redraw );
3583 "sName
": "pagination
"
3600 function _fnPageChange ( settings, action, redraw )
3603 start = settings._iDisplayStart,
3604 len = settings._iDisplayLength,
3605 records = settings.fnRecordsDisplay();
3607 if ( records === 0 || len === -1 )
3611 else if ( typeof action === "number
" )
3613 start = action * len;
3615 if ( start > records )
3620 else if ( action == "first
" )
3624 else if ( action == "previous
" )
3635 else if ( action == "next
" )
3637 if ( start + len < records )
3642 else if ( action == "last
" )
3644 start = Math.floor( (records-1) / len) * len;
3648 _fnLog( settings, 0, "Unknown paging action:
"+action, 5 );
3651 var changed = settings._iDisplayStart !== start;
3652 settings._iDisplayStart = start;
3655 _fnCallbackFire( settings, null, 'page', [settings] );
3658 _fnDraw( settings );
3673 function _fnFeatureHtmlProcessing ( settings )
3675 return $('<div/>', {
3676 'id': ! settings.aanFeatures.r ? settings.sTableId+'_processing' : null,
3677 'class': settings.oClasses.sProcessing
3679 .html( settings.oLanguage.sProcessing )
3680 .insertBefore( settings.nTable )[0];
3690 function _fnProcessingDisplay ( settings, show )
3692 if ( settings.oFeatures.bProcessing ) {
3693 $(settings.aanFeatures.r).css( 'display', show ? 'block' : 'none' );
3696 _fnCallbackFire( settings, null, 'processing', [settings, show] );
3705 function _fnFeatureHtmlTable ( settings )
3707 var table = $(settings.nTable);
3709 // Add the ARIA grid role to the table
3710 table.attr( 'role', 'grid' );
3712 // Scrolling from here on in
3713 var scroll = settings.oScroll;
3715 if ( scroll.sX === '' && scroll.sY === '' ) {
3716 return settings.nTable;
3719 var scrollX = scroll.sX;
3720 var scrollY = scroll.sY;
3721 var classes = settings.oClasses;
3722 var caption = table.children('caption');
3723 var captionSide = caption.length ? caption[0]._captionSide : null;
3724 var headerClone = $( table[0].cloneNode(false) );
3725 var footerClone = $( table[0].cloneNode(false) );
3726 var footer = table.children('tfoot');
3727 var _div = '<div/>';
3728 var size = function ( s ) {
3729 return !s ? null : _fnStringToCss( s );
3732 if ( ! footer.length ) {
3737 * The HTML structure that we want to generate in this function is:
3740 * div - scroll head inner
3741 * table - scroll head table
3744 * table - table (master table)
3745 * thead - thead clone for sizing
3748 * div - scroll foot inner
3749 * table - scroll foot table
3752 var scroller = $( _div, { 'class': classes.sScrollWrapper } )
3754 $(_div, { 'class': classes.sScrollHead } )
3757 position: 'relative',
3759 width: scrollX ? size(scrollX) : '100%'
3762 $(_div, { 'class': classes.sScrollHeadInner } )
3764 'box-sizing': 'content-box',
3765 width: scroll.sXInner || '100%'
3770 .css( 'margin-left', 0 )
3771 .append( captionSide === 'top' ? caption : null )
3773 table.children('thead')
3779 $(_div, { 'class': classes.sScrollBody } )
3781 position: 'relative',
3783 width: size( scrollX )
3790 $(_div, { 'class': classes.sScrollFoot } )
3794 width: scrollX ? size(scrollX) : '100%'
3797 $(_div, { 'class': classes.sScrollFootInner } )
3801 .css( 'margin-left', 0 )
3802 .append( captionSide === 'bottom' ? caption : null )
3804 table.children('tfoot')
3811 var children = scroller.children();
3812 var scrollHead = children[0];
3813 var scrollBody = children[1];
3814 var scrollFoot = footer ? children[2] : null;
3816 // When the body is scrolled, then we also want to scroll the headers
3818 $(scrollBody).on( 'scroll.DT', function (e) {
3819 var scrollLeft = this.scrollLeft;
3821 scrollHead.scrollLeft = scrollLeft;
3824 scrollFoot.scrollLeft = scrollLeft;
3830 scrollY && scroll.bCollapse ? 'max-height' : 'height',
3834 settings.nScrollHead = scrollHead;
3835 settings.nScrollBody = scrollBody;
3836 settings.nScrollFoot = scrollFoot;
3838 // On redraw - align columns
3839 settings.aoDrawCallback.push( {
3840 "fn
": _fnScrollDraw,
3841 "sName
": "scrolling
"
3863 function _fnScrollDraw ( settings )
3865 // Given that this is such a monster function, a lot of variables are use
3866 // to try and keep the minimised size as small as possible
3868 scroll = settings.oScroll,
3869 scrollX = scroll.sX,
3870 scrollXInner = scroll.sXInner,
3871 scrollY = scroll.sY,
3872 barWidth = scroll.iBarWidth,
3873 divHeader = $(settings.nScrollHead),
3874 divHeaderStyle = divHeader[0].style,
3875 divHeaderInner = divHeader.children('div'),
3876 divHeaderInnerStyle = divHeaderInner[0].style,
3877 divHeaderTable = divHeaderInner.children('table'),
3878 divBodyEl = settings.nScrollBody,
3879 divBody = $(divBodyEl),
3880 divBodyStyle = divBodyEl.style,
3881 divFooter = $(settings.nScrollFoot),
3882 divFooterInner = divFooter.children('div'),
3883 divFooterTable = divFooterInner.children('table'),
3884 header = $(settings.nTHead),
3885 table = $(settings.nTable),
3887 tableStyle = tableEl.style,
3888 footer = settings.nTFoot ? $(settings.nTFoot) : null,
3889 browser = settings.oBrowser,
3890 ie67 = browser.bScrollOversize,
3891 dtHeaderCells = _pluck( settings.aoColumns, 'nTh' ),
3892 headerTrgEls, footerTrgEls,
3893 headerSrcEls, footerSrcEls,
3894 headerCopy, footerCopy,
3895 headerWidths=[], footerWidths=[],
3896 headerContent=[], footerContent=[],
3897 idx, correction, sanityWidth,
3898 zeroOut = function(nSizer) {
3899 var style = nSizer.style;
3900 style.paddingTop = "0
";
3901 style.paddingBottom = "0
";
3902 style.borderTopWidth = "0
";
3903 style.borderBottomWidth = "0
";
3907 // If the scrollbar visibility has changed from the last draw, we need to
3908 // adjust the column sizes as the table width will have changed to account
3909 // for the scrollbar
3910 var scrollBarVis = divBodyEl.scrollHeight > divBodyEl.clientHeight;
3912 if ( settings.scrollBarVis !== scrollBarVis && settings.scrollBarVis !== undefined ) {
3913 settings.scrollBarVis = scrollBarVis;
3914 _fnAdjustColumnSizing( settings );
3915 return; // adjust column sizing will call this function again
3918 settings.scrollBarVis = scrollBarVis;
3922 * 1. Re-create the table inside the scrolling div
3925 // Remove the old minimised thead and tfoot elements in the inner table
3926 table.children('thead, tfoot').remove();
3929 footerCopy = footer.clone().prependTo( table );
3930 footerTrgEls = footer.find('tr'); // the original tfoot is in its own table and must be sized
3931 footerSrcEls = footerCopy.find('tr');
3934 // Clone the current header and footer elements and then place it into the inner table
3935 headerCopy = header.clone().prependTo( table );
3936 headerTrgEls = header.find('tr'); // original header is in its own table
3937 headerSrcEls = headerCopy.find('tr');
3938 headerCopy.find('th, td').removeAttr('tabindex');
3942 * 2. Take live measurements from the DOM - do not alter the DOM itself!
3945 // Remove old sizing and apply the calculated column widths
3946 // Get the unique column headers in the newly created (cloned) header. We want to apply the
3947 // calculated sizes to this header
3950 divBodyStyle.width = '100%';
3951 divHeader[0].style.width = '100%';
3954 $.each( _fnGetUniqueThs( settings, headerCopy ), function ( i, el ) {
3955 idx = _fnVisibleToColumnIndex( settings, i );
3956 el.style.width = settings.aoColumns[idx].sWidth;
3960 _fnApplyToChildren( function(n) {
3965 // Size the table as a whole
3966 sanityWidth = table.outerWidth();
3967 if ( scrollX === "" ) {
3969 tableStyle.width = "100%
";
3971 // IE7 will make the width of the table when 100% include the scrollbar
3972 // - which is shouldn't. When there is a scrollbar we need to take this
3974 if ( ie67 && (table.find('tbody').height() > divBodyEl.offsetHeight ||
3975 divBody.css('overflow-y') == "scroll
")
3977 tableStyle.width = _fnStringToCss( table.outerWidth() - barWidth);
3980 // Recalculate the sanity width
3981 sanityWidth = table.outerWidth();
3983 else if ( scrollXInner !== "" ) {
3984 // legacy x scroll inner has been given - use it
3985 tableStyle.width = _fnStringToCss(scrollXInner);
3987 // Recalculate the sanity width
3988 sanityWidth = table.outerWidth();
3991 // Hidden header should have zero height, so remove padding and borders. Then
3992 // set the width based on the real headers
3994 // Apply all styles in one pass
3995 _fnApplyToChildren( zeroOut, headerSrcEls );
3997 // Read all widths in next pass
3998 _fnApplyToChildren( function(nSizer) {
3999 headerContent.push( nSizer.innerHTML );
4000 headerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
4003 // Apply all widths in final pass
4004 _fnApplyToChildren( function(nToSize, i) {
4005 // Only apply widths to the DataTables detected header cells - this
4006 // prevents complex headers from having contradictory sizes applied
4007 if ( $.inArray( nToSize, dtHeaderCells ) !== -1 ) {
4008 nToSize.style.width = headerWidths[i];
4012 $(headerSrcEls).height(0);
4014 /* Same again with the footer if we have one */
4017 _fnApplyToChildren( zeroOut, footerSrcEls );
4019 _fnApplyToChildren( function(nSizer) {
4020 footerContent.push( nSizer.innerHTML );
4021 footerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
4024 _fnApplyToChildren( function(nToSize, i) {
4025 nToSize.style.width = footerWidths[i];
4028 $(footerSrcEls).height(0);
4033 * 3. Apply the measurements
4036 // "Hide
" the header and footer that we used for the sizing. We need to keep
4037 // the content of the cell so that the width applied to the header and body
4038 // both match, but we want to hide it completely. We want to also fix their
4039 // width to what they currently are
4040 _fnApplyToChildren( function(nSizer, i) {
4041 nSizer.innerHTML = '<div class="dataTables_sizing
" style="height:0;overflow:hidden;
">'+headerContent[i]+'</div>';
4042 nSizer.style.width = headerWidths[i];
4047 _fnApplyToChildren( function(nSizer, i) {
4048 nSizer.innerHTML = '<div class="dataTables_sizing
" style="height:0;overflow:hidden;
">'+footerContent[i]+'</div>';
4049 nSizer.style.width = footerWidths[i];
4053 // Sanity check that the table is of a sensible width. If not then we are going to get
4054 // misalignment - try to prevent this by not allowing the table to shrink below its min width
4055 if ( table.outerWidth() < sanityWidth )
4057 // The min width depends upon if we have a vertical scrollbar visible or not */
4058 correction = ((divBodyEl.scrollHeight > divBodyEl.offsetHeight ||
4059 divBody.css('overflow-y') == "scroll
")) ?
4060 sanityWidth+barWidth :
4063 // IE6/7 are a law unto themselves...
4064 if ( ie67 && (divBodyEl.scrollHeight >
4065 divBodyEl.offsetHeight || divBody.css('overflow-y') == "scroll
")
4067 tableStyle.width = _fnStringToCss( correction-barWidth );
4070 // And give the user a warning that we've stopped the table getting too small
4071 if ( scrollX === "" || scrollXInner !== "" ) {
4072 _fnLog( settings, 1, 'Possible column misalignment', 6 );
4077 correction = '100%';
4080 // Apply to the container elements
4081 divBodyStyle.width = _fnStringToCss( correction );
4082 divHeaderStyle.width = _fnStringToCss( correction );
4085 settings.nScrollFoot.style.width = _fnStringToCss( correction );
4093 /* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting
4094 * the scrollbar height from the visible display, rather than adding it on. We need to
4095 * set the height in order to sort this. Don't want to do it in any other browsers.
4098 divBodyStyle.height = _fnStringToCss( tableEl.offsetHeight+barWidth );
4102 /* Finally set the width's of the header and footer tables */
4103 var iOuterWidth = table.outerWidth();
4104 divHeaderTable[0].style.width = _fnStringToCss( iOuterWidth );
4105 divHeaderInnerStyle.width = _fnStringToCss( iOuterWidth );
4107 // Figure out if there are scrollbar present - if so then we need a the header and footer to
4108 // provide a bit more space to allow "overflow
" scrolling (i.e. past the scrollbar)
4109 var bScrolling = table.height() > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll
";
4110 var padding = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' );
4111 divHeaderInnerStyle[ padding ] = bScrolling ? barWidth+"px
" : "0px
";
4114 divFooterTable[0].style.width = _fnStringToCss( iOuterWidth );
4115 divFooterInner[0].style.width = _fnStringToCss( iOuterWidth );
4116 divFooterInner[0].style[padding] = bScrolling ? barWidth+"px
" : "0px
";
4119 // Correct DOM ordering for colgroup - comes before the thead
4120 table.children('colgroup').insertBefore( table.children('thead') );
4122 /* Adjust the position of the header in case we loose the y-scrollbar */
4125 // If sorting or filtering has occurred, jump the scrolling back to the top
4126 // only if we aren't holding the position
4127 if ( (settings.bSorted || settings.bFiltered) && ! settings._drawHold ) {
4128 divBodyEl.scrollTop = 0;
4142 function _fnApplyToChildren( fn, an1, an2 )
4144 var index=0, i=0, iLen=an1.length;
4147 while ( i < iLen ) {
4148 nNode1 = an1[i].firstChild;
4149 nNode2 = an2 ? an2[i].firstChild : null;
4152 if ( nNode1.nodeType === 1 ) {
4154 fn( nNode1, nNode2, index );
4157 fn( nNode1, index );
4163 nNode1 = nNode1.nextSibling;
4164 nNode2 = an2 ? nNode2.nextSibling : null;
4173 var __re_html_remove = /<.*?>/g;
4181 function _fnCalculateColumnWidths ( oSettings )
4184 table = oSettings.nTable,
4185 columns = oSettings.aoColumns,
4186 scroll = oSettings.oScroll,
4187 scrollY = scroll.sY,
4188 scrollX = scroll.sX,
4189 scrollXInner = scroll.sXInner,
4190 columnCount = columns.length,
4191 visibleColumns = _fnGetColumns( oSettings, 'bVisible' ),
4192 headerCells = $('th', oSettings.nTHead),
4193 tableWidthAttr = table.getAttribute('width'), // from DOM element
4194 tableContainer = table.parentNode,
4196 i, column, columnIdx, width, outerWidth,
4197 browser = oSettings.oBrowser,
4198 ie67 = browser.bScrollOversize;
4200 var styleWidth = table.style.width;
4201 if ( styleWidth && styleWidth.indexOf('%') !== -1 ) {
4202 tableWidthAttr = styleWidth;
4205 /* Convert any user input sizes into pixel sizes */
4206 for ( i=0 ; i<visibleColumns.length ; i++ ) {
4207 column = columns[ visibleColumns[i] ];
4209 if ( column.sWidth !== null ) {
4210 column.sWidth = _fnConvertToWidth( column.sWidthOrig, tableContainer );
4216 /* If the number of columns in the DOM equals the number that we have to
4217 * process in DataTables, then we can use the offsets that are created by
4218 * the web- browser. No custom sizes can be set in order for this to happen,
4219 * nor scrolling used
4221 if ( ie67 || ! userInputs && ! scrollX && ! scrollY &&
4222 columnCount == _fnVisbleColumns( oSettings ) &&
4223 columnCount == headerCells.length
4225 for ( i=0 ; i<columnCount ; i++ ) {
4226 var colIdx = _fnVisibleToColumnIndex( oSettings, i );
4228 if ( colIdx !== null ) {
4229 columns[ colIdx ].sWidth = _fnStringToCss( headerCells.eq(i).width() );
4235 // Otherwise construct a single row, worst case, table with the widest
4236 // node in the data, assign any user defined widths, then insert it into
4237 // the DOM and allow the browser to do all the hard work of calculating
4239 var tmpTable = $(table).clone() // don't use cloneNode - IE8 will remove events on the main table
4240 .css( 'visibility', 'hidden' )
4241 .removeAttr( 'id' );
4243 // Clean up the table body
4244 tmpTable.find('tbody tr').remove();
4245 var tr = $('<tr/>').appendTo( tmpTable.find('tbody') );
4247 // Clone the table header and footer - we can't use the header / footer
4248 // from the cloned table, since if scrolling is active, the table's
4249 // real header and footer are contained in different table tags
4250 tmpTable.find('thead, tfoot').remove();
4252 .append( $(oSettings.nTHead).clone() )
4253 .append( $(oSettings.nTFoot).clone() );
4255 // Remove any assigned widths from the footer (from scrolling)
4256 tmpTable.find('tfoot th, tfoot td').css('width', '');
4258 // Apply custom sizing to the cloned header
4259 headerCells = _fnGetUniqueThs( oSettings, tmpTable.find('thead')[0] );
4261 for ( i=0 ; i<visibleColumns.length ; i++ ) {
4262 column = columns[ visibleColumns[i] ];
4264 headerCells[i].style.width = column.sWidthOrig !== null && column.sWidthOrig !== '' ?
4265 _fnStringToCss( column.sWidthOrig ) :
4268 // For scrollX we need to force the column width otherwise the
4269 // browser will collapse it. If this width is smaller than the
4270 // width the column requires, then it will have no effect
4271 if ( column.sWidthOrig && scrollX ) {
4272 $( headerCells[i] ).append( $('<div/>').css( {
4273 width: column.sWidthOrig,
4282 // Find the widest cell for each column and put it into the table
4283 if ( oSettings.aoData.length ) {
4284 for ( i=0 ; i<visibleColumns.length ; i++ ) {
4285 columnIdx = visibleColumns[i];
4286 column = columns[ columnIdx ];
4288 $( _fnGetWidestNode( oSettings, columnIdx ) )
4290 .append( column.sContentPadding )
4295 // Tidy the temporary table - remove name attributes so there aren't
4296 // duplicated in the dom (radio elements for example)
4297 $('[name]', tmpTable).removeAttr('name');
4299 // Table has been built, attach to the document so we can work with it.
4300 // A holding element is used, positioned at the top of the container
4301 // with minimal height, so it has no effect on if the container scrolls
4302 // or not. Otherwise it might trigger scrolling when it actually isn't
4304 var holder = $('<div/>').css( scrollX || scrollY ?
4306 position: 'absolute',
4316 .appendTo( tableContainer );
4318 // When scrolling (X or Y) we want to set the width of the table as
4319 // appropriate. However, when not scrolling leave the table width as it
4320 // is. This results in slightly different, but I think correct behaviour
4321 if ( scrollX && scrollXInner ) {
4322 tmpTable.width( scrollXInner );
4324 else if ( scrollX ) {
4325 tmpTable.css( 'width', 'auto' );
4326 tmpTable.removeAttr('width');
4328 // If there is no width attribute or style, then allow the table to
4330 if ( tmpTable.width() < tableContainer.clientWidth && tableWidthAttr ) {
4331 tmpTable.width( tableContainer.clientWidth );
4334 else if ( scrollY ) {
4335 tmpTable.width( tableContainer.clientWidth );
4337 else if ( tableWidthAttr ) {
4338 tmpTable.width( tableWidthAttr );
4341 // Get the width of each column in the constructed table - we need to
4342 // know the inner width (so it can be assigned to the other table's
4343 // cells) and the outer width so we can calculate the full width of the
4344 // table. This is safe since DataTables requires a unique cell for each
4345 // column, but if ever a header can span multiple columns, this will
4346 // need to be modified.
4348 for ( i=0 ; i<visibleColumns.length ; i++ ) {
4349 var cell = $(headerCells[i]);
4350 var border = cell.outerWidth() - cell.width();
4352 // Use getBounding... where possible (not IE8-) because it can give
4353 // sub-pixel accuracy, which we then want to round up!
4354 var bounding = browser.bBounding ?
4355 Math.ceil( headerCells[i].getBoundingClientRect().width ) :
4358 // Total is tracked to remove any sub-pixel errors as the outerWidth
4359 // of the table might not equal the total given here (IE!).
4362 // Width for each column to use
4363 columns[ visibleColumns[i] ].sWidth = _fnStringToCss( bounding - border );
4366 table.style.width = _fnStringToCss( total );
4368 // Finished with the table - ditch it
4372 // If there is a width attr, we want to attach an event listener which
4373 // allows the table sizing to automatically adjust when the window is
4374 // resized. Use the width attr rather than CSS, since we can't know if the
4375 // CSS is a relative value or absolute - DOM read is always px.
4376 if ( tableWidthAttr ) {
4377 table.style.width = _fnStringToCss( tableWidthAttr );
4380 if ( (tableWidthAttr || scrollX) && ! oSettings._reszEvt ) {
4381 var bindResize = function () {
4382 $(window).bind('resize.DT-'+oSettings.sInstance, _fnThrottle( function () {
4383 _fnAdjustColumnSizing( oSettings );
4387 // IE6/7 will crash if we bind a resize event handler on page load.
4388 // To be removed in 1.11 which drops IE6/7 support
4390 setTimeout( bindResize, 1000 );
4396 oSettings._reszEvt = true;
4409 function _fnThrottle( fn, freq ) {
4411 frequency = freq !== undefined ? freq : 200,
4415 return function () {
4421 if ( last && now < last + frequency ) {
4422 clearTimeout( timer );
4424 timer = setTimeout( function () {
4426 fn.apply( that, args );
4431 fn.apply( that, args );
4444 function _fnConvertToWidth ( width, parent )
4451 .css( 'width', _fnStringToCss( width ) )
4452 .appendTo( parent || document.body );
4454 var val = n[0].offsetWidth;
4468 function _fnGetWidestNode( settings, colIdx )
4470 var idx = _fnGetMaxLenString( settings, colIdx );
4475 var data = settings.aoData[ idx ];
4476 return ! data.nTr ? // Might not have been created when deferred rendering
4477 $('<td/>').html( _fnGetCellData( settings, idx, colIdx, 'display' ) )[0] :
4478 data.anCells[ colIdx ];
4489 function _fnGetMaxLenString( settings, colIdx )
4491 var s, max=-1, maxIdx = -1;
4493 for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
4494 s = _fnGetCellData( settings, i, colIdx, 'display' )+'';
4495 s = s.replace( __re_html_remove, '' );
4496 s = s.replace( / /g, ' ' );
4498 if ( s.length > max ) {
4514 function _fnStringToCss( s )
4520 if ( typeof s == 'number' ) {
4526 // Check it has a unit character already
4527 return s.match(/\d$/) ?
4534 function _fnSortFlatten ( settings )
4540 aoColumns = settings.aoColumns,
4541 aDataSort, iCol, sType, srcCol,
4542 fixed = settings.aaSortingFixed,
4543 fixedObj = $.isPlainObject( fixed ),
4545 add = function ( a ) {
4546 if ( a.length && ! $.isArray( a[0] ) ) {
4548 nestedSort.push( a );
4552 $.merge( nestedSort, a );
4556 // Build the sort array, with pre-fix and post-fix options if they have been
4558 if ( $.isArray( fixed ) ) {
4562 if ( fixedObj && fixed.pre ) {
4566 add( settings.aaSorting );
4568 if (fixedObj && fixed.post ) {
4572 for ( i=0 ; i<nestedSort.length ; i++ )
4574 srcCol = nestedSort[i][0];
4575 aDataSort = aoColumns[ srcCol ].aDataSort;
4577 for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )
4579 iCol = aDataSort[k];
4580 sType = aoColumns[ iCol ].sType || 'string';
4582 if ( nestedSort[i]._idx === undefined ) {
4583 nestedSort[i]._idx = $.inArray( nestedSort[i][1], aoColumns[iCol].asSorting );
4589 dir: nestedSort[i][1],
4590 index: nestedSort[i]._idx,
4592 formatter: DataTable.ext.type.order[ sType+"-pre
" ]
4606 function _fnSort ( oSettings )
4609 i, ien, iLen, j, jLen, k, kLen,
4612 oExtSort = DataTable.ext.type.order,
4613 aoData = oSettings.aoData,
4614 aoColumns = oSettings.aoColumns,
4615 aDataSort, data, iCol, sType, oSort,
4618 displayMaster = oSettings.aiDisplayMaster,
4621 // Resolve any column types that are unknown due to addition or invalidation
4622 // @todo Can this be moved into a 'data-ready' handler which is called when
4623 // data is going to be used in the table?
4624 _fnColumnTypes( oSettings );
4626 aSort = _fnSortFlatten( oSettings );
4628 for ( i=0, ien=aSort.length ; i<ien ; i++ ) {
4631 // Track if we can use the fast sort algorithm
4632 if ( sortCol.formatter ) {
4636 // Load the data needed for the sort, for each cell
4637 _fnSortData( oSettings, sortCol.col );
4640 /* No sorting required if server-side or no sorting array */
4641 if ( _fnDataSource( oSettings ) != 'ssp' && aSort.length !== 0 )
4643 // Create a value - key array of the current row positions such that we can use their
4644 // current position during the sort, if values match, in order to perform stable sorting
4645 for ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) {
4646 aiOrig[ displayMaster[i] ] = i;
4649 /* Do the sort - here we want multi-column sorting based on a given data source (column)
4650 * and sorting function (from oSort) in a certain direction. It's reasonably complex to
4651 * follow on it's own, but this is what we want (example two column sorting):
4652 * fnLocalSorting = function(a,b){
4654 * iTest = oSort['string-asc']('data11', 'data12');
4657 * iTest = oSort['numeric-desc']('data21', 'data22');
4660 * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
4662 * Basically we have a test for each sorting column, if the data in that column is equal,
4663 * test the next column. If all columns match, then we use a numeric sort on the row
4664 * positions in the original data array to provide a stable sort.
4666 * Note - I know it seems excessive to have two sorting methods, but the first is around
4667 * 15% faster, so the second is only maintained for backwards compatibility with sorting
4668 * methods which do not have a pre-sort formatting function.
4670 if ( formatters === aSort.length ) {
4671 // All sort types have formatting functions
4672 displayMaster.sort( function ( a, b ) {
4674 x, y, k, test, sort,
4676 dataA = aoData[a]._aSortData,
4677 dataB = aoData[b]._aSortData;
4679 for ( k=0 ; k<len ; k++ ) {
4682 x = dataA[ sort.col ];
4683 y = dataB[ sort.col ];
4685 test = x<y ? -1 : x>y ? 1 : 0;
4687 return sort.dir === 'asc' ? test : -test;
4693 return x<y ? -1 : x>y ? 1 : 0;
4697 // Depreciated - remove in 1.11 (providing a plug-in option)
4698 // Not all sort types have formatting methods, so we have to call their sorting
4700 displayMaster.sort( function ( a, b ) {
4702 x, y, k, l, test, sort, fn,
4704 dataA = aoData[a]._aSortData,
4705 dataB = aoData[b]._aSortData;
4707 for ( k=0 ; k<len ; k++ ) {
4710 x = dataA[ sort.col ];
4711 y = dataB[ sort.col ];
4713 fn = oExtSort[ sort.type+"-
"+sort.dir ] || oExtSort[ "string-
"+sort.dir ];
4722 return x<y ? -1 : x>y ? 1 : 0;
4727 /* Tell the draw function that we have sorted the data */
4728 oSettings.bSorted = true;
4732 function _fnSortAria ( settings )
4736 var columns = settings.aoColumns;
4737 var aSort = _fnSortFlatten( settings );
4738 var oAria = settings.oLanguage.oAria;
4740 // ARIA attributes - need to loop all columns, to update all (removing old
4741 // attributes as needed)
4742 for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
4744 var col = columns[i];
4745 var asSorting = col.asSorting;
4746 var sTitle = col.sTitle.replace( /<.*?>/g, "" );
4749 // IE7 is throwing an error when setting these properties with jQuery's
4750 // attr() and removeAttr() methods...
4751 th.removeAttribute('aria-sort');
4753 /* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */
4754 if ( col.bSortable ) {
4755 if ( aSort.length > 0 && aSort[0].col == i ) {
4756 th.setAttribute('aria-sort', aSort[0].dir=="asc
" ? "ascending
" : "descending
" );
4757 nextSort = asSorting[ aSort[0].index+1 ] || asSorting[0];
4760 nextSort = asSorting[0];
4763 label = sTitle + ( nextSort === "asc
" ?
4764 oAria.sSortAscending :
4765 oAria.sSortDescending
4772 th.setAttribute('aria-label', label);
4787 function _fnSortListener ( settings, colIdx, append, callback )
4789 var col = settings.aoColumns[ colIdx ];
4790 var sorting = settings.aaSorting;
4791 var asSorting = col.asSorting;
4793 var next = function ( a, overflow ) {
4795 if ( idx === undefined ) {
4796 idx = $.inArray( a[1], asSorting );
4799 return idx+1 < asSorting.length ?
4806 // Convert to 2D array if needed
4807 if ( typeof sorting[0] === 'number' ) {
4808 sorting = settings.aaSorting = [ sorting ];
4811 // If appending the sort then we are multi-column sorting
4812 if ( append && settings.oFeatures.bSortMulti ) {
4813 // Are we already doing some kind of sort on this column?
4814 var sortIdx = $.inArray( colIdx, _pluck(sorting, '0') );
4816 if ( sortIdx !== -1 ) {
4817 // Yes, modify the sort
4818 nextSortIdx = next( sorting[sortIdx], true );
4820 if ( nextSortIdx === null && sorting.length === 1 ) {
4821 nextSortIdx = 0; // can't remove sorting completely
4824 if ( nextSortIdx === null ) {
4825 sorting.splice( sortIdx, 1 );
4828 sorting[sortIdx][1] = asSorting[ nextSortIdx ];
4829 sorting[sortIdx]._idx = nextSortIdx;
4833 // No sort on this column yet
4834 sorting.push( [ colIdx, asSorting[0], 0 ] );
4835 sorting[sorting.length-1]._idx = 0;
4838 else if ( sorting.length && sorting[0][0] == colIdx ) {
4839 // Single column - already sorting on this column, modify the sort
4840 nextSortIdx = next( sorting[0] );
4843 sorting[0][1] = asSorting[ nextSortIdx ];
4844 sorting[0]._idx = nextSortIdx;
4847 // Single column - sort only on this column
4849 sorting.push( [ colIdx, asSorting[0] ] );
4850 sorting[0]._idx = 0;
4853 // Run the sort by calling a full redraw
4854 _fnReDraw( settings );
4856 // callback used for async user interaction
4857 if ( typeof callback == 'function' ) {
4858 callback( settings );
4871 function _fnSortAttachListener ( settings, attachTo, colIdx, callback )
4873 var col = settings.aoColumns[ colIdx ];
4875 _fnBindAction( attachTo, {}, function (e) {
4876 /* If the column is not sortable - don't to anything */
4877 if ( col.bSortable === false ) {
4881 // If processing is enabled use a timeout to allow the processing
4882 // display to be shown - otherwise to it synchronously
4883 if ( settings.oFeatures.bProcessing ) {
4884 _fnProcessingDisplay( settings, true );
4886 setTimeout( function() {
4887 _fnSortListener( settings, colIdx, e.shiftKey, callback );
4889 // In server-side processing, the draw callback will remove the
4890 // processing display
4891 if ( _fnDataSource( settings ) !== 'ssp' ) {
4892 _fnProcessingDisplay( settings, false );
4897 _fnSortListener( settings, colIdx, e.shiftKey, callback );
4909 function _fnSortingClasses( settings )
4911 var oldSort = settings.aLastSort;
4912 var sortClass = settings.oClasses.sSortColumn;
4913 var sort = _fnSortFlatten( settings );
4914 var features = settings.oFeatures;
4917 if ( features.bSort && features.bSortClasses ) {
4918 // Remove old sorting classes
4919 for ( i=0, ien=oldSort.length ; i<ien ; i++ ) {
4920 colIdx = oldSort[i].src;
4922 // Remove column sorting
4923 $( _pluck( settings.aoData, 'anCells', colIdx ) )
4924 .removeClass( sortClass + (i<2 ? i+1 : 3) );
4927 // Add new column sorting
4928 for ( i=0, ien=sort.length ; i<ien ; i++ ) {
4929 colIdx = sort[i].src;
4931 $( _pluck( settings.aoData, 'anCells', colIdx ) )
4932 .addClass( sortClass + (i<2 ? i+1 : 3) );
4936 settings.aLastSort = sort;
4940 // Get the data to sort a column, be it from cache, fresh (populating the
4941 // cache), or from a sort formatter
4942 function _fnSortData( settings, idx )
4944 // Custom sorting function - provided by the sort data type
4945 var column = settings.aoColumns[ idx ];
4946 var customSort = DataTable.ext.order[ column.sSortDataType ];
4950 customData = customSort.call( settings.oInstance, settings, idx,
4951 _fnColumnIndexToVisible( settings, idx )
4955 // Use / populate cache
4957 var formatter = DataTable.ext.type.order[ column.sType+"-pre
" ];
4959 for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
4960 row = settings.aoData[i];
4962 if ( ! row._aSortData ) {
4963 row._aSortData = [];
4966 if ( ! row._aSortData[idx] || customSort ) {
4967 cellData = customSort ?
4968 customData[i] : // If there was a custom sort function, use data from there
4969 _fnGetCellData( settings, i, idx, 'sort' );
4971 row._aSortData[ idx ] = formatter ?
4972 formatter( cellData ) :
4985 function _fnSaveState ( settings )
4987 if ( !settings.oFeatures.bStateSave || settings.bDestroying )
4992 /* Store the interesting variables */
4995 start: settings._iDisplayStart,
4996 length: settings._iDisplayLength,
4997 order: $.extend( true, [], settings.aaSorting ),
4998 search: _fnSearchToCamel( settings.oPreviousSearch ),
4999 columns: $.map( settings.aoColumns, function ( col, i ) {
5001 visible: col.bVisible,
5002 search: _fnSearchToCamel( settings.aoPreSearchCols[i] )
5007 _fnCallbackFire( settings, "aoStateSaveParams
", 'stateSaveParams', [settings, state] );
5009 settings.oSavedState = state;
5010 settings.fnStateSaveCallback.call( settings.oInstance, settings, state );
5020 function _fnLoadState ( settings, oInit )
5023 var columns = settings.aoColumns;
5025 if ( ! settings.oFeatures.bStateSave ) {
5029 var state = settings.fnStateLoadCallback.call( settings.oInstance, settings );
5030 if ( ! state || ! state.time ) {
5034 /* Allow custom and plug-in manipulation functions to alter the saved data set and
5035 * cancelling of loading by returning false
5037 var abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, state] );
5038 if ( $.inArray( false, abStateLoad ) !== -1 ) {
5042 /* Reject old data */
5043 var duration = settings.iStateDuration;
5044 if ( duration > 0 && state.time < +new Date() - (duration*1000) ) {
5048 // Number of columns have changed - all bets are off, no restore of settings
5049 if ( columns.length !== state.columns.length ) {
5053 // Store the saved state so it might be accessed at any time
5054 settings.oLoadedState = $.extend( true, {}, state );
5056 // Restore key features - todo - for 1.11 this needs to be done by
5057 // subscribed events
5058 if ( state.start !== undefined ) {
5059 settings._iDisplayStart = state.start;
5060 settings.iInitDisplayStart = state.start;
5062 if ( state.length !== undefined ) {
5063 settings._iDisplayLength = state.length;
5067 if ( state.order !== undefined ) {
5068 settings.aaSorting = [];
5069 $.each( state.order, function ( i, col ) {
5070 settings.aaSorting.push( col[0] >= columns.length ?
5078 if ( state.search !== undefined ) {
5079 $.extend( settings.oPreviousSearch, _fnSearchToHung( state.search ) );
5083 for ( i=0, ien=state.columns.length ; i<ien ; i++ ) {
5084 var col = state.columns[i];
5087 if ( col.visible !== undefined ) {
5088 columns[i].bVisible = col.visible;
5092 if ( col.search !== undefined ) {
5093 $.extend( settings.aoPreSearchCols[i], _fnSearchToHung( col.search ) );
5097 _fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, state] );
5107 function _fnSettingsFromNode ( table )
5109 var settings = DataTable.settings;
5110 var idx = $.inArray( table, _pluck( settings, 'nTable' ) );
5126 function _fnLog( settings, level, msg, tn )
5128 msg = 'DataTables warning: '+
5129 (settings ? 'table id='+settings.sTableId+' - ' : '')+msg;
5132 msg += '. For more information about this error, please see '+
5133 'http://datatables.net/tn/'+tn;
5137 // Backwards compatibility pre 1.10
5138 var ext = DataTable.ext;
5139 var type = ext.sErrMode || ext.errMode;
5142 _fnCallbackFire( settings, null, 'error', [ settings, tn, msg ] );
5145 if ( type == 'alert' ) {
5148 else if ( type == 'throw' ) {
5149 throw new Error(msg);
5151 else if ( typeof type == 'function' ) {
5152 type( settings, tn, msg );
5155 else if ( window.console && console.log ) {
5169 function _fnMap( ret, src, name, mappedName )
5171 if ( $.isArray( name ) ) {
5172 $.each( name, function (i, val) {
5173 if ( $.isArray( val ) ) {
5174 _fnMap( ret, src, val[0], val[1] );
5177 _fnMap( ret, src, val );
5184 if ( mappedName === undefined ) {
5188 if ( src[name] !== undefined ) {
5189 ret[mappedName] = src[name];
5211 function _fnExtend( out, extender, breakRefs )
5215 for ( var prop in extender ) {
5216 if ( extender.hasOwnProperty(prop) ) {
5217 val = extender[prop];
5219 if ( $.isPlainObject( val ) ) {
5220 if ( ! $.isPlainObject( out[prop] ) ) {
5223 $.extend( true, out[prop], val );
5225 else if ( breakRefs && prop !== 'data' && prop !== 'aaData' && $.isArray(val) ) {
5226 out[prop] = val.slice();
5247 function _fnBindAction( n, oData, fn )
5250 .bind( 'click.DT', oData, function (e) {
5251 n.blur(); // Remove focus outline for mouse users
5254 .bind( 'keypress.DT', oData, function (e){
5255 if ( e.which === 13 ) {
5260 .bind( 'selectstart.DT', function () {
5261 /* Take the brutal approach to cancelling text selection */
5276 function _fnCallbackReg( oSettings, sStore, fn, sName )
5280 oSettings[sStore].push( {
5302 function _fnCallbackFire( settings, callbackArr, eventName, args )
5306 if ( callbackArr ) {
5307 ret = $.map( settings[callbackArr].slice().reverse(), function (val, i) {
5308 return val.fn.apply( settings.oInstance, args );
5312 if ( eventName !== null ) {
5313 var e = $.Event( eventName+'.dt' );
5315 $(settings.nTable).trigger( e, args );
5317 ret.push( e.result );
5324 function _fnLengthOverflow ( settings )
5327 start = settings._iDisplayStart,
5328 end = settings.fnDisplayEnd(),
5329 len = settings._iDisplayLength;
5331 /* If we have space to show extra rows (backing up from the end point - then do so */
5337 // Keep the start record on the current page
5338 start -= (start % len);
5340 if ( len === -1 || start < 0 )
5345 settings._iDisplayStart = start;
5349 function _fnRenderer( settings, type )
5351 var renderer = settings.renderer;
5352 var host = DataTable.ext.renderer[type];
5354 if ( $.isPlainObject( renderer ) && renderer[type] ) {
5355 // Specific renderer for this type. If available use it, otherwise use
5357 return host[renderer[type]] || host._;
5359 else if ( typeof renderer === 'string' ) {
5360 // Common renderer - if there is one available for this type use it,
5361 // otherwise use the default
5362 return host[renderer] || host._;
5378 function _fnDataSource ( settings )
5380 if ( settings.oFeatures.bServerSide ) {
5383 else if ( settings.ajax || settings.sAjaxSource ) {
5390 DataTable = function( options )
5428 this.$ = function ( sSelector, oOpts )
5430 return this.api(true).$( sSelector, oOpts );
5482 this._ = function ( sSelector, oOpts )
5484 return this.api(true).rows( sSelector, oOpts ).data();
5497 this.api = function ( traditional )
5499 return traditional ?
5501 _fnSettingsFromNode( this[ _ext.iApiIndex ] )
5545 this.fnAddData = function( data, redraw )
5547 var api = this.api( true );
5549 /* Check if we want to add multiple rows or not */
5550 var rows = $.isArray(data) && ( $.isArray(data[0]) || $.isPlainObject(data[0]) ) ?
5551 api.rows.add( data ) :
5552 api.row.add( data );
5554 if ( redraw === undefined || redraw ) {
5558 return rows.flatten().toArray();
5583 this.fnAdjustColumnSizing = function ( bRedraw )
5585 var api = this.api( true ).columns.adjust();
5586 var settings = api.settings()[0];
5587 var scroll = settings.oScroll;
5589 if ( bRedraw === undefined || bRedraw ) {
5592 else if ( scroll.sX !== "" || scroll.sY !== "" ) {
5593 /* If not redrawing, but scrolling, we want to apply the new column sizes anyway */
5594 _fnScrollDraw( settings );
5613 this.fnClearTable = function( bRedraw )
5615 var api = this.api( true ).clear();
5617 if ( bRedraw === undefined || bRedraw ) {
5647 this.fnClose = function( nTr )
5649 this.api( true ).row( nTr ).child.hide();
5671 this.fnDeleteRow = function( target, callback, redraw )
5673 var api = this.api( true );
5674 var rows = api.rows( target );
5675 var settings = rows.settings()[0];
5676 var data = settings.aoData[ rows[0][0] ];
5681 callback.call( this, settings, data );
5684 if ( redraw === undefined || redraw ) {
5706 this.fnDestroy = function ( remove )
5708 this.api( true ).destroy( remove );
5726 this.fnDraw = function( complete )
5728 // Note that this isn't an exact match to the old call to _fnDraw - it takes
5729 // into account the new data, but can hold position.
5730 this.api( true ).draw( complete );
5753 this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive )
5755 var api = this.api( true );
5757 if ( iColumn === null || iColumn === undefined ) {
5758 api.search( sInput, bRegex, bSmart, bCaseInsensitive );
5761 api.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive );
5805 this.fnGetData = function( src, col )
5807 var api = this.api( true );
5809 if ( src !== undefined ) {
5810 var type = src.nodeName ? src.nodeName.toLowerCase() : '';
5812 return col !== undefined || type == 'td' || type == 'th' ?
5813 api.cell( src, col ).data() :
5814 api.row( src ).data() || null;
5817 return api.data().toArray();
5839 this.fnGetNodes = function( iRow )
5841 var api = this.api( true );
5843 return iRow !== undefined ?
5844 api.row( iRow ).node() :
5845 api.rows().nodes().flatten().toArray();
5877 this.fnGetPosition = function( node )
5879 var api = this.api( true );
5880 var nodeName = node.nodeName.toUpperCase();
5882 if ( nodeName == 'TR' ) {
5883 return api.row( node ).index();
5885 else if ( nodeName == 'TD' || nodeName == 'TH' ) {
5886 var cell = api.cell( node ).index();
5921 this.fnIsOpen = function( nTr )
5923 return this.api( true ).row( nTr ).child.isShown();
5957 this.fnOpen = function( nTr, mHtml, sClass )
5959 return this.api( true )
5961 .child( mHtml, sClass )
5983 this.fnPageChange = function ( mAction, bRedraw )
5985 var api = this.api( true ).page( mAction );
5987 if ( bRedraw === undefined || bRedraw ) {
6009 this.fnSetColumnVis = function ( iCol, bShow, bRedraw )
6011 var api = this.api( true ).column( iCol ).visible( bShow );
6013 if ( bRedraw === undefined || bRedraw ) {
6014 api.columns.adjust().draw();
6035 this.fnSettings = function()
6037 return _fnSettingsFromNode( this[_ext.iApiIndex] );
6056 this.fnSort = function( aaSort )
6058 this.api( true ).order( aaSort ).draw();
6078 this.fnSortListener = function( nNode, iColumn, fnCallback )
6080 this.api( true ).order.listener( nNode, iColumn, fnCallback );
6106 this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction )
6108 var api = this.api( true );
6110 if ( iColumn === undefined || iColumn === null ) {
6111 api.row( mRow ).data( mData );
6114 api.cell( mRow, iColumn ).data( mData );
6117 if ( bAction === undefined || bAction ) {
6118 api.columns.adjust();
6121 if ( bRedraw === undefined || bRedraw ) {
6145 this.fnVersionCheck = _ext.fnVersionCheck;
6149 var emptyInit = options === undefined;
6150 var len = this.length;
6156 this.oApi = this.internal = _ext.internal;
6158 // Extend with old style plug-in API methods
6159 for ( var fn in DataTable.ext.internal ) {
6161 this[fn] = _fnExternApiFunc(fn);
6165 this.each(function() {
6166 // For each initialisation we want to give it a clean initialisation
6167 // object that can be bashed around
6169 var oInit = len > 1 ? // optimisation for single table case
6170 _fnExtend( o, options, true ) :
6173 /*global oInit,_that,emptyInit*/
6174 var i=0, iLen, j, jLen, k, kLen;
6175 var sId = this.getAttribute( 'id' );
6176 var bInitHandedOff = false;
6177 var defaults = DataTable.defaults;
6178 var $this = $(this);
6182 if ( this.nodeName.toLowerCase() != 'table' )
6184 _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 );
6188 /* Backwards compatibility for the defaults */
6189 _fnCompatOpts( defaults );
6190 _fnCompatCols( defaults.column );
6192 /* Convert the camel-case defaults to Hungarian */
6193 _fnCamelToHungarian( defaults, defaults, true );
6194 _fnCamelToHungarian( defaults.column, defaults.column, true );
6196 /* Setting up the initialisation object */
6197 _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ) );
6201 /* Check to see if we are re-initialising a table */
6202 var allSettings = DataTable.settings;
6203 for ( i=0, iLen=allSettings.length ; i<iLen ; i++ )
6205 var s = allSettings[i];
6207 /* Base check on table node */
6208 if ( s.nTable == this || s.nTHead.parentNode == this || (s.nTFoot && s.nTFoot.parentNode == this) )
6210 var bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve;
6211 var bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy;
6213 if ( emptyInit || bRetrieve )
6217 else if ( bDestroy )
6219 s.oInstance.fnDestroy();
6224 _fnLog( s, 0, 'Cannot reinitialise DataTable', 3 );
6229 /* If the element we are initialising has the same ID as a table which was previously
6230 * initialised, but the table nodes don't match (from before) then we destroy the old
6231 * instance by simply deleting it. This is under the assumption that the table has been
6232 * destroyed by other methods. Anyone using non-id selectors will need to do this manually
6234 if ( s.sTableId == this.id )
6236 allSettings.splice( i, 1 );
6241 /* Ensure the table has an ID - required for accessibility */
6242 if ( sId === null || sId === "" )
6244 sId = "DataTables_Table_
"+(DataTable.ext._unique++);
6248 /* Create the settings object for this table and set some of the default parameters */
6249 var oSettings = $.extend( true, {}, DataTable.models.oSettings, {
6250 "sDestroyWidth
": $this[0].style.width,
6254 oSettings.nTable = this;
6255 oSettings.oApi = _that.internal;
6256 oSettings.oInit = oInit;
6258 allSettings.push( oSettings );
6260 // Need to add the instance after the instance after the settings object has been added
6261 // to the settings array, so we can self reference the table instance if more than one
6262 oSettings.oInstance = (_that.length===1) ? _that : $this.dataTable();
6264 // Backwards compatibility, before we apply all the defaults
6265 _fnCompatOpts( oInit );
6267 if ( oInit.oLanguage )
6269 _fnLanguageCompat( oInit.oLanguage );
6272 // If the length menu is given, but the init display length is not, use the length menu
6273 if ( oInit.aLengthMenu && ! oInit.iDisplayLength )
6275 oInit.iDisplayLength = $.isArray( oInit.aLengthMenu[0] ) ?
6276 oInit.aLengthMenu[0][0] : oInit.aLengthMenu[0];
6279 // Apply the defaults and init options to make a single init object will all
6280 // options defined from defaults and instance options.
6281 oInit = _fnExtend( $.extend( true, {}, defaults ), oInit );
6284 // Map the initialisation options onto the settings object
6285 _fnMap( oSettings.oFeatures, oInit, [
6298 _fnMap( oSettings, oInit, [
6314 "fnStateLoadCallback
",
6315 "fnStateSaveCallback
",
6319 [ "iCookieDuration
", "iStateDuration
" ], // backwards compat
6320 [ "oSearch
", "oPreviousSearch
" ],
6321 [ "aoSearchCols
", "aoPreSearchCols
" ],
6322 [ "iDisplayLength
", "_iDisplayLength
" ],
6323 [ "bJQueryUI
", "bJUI
" ]
6325 _fnMap( oSettings.oScroll, oInit, [
6326 [ "sScrollX
", "sX
" ],
6327 [ "sScrollXInner
", "sXInner
" ],
6328 [ "sScrollY
", "sY
" ],
6329 [ "bScrollCollapse
", "bCollapse
" ]
6331 _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback
" );
6333 /* Callback functions which are array driven */
6334 _fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback, 'user' );
6335 _fnCallbackReg( oSettings, 'aoServerParams', oInit.fnServerParams, 'user' );
6336 _fnCallbackReg( oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams, 'user' );
6337 _fnCallbackReg( oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams, 'user' );
6338 _fnCallbackReg( oSettings, 'aoStateLoaded', oInit.fnStateLoaded, 'user' );
6339 _fnCallbackReg( oSettings, 'aoRowCallback', oInit.fnRowCallback, 'user' );
6340 _fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow, 'user' );
6341 _fnCallbackReg( oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback, 'user' );
6342 _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback, 'user' );
6343 _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete, 'user' );
6344 _fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback, 'user' );
6346 oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId );
6348 /* Browser support detection */
6349 _fnBrowserDetect( oSettings );
6351 var oClasses = oSettings.oClasses;
6353 // @todo Remove in 1.11
6354 if ( oInit.bJQueryUI )
6356 /* Use the JUI classes object for display. You could clone the oStdClasses object if
6357 * you want to have multiple tables with multiple independent classes
6359 $.extend( oClasses, DataTable.ext.oJUIClasses, oInit.oClasses );
6361 if ( oInit.sDom === defaults.sDom && defaults.sDom === "lfrtip
" )
6363 /* Set the DOM to use a layout suitable for jQuery UI's theming */
6364 oSettings.sDom = '<"H
"lfr>t<"F
"ip>';
6367 if ( ! oSettings.renderer ) {
6368 oSettings.renderer = 'jqueryui';
6370 else if ( $.isPlainObject( oSettings.renderer ) && ! oSettings.renderer.header ) {
6371 oSettings.renderer.header = 'jqueryui';
6376 $.extend( oClasses, DataTable.ext.classes, oInit.oClasses );
6378 $this.addClass( oClasses.sTable );
6381 if ( oSettings.iInitDisplayStart === undefined )
6383 /* Display start point, taking into account the save saving */
6384 oSettings.iInitDisplayStart = oInit.iDisplayStart;
6385 oSettings._iDisplayStart = oInit.iDisplayStart;
6388 if ( oInit.iDeferLoading !== null )
6390 oSettings.bDeferLoading = true;
6391 var tmp = $.isArray( oInit.iDeferLoading );
6392 oSettings._iRecordsDisplay = tmp ? oInit.iDeferLoading[0] : oInit.iDeferLoading;
6393 oSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1] : oInit.iDeferLoading;
6396 /* Language definitions */
6397 var oLanguage = oSettings.oLanguage;
6398 $.extend( true, oLanguage, oInit.oLanguage );
6400 if ( oLanguage.sUrl !== "" )
6402 /* Get the language definitions from a file - because this Ajax call makes the language
6403 * get async to the remainder of this function we use bInitHandedOff to indicate that
6404 * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor
6408 url: oLanguage.sUrl,
6409 success: function ( json ) {
6410 _fnLanguageCompat( json );
6411 _fnCamelToHungarian( defaults.oLanguage, json );
6412 $.extend( true, oLanguage, json );
6413 _fnInitialise( oSettings );
6415 error: function () {
6416 // Error occurred loading language file, continue on as best we can
6417 _fnInitialise( oSettings );
6420 bInitHandedOff = true;
6426 if ( oInit.asStripeClasses === null )
6428 oSettings.asStripeClasses =[
6429 oClasses.sStripeOdd,
6430 oClasses.sStripeEven
6434 /* Remove row stripe classes if they are already on the table row */
6435 var stripeClasses = oSettings.asStripeClasses;
6436 var rowOne = $this.children('tbody').find('tr').eq(0);
6437 if ( $.inArray( true, $.map( stripeClasses, function(el, i) {
6438 return rowOne.hasClass(el);
6440 $('tbody tr', this).removeClass( stripeClasses.join(' ') );
6441 oSettings.asDestroyStripes = stripeClasses.slice();
6446 * See if we should load columns automatically or use defined ones
6450 var nThead = this.getElementsByTagName('thead');
6451 if ( nThead.length !== 0 )
6453 _fnDetectHeader( oSettings.aoHeader, nThead[0] );
6454 anThs = _fnGetUniqueThs( oSettings );
6457 /* If not given a column array, generate one with nulls */
6458 if ( oInit.aoColumns === null )
6461 for ( i=0, iLen=anThs.length ; i<iLen ; i++ )
6463 aoColumnsInit.push( null );
6468 aoColumnsInit = oInit.aoColumns;
6471 /* Add the columns */
6472 for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
6474 _fnAddColumn( oSettings, anThs ? anThs[i] : null );
6477 /* Apply the column definitions */
6478 _fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, aoColumnsInit, function (iCol, oDef) {
6479 _fnColumnOptions( oSettings, iCol, oDef );
6482 /* HTML5 attribute detection - build an mData object automatically if the
6483 * attributes are found
6485 if ( rowOne.length ) {
6486 var a = function ( cell, name ) {
6487 return cell.getAttribute( 'data-'+name ) !== null ? name : null;
6490 $( rowOne[0] ).children('th, td').each( function (i, cell) {
6491 var col = oSettings.aoColumns[i];
6493 if ( col.mData === i ) {
6494 var sort = a( cell, 'sort' ) || a( cell, 'order' );
6495 var filter = a( cell, 'filter' ) || a( cell, 'search' );
6497 if ( sort !== null || filter !== null ) {
6500 sort: sort !== null ? i+'.@data-'+sort : undefined,
6501 type: sort !== null ? i+'.@data-'+sort : undefined,
6502 filter: filter !== null ? i+'.@data-'+filter : undefined
6505 _fnColumnOptions( oSettings, i );
6511 var features = oSettings.oFeatures;
6513 /* Must be done after everything which can be overridden by the state saving! */
6514 if ( oInit.bStateSave )
6516 features.bStateSave = true;
6517 _fnLoadState( oSettings, oInit );
6518 _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState, 'state_save' );
6524 * @todo For modularisation (1.11) this needs to do into a sort start up handler
6527 // If aaSorting is not defined, then we use the first indicator in asSorting
6528 // in case that has been altered, so the default sort reflects that option
6529 if ( oInit.aaSorting === undefined )
6531 var sorting = oSettings.aaSorting;
6532 for ( i=0, iLen=sorting.length ; i<iLen ; i++ )
6534 sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0];
6538 /* Do a first pass on the sorting classes (allows any size changes to be taken into
6539 * account, and also will apply sorting disabled classes if disabled
6541 _fnSortingClasses( oSettings );
6543 if ( features.bSort )
6545 _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
6546 if ( oSettings.bSorted ) {
6547 var aSort = _fnSortFlatten( oSettings );
6548 var sortedColumns = {};
6550 $.each( aSort, function (i, val) {
6551 sortedColumns[ val.src ] = val.dir;
6554 _fnCallbackFire( oSettings, null, 'order', [oSettings, aSort, sortedColumns] );
6555 _fnSortAria( oSettings );
6560 _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
6561 if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) {
6562 _fnSortingClasses( oSettings );
6569 * Cache the header, body and footer as required, creating them if needed
6572 // Work around for Webkit bug 83867 - store the caption-side before removing from doc
6573 var captions = $this.children('caption').each( function () {
6574 this._captionSide = $this.css('caption-side');
6577 var thead = $this.children('thead');
6578 if ( thead.length === 0 )
6580 thead = $('<thead/>').appendTo(this);
6582 oSettings.nTHead = thead[0];
6584 var tbody = $this.children('tbody');
6585 if ( tbody.length === 0 )
6587 tbody = $('<tbody/>').appendTo(this);
6589 oSettings.nTBody = tbody[0];
6591 var tfoot = $this.children('tfoot');
6592 if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") )
6594 // If we are a scrolling table, and no footer has been given, then we need to create
6595 // a tfoot element for the caption element to be appended to
6596 tfoot = $('<tfoot/>').appendTo(this);
6599 if ( tfoot.length === 0 || tfoot.children().length === 0 ) {
6600 $this.addClass( oClasses.sNoFooter );
6602 else if ( tfoot.length > 0 ) {
6603 oSettings.nTFoot = tfoot[0];
6604 _fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot );
6607 /* Check if there is data passing into the constructor */
6610 for ( i=0 ; i<oInit.aaData.length ; i++ )
6612 _fnAddData( oSettings, oInit.aaData[ i ] );
6615 else if ( oSettings.bDeferLoading || _fnDataSource( oSettings ) == 'dom' )
6617 /* Grab the data from the page - only do this when deferred loading or no Ajax
6618 * source since there is no point in reading the DOM data if we are then going
6619 * to replace it with Ajax data
6621 _fnAddTr( oSettings, $(oSettings.nTBody).children('tr') );
6624 /* Copy the data index array */
6625 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
6627 /* Initialisation complete - table can be drawn */
6628 oSettings.bInitialised = true;
6630 /* Check if we need to initialise the table (it might not have been handed off to the
6631 * language processor)
6633 if ( bInitHandedOff === false )
6635 _fnInitialise( oSettings );
6680 var __apiStruct = [];
6689 var __arrayProto = Array.prototype;
6712 var _toSettings = function ( mixed )
6715 var settings = DataTable.settings;
6716 var tables = $.map( settings, function (el, i) {
6723 else if ( mixed.nTable && mixed.oApi ) {
6724 // DataTables settings object
6727 else if ( mixed.nodeName && mixed.nodeName.toLowerCase() === 'table' ) {
6729 idx = $.inArray( mixed, tables );
6730 return idx !== -1 ? [ settings[idx] ] : null;
6732 else if ( mixed && typeof mixed.settings === 'function' ) {
6733 return mixed.settings().toArray();
6735 else if ( typeof mixed === 'string' ) {
6739 else if ( mixed instanceof $ ) {
6740 // jQuery object (also DataTables instance)
6745 return jq.map( function(i) {
6746 idx = $.inArray( this, tables );
6747 return idx !== -1 ? settings[idx] : null;
6807 _Api = function ( context, data )
6809 if ( ! (this instanceof _Api) ) {
6810 return new _Api( context, data );
6814 var ctxSettings = function ( o ) {
6815 var a = _toSettings( o );
6817 settings = settings.concat( a );
6821 if ( $.isArray( context ) ) {
6822 for ( var i=0, ien=context.length ; i<ien ; i++ ) {
6823 ctxSettings( context[i] );
6827 ctxSettings( context );
6830 // Remove duplicates
6831 this.context = _unique( settings );
6835 $.merge( this, data );
6845 _Api.extend( this, this, __apiStruct );
6848 DataTable.Api = _Api;
6850 // Don't destroy the existing prototype, just extend it. Required for jQuery 2's
6852 $.extend( _Api.prototype, {
6855 return this.count() !== 0;
6859 concat: __arrayProto.concat,
6862 context: [], // array of table settings objects
6867 return this.flatten().length;
6871 each: function ( fn )
6873 for ( var i=0, ien=this.length ; i<ien; i++ ) {
6874 fn.call( this, this[i], i, this );
6881 eq: function ( idx )
6883 var ctx = this.context;
6885 return ctx.length > idx ?
6886 new _Api( ctx[idx], this[idx] ) :
6891 filter: function ( fn )
6895 if ( __arrayProto.filter ) {
6896 a = __arrayProto.filter.call( this, fn, this );
6899 // Compatibility for browsers without EMCA-252-5 (JS 1.6)
6900 for ( var i=0, ien=this.length ; i<ien ; i++ ) {
6901 if ( fn.call( this, this[i], i, this ) ) {
6907 return new _Api( this.context, a );
6911 flatten: function ()
6914 return new _Api( this.context, a.concat.apply( a, this.toArray() ) );
6918 join: __arrayProto.join,
6921 indexOf: __arrayProto.indexOf || function (obj, start)
6923 for ( var i=(start || 0), ien=this.length ; i<ien ; i++ ) {
6924 if ( this[i] === obj ) {
6931 iterator: function ( flatten, type, fn, alwaysNew ) {
6935 context = this.context,
6937 selector = this.selector;
6939 // Argument shifting
6940 if ( typeof flatten === 'string' ) {
6947 for ( i=0, ien=context.length ; i<ien ; i++ ) {
6948 var apiInst = new _Api( context[i] );
6950 if ( type === 'table' ) {
6951 ret = fn.call( apiInst, context[i], i );
6953 if ( ret !== undefined ) {
6957 else if ( type === 'columns' || type === 'rows' ) {
6958 // this has same length as context - one entry for each table
6959 ret = fn.call( apiInst, context[i], this[i], i );
6961 if ( ret !== undefined ) {
6965 else if ( type === 'column' || type === 'column-rows' || type === 'row' || type === 'cell' ) {
6966 // columns and rows share the same structure.
6967 // 'this' is an array of column indexes for each context
6970 if ( type === 'column-rows' ) {
6971 rows = _selector_row_indexes( context[i], selector.opts );
6974 for ( j=0, jen=items.length ; j<jen ; j++ ) {
6977 if ( type === 'cell' ) {
6978 ret = fn.call( apiInst, context[i], item.row, item.column, i, j );
6981 ret = fn.call( apiInst, context[i], item, i, j, rows );
6984 if ( ret !== undefined ) {
6991 if ( a.length || alwaysNew ) {
6992 var api = new _Api( context, flatten ? a.concat.apply( [], a ) : a );
6993 var apiSelector = api.selector;
6994 apiSelector.rows = selector.rows;
6995 apiSelector.cols = selector.cols;
6996 apiSelector.opts = selector.opts;
7003 lastIndexOf: __arrayProto.lastIndexOf || function (obj, start)
7006 return this.indexOf.apply( this.toArray.reverse(), arguments );
7013 map: function ( fn )
7017 if ( __arrayProto.map ) {
7018 a = __arrayProto.map.call( this, fn, this );
7021 // Compatibility for browsers without EMCA-252-5 (JS 1.6)
7022 for ( var i=0, ien=this.length ; i<ien ; i++ ) {
7023 a.push( fn.call( this, this[i], i ) );
7027 return new _Api( this.context, a );
7031 pluck: function ( prop )
7033 return this.map( function ( el ) {
7038 pop: __arrayProto.pop,
7041 push: __arrayProto.push,
7044 // Does not return an API instance
7045 reduce: __arrayProto.reduce || function ( fn, init )
7047 return _fnReduce( this, fn, init, 0, this.length, 1 );
7051 reduceRight: __arrayProto.reduceRight || function ( fn, init )
7053 return _fnReduce( this, fn, init, this.length-1, -1, -1 );
7057 reverse: __arrayProto.reverse,
7060 // Object with rows, columns and opts
7064 shift: __arrayProto.shift,
7067 sort: __arrayProto.sort, // ? name - order?
7070 splice: __arrayProto.splice,
7073 toArray: function ()
7075 return __arrayProto.slice.call( this );
7085 toJQuery: function ()
7093 return new _Api( this.context, _unique(this) );
7097 unshift: __arrayProto.unshift
7101 _Api.extend = function ( scope, obj, ext )
7103 // Only extend API instances and static properties of the API
7104 if ( ! ext.length || ! obj || ( ! (obj instanceof _Api) && ! obj.__dt_wrapper ) ) {
7112 methodScoping = function ( scope, fn, struc ) {
7113 return function () {
7114 var ret = fn.apply( scope, arguments );
7117 _Api.extend( ret, ret, struc.methodExt );
7122 for ( i=0, ien=ext.length ; i<ien ; i++ ) {
7126 obj[ struct.name ] = typeof struct.val === 'function' ?
7127 methodScoping( scope, struct.val, struct ) :
7128 $.isPlainObject( struct.val ) ?
7132 obj[ struct.name ].__dt_wrapper = true;
7134 // Property extension
7135 _Api.extend( scope, obj[ struct.name ], struct.propExt );
7140 // @todo - Is there need for an augment function?
7141 // _Api.augment = function ( inst, name )
7143 // // Find src object in the structure from the name
7144 // var parts = name.split('.');
7146 // _Api.extend( inst, obj );
7152 // name: 'data' -- string - Property name
7153 // val: function () {}, -- function - Api method (or undefined if just an object
7154 // methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result
7155 // propExt: [ ... ] -- array - Array of Api object definitions to extend the property
7160 // methodExt: [ ... ],
7164 // val: function () {},
7165 // methodExt: [ ... ],
7173 _Api.register = _api_register = function ( name, val )
7175 if ( $.isArray( name ) ) {
7176 for ( var j=0, jen=name.length ; j<jen ; j++ ) {
7177 _Api.register( name[j], val );
7184 heir = name.split('.'),
7185 struct = __apiStruct,
7188 var find = function ( src, name ) {
7189 for ( var i=0, ien=src.length ; i<ien ; i++ ) {
7190 if ( src[i].name === name ) {
7197 for ( i=0, ien=heir.length ; i<ien ; i++ ) {
7198 method = heir[i].indexOf('()') !== -1;
7200 heir[i].replace('()', '') :
7203 var src = find( struct, key );
7214 if ( i === ien-1 ) {
7226 _Api.registerPlural = _api_registerPlural = function ( pluralName, singularName, val ) {
7227 _Api.register( pluralName, val );
7229 _Api.register( singularName, function () {
7230 var ret = val.apply( this, arguments );
7232 if ( ret === this ) {
7233 // Returned item is the API instance that was passed in, return it
7236 else if ( ret instanceof _Api ) {
7237 // New API instance returned, want the value from the first item
7238 // in the returned array for the singular result.
7240 $.isArray( ret[0] ) ?
7241 new _Api( ret.context, ret[0] ) : // Array results are 'enhanced'
7246 // Non-API return - just fire it back
7261 var __table_selector = function ( selector, a )
7263 // Integer is used to pick out a table by index
7264 if ( typeof selector === 'number' ) {
7265 return [ a[ selector ] ];
7268 // Perform a jQuery selector on the table nodes
7269 var nodes = $.map( a, function (el, i) {
7275 .map( function (i) {
7276 // Need to translate back from the table node to the settings
7277 var idx = $.inArray( this, nodes );
7296 _api_register( 'tables()', function ( selector ) {
7297 // A new instance is created if there was a selector specified
7299 new _Api( __table_selector( selector, this.context ) ) :
7304 _api_register( 'table()', function ( selector ) {
7305 var tables = this.tables( selector );
7306 var ctx = tables.context;
7308 // Truncate to the first matched table
7310 new _Api( ctx[0] ) :
7315 _api_registerPlural( 'tables().nodes()', 'table().node()' , function () {
7316 return this.iterator( 'table', function ( ctx ) {
7322 _api_registerPlural( 'tables().body()', 'table().body()' , function () {
7323 return this.iterator( 'table', function ( ctx ) {
7329 _api_registerPlural( 'tables().header()', 'table().header()' , function () {
7330 return this.iterator( 'table', function ( ctx ) {
7336 _api_registerPlural( 'tables().footer()', 'table().footer()' , function () {
7337 return this.iterator( 'table', function ( ctx ) {
7343 _api_registerPlural( 'tables().containers()', 'table().container()' , function () {
7344 return this.iterator( 'table', function ( ctx ) {
7345 return ctx.nTableWrapper;
7354 _api_register( 'draw()', function ( paging ) {
7355 return this.iterator( 'table', function ( settings ) {
7356 if ( paging === 'page' ) {
7357 _fnDraw( settings );
7360 if ( typeof paging === 'string' ) {
7361 paging = paging === 'full-hold' ?
7366 _fnReDraw( settings, paging===false );
7378 * Set the current page.
7380 * Note that if you attempt to show a page which does not exist, DataTables will
7381 * not throw an error, but rather reset the paging.
7383 * @param {integer|string} action The paging action to take. This can be one of:
7384 * * `integer` - The page index to jump to
7385 * * `string` - An action to take:
7386 * * `first` - Jump to first page.
7387 * * `next` - Jump to the next page
7388 * * `previous` - Jump to previous page
7389 * * `last` - Jump to the last page.
7390 * @returns {DataTables.Api} this
7392 _api_register( 'page()', function ( action ) {
7393 if ( action === undefined ) {
7394 return this.page.info().page; // not an expensive call
7397 // else, have an action to take on all tables
7398 return this.iterator( 'table', function ( settings ) {
7399 _fnPageChange( settings, action );
7422 _api_register( 'page.info()', function ( action ) {
7423 if ( this.context.length === 0 ) {
7428 settings = this.context[0],
7429 start = settings._iDisplayStart,
7430 len = settings.oFeatures.bPaginate ? settings._iDisplayLength : -1,
7431 visRecords = settings.fnRecordsDisplay(),
7435 "page
": all ? 0 : Math.floor( start / len ),
7436 "pages
": all ? 1 : Math.ceil( visRecords / len ),
7438 "end
": settings.fnDisplayEnd(),
7440 "recordsTotal
": settings.fnRecordsTotal(),
7441 "recordsDisplay
": visRecords,
7442 "serverSide
": _fnDataSource( settings ) === 'ssp'
7453 * Set the current page length.
7455 * @param {integer} Page length to set. Use `-1` to show all records.
7456 * @returns {DataTables.Api} this
7458 _api_register( 'page.len()', function ( len ) {
7459 // Note that we can't call this function 'length()' because `length`
7460 // is a Javascript property of functions which defines how many arguments
7461 // the function expects.
7462 if ( len === undefined ) {
7463 return this.context.length !== 0 ?
7464 this.context[0]._iDisplayLength :
7468 // else, set the page length
7469 return this.iterator( 'table', function ( settings ) {
7470 _fnLengthChange( settings, len );
7476 var __reload = function ( settings, holdPosition, callback ) {
7477 // Use the draw event to trigger a callback
7479 var api = new _Api( settings );
7481 api.one( 'draw', function () {
7482 callback( api.ajax.json() );
7486 if ( _fnDataSource( settings ) == 'ssp' ) {
7487 _fnReDraw( settings, holdPosition );
7490 _fnProcessingDisplay( settings, true );
7492 // Cancel an existing request
7493 var xhr = settings.jqXHR;
7494 if ( xhr && xhr.readyState !== 4 ) {
7499 _fnBuildAjax( settings, [], function( json ) {
7500 _fnClearTable( settings );
7502 var data = _fnAjaxDataSrc( settings, json );
7503 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
7504 _fnAddData( settings, data[i] );
7507 _fnReDraw( settings, holdPosition );
7508 _fnProcessingDisplay( settings, false );
7521 _api_register( 'ajax.json()', function () {
7522 var ctx = this.context;
7524 if ( ctx.length > 0 ) {
7528 // else return undefined;
7535 _api_register( 'ajax.params()', function () {
7536 var ctx = this.context;
7538 if ( ctx.length > 0 ) {
7539 return ctx[0].oAjaxData;
7542 // else return undefined;
7555 _api_register( 'ajax.reload()', function ( callback, resetPaging ) {
7556 return this.iterator( 'table', function (settings) {
7557 __reload( settings, resetPaging===false, callback );
7568 * Set the Ajax URL. Note that this will set the URL for all tables in the
7571 * @param {string} url URL to set.
7572 * @returns {DataTables.Api} this
7574 _api_register( 'ajax.url()', function ( url ) {
7575 var ctx = this.context;
7577 if ( url === undefined ) {
7579 if ( ctx.length === 0 ) {
7585 $.isPlainObject( ctx.ajax ) ?
7592 return this.iterator( 'table', function ( settings ) {
7593 if ( $.isPlainObject( settings.ajax ) ) {
7594 settings.ajax.url = url;
7597 settings.ajax = url;
7599 // No need to consider sAjaxSource here since DataTables gives priority
7600 // to `ajax` over `sAjaxSource`. So setting `ajax` here, renders any
7601 // value of `sAjaxSource` redundant.
7615 _api_register( 'ajax.url().load()', function ( callback, resetPaging ) {
7616 // Same as a reload, but makes sense to present it for easy access after a
7618 return this.iterator( 'table', function ( ctx ) {
7619 __reload( ctx, resetPaging===false, callback );
7626 var _selector_run = function ( type, selector, selectFn, settings, opts )
7631 selectorType = typeof selector;
7633 // Can't just check for isArray here, as an API or jQuery instance might be
7634 // given with their array like look
7635 if ( ! selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined ) {
7636 selector = [ selector ];
7639 for ( i=0, ien=selector.length ; i<ien ; i++ ) {
7640 a = selector[i] && selector[i].split ?
7641 selector[i].split(',') :
7644 for ( j=0, jen=a.length ; j<jen ; j++ ) {
7645 res = selectFn( typeof a[j] === 'string' ? $.trim(a[j]) : a[j] );
7647 if ( res && res.length ) {
7648 out = out.concat( res );
7653 // selector extensions
7654 var ext = _ext.selector[ type ];
7656 for ( i=0, ien=ext.length ; i<ien ; i++ ) {
7657 out = ext[i]( settings, opts, out );
7661 return _unique( out );
7665 var _selector_opts = function ( opts )
7671 // Backwards compatibility for 1.9- which used the terminology filter rather
7673 if ( opts.filter && opts.search === undefined ) {
7674 opts.search = opts.filter;
7685 var _selector_first = function ( inst )
7687 // Reduce the API instance to the first item found
7688 for ( var i=0, ien=inst.length ; i<ien ; i++ ) {
7689 if ( inst[i].length > 0 ) {
7690 // Assign the first element to the first item in the instance
7691 // and truncate the instance and context
7695 inst.context = [ inst.context[i] ];
7701 // Not found - return an empty instance
7707 var _selector_row_indexes = function ( settings, opts )
7711 displayFiltered = settings.aiDisplay,
7712 displayMaster = settings.aiDisplayMaster;
7715 search = opts.search, // none, applied, removed
7716 order = opts.order, // applied, current, index (original - compatibility with 1.9)
7717 page = opts.page; // all, current
7719 if ( _fnDataSource( settings ) == 'ssp' ) {
7720 // In server-side processing mode, most options are irrelevant since
7721 // rows not shown don't exist and the index order is the applied order
7722 // Removed is a special case - for consistency just return an empty
7724 return search === 'removed' ?
7726 _range( 0, displayMaster.length );
7728 else if ( page == 'current' ) {
7729 // Current page implies that order=current and fitler=applied, since it is
7730 // fairly senseless otherwise, regardless of what order and search actually
7732 for ( i=settings._iDisplayStart, ien=settings.fnDisplayEnd() ; i<ien ; i++ ) {
7733 a.push( displayFiltered[i] );
7736 else if ( order == 'current' || order == 'applied' ) {
7737 a = search == 'none' ?
7738 displayMaster.slice() : // no search
7739 search == 'applied' ?
7740 displayFiltered.slice() : // applied search
7741 $.map( displayMaster, function (el, i) { // removed search
7742 return $.inArray( el, displayFiltered ) === -1 ? el : null;
7745 else if ( order == 'index' || order == 'original' ) {
7746 for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
7747 if ( search == 'none' ) {
7750 else { // applied | removed
7751 tmp = $.inArray( i, displayFiltered );
7753 if ((tmp === -1 && search == 'removed') ||
7754 (tmp >= 0 && search == 'applied') )
7766 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7769 * {} - no selector - use all available rows
7770 * {integer} - row aoData index
7772 * {string} - jQuery selector to apply to the TR elements
7773 * {array} - jQuery array of nodes, or simply an array of TR nodes
7778 var __row_selector = function ( settings, selector, opts )
7780 var run = function ( sel ) {
7781 var selInt = _intVal( sel );
7784 // Short cut - selector is a number and no options provided (default is
7785 // all records, so no need to check if the index is in there, since it
7786 // must be - dev error if the index doesn't exist).
7787 if ( selInt !== null && ! opts ) {
7791 var rows = _selector_row_indexes( settings, opts );
7793 if ( selInt !== null && $.inArray( selInt, rows ) !== -1 ) {
7794 // Selector - integer
7802 // Selector - function
7803 if ( typeof sel === 'function' ) {
7804 return $.map( rows, function (idx) {
7805 var row = settings.aoData[ idx ];
7806 return sel( idx, row._aData, row.nTr ) ? idx : null;
7810 // Get nodes in the order from the `rows` array with null values removed
7811 var nodes = _removeEmpty(
7812 _pluck_order( settings.aoData, rows, 'nTr' )
7816 if ( sel.nodeName ) {
7817 if ( sel._DT_RowIndex !== undefined ) {
7818 return [ sel._DT_RowIndex ]; // Property added by DT for fast lookup
7820 else if ( sel._DT_CellIndex ) {
7821 return [ sel._DT_CellIndex.row ];
7824 var host = $(sel).closest('*[data-dt-row]');
7825 return host.length ?
7826 [ host.data('dt-row') ] :
7831 // ID selector. Want to always be able to select rows by id, regardless
7832 // of if the tr element has been created or not, so can't rely upon
7833 // jQuery here - hence a custom implementation. This does not match
7834 // Sizzle's fast selector or HTML4 - in HTML5 the ID can be anything,
7835 // but to select it using a CSS selector engine (like Sizzle or
7836 // querySelect) it would need to need to be escaped for some characters.
7837 // DataTables simplifies this for row selectors since you can select
7838 // only a row. A # indicates an id any anything that follows is the id -
7840 if ( typeof sel === 'string' && sel.charAt(0) === '#' ) {
7841 // get row index from id
7842 var rowObj = settings.aIds[ sel.replace( /^#/, '' ) ];
7843 if ( rowObj !== undefined ) {
7844 return [ rowObj.idx ];
7847 // need to fall through to jQuery in case there is DOM id that
7851 // Selector - jQuery selector string, array of nodes or jQuery object/
7852 // As jQuery's .filter() allows jQuery objects to be passed in filter,
7853 // it also allows arrays, so this will cope with all three options
7857 return this._DT_RowIndex;
7862 return _selector_run( 'row', selector, run, settings, opts );
7866 _api_register( 'rows()', function ( selector, opts ) {
7867 // argument shifting
7868 if ( selector === undefined ) {
7871 else if ( $.isPlainObject( selector ) ) {
7876 opts = _selector_opts( opts );
7878 var inst = this.iterator( 'table', function ( settings ) {
7879 return __row_selector( settings, selector, opts );
7882 // Want argument shifting here and in __row_selector?
7883 inst.selector.rows = selector;
7884 inst.selector.opts = opts;
7889 _api_register( 'rows().nodes()', function () {
7890 return this.iterator( 'row', function ( settings, row ) {
7891 return settings.aoData[ row ].nTr || undefined;
7895 _api_register( 'rows().data()', function () {
7896 return this.iterator( true, 'rows', function ( settings, rows ) {
7897 return _pluck_order( settings.aoData, rows, '_aData' );
7901 _api_registerPlural( 'rows().cache()', 'row().cache()', function ( type ) {
7902 return this.iterator( 'row', function ( settings, row ) {
7903 var r = settings.aoData[ row ];
7904 return type === 'search' ? r._aFilterData : r._aSortData;
7908 _api_registerPlural( 'rows().invalidate()', 'row().invalidate()', function ( src ) {
7909 return this.iterator( 'row', function ( settings, row ) {
7910 _fnInvalidate( settings, row, src );
7914 _api_registerPlural( 'rows().indexes()', 'row().index()', function () {
7915 return this.iterator( 'row', function ( settings, row ) {
7920 _api_registerPlural( 'rows().ids()', 'row().id()', function ( hash ) {
7922 var context = this.context;
7924 // `iterator` will drop undefined values, but in this case we want them
7925 for ( var i=0, ien=context.length ; i<ien ; i++ ) {
7926 for ( var j=0, jen=this[i].length ; j<jen ; j++ ) {
7927 var id = context[i].rowIdFn( context[i].aoData[ this[i][j] ]._aData );
7928 a.push( (hash === true ? '#' : '' )+ id );
7932 return new _Api( context, a );
7935 _api_registerPlural( 'rows().remove()', 'row().remove()', function () {
7938 this.iterator( 'row', function ( settings, row, thatIdx ) {
7939 var data = settings.aoData;
7940 var rowData = data[ row ];
7942 var loopRow, loopCells;
7944 data.splice( row, 1 );
7946 // Update the cached indexes
7947 for ( i=0, ien=data.length ; i<ien ; i++ ) {
7949 loopCells = loopRow.anCells;
7952 if ( loopRow.nTr !== null ) {
7953 loopRow.nTr._DT_RowIndex = i;
7957 if ( loopCells !== null ) {
7958 for ( j=0, jen=loopCells.length ; j<jen ; j++ ) {
7959 loopCells[j]._DT_CellIndex.row = i;
7964 // Delete from the display arrays
7965 _fnDeleteIndex( settings.aiDisplayMaster, row );
7966 _fnDeleteIndex( settings.aiDisplay, row );
7967 _fnDeleteIndex( that[ thatIdx ], row, false ); // maintain local indexes
7969 // Check for an 'overflow' they case for displaying the table
7970 _fnLengthOverflow( settings );
7972 // Remove the row's ID reference if there is one
7973 var id = settings.rowIdFn( rowData._aData );
7974 if ( id !== undefined ) {
7975 delete settings.aIds[ id ];
7979 this.iterator( 'table', function ( settings ) {
7980 for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
7981 settings.aoData[i].idx = i;
7989 _api_register( 'rows.add()', function ( rows ) {
7990 var newRows = this.iterator( 'table', function ( settings ) {
7994 for ( i=0, ien=rows.length ; i<ien ; i++ ) {
7997 if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
7998 out.push( _fnAddTr( settings, row )[0] );
8001 out.push( _fnAddData( settings, row ) );
8008 // Return an Api.rows() extended instance, so rows().nodes() etc can be used
8009 var modRows = this.rows( -1 );
8011 $.merge( modRows, newRows );
8023 _api_register( 'row()', function ( selector, opts ) {
8024 return _selector_first( this.rows( selector, opts ) );
8028 _api_register( 'row().data()', function ( data ) {
8029 var ctx = this.context;
8031 if ( data === undefined ) {
8033 return ctx.length && this.length ?
8034 ctx[0].aoData[ this[0] ]._aData :
8039 ctx[0].aoData[ this[0] ]._aData = data;
8041 // Automatically invalidate
8042 _fnInvalidate( ctx[0], this[0], 'data' );
8048 _api_register( 'row().node()', function () {
8049 var ctx = this.context;
8051 return ctx.length && this.length ?
8052 ctx[0].aoData[ this[0] ].nTr || null :
8057 _api_register( 'row.add()', function ( row ) {
8058 // Allow a jQuery object to be passed in - only a single row is added from
8059 // it though - the first element in the set
8060 if ( row instanceof $ && row.length ) {
8064 var rows = this.iterator( 'table', function ( settings ) {
8065 if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
8066 return _fnAddTr( settings, row )[0];
8068 return _fnAddData( settings, row );
8071 // Return an Api.rows() extended instance, with the newly added row selected
8072 return this.row( rows[0] );
8077 var __details_add = function ( ctx, row, data, klass )
8079 // Convert to array of TR elements
8081 var addRow = function ( r, k ) {
8082 // Recursion to allow for arrays of jQuery objects
8083 if ( $.isArray( r ) || r instanceof $ ) {
8084 for ( var i=0, ien=r.length ; i<ien ; i++ ) {
8090 // If we get a TR element, then just add it directly - up to the dev
8091 // to add the correct number of columns etc
8092 if ( r.nodeName && r.nodeName.toLowerCase() === 'tr' ) {
8096 // Otherwise create a row with a wrapper
8097 var created = $('<tr><td/></tr>').addClass( k );
8101 [0].colSpan = _fnVisbleColumns( ctx );
8103 rows.push( created[0] );
8107 addRow( data, klass );
8109 if ( row._details ) {
8110 row._details.remove();
8113 row._details = $(rows);
8115 // If the children were already shown, that state should be retained
8116 if ( row._detailsShow ) {
8117 row._details.insertAfter( row.nTr );
8122 var __details_remove = function ( api, idx )
8124 var ctx = api.context;
8127 var row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ];
8129 if ( row && row._details ) {
8130 row._details.remove();
8132 row._detailsShow = undefined;
8133 row._details = undefined;
8139 var __details_display = function ( api, show ) {
8140 var ctx = api.context;
8142 if ( ctx.length && api.length ) {
8143 var row = ctx[0].aoData[ api[0] ];
8145 if ( row._details ) {
8146 row._detailsShow = show;
8149 row._details.insertAfter( row.nTr );
8152 row._details.detach();
8155 __details_events( ctx[0] );
8161 var __details_events = function ( settings )
8163 var api = new _Api( settings );
8164 var namespace = '.dt.DT_details';
8165 var drawEvent = 'draw'+namespace;
8166 var colvisEvent = 'column-visibility'+namespace;
8167 var destroyEvent = 'destroy'+namespace;
8168 var data = settings.aoData;
8170 api.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent );
8172 if ( _pluck( data, '_details' ).length > 0 ) {
8173 // On each draw, insert the required elements into the document
8174 api.on( drawEvent, function ( e, ctx ) {
8175 if ( settings !== ctx ) {
8179 api.rows( {page:'current'} ).eq(0).each( function (idx) {
8180 // Internal data grab
8181 var row = data[ idx ];
8183 if ( row._detailsShow ) {
8184 row._details.insertAfter( row.nTr );
8189 // Column visibility change - update the colspan
8190 api.on( colvisEvent, function ( e, ctx, idx, vis ) {
8191 if ( settings !== ctx ) {
8195 // Update the colspan for the details rows (note, only if it already has
8197 var row, visible = _fnVisbleColumns( ctx );
8199 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
8202 if ( row._details ) {
8203 row._details.children('td[colspan]').attr('colspan', visible );
8208 // Table destroyed - nuke any child rows
8209 api.on( destroyEvent, function ( e, ctx ) {
8210 if ( settings !== ctx ) {
8214 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
8215 if ( data[i]._details ) {
8216 __details_remove( api, i );
8223 // Strings for the method names to help minification
8225 var _child_obj = _emp+'row().child';
8226 var _child_mth = _child_obj+'()';
8231 // jQuery or array of any of the above
8232 _api_register( _child_mth, function ( data, klass ) {
8233 var ctx = this.context;
8235 if ( data === undefined ) {
8237 return ctx.length && this.length ?
8238 ctx[0].aoData[ this[0] ]._details :
8241 else if ( data === true ) {
8245 else if ( data === false ) {
8247 __details_remove( this );
8249 else if ( ctx.length && this.length ) {
8251 __details_add( ctx[0], ctx[0].aoData[ this[0] ], data, klass );
8259 _child_obj+'.show()',
8260 _child_mth+'.show()' // only when `child()` was called with parameters (without
8261 ], function ( show ) { // it returns an object and this method is not executed)
8262 __details_display( this, true );
8268 _child_obj+'.hide()',
8269 _child_mth+'.hide()' // only when `child()` was called with parameters (without
8270 ], function () { // it returns an object and this method is not executed)
8271 __details_display( this, false );
8277 _child_obj+'.remove()',
8278 _child_mth+'.remove()' // only when `child()` was called with parameters (without
8279 ], function () { // it returns an object and this method is not executed)
8280 __details_remove( this );
8285 _api_register( _child_obj+'.isShown()', function () {
8286 var ctx = this.context;
8288 if ( ctx.length && this.length ) {
8289 // _detailsShown as false or undefined will fall through to return false
8290 return ctx[0].aoData[ this[0] ]._detailsShow || false;
8297 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8300 * {integer} - column index (>=0 count from left, <0 count from right)
8301 * "{integer}:visIdx
" - visible column index (i.e. translate to column index) (>=0 count from left, <0 count from right)
8302 * "{integer}:visible
" - alias for {integer}:visIdx (>=0 count from left, <0 count from right)
8303 * "{
string}:name
" - column name
8304 * "{
string}
" - jQuery selector on column header nodes
8308 // can be an array of these items, comma separated list, or an array of comma
8311 var __re_column_selector = /^(.+):(name|visIdx|visible)$/;
8314 // r1 and r2 are redundant - but it means that the parameters match for the
8315 // iterator callback in columns().data()
8316 var __columnData = function ( settings, column, r1, r2, rows ) {
8318 for ( var row=0, ien=rows.length ; row<ien ; row++ ) {
8319 a.push( _fnGetCellData( settings, rows[row], column ) );
8325 var __column_selector = function ( settings, selector, opts )
8328 columns = settings.aoColumns,
8329 names = _pluck( columns, 'sName' ),
8330 nodes = _pluck( columns, 'nTh' );
8332 var run = function ( s ) {
8333 var selInt = _intVal( s );
8337 return _range( columns.length );
8341 if ( selInt !== null ) {
8342 return [ selInt >= 0 ?
8343 selInt : // Count from left
8344 columns.length + selInt // Count from right (+ because its a negative value)
8348 // Selector = function
8349 if ( typeof s === 'function' ) {
8350 var rows = _selector_row_indexes( settings, opts );
8352 return $.map( columns, function (col, idx) {
8355 __columnData( settings, idx, 0, 0, rows ),
8361 // jQuery or string selector
8362 var match = typeof s === 'string' ?
8363 s.match( __re_column_selector ) :
8367 switch( match[2] ) {
8370 var idx = parseInt( match[1], 10 );
8371 // Visible index given, convert to column index
8373 // Counting from the right
8374 var visColumns = $.map( columns, function (col,i) {
8375 return col.bVisible ? i : null;
8377 return [ visColumns[ visColumns.length + idx ] ];
8379 // Counting from the left
8380 return [ _fnVisibleToColumnIndex( settings, idx ) ];
8383 // match by name. `names` is column index complete and in order
8384 return $.map( names, function (name, i) {
8385 return name === match[1] ? i : null;
8393 // Cell in the table body
8394 if ( s.nodeName && s._DT_CellIndex ) {
8395 return [ s._DT_CellIndex.column ];
8398 // jQuery selector on the TH elements for the columns
8399 var jqResult = $( nodes )
8402 return $.inArray( this, nodes ); // `nodes` is column index complete and in order
8406 if ( jqResult.length || ! s.nodeName ) {
8410 // Otherwise a node which might have a `dt-column` data attribute, or be
8411 // a child or such an element
8412 var host = $(s).closest('*[data-dt-column]');
8413 return host.length ?
8414 [ host.data('dt-column') ] :
8418 return _selector_run( 'column', selector, run, settings, opts );
8422 var __setColumnVis = function ( settings, column, vis, recalc ) {
8424 cols = settings.aoColumns,
8425 col = cols[ column ],
8426 data = settings.aoData,
8427 row, cells, i, ien, tr;
8430 if ( vis === undefined ) {
8431 return col.bVisible;
8436 if ( col.bVisible === vis ) {
8442 // Need to decide if we should use appendChild or insertBefore
8443 var insertBefore = $.inArray( true, _pluck(cols, 'bVisible'), column+1 );
8445 for ( i=0, ien=data.length ; i<ien ; i++ ) {
8447 cells = data[i].anCells;
8450 // insertBefore can act like appendChild if 2nd arg is null
8451 tr.insertBefore( cells[ column ], cells[ insertBefore ] || null );
8457 $( _pluck( settings.aoData, 'anCells', column ) ).detach();
8462 _fnDrawHead( settings, settings.aoHeader );
8463 _fnDrawHead( settings, settings.aoFooter );
8465 if ( recalc === undefined || recalc ) {
8466 // Automatically adjust column sizing
8467 _fnAdjustColumnSizing( settings );
8470 _fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis, recalc] );
8472 _fnSaveState( settings );
8476 _api_register( 'columns()', function ( selector, opts ) {
8477 // argument shifting
8478 if ( selector === undefined ) {
8481 else if ( $.isPlainObject( selector ) ) {
8486 opts = _selector_opts( opts );
8488 var inst = this.iterator( 'table', function ( settings ) {
8489 return __column_selector( settings, selector, opts );
8492 // Want argument shifting here and in _row_selector?
8493 inst.selector.cols = selector;
8494 inst.selector.opts = opts;
8499 _api_registerPlural( 'columns().header()', 'column().header()', function ( selector, opts ) {
8500 return this.iterator( 'column', function ( settings, column ) {
8501 return settings.aoColumns[column].nTh;
8505 _api_registerPlural( 'columns().footer()', 'column().footer()', function ( selector, opts ) {
8506 return this.iterator( 'column', function ( settings, column ) {
8507 return settings.aoColumns[column].nTf;
8511 _api_registerPlural( 'columns().data()', 'column().data()', function () {
8512 return this.iterator( 'column-rows', __columnData, 1 );
8515 _api_registerPlural( 'columns().dataSrc()', 'column().dataSrc()', function () {
8516 return this.iterator( 'column', function ( settings, column ) {
8517 return settings.aoColumns[column].mData;
8521 _api_registerPlural( 'columns().cache()', 'column().cache()', function ( type ) {
8522 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
8523 return _pluck_order( settings.aoData, rows,
8524 type === 'search' ? '_aFilterData' : '_aSortData', column
8529 _api_registerPlural( 'columns().nodes()', 'column().nodes()', function () {
8530 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
8531 return _pluck_order( settings.aoData, rows, 'anCells', column ) ;
8535 _api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) {
8536 return this.iterator( 'column', function ( settings, column ) {
8537 if ( vis === undefined ) {
8538 return settings.aoColumns[ column ].bVisible;
8540 __setColumnVis( settings, column, vis, calc );
8544 _api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) {
8545 return this.iterator( 'column', function ( settings, column ) {
8546 return type === 'visible' ?
8547 _fnColumnIndexToVisible( settings, column ) :
8552 _api_register( 'columns.adjust()', function () {
8553 return this.iterator( 'table', function ( settings ) {
8554 _fnAdjustColumnSizing( settings );
8558 _api_register( 'column.index()', function ( type, idx ) {
8559 if ( this.context.length !== 0 ) {
8560 var ctx = this.context[0];
8562 if ( type === 'fromVisible' || type === 'toData' ) {
8563 return _fnVisibleToColumnIndex( ctx, idx );
8565 else if ( type === 'fromData' || type === 'toVisible' ) {
8566 return _fnColumnIndexToVisible( ctx, idx );
8571 _api_register( 'column()', function ( selector, opts ) {
8572 return _selector_first( this.columns( selector, opts ) );
8578 var __cell_selector = function ( settings, selector, opts )
8580 var data = settings.aoData;
8581 var rows = _selector_row_indexes( settings, opts );
8582 var cells = _removeEmpty( _pluck_order( data, rows, 'anCells' ) );
8583 var allCells = $( [].concat.apply([], cells) );
8585 var columns = settings.aoColumns.length;
8586 var a, i, ien, j, o, host;
8588 var run = function ( s ) {
8589 var fnSelector = typeof s === 'function';
8591 if ( s === null || s === undefined || fnSelector ) {
8592 // All cells and function selectors
8595 for ( i=0, ien=rows.length ; i<ien ; i++ ) {
8598 for ( j=0 ; j<columns ; j++ ) {
8605 // Selector - function
8608 if ( s( o, _fnGetCellData(settings, row, j), host.anCells ? host.anCells[j] : null ) ) {
8623 if ( $.isPlainObject( s ) ) {
8627 // Selector - jQuery filtered cells
8628 var jqResult = allCells
8630 .map( function (i, el) {
8631 return { // use a new object, in case someone changes the values
8632 row: el._DT_CellIndex.row,
8633 column: el._DT_CellIndex.column
8638 if ( jqResult.length || ! s.nodeName ) {
8642 // Otherwise the selector is a node, and there is one last option - the
8643 // element might be a child of an element which has dt-row and dt-column
8645 host = $(s).closest('*[data-dt-row]');
8646 return host.length ?
8648 row: host.data('dt-row'),
8649 column: host.data('dt-column')
8654 return _selector_run( 'cell', selector, run, settings, opts );
8660 _api_register( 'cells()', function ( rowSelector, columnSelector, opts ) {
8661 // Argument shifting
8662 if ( $.isPlainObject( rowSelector ) ) {
8664 if ( rowSelector.row === undefined ) {
8665 // Selector options in first parameter
8670 // Cell index objects in first parameter
8671 opts = columnSelector;
8672 columnSelector = null;
8675 if ( $.isPlainObject( columnSelector ) ) {
8676 opts = columnSelector;
8677 columnSelector = null;
8681 if ( columnSelector === null || columnSelector === undefined ) {
8682 return this.iterator( 'table', function ( settings ) {
8683 return __cell_selector( settings, rowSelector, _selector_opts( opts ) );
8687 // Row + column selector
8688 var columns = this.columns( columnSelector, opts );
8689 var rows = this.rows( rowSelector, opts );
8690 var a, i, ien, j, jen;
8692 var cells = this.iterator( 'table', function ( settings, idx ) {
8695 for ( i=0, ien=rows[idx].length ; i<ien ; i++ ) {
8696 for ( j=0, jen=columns[idx].length ; j<jen ; j++ ) {
8699 column: columns[idx][j]
8707 $.extend( cells.selector, {
8708 cols: columnSelector,
8717 _api_registerPlural( 'cells().nodes()', 'cell().node()', function () {
8718 return this.iterator( 'cell', function ( settings, row, column ) {
8719 var data = settings.aoData[ row ];
8721 return data && data.anCells ?
8722 data.anCells[ column ] :
8728 _api_register( 'cells().data()', function () {
8729 return this.iterator( 'cell', function ( settings, row, column ) {
8730 return _fnGetCellData( settings, row, column );
8735 _api_registerPlural( 'cells().cache()', 'cell().cache()', function ( type ) {
8736 type = type === 'search' ? '_aFilterData' : '_aSortData';
8738 return this.iterator( 'cell', function ( settings, row, column ) {
8739 return settings.aoData[ row ][ type ][ column ];
8744 _api_registerPlural( 'cells().render()', 'cell().render()', function ( type ) {
8745 return this.iterator( 'cell', function ( settings, row, column ) {
8746 return _fnGetCellData( settings, row, column, type );
8751 _api_registerPlural( 'cells().indexes()', 'cell().index()', function () {
8752 return this.iterator( 'cell', function ( settings, row, column ) {
8756 columnVisible: _fnColumnIndexToVisible( settings, column )
8762 _api_registerPlural( 'cells().invalidate()', 'cell().invalidate()', function ( src ) {
8763 return this.iterator( 'cell', function ( settings, row, column ) {
8764 _fnInvalidate( settings, row, src, column );
8770 _api_register( 'cell()', function ( rowSelector, columnSelector, opts ) {
8771 return _selector_first( this.cells( rowSelector, columnSelector, opts ) );
8775 _api_register( 'cell().data()', function ( data ) {
8776 var ctx = this.context;
8779 if ( data === undefined ) {
8781 return ctx.length && cell.length ?
8782 _fnGetCellData( ctx[0], cell[0].row, cell[0].column ) :
8787 _fnSetCellData( ctx[0], cell[0].row, cell[0].column, data );
8788 _fnInvalidate( ctx[0], cell[0].row, 'data', cell[0].column );
8806 * Set the ordering for the table.
8808 * @param {integer} order Column index to sort upon.
8809 * @param {string} direction Direction of the sort to be applied (`asc` or `desc`)
8810 * @returns {DataTables.Api} this
8812 * Set the ordering for the table.
8814 * @param {array} order 1D array of sorting information to be applied.
8815 * @param {array} [...] Optional additional sorting conditions
8816 * @returns {DataTables.Api} this
8818 * Set the ordering for the table.
8820 * @param {array} order 2D array of sorting information to be applied.
8821 * @returns {DataTables.Api} this
8823 _api_register( 'order()', function ( order, dir ) {
8824 var ctx = this.context;
8826 if ( order === undefined ) {
8828 return ctx.length !== 0 ?
8834 if ( typeof order === 'number' ) {
8835 // Simple column / direction passed in
8836 order = [ [ order, dir ] ];
8838 else if ( ! $.isArray( order[0] ) ) {
8839 // Arguments passed in (list of 1D arrays)
8840 order = Array.prototype.slice.call( arguments );
8842 // otherwise a 2D array was passed in
8844 return this.iterator( 'table', function ( settings ) {
8845 settings.aaSorting = order.slice();
8860 _api_register( 'order.listener()', function ( node, column, callback ) {
8861 return this.iterator( 'table', function ( settings ) {
8862 _fnSortAttachListener( settings, node, column, callback );
8867 _api_register( 'order.fixed()', function ( set ) {
8869 var ctx = this.context;
8870 var fixed = ctx.length ?
8871 ctx[0].aaSortingFixed :
8874 return $.isArray( fixed ) ?
8879 return this.iterator( 'table', function ( settings ) {
8880 settings.aaSortingFixed = $.extend( true, {}, set );
8885 // Order by the selected column(s)
8887 'columns().order()',
8889 ], function ( dir ) {
8892 return this.iterator( 'table', function ( settings, i ) {
8895 $.each( that[i], function (j, col) {
8896 sort.push( [ col, dir ] );
8899 settings.aaSorting = sort;
8905 _api_register( 'search()', function ( input, regex, smart, caseInsen ) {
8906 var ctx = this.context;
8908 if ( input === undefined ) {
8910 return ctx.length !== 0 ?
8911 ctx[0].oPreviousSearch.sSearch :
8916 return this.iterator( 'table', function ( settings ) {
8917 if ( ! settings.oFeatures.bFilter ) {
8921 _fnFilterComplete( settings, $.extend( {}, settings.oPreviousSearch, {
8922 "sSearch
": input+"",
8923 "bRegex
": regex === null ? false : regex,
8924 "bSmart
": smart === null ? true : smart,
8925 "bCaseInsensitive
": caseInsen === null ? true : caseInsen
8931 _api_registerPlural(
8932 'columns().search()',
8933 'column().search()',
8934 function ( input, regex, smart, caseInsen ) {
8935 return this.iterator( 'column', function ( settings, column ) {
8936 var preSearch = settings.aoPreSearchCols;
8938 if ( input === undefined ) {
8940 return preSearch[ column ].sSearch;
8944 if ( ! settings.oFeatures.bFilter ) {
8948 $.extend( preSearch[ column ], {
8949 "sSearch
": input+"",
8950 "bRegex
": regex === null ? false : regex,
8951 "bSmart
": smart === null ? true : smart,
8952 "bCaseInsensitive
": caseInsen === null ? true : caseInsen
8955 _fnFilterComplete( settings, settings.oPreviousSearch, 1 );
8964 _api_register( 'state()', function () {
8965 return this.context.length ?
8966 this.context[0].oSavedState :
8971 _api_register( 'state.clear()', function () {
8972 return this.iterator( 'table', function ( settings ) {
8973 // Save an empty object
8974 settings.fnStateSaveCallback.call( settings.oInstance, settings, {} );
8979 _api_register( 'state.loaded()', function () {
8980 return this.context.length ?
8981 this.context[0].oLoadedState :
8986 _api_register( 'state.save()', function () {
8987 return this.iterator( 'table', function ( settings ) {
8988 _fnSaveState( settings );
9009 DataTable.versionCheck = DataTable.fnVersionCheck = function( version )
9011 var aThis = DataTable.version.split('.');
9012 var aThat = version.split('.');
9015 for ( var i=0, iLen=aThat.length ; i<iLen ; i++ ) {
9016 iThis = parseInt( aThis[i], 10 ) || 0;
9017 iThat = parseInt( aThat[i], 10 ) || 0;
9019 // Parts are the same, keep comparing
9020 if (iThis === iThat) {
9024 // Parts are different, return immediately
9025 return iThis > iThat;
9047 DataTable.isDataTable = DataTable.fnIsDataTable = function ( table )
9049 var t = $(table).get(0);
9052 $.each( DataTable.settings, function (i, o) {
9053 var head = o.nScrollHead ? $('table', o.nScrollHead)[0] : null;
9054 var foot = o.nScrollFoot ? $('table', o.nScrollFoot)[0] : null;
9056 if ( o.nTable === t || head === t || foot === t ) {
9081 DataTable.tables = DataTable.fnTables = function ( visible )
9085 if ( $.isPlainObject( visible ) ) {
9087 visible = visible.visible;
9090 var a = $.map( DataTable.settings, function (o) {
9091 if ( !visible || (visible && $(o.nTable).is(':visible')) ) {
9121 throttle: _fnThrottle,
9130 escapeRegex: _fnEscapeRegex
9147 DataTable.camelToHungarian = _fnCamelToHungarian;
9154 _api_register( '$()', function ( selector, opts ) {
9156 rows = this.rows( opts ).nodes(), // Get all rows
9159 return $( [].concat(
9160 jqRows.filter( selector ).toArray(),
9161 jqRows.find( selector ).toArray()
9166 // jQuery functions to operate on the tables
9167 $.each( [ 'on', 'one', 'off' ], function (i, key) {
9168 _api_register( key+'()', function ( /* event, handler */ ) {
9169 var args = Array.prototype.slice.call(arguments);
9171 // Add the `dt` namespace automatically if it isn't already present
9172 if ( ! args[0].match(/\.dt\b/) ) {
9176 var inst = $( this.tables().nodes() );
9177 inst[key].apply( inst, args );
9183 _api_register( 'clear()', function () {
9184 return this.iterator( 'table', function ( settings ) {
9185 _fnClearTable( settings );
9190 _api_register( 'settings()', function () {
9191 return new _Api( this.context, this.context );
9195 _api_register( 'init()', function () {
9196 var ctx = this.context;
9197 return ctx.length ? ctx[0].oInit : null;
9201 _api_register( 'data()', function () {
9202 return this.iterator( 'table', function ( settings ) {
9203 return _pluck( settings.aoData, '_aData' );
9208 _api_register( 'destroy()', function ( remove ) {
9209 remove = remove || false;
9211 return this.iterator( 'table', function ( settings ) {
9212 var orig = settings.nTableWrapper.parentNode;
9213 var classes = settings.oClasses;
9214 var table = settings.nTable;
9215 var tbody = settings.nTBody;
9216 var thead = settings.nTHead;
9217 var tfoot = settings.nTFoot;
9218 var jqTable = $(table);
9219 var jqTbody = $(tbody);
9220 var jqWrapper = $(settings.nTableWrapper);
9221 var rows = $.map( settings.aoData, function (r) { return r.nTr; } );
9224 // Flag to note that the table is currently being destroyed - no action
9226 settings.bDestroying = true;
9228 // Fire off the destroy callbacks for plug-ins etc
9229 _fnCallbackFire( settings, "aoDestroyCallback
", "destroy
", [settings] );
9231 // If not being removed from the document, make all columns visible
9233 new _Api( settings ).columns().visible( true );
9236 // Blitz all `DT` namespaced events (these are internal events, the
9237 // lowercase, `dt` events are user subscribed and they are responsible
9238 // for removing them
9239 jqWrapper.unbind('.DT').find(':not(tbody *)').unbind('.DT');
9240 $(window).unbind('.DT-'+settings.sInstance);
9242 // When scrolling we had to break the table up - restore it
9243 if ( table != thead.parentNode ) {
9244 jqTable.children('thead').detach();
9245 jqTable.append( thead );
9248 if ( tfoot && table != tfoot.parentNode ) {
9249 jqTable.children('tfoot').detach();
9250 jqTable.append( tfoot );
9253 settings.aaSorting = [];
9254 settings.aaSortingFixed = [];
9255 _fnSortingClasses( settings );
9257 $( rows ).removeClass( settings.asStripeClasses.join(' ') );
9259 $('th, td', thead).removeClass( classes.sSortable+' '+
9260 classes.sSortableAsc+' '+classes.sSortableDesc+' '+classes.sSortableNone
9263 if ( settings.bJUI ) {
9264 $('th span.'+classes.sSortIcon+ ', td span.'+classes.sSortIcon, thead).detach();
9265 $('th, td', thead).each( function () {
9266 var wrapper = $('div.'+classes.sSortJUIWrapper, this);
9267 $(this).append( wrapper.contents() );
9272 // Add the TR elements back into the table in their original order
9273 jqTbody.children().detach();
9274 jqTbody.append( rows );
9276 // Remove the DataTables generated nodes, events and classes
9277 var removedMethod = remove ? 'remove' : 'detach';
9278 jqTable[ removedMethod ]();
9279 jqWrapper[ removedMethod ]();
9281 // If we need to reattach the table to the document
9282 if ( ! remove && orig ) {
9283 // insertBefore acts like appendChild if !arg[1]
9284 orig.insertBefore( table, settings.nTableReinsertBefore );
9286 // Restore the width of the original table - was read from the style property,
9287 // so we can restore directly to that
9289 .css( 'width', settings.sDestroyWidth )
9290 .removeClass( classes.sTable );
9292 // If the were originally stripe classes - then we add them back here.
9293 // Note this is not fool proof (for example if not all rows had stripe
9294 // classes - but it's a good effort without getting carried away
9295 ien = settings.asDestroyStripes.length;
9298 jqTbody.children().each( function (i) {
9299 $(this).addClass( settings.asDestroyStripes[i % ien] );
9304 /* Remove the settings object from the settings array */
9305 var idx = $.inArray( settings, DataTable.settings );
9307 DataTable.settings.splice( idx, 1 );
9313 // Add the `every()` method for rows, columns and cells in a compact form
9314 $.each( [ 'column', 'row', 'cell' ], function ( i, type ) {
9315 _api_register( type+'s().every()', function ( fn ) {
9316 var opts = this.selector.opts;
9319 return this.iterator( type, function ( settings, arg1, arg2, arg3, arg4 ) {
9320 // Rows and columns:
9322 // arg2 - table counter
9323 // arg3 - loop counter
9327 // arg2 - column index
9328 // arg3 - table counter
9329 // arg4 - loop counter
9333 type==='cell' ? arg2 : opts,
9334 type==='cell' ? opts : undefined
9336 arg1, arg2, arg3, arg4
9343 // i18n method for extensions to be able to use the language object from the
9345 _api_register( 'i18n()', function ( token, def, plural ) {
9346 var ctx = this.context[0];
9347 var resolved = _fnGetObjectDataFn( token )( ctx.oLanguage );
9349 if ( resolved === undefined ) {
9353 if ( plural !== undefined && $.isPlainObject( resolved ) ) {
9354 resolved = resolved[ plural ] !== undefined ?
9355 resolved[ plural ] :
9359 return resolved.replace( '%d', plural ); // nb: plural might be undefined,
9370 DataTable.version = "1.10.12-dev
";
9384 DataTable.settings = [];
9392 DataTable.models = {};
9401 DataTable.models.oSearch = {
9407 "bCaseInsensitive
": true,
9442 DataTable.models.oRow = {
9490 "_aFilterData
": null,
9501 "_sFilterRow
": null,
9546 DataTable.models.oColumn = {
9581 "bSearchable
": null,
9602 "_sManualType
": null,
9625 "fnCreatedCell
": null,
9710 "sContentPadding
": null,
9719 "sDefaultContent
": null,
9734 "sSortDataType
": 'std',
9741 "sSortingClass
": null,
9749 "sSortingClassJUI
": null,
9781 * Developer note: The properties of the object below are given in Hungarian
9782 * notation, that was used as the interface for DataTables prior to v1.10, however
9783 * from v1.10 onwards the primary interface is camel case. In order to avoid
9784 * breaking backwards compatibility utterly with this change, the Hungarian
9785 * version is still, internally the primary interface, but is is not documented
9786 * - hence the @name tags in each doc comment. This allows a Javascript function
9787 * to create a map from Hungarian notation to camel case (going the other direction
9788 * would require each property to be listed, which would at around 3K to the size
9789 * of DataTables, while this method is about a 0.5K hit.
9791 * Ultimately this does pave the way for Hungarian notation to be dropped
9792 * completely, but that is a massive amount of work and will break current
9793 * installs (therefore is on-hold until v2).
9801 DataTable.defaults = {
9891 "aaSorting
": [[0,'asc']],
9914 "aaSortingFixed
": [],
10097 "aLengthMenu
": [ 10, 25, 50, 100 ],
10133 "aoColumnDefs
": null,
10160 "aoSearchCols
": [],
10181 "asStripeClasses
": null,
10201 "bAutoWidth
": true,
10224 "bDeferRender
": false,
10316 "bJQueryUI
": false,
10335 "bLengthChange
": true,
10374 "bProcessing
": false,
10412 "bRetrieve
": false,
10436 "bScrollCollapse
": false,
10458 "bServerSide
": false,
10497 "bSortMulti
": true,
10517 "bSortCellsTop
": false,
10539 "bSortClasses
": true,
10564 "bStateSave
": false,
10592 "fnCreatedRow
": null,
10613 "fnDrawCallback
": null,
10641 "fnFooterCallback
": null,
10671 "fnFormatNumber
": function ( toFormat ) {
10672 return toFormat.toString().replace(
10673 /\B(?=(\d{3})+(?!\d))/g,
10674 this.oLanguage.sThousands
10705 "fnHeaderCallback
": null,
10735 "fnInfoCallback
": null,
10760 "fnInitComplete
": null,
10786 "fnPreDrawCallback
": null,
10815 "fnRowCallback
": null,
10841 "fnServerData
": null,
10868 "fnServerParams
": null,
10906 "fnStateLoadCallback
": function ( settings ) {
10909 (settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem(
10910 'DataTables_'+settings.sInstance+'_'+location.pathname
10952 "fnStateLoadParams
": null,
10976 "fnStateLoaded
": null,
11008 "fnStateSaveCallback
": function ( settings, data ) {
11010 (settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem(
11011 'DataTables_'+settings.sInstance+'_'+location.pathname,
11012 JSON.stringify( data )
11042 "fnStateSaveParams
": null,
11062 "iStateDuration
": 7200,
11106 "iDeferLoading
": null,
11126 "iDisplayLength
": 10,
11147 "iDisplayStart
": 0,
11221 "sSortAscending
": ": activate to sort column ascending
",
11244 "sSortDescending
": ": activate to sort column descending
"
11343 "sPrevious
": "Previous
"
11366 "sEmptyTable
": "No data available in table
",
11398 "sInfo
": "Showing _START_ to _END_ of _TOTAL_ entries
",
11419 "sInfoEmpty
": "Showing 0 to 0 of 0 entries
",
11441 "sInfoFiltered
": "(filtered from _MAX_ total entries)
",
11464 "sInfoPostFix
": "",
11559 "sLengthMenu
": "Show _MENU_ entries
",
11583 "sLoadingRecords
": "Loading...
",
11604 "sProcessing
": "Processing...
",
11639 "sSearch
": "Search:
",
11650 "sSearchPlaceholder
": "",
11697 "sZeroRecords
": "No matching records found
"
11722 "oSearch
": $.extend( {}, DataTable.models.oSearch ),
11743 "sAjaxDataProp
": "data
",
11762 "sAjaxSource
": null,
11836 "searchDelay
": null,
11863 "sPaginationType
": "simple_numbers
",
11912 "sScrollXInner
": "",
11954 "sServerMethod
": "GET
",
11983 "rowId
": "DT_RowId
"
11986 _fnHungarianMap( DataTable.defaults );
11991 * Developer note - See note in model.defaults.js about the use of Hungarian
11992 * notation and camel case.
11999 DataTable.defaults.column = {
12078 "asSorting
": [ 'asc', 'desc' ],
12111 "bSearchable
": true,
12209 "fnCreatedCell
": null,
12604 "sContentPadding
": "",
12647 "sDefaultContent
": null,
12733 "sSortDataType
": "std
",
12851 _fnHungarianMap( DataTable.defaults.column );
12877 DataTable.models.oSettings = {
12891 "bAutoWidth
": null,
12902 "bDeferRender
": null,
12930 "bLengthChange
": null,
12948 "bProcessing
": null,
12958 "bServerSide
": null,
12974 "bSortMulti
": null,
12984 "bSortClasses
": null,
13059 "fnInfoCallback
": null
13073 "bScrollOversize
": false,
13082 "bScrollbarLeft
": false,
13089 "bBounding
": false,
13139 "aiDisplayMaster
": [],
13177 "oPreviousSearch
": {},
13186 "aoPreSearchCols
": [],
13210 "aaSortingFixed
": [],
13219 "asStripeClasses
": null,
13226 "asDestroyStripes
": [],
13233 "sDestroyWidth
": 0,
13240 "aoRowCallback
": [],
13247 "aoHeaderCallback
": [],
13254 "aoFooterCallback
": [],
13261 "aoDrawCallback
": [],
13268 "aoRowCreatedCallback
": [],
13276 "aoPreDrawCallback
": [],
13283 "aoInitComplete
": [],
13292 "aoStateSaveParams
": [],
13300 "aoStateLoadParams
": [],
13308 "aoStateLoaded
": [],
13350 "nTableWrapper
": null,
13360 "bDeferLoading
": false,
13367 "bInitialised
": false,
13392 "searchDelay
": null,
13401 "sPaginationType
": "two_button
",
13410 "iStateDuration
": 0,
13445 "oSavedState
": null,
13452 "oLoadedState
": null,
13461 "sAjaxSource
": null,
13471 "sAjaxDataProp
": null,
13478 "bAjaxDataGet
": true,
13501 "oAjaxData
": undefined,
13509 "fnServerData
": null,
13517 "aoServerParams
": [],
13526 "sServerMethod
": null,
13534 "fnFormatNumber
": null,
13543 "aLengthMenu
": null,
13572 "_iDisplayLength
": 10,
13579 "_iDisplayStart
": 0,
13590 "_iRecordsTotal
": 0,
13601 "_iRecordsDisplay
": 0,
13626 "bFiltered
": false,
13646 "bSortCellsTop
": null,
13661 "aoDestroyCallback
": [],
13668 "fnRecordsTotal
": function ()
13670 return _fnDataSource( this ) == 'ssp' ?
13671 this._iRecordsTotal * 1 :
13672 this.aiDisplayMaster.length;
13679 "fnRecordsDisplay
": function ()
13681 return _fnDataSource( this ) == 'ssp' ?
13682 this._iRecordsDisplay * 1 :
13683 this.aiDisplay.length;
13690 "fnDisplayEnd
": function ()
13693 len = this._iDisplayLength,
13694 start = this._iDisplayStart,
13695 calc = start + len,
13696 records = this.aiDisplay.length,
13697 features = this.oFeatures,
13698 paginate = features.bPaginate;
13700 if ( features.bServerSide ) {
13701 return paginate === false || len === -1 ?
13703 Math.min( start+len, this._iRecordsDisplay );
13706 return ! paginate || calc>records || len===-1 ?
13737 "nScrollHead
": null,
13742 "nScrollFoot
": null,
13798 DataTable.ext = _ext = {
13824 builder: "-source-
",
14270 // The following properties are retained for backwards compatiblity only.
14271 // The should not be used in new projects and will be removed in a future
14280 fnVersionCheck: DataTable.fnVersionCheck,
14304 sVersion: DataTable.version
14309 // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts
14312 afnFiltering: _ext.search,
14313 aTypes: _ext.type.detect,
14314 ofnSearch: _ext.type.search,
14315 oSort: _ext.type.order,
14316 afnSortData: _ext.order,
14317 aoFeatures: _ext.feature,
14318 oApi: _ext.internal,
14319 oStdClasses: _ext.classes,
14320 oPagination: _ext.pager
14324 $.extend( DataTable.ext.classes, {
14325 "sTable
": "dataTable
",
14326 "sNoFooter
": "no-footer
",
14328 /* Paging buttons */
14329 "sPageButton
": "paginate_button
",
14330 "sPageButtonActive
": "current
",
14331 "sPageButtonDisabled
": "disabled
",
14333 /* Striping classes */
14334 "sStripeOdd
": "odd
",
14335 "sStripeEven
": "even
",
14338 "sRowEmpty
": "dataTables_empty
",
14341 "sWrapper
": "dataTables_wrapper
",
14342 "sFilter
": "dataTables_filter
",
14343 "sInfo
": "dataTables_info
",
14344 "sPaging
": "dataTables_paginate paging_
", /* Note that the type is postfixed */
14345 "sLength
": "dataTables_length
",
14346 "sProcessing
": "dataTables_processing
",
14349 "sSortAsc
": "sorting_asc
",
14350 "sSortDesc
": "sorting_desc
",
14351 "sSortable
": "sorting
", /* Sortable in both directions */
14352 "sSortableAsc
": "sorting_asc_disabled
",
14353 "sSortableDesc
": "sorting_desc_disabled
",
14354 "sSortableNone
": "sorting_disabled
",
14355 "sSortColumn
": "sorting_
", /* Note that an int is postfixed for the sorting order */
14358 "sFilterInput
": "",
14361 "sLengthSelect
": "",
14364 "sScrollWrapper
": "dataTables_scroll
",
14365 "sScrollHead
": "dataTables_scrollHead
",
14366 "sScrollHeadInner
": "dataTables_scrollHeadInner
",
14367 "sScrollBody
": "dataTables_scrollBody
",
14368 "sScrollFoot
": "dataTables_scrollFoot
",
14369 "sScrollFootInner
": "dataTables_scrollFootInner
",
14377 "sSortJUIDesc
": "",
14379 "sSortJUIAscAllowed
": "",
14380 "sSortJUIDescAllowed
": "",
14381 "sSortJUIWrapper
": "",
14390 // Reused strings for better compression. Closure compiler appears to have a
14391 // weird edge case where it is trying to expand strings rather than use the
14392 // variable version. This results in about 200 bytes being added, for very
14393 // little preference benefit since it this run on script load only.
14397 var _stateDefault = _empty + 'ui-state-default';
14398 var _sortIcon = _empty + 'css_right ui-icon ui-icon-';
14399 var _headerFooter = _empty + 'fg-toolbar ui-toolbar ui-widget-header ui-helper-clearfix';
14401 $.extend( DataTable.ext.oJUIClasses, DataTable.ext.classes, {
14402 /* Full numbers paging buttons */
14403 "sPageButton
": "fg-button ui-button
"+_stateDefault,
14404 "sPageButtonActive
": "ui-state-disabled
",
14405 "sPageButtonDisabled
": "ui-state-disabled
",
14408 "sPaging
": "dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi
"+
14409 "ui-buttonset-multi paging_
", /* Note that the type is postfixed */
14412 "sSortAsc
": _stateDefault+" sorting_asc
",
14413 "sSortDesc
": _stateDefault+" sorting_desc
",
14414 "sSortable
": _stateDefault+" sorting
",
14415 "sSortableAsc
": _stateDefault+" sorting_asc_disabled
",
14416 "sSortableDesc
": _stateDefault+" sorting_desc_disabled
",
14417 "sSortableNone
": _stateDefault+" sorting_disabled
",
14418 "sSortJUIAsc
": _sortIcon+"triangle-1-n
",
14419 "sSortJUIDesc
": _sortIcon+"triangle-1-s
",
14420 "sSortJUI
": _sortIcon+"carat-2-n-s
",
14421 "sSortJUIAscAllowed
": _sortIcon+"carat-1-n
",
14422 "sSortJUIDescAllowed
": _sortIcon+"carat-1-s
",
14423 "sSortJUIWrapper
": "DataTables_sort_wrapper
",
14424 "sSortIcon
": "DataTables_sort_icon
",
14427 "sScrollHead
": "dataTables_scrollHead
"+_stateDefault,
14428 "sScrollFoot
": "dataTables_scrollFoot
"+_stateDefault,
14431 "sHeaderTH
": _stateDefault,
14432 "sFooterTH
": _stateDefault,
14433 "sJUIHeader
": _headerFooter+" ui-corner-tl ui-corner-tr
",
14434 "sJUIFooter
": _headerFooter+" ui-corner-bl ui-corner-br
"
14441 var extPagination = DataTable.ext.pager;
14443 function _numbers ( page, pages ) {
14446 buttons = extPagination.numbers_length,
14447 half = Math.floor( buttons / 2 ),
14450 if ( pages <= buttons ) {
14451 numbers = _range( 0, pages );
14453 else if ( page <= half ) {
14454 numbers = _range( 0, buttons-2 );
14455 numbers.push( 'ellipsis' );
14456 numbers.push( pages-1 );
14458 else if ( page >= pages - 1 - half ) {
14459 numbers = _range( pages-(buttons-2), pages );
14460 numbers.splice( 0, 0, 'ellipsis' ); // no unshift in ie6
14461 numbers.splice( 0, 0, 0 );
14464 numbers = _range( page-half+2, page+half-1 );
14465 numbers.push( 'ellipsis' );
14466 numbers.push( pages-1 );
14467 numbers.splice( 0, 0, 'ellipsis' );
14468 numbers.splice( 0, 0, 0 );
14471 numbers.DT_el = 'span';
14476 $.extend( extPagination, {
14477 simple: function ( page, pages ) {
14478 return [ 'previous', 'next' ];
14481 full: function ( page, pages ) {
14482 return [ 'first', 'previous', 'next', 'last' ];
14485 numbers: function ( page, pages ) {
14486 return [ _numbers(page, pages) ];
14489 simple_numbers: function ( page, pages ) {
14490 return [ 'previous', _numbers(page, pages), 'next' ];
14493 full_numbers: function ( page, pages ) {
14494 return [ 'first', 'previous', _numbers(page, pages), 'next', 'last' ];
14497 // For testing and plug-ins to use
14498 _numbers: _numbers,
14500 // Number of number buttons (including ellipsis) to show. _Must be odd!_
14505 $.extend( true, DataTable.ext.renderer, {
14507 _: function ( settings, host, idx, buttons, page, pages ) {
14508 var classes = settings.oClasses;
14509 var lang = settings.oLanguage.oPaginate;
14510 var aria = settings.oLanguage.oAria.paginate || {};
14511 var btnDisplay, btnClass, counter=0;
14513 var attach = function( container, buttons ) {
14514 var i, ien, node, button;
14515 var clickHandler = function ( e ) {
14516 _fnPageChange( settings, e.data.action, true );
14519 for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
14520 button = buttons[i];
14522 if ( $.isArray( button ) ) {
14523 var inner = $( '<'+(button.DT_el || 'div')+'/>' )
14524 .appendTo( container );
14525 attach( inner, button );
14531 switch ( button ) {
14533 container.append('<span class="ellipsis
">…</span>');
14537 btnDisplay = lang.sFirst;
14538 btnClass = button + (page > 0 ?
14539 '' : ' '+classes.sPageButtonDisabled);
14543 btnDisplay = lang.sPrevious;
14544 btnClass = button + (page > 0 ?
14545 '' : ' '+classes.sPageButtonDisabled);
14549 btnDisplay = lang.sNext;
14550 btnClass = button + (page < pages-1 ?
14551 '' : ' '+classes.sPageButtonDisabled);
14555 btnDisplay = lang.sLast;
14556 btnClass = button + (page < pages-1 ?
14557 '' : ' '+classes.sPageButtonDisabled);
14561 btnDisplay = button + 1;
14562 btnClass = page === button ?
14563 classes.sPageButtonActive : '';
14567 if ( btnDisplay !== null ) {
14569 'class': classes.sPageButton+' '+btnClass,
14570 'aria-controls': settings.sTableId,
14571 'aria-label': aria[ button ],
14572 'data-dt-idx': counter,
14573 'tabindex': settings.iTabIndex,
14574 'id': idx === 0 && typeof button === 'string' ?
14575 settings.sTableId +'_'+ button :
14578 .html( btnDisplay )
14579 .appendTo( container );
14582 node, {action: button}, clickHandler
14591 // IE9 throws an 'unknown error' if document.activeElement is used
14592 // inside an iframe or frame. Try / catch the error. Not good for
14593 // accessibility, but neither are frames.
14597 // Because this approach is destroying and recreating the paging
14598 // elements, focus is lost on the select button which is bad for
14599 // accessibility. So we want to restore focus once the draw has
14601 activeEl = $(host).find(document.activeElement).data('dt-idx');
14605 attach( $(host).empty(), buttons );
14608 $(host).find( '[data-dt-idx='+activeEl+']' ).focus();
14616 // Built in type detection. See model.ext.aTypes for information about
14617 // what is required from this methods.
14618 $.extend( DataTable.ext.type.detect, [
14619 // Plain numbers - first since V8 detects some plain numbers as dates
14620 // e.g. Date.parse('55') (but not all, e.g. Date.parse('22')...).
14621 function ( d, settings )
14623 var decimal = settings.oLanguage.sDecimal;
14624 return _isNumber( d, decimal ) ? 'num'+decimal : null;
14627 // Dates (only those recognised by the browser's Date.parse)
14628 function ( d, settings )
14630 // V8 will remove any unknown characters at the start and end of the
14631 // expression, leading to false matches such as `$245.12` or `10%` being
14632 // a valid date. See forum thread 18941 for detail.
14633 if ( d && !(d instanceof Date) && ( ! _re_date_start.test(d) || ! _re_date_end.test(d) ) ) {
14636 var parsed = Date.parse(d);
14637 return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null;
14640 // Formatted numbers
14641 function ( d, settings )
14643 var decimal = settings.oLanguage.sDecimal;
14644 return _isNumber( d, decimal, true ) ? 'num-fmt'+decimal : null;
14648 function ( d, settings )
14650 var decimal = settings.oLanguage.sDecimal;
14651 return _htmlNumeric( d, decimal ) ? 'html-num'+decimal : null;
14654 // HTML numeric, formatted
14655 function ( d, settings )
14657 var decimal = settings.oLanguage.sDecimal;
14658 return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt'+decimal : null;
14661 // HTML (this is strict checking - there must be html)
14662 function ( d, settings )
14664 return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ?
14671 // Filter formatting functions. See model.ext.ofnSearch for information about
14672 // what is required from these methods.
14674 // Note that additional search methods are added for the html numbers and
14675 // html formatted numbers by `_addNumericSort()` when we know what the decimal
14679 $.extend( DataTable.ext.type.search, {
14680 html: function ( data ) {
14681 return _empty(data) ?
14683 typeof data === 'string' ?
14685 .replace( _re_new_lines, " " )
14686 .replace( _re_html, "" ) :
14690 string: function ( data ) {
14691 return _empty(data) ?
14693 typeof data === 'string' ?
14694 data.replace( _re_new_lines, " " ) :
14701 var __numericReplace = function ( d, decimalPlace, re1, re2 ) {
14702 if ( d !== 0 && (!d || d === '-') ) {
14706 // If a decimal place other than `.` is used, it needs to be given to the
14707 // function so we can detect it and replace with a `.` which is the only
14708 // decimal place Javascript recognises - it is not locale aware.
14709 if ( decimalPlace ) {
14710 d = _numToDecimal( d, decimalPlace );
14715 d = d.replace( re1, '' );
14719 d = d.replace( re2, '' );
14727 // Add the numeric 'deformatting' functions for sorting and search. This is done
14728 // in a function to provide an easy ability for the language options to add
14729 // additional methods if a non-period decimal place is used.
14730 function _addNumericSort ( decimalPlace ) {
14734 "num
": function ( d ) {
14735 return __numericReplace( d, decimalPlace );
14738 // Formatted numbers
14739 "num-fmt
": function ( d ) {
14740 return __numericReplace( d, decimalPlace, _re_formatted_numeric );
14744 "html-num
": function ( d ) {
14745 return __numericReplace( d, decimalPlace, _re_html );
14748 // HTML numeric, formatted
14749 "html-num-fmt
": function ( d ) {
14750 return __numericReplace( d, decimalPlace, _re_html, _re_formatted_numeric );
14753 function ( key, fn ) {
14754 // Add the ordering method
14755 _ext.type.order[ key+decimalPlace+'-pre' ] = fn;
14757 // For HTML types add a search formatter that will strip the HTML
14758 if ( key.match(/^html\-/) ) {
14759 _ext.type.search[ key+decimalPlace ] = _ext.type.search.html;
14766 // Default sort methods
14767 $.extend( _ext.type.order, {
14769 "date-pre
": function ( d ) {
14770 return Date.parse( d ) || 0;
14774 "html-pre
": function ( a ) {
14778 a.replace( /<.*?>/g, "" ).toLowerCase() :
14783 "string-pre
": function ( a ) {
14784 // This is a little complex, but faster than always calling toString,
14785 // http://jsperf.com/tostring-v-check
14788 typeof a === 'string' ?
14795 // string-asc and -desc are retained only for compatibility with the old
14797 "string-asc
": function ( x, y ) {
14798 return ((x < y) ? -1 : ((x > y) ? 1 : 0));
14801 "string-desc
": function ( x, y ) {
14802 return ((x < y) ? 1 : ((x > y) ? -1 : 0));
14807 // Numeric sorting types - order doesn't matter here
14808 _addNumericSort( '' );
14811 $.extend( true, DataTable.ext.renderer, {
14813 _: function ( settings, cell, column, classes ) {
14814 // No additional mark-up required
14815 // Attach a sort listener to update on sort - note that using the
14816 // `DT` namespace will allow the event to be removed automatically
14817 // on destroy, while the `dt` namespaced event is the one we are
14819 $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
14820 if ( settings !== ctx ) { // need to check this this is the host
14821 return; // table, not a nested one
14824 var colIdx = column.idx;
14828 column.sSortingClass +' '+
14829 classes.sSortAsc +' '+
14832 .addClass( columns[ colIdx ] == 'asc' ?
14833 classes.sSortAsc : columns[ colIdx ] == 'desc' ?
14834 classes.sSortDesc :
14835 column.sSortingClass
14840 jqueryui: function ( settings, cell, column, classes ) {
14842 .addClass( classes.sSortJUIWrapper )
14843 .append( cell.contents() )
14844 .append( $('<span/>')
14845 .addClass( classes.sSortIcon+' '+column.sSortingClassJUI )
14849 // Attach a sort listener to update on sort
14850 $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
14851 if ( settings !== ctx ) {
14855 var colIdx = column.idx;
14858 .removeClass( classes.sSortAsc +" "+classes.sSortDesc )
14859 .addClass( columns[ colIdx ] == 'asc' ?
14860 classes.sSortAsc : columns[ colIdx ] == 'desc' ?
14861 classes.sSortDesc :
14862 column.sSortingClass
14866 .find( 'span.'+classes.sSortIcon )
14868 classes.sSortJUIAsc +" "+
14869 classes.sSortJUIDesc +" "+
14870 classes.sSortJUI +" "+
14871 classes.sSortJUIAscAllowed +" "+
14872 classes.sSortJUIDescAllowed
14874 .addClass( columns[ colIdx ] == 'asc' ?
14875 classes.sSortJUIAsc : columns[ colIdx ] == 'desc' ?
14876 classes.sSortJUIDesc :
14877 column.sSortingClassJUI
14885 * Public helper functions. These aren't used internally by DataTables, or
14886 * called by any of the options passed into DataTables, but they can be used
14887 * externally by developers working with DataTables. They are helper functions
14888 * to make working with DataTables a little bit easier.
14891 var __htmlEscapeEntities = function ( d ) {
14892 return typeof d === 'string' ?
14893 d.replace(/</g, '<').replace(/>/g, '>').replace(/"/g,
'"') :
14923 DataTable.render = {
14924 number:
function ( thousands, decimal, precision, prefix, postfix ) {
14926 display:
function ( d ) {
14927 if ( typeof d !==
'number' && typeof d !==
'string' ) {
14931 var negative = d < 0 ?
'-' :
'';
14932 var flo = parseFloat( d );
14937 if ( isNaN( flo ) ) {
14938 return __htmlEscapeEntities( d );
14941 d = Math.abs( flo );
14943 var intPart = parseInt( d, 10 );
14944 var floatPart = precision ?
14945 decimal+(d - intPart).toFixed( precision ).substring( 2 ):
14948 return negative + (prefix||
'') +
14949 intPart.toString().replace(
14950 /\B(?=(\d{3})+(?!\d))/g, thousands
14958 text:
function () {
14960 display: __htmlEscapeEntities
14978 function _fnExternApiFunc (fn)
14980 return function() {
14981 var args = [_fnSettingsFromNode(
this[DataTable.ext.iApiIndex] )].concat(
14982 Array.prototype.slice.call(arguments)
14984 return DataTable.ext.internal[fn].apply(
this, args );
14996 $.extend( DataTable.ext.internal, {
14997 _fnExternApiFunc: _fnExternApiFunc,
14998 _fnBuildAjax: _fnBuildAjax,
14999 _fnAjaxUpdate: _fnAjaxUpdate,
15000 _fnAjaxParameters: _fnAjaxParameters,
15001 _fnAjaxUpdateDraw: _fnAjaxUpdateDraw,
15002 _fnAjaxDataSrc: _fnAjaxDataSrc,
15003 _fnAddColumn: _fnAddColumn,
15004 _fnColumnOptions: _fnColumnOptions,
15005 _fnAdjustColumnSizing: _fnAdjustColumnSizing,
15006 _fnVisibleToColumnIndex: _fnVisibleToColumnIndex,
15007 _fnColumnIndexToVisible: _fnColumnIndexToVisible,
15008 _fnVisbleColumns: _fnVisbleColumns,
15009 _fnGetColumns: _fnGetColumns,
15010 _fnColumnTypes: _fnColumnTypes,
15011 _fnApplyColumnDefs: _fnApplyColumnDefs,
15012 _fnHungarianMap: _fnHungarianMap,
15013 _fnCamelToHungarian: _fnCamelToHungarian,
15014 _fnLanguageCompat: _fnLanguageCompat,
15015 _fnBrowserDetect: _fnBrowserDetect,
15016 _fnAddData: _fnAddData,
15017 _fnAddTr: _fnAddTr,
15018 _fnNodeToDataIndex: _fnNodeToDataIndex,
15019 _fnNodeToColumnIndex: _fnNodeToColumnIndex,
15020 _fnGetCellData: _fnGetCellData,
15021 _fnSetCellData: _fnSetCellData,
15022 _fnSplitObjNotation: _fnSplitObjNotation,
15023 _fnGetObjectDataFn: _fnGetObjectDataFn,
15024 _fnSetObjectDataFn: _fnSetObjectDataFn,
15025 _fnGetDataMaster: _fnGetDataMaster,
15026 _fnClearTable: _fnClearTable,
15027 _fnDeleteIndex: _fnDeleteIndex,
15028 _fnInvalidate: _fnInvalidate,
15029 _fnGetRowElements: _fnGetRowElements,
15030 _fnCreateTr: _fnCreateTr,
15031 _fnBuildHead: _fnBuildHead,
15032 _fnDrawHead: _fnDrawHead,
15034 _fnReDraw: _fnReDraw,
15035 _fnAddOptionsHtml: _fnAddOptionsHtml,
15036 _fnDetectHeader: _fnDetectHeader,
15037 _fnGetUniqueThs: _fnGetUniqueThs,
15038 _fnFeatureHtmlFilter: _fnFeatureHtmlFilter,
15039 _fnFilterComplete: _fnFilterComplete,
15040 _fnFilterCustom: _fnFilterCustom,
15041 _fnFilterColumn: _fnFilterColumn,
15042 _fnFilter: _fnFilter,
15043 _fnFilterCreateSearch: _fnFilterCreateSearch,
15044 _fnEscapeRegex: _fnEscapeRegex,
15045 _fnFilterData: _fnFilterData,
15046 _fnFeatureHtmlInfo: _fnFeatureHtmlInfo,
15047 _fnUpdateInfo: _fnUpdateInfo,
15048 _fnInfoMacros: _fnInfoMacros,
15049 _fnInitialise: _fnInitialise,
15050 _fnInitComplete: _fnInitComplete,
15051 _fnLengthChange: _fnLengthChange,
15052 _fnFeatureHtmlLength: _fnFeatureHtmlLength,
15053 _fnFeatureHtmlPaginate: _fnFeatureHtmlPaginate,
15054 _fnPageChange: _fnPageChange,
15055 _fnFeatureHtmlProcessing: _fnFeatureHtmlProcessing,
15056 _fnProcessingDisplay: _fnProcessingDisplay,
15057 _fnFeatureHtmlTable: _fnFeatureHtmlTable,
15058 _fnScrollDraw: _fnScrollDraw,
15059 _fnApplyToChildren: _fnApplyToChildren,
15060 _fnCalculateColumnWidths: _fnCalculateColumnWidths,
15061 _fnThrottle: _fnThrottle,
15062 _fnConvertToWidth: _fnConvertToWidth,
15063 _fnGetWidestNode: _fnGetWidestNode,
15064 _fnGetMaxLenString: _fnGetMaxLenString,
15065 _fnStringToCss: _fnStringToCss,
15066 _fnSortFlatten: _fnSortFlatten,
15068 _fnSortAria: _fnSortAria,
15069 _fnSortListener: _fnSortListener,
15070 _fnSortAttachListener: _fnSortAttachListener,
15071 _fnSortingClasses: _fnSortingClasses,
15072 _fnSortData: _fnSortData,
15073 _fnSaveState: _fnSaveState,
15074 _fnLoadState: _fnLoadState,
15075 _fnSettingsFromNode: _fnSettingsFromNode,
15078 _fnBindAction: _fnBindAction,
15079 _fnCallbackReg: _fnCallbackReg,
15080 _fnCallbackFire: _fnCallbackFire,
15081 _fnLengthOverflow: _fnLengthOverflow,
15082 _fnRenderer: _fnRenderer,
15083 _fnDataSource: _fnDataSource,
15084 _fnRowAttributes: _fnRowAttributes,
15085 _fnCalculateEnd: function () {}
15092 $.fn.dataTable = DataTable;
15098 $.fn.dataTableSettings = DataTable.settings;
15099 $.fn.dataTableExt = DataTable.ext;
15103 $.fn.DataTable =
function ( opts ) {
15104 return $(
this).dataTable( opts ).api();
15109 $.each( DataTable,
function ( prop, val ) {
15110 $.fn.DataTable[ prop ] = val;
15276 return $.fn.dataTable;