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.js CHANGED
@@ -1,11 +1,11 @@
1
- /*! DataTables 2.0.7
1
+ /*! DataTables 2.1.0
2
2
  * © SpryMedia Ltd - datatables.net/license
3
3
  */
4
4
 
5
5
  /**
6
6
  * @summary DataTables
7
7
  * @description Paginate, search and order HTML tables
8
- * @version 2.0.7
8
+ * @version 2.1.0
9
9
  * @author SpryMedia Ltd
10
10
  * @contact www.datatables.net
11
11
  * @copyright SpryMedia Ltd.
@@ -104,7 +104,6 @@
104
104
 
105
105
  var i=0, iLen;
106
106
  var sId = this.getAttribute( 'id' );
107
- var bInitHandedOff = false;
108
107
  var defaults = DataTable.defaults;
109
108
  var $this = $(this);
110
109
 
@@ -254,6 +253,7 @@
254
253
  "rowId",
255
254
  "caption",
256
255
  "layout",
256
+ "orderDescReverse",
257
257
  [ "iCookieDuration", "iStateDuration" ], // backwards compat
258
258
  [ "oSearch", "oPreviousSearch" ],
259
259
  [ "aoSearchCols", "aoPreSearchCols" ],
@@ -300,38 +300,14 @@
300
300
  oSettings._iDisplayStart = oInit.iDisplayStart;
301
301
  }
302
302
 
303
- /* Language definitions */
304
- var oLanguage = oSettings.oLanguage;
305
- $.extend( true, oLanguage, oInit.oLanguage );
306
-
307
- if ( oLanguage.sUrl )
303
+ var defer = oInit.iDeferLoading;
304
+ if ( defer !== null )
308
305
  {
309
- /* Get the language definitions from a file - because this Ajax call makes the language
310
- * get async to the remainder of this function we use bInitHandedOff to indicate that
311
- * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor
312
- */
313
- $.ajax( {
314
- dataType: 'json',
315
- url: oLanguage.sUrl,
316
- success: function ( json ) {
317
- _fnCamelToHungarian( defaults.oLanguage, json );
318
- $.extend( true, oLanguage, json, oSettings.oInit.oLanguage );
306
+ oSettings.deferLoading = true;
319
307
 
320
- _fnCallbackFire( oSettings, null, 'i18n', [oSettings], true);
321
- _fnInitialise( oSettings );
322
- },
323
- error: function () {
324
- // Error occurred loading language file
325
- _fnLog( oSettings, 0, 'i18n file loading error', 21 );
326
-
327
- // continue on as best we can
328
- _fnInitialise( oSettings );
329
- }
330
- } );
331
- bInitHandedOff = true;
332
- }
333
- else {
334
- _fnCallbackFire( oSettings, null, 'i18n', [oSettings]);
308
+ var tmp = Array.isArray(defer);
309
+ oSettings._iRecordsDisplay = tmp ? defer[0] : defer;
310
+ oSettings._iRecordsTotal = tmp ? defer[1] : defer;
335
311
  }
336
312
 
337
313
  /*
@@ -398,113 +374,112 @@
398
374
  } );
399
375
  }
400
376
 
377
+ // Must be done after everything which can be overridden by the state saving!
378
+ _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState );
379
+
401
380
  var features = oSettings.oFeatures;
402
- var loadedInit = function () {
403
- /*
404
- * Sorting
405
- * @todo For modularisation (1.11) this needs to do into a sort start up handler
406
- */
381
+ if ( oInit.bStateSave )
382
+ {
383
+ features.bStateSave = true;
384
+ }
407
385
 
408
- // If aaSorting is not defined, then we use the first indicator in asSorting
409
- // in case that has been altered, so the default sort reflects that option
410
- if ( oInit.aaSorting === undefined ) {
411
- var sorting = oSettings.aaSorting;
412
- for ( i=0, iLen=sorting.length ; i<iLen ; i++ ) {
413
- sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0];
414
- }
386
+ // If aaSorting is not defined, then we use the first indicator in asSorting
387
+ // in case that has been altered, so the default sort reflects that option
388
+ if ( oInit.aaSorting === undefined ) {
389
+ var sorting = oSettings.aaSorting;
390
+ for ( i=0, iLen=sorting.length ; i<iLen ; i++ ) {
391
+ sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0];
415
392
  }
393
+ }
416
394
 
417
- /* Do a first pass on the sorting classes (allows any size changes to be taken into
418
- * account, and also will apply sorting disabled classes if disabled
419
- */
420
- _fnSortingClasses( oSettings );
421
-
422
- _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
423
- if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) {
424
- _fnSortingClasses( oSettings );
425
- }
426
- } );
395
+ // Do a first pass on the sorting classes (allows any size changes to be taken into
396
+ // account, and also will apply sorting disabled classes if disabled
397
+ _fnSortingClasses( oSettings );
427
398
 
399
+ _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
400
+ if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) {
401
+ _fnSortingClasses( oSettings );
402
+ }
403
+ } );
428
404
 
429
- /*
430
- * Final init
431
- * Cache the header, body and footer as required, creating them if needed
432
- */
433
- var caption = $this.children('caption');
434
405
 
435
- if ( oSettings.caption ) {
436
- if ( caption.length === 0 ) {
437
- caption = $('<caption/>').appendTo( $this );
438
- }
406
+ /*
407
+ * Table HTML init
408
+ * Cache the header, body and footer as required, creating them if needed
409
+ */
410
+ var caption = $this.children('caption');
439
411
 
440
- caption.html( oSettings.caption );
412
+ if ( oSettings.caption ) {
413
+ if ( caption.length === 0 ) {
414
+ caption = $('<caption/>').appendTo( $this );
441
415
  }
442
416
 
443
- // Store the caption side, so we can remove the element from the document
444
- // when creating the element
445
- if (caption.length) {
446
- caption[0]._captionSide = caption.css('caption-side');
447
- oSettings.captionNode = caption[0];
448
- }
417
+ caption.html( oSettings.caption );
418
+ }
449
419
 
450
- if ( thead.length === 0 ) {
451
- thead = $('<thead/>').appendTo($this);
452
- }
453
- oSettings.nTHead = thead[0];
454
- $('tr', thead).addClass(oClasses.thead.row);
420
+ // Store the caption side, so we can remove the element from the document
421
+ // when creating the element
422
+ if (caption.length) {
423
+ caption[0]._captionSide = caption.css('caption-side');
424
+ oSettings.captionNode = caption[0];
425
+ }
455
426
 
456
- var tbody = $this.children('tbody');
457
- if ( tbody.length === 0 ) {
458
- tbody = $('<tbody/>').insertAfter(thead);
459
- }
460
- oSettings.nTBody = tbody[0];
427
+ if ( thead.length === 0 ) {
428
+ thead = $('<thead/>').appendTo($this);
429
+ }
430
+ oSettings.nTHead = thead[0];
431
+ $('tr', thead).addClass(oClasses.thead.row);
461
432
 
462
- var tfoot = $this.children('tfoot');
463
- if ( tfoot.length === 0 ) {
464
- // If we are a scrolling table, and no footer has been given, then we need to create
465
- // a tfoot element for the caption element to be appended to
466
- tfoot = $('<tfoot/>').appendTo($this);
467
- }
468
- oSettings.nTFoot = tfoot[0];
469
- $('tr', tfoot).addClass(oClasses.tfoot.row);
433
+ var tbody = $this.children('tbody');
434
+ if ( tbody.length === 0 ) {
435
+ tbody = $('<tbody/>').insertAfter(thead);
436
+ }
437
+ oSettings.nTBody = tbody[0];
470
438
 
471
- // Check if there is data passing into the constructor
472
- if ( oInit.aaData ) {
473
- for ( i=0 ; i<oInit.aaData.length ; i++ ) {
474
- _fnAddData( oSettings, oInit.aaData[ i ] );
475
- }
476
- }
477
- else if ( _fnDataSource( oSettings ) == 'dom' ) {
478
- // Grab the data from the page
479
- _fnAddTr( oSettings, $(oSettings.nTBody).children('tr') );
480
- }
439
+ var tfoot = $this.children('tfoot');
440
+ if ( tfoot.length === 0 ) {
441
+ // If we are a scrolling table, and no footer has been given, then we need to create
442
+ // a tfoot element for the caption element to be appended to
443
+ tfoot = $('<tfoot/>').appendTo($this);
444
+ }
445
+ oSettings.nTFoot = tfoot[0];
446
+ $('tr', tfoot).addClass(oClasses.tfoot.row);
481
447
 
482
- /* Copy the data index array */
483
- oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
448
+ // Copy the data index array
449
+ oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
484
450
 
485
- /* Initialisation complete - table can be drawn */
486
- oSettings.bInitialised = true;
451
+ // Initialisation complete - table can be drawn
452
+ oSettings.bInitialised = true;
487
453
 
488
- /* Check if we need to initialise the table (it might not have been handed off to the
489
- * language processor)
490
- */
491
- if ( bInitHandedOff === false ) {
492
- _fnInitialise( oSettings );
493
- }
494
- };
454
+ // Language definitions
455
+ var oLanguage = oSettings.oLanguage;
456
+ $.extend( true, oLanguage, oInit.oLanguage );
495
457
 
496
- /* Must be done after everything which can be overridden by the state saving! */
497
- _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState );
458
+ if ( oLanguage.sUrl ) {
459
+ // Get the language definitions from a file
460
+ $.ajax( {
461
+ dataType: 'json',
462
+ url: oLanguage.sUrl,
463
+ success: function ( json ) {
464
+ _fnCamelToHungarian( defaults.oLanguage, json );
465
+ $.extend( true, oLanguage, json, oSettings.oInit.oLanguage );
498
466
 
499
- if ( oInit.bStateSave )
500
- {
501
- features.bStateSave = true;
502
- _fnLoadState( oSettings, oInit, loadedInit );
467
+ _fnCallbackFire( oSettings, null, 'i18n', [oSettings], true);
468
+ _fnInitialise( oSettings );
469
+ },
470
+ error: function () {
471
+ // Error occurred loading language file
472
+ _fnLog( oSettings, 0, 'i18n file loading error', 21 );
473
+
474
+ // Continue on as best we can
475
+ _fnInitialise( oSettings );
476
+ }
477
+ } );
503
478
  }
504
479
  else {
505
- loadedInit();
480
+ _fnCallbackFire( oSettings, null, 'i18n', [oSettings]);
481
+ _fnInitialise( oSettings );
506
482
  }
507
-
508
483
  } );
509
484
  _that = null;
510
485
  return this;
@@ -1021,6 +996,15 @@
1021
996
  info: {
1022
997
  container: 'dt-info'
1023
998
  },
999
+ layout: {
1000
+ row: 'dt-layout-row',
1001
+ cell: 'dt-layout-cell',
1002
+ tableRow: 'dt-layout-table',
1003
+ tableCell: '',
1004
+ start: 'dt-layout-start',
1005
+ end: 'dt-layout-end',
1006
+ full: 'dt-layout-full'
1007
+ },
1024
1008
  length: {
1025
1009
  container: 'dt-length',
1026
1010
  select: 'dt-input'
@@ -1144,7 +1128,7 @@
1144
1128
  };
1145
1129
 
1146
1130
 
1147
- var _isNumber = function ( d, decimalPoint, formatted ) {
1131
+ var _isNumber = function ( d, decimalPoint, formatted, allowEmpty ) {
1148
1132
  var type = typeof d;
1149
1133
  var strType = type === 'string';
1150
1134
 
@@ -1155,7 +1139,7 @@
1155
1139
  // If empty return immediately so there must be a number if it is a
1156
1140
  // formatted string (this stops the string "k", or "kr", etc being detected
1157
1141
  // as a formatted number for currency
1158
- if ( _empty( d ) ) {
1142
+ if ( allowEmpty && _empty( d ) ) {
1159
1143
  return true;
1160
1144
  }
1161
1145
 
@@ -1177,8 +1161,8 @@
1177
1161
  };
1178
1162
 
1179
1163
  // Is a string a number surrounded by HTML?
1180
- var _htmlNumeric = function ( d, decimalPoint, formatted ) {
1181
- if ( _empty( d ) ) {
1164
+ var _htmlNumeric = function ( d, decimalPoint, formatted, allowEmpty ) {
1165
+ if ( allowEmpty && _empty( d ) ) {
1182
1166
  return true;
1183
1167
  }
1184
1168
 
@@ -1190,7 +1174,7 @@
1190
1174
  var html = _isHtml( d );
1191
1175
  return ! html ?
1192
1176
  null :
1193
- _isNumber( _stripHtml( d ), decimalPoint, formatted ) ?
1177
+ _isNumber( _stripHtml( d ), decimalPoint, formatted, allowEmpty ) ?
1194
1178
  true :
1195
1179
  null;
1196
1180
  };
@@ -1286,6 +1270,10 @@
1286
1270
 
1287
1271
  // Replaceable function in api.util
1288
1272
  var _stripHtml = function (input) {
1273
+ if (! input) {
1274
+ return input;
1275
+ }
1276
+
1289
1277
  // Irrelevant check to workaround CodeQL's false positive on the regex
1290
1278
  if (input.length > _max_str_len) {
1291
1279
  throw new Error('Exceeded max str len');
@@ -2252,6 +2240,21 @@
2252
2240
  return a;
2253
2241
  }
2254
2242
 
2243
+ /**
2244
+ * Allow the result from a type detection function to be `true` while
2245
+ * translating that into a string. Old type detection functions will
2246
+ * return the type name if it passes. An obect store would be better,
2247
+ * but not backwards compatible.
2248
+ *
2249
+ * @param {*} typeDetect Object or function for type detection
2250
+ * @param {*} res Result from the type detection function
2251
+ * @returns Type name or false
2252
+ */
2253
+ function _typeResult (typeDetect, res) {
2254
+ return res === true
2255
+ ? typeDetect.name
2256
+ : res;
2257
+ }
2255
2258
 
2256
2259
  /**
2257
2260
  * Calculate the 'type' of a column
@@ -2266,7 +2269,13 @@
2266
2269
  var i, ien, j, jen, k, ken;
2267
2270
  var col, detectedType, cache;
2268
2271
 
2269
- // For each column, spin over the
2272
+ // If SSP then we don't have the full data set, so any type detection would be
2273
+ // unreliable and error prone
2274
+ if (_fnDataSource( settings ) === 'ssp') {
2275
+ return;
2276
+ }
2277
+
2278
+ // For each column, spin over the data type detection functions, seeing if one matches
2270
2279
  for ( i=0, ien=columns.length ; i<ien ; i++ ) {
2271
2280
  col = columns[i];
2272
2281
  cache = [];
@@ -2276,8 +2285,27 @@
2276
2285
  }
2277
2286
  else if ( ! col.sType ) {
2278
2287
  for ( j=0, jen=types.length ; j<jen ; j++ ) {
2279
- for ( k=0, ken=data.length ; k<ken ; k++ ) {
2288
+ var typeDetect = types[j];
2289
+
2290
+ // There can be either one, or three type detection functions
2291
+ var oneOf = typeDetect.oneOf;
2292
+ var allOf = typeDetect.allOf || typeDetect;
2293
+ var init = typeDetect.init;
2294
+ var one = false;
2295
+
2296
+ detectedType = null;
2297
+
2298
+ // Fast detect based on column assignment
2299
+ if (init) {
2300
+ detectedType = _typeResult(typeDetect, init(settings, col, i));
2301
+
2302
+ if (detectedType) {
2303
+ col.sType = detectedType;
2304
+ break;
2305
+ }
2306
+ }
2280
2307
 
2308
+ for ( k=0, ken=data.length ; k<ken ; k++ ) {
2281
2309
  if (! data[k]) {
2282
2310
  continue;
2283
2311
  }
@@ -2288,14 +2316,20 @@
2288
2316
  cache[k] = _fnGetCellData( settings, k, i, 'type' );
2289
2317
  }
2290
2318
 
2291
- detectedType = types[j]( cache[k], settings );
2319
+ // Only one data point in the column needs to match this function
2320
+ if (oneOf && ! one) {
2321
+ one = _typeResult(typeDetect, oneOf( cache[k], settings ));
2322
+ }
2323
+
2324
+ // All data points need to match this function
2325
+ detectedType = _typeResult(typeDetect, allOf( cache[k], settings ));
2292
2326
 
2293
2327
  // If null, then this type can't apply to this column, so
2294
2328
  // rather than testing all cells, break out. There is an
2295
2329
  // exception for the last type which is `html`. We need to
2296
2330
  // scan all rows since it is possible to mix string and HTML
2297
2331
  // types
2298
- if ( ! detectedType && j !== types.length-2 ) {
2332
+ if ( ! detectedType && j !== types.length-3 ) {
2299
2333
  break;
2300
2334
  }
2301
2335
 
@@ -2309,7 +2343,7 @@
2309
2343
 
2310
2344
  // Type is valid for all data points in the column - use this
2311
2345
  // type
2312
- if ( detectedType ) {
2346
+ if ( (oneOf && one && detectedType) || (!oneOf && detectedType) ) {
2313
2347
  col.sType = detectedType;
2314
2348
  break;
2315
2349
  }
@@ -2331,7 +2365,7 @@
2331
2365
 
2332
2366
  var renderer = _ext.type.render[col.sType];
2333
2367
 
2334
- // This can only happen once! There is no way to remover
2368
+ // This can only happen once! There is no way to remove
2335
2369
  // a renderer. After the first time the renderer has
2336
2370
  // already been set so createTr will run the renderer itself.
2337
2371
  if (renderer && ! col._render) {
@@ -3375,7 +3409,13 @@
3375
3409
  oSettings.bDrawing = true;
3376
3410
 
3377
3411
  /* Server-side processing draw intercept */
3378
- if ( !bServerSide )
3412
+ if ( oSettings.deferLoading )
3413
+ {
3414
+ oSettings.deferLoading = false;
3415
+ oSettings.iDraw++;
3416
+ _fnProcessingDisplay( oSettings, false );
3417
+ }
3418
+ else if ( !bServerSide )
3379
3419
  {
3380
3420
  oSettings.iDraw++;
3381
3421
  }
@@ -3476,6 +3516,9 @@
3476
3516
  filter = features.bFilter;
3477
3517
 
3478
3518
  if (recompute === undefined || recompute === true) {
3519
+ // Resolve any column types that are unknown due to addition or invalidation
3520
+ _fnColumnTypes( settings );
3521
+
3479
3522
  if ( sort ) {
3480
3523
  _fnSort( settings );
3481
3524
  }
@@ -3531,113 +3574,154 @@
3531
3574
 
3532
3575
 
3533
3576
  /**
3534
- * Convert a `layout` object given by a user to the object structure needed
3535
- * for the renderer. This is done twice, once for above and once for below
3536
- * the table. Ordering must also be considered.
3537
- *
3538
- * @param {*} settings DataTables settings object
3539
- * @param {*} layout Layout object to convert
3540
- * @param {string} side `top` or `bottom`
3541
- * @returns Converted array structure - one item for each row.
3577
+ * Expand the layout items into an object for the rendering function
3542
3578
  */
3543
- function _layoutArray ( settings, layout, side )
3544
- {
3545
- var groups = {};
3546
-
3547
- // Combine into like groups (e.g. `top`, `top2`, etc)
3548
- $.each( layout, function ( pos, val ) {
3549
- if (val === null) {
3550
- return;
3579
+ function _layoutItems (row, align, items) {
3580
+ if ( Array.isArray(items)) {
3581
+ for (var i=0 ; i<items.length ; i++) {
3582
+ _layoutItems(row, align, items[i]);
3551
3583
  }
3552
3584
 
3553
- var splitPos = pos.replace(/([A-Z])/g, ' $1').split(' ');
3585
+ return;
3586
+ }
3554
3587
 
3555
- if ( ! groups[ splitPos[0] ] ) {
3556
- groups[ splitPos[0] ] = {};
3557
- }
3588
+ var rowCell = row[align];
3558
3589
 
3559
- var align = splitPos.length === 1 ?
3560
- 'full' :
3561
- splitPos[1].toLowerCase();
3562
- var group = groups[ splitPos[0] ];
3563
- var groupRun = function (contents, innerVal) {
3564
- // If it is an object, then there can be multiple features contained in it
3565
- if ( $.isPlainObject( innerVal ) ) {
3566
- Object.keys(innerVal).map(function (key) {
3567
- contents.push( {
3568
- feature: key,
3569
- opts: innerVal[key]
3570
- });
3571
- });
3590
+ // If it is an object, then there can be multiple features contained in it
3591
+ if ( $.isPlainObject( items ) ) {
3592
+ // A feature plugin cannot be named "features" due to this check
3593
+ if (items.features) {
3594
+ if (items.rowId) {
3595
+ row.id = items.rowId;
3572
3596
  }
3573
- else {
3574
- contents.push(innerVal);
3597
+ if (items.rowClass) {
3598
+ row.className = items.rowClass;
3575
3599
  }
3576
- }
3577
3600
 
3578
- // Transform to an object with a contents property
3579
- if (! group[align] || ! group[align].contents) {
3580
- group[align] = { contents: [] };
3601
+ rowCell.id = items.id;
3602
+ rowCell.className = items.className;
3603
+
3604
+ _layoutItems(row, align, items.features);
3605
+ }
3606
+ else {
3607
+ Object.keys(items).map(function (key) {
3608
+ rowCell.contents.push( {
3609
+ feature: key,
3610
+ opts: items[key]
3611
+ });
3612
+ });
3581
3613
  }
3614
+ }
3615
+ else {
3616
+ rowCell.contents.push(items);
3617
+ }
3618
+ }
3619
+
3620
+ /**
3621
+ * Find, or create a layout row
3622
+ */
3623
+ function _layoutGetRow(rows, rowNum, align) {
3624
+ var row;
3625
+
3626
+ // Find existing rows
3627
+ for (var i=0; i<rows.length; i++) {
3628
+ row = rows[i];
3629
+
3630
+ if (row.rowNum === rowNum) {
3631
+ // full is on its own, but start and end share a row
3632
+ if (
3633
+ (align === 'full' && row.full) ||
3634
+ ((align === 'start' || align === 'end') && (row.start || row.end))
3635
+ ) {
3636
+ if (! row[align]) {
3637
+ row[align] = {
3638
+ contents: []
3639
+ };
3640
+ }
3582
3641
 
3583
- // Allow for an array or just a single object
3584
- if ( Array.isArray(val)) {
3585
- for (var i=0 ; i<val.length ; i++) {
3586
- groupRun(group[align].contents, val[i]);
3642
+ return row;
3587
3643
  }
3588
3644
  }
3589
- else {
3590
- groupRun(group[ align ].contents, val);
3645
+ }
3646
+
3647
+ // If we get this far, then there was no match, create a new row
3648
+ row = {
3649
+ rowNum: rowNum
3650
+ };
3651
+
3652
+ row[align] = {
3653
+ contents: []
3654
+ };
3655
+
3656
+ rows.push(row);
3657
+
3658
+ return row;
3659
+ }
3660
+
3661
+ /**
3662
+ * Convert a `layout` object given by a user to the object structure needed
3663
+ * for the renderer. This is done twice, once for above and once for below
3664
+ * the table. Ordering must also be considered.
3665
+ *
3666
+ * @param {*} settings DataTables settings object
3667
+ * @param {*} layout Layout object to convert
3668
+ * @param {string} side `top` or `bottom`
3669
+ * @returns Converted array structure - one item for each row.
3670
+ */
3671
+ function _layoutArray ( settings, layout, side ) {
3672
+ var rows = [];
3673
+
3674
+ // Split out into an array
3675
+ $.each( layout, function ( pos, items ) {
3676
+ if (items === null) {
3677
+ return;
3591
3678
  }
3592
3679
 
3593
- // And make contents an array
3594
- if ( ! Array.isArray( group[ align ].contents ) ) {
3595
- group[ align ].contents = [ group[ align ].contents ];
3680
+ var parts = pos.match(/^([a-z]+)([0-9]*)([A-Za-z]*)$/);
3681
+ var rowNum = parts[2]
3682
+ ? parts[2] * 1
3683
+ : 0;
3684
+ var align = parts[3]
3685
+ ? parts[3].toLowerCase()
3686
+ : 'full';
3687
+
3688
+ // Filter out the side we aren't interested in
3689
+ if (parts[1] !== side) {
3690
+ return;
3596
3691
  }
3597
- } );
3598
3692
 
3599
- var filtered = Object.keys(groups)
3600
- .map( function ( pos ) {
3601
- // Filter to only the side we need
3602
- if ( pos.indexOf(side) !== 0 ) {
3603
- return null;
3604
- }
3693
+ // Get or create the row we should attach to
3694
+ var row = _layoutGetRow(rows, rowNum, align);
3605
3695
 
3606
- return {
3607
- name: pos,
3608
- val: groups[pos]
3609
- };
3610
- } )
3611
- .filter( function (item) {
3612
- return item !== null;
3613
- });
3696
+ _layoutItems(row, align, items);
3697
+ });
3614
3698
 
3615
3699
  // Order by item identifier
3616
- filtered.sort( function ( a, b ) {
3617
- var order1 = a.name.replace(/[^0-9]/g, '') * 1;
3618
- var order2 = b.name.replace(/[^0-9]/g, '') * 1;
3700
+ rows.sort( function ( a, b ) {
3701
+ var order1 = a.rowNum;
3702
+ var order2 = b.rowNum;
3703
+
3704
+ // If both in the same row, then the row with `full` comes first
3705
+ if (order1 === order2) {
3706
+ var ret = a.full && ! b.full ? -1 : 1;
3707
+
3708
+ return side === 'bottom'
3709
+ ? ret * -1
3710
+ : ret;
3711
+ }
3619
3712
 
3620
3713
  return order2 - order1;
3621
3714
  } );
3622
-
3715
+
3716
+ // Invert for below the table
3623
3717
  if ( side === 'bottom' ) {
3624
- filtered.reverse();
3718
+ rows.reverse();
3625
3719
  }
3626
3720
 
3627
- // Split into rows
3628
- var rows = [];
3629
- for ( var i=0, ien=filtered.length ; i<ien ; i++ ) {
3630
- if ( filtered[i].val.full ) {
3631
- rows.push( { full: filtered[i].val.full } );
3632
- _layoutResolve( settings, rows[ rows.length - 1 ] );
3633
-
3634
- delete filtered[i].val.full;
3635
- }
3721
+ for (var row = 0; row<rows.length; row++) {
3722
+ delete rows[row].rowNum;
3636
3723
 
3637
- if ( Object.keys(filtered[i].val).length ) {
3638
- rows.push( filtered[i].val );
3639
- _layoutResolve( settings, rows[ rows.length - 1 ] );
3640
- }
3724
+ _layoutResolve(settings, rows[row]);
3641
3725
  }
3642
3726
 
3643
3727
  return rows;
@@ -3661,6 +3745,10 @@
3661
3745
  };
3662
3746
 
3663
3747
  var resolve = function ( item ) {
3748
+ if (! row[ item ]) {
3749
+ return;
3750
+ }
3751
+
3664
3752
  var line = row[ item ].contents;
3665
3753
 
3666
3754
  for ( var i=0, ien=line.length ; i<ien ; i++ ) {
@@ -3688,9 +3776,9 @@
3688
3776
  }
3689
3777
  };
3690
3778
 
3691
- $.each( row, function ( key ) {
3692
- resolve( key );
3693
- } );
3779
+ resolve('start');
3780
+ resolve('end');
3781
+ resolve('full');
3694
3782
  }
3695
3783
 
3696
3784
 
@@ -4023,6 +4111,17 @@
4023
4111
  _fnLog( oSettings, 0, error );
4024
4112
  }
4025
4113
 
4114
+ // Microsoft often wrap JSON as a string in another JSON object
4115
+ // Let's handle that automatically
4116
+ if (json.d && typeof json.d === 'string') {
4117
+ try {
4118
+ json = JSON.parse(json.d);
4119
+ }
4120
+ catch (e) {
4121
+ // noop
4122
+ }
4123
+ }
4124
+
4026
4125
  oSettings.json = json;
4027
4126
 
4028
4127
  _fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR], true );
@@ -4099,11 +4198,11 @@
4099
4198
  else {
4100
4199
  // Object to extend the base settings
4101
4200
  oSettings.jqXHR = $.ajax( baseAjax );
4201
+ }
4102
4202
 
4103
- // Restore for next time around
4104
- if ( ajaxData ) {
4105
- ajax.data = ajaxData;
4106
- }
4203
+ // Restore for next time around
4204
+ if ( ajaxData ) {
4205
+ ajax.data = ajaxData;
4107
4206
  }
4108
4207
  }
4109
4208
 
@@ -4328,10 +4427,6 @@
4328
4427
  {
4329
4428
  var columnsSearch = settings.aoPreSearchCols;
4330
4429
 
4331
- // Resolve any column types that are unknown due to addition or invalidation
4332
- // @todo As per sort - can this be moved into an event handler?
4333
- _fnColumnTypes( settings );
4334
-
4335
4430
  // In server-side processing all filtering is done by the server, so no point hanging around here
4336
4431
  if ( _fnDataSource( settings ) != 'ssp' )
4337
4432
  {
@@ -4637,8 +4732,11 @@
4637
4732
  function _fnInitialise ( settings )
4638
4733
  {
4639
4734
  var i, iAjaxStart=settings.iInitDisplayStart;
4735
+ var init = settings.oInit;
4736
+ var deferLoading = settings.deferLoading;
4737
+ var dataSrc = _fnDataSource( settings );
4640
4738
 
4641
- /* Ensure that the table data is fully initialised */
4739
+ // Ensure that the table data is fully initialised
4642
4740
  if ( ! settings.bInitialised ) {
4643
4741
  setTimeout( function(){ _fnInitialise( settings ); }, 200 );
4644
4742
  return;
@@ -4650,52 +4748,68 @@
4650
4748
  _fnDrawHead( settings, settings.aoHeader );
4651
4749
  _fnDrawHead( settings, settings.aoFooter );
4652
4750
 
4653
- // Enable features
4654
- _fnAddOptionsHtml( settings );
4655
- _fnSortInit( settings );
4751
+ // Load the table's state (if needed) and then render around it and draw draw
4752
+ _fnLoadState( settings, init, function () {
4753
+ // Local data load
4754
+ // Check if there is data passing into the constructor
4755
+ if ( init.aaData ) {
4756
+ for ( i=0 ; i<init.aaData.length ; i++ ) {
4757
+ _fnAddData( settings, init.aaData[ i ] );
4758
+ }
4759
+ }
4760
+ else if ( deferLoading || dataSrc == 'dom' ) {
4761
+ // Grab the data from the page
4762
+ _fnAddTr( settings, $(settings.nTBody).children('tr') );
4763
+ }
4656
4764
 
4657
- _colGroup( settings );
4765
+ // Filter not yet applied - copy the display master
4766
+ settings.aiDisplay = settings.aiDisplayMaster.slice();
4658
4767
 
4659
- /* Okay to show that something is going on now */
4660
- _fnProcessingDisplay( settings, true );
4768
+ // Enable features
4769
+ _fnAddOptionsHtml( settings );
4770
+ _fnSortInit( settings );
4661
4771
 
4662
- _fnCallbackFire( settings, null, 'preInit', [settings], true );
4772
+ _colGroup( settings );
4663
4773
 
4664
- // If there is default sorting required - let's do it. The sort function
4665
- // will do the drawing for us. Otherwise we draw the table regardless of the
4666
- // Ajax source - this allows the table to look initialised for Ajax sourcing
4667
- // data (show 'loading' message possibly)
4668
- _fnReDraw( settings );
4774
+ /* Okay to show that something is going on now */
4775
+ _fnProcessingDisplay( settings, true );
4669
4776
 
4670
- var dataSrc = _fnDataSource( settings );
4777
+ _fnCallbackFire( settings, null, 'preInit', [settings], true );
4671
4778
 
4672
- // Server-side processing init complete is done by _fnAjaxUpdateDraw
4673
- if ( dataSrc != 'ssp' ) {
4674
- // if there is an ajax source load the data
4675
- if ( dataSrc == 'ajax' ) {
4676
- _fnBuildAjax( settings, {}, function(json) {
4677
- var aData = _fnAjaxDataSrc( settings, json );
4779
+ // If there is default sorting required - let's do it. The sort function
4780
+ // will do the drawing for us. Otherwise we draw the table regardless of the
4781
+ // Ajax source - this allows the table to look initialised for Ajax sourcing
4782
+ // data (show 'loading' message possibly)
4783
+ _fnReDraw( settings );
4678
4784
 
4679
- // Got the data - add it to the table
4680
- for ( i=0 ; i<aData.length ; i++ ) {
4681
- _fnAddData( settings, aData[i] );
4682
- }
4785
+ // Server-side processing init complete is done by _fnAjaxUpdateDraw
4786
+ if ( dataSrc != 'ssp' || deferLoading ) {
4787
+ // if there is an ajax source load the data
4788
+ if ( dataSrc == 'ajax' ) {
4789
+ _fnBuildAjax( settings, {}, function(json) {
4790
+ var aData = _fnAjaxDataSrc( settings, json );
4683
4791
 
4684
- // Reset the init display for cookie saving. We've already done
4685
- // a filter, and therefore cleared it before. So we need to make
4686
- // it appear 'fresh'
4687
- settings.iInitDisplayStart = iAjaxStart;
4792
+ // Got the data - add it to the table
4793
+ for ( i=0 ; i<aData.length ; i++ ) {
4794
+ _fnAddData( settings, aData[i] );
4795
+ }
4688
4796
 
4689
- _fnReDraw( settings );
4690
- _fnProcessingDisplay( settings, false );
4797
+ // Reset the init display for cookie saving. We've already done
4798
+ // a filter, and therefore cleared it before. So we need to make
4799
+ // it appear 'fresh'
4800
+ settings.iInitDisplayStart = iAjaxStart;
4801
+
4802
+ _fnReDraw( settings );
4803
+ _fnProcessingDisplay( settings, false );
4804
+ _fnInitComplete( settings );
4805
+ }, settings );
4806
+ }
4807
+ else {
4691
4808
  _fnInitComplete( settings );
4692
- }, settings );
4693
- }
4694
- else {
4695
- _fnInitComplete( settings );
4696
- _fnProcessingDisplay( settings, false );
4809
+ _fnProcessingDisplay( settings, false );
4810
+ }
4697
4811
  }
4698
- }
4812
+ } );
4699
4813
  }
4700
4814
 
4701
4815
 
@@ -4852,6 +4966,30 @@
4852
4966
  {
4853
4967
  _fnCallbackFire( settings, null, 'processing', [settings, show] );
4854
4968
  }
4969
+
4970
+ /**
4971
+ * Show the processing element if an action takes longer than a given time
4972
+ *
4973
+ * @param {*} settings DataTables settings object
4974
+ * @param {*} enable Do (true) or not (false) async processing (local feature enablement)
4975
+ * @param {*} run Function to run
4976
+ */
4977
+ function _fnProcessingRun( settings, enable, run ) {
4978
+ if (! enable) {
4979
+ // Immediate execution, synchronous
4980
+ run();
4981
+ }
4982
+ else {
4983
+ _fnProcessingDisplay(settings, true);
4984
+
4985
+ // Allow the processing display to show if needed
4986
+ setTimeout(function () {
4987
+ run();
4988
+
4989
+ _fnProcessingDisplay(settings, false);
4990
+ }, 0);
4991
+ }
4992
+ }
4855
4993
  /**
4856
4994
  * Add any control elements for the table - specifically scrolling
4857
4995
  * @param {object} settings dataTables settings object
@@ -5182,8 +5320,17 @@
5182
5320
  tableWidthAttr = table.getAttribute('width'), // from DOM element
5183
5321
  tableContainer = table.parentNode,
5184
5322
  i, column, columnIdx;
5185
-
5323
+
5186
5324
  var styleWidth = table.style.width;
5325
+
5326
+ // If there is no width applied as a CSS style or as an attribute, we assume that
5327
+ // the width is intended to be 100%, which is usually is in CSS, but it is very
5328
+ // difficult to correctly parse the rules to get the final result.
5329
+ if ( ! styleWidth && ! tableWidthAttr) {
5330
+ table.style.width = '100%';
5331
+ styleWidth = '100%';
5332
+ }
5333
+
5187
5334
  if ( styleWidth && styleWidth.indexOf('%') !== -1 ) {
5188
5335
  tableWidthAttr = styleWidth;
5189
5336
  }
@@ -5497,22 +5644,16 @@
5497
5644
  }
5498
5645
 
5499
5646
  if (run) {
5500
- _fnProcessingDisplay( settings, true );
5501
-
5502
- // Allow the processing display to show
5503
- setTimeout( function () {
5647
+ _fnProcessingRun(settings, true, function () {
5504
5648
  _fnSort( settings );
5505
5649
  _fnSortDisplay( settings, settings.aiDisplay );
5506
5650
 
5507
- // Sort processing done - redraw has its own processing display
5508
- _fnProcessingDisplay( settings, false );
5509
-
5510
5651
  _fnReDraw( settings, false, false );
5511
5652
 
5512
5653
  if (callback) {
5513
5654
  callback();
5514
5655
  }
5515
- }, 0);
5656
+ });
5516
5657
  }
5517
5658
  }
5518
5659
  } );
@@ -5671,11 +5812,6 @@
5671
5812
  displayMaster = oSettings.aiDisplayMaster,
5672
5813
  aSort;
5673
5814
 
5674
- // Resolve any column types that are unknown due to addition or invalidation
5675
- // @todo Can this be moved into a 'data-ready' handler which is called when
5676
- // data is going to be used in the table?
5677
- _fnColumnTypes( oSettings );
5678
-
5679
5815
  // Allow a specific column to be sorted, which will _not_ alter the display
5680
5816
  // master
5681
5817
  if (col !== undefined) {
@@ -5712,7 +5848,7 @@
5712
5848
 
5713
5849
  // If the first sort is desc, then reverse the array to preserve original
5714
5850
  // order, just in reverse
5715
- if (aSort.length && aSort[0].dir === 'desc') {
5851
+ if (aSort.length && aSort[0].dir === 'desc' && oSettings.orderDescReverse) {
5716
5852
  aiOrig.reverse();
5717
5853
  }
5718
5854
 
@@ -5782,6 +5918,7 @@
5782
5918
  if (col === undefined) {
5783
5919
  // Tell the draw function that we have sorted the data
5784
5920
  oSettings.bSorted = true;
5921
+ oSettings.sortDetails = aSort;
5785
5922
 
5786
5923
  _fnCallbackFire( oSettings, null, 'order', [oSettings, aSort] );
5787
5924
  }
@@ -7560,6 +7697,16 @@
7560
7697
  order = opts.order, // applied, current, index (original - compatibility with 1.9)
7561
7698
  page = opts.page; // all, current
7562
7699
 
7700
+ if ( _fnDataSource( settings ) == 'ssp' ) {
7701
+ // In server-side processing mode, most options are irrelevant since
7702
+ // rows not shown don't exist and the index order is the applied order
7703
+ // Removed is a special case - for consistency just return an empty
7704
+ // array
7705
+ return search === 'removed' ?
7706
+ [] :
7707
+ _range( 0, displayMaster.length );
7708
+ }
7709
+
7563
7710
  if ( page == 'current' ) {
7564
7711
  // Current page implies that order=current and filter=applied, since it is
7565
7712
  // fairly senseless otherwise, regardless of what order and search actually
@@ -8231,7 +8378,7 @@
8231
8378
  _api_register( _child_obj+'.isShown()', function () {
8232
8379
  var ctx = this.context;
8233
8380
 
8234
- if ( ctx.length && this.length ) {
8381
+ if ( ctx.length && this.length && ctx[0].aoData[ this[0] ] ) {
8235
8382
  // _detailsShown as false or undefined will fall through to return false
8236
8383
  return ctx[0].aoData[ this[0] ]._detailsShow || false;
8237
8384
  }
@@ -8254,7 +8401,7 @@
8254
8401
  // can be an array of these items, comma separated list, or an array of comma
8255
8402
  // separated lists
8256
8403
 
8257
- var __re_column_selector = /^([^:]+):(name|title|visIdx|visible)$/;
8404
+ var __re_column_selector = /^([^:]+)?:(name|title|visIdx|visible)$/;
8258
8405
 
8259
8406
 
8260
8407
  // r1 and r2 are redundant - but it means that the parameters match for the
@@ -8326,17 +8473,24 @@
8326
8473
  switch( match[2] ) {
8327
8474
  case 'visIdx':
8328
8475
  case 'visible':
8329
- var idx = parseInt( match[1], 10 );
8330
- // Visible index given, convert to column index
8331
- if ( idx < 0 ) {
8332
- // Counting from the right
8333
- var visColumns = columns.map( function (col,i) {
8334
- return col.bVisible ? i : null;
8335
- } );
8336
- return [ visColumns[ visColumns.length + idx ] ];
8476
+ if (match[1]) {
8477
+ var idx = parseInt( match[1], 10 );
8478
+ // Visible index given, convert to column index
8479
+ if ( idx < 0 ) {
8480
+ // Counting from the right
8481
+ var visColumns = columns.map( function (col,i) {
8482
+ return col.bVisible ? i : null;
8483
+ } );
8484
+ return [ visColumns[ visColumns.length + idx ] ];
8485
+ }
8486
+ // Counting from the left
8487
+ return [ _fnVisibleToColumnIndex( settings, idx ) ];
8337
8488
  }
8338
- // Counting from the left
8339
- return [ _fnVisibleToColumnIndex( settings, idx ) ];
8489
+
8490
+ // `:visible` on its own
8491
+ return columns.map( function (col, i) {
8492
+ return col.bVisible ? i : null;
8493
+ } );
8340
8494
 
8341
8495
  case 'name':
8342
8496
  // match by name. `names` is column index complete and in order
@@ -9186,23 +9340,60 @@
9186
9340
  } );
9187
9341
 
9188
9342
  /**
9189
- * Set the jQuery or window object to be used by DataTables
9190
- *
9191
- * @param {*} module Library / container object
9192
- * @param {string} [type] Library or container type `lib`, `win` or `datetime`.
9193
- * If not provided, automatic detection is attempted.
9194
- */
9195
- DataTable.use = function (module, type) {
9196
- if (type === 'lib' || module.fn) {
9343
+ * Set the libraries that DataTables uses, or the global objects.
9344
+ * Note that the arguments can be either way around (legacy support)
9345
+ * and the second is optional. See docs.
9346
+ */
9347
+ DataTable.use = function (arg1, arg2) {
9348
+ // Reverse arguments for legacy support
9349
+ var module = typeof arg1 === 'string'
9350
+ ? arg2
9351
+ : arg1;
9352
+ var type = typeof arg2 === 'string'
9353
+ ? arg2
9354
+ : arg1;
9355
+
9356
+ // Getter
9357
+ if (module === undefined && typeof type === 'string') {
9358
+ switch (type) {
9359
+ case 'lib':
9360
+ case 'jq':
9361
+ return $;
9362
+
9363
+ case 'win':
9364
+ return window;
9365
+
9366
+ case 'datetime':
9367
+ return DataTable.DateTime;
9368
+
9369
+ case 'luxon':
9370
+ return __luxon;
9371
+
9372
+ case 'moment':
9373
+ return __moment;
9374
+
9375
+ default:
9376
+ return null;
9377
+ }
9378
+ }
9379
+
9380
+ // Setter
9381
+ if (type === 'lib' || type === 'jq' || (module && module.fn && module.fn.jquery)) {
9197
9382
  $ = module;
9198
9383
  }
9199
- else if (type == 'win' || module.document) {
9384
+ else if (type == 'win' || (module && module.document)) {
9200
9385
  window = module;
9201
9386
  document = module.document;
9202
9387
  }
9203
- else if (type === 'datetime' || module.type === 'DateTime') {
9388
+ else if (type === 'datetime' || (module && module.type === 'DateTime')) {
9204
9389
  DataTable.DateTime = module;
9205
9390
  }
9391
+ else if (type === 'luxon' || (module && module.FixedOffsetZone)) {
9392
+ __luxon = module;
9393
+ }
9394
+ else if (type === 'moment' || (module && module.isMoment)) {
9395
+ __moment = module;
9396
+ }
9206
9397
  }
9207
9398
 
9208
9399
  /**
@@ -9611,7 +9802,7 @@
9611
9802
  * @type string
9612
9803
  * @default Version number
9613
9804
  */
9614
- DataTable.version = "2.0.7";
9805
+ DataTable.version = "2.1.0";
9615
9806
 
9616
9807
  /**
9617
9808
  * Private data store, containing all of the settings objects that are
@@ -10456,7 +10647,8 @@
10456
10647
  first: 'First',
10457
10648
  last: 'Last',
10458
10649
  next: 'Next',
10459
- previous: 'Previous'
10650
+ previous: 'Previous',
10651
+ number: ''
10460
10652
  }
10461
10653
  },
10462
10654
 
@@ -10636,6 +10828,10 @@
10636
10828
  },
10637
10829
 
10638
10830
 
10831
+ /** The initial data order is reversed when `desc` ordering */
10832
+ orderDescReverse: true,
10833
+
10834
+
10639
10835
  /**
10640
10836
  * This parameter allows you to have define the global filtering state at
10641
10837
  * initialisation time. As an object the `search` parameter must be
@@ -10684,7 +10880,7 @@
10684
10880
  * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers
10685
10881
  * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers
10686
10882
  */
10687
- "sPaginationType": "full_numbers",
10883
+ "sPaginationType": "",
10688
10884
 
10689
10885
 
10690
10886
  /**
@@ -10754,7 +10950,13 @@
10754
10950
  /**
10755
10951
  * Caption value
10756
10952
  */
10757
- "caption": null
10953
+ "caption": null,
10954
+
10955
+
10956
+ /**
10957
+ * For server-side processing - use the data from the DOM for the first draw
10958
+ */
10959
+ iDeferLoading: null
10758
10960
  };
10759
10961
 
10760
10962
  _fnHungarianMap( DataTable.defaults );
@@ -11697,7 +11899,10 @@
11697
11899
 
11698
11900
  captionNode: null,
11699
11901
 
11700
- colgroup: null
11902
+ colgroup: null,
11903
+
11904
+ /** Delay loading of data */
11905
+ deferLoading: null
11701
11906
  };
11702
11907
 
11703
11908
  /**
@@ -11721,7 +11926,7 @@
11721
11926
  },
11722
11927
 
11723
11928
  full: function () {
11724
- return [ 'first', 'previous', 'next', 'last' ];
11929
+ return [ 'first', 'previous', 'next', 'last' ];
11725
11930
  },
11726
11931
 
11727
11932
  numbers: function () {
@@ -11735,11 +11940,11 @@
11735
11940
  full_numbers: function () {
11736
11941
  return [ 'first', 'previous', 'numbers', 'next', 'last' ];
11737
11942
  },
11738
-
11943
+
11739
11944
  first_last: function () {
11740
11945
  return ['first', 'last'];
11741
11946
  },
11742
-
11947
+
11743
11948
  first_last_numbers: function () {
11744
11949
  return ['first', 'numbers', 'last'];
11745
11950
  },
@@ -11821,38 +12026,56 @@
11821
12026
  * to make working with DataTables a little bit easier.
11822
12027
  */
11823
12028
 
11824
- function __mldFnName(name) {
11825
- return name.replace(/[\W]/g, '_')
11826
- }
11827
-
11828
- // Common logic for moment, luxon or a date action
11829
- function __mld( dt, momentFn, luxonFn, dateFn, arg1 ) {
11830
- if (window.moment) {
11831
- return dt[momentFn]( arg1 );
12029
+ /**
12030
+ * Common logic for moment, luxon or a date action.
12031
+ *
12032
+ * Happens after __mldObj, so don't need to call `resolveWindowsLibs` again
12033
+ */
12034
+ function __mld( dtLib, momentFn, luxonFn, dateFn, arg1 ) {
12035
+ if (__moment) {
12036
+ return dtLib[momentFn]( arg1 );
11832
12037
  }
11833
- else if (window.luxon) {
11834
- return dt[luxonFn]( arg1 );
12038
+ else if (__luxon) {
12039
+ return dtLib[luxonFn]( arg1 );
11835
12040
  }
11836
12041
 
11837
- return dateFn ? dt[dateFn]( arg1 ) : dt;
12042
+ return dateFn ? dtLib[dateFn]( arg1 ) : dtLib;
11838
12043
  }
11839
12044
 
11840
12045
 
11841
12046
  var __mlWarning = false;
12047
+ var __luxon; // Can be assigned in DateTeble.use()
12048
+ var __moment; // Can be assigned in DateTeble.use()
12049
+
12050
+ /**
12051
+ *
12052
+ */
12053
+ function resolveWindowLibs() {
12054
+ if (window.luxon && ! __luxon) {
12055
+ __luxon = window.luxon;
12056
+ }
12057
+
12058
+ if (window.moment && ! __moment) {
12059
+ __moment = window.moment;
12060
+ }
12061
+ }
12062
+
11842
12063
  function __mldObj (d, format, locale) {
11843
12064
  var dt;
11844
12065
 
11845
- if (window.moment) {
11846
- dt = window.moment.utc( d, format, locale, true );
12066
+ resolveWindowLibs();
12067
+
12068
+ if (__moment) {
12069
+ dt = __moment.utc( d, format, locale, true );
11847
12070
 
11848
12071
  if (! dt.isValid()) {
11849
12072
  return null;
11850
12073
  }
11851
12074
  }
11852
- else if (window.luxon) {
12075
+ else if (__luxon) {
11853
12076
  dt = format && typeof d === 'string'
11854
- ? window.luxon.DateTime.fromFormat( d, format )
11855
- : window.luxon.DateTime.fromISO( d );
12077
+ ? __luxon.DateTime.fromFormat( d, format )
12078
+ : __luxon.DateTime.fromISO( d );
11856
12079
 
11857
12080
  if (! dt.isValid) {
11858
12081
  return null;
@@ -11897,7 +12120,7 @@
11897
12120
  from = null;
11898
12121
  }
11899
12122
 
11900
- var typeName = 'datetime' + (to ? '-' + __mldFnName(to) : '');
12123
+ var typeName = 'datetime' + (to ? '-' + to : '');
11901
12124
 
11902
12125
  // Add type detection and sorting specific to this date format - we need to be able to identify
11903
12126
  // date type columns as such, rather than as numbers in extensions. Hence the need for this.
@@ -12000,7 +12223,7 @@
12000
12223
 
12001
12224
  // Formatted date time detection - use by declaring the formats you are going to use
12002
12225
  DataTable.datetime = function ( format, locale ) {
12003
- var typeName = 'datetime-detect-' + __mldFnName(format);
12226
+ var typeName = 'datetime-' + format;
12004
12227
 
12005
12228
  if (! locale) {
12006
12229
  locale = 'en';
@@ -12155,27 +12378,20 @@
12155
12378
  var setProp = function(prop, propVal) {
12156
12379
  _extTypes[prop][name] = propVal;
12157
12380
  };
12158
- var setDetect = function (fn) {
12159
- // Wrap to allow the function to return `true` rather than
12160
- // specifying the type name.
12161
- var cb = function (d, s) {
12162
- var ret = fn(d, s);
12163
-
12164
- return ret === true
12165
- ? name
12166
- : ret;
12167
- };
12168
- Object.defineProperty(cb, "name", {value: name});
12381
+ var setDetect = function (detect) {
12382
+ // `detect` can be a function or an object - we set a name
12383
+ // property for either - that is used for the detection
12384
+ Object.defineProperty(detect, "name", {value: name});
12169
12385
 
12170
- var idx = _extTypes.detect.findIndex(function (fn) {
12171
- return fn.name === name;
12386
+ var idx = _extTypes.detect.findIndex(function (item) {
12387
+ return item.name === name;
12172
12388
  });
12173
12389
 
12174
12390
  if (idx === -1) {
12175
- _extTypes.detect.unshift(cb);
12391
+ _extTypes.detect.unshift(detect);
12176
12392
  }
12177
12393
  else {
12178
- _extTypes.detect.splice(idx, 1, cb);
12394
+ _extTypes.detect.splice(idx, 1, detect);
12179
12395
  }
12180
12396
  };
12181
12397
  var setOrder = function (obj) {
@@ -12235,6 +12451,19 @@
12235
12451
  });
12236
12452
  };
12237
12453
 
12454
+ var __diacriticSort = function (a, b) {
12455
+ a = a.toString().toLowerCase();
12456
+ b = b.toString().toLowerCase();
12457
+
12458
+ // Checked for `navigator.languages` support in `oneOf` so this code can't execute in old
12459
+ // Safari and thus can disable this check
12460
+ // eslint-disable-next-line compat/compat
12461
+ return a.localeCompare(b, navigator.languages[0] || navigator.language, {
12462
+ numeric: true,
12463
+ ignorePunctuation: true,
12464
+ });
12465
+ }
12466
+
12238
12467
  //
12239
12468
  // Built in data types
12240
12469
  //
@@ -12259,11 +12488,38 @@
12259
12488
  search: _filterString(false, true)
12260
12489
  });
12261
12490
 
12491
+ DataTable.type('string-utf8', {
12492
+ detect: {
12493
+ allOf: function ( d ) {
12494
+ return true;
12495
+ },
12496
+ oneOf: function ( d ) {
12497
+ // At least one data point must contain a non-ASCII character
12498
+ // This line will also check if navigator.languages is supported or not. If not (Safari 10.0-)
12499
+ // this data type won't be supported.
12500
+ // eslint-disable-next-line compat/compat
12501
+ return ! _empty( d ) && navigator.languages && typeof d === 'string' && d.match(/[^\x00-\x7F]/);
12502
+ }
12503
+ },
12504
+ order: {
12505
+ asc: __diacriticSort,
12506
+ desc: function (a, b) {
12507
+ return __diacriticSort(a, b) * -1;
12508
+ }
12509
+ },
12510
+ search: _filterString(false, true)
12511
+ });
12512
+
12262
12513
 
12263
12514
  DataTable.type('html', {
12264
- detect: function ( d ) {
12265
- return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ?
12266
- 'html' : null;
12515
+ detect: {
12516
+ allOf: function ( d ) {
12517
+ return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1);
12518
+ },
12519
+ oneOf: function ( d ) {
12520
+ // At least one data point must contain a `<`
12521
+ return ! _empty( d ) && typeof d === 'string' && d.indexOf('<') !== -1;
12522
+ }
12267
12523
  },
12268
12524
  order: {
12269
12525
  pre: function ( a ) {
@@ -12280,16 +12536,21 @@
12280
12536
 
12281
12537
  DataTable.type('date', {
12282
12538
  className: 'dt-type-date',
12283
- detect: function ( d )
12284
- {
12285
- // V8 tries _very_ hard to make a string passed into `Date.parse()`
12286
- // valid, so we need to use a regex to restrict date formats. Use a
12287
- // plug-in for anything other than ISO8601 style strings
12288
- if ( d && !(d instanceof Date) && ! _re_date.test(d) ) {
12289
- return null;
12539
+ detect: {
12540
+ allOf: function ( d ) {
12541
+ // V8 tries _very_ hard to make a string passed into `Date.parse()`
12542
+ // valid, so we need to use a regex to restrict date formats. Use a
12543
+ // plug-in for anything other than ISO8601 style strings
12544
+ if ( d && !(d instanceof Date) && ! _re_date.test(d) ) {
12545
+ return null;
12546
+ }
12547
+ var parsed = Date.parse(d);
12548
+ return (parsed !== null && !isNaN(parsed)) || _empty(d);
12549
+ },
12550
+ oneOf: function ( d ) {
12551
+ // At least one entry must be a date or a string with a date
12552
+ return (d instanceof Date) || (typeof d === 'string' && _re_date.test(d));
12290
12553
  }
12291
- var parsed = Date.parse(d);
12292
- return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null;
12293
12554
  },
12294
12555
  order: {
12295
12556
  pre: function ( d ) {
@@ -12302,10 +12563,16 @@
12302
12563
 
12303
12564
  DataTable.type('html-num-fmt', {
12304
12565
  className: 'dt-type-numeric',
12305
- detect: function ( d, settings )
12306
- {
12307
- var decimal = settings.oLanguage.sDecimal;
12308
- return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt' : null;
12566
+ detect: {
12567
+ allOf: function ( d, settings ) {
12568
+ var decimal = settings.oLanguage.sDecimal;
12569
+ return _htmlNumeric( d, decimal, true, false );
12570
+ },
12571
+ oneOf: function (d, settings) {
12572
+ // At least one data point must contain a numeric value
12573
+ var decimal = settings.oLanguage.sDecimal;
12574
+ return _htmlNumeric( d, decimal, true, false );
12575
+ }
12309
12576
  },
12310
12577
  order: {
12311
12578
  pre: function ( d, s ) {
@@ -12319,10 +12586,16 @@
12319
12586
 
12320
12587
  DataTable.type('html-num', {
12321
12588
  className: 'dt-type-numeric',
12322
- detect: function ( d, settings )
12323
- {
12324
- var decimal = settings.oLanguage.sDecimal;
12325
- return _htmlNumeric( d, decimal ) ? 'html-num' : null;
12589
+ detect: {
12590
+ allOf: function ( d, settings ) {
12591
+ var decimal = settings.oLanguage.sDecimal;
12592
+ return _htmlNumeric( d, decimal, false, true );
12593
+ },
12594
+ oneOf: function (d, settings) {
12595
+ // At least one data point must contain a numeric value
12596
+ var decimal = settings.oLanguage.sDecimal;
12597
+ return _htmlNumeric( d, decimal, false, false );
12598
+ }
12326
12599
  },
12327
12600
  order: {
12328
12601
  pre: function ( d, s ) {
@@ -12336,10 +12609,16 @@
12336
12609
 
12337
12610
  DataTable.type('num-fmt', {
12338
12611
  className: 'dt-type-numeric',
12339
- detect: function ( d, settings )
12340
- {
12341
- var decimal = settings.oLanguage.sDecimal;
12342
- return _isNumber( d, decimal, true ) ? 'num-fmt' : null;
12612
+ detect: {
12613
+ allOf: function ( d, settings ) {
12614
+ var decimal = settings.oLanguage.sDecimal;
12615
+ return _isNumber( d, decimal, true, true );
12616
+ },
12617
+ oneOf: function (d, settings) {
12618
+ // At least one data point must contain a numeric value
12619
+ var decimal = settings.oLanguage.sDecimal;
12620
+ return _isNumber( d, decimal, true, false );
12621
+ }
12343
12622
  },
12344
12623
  order: {
12345
12624
  pre: function ( d, s ) {
@@ -12352,10 +12631,16 @@
12352
12631
 
12353
12632
  DataTable.type('num', {
12354
12633
  className: 'dt-type-numeric',
12355
- detect: function ( d, settings )
12356
- {
12357
- var decimal = settings.oLanguage.sDecimal;
12358
- return _isNumber( d, decimal ) ? 'num' : null;
12634
+ detect: {
12635
+ allOf: function ( d, settings ) {
12636
+ var decimal = settings.oLanguage.sDecimal;
12637
+ return _isNumber( d, decimal, false, true );
12638
+ },
12639
+ oneOf: function (d, settings) {
12640
+ // At least one data point must contain a numeric value
12641
+ var decimal = settings.oLanguage.sDecimal;
12642
+ return _isNumber( d, decimal, false, false );
12643
+ }
12359
12644
  },
12360
12645
  order: {
12361
12646
  pre: function (d, s) {
@@ -12439,7 +12724,7 @@
12439
12724
  // `DT` namespace will allow the event to be removed automatically
12440
12725
  // on destroy, while the `dt` namespaced event is the one we are
12441
12726
  // listening for
12442
- $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting ) {
12727
+ $(settings.nTable).on( 'order.dt.DT column-visibility.dt.DT', function ( e, ctx ) {
12443
12728
  if ( settings !== ctx ) { // need to check this this is the host
12444
12729
  return; // table, not a nested one
12445
12730
  }
@@ -12451,9 +12736,15 @@
12451
12736
  var ariaType = '';
12452
12737
  var indexes = columns.indexes();
12453
12738
  var sortDirs = columns.orderable(true).flatten();
12454
- var orderedColumns = ',' + sorting.map( function (val) {
12455
- return val.col;
12456
- } ).join(',') + ',';
12739
+ var sorting = ctx.sortDetails;
12740
+ var orderedColumns = ',' + sorting
12741
+ .filter( function (sort) {
12742
+ // Filter to just the visible columns
12743
+ return ctx.aoColumns[sort.col].bVisible;
12744
+ } )
12745
+ .map( function (sort) {
12746
+ return sort.col;
12747
+ } ).join(',') + ',';
12457
12748
 
12458
12749
  cell
12459
12750
  .removeClass(
@@ -12463,7 +12754,8 @@
12463
12754
  .toggleClass( orderClasses.none, ! orderable )
12464
12755
  .toggleClass( orderClasses.canAsc, orderable && sortDirs.includes('asc') )
12465
12756
  .toggleClass( orderClasses.canDesc, orderable && sortDirs.includes('desc') );
12466
-
12757
+
12758
+ // Get the index of this cell in the sort array
12467
12759
  var sortIdx = orderedColumns.indexOf( ',' + indexes.toArray().join(',') + ',' );
12468
12760
 
12469
12761
  if ( sortIdx !== -1 ) {
@@ -12507,23 +12799,40 @@
12507
12799
 
12508
12800
  layout: {
12509
12801
  _: function ( settings, container, items ) {
12802
+ var classes = settings.oClasses.layout;
12510
12803
  var row = $('<div/>')
12511
- .addClass('dt-layout-row')
12804
+ .attr('id', items.id || null)
12805
+ .addClass(items.className || classes.row)
12512
12806
  .appendTo( container );
12513
12807
 
12514
12808
  $.each( items, function (key, val) {
12515
- var klass = ! val.table ?
12516
- 'dt-'+key+' ' :
12517
- '';
12809
+ if (key === 'id' || key === 'className') {
12810
+ return;
12811
+ }
12812
+
12813
+ var klass = '';
12518
12814
 
12519
12815
  if (val.table) {
12520
- row.addClass('dt-layout-table');
12816
+ row.addClass(classes.tableRow);
12817
+ klass += classes.tableCell + ' ';
12818
+ }
12819
+
12820
+ if (key === 'start') {
12821
+ klass += classes.start;
12822
+ }
12823
+ else if (key === 'end') {
12824
+ klass += classes.end;
12825
+ }
12826
+ else {
12827
+ klass += classes.full;
12521
12828
  }
12522
12829
 
12523
12830
  $('<div/>')
12524
12831
  .attr({
12525
12832
  id: val.id || null,
12526
- "class": 'dt-layout-cell '+klass+(val.className || '')
12833
+ "class": val.className
12834
+ ? val.className
12835
+ : classes.cell + ' ' + klass
12527
12836
  })
12528
12837
  .append( val.contents )
12529
12838
  .appendTo( row );
@@ -12547,6 +12856,25 @@
12547
12856
  }
12548
12857
  };
12549
12858
 
12859
+ function _divProp(el, prop, val) {
12860
+ if (val) {
12861
+ el[prop] = val;
12862
+ }
12863
+ }
12864
+
12865
+ DataTable.feature.register( 'div', function ( settings, opts ) {
12866
+ var n = $('<div>')[0];
12867
+
12868
+ if (opts) {
12869
+ _divProp(n, 'className', opts.className);
12870
+ _divProp(n, 'id', opts.id);
12871
+ _divProp(n, 'innerHTML', opts.html);
12872
+ _divProp(n, 'textContent', opts.text);
12873
+ }
12874
+
12875
+ return n;
12876
+ } );
12877
+
12550
12878
  DataTable.feature.register( 'info', function ( settings, opts ) {
12551
12879
  // For compatibility with the legacy `info` top level option
12552
12880
  if (! settings.oFeatures.bInfo) {
@@ -12646,6 +12974,7 @@
12646
12974
 
12647
12975
  opts = $.extend({
12648
12976
  placeholder: language.sSearchPlaceholder,
12977
+ processing: false,
12649
12978
  text: language.sSearch
12650
12979
  }, opts);
12651
12980
 
@@ -12689,13 +13018,15 @@
12689
13018
 
12690
13019
  /* Now do the filter */
12691
13020
  if ( val != previousSearch.search ) {
12692
- previousSearch.search = val;
12693
-
12694
- _fnFilterComplete( settings, previousSearch );
12695
-
12696
- // Need to redraw, without resorting
12697
- settings._iDisplayStart = 0;
12698
- _fnDraw( settings );
13021
+ _fnProcessingRun(settings, opts.processing, function () {
13022
+ previousSearch.search = val;
13023
+
13024
+ _fnFilterComplete( settings, previousSearch );
13025
+
13026
+ // Need to redraw, without resorting
13027
+ settings._iDisplayStart = 0;
13028
+ _fnDraw( settings );
13029
+ });
12699
13030
  }
12700
13031
  };
12701
13032
 
@@ -12753,17 +13084,17 @@
12753
13084
  opts = $.extend({
12754
13085
  buttons: DataTable.ext.pager.numbers_length,
12755
13086
  type: settings.sPaginationType,
12756
- boundaryNumbers: true
13087
+ boundaryNumbers: true,
13088
+ firstLast: true,
13089
+ previousNext: true,
13090
+ numbers: true
12757
13091
  }, opts);
12758
13092
 
12759
- // To be removed in 2.1
12760
- if (opts.numbers) {
12761
- opts.buttons = opts.numbers;
12762
- }
12763
-
12764
- var host = $('<div/>').addClass( settings.oClasses.paging.container + ' paging_' + opts.type );
13093
+ var host = $('<div/>')
13094
+ .addClass(settings.oClasses.paging.container + (opts.type ? ' paging_' + opts.type : ''))
13095
+ .append('<nav>');
12765
13096
  var draw = function () {
12766
- _pagingDraw(settings, host, opts);
13097
+ _pagingDraw(settings, host.children(), opts);
12767
13098
  };
12768
13099
 
12769
13100
  settings.aoDrawCallback.push(draw);
@@ -12774,13 +13105,39 @@
12774
13105
  return host;
12775
13106
  }, 'p' );
12776
13107
 
13108
+ /**
13109
+ * Dynamically create the button type array based on the configuration options.
13110
+ * This will only happen if the paging type is not defined.
13111
+ */
13112
+ function _pagingDynamic(opts) {
13113
+ var out = [];
13114
+
13115
+ if (opts.numbers) {
13116
+ out.push('numbers');
13117
+ }
13118
+
13119
+ if (opts.previousNext) {
13120
+ out.unshift('previous');
13121
+ out.push('next');
13122
+ }
13123
+
13124
+ if (opts.firstLast) {
13125
+ out.unshift('first');
13126
+ out.push('last');
13127
+ }
13128
+
13129
+ return out;
13130
+ }
13131
+
12777
13132
  function _pagingDraw(settings, host, opts) {
12778
13133
  if (! settings._bInitComplete) {
12779
13134
  return;
12780
13135
  }
12781
13136
 
12782
13137
  var
12783
- plugin = DataTable.ext.pager[ opts.type ],
13138
+ plugin = opts.type
13139
+ ? DataTable.ext.pager[ opts.type ]
13140
+ : _pagingDynamic,
12784
13141
  aria = settings.oLanguage.oAria.paginate || {},
12785
13142
  start = settings._iDisplayStart,
12786
13143
  len = settings._iDisplayLength,
@@ -12788,7 +13145,7 @@
12788
13145
  all = len === -1,
12789
13146
  page = all ? 0 : Math.ceil( start / len ),
12790
13147
  pages = all ? 1 : Math.ceil( visRecords / len ),
12791
- buttons = plugin()
13148
+ buttons = plugin(opts)
12792
13149
  .map(function (val) {
12793
13150
  return val === 'numbers'
12794
13151
  ? _pagingNumbers(page, pages, opts.buttons, opts.boundaryNumbers)
@@ -12810,14 +13167,24 @@
12810
13167
  btnInfo.disabled
12811
13168
  );
12812
13169
 
13170
+ var ariaLabel = typeof button === 'string'
13171
+ ? aria[ button ]
13172
+ : aria.number
13173
+ ? aria.number + (button+1)
13174
+ : null;
13175
+
12813
13176
  // Common attributes
12814
13177
  $(btn.clicker).attr({
12815
13178
  'aria-controls': settings.sTableId,
12816
13179
  'aria-disabled': btnInfo.disabled ? 'true' : null,
12817
13180
  'aria-current': btnInfo.active ? 'page' : null,
12818
- 'aria-label': aria[ button ],
13181
+ 'aria-label': ariaLabel,
12819
13182
  'data-dt-idx': button,
12820
- 'tabIndex': btnInfo.disabled ? -1 : settings.iTabIndex,
13183
+ 'tabIndex': btnInfo.disabled
13184
+ ? -1
13185
+ : settings.iTabIndex
13186
+ ? settings.iTabIndex
13187
+ : null, // `0` doesn't need a tabIndex since it is the default
12821
13188
  });
12822
13189
 
12823
13190
  if (typeof button !== 'number') {
@@ -12851,10 +13218,10 @@
12851
13218
  // height of the buttons and the container.
12852
13219
  if (
12853
13220
  buttonEls.length && // any buttons
12854
- opts.numbers > 1 && // prevent infinite
13221
+ opts.buttons > 1 && // prevent infinite
12855
13222
  $(host).height() >= ($(buttonEls[0]).outerHeight() * 2) - 10
12856
13223
  ) {
12857
- _pagingDraw(settings, host, $.extend({}, opts, { numbers: opts.numbers - 2 }));
13224
+ _pagingDraw(settings, host, $.extend({}, opts, { buttons: opts.buttons - 2 }));
12858
13225
  }
12859
13226
  }
12860
13227
 
@@ -13056,10 +13423,11 @@
13056
13423
  }
13057
13424
 
13058
13425
  // Wrapper element - use a span as a holder for where the select will go
13426
+ var tmpId = 'tmp-' + (+new Date())
13059
13427
  var div = $('<div/>')
13060
13428
  .addClass( classes.container )
13061
13429
  .append(
13062
- str.replace( '_MENU_', '<span></span>' )
13430
+ str.replace( '_MENU_', '<span id="'+tmpId+'"></span>' )
13063
13431
  );
13064
13432
 
13065
13433
  // Save text node content for macro updating
@@ -13102,7 +13470,7 @@
13102
13470
  __lengthCounter++;
13103
13471
 
13104
13472
  // Swap in the select list
13105
- div.find('span').replaceWith(select);
13473
+ div.find('#' + tmpId).replaceWith(select);
13106
13474
 
13107
13475
  // Can't use `select` variable as user might provide their own and the
13108
13476
  // reference is broken by the use of outerHTML