@ntlab/ntjs-assets 2.0.31 → 2.0.32

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.
@@ -1,11 +1,11 @@
1
- /*! DataTables 2.0.8
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.8
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));
2280
2301
 
2302
+ if (detectedType) {
2303
+ col.sType = detectedType;
2304
+ break;
2305
+ }
2306
+ }
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);
3581
3605
  }
3606
+ else {
3607
+ Object.keys(items).map(function (key) {
3608
+ rowCell.contents.push( {
3609
+ feature: key,
3610
+ opts: items[key]
3611
+ });
3612
+ });
3613
+ }
3614
+ }
3615
+ else {
3616
+ rowCell.contents.push(items);
3617
+ }
3618
+ }
3582
3619
 
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]);
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
+ }
3641
+
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
  }
@@ -9203,23 +9340,60 @@
9203
9340
  } );
9204
9341
 
9205
9342
  /**
9206
- * Set the jQuery or window object to be used by DataTables
9207
- *
9208
- * @param {*} module Library / container object
9209
- * @param {string} [type] Library or container type `lib`, `win` or `datetime`.
9210
- * If not provided, automatic detection is attempted.
9211
- */
9212
- DataTable.use = function (module, type) {
9213
- 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)) {
9214
9382
  $ = module;
9215
9383
  }
9216
- else if (type == 'win' || module.document) {
9384
+ else if (type == 'win' || (module && module.document)) {
9217
9385
  window = module;
9218
9386
  document = module.document;
9219
9387
  }
9220
- else if (type === 'datetime' || module.type === 'DateTime') {
9388
+ else if (type === 'datetime' || (module && module.type === 'DateTime')) {
9221
9389
  DataTable.DateTime = module;
9222
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
+ }
9223
9397
  }
9224
9398
 
9225
9399
  /**
@@ -9628,7 +9802,7 @@
9628
9802
  * @type string
9629
9803
  * @default Version number
9630
9804
  */
9631
- DataTable.version = "2.0.8";
9805
+ DataTable.version = "2.1.0";
9632
9806
 
9633
9807
  /**
9634
9808
  * Private data store, containing all of the settings objects that are
@@ -10473,7 +10647,8 @@
10473
10647
  first: 'First',
10474
10648
  last: 'Last',
10475
10649
  next: 'Next',
10476
- previous: 'Previous'
10650
+ previous: 'Previous',
10651
+ number: ''
10477
10652
  }
10478
10653
  },
10479
10654
 
@@ -10653,6 +10828,10 @@
10653
10828
  },
10654
10829
 
10655
10830
 
10831
+ /** The initial data order is reversed when `desc` ordering */
10832
+ orderDescReverse: true,
10833
+
10834
+
10656
10835
  /**
10657
10836
  * This parameter allows you to have define the global filtering state at
10658
10837
  * initialisation time. As an object the `search` parameter must be
@@ -10701,7 +10880,7 @@
10701
10880
  * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers
10702
10881
  * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers
10703
10882
  */
10704
- "sPaginationType": "full_numbers",
10883
+ "sPaginationType": "",
10705
10884
 
10706
10885
 
10707
10886
  /**
@@ -10771,7 +10950,13 @@
10771
10950
  /**
10772
10951
  * Caption value
10773
10952
  */
10774
- "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
10775
10960
  };
10776
10961
 
10777
10962
  _fnHungarianMap( DataTable.defaults );
@@ -11714,7 +11899,10 @@
11714
11899
 
11715
11900
  captionNode: null,
11716
11901
 
11717
- colgroup: null
11902
+ colgroup: null,
11903
+
11904
+ /** Delay loading of data */
11905
+ deferLoading: null
11718
11906
  };
11719
11907
 
11720
11908
  /**
@@ -11738,7 +11926,7 @@
11738
11926
  },
11739
11927
 
11740
11928
  full: function () {
11741
- return [ 'first', 'previous', 'next', 'last' ];
11929
+ return [ 'first', 'previous', 'next', 'last' ];
11742
11930
  },
11743
11931
 
11744
11932
  numbers: function () {
@@ -11752,11 +11940,11 @@
11752
11940
  full_numbers: function () {
11753
11941
  return [ 'first', 'previous', 'numbers', 'next', 'last' ];
11754
11942
  },
11755
-
11943
+
11756
11944
  first_last: function () {
11757
11945
  return ['first', 'last'];
11758
11946
  },
11759
-
11947
+
11760
11948
  first_last_numbers: function () {
11761
11949
  return ['first', 'numbers', 'last'];
11762
11950
  },
@@ -11838,38 +12026,56 @@
11838
12026
  * to make working with DataTables a little bit easier.
11839
12027
  */
11840
12028
 
11841
- function __mldFnName(name) {
11842
- return name.replace(/[\W]/g, '_')
11843
- }
11844
-
11845
- // Common logic for moment, luxon or a date action
11846
- function __mld( dt, momentFn, luxonFn, dateFn, arg1 ) {
11847
- if (window.moment) {
11848
- 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 );
11849
12037
  }
11850
- else if (window.luxon) {
11851
- return dt[luxonFn]( arg1 );
12038
+ else if (__luxon) {
12039
+ return dtLib[luxonFn]( arg1 );
11852
12040
  }
11853
12041
 
11854
- return dateFn ? dt[dateFn]( arg1 ) : dt;
12042
+ return dateFn ? dtLib[dateFn]( arg1 ) : dtLib;
11855
12043
  }
11856
12044
 
11857
12045
 
11858
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
+
11859
12063
  function __mldObj (d, format, locale) {
11860
12064
  var dt;
11861
12065
 
11862
- if (window.moment) {
11863
- dt = window.moment.utc( d, format, locale, true );
12066
+ resolveWindowLibs();
12067
+
12068
+ if (__moment) {
12069
+ dt = __moment.utc( d, format, locale, true );
11864
12070
 
11865
12071
  if (! dt.isValid()) {
11866
12072
  return null;
11867
12073
  }
11868
12074
  }
11869
- else if (window.luxon) {
12075
+ else if (__luxon) {
11870
12076
  dt = format && typeof d === 'string'
11871
- ? window.luxon.DateTime.fromFormat( d, format )
11872
- : window.luxon.DateTime.fromISO( d );
12077
+ ? __luxon.DateTime.fromFormat( d, format )
12078
+ : __luxon.DateTime.fromISO( d );
11873
12079
 
11874
12080
  if (! dt.isValid) {
11875
12081
  return null;
@@ -11914,7 +12120,7 @@
11914
12120
  from = null;
11915
12121
  }
11916
12122
 
11917
- var typeName = 'datetime' + (to ? '-' + __mldFnName(to) : '');
12123
+ var typeName = 'datetime' + (to ? '-' + to : '');
11918
12124
 
11919
12125
  // Add type detection and sorting specific to this date format - we need to be able to identify
11920
12126
  // date type columns as such, rather than as numbers in extensions. Hence the need for this.
@@ -12017,7 +12223,7 @@
12017
12223
 
12018
12224
  // Formatted date time detection - use by declaring the formats you are going to use
12019
12225
  DataTable.datetime = function ( format, locale ) {
12020
- var typeName = 'datetime-detect-' + __mldFnName(format);
12226
+ var typeName = 'datetime-' + format;
12021
12227
 
12022
12228
  if (! locale) {
12023
12229
  locale = 'en';
@@ -12172,27 +12378,20 @@
12172
12378
  var setProp = function(prop, propVal) {
12173
12379
  _extTypes[prop][name] = propVal;
12174
12380
  };
12175
- var setDetect = function (fn) {
12176
- // Wrap to allow the function to return `true` rather than
12177
- // specifying the type name.
12178
- var cb = function (d, s) {
12179
- var ret = fn(d, s);
12180
-
12181
- return ret === true
12182
- ? name
12183
- : ret;
12184
- };
12185
- 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});
12186
12385
 
12187
- var idx = _extTypes.detect.findIndex(function (fn) {
12188
- return fn.name === name;
12386
+ var idx = _extTypes.detect.findIndex(function (item) {
12387
+ return item.name === name;
12189
12388
  });
12190
12389
 
12191
12390
  if (idx === -1) {
12192
- _extTypes.detect.unshift(cb);
12391
+ _extTypes.detect.unshift(detect);
12193
12392
  }
12194
12393
  else {
12195
- _extTypes.detect.splice(idx, 1, cb);
12394
+ _extTypes.detect.splice(idx, 1, detect);
12196
12395
  }
12197
12396
  };
12198
12397
  var setOrder = function (obj) {
@@ -12252,6 +12451,19 @@
12252
12451
  });
12253
12452
  };
12254
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
+
12255
12467
  //
12256
12468
  // Built in data types
12257
12469
  //
@@ -12276,11 +12488,38 @@
12276
12488
  search: _filterString(false, true)
12277
12489
  });
12278
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
+
12279
12513
 
12280
12514
  DataTable.type('html', {
12281
- detect: function ( d ) {
12282
- return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ?
12283
- '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
+ }
12284
12523
  },
12285
12524
  order: {
12286
12525
  pre: function ( a ) {
@@ -12297,16 +12536,21 @@
12297
12536
 
12298
12537
  DataTable.type('date', {
12299
12538
  className: 'dt-type-date',
12300
- detect: function ( d )
12301
- {
12302
- // V8 tries _very_ hard to make a string passed into `Date.parse()`
12303
- // valid, so we need to use a regex to restrict date formats. Use a
12304
- // plug-in for anything other than ISO8601 style strings
12305
- if ( d && !(d instanceof Date) && ! _re_date.test(d) ) {
12306
- 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));
12307
12553
  }
12308
- var parsed = Date.parse(d);
12309
- return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null;
12310
12554
  },
12311
12555
  order: {
12312
12556
  pre: function ( d ) {
@@ -12319,10 +12563,16 @@
12319
12563
 
12320
12564
  DataTable.type('html-num-fmt', {
12321
12565
  className: 'dt-type-numeric',
12322
- detect: function ( d, settings )
12323
- {
12324
- var decimal = settings.oLanguage.sDecimal;
12325
- 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
+ }
12326
12576
  },
12327
12577
  order: {
12328
12578
  pre: function ( d, s ) {
@@ -12336,10 +12586,16 @@
12336
12586
 
12337
12587
  DataTable.type('html-num', {
12338
12588
  className: 'dt-type-numeric',
12339
- detect: function ( d, settings )
12340
- {
12341
- var decimal = settings.oLanguage.sDecimal;
12342
- 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
+ }
12343
12599
  },
12344
12600
  order: {
12345
12601
  pre: function ( d, s ) {
@@ -12353,10 +12609,16 @@
12353
12609
 
12354
12610
  DataTable.type('num-fmt', {
12355
12611
  className: 'dt-type-numeric',
12356
- detect: function ( d, settings )
12357
- {
12358
- var decimal = settings.oLanguage.sDecimal;
12359
- 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
+ }
12360
12622
  },
12361
12623
  order: {
12362
12624
  pre: function ( d, s ) {
@@ -12369,10 +12631,16 @@
12369
12631
 
12370
12632
  DataTable.type('num', {
12371
12633
  className: 'dt-type-numeric',
12372
- detect: function ( d, settings )
12373
- {
12374
- var decimal = settings.oLanguage.sDecimal;
12375
- 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
+ }
12376
12644
  },
12377
12645
  order: {
12378
12646
  pre: function (d, s) {
@@ -12456,7 +12724,7 @@
12456
12724
  // `DT` namespace will allow the event to be removed automatically
12457
12725
  // on destroy, while the `dt` namespaced event is the one we are
12458
12726
  // listening for
12459
- $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting ) {
12727
+ $(settings.nTable).on( 'order.dt.DT column-visibility.dt.DT', function ( e, ctx ) {
12460
12728
  if ( settings !== ctx ) { // need to check this this is the host
12461
12729
  return; // table, not a nested one
12462
12730
  }
@@ -12468,9 +12736,15 @@
12468
12736
  var ariaType = '';
12469
12737
  var indexes = columns.indexes();
12470
12738
  var sortDirs = columns.orderable(true).flatten();
12471
- var orderedColumns = ',' + sorting.map( function (val) {
12472
- return val.col;
12473
- } ).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(',') + ',';
12474
12748
 
12475
12749
  cell
12476
12750
  .removeClass(
@@ -12480,7 +12754,8 @@
12480
12754
  .toggleClass( orderClasses.none, ! orderable )
12481
12755
  .toggleClass( orderClasses.canAsc, orderable && sortDirs.includes('asc') )
12482
12756
  .toggleClass( orderClasses.canDesc, orderable && sortDirs.includes('desc') );
12483
-
12757
+
12758
+ // Get the index of this cell in the sort array
12484
12759
  var sortIdx = orderedColumns.indexOf( ',' + indexes.toArray().join(',') + ',' );
12485
12760
 
12486
12761
  if ( sortIdx !== -1 ) {
@@ -12524,23 +12799,40 @@
12524
12799
 
12525
12800
  layout: {
12526
12801
  _: function ( settings, container, items ) {
12802
+ var classes = settings.oClasses.layout;
12527
12803
  var row = $('<div/>')
12528
- .addClass('dt-layout-row')
12804
+ .attr('id', items.id || null)
12805
+ .addClass(items.className || classes.row)
12529
12806
  .appendTo( container );
12530
12807
 
12531
12808
  $.each( items, function (key, val) {
12532
- var klass = ! val.table ?
12533
- 'dt-'+key+' ' :
12534
- '';
12809
+ if (key === 'id' || key === 'className') {
12810
+ return;
12811
+ }
12812
+
12813
+ var klass = '';
12535
12814
 
12536
12815
  if (val.table) {
12537
- 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;
12538
12828
  }
12539
12829
 
12540
12830
  $('<div/>')
12541
12831
  .attr({
12542
12832
  id: val.id || null,
12543
- "class": 'dt-layout-cell '+klass+(val.className || '')
12833
+ "class": val.className
12834
+ ? val.className
12835
+ : classes.cell + ' ' + klass
12544
12836
  })
12545
12837
  .append( val.contents )
12546
12838
  .appendTo( row );
@@ -12564,6 +12856,25 @@
12564
12856
  }
12565
12857
  };
12566
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
+
12567
12878
  DataTable.feature.register( 'info', function ( settings, opts ) {
12568
12879
  // For compatibility with the legacy `info` top level option
12569
12880
  if (! settings.oFeatures.bInfo) {
@@ -12663,6 +12974,7 @@
12663
12974
 
12664
12975
  opts = $.extend({
12665
12976
  placeholder: language.sSearchPlaceholder,
12977
+ processing: false,
12666
12978
  text: language.sSearch
12667
12979
  }, opts);
12668
12980
 
@@ -12706,13 +13018,15 @@
12706
13018
 
12707
13019
  /* Now do the filter */
12708
13020
  if ( val != previousSearch.search ) {
12709
- previousSearch.search = val;
12710
-
12711
- _fnFilterComplete( settings, previousSearch );
12712
-
12713
- // Need to redraw, without resorting
12714
- settings._iDisplayStart = 0;
12715
- _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
+ });
12716
13030
  }
12717
13031
  };
12718
13032
 
@@ -12770,17 +13084,17 @@
12770
13084
  opts = $.extend({
12771
13085
  buttons: DataTable.ext.pager.numbers_length,
12772
13086
  type: settings.sPaginationType,
12773
- boundaryNumbers: true
13087
+ boundaryNumbers: true,
13088
+ firstLast: true,
13089
+ previousNext: true,
13090
+ numbers: true
12774
13091
  }, opts);
12775
13092
 
12776
- // To be removed in 2.1
12777
- if (opts.numbers) {
12778
- opts.buttons = opts.numbers;
12779
- }
12780
-
12781
- 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>');
12782
13096
  var draw = function () {
12783
- _pagingDraw(settings, host, opts);
13097
+ _pagingDraw(settings, host.children(), opts);
12784
13098
  };
12785
13099
 
12786
13100
  settings.aoDrawCallback.push(draw);
@@ -12791,13 +13105,39 @@
12791
13105
  return host;
12792
13106
  }, 'p' );
12793
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
+
12794
13132
  function _pagingDraw(settings, host, opts) {
12795
13133
  if (! settings._bInitComplete) {
12796
13134
  return;
12797
13135
  }
12798
13136
 
12799
13137
  var
12800
- plugin = DataTable.ext.pager[ opts.type ],
13138
+ plugin = opts.type
13139
+ ? DataTable.ext.pager[ opts.type ]
13140
+ : _pagingDynamic,
12801
13141
  aria = settings.oLanguage.oAria.paginate || {},
12802
13142
  start = settings._iDisplayStart,
12803
13143
  len = settings._iDisplayLength,
@@ -12805,7 +13145,7 @@
12805
13145
  all = len === -1,
12806
13146
  page = all ? 0 : Math.ceil( start / len ),
12807
13147
  pages = all ? 1 : Math.ceil( visRecords / len ),
12808
- buttons = plugin()
13148
+ buttons = plugin(opts)
12809
13149
  .map(function (val) {
12810
13150
  return val === 'numbers'
12811
13151
  ? _pagingNumbers(page, pages, opts.buttons, opts.boundaryNumbers)
@@ -12827,14 +13167,24 @@
12827
13167
  btnInfo.disabled
12828
13168
  );
12829
13169
 
13170
+ var ariaLabel = typeof button === 'string'
13171
+ ? aria[ button ]
13172
+ : aria.number
13173
+ ? aria.number + (button+1)
13174
+ : null;
13175
+
12830
13176
  // Common attributes
12831
13177
  $(btn.clicker).attr({
12832
13178
  'aria-controls': settings.sTableId,
12833
13179
  'aria-disabled': btnInfo.disabled ? 'true' : null,
12834
13180
  'aria-current': btnInfo.active ? 'page' : null,
12835
- 'aria-label': aria[ button ],
13181
+ 'aria-label': ariaLabel,
12836
13182
  'data-dt-idx': button,
12837
- '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
12838
13188
  });
12839
13189
 
12840
13190
  if (typeof button !== 'number') {
@@ -12868,10 +13218,10 @@
12868
13218
  // height of the buttons and the container.
12869
13219
  if (
12870
13220
  buttonEls.length && // any buttons
12871
- opts.numbers > 1 && // prevent infinite
13221
+ opts.buttons > 1 && // prevent infinite
12872
13222
  $(host).height() >= ($(buttonEls[0]).outerHeight() * 2) - 10
12873
13223
  ) {
12874
- _pagingDraw(settings, host, $.extend({}, opts, { numbers: opts.numbers - 2 }));
13224
+ _pagingDraw(settings, host, $.extend({}, opts, { buttons: opts.buttons - 2 }));
12875
13225
  }
12876
13226
  }
12877
13227
 
@@ -13073,10 +13423,11 @@
13073
13423
  }
13074
13424
 
13075
13425
  // Wrapper element - use a span as a holder for where the select will go
13426
+ var tmpId = 'tmp-' + (+new Date())
13076
13427
  var div = $('<div/>')
13077
13428
  .addClass( classes.container )
13078
13429
  .append(
13079
- str.replace( '_MENU_', '<span></span>' )
13430
+ str.replace( '_MENU_', '<span id="'+tmpId+'"></span>' )
13080
13431
  );
13081
13432
 
13082
13433
  // Save text node content for macro updating
@@ -13119,7 +13470,7 @@
13119
13470
  __lengthCounter++;
13120
13471
 
13121
13472
  // Swap in the select list
13122
- div.find('span').replaceWith(select);
13473
+ div.find('#' + tmpId).replaceWith(select);
13123
13474
 
13124
13475
  // Can't use `select` variable as user might provide their own and the
13125
13476
  // reference is broken by the use of outerHTML