datatables.net 2.0.7 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/js/dataTables.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! DataTables 2.0.7
1
+ /*! DataTables 2.1.0
2
2
  * © SpryMedia Ltd - datatables.net/license
3
3
  */
4
4
 
@@ -51,7 +51,6 @@ var DataTable = function ( selector, options )
51
51
 
52
52
  var i=0, iLen;
53
53
  var sId = this.getAttribute( 'id' );
54
- var bInitHandedOff = false;
55
54
  var defaults = DataTable.defaults;
56
55
  var $this = $(this);
57
56
 
@@ -201,6 +200,7 @@ var DataTable = function ( selector, options )
201
200
  "rowId",
202
201
  "caption",
203
202
  "layout",
203
+ "orderDescReverse",
204
204
  [ "iCookieDuration", "iStateDuration" ], // backwards compat
205
205
  [ "oSearch", "oPreviousSearch" ],
206
206
  [ "aoSearchCols", "aoPreSearchCols" ],
@@ -247,38 +247,14 @@ var DataTable = function ( selector, options )
247
247
  oSettings._iDisplayStart = oInit.iDisplayStart;
248
248
  }
249
249
 
250
- /* Language definitions */
251
- var oLanguage = oSettings.oLanguage;
252
- $.extend( true, oLanguage, oInit.oLanguage );
253
-
254
- if ( oLanguage.sUrl )
250
+ var defer = oInit.iDeferLoading;
251
+ if ( defer !== null )
255
252
  {
256
- /* Get the language definitions from a file - because this Ajax call makes the language
257
- * get async to the remainder of this function we use bInitHandedOff to indicate that
258
- * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor
259
- */
260
- $.ajax( {
261
- dataType: 'json',
262
- url: oLanguage.sUrl,
263
- success: function ( json ) {
264
- _fnCamelToHungarian( defaults.oLanguage, json );
265
- $.extend( true, oLanguage, json, oSettings.oInit.oLanguage );
253
+ oSettings.deferLoading = true;
266
254
 
267
- _fnCallbackFire( oSettings, null, 'i18n', [oSettings], true);
268
- _fnInitialise( oSettings );
269
- },
270
- error: function () {
271
- // Error occurred loading language file
272
- _fnLog( oSettings, 0, 'i18n file loading error', 21 );
273
-
274
- // continue on as best we can
275
- _fnInitialise( oSettings );
276
- }
277
- } );
278
- bInitHandedOff = true;
279
- }
280
- else {
281
- _fnCallbackFire( oSettings, null, 'i18n', [oSettings]);
255
+ var tmp = Array.isArray(defer);
256
+ oSettings._iRecordsDisplay = tmp ? defer[0] : defer;
257
+ oSettings._iRecordsTotal = tmp ? defer[1] : defer;
282
258
  }
283
259
 
284
260
  /*
@@ -345,113 +321,112 @@ var DataTable = function ( selector, options )
345
321
  } );
346
322
  }
347
323
 
324
+ // Must be done after everything which can be overridden by the state saving!
325
+ _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState );
326
+
348
327
  var features = oSettings.oFeatures;
349
- var loadedInit = function () {
350
- /*
351
- * Sorting
352
- * @todo For modularisation (1.11) this needs to do into a sort start up handler
353
- */
328
+ if ( oInit.bStateSave )
329
+ {
330
+ features.bStateSave = true;
331
+ }
354
332
 
355
- // If aaSorting is not defined, then we use the first indicator in asSorting
356
- // in case that has been altered, so the default sort reflects that option
357
- if ( oInit.aaSorting === undefined ) {
358
- var sorting = oSettings.aaSorting;
359
- for ( i=0, iLen=sorting.length ; i<iLen ; i++ ) {
360
- sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0];
361
- }
333
+ // If aaSorting is not defined, then we use the first indicator in asSorting
334
+ // in case that has been altered, so the default sort reflects that option
335
+ if ( oInit.aaSorting === undefined ) {
336
+ var sorting = oSettings.aaSorting;
337
+ for ( i=0, iLen=sorting.length ; i<iLen ; i++ ) {
338
+ sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0];
362
339
  }
340
+ }
363
341
 
364
- /* Do a first pass on the sorting classes (allows any size changes to be taken into
365
- * account, and also will apply sorting disabled classes if disabled
366
- */
367
- _fnSortingClasses( oSettings );
368
-
369
- _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
370
- if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) {
371
- _fnSortingClasses( oSettings );
372
- }
373
- } );
342
+ // Do a first pass on the sorting classes (allows any size changes to be taken into
343
+ // account, and also will apply sorting disabled classes if disabled
344
+ _fnSortingClasses( oSettings );
374
345
 
346
+ _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
347
+ if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) {
348
+ _fnSortingClasses( oSettings );
349
+ }
350
+ } );
375
351
 
376
- /*
377
- * Final init
378
- * Cache the header, body and footer as required, creating them if needed
379
- */
380
- var caption = $this.children('caption');
381
352
 
382
- if ( oSettings.caption ) {
383
- if ( caption.length === 0 ) {
384
- caption = $('<caption/>').appendTo( $this );
385
- }
353
+ /*
354
+ * Table HTML init
355
+ * Cache the header, body and footer as required, creating them if needed
356
+ */
357
+ var caption = $this.children('caption');
386
358
 
387
- caption.html( oSettings.caption );
359
+ if ( oSettings.caption ) {
360
+ if ( caption.length === 0 ) {
361
+ caption = $('<caption/>').appendTo( $this );
388
362
  }
389
363
 
390
- // Store the caption side, so we can remove the element from the document
391
- // when creating the element
392
- if (caption.length) {
393
- caption[0]._captionSide = caption.css('caption-side');
394
- oSettings.captionNode = caption[0];
395
- }
364
+ caption.html( oSettings.caption );
365
+ }
396
366
 
397
- if ( thead.length === 0 ) {
398
- thead = $('<thead/>').appendTo($this);
399
- }
400
- oSettings.nTHead = thead[0];
401
- $('tr', thead).addClass(oClasses.thead.row);
367
+ // Store the caption side, so we can remove the element from the document
368
+ // when creating the element
369
+ if (caption.length) {
370
+ caption[0]._captionSide = caption.css('caption-side');
371
+ oSettings.captionNode = caption[0];
372
+ }
402
373
 
403
- var tbody = $this.children('tbody');
404
- if ( tbody.length === 0 ) {
405
- tbody = $('<tbody/>').insertAfter(thead);
406
- }
407
- oSettings.nTBody = tbody[0];
374
+ if ( thead.length === 0 ) {
375
+ thead = $('<thead/>').appendTo($this);
376
+ }
377
+ oSettings.nTHead = thead[0];
378
+ $('tr', thead).addClass(oClasses.thead.row);
408
379
 
409
- var tfoot = $this.children('tfoot');
410
- if ( tfoot.length === 0 ) {
411
- // If we are a scrolling table, and no footer has been given, then we need to create
412
- // a tfoot element for the caption element to be appended to
413
- tfoot = $('<tfoot/>').appendTo($this);
414
- }
415
- oSettings.nTFoot = tfoot[0];
416
- $('tr', tfoot).addClass(oClasses.tfoot.row);
380
+ var tbody = $this.children('tbody');
381
+ if ( tbody.length === 0 ) {
382
+ tbody = $('<tbody/>').insertAfter(thead);
383
+ }
384
+ oSettings.nTBody = tbody[0];
417
385
 
418
- // Check if there is data passing into the constructor
419
- if ( oInit.aaData ) {
420
- for ( i=0 ; i<oInit.aaData.length ; i++ ) {
421
- _fnAddData( oSettings, oInit.aaData[ i ] );
422
- }
423
- }
424
- else if ( _fnDataSource( oSettings ) == 'dom' ) {
425
- // Grab the data from the page
426
- _fnAddTr( oSettings, $(oSettings.nTBody).children('tr') );
427
- }
386
+ var tfoot = $this.children('tfoot');
387
+ if ( tfoot.length === 0 ) {
388
+ // If we are a scrolling table, and no footer has been given, then we need to create
389
+ // a tfoot element for the caption element to be appended to
390
+ tfoot = $('<tfoot/>').appendTo($this);
391
+ }
392
+ oSettings.nTFoot = tfoot[0];
393
+ $('tr', tfoot).addClass(oClasses.tfoot.row);
428
394
 
429
- /* Copy the data index array */
430
- oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
395
+ // Copy the data index array
396
+ oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
431
397
 
432
- /* Initialisation complete - table can be drawn */
433
- oSettings.bInitialised = true;
398
+ // Initialisation complete - table can be drawn
399
+ oSettings.bInitialised = true;
434
400
 
435
- /* Check if we need to initialise the table (it might not have been handed off to the
436
- * language processor)
437
- */
438
- if ( bInitHandedOff === false ) {
439
- _fnInitialise( oSettings );
440
- }
441
- };
401
+ // Language definitions
402
+ var oLanguage = oSettings.oLanguage;
403
+ $.extend( true, oLanguage, oInit.oLanguage );
442
404
 
443
- /* Must be done after everything which can be overridden by the state saving! */
444
- _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState );
405
+ if ( oLanguage.sUrl ) {
406
+ // Get the language definitions from a file
407
+ $.ajax( {
408
+ dataType: 'json',
409
+ url: oLanguage.sUrl,
410
+ success: function ( json ) {
411
+ _fnCamelToHungarian( defaults.oLanguage, json );
412
+ $.extend( true, oLanguage, json, oSettings.oInit.oLanguage );
445
413
 
446
- if ( oInit.bStateSave )
447
- {
448
- features.bStateSave = true;
449
- _fnLoadState( oSettings, oInit, loadedInit );
414
+ _fnCallbackFire( oSettings, null, 'i18n', [oSettings], true);
415
+ _fnInitialise( oSettings );
416
+ },
417
+ error: function () {
418
+ // Error occurred loading language file
419
+ _fnLog( oSettings, 0, 'i18n file loading error', 21 );
420
+
421
+ // Continue on as best we can
422
+ _fnInitialise( oSettings );
423
+ }
424
+ } );
450
425
  }
451
426
  else {
452
- loadedInit();
427
+ _fnCallbackFire( oSettings, null, 'i18n', [oSettings]);
428
+ _fnInitialise( oSettings );
453
429
  }
454
-
455
430
  } );
456
431
  _that = null;
457
432
  return this;
@@ -968,6 +943,15 @@ $.extend( DataTable.ext.classes, {
968
943
  info: {
969
944
  container: 'dt-info'
970
945
  },
946
+ layout: {
947
+ row: 'dt-layout-row',
948
+ cell: 'dt-layout-cell',
949
+ tableRow: 'dt-layout-table',
950
+ tableCell: '',
951
+ start: 'dt-layout-start',
952
+ end: 'dt-layout-end',
953
+ full: 'dt-layout-full'
954
+ },
971
955
  length: {
972
956
  container: 'dt-length',
973
957
  select: 'dt-input'
@@ -1091,7 +1075,7 @@ var _numToDecimal = function ( num, decimalPoint ) {
1091
1075
  };
1092
1076
 
1093
1077
 
1094
- var _isNumber = function ( d, decimalPoint, formatted ) {
1078
+ var _isNumber = function ( d, decimalPoint, formatted, allowEmpty ) {
1095
1079
  var type = typeof d;
1096
1080
  var strType = type === 'string';
1097
1081
 
@@ -1102,7 +1086,7 @@ var _isNumber = function ( d, decimalPoint, formatted ) {
1102
1086
  // If empty return immediately so there must be a number if it is a
1103
1087
  // formatted string (this stops the string "k", or "kr", etc being detected
1104
1088
  // as a formatted number for currency
1105
- if ( _empty( d ) ) {
1089
+ if ( allowEmpty && _empty( d ) ) {
1106
1090
  return true;
1107
1091
  }
1108
1092
 
@@ -1124,8 +1108,8 @@ var _isHtml = function ( d ) {
1124
1108
  };
1125
1109
 
1126
1110
  // Is a string a number surrounded by HTML?
1127
- var _htmlNumeric = function ( d, decimalPoint, formatted ) {
1128
- if ( _empty( d ) ) {
1111
+ var _htmlNumeric = function ( d, decimalPoint, formatted, allowEmpty ) {
1112
+ if ( allowEmpty && _empty( d ) ) {
1129
1113
  return true;
1130
1114
  }
1131
1115
 
@@ -1137,7 +1121,7 @@ var _htmlNumeric = function ( d, decimalPoint, formatted ) {
1137
1121
  var html = _isHtml( d );
1138
1122
  return ! html ?
1139
1123
  null :
1140
- _isNumber( _stripHtml( d ), decimalPoint, formatted ) ?
1124
+ _isNumber( _stripHtml( d ), decimalPoint, formatted, allowEmpty ) ?
1141
1125
  true :
1142
1126
  null;
1143
1127
  };
@@ -1233,6 +1217,10 @@ var _removeEmpty = function ( a )
1233
1217
 
1234
1218
  // Replaceable function in api.util
1235
1219
  var _stripHtml = function (input) {
1220
+ if (! input) {
1221
+ return input;
1222
+ }
1223
+
1236
1224
  // Irrelevant check to workaround CodeQL's false positive on the regex
1237
1225
  if (input.length > _max_str_len) {
1238
1226
  throw new Error('Exceeded max str len');
@@ -2199,6 +2187,21 @@ function _fnGetColumns( oSettings, sParam )
2199
2187
  return a;
2200
2188
  }
2201
2189
 
2190
+ /**
2191
+ * Allow the result from a type detection function to be `true` while
2192
+ * translating that into a string. Old type detection functions will
2193
+ * return the type name if it passes. An obect store would be better,
2194
+ * but not backwards compatible.
2195
+ *
2196
+ * @param {*} typeDetect Object or function for type detection
2197
+ * @param {*} res Result from the type detection function
2198
+ * @returns Type name or false
2199
+ */
2200
+ function _typeResult (typeDetect, res) {
2201
+ return res === true
2202
+ ? typeDetect.name
2203
+ : res;
2204
+ }
2202
2205
 
2203
2206
  /**
2204
2207
  * Calculate the 'type' of a column
@@ -2213,7 +2216,13 @@ function _fnColumnTypes ( settings )
2213
2216
  var i, ien, j, jen, k, ken;
2214
2217
  var col, detectedType, cache;
2215
2218
 
2216
- // For each column, spin over the
2219
+ // If SSP then we don't have the full data set, so any type detection would be
2220
+ // unreliable and error prone
2221
+ if (_fnDataSource( settings ) === 'ssp') {
2222
+ return;
2223
+ }
2224
+
2225
+ // For each column, spin over the data type detection functions, seeing if one matches
2217
2226
  for ( i=0, ien=columns.length ; i<ien ; i++ ) {
2218
2227
  col = columns[i];
2219
2228
  cache = [];
@@ -2223,8 +2232,27 @@ function _fnColumnTypes ( settings )
2223
2232
  }
2224
2233
  else if ( ! col.sType ) {
2225
2234
  for ( j=0, jen=types.length ; j<jen ; j++ ) {
2226
- for ( k=0, ken=data.length ; k<ken ; k++ ) {
2235
+ var typeDetect = types[j];
2236
+
2237
+ // There can be either one, or three type detection functions
2238
+ var oneOf = typeDetect.oneOf;
2239
+ var allOf = typeDetect.allOf || typeDetect;
2240
+ var init = typeDetect.init;
2241
+ var one = false;
2242
+
2243
+ detectedType = null;
2244
+
2245
+ // Fast detect based on column assignment
2246
+ if (init) {
2247
+ detectedType = _typeResult(typeDetect, init(settings, col, i));
2248
+
2249
+ if (detectedType) {
2250
+ col.sType = detectedType;
2251
+ break;
2252
+ }
2253
+ }
2227
2254
 
2255
+ for ( k=0, ken=data.length ; k<ken ; k++ ) {
2228
2256
  if (! data[k]) {
2229
2257
  continue;
2230
2258
  }
@@ -2235,14 +2263,20 @@ function _fnColumnTypes ( settings )
2235
2263
  cache[k] = _fnGetCellData( settings, k, i, 'type' );
2236
2264
  }
2237
2265
 
2238
- detectedType = types[j]( cache[k], settings );
2266
+ // Only one data point in the column needs to match this function
2267
+ if (oneOf && ! one) {
2268
+ one = _typeResult(typeDetect, oneOf( cache[k], settings ));
2269
+ }
2270
+
2271
+ // All data points need to match this function
2272
+ detectedType = _typeResult(typeDetect, allOf( cache[k], settings ));
2239
2273
 
2240
2274
  // If null, then this type can't apply to this column, so
2241
2275
  // rather than testing all cells, break out. There is an
2242
2276
  // exception for the last type which is `html`. We need to
2243
2277
  // scan all rows since it is possible to mix string and HTML
2244
2278
  // types
2245
- if ( ! detectedType && j !== types.length-2 ) {
2279
+ if ( ! detectedType && j !== types.length-3 ) {
2246
2280
  break;
2247
2281
  }
2248
2282
 
@@ -2256,7 +2290,7 @@ function _fnColumnTypes ( settings )
2256
2290
 
2257
2291
  // Type is valid for all data points in the column - use this
2258
2292
  // type
2259
- if ( detectedType ) {
2293
+ if ( (oneOf && one && detectedType) || (!oneOf && detectedType) ) {
2260
2294
  col.sType = detectedType;
2261
2295
  break;
2262
2296
  }
@@ -2278,7 +2312,7 @@ function _fnColumnTypes ( settings )
2278
2312
 
2279
2313
  var renderer = _ext.type.render[col.sType];
2280
2314
 
2281
- // This can only happen once! There is no way to remover
2315
+ // This can only happen once! There is no way to remove
2282
2316
  // a renderer. After the first time the renderer has
2283
2317
  // already been set so createTr will run the renderer itself.
2284
2318
  if (renderer && ! col._render) {
@@ -3322,7 +3356,13 @@ function _fnDraw( oSettings, ajaxComplete )
3322
3356
  oSettings.bDrawing = true;
3323
3357
 
3324
3358
  /* Server-side processing draw intercept */
3325
- if ( !bServerSide )
3359
+ if ( oSettings.deferLoading )
3360
+ {
3361
+ oSettings.deferLoading = false;
3362
+ oSettings.iDraw++;
3363
+ _fnProcessingDisplay( oSettings, false );
3364
+ }
3365
+ else if ( !bServerSide )
3326
3366
  {
3327
3367
  oSettings.iDraw++;
3328
3368
  }
@@ -3423,6 +3463,9 @@ function _fnReDraw( settings, holdPosition, recompute )
3423
3463
  filter = features.bFilter;
3424
3464
 
3425
3465
  if (recompute === undefined || recompute === true) {
3466
+ // Resolve any column types that are unknown due to addition or invalidation
3467
+ _fnColumnTypes( settings );
3468
+
3426
3469
  if ( sort ) {
3427
3470
  _fnSort( settings );
3428
3471
  }
@@ -3478,113 +3521,154 @@ function _emptyRow ( settings ) {
3478
3521
 
3479
3522
 
3480
3523
  /**
3481
- * Convert a `layout` object given by a user to the object structure needed
3482
- * for the renderer. This is done twice, once for above and once for below
3483
- * the table. Ordering must also be considered.
3484
- *
3485
- * @param {*} settings DataTables settings object
3486
- * @param {*} layout Layout object to convert
3487
- * @param {string} side `top` or `bottom`
3488
- * @returns Converted array structure - one item for each row.
3524
+ * Expand the layout items into an object for the rendering function
3489
3525
  */
3490
- function _layoutArray ( settings, layout, side )
3491
- {
3492
- var groups = {};
3493
-
3494
- // Combine into like groups (e.g. `top`, `top2`, etc)
3495
- $.each( layout, function ( pos, val ) {
3496
- if (val === null) {
3497
- return;
3526
+ function _layoutItems (row, align, items) {
3527
+ if ( Array.isArray(items)) {
3528
+ for (var i=0 ; i<items.length ; i++) {
3529
+ _layoutItems(row, align, items[i]);
3498
3530
  }
3499
3531
 
3500
- var splitPos = pos.replace(/([A-Z])/g, ' $1').split(' ');
3532
+ return;
3533
+ }
3501
3534
 
3502
- if ( ! groups[ splitPos[0] ] ) {
3503
- groups[ splitPos[0] ] = {};
3504
- }
3535
+ var rowCell = row[align];
3505
3536
 
3506
- var align = splitPos.length === 1 ?
3507
- 'full' :
3508
- splitPos[1].toLowerCase();
3509
- var group = groups[ splitPos[0] ];
3510
- var groupRun = function (contents, innerVal) {
3511
- // If it is an object, then there can be multiple features contained in it
3512
- if ( $.isPlainObject( innerVal ) ) {
3513
- Object.keys(innerVal).map(function (key) {
3514
- contents.push( {
3515
- feature: key,
3516
- opts: innerVal[key]
3517
- });
3518
- });
3537
+ // If it is an object, then there can be multiple features contained in it
3538
+ if ( $.isPlainObject( items ) ) {
3539
+ // A feature plugin cannot be named "features" due to this check
3540
+ if (items.features) {
3541
+ if (items.rowId) {
3542
+ row.id = items.rowId;
3519
3543
  }
3520
- else {
3521
- contents.push(innerVal);
3544
+ if (items.rowClass) {
3545
+ row.className = items.rowClass;
3522
3546
  }
3523
- }
3524
3547
 
3525
- // Transform to an object with a contents property
3526
- if (! group[align] || ! group[align].contents) {
3527
- group[align] = { contents: [] };
3548
+ rowCell.id = items.id;
3549
+ rowCell.className = items.className;
3550
+
3551
+ _layoutItems(row, align, items.features);
3552
+ }
3553
+ else {
3554
+ Object.keys(items).map(function (key) {
3555
+ rowCell.contents.push( {
3556
+ feature: key,
3557
+ opts: items[key]
3558
+ });
3559
+ });
3528
3560
  }
3561
+ }
3562
+ else {
3563
+ rowCell.contents.push(items);
3564
+ }
3565
+ }
3566
+
3567
+ /**
3568
+ * Find, or create a layout row
3569
+ */
3570
+ function _layoutGetRow(rows, rowNum, align) {
3571
+ var row;
3572
+
3573
+ // Find existing rows
3574
+ for (var i=0; i<rows.length; i++) {
3575
+ row = rows[i];
3576
+
3577
+ if (row.rowNum === rowNum) {
3578
+ // full is on its own, but start and end share a row
3579
+ if (
3580
+ (align === 'full' && row.full) ||
3581
+ ((align === 'start' || align === 'end') && (row.start || row.end))
3582
+ ) {
3583
+ if (! row[align]) {
3584
+ row[align] = {
3585
+ contents: []
3586
+ };
3587
+ }
3529
3588
 
3530
- // Allow for an array or just a single object
3531
- if ( Array.isArray(val)) {
3532
- for (var i=0 ; i<val.length ; i++) {
3533
- groupRun(group[align].contents, val[i]);
3589
+ return row;
3534
3590
  }
3535
3591
  }
3536
- else {
3537
- groupRun(group[ align ].contents, val);
3592
+ }
3593
+
3594
+ // If we get this far, then there was no match, create a new row
3595
+ row = {
3596
+ rowNum: rowNum
3597
+ };
3598
+
3599
+ row[align] = {
3600
+ contents: []
3601
+ };
3602
+
3603
+ rows.push(row);
3604
+
3605
+ return row;
3606
+ }
3607
+
3608
+ /**
3609
+ * Convert a `layout` object given by a user to the object structure needed
3610
+ * for the renderer. This is done twice, once for above and once for below
3611
+ * the table. Ordering must also be considered.
3612
+ *
3613
+ * @param {*} settings DataTables settings object
3614
+ * @param {*} layout Layout object to convert
3615
+ * @param {string} side `top` or `bottom`
3616
+ * @returns Converted array structure - one item for each row.
3617
+ */
3618
+ function _layoutArray ( settings, layout, side ) {
3619
+ var rows = [];
3620
+
3621
+ // Split out into an array
3622
+ $.each( layout, function ( pos, items ) {
3623
+ if (items === null) {
3624
+ return;
3538
3625
  }
3539
3626
 
3540
- // And make contents an array
3541
- if ( ! Array.isArray( group[ align ].contents ) ) {
3542
- group[ align ].contents = [ group[ align ].contents ];
3627
+ var parts = pos.match(/^([a-z]+)([0-9]*)([A-Za-z]*)$/);
3628
+ var rowNum = parts[2]
3629
+ ? parts[2] * 1
3630
+ : 0;
3631
+ var align = parts[3]
3632
+ ? parts[3].toLowerCase()
3633
+ : 'full';
3634
+
3635
+ // Filter out the side we aren't interested in
3636
+ if (parts[1] !== side) {
3637
+ return;
3543
3638
  }
3544
- } );
3545
3639
 
3546
- var filtered = Object.keys(groups)
3547
- .map( function ( pos ) {
3548
- // Filter to only the side we need
3549
- if ( pos.indexOf(side) !== 0 ) {
3550
- return null;
3551
- }
3640
+ // Get or create the row we should attach to
3641
+ var row = _layoutGetRow(rows, rowNum, align);
3552
3642
 
3553
- return {
3554
- name: pos,
3555
- val: groups[pos]
3556
- };
3557
- } )
3558
- .filter( function (item) {
3559
- return item !== null;
3560
- });
3643
+ _layoutItems(row, align, items);
3644
+ });
3561
3645
 
3562
3646
  // Order by item identifier
3563
- filtered.sort( function ( a, b ) {
3564
- var order1 = a.name.replace(/[^0-9]/g, '') * 1;
3565
- var order2 = b.name.replace(/[^0-9]/g, '') * 1;
3647
+ rows.sort( function ( a, b ) {
3648
+ var order1 = a.rowNum;
3649
+ var order2 = b.rowNum;
3650
+
3651
+ // If both in the same row, then the row with `full` comes first
3652
+ if (order1 === order2) {
3653
+ var ret = a.full && ! b.full ? -1 : 1;
3654
+
3655
+ return side === 'bottom'
3656
+ ? ret * -1
3657
+ : ret;
3658
+ }
3566
3659
 
3567
3660
  return order2 - order1;
3568
3661
  } );
3569
-
3662
+
3663
+ // Invert for below the table
3570
3664
  if ( side === 'bottom' ) {
3571
- filtered.reverse();
3665
+ rows.reverse();
3572
3666
  }
3573
3667
 
3574
- // Split into rows
3575
- var rows = [];
3576
- for ( var i=0, ien=filtered.length ; i<ien ; i++ ) {
3577
- if ( filtered[i].val.full ) {
3578
- rows.push( { full: filtered[i].val.full } );
3579
- _layoutResolve( settings, rows[ rows.length - 1 ] );
3580
-
3581
- delete filtered[i].val.full;
3582
- }
3668
+ for (var row = 0; row<rows.length; row++) {
3669
+ delete rows[row].rowNum;
3583
3670
 
3584
- if ( Object.keys(filtered[i].val).length ) {
3585
- rows.push( filtered[i].val );
3586
- _layoutResolve( settings, rows[ rows.length - 1 ] );
3587
- }
3671
+ _layoutResolve(settings, rows[row]);
3588
3672
  }
3589
3673
 
3590
3674
  return rows;
@@ -3608,6 +3692,10 @@ function _layoutResolve( settings, row ) {
3608
3692
  };
3609
3693
 
3610
3694
  var resolve = function ( item ) {
3695
+ if (! row[ item ]) {
3696
+ return;
3697
+ }
3698
+
3611
3699
  var line = row[ item ].contents;
3612
3700
 
3613
3701
  for ( var i=0, ien=line.length ; i<ien ; i++ ) {
@@ -3635,9 +3723,9 @@ function _layoutResolve( settings, row ) {
3635
3723
  }
3636
3724
  };
3637
3725
 
3638
- $.each( row, function ( key ) {
3639
- resolve( key );
3640
- } );
3726
+ resolve('start');
3727
+ resolve('end');
3728
+ resolve('full');
3641
3729
  }
3642
3730
 
3643
3731
 
@@ -3970,6 +4058,17 @@ function _fnBuildAjax( oSettings, data, fn )
3970
4058
  _fnLog( oSettings, 0, error );
3971
4059
  }
3972
4060
 
4061
+ // Microsoft often wrap JSON as a string in another JSON object
4062
+ // Let's handle that automatically
4063
+ if (json.d && typeof json.d === 'string') {
4064
+ try {
4065
+ json = JSON.parse(json.d);
4066
+ }
4067
+ catch (e) {
4068
+ // noop
4069
+ }
4070
+ }
4071
+
3973
4072
  oSettings.json = json;
3974
4073
 
3975
4074
  _fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR], true );
@@ -4046,11 +4145,11 @@ function _fnBuildAjax( oSettings, data, fn )
4046
4145
  else {
4047
4146
  // Object to extend the base settings
4048
4147
  oSettings.jqXHR = $.ajax( baseAjax );
4148
+ }
4049
4149
 
4050
- // Restore for next time around
4051
- if ( ajaxData ) {
4052
- ajax.data = ajaxData;
4053
- }
4150
+ // Restore for next time around
4151
+ if ( ajaxData ) {
4152
+ ajax.data = ajaxData;
4054
4153
  }
4055
4154
  }
4056
4155
 
@@ -4275,10 +4374,6 @@ function _fnFilterComplete ( settings, input )
4275
4374
  {
4276
4375
  var columnsSearch = settings.aoPreSearchCols;
4277
4376
 
4278
- // Resolve any column types that are unknown due to addition or invalidation
4279
- // @todo As per sort - can this be moved into an event handler?
4280
- _fnColumnTypes( settings );
4281
-
4282
4377
  // In server-side processing all filtering is done by the server, so no point hanging around here
4283
4378
  if ( _fnDataSource( settings ) != 'ssp' )
4284
4379
  {
@@ -4584,8 +4679,11 @@ function _fnFilterData ( settings )
4584
4679
  function _fnInitialise ( settings )
4585
4680
  {
4586
4681
  var i, iAjaxStart=settings.iInitDisplayStart;
4682
+ var init = settings.oInit;
4683
+ var deferLoading = settings.deferLoading;
4684
+ var dataSrc = _fnDataSource( settings );
4587
4685
 
4588
- /* Ensure that the table data is fully initialised */
4686
+ // Ensure that the table data is fully initialised
4589
4687
  if ( ! settings.bInitialised ) {
4590
4688
  setTimeout( function(){ _fnInitialise( settings ); }, 200 );
4591
4689
  return;
@@ -4597,52 +4695,68 @@ function _fnInitialise ( settings )
4597
4695
  _fnDrawHead( settings, settings.aoHeader );
4598
4696
  _fnDrawHead( settings, settings.aoFooter );
4599
4697
 
4600
- // Enable features
4601
- _fnAddOptionsHtml( settings );
4602
- _fnSortInit( settings );
4698
+ // Load the table's state (if needed) and then render around it and draw draw
4699
+ _fnLoadState( settings, init, function () {
4700
+ // Local data load
4701
+ // Check if there is data passing into the constructor
4702
+ if ( init.aaData ) {
4703
+ for ( i=0 ; i<init.aaData.length ; i++ ) {
4704
+ _fnAddData( settings, init.aaData[ i ] );
4705
+ }
4706
+ }
4707
+ else if ( deferLoading || dataSrc == 'dom' ) {
4708
+ // Grab the data from the page
4709
+ _fnAddTr( settings, $(settings.nTBody).children('tr') );
4710
+ }
4603
4711
 
4604
- _colGroup( settings );
4712
+ // Filter not yet applied - copy the display master
4713
+ settings.aiDisplay = settings.aiDisplayMaster.slice();
4605
4714
 
4606
- /* Okay to show that something is going on now */
4607
- _fnProcessingDisplay( settings, true );
4715
+ // Enable features
4716
+ _fnAddOptionsHtml( settings );
4717
+ _fnSortInit( settings );
4608
4718
 
4609
- _fnCallbackFire( settings, null, 'preInit', [settings], true );
4719
+ _colGroup( settings );
4610
4720
 
4611
- // If there is default sorting required - let's do it. The sort function
4612
- // will do the drawing for us. Otherwise we draw the table regardless of the
4613
- // Ajax source - this allows the table to look initialised for Ajax sourcing
4614
- // data (show 'loading' message possibly)
4615
- _fnReDraw( settings );
4721
+ /* Okay to show that something is going on now */
4722
+ _fnProcessingDisplay( settings, true );
4616
4723
 
4617
- var dataSrc = _fnDataSource( settings );
4724
+ _fnCallbackFire( settings, null, 'preInit', [settings], true );
4618
4725
 
4619
- // Server-side processing init complete is done by _fnAjaxUpdateDraw
4620
- if ( dataSrc != 'ssp' ) {
4621
- // if there is an ajax source load the data
4622
- if ( dataSrc == 'ajax' ) {
4623
- _fnBuildAjax( settings, {}, function(json) {
4624
- var aData = _fnAjaxDataSrc( settings, json );
4726
+ // If there is default sorting required - let's do it. The sort function
4727
+ // will do the drawing for us. Otherwise we draw the table regardless of the
4728
+ // Ajax source - this allows the table to look initialised for Ajax sourcing
4729
+ // data (show 'loading' message possibly)
4730
+ _fnReDraw( settings );
4625
4731
 
4626
- // Got the data - add it to the table
4627
- for ( i=0 ; i<aData.length ; i++ ) {
4628
- _fnAddData( settings, aData[i] );
4629
- }
4732
+ // Server-side processing init complete is done by _fnAjaxUpdateDraw
4733
+ if ( dataSrc != 'ssp' || deferLoading ) {
4734
+ // if there is an ajax source load the data
4735
+ if ( dataSrc == 'ajax' ) {
4736
+ _fnBuildAjax( settings, {}, function(json) {
4737
+ var aData = _fnAjaxDataSrc( settings, json );
4630
4738
 
4631
- // Reset the init display for cookie saving. We've already done
4632
- // a filter, and therefore cleared it before. So we need to make
4633
- // it appear 'fresh'
4634
- settings.iInitDisplayStart = iAjaxStart;
4739
+ // Got the data - add it to the table
4740
+ for ( i=0 ; i<aData.length ; i++ ) {
4741
+ _fnAddData( settings, aData[i] );
4742
+ }
4635
4743
 
4636
- _fnReDraw( settings );
4637
- _fnProcessingDisplay( settings, false );
4744
+ // Reset the init display for cookie saving. We've already done
4745
+ // a filter, and therefore cleared it before. So we need to make
4746
+ // it appear 'fresh'
4747
+ settings.iInitDisplayStart = iAjaxStart;
4748
+
4749
+ _fnReDraw( settings );
4750
+ _fnProcessingDisplay( settings, false );
4751
+ _fnInitComplete( settings );
4752
+ }, settings );
4753
+ }
4754
+ else {
4638
4755
  _fnInitComplete( settings );
4639
- }, settings );
4640
- }
4641
- else {
4642
- _fnInitComplete( settings );
4643
- _fnProcessingDisplay( settings, false );
4756
+ _fnProcessingDisplay( settings, false );
4757
+ }
4644
4758
  }
4645
- }
4759
+ } );
4646
4760
  }
4647
4761
 
4648
4762
 
@@ -4799,6 +4913,30 @@ function _fnProcessingDisplay ( settings, show )
4799
4913
  {
4800
4914
  _fnCallbackFire( settings, null, 'processing', [settings, show] );
4801
4915
  }
4916
+
4917
+ /**
4918
+ * Show the processing element if an action takes longer than a given time
4919
+ *
4920
+ * @param {*} settings DataTables settings object
4921
+ * @param {*} enable Do (true) or not (false) async processing (local feature enablement)
4922
+ * @param {*} run Function to run
4923
+ */
4924
+ function _fnProcessingRun( settings, enable, run ) {
4925
+ if (! enable) {
4926
+ // Immediate execution, synchronous
4927
+ run();
4928
+ }
4929
+ else {
4930
+ _fnProcessingDisplay(settings, true);
4931
+
4932
+ // Allow the processing display to show if needed
4933
+ setTimeout(function () {
4934
+ run();
4935
+
4936
+ _fnProcessingDisplay(settings, false);
4937
+ }, 0);
4938
+ }
4939
+ }
4802
4940
  /**
4803
4941
  * Add any control elements for the table - specifically scrolling
4804
4942
  * @param {object} settings dataTables settings object
@@ -5129,8 +5267,17 @@ function _fnCalculateColumnWidths ( settings )
5129
5267
  tableWidthAttr = table.getAttribute('width'), // from DOM element
5130
5268
  tableContainer = table.parentNode,
5131
5269
  i, column, columnIdx;
5132
-
5270
+
5133
5271
  var styleWidth = table.style.width;
5272
+
5273
+ // If there is no width applied as a CSS style or as an attribute, we assume that
5274
+ // the width is intended to be 100%, which is usually is in CSS, but it is very
5275
+ // difficult to correctly parse the rules to get the final result.
5276
+ if ( ! styleWidth && ! tableWidthAttr) {
5277
+ table.style.width = '100%';
5278
+ styleWidth = '100%';
5279
+ }
5280
+
5134
5281
  if ( styleWidth && styleWidth.indexOf('%') !== -1 ) {
5135
5282
  tableWidthAttr = styleWidth;
5136
5283
  }
@@ -5444,22 +5591,16 @@ function _fnSortAttachListener(settings, node, selector, column, callback) {
5444
5591
  }
5445
5592
 
5446
5593
  if (run) {
5447
- _fnProcessingDisplay( settings, true );
5448
-
5449
- // Allow the processing display to show
5450
- setTimeout( function () {
5594
+ _fnProcessingRun(settings, true, function () {
5451
5595
  _fnSort( settings );
5452
5596
  _fnSortDisplay( settings, settings.aiDisplay );
5453
5597
 
5454
- // Sort processing done - redraw has its own processing display
5455
- _fnProcessingDisplay( settings, false );
5456
-
5457
5598
  _fnReDraw( settings, false, false );
5458
5599
 
5459
5600
  if (callback) {
5460
5601
  callback();
5461
5602
  }
5462
- }, 0);
5603
+ });
5463
5604
  }
5464
5605
  }
5465
5606
  } );
@@ -5618,11 +5759,6 @@ function _fnSort ( oSettings, col, dir )
5618
5759
  displayMaster = oSettings.aiDisplayMaster,
5619
5760
  aSort;
5620
5761
 
5621
- // Resolve any column types that are unknown due to addition or invalidation
5622
- // @todo Can this be moved into a 'data-ready' handler which is called when
5623
- // data is going to be used in the table?
5624
- _fnColumnTypes( oSettings );
5625
-
5626
5762
  // Allow a specific column to be sorted, which will _not_ alter the display
5627
5763
  // master
5628
5764
  if (col !== undefined) {
@@ -5659,7 +5795,7 @@ function _fnSort ( oSettings, col, dir )
5659
5795
 
5660
5796
  // If the first sort is desc, then reverse the array to preserve original
5661
5797
  // order, just in reverse
5662
- if (aSort.length && aSort[0].dir === 'desc') {
5798
+ if (aSort.length && aSort[0].dir === 'desc' && oSettings.orderDescReverse) {
5663
5799
  aiOrig.reverse();
5664
5800
  }
5665
5801
 
@@ -5729,6 +5865,7 @@ function _fnSort ( oSettings, col, dir )
5729
5865
  if (col === undefined) {
5730
5866
  // Tell the draw function that we have sorted the data
5731
5867
  oSettings.bSorted = true;
5868
+ oSettings.sortDetails = aSort;
5732
5869
 
5733
5870
  _fnCallbackFire( oSettings, null, 'order', [oSettings, aSort] );
5734
5871
  }
@@ -7507,6 +7644,16 @@ var _selector_row_indexes = function ( settings, opts )
7507
7644
  order = opts.order, // applied, current, index (original - compatibility with 1.9)
7508
7645
  page = opts.page; // all, current
7509
7646
 
7647
+ if ( _fnDataSource( settings ) == 'ssp' ) {
7648
+ // In server-side processing mode, most options are irrelevant since
7649
+ // rows not shown don't exist and the index order is the applied order
7650
+ // Removed is a special case - for consistency just return an empty
7651
+ // array
7652
+ return search === 'removed' ?
7653
+ [] :
7654
+ _range( 0, displayMaster.length );
7655
+ }
7656
+
7510
7657
  if ( page == 'current' ) {
7511
7658
  // Current page implies that order=current and filter=applied, since it is
7512
7659
  // fairly senseless otherwise, regardless of what order and search actually
@@ -8178,7 +8325,7 @@ _api_register( [
8178
8325
  _api_register( _child_obj+'.isShown()', function () {
8179
8326
  var ctx = this.context;
8180
8327
 
8181
- if ( ctx.length && this.length ) {
8328
+ if ( ctx.length && this.length && ctx[0].aoData[ this[0] ] ) {
8182
8329
  // _detailsShown as false or undefined will fall through to return false
8183
8330
  return ctx[0].aoData[ this[0] ]._detailsShow || false;
8184
8331
  }
@@ -8201,7 +8348,7 @@ _api_register( _child_obj+'.isShown()', function () {
8201
8348
  // can be an array of these items, comma separated list, or an array of comma
8202
8349
  // separated lists
8203
8350
 
8204
- var __re_column_selector = /^([^:]+):(name|title|visIdx|visible)$/;
8351
+ var __re_column_selector = /^([^:]+)?:(name|title|visIdx|visible)$/;
8205
8352
 
8206
8353
 
8207
8354
  // r1 and r2 are redundant - but it means that the parameters match for the
@@ -8273,17 +8420,24 @@ var __column_selector = function ( settings, selector, opts )
8273
8420
  switch( match[2] ) {
8274
8421
  case 'visIdx':
8275
8422
  case 'visible':
8276
- var idx = parseInt( match[1], 10 );
8277
- // Visible index given, convert to column index
8278
- if ( idx < 0 ) {
8279
- // Counting from the right
8280
- var visColumns = columns.map( function (col,i) {
8281
- return col.bVisible ? i : null;
8282
- } );
8283
- return [ visColumns[ visColumns.length + idx ] ];
8423
+ if (match[1]) {
8424
+ var idx = parseInt( match[1], 10 );
8425
+ // Visible index given, convert to column index
8426
+ if ( idx < 0 ) {
8427
+ // Counting from the right
8428
+ var visColumns = columns.map( function (col,i) {
8429
+ return col.bVisible ? i : null;
8430
+ } );
8431
+ return [ visColumns[ visColumns.length + idx ] ];
8432
+ }
8433
+ // Counting from the left
8434
+ return [ _fnVisibleToColumnIndex( settings, idx ) ];
8284
8435
  }
8285
- // Counting from the left
8286
- return [ _fnVisibleToColumnIndex( settings, idx ) ];
8436
+
8437
+ // `:visible` on its own
8438
+ return columns.map( function (col, i) {
8439
+ return col.bVisible ? i : null;
8440
+ } );
8287
8441
 
8288
8442
  case 'name':
8289
8443
  // match by name. `names` is column index complete and in order
@@ -9133,23 +9287,60 @@ _api_register( 'state.save()', function () {
9133
9287
  } );
9134
9288
 
9135
9289
  /**
9136
- * Set the jQuery or window object to be used by DataTables
9137
- *
9138
- * @param {*} module Library / container object
9139
- * @param {string} [type] Library or container type `lib`, `win` or `datetime`.
9140
- * If not provided, automatic detection is attempted.
9141
- */
9142
- DataTable.use = function (module, type) {
9143
- if (type === 'lib' || module.fn) {
9290
+ * Set the libraries that DataTables uses, or the global objects.
9291
+ * Note that the arguments can be either way around (legacy support)
9292
+ * and the second is optional. See docs.
9293
+ */
9294
+ DataTable.use = function (arg1, arg2) {
9295
+ // Reverse arguments for legacy support
9296
+ var module = typeof arg1 === 'string'
9297
+ ? arg2
9298
+ : arg1;
9299
+ var type = typeof arg2 === 'string'
9300
+ ? arg2
9301
+ : arg1;
9302
+
9303
+ // Getter
9304
+ if (module === undefined && typeof type === 'string') {
9305
+ switch (type) {
9306
+ case 'lib':
9307
+ case 'jq':
9308
+ return $;
9309
+
9310
+ case 'win':
9311
+ return window;
9312
+
9313
+ case 'datetime':
9314
+ return DataTable.DateTime;
9315
+
9316
+ case 'luxon':
9317
+ return __luxon;
9318
+
9319
+ case 'moment':
9320
+ return __moment;
9321
+
9322
+ default:
9323
+ return null;
9324
+ }
9325
+ }
9326
+
9327
+ // Setter
9328
+ if (type === 'lib' || type === 'jq' || (module && module.fn && module.fn.jquery)) {
9144
9329
  $ = module;
9145
9330
  }
9146
- else if (type == 'win' || module.document) {
9331
+ else if (type == 'win' || (module && module.document)) {
9147
9332
  window = module;
9148
9333
  document = module.document;
9149
9334
  }
9150
- else if (type === 'datetime' || module.type === 'DateTime') {
9335
+ else if (type === 'datetime' || (module && module.type === 'DateTime')) {
9151
9336
  DataTable.DateTime = module;
9152
9337
  }
9338
+ else if (type === 'luxon' || (module && module.FixedOffsetZone)) {
9339
+ __luxon = module;
9340
+ }
9341
+ else if (type === 'moment' || (module && module.isMoment)) {
9342
+ __moment = module;
9343
+ }
9153
9344
  }
9154
9345
 
9155
9346
  /**
@@ -9558,7 +9749,7 @@ _api_register( 'i18n()', function ( token, def, plural ) {
9558
9749
  * @type string
9559
9750
  * @default Version number
9560
9751
  */
9561
- DataTable.version = "2.0.7";
9752
+ DataTable.version = "2.1.0";
9562
9753
 
9563
9754
  /**
9564
9755
  * Private data store, containing all of the settings objects that are
@@ -10403,7 +10594,8 @@ DataTable.defaults = {
10403
10594
  first: 'First',
10404
10595
  last: 'Last',
10405
10596
  next: 'Next',
10406
- previous: 'Previous'
10597
+ previous: 'Previous',
10598
+ number: ''
10407
10599
  }
10408
10600
  },
10409
10601
 
@@ -10583,6 +10775,10 @@ DataTable.defaults = {
10583
10775
  },
10584
10776
 
10585
10777
 
10778
+ /** The initial data order is reversed when `desc` ordering */
10779
+ orderDescReverse: true,
10780
+
10781
+
10586
10782
  /**
10587
10783
  * This parameter allows you to have define the global filtering state at
10588
10784
  * initialisation time. As an object the `search` parameter must be
@@ -10631,7 +10827,7 @@ DataTable.defaults = {
10631
10827
  * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers
10632
10828
  * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers
10633
10829
  */
10634
- "sPaginationType": "full_numbers",
10830
+ "sPaginationType": "",
10635
10831
 
10636
10832
 
10637
10833
  /**
@@ -10701,7 +10897,13 @@ DataTable.defaults = {
10701
10897
  /**
10702
10898
  * Caption value
10703
10899
  */
10704
- "caption": null
10900
+ "caption": null,
10901
+
10902
+
10903
+ /**
10904
+ * For server-side processing - use the data from the DOM for the first draw
10905
+ */
10906
+ iDeferLoading: null
10705
10907
  };
10706
10908
 
10707
10909
  _fnHungarianMap( DataTable.defaults );
@@ -11644,7 +11846,10 @@ DataTable.models.oSettings = {
11644
11846
 
11645
11847
  captionNode: null,
11646
11848
 
11647
- colgroup: null
11849
+ colgroup: null,
11850
+
11851
+ /** Delay loading of data */
11852
+ deferLoading: null
11648
11853
  };
11649
11854
 
11650
11855
  /**
@@ -11668,7 +11873,7 @@ $.extend( extPagination, {
11668
11873
  },
11669
11874
 
11670
11875
  full: function () {
11671
- return [ 'first', 'previous', 'next', 'last' ];
11876
+ return [ 'first', 'previous', 'next', 'last' ];
11672
11877
  },
11673
11878
 
11674
11879
  numbers: function () {
@@ -11682,11 +11887,11 @@ $.extend( extPagination, {
11682
11887
  full_numbers: function () {
11683
11888
  return [ 'first', 'previous', 'numbers', 'next', 'last' ];
11684
11889
  },
11685
-
11890
+
11686
11891
  first_last: function () {
11687
11892
  return ['first', 'last'];
11688
11893
  },
11689
-
11894
+
11690
11895
  first_last_numbers: function () {
11691
11896
  return ['first', 'numbers', 'last'];
11692
11897
  },
@@ -11768,38 +11973,56 @@ var _filterString = function (stripHtml, normalize) {
11768
11973
  * to make working with DataTables a little bit easier.
11769
11974
  */
11770
11975
 
11771
- function __mldFnName(name) {
11772
- return name.replace(/[\W]/g, '_')
11773
- }
11774
-
11775
- // Common logic for moment, luxon or a date action
11776
- function __mld( dt, momentFn, luxonFn, dateFn, arg1 ) {
11777
- if (window.moment) {
11778
- return dt[momentFn]( arg1 );
11976
+ /**
11977
+ * Common logic for moment, luxon or a date action.
11978
+ *
11979
+ * Happens after __mldObj, so don't need to call `resolveWindowsLibs` again
11980
+ */
11981
+ function __mld( dtLib, momentFn, luxonFn, dateFn, arg1 ) {
11982
+ if (__moment) {
11983
+ return dtLib[momentFn]( arg1 );
11779
11984
  }
11780
- else if (window.luxon) {
11781
- return dt[luxonFn]( arg1 );
11985
+ else if (__luxon) {
11986
+ return dtLib[luxonFn]( arg1 );
11782
11987
  }
11783
11988
 
11784
- return dateFn ? dt[dateFn]( arg1 ) : dt;
11989
+ return dateFn ? dtLib[dateFn]( arg1 ) : dtLib;
11785
11990
  }
11786
11991
 
11787
11992
 
11788
11993
  var __mlWarning = false;
11994
+ var __luxon; // Can be assigned in DateTeble.use()
11995
+ var __moment; // Can be assigned in DateTeble.use()
11996
+
11997
+ /**
11998
+ *
11999
+ */
12000
+ function resolveWindowLibs() {
12001
+ if (window.luxon && ! __luxon) {
12002
+ __luxon = window.luxon;
12003
+ }
12004
+
12005
+ if (window.moment && ! __moment) {
12006
+ __moment = window.moment;
12007
+ }
12008
+ }
12009
+
11789
12010
  function __mldObj (d, format, locale) {
11790
12011
  var dt;
11791
12012
 
11792
- if (window.moment) {
11793
- dt = window.moment.utc( d, format, locale, true );
12013
+ resolveWindowLibs();
12014
+
12015
+ if (__moment) {
12016
+ dt = __moment.utc( d, format, locale, true );
11794
12017
 
11795
12018
  if (! dt.isValid()) {
11796
12019
  return null;
11797
12020
  }
11798
12021
  }
11799
- else if (window.luxon) {
12022
+ else if (__luxon) {
11800
12023
  dt = format && typeof d === 'string'
11801
- ? window.luxon.DateTime.fromFormat( d, format )
11802
- : window.luxon.DateTime.fromISO( d );
12024
+ ? __luxon.DateTime.fromFormat( d, format )
12025
+ : __luxon.DateTime.fromISO( d );
11803
12026
 
11804
12027
  if (! dt.isValid) {
11805
12028
  return null;
@@ -11844,7 +12067,7 @@ function __mlHelper (localeString) {
11844
12067
  from = null;
11845
12068
  }
11846
12069
 
11847
- var typeName = 'datetime' + (to ? '-' + __mldFnName(to) : '');
12070
+ var typeName = 'datetime' + (to ? '-' + to : '');
11848
12071
 
11849
12072
  // Add type detection and sorting specific to this date format - we need to be able to identify
11850
12073
  // date type columns as such, rather than as numbers in extensions. Hence the need for this.
@@ -11947,7 +12170,7 @@ if (window.Intl !== undefined) {
11947
12170
 
11948
12171
  // Formatted date time detection - use by declaring the formats you are going to use
11949
12172
  DataTable.datetime = function ( format, locale ) {
11950
- var typeName = 'datetime-detect-' + __mldFnName(format);
12173
+ var typeName = 'datetime-' + format;
11951
12174
 
11952
12175
  if (! locale) {
11953
12176
  locale = 'en';
@@ -12102,27 +12325,20 @@ DataTable.type = function (name, prop, val) {
12102
12325
  var setProp = function(prop, propVal) {
12103
12326
  _extTypes[prop][name] = propVal;
12104
12327
  };
12105
- var setDetect = function (fn) {
12106
- // Wrap to allow the function to return `true` rather than
12107
- // specifying the type name.
12108
- var cb = function (d, s) {
12109
- var ret = fn(d, s);
12110
-
12111
- return ret === true
12112
- ? name
12113
- : ret;
12114
- };
12115
- Object.defineProperty(cb, "name", {value: name});
12328
+ var setDetect = function (detect) {
12329
+ // `detect` can be a function or an object - we set a name
12330
+ // property for either - that is used for the detection
12331
+ Object.defineProperty(detect, "name", {value: name});
12116
12332
 
12117
- var idx = _extTypes.detect.findIndex(function (fn) {
12118
- return fn.name === name;
12333
+ var idx = _extTypes.detect.findIndex(function (item) {
12334
+ return item.name === name;
12119
12335
  });
12120
12336
 
12121
12337
  if (idx === -1) {
12122
- _extTypes.detect.unshift(cb);
12338
+ _extTypes.detect.unshift(detect);
12123
12339
  }
12124
12340
  else {
12125
- _extTypes.detect.splice(idx, 1, cb);
12341
+ _extTypes.detect.splice(idx, 1, detect);
12126
12342
  }
12127
12343
  };
12128
12344
  var setOrder = function (obj) {
@@ -12182,6 +12398,19 @@ DataTable.types = function () {
12182
12398
  });
12183
12399
  };
12184
12400
 
12401
+ var __diacriticSort = function (a, b) {
12402
+ a = a.toString().toLowerCase();
12403
+ b = b.toString().toLowerCase();
12404
+
12405
+ // Checked for `navigator.languages` support in `oneOf` so this code can't execute in old
12406
+ // Safari and thus can disable this check
12407
+ // eslint-disable-next-line compat/compat
12408
+ return a.localeCompare(b, navigator.languages[0] || navigator.language, {
12409
+ numeric: true,
12410
+ ignorePunctuation: true,
12411
+ });
12412
+ }
12413
+
12185
12414
  //
12186
12415
  // Built in data types
12187
12416
  //
@@ -12206,11 +12435,38 @@ DataTable.type('string', {
12206
12435
  search: _filterString(false, true)
12207
12436
  });
12208
12437
 
12438
+ DataTable.type('string-utf8', {
12439
+ detect: {
12440
+ allOf: function ( d ) {
12441
+ return true;
12442
+ },
12443
+ oneOf: function ( d ) {
12444
+ // At least one data point must contain a non-ASCII character
12445
+ // This line will also check if navigator.languages is supported or not. If not (Safari 10.0-)
12446
+ // this data type won't be supported.
12447
+ // eslint-disable-next-line compat/compat
12448
+ return ! _empty( d ) && navigator.languages && typeof d === 'string' && d.match(/[^\x00-\x7F]/);
12449
+ }
12450
+ },
12451
+ order: {
12452
+ asc: __diacriticSort,
12453
+ desc: function (a, b) {
12454
+ return __diacriticSort(a, b) * -1;
12455
+ }
12456
+ },
12457
+ search: _filterString(false, true)
12458
+ });
12459
+
12209
12460
 
12210
12461
  DataTable.type('html', {
12211
- detect: function ( d ) {
12212
- return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ?
12213
- 'html' : null;
12462
+ detect: {
12463
+ allOf: function ( d ) {
12464
+ return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1);
12465
+ },
12466
+ oneOf: function ( d ) {
12467
+ // At least one data point must contain a `<`
12468
+ return ! _empty( d ) && typeof d === 'string' && d.indexOf('<') !== -1;
12469
+ }
12214
12470
  },
12215
12471
  order: {
12216
12472
  pre: function ( a ) {
@@ -12227,16 +12483,21 @@ DataTable.type('html', {
12227
12483
 
12228
12484
  DataTable.type('date', {
12229
12485
  className: 'dt-type-date',
12230
- detect: function ( d )
12231
- {
12232
- // V8 tries _very_ hard to make a string passed into `Date.parse()`
12233
- // valid, so we need to use a regex to restrict date formats. Use a
12234
- // plug-in for anything other than ISO8601 style strings
12235
- if ( d && !(d instanceof Date) && ! _re_date.test(d) ) {
12236
- return null;
12486
+ detect: {
12487
+ allOf: function ( d ) {
12488
+ // V8 tries _very_ hard to make a string passed into `Date.parse()`
12489
+ // valid, so we need to use a regex to restrict date formats. Use a
12490
+ // plug-in for anything other than ISO8601 style strings
12491
+ if ( d && !(d instanceof Date) && ! _re_date.test(d) ) {
12492
+ return null;
12493
+ }
12494
+ var parsed = Date.parse(d);
12495
+ return (parsed !== null && !isNaN(parsed)) || _empty(d);
12496
+ },
12497
+ oneOf: function ( d ) {
12498
+ // At least one entry must be a date or a string with a date
12499
+ return (d instanceof Date) || (typeof d === 'string' && _re_date.test(d));
12237
12500
  }
12238
- var parsed = Date.parse(d);
12239
- return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null;
12240
12501
  },
12241
12502
  order: {
12242
12503
  pre: function ( d ) {
@@ -12249,10 +12510,16 @@ DataTable.type('date', {
12249
12510
 
12250
12511
  DataTable.type('html-num-fmt', {
12251
12512
  className: 'dt-type-numeric',
12252
- detect: function ( d, settings )
12253
- {
12254
- var decimal = settings.oLanguage.sDecimal;
12255
- return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt' : null;
12513
+ detect: {
12514
+ allOf: function ( d, settings ) {
12515
+ var decimal = settings.oLanguage.sDecimal;
12516
+ return _htmlNumeric( d, decimal, true, false );
12517
+ },
12518
+ oneOf: function (d, settings) {
12519
+ // At least one data point must contain a numeric value
12520
+ var decimal = settings.oLanguage.sDecimal;
12521
+ return _htmlNumeric( d, decimal, true, false );
12522
+ }
12256
12523
  },
12257
12524
  order: {
12258
12525
  pre: function ( d, s ) {
@@ -12266,10 +12533,16 @@ DataTable.type('html-num-fmt', {
12266
12533
 
12267
12534
  DataTable.type('html-num', {
12268
12535
  className: 'dt-type-numeric',
12269
- detect: function ( d, settings )
12270
- {
12271
- var decimal = settings.oLanguage.sDecimal;
12272
- return _htmlNumeric( d, decimal ) ? 'html-num' : null;
12536
+ detect: {
12537
+ allOf: function ( d, settings ) {
12538
+ var decimal = settings.oLanguage.sDecimal;
12539
+ return _htmlNumeric( d, decimal, false, true );
12540
+ },
12541
+ oneOf: function (d, settings) {
12542
+ // At least one data point must contain a numeric value
12543
+ var decimal = settings.oLanguage.sDecimal;
12544
+ return _htmlNumeric( d, decimal, false, false );
12545
+ }
12273
12546
  },
12274
12547
  order: {
12275
12548
  pre: function ( d, s ) {
@@ -12283,10 +12556,16 @@ DataTable.type('html-num', {
12283
12556
 
12284
12557
  DataTable.type('num-fmt', {
12285
12558
  className: 'dt-type-numeric',
12286
- detect: function ( d, settings )
12287
- {
12288
- var decimal = settings.oLanguage.sDecimal;
12289
- return _isNumber( d, decimal, true ) ? 'num-fmt' : null;
12559
+ detect: {
12560
+ allOf: function ( d, settings ) {
12561
+ var decimal = settings.oLanguage.sDecimal;
12562
+ return _isNumber( d, decimal, true, true );
12563
+ },
12564
+ oneOf: function (d, settings) {
12565
+ // At least one data point must contain a numeric value
12566
+ var decimal = settings.oLanguage.sDecimal;
12567
+ return _isNumber( d, decimal, true, false );
12568
+ }
12290
12569
  },
12291
12570
  order: {
12292
12571
  pre: function ( d, s ) {
@@ -12299,10 +12578,16 @@ DataTable.type('num-fmt', {
12299
12578
 
12300
12579
  DataTable.type('num', {
12301
12580
  className: 'dt-type-numeric',
12302
- detect: function ( d, settings )
12303
- {
12304
- var decimal = settings.oLanguage.sDecimal;
12305
- return _isNumber( d, decimal ) ? 'num' : null;
12581
+ detect: {
12582
+ allOf: function ( d, settings ) {
12583
+ var decimal = settings.oLanguage.sDecimal;
12584
+ return _isNumber( d, decimal, false, true );
12585
+ },
12586
+ oneOf: function (d, settings) {
12587
+ // At least one data point must contain a numeric value
12588
+ var decimal = settings.oLanguage.sDecimal;
12589
+ return _isNumber( d, decimal, false, false );
12590
+ }
12306
12591
  },
12307
12592
  order: {
12308
12593
  pre: function (d, s) {
@@ -12386,7 +12671,7 @@ $.extend( true, DataTable.ext.renderer, {
12386
12671
  // `DT` namespace will allow the event to be removed automatically
12387
12672
  // on destroy, while the `dt` namespaced event is the one we are
12388
12673
  // listening for
12389
- $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting ) {
12674
+ $(settings.nTable).on( 'order.dt.DT column-visibility.dt.DT', function ( e, ctx ) {
12390
12675
  if ( settings !== ctx ) { // need to check this this is the host
12391
12676
  return; // table, not a nested one
12392
12677
  }
@@ -12398,9 +12683,15 @@ $.extend( true, DataTable.ext.renderer, {
12398
12683
  var ariaType = '';
12399
12684
  var indexes = columns.indexes();
12400
12685
  var sortDirs = columns.orderable(true).flatten();
12401
- var orderedColumns = ',' + sorting.map( function (val) {
12402
- return val.col;
12403
- } ).join(',') + ',';
12686
+ var sorting = ctx.sortDetails;
12687
+ var orderedColumns = ',' + sorting
12688
+ .filter( function (sort) {
12689
+ // Filter to just the visible columns
12690
+ return ctx.aoColumns[sort.col].bVisible;
12691
+ } )
12692
+ .map( function (sort) {
12693
+ return sort.col;
12694
+ } ).join(',') + ',';
12404
12695
 
12405
12696
  cell
12406
12697
  .removeClass(
@@ -12410,7 +12701,8 @@ $.extend( true, DataTable.ext.renderer, {
12410
12701
  .toggleClass( orderClasses.none, ! orderable )
12411
12702
  .toggleClass( orderClasses.canAsc, orderable && sortDirs.includes('asc') )
12412
12703
  .toggleClass( orderClasses.canDesc, orderable && sortDirs.includes('desc') );
12413
-
12704
+
12705
+ // Get the index of this cell in the sort array
12414
12706
  var sortIdx = orderedColumns.indexOf( ',' + indexes.toArray().join(',') + ',' );
12415
12707
 
12416
12708
  if ( sortIdx !== -1 ) {
@@ -12454,23 +12746,40 @@ $.extend( true, DataTable.ext.renderer, {
12454
12746
 
12455
12747
  layout: {
12456
12748
  _: function ( settings, container, items ) {
12749
+ var classes = settings.oClasses.layout;
12457
12750
  var row = $('<div/>')
12458
- .addClass('dt-layout-row')
12751
+ .attr('id', items.id || null)
12752
+ .addClass(items.className || classes.row)
12459
12753
  .appendTo( container );
12460
12754
 
12461
12755
  $.each( items, function (key, val) {
12462
- var klass = ! val.table ?
12463
- 'dt-'+key+' ' :
12464
- '';
12756
+ if (key === 'id' || key === 'className') {
12757
+ return;
12758
+ }
12759
+
12760
+ var klass = '';
12465
12761
 
12466
12762
  if (val.table) {
12467
- row.addClass('dt-layout-table');
12763
+ row.addClass(classes.tableRow);
12764
+ klass += classes.tableCell + ' ';
12765
+ }
12766
+
12767
+ if (key === 'start') {
12768
+ klass += classes.start;
12769
+ }
12770
+ else if (key === 'end') {
12771
+ klass += classes.end;
12772
+ }
12773
+ else {
12774
+ klass += classes.full;
12468
12775
  }
12469
12776
 
12470
12777
  $('<div/>')
12471
12778
  .attr({
12472
12779
  id: val.id || null,
12473
- "class": 'dt-layout-cell '+klass+(val.className || '')
12780
+ "class": val.className
12781
+ ? val.className
12782
+ : classes.cell + ' ' + klass
12474
12783
  })
12475
12784
  .append( val.contents )
12476
12785
  .appendTo( row );
@@ -12494,6 +12803,25 @@ DataTable.feature.register = function ( name, cb, legacy ) {
12494
12803
  }
12495
12804
  };
12496
12805
 
12806
+ function _divProp(el, prop, val) {
12807
+ if (val) {
12808
+ el[prop] = val;
12809
+ }
12810
+ }
12811
+
12812
+ DataTable.feature.register( 'div', function ( settings, opts ) {
12813
+ var n = $('<div>')[0];
12814
+
12815
+ if (opts) {
12816
+ _divProp(n, 'className', opts.className);
12817
+ _divProp(n, 'id', opts.id);
12818
+ _divProp(n, 'innerHTML', opts.html);
12819
+ _divProp(n, 'textContent', opts.text);
12820
+ }
12821
+
12822
+ return n;
12823
+ } );
12824
+
12497
12825
  DataTable.feature.register( 'info', function ( settings, opts ) {
12498
12826
  // For compatibility with the legacy `info` top level option
12499
12827
  if (! settings.oFeatures.bInfo) {
@@ -12593,6 +12921,7 @@ DataTable.feature.register( 'search', function ( settings, opts ) {
12593
12921
 
12594
12922
  opts = $.extend({
12595
12923
  placeholder: language.sSearchPlaceholder,
12924
+ processing: false,
12596
12925
  text: language.sSearch
12597
12926
  }, opts);
12598
12927
 
@@ -12636,13 +12965,15 @@ DataTable.feature.register( 'search', function ( settings, opts ) {
12636
12965
 
12637
12966
  /* Now do the filter */
12638
12967
  if ( val != previousSearch.search ) {
12639
- previousSearch.search = val;
12640
-
12641
- _fnFilterComplete( settings, previousSearch );
12642
-
12643
- // Need to redraw, without resorting
12644
- settings._iDisplayStart = 0;
12645
- _fnDraw( settings );
12968
+ _fnProcessingRun(settings, opts.processing, function () {
12969
+ previousSearch.search = val;
12970
+
12971
+ _fnFilterComplete( settings, previousSearch );
12972
+
12973
+ // Need to redraw, without resorting
12974
+ settings._iDisplayStart = 0;
12975
+ _fnDraw( settings );
12976
+ });
12646
12977
  }
12647
12978
  };
12648
12979
 
@@ -12700,17 +13031,17 @@ DataTable.feature.register( 'paging', function ( settings, opts ) {
12700
13031
  opts = $.extend({
12701
13032
  buttons: DataTable.ext.pager.numbers_length,
12702
13033
  type: settings.sPaginationType,
12703
- boundaryNumbers: true
13034
+ boundaryNumbers: true,
13035
+ firstLast: true,
13036
+ previousNext: true,
13037
+ numbers: true
12704
13038
  }, opts);
12705
13039
 
12706
- // To be removed in 2.1
12707
- if (opts.numbers) {
12708
- opts.buttons = opts.numbers;
12709
- }
12710
-
12711
- var host = $('<div/>').addClass( settings.oClasses.paging.container + ' paging_' + opts.type );
13040
+ var host = $('<div/>')
13041
+ .addClass(settings.oClasses.paging.container + (opts.type ? ' paging_' + opts.type : ''))
13042
+ .append('<nav>');
12712
13043
  var draw = function () {
12713
- _pagingDraw(settings, host, opts);
13044
+ _pagingDraw(settings, host.children(), opts);
12714
13045
  };
12715
13046
 
12716
13047
  settings.aoDrawCallback.push(draw);
@@ -12721,13 +13052,39 @@ DataTable.feature.register( 'paging', function ( settings, opts ) {
12721
13052
  return host;
12722
13053
  }, 'p' );
12723
13054
 
13055
+ /**
13056
+ * Dynamically create the button type array based on the configuration options.
13057
+ * This will only happen if the paging type is not defined.
13058
+ */
13059
+ function _pagingDynamic(opts) {
13060
+ var out = [];
13061
+
13062
+ if (opts.numbers) {
13063
+ out.push('numbers');
13064
+ }
13065
+
13066
+ if (opts.previousNext) {
13067
+ out.unshift('previous');
13068
+ out.push('next');
13069
+ }
13070
+
13071
+ if (opts.firstLast) {
13072
+ out.unshift('first');
13073
+ out.push('last');
13074
+ }
13075
+
13076
+ return out;
13077
+ }
13078
+
12724
13079
  function _pagingDraw(settings, host, opts) {
12725
13080
  if (! settings._bInitComplete) {
12726
13081
  return;
12727
13082
  }
12728
13083
 
12729
13084
  var
12730
- plugin = DataTable.ext.pager[ opts.type ],
13085
+ plugin = opts.type
13086
+ ? DataTable.ext.pager[ opts.type ]
13087
+ : _pagingDynamic,
12731
13088
  aria = settings.oLanguage.oAria.paginate || {},
12732
13089
  start = settings._iDisplayStart,
12733
13090
  len = settings._iDisplayLength,
@@ -12735,7 +13092,7 @@ function _pagingDraw(settings, host, opts) {
12735
13092
  all = len === -1,
12736
13093
  page = all ? 0 : Math.ceil( start / len ),
12737
13094
  pages = all ? 1 : Math.ceil( visRecords / len ),
12738
- buttons = plugin()
13095
+ buttons = plugin(opts)
12739
13096
  .map(function (val) {
12740
13097
  return val === 'numbers'
12741
13098
  ? _pagingNumbers(page, pages, opts.buttons, opts.boundaryNumbers)
@@ -12757,14 +13114,24 @@ function _pagingDraw(settings, host, opts) {
12757
13114
  btnInfo.disabled
12758
13115
  );
12759
13116
 
13117
+ var ariaLabel = typeof button === 'string'
13118
+ ? aria[ button ]
13119
+ : aria.number
13120
+ ? aria.number + (button+1)
13121
+ : null;
13122
+
12760
13123
  // Common attributes
12761
13124
  $(btn.clicker).attr({
12762
13125
  'aria-controls': settings.sTableId,
12763
13126
  'aria-disabled': btnInfo.disabled ? 'true' : null,
12764
13127
  'aria-current': btnInfo.active ? 'page' : null,
12765
- 'aria-label': aria[ button ],
13128
+ 'aria-label': ariaLabel,
12766
13129
  'data-dt-idx': button,
12767
- 'tabIndex': btnInfo.disabled ? -1 : settings.iTabIndex,
13130
+ 'tabIndex': btnInfo.disabled
13131
+ ? -1
13132
+ : settings.iTabIndex
13133
+ ? settings.iTabIndex
13134
+ : null, // `0` doesn't need a tabIndex since it is the default
12768
13135
  });
12769
13136
 
12770
13137
  if (typeof button !== 'number') {
@@ -12798,10 +13165,10 @@ function _pagingDraw(settings, host, opts) {
12798
13165
  // height of the buttons and the container.
12799
13166
  if (
12800
13167
  buttonEls.length && // any buttons
12801
- opts.numbers > 1 && // prevent infinite
13168
+ opts.buttons > 1 && // prevent infinite
12802
13169
  $(host).height() >= ($(buttonEls[0]).outerHeight() * 2) - 10
12803
13170
  ) {
12804
- _pagingDraw(settings, host, $.extend({}, opts, { numbers: opts.numbers - 2 }));
13171
+ _pagingDraw(settings, host, $.extend({}, opts, { buttons: opts.buttons - 2 }));
12805
13172
  }
12806
13173
  }
12807
13174
 
@@ -13003,10 +13370,11 @@ DataTable.feature.register( 'pageLength', function ( settings, opts ) {
13003
13370
  }
13004
13371
 
13005
13372
  // Wrapper element - use a span as a holder for where the select will go
13373
+ var tmpId = 'tmp-' + (+new Date())
13006
13374
  var div = $('<div/>')
13007
13375
  .addClass( classes.container )
13008
13376
  .append(
13009
- str.replace( '_MENU_', '<span></span>' )
13377
+ str.replace( '_MENU_', '<span id="'+tmpId+'"></span>' )
13010
13378
  );
13011
13379
 
13012
13380
  // Save text node content for macro updating
@@ -13049,7 +13417,7 @@ DataTable.feature.register( 'pageLength', function ( settings, opts ) {
13049
13417
  __lengthCounter++;
13050
13418
 
13051
13419
  // Swap in the select list
13052
- div.find('span').replaceWith(select);
13420
+ div.find('#' + tmpId).replaceWith(select);
13053
13421
 
13054
13422
  // Can't use `select` variable as user might provide their own and the
13055
13423
  // reference is broken by the use of outerHTML