datatables.net 2.2.2 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/js/dataTables.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! DataTables 2.2.2
1
+ /*! DataTables 2.3.1
2
2
  * © SpryMedia Ltd - datatables.net/license
3
3
  */
4
4
 
@@ -54,15 +54,19 @@ var DataTable = function ( selector, options )
54
54
  var defaults = DataTable.defaults;
55
55
  var $this = $(this);
56
56
 
57
-
58
- /* Sanity check */
57
+ // Sanity check
59
58
  if ( this.nodeName.toLowerCase() != 'table' )
60
59
  {
61
60
  _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 );
62
61
  return;
63
62
  }
64
63
 
65
- $(this).trigger( 'options.dt', oInit );
64
+ // Special case for options
65
+ if (oInit.on && oInit.on.options) {
66
+ _fnListener($this, 'options', oInit.on.options);
67
+ }
68
+
69
+ $this.trigger( 'options.dt', oInit );
66
70
 
67
71
  /* Backwards compatibility for the defaults */
68
72
  _fnCompatOpts( defaults );
@@ -201,6 +205,9 @@ var DataTable = function ( selector, options )
201
205
  "caption",
202
206
  "layout",
203
207
  "orderDescReverse",
208
+ "orderIndicators",
209
+ "orderHandler",
210
+ "titleRow",
204
211
  "typeDetect",
205
212
  [ "iCookieDuration", "iStateDuration" ], // backwards compat
206
213
  [ "oSearch", "oPreviousSearch" ],
@@ -229,6 +236,13 @@ var DataTable = function ( selector, options )
229
236
 
230
237
  oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId );
231
238
 
239
+ // Add event listeners
240
+ if (oInit.on) {
241
+ Object.keys(oInit.on).forEach(function (key) {
242
+ _fnListener($this, key, oInit.on[key]);
243
+ });
244
+ }
245
+
232
246
  /* Browser support detection */
233
247
  _fnBrowserDetect( oSettings );
234
248
 
@@ -289,7 +303,7 @@ var DataTable = function ( selector, options )
289
303
  /* HTML5 attribute detection - build an mData object automatically if the
290
304
  * attributes are found
291
305
  */
292
- var rowOne = $this.children('tbody').find('tr').eq(0);
306
+ var rowOne = $this.children('tbody').find('tr:first-child').eq(0);
293
307
 
294
308
  if ( rowOne.length ) {
295
309
  var a = function ( cell, name ) {
@@ -447,6 +461,13 @@ var DataTable = function ( selector, options )
447
461
  * @namespace
448
462
  */
449
463
  DataTable.ext = _ext = {
464
+ /**
465
+ * DataTables build type (expanded by the download builder)
466
+ *
467
+ * @type string
468
+ */
469
+ builder: "-source-",
470
+
450
471
  /**
451
472
  * Buttons. For use with the Buttons extension for DataTables. This is
452
473
  * defined here so other extensions can define buttons regardless of load
@@ -459,20 +480,20 @@ DataTable.ext = _ext = {
459
480
 
460
481
 
461
482
  /**
462
- * Element class names
483
+ * ColumnControl buttons and content
463
484
  *
464
485
  * @type object
465
- * @default {}
466
486
  */
467
- classes: {},
487
+ ccContent: {},
468
488
 
469
489
 
470
490
  /**
471
- * DataTables build type (expanded by the download builder)
491
+ * Element class names
472
492
  *
473
- * @type string
493
+ * @type object
494
+ * @default {}
474
495
  */
475
- builder: "-source-",
496
+ classes: {},
476
497
 
477
498
 
478
499
  /**
@@ -1840,6 +1861,26 @@ function _fnCompatOpts ( init )
1840
1861
  init.scrollX = init.scrollX ? '100%' : '';
1841
1862
  }
1842
1863
 
1864
+ // Objects for ordering
1865
+ if ( typeof init.bSort === 'object' ) {
1866
+ init.orderIndicators = init.bSort.indicators !== undefined ? init.bSort.indicators : true;
1867
+ init.orderHandler = init.bSort.handler !== undefined ? init.bSort.handler : true;
1868
+ init.bSort = true;
1869
+ }
1870
+ else if (init.bSort === false) {
1871
+ init.orderIndicators = false;
1872
+ init.orderHandler = false;
1873
+ }
1874
+ else if (init.bSort === true) {
1875
+ init.orderIndicators = true;
1876
+ init.orderHandler = true;
1877
+ }
1878
+
1879
+ // Which cells are the title cells?
1880
+ if (typeof init.bSortCellsTop === 'boolean') {
1881
+ init.titleRow = init.bSortCellsTop;
1882
+ }
1883
+
1843
1884
  // Column search objects are in an array, so it needs to be converted
1844
1885
  // element by element
1845
1886
  var searchCols = init.aoSearchCols;
@@ -3217,7 +3258,7 @@ function _fnBuildHead( settings, side )
3217
3258
  * @param {*} settings DataTables settings
3218
3259
  * @param {*} source Source layout array
3219
3260
  * @param {*} incColumns What columns should be included
3220
- * @returns Layout array
3261
+ * @returns Layout array in column index order
3221
3262
  */
3222
3263
  function _fnHeaderLayout( settings, source, incColumns )
3223
3264
  {
@@ -3501,7 +3542,9 @@ function _fnReDraw( settings, holdPosition, recompute )
3501
3542
 
3502
3543
  _fnDraw( settings );
3503
3544
 
3504
- settings._drawHold = false;
3545
+ settings.api.one('draw', function () {
3546
+ settings._drawHold = false;
3547
+ });
3505
3548
  }
3506
3549
 
3507
3550
 
@@ -3513,10 +3556,9 @@ function _emptyRow ( settings ) {
3513
3556
  var zero = oLang.sZeroRecords;
3514
3557
  var dataSrc = _fnDataSource( settings );
3515
3558
 
3516
- if (
3517
- (settings.iDraw < 1 && dataSrc === 'ssp') ||
3518
- (settings.iDraw <= 1 && dataSrc === 'ajax')
3519
- ) {
3559
+ // Make use of the fact that settings.json is only set once the initial data has
3560
+ // been loaded. Show loading when that isn't the case
3561
+ if ((dataSrc === 'ssp' || dataSrc === 'ajax') && ! settings.json) {
3520
3562
  zero = oLang.sLoadingRecords;
3521
3563
  }
3522
3564
  else if ( oLang.sEmptyTable && settings.fnRecordsTotal() === 0 )
@@ -3886,6 +3928,7 @@ function _fnDetectHeader ( settings, thead, write )
3886
3928
  var rows = $(thead).children('tr');
3887
3929
  var row, cell;
3888
3930
  var i, k, l, iLen, shifted, column, colspan, rowspan;
3931
+ var titleRow = settings.titleRow;
3889
3932
  var isHeader = thead && thead.nodeName.toLowerCase() === 'thead';
3890
3933
  var layout = [];
3891
3934
  var unique;
@@ -3914,6 +3957,7 @@ function _fnDetectHeader ( settings, thead, write )
3914
3957
  cell.nodeName.toUpperCase() == 'TH'
3915
3958
  ) {
3916
3959
  var cols = [];
3960
+ var jqCell = $(cell);
3917
3961
 
3918
3962
  // Get the col and rowspan attributes from the DOM and sanitise them
3919
3963
  colspan = cell.getAttribute('colspan') * 1;
@@ -3934,7 +3978,7 @@ function _fnDetectHeader ( settings, thead, write )
3934
3978
  if ( write ) {
3935
3979
  if (unique) {
3936
3980
  // Allow column options to be set from HTML attributes
3937
- _fnColumnOptions( settings, shifted, $(cell).data() );
3981
+ _fnColumnOptions( settings, shifted, jqCell.data() );
3938
3982
 
3939
3983
  // Get the width for the column. This can be defined from the
3940
3984
  // width attribute, style attribute or `columns.width` option
@@ -3951,7 +3995,14 @@ function _fnDetectHeader ( settings, thead, write )
3951
3995
  // Column title handling - can be user set, or read from the DOM
3952
3996
  // This happens before the render, so the original is still in place
3953
3997
  if ( columnDef.sTitle !== null && ! columnDef.autoTitle ) {
3954
- cell.innerHTML = columnDef.sTitle;
3998
+ if (
3999
+ (titleRow === true && i === 0) || // top row
4000
+ (titleRow === false && i === rows.length -1) || // bottom row
4001
+ (titleRow === i) || // specific row
4002
+ (titleRow === null)
4003
+ ) {
4004
+ cell.innerHTML = columnDef.sTitle;
4005
+ }
3955
4006
  }
3956
4007
 
3957
4008
  if (! columnDef.sTitle && unique) {
@@ -3969,12 +4020,12 @@ function _fnDetectHeader ( settings, thead, write )
3969
4020
  // Fall back to the aria-label attribute on the table header if no ariaTitle is
3970
4021
  // provided.
3971
4022
  if (! columnDef.ariaTitle) {
3972
- columnDef.ariaTitle = $(cell).attr("aria-label") || columnDef.sTitle;
4023
+ columnDef.ariaTitle = jqCell.attr("aria-label") || columnDef.sTitle;
3973
4024
  }
3974
4025
 
3975
4026
  // Column specific class names
3976
4027
  if ( columnDef.className ) {
3977
- $(cell).addClass( columnDef.className );
4028
+ jqCell.addClass( columnDef.className );
3978
4029
  }
3979
4030
  }
3980
4031
 
@@ -3986,11 +4037,28 @@ function _fnDetectHeader ( settings, thead, write )
3986
4037
  .appendTo(cell);
3987
4038
  }
3988
4039
 
3989
- if ( isHeader && $('span.dt-column-order', cell).length === 0) {
4040
+ if (
4041
+ settings.orderIndicators &&
4042
+ isHeader &&
4043
+ jqCell.filter(':not([data-dt-order=disable])').length !== 0 &&
4044
+ jqCell.parent(':not([data-dt-order=disable])').length !== 0 &&
4045
+ $('span.dt-column-order', cell).length === 0
4046
+ ) {
3990
4047
  $('<span>')
3991
4048
  .addClass('dt-column-order')
3992
4049
  .appendTo(cell);
3993
4050
  }
4051
+
4052
+ // We need to wrap the elements in the header in another element to use flexbox
4053
+ // layout for those elements
4054
+ var headerFooter = isHeader ? 'header' : 'footer';
4055
+
4056
+ if ( $('span.dt-column-' + headerFooter, cell).length === 0) {
4057
+ $('<div>')
4058
+ .addClass('dt-column-' + headerFooter)
4059
+ .append(cell.childNodes)
4060
+ .appendTo(cell);
4061
+ }
3994
4062
  }
3995
4063
 
3996
4064
  // If there is col / rowspan, copy the information into the layout grid
@@ -4141,6 +4209,11 @@ function _fnBuildAjax( oSettings, data, fn )
4141
4209
  // Allow plug-ins and external processes to modify the data
4142
4210
  _fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data, baseAjax], true );
4143
4211
 
4212
+ // Custom Ajax option to submit the parameters as a JSON string
4213
+ if (baseAjax.submitAs === 'json' && typeof data === 'object') {
4214
+ baseAjax.data = JSON.stringify(data);
4215
+ }
4216
+
4144
4217
  if ( typeof ajax === 'function' )
4145
4218
  {
4146
4219
  // Is a function - let the caller define what needs to be done
@@ -5641,24 +5714,30 @@ function _colGroup( settings ) {
5641
5714
  function _fnSortInit( settings ) {
5642
5715
  var target = settings.nTHead;
5643
5716
  var headerRows = target.querySelectorAll('tr');
5644
- var legacyTop = settings.bSortCellsTop;
5717
+ var titleRow = settings.titleRow;
5645
5718
  var notSelector = ':not([data-dt-order="disable"]):not([data-dt-order="icon-only"])';
5646
5719
 
5647
5720
  // Legacy support for `orderCellsTop`
5648
- if (legacyTop === true) {
5721
+ if (titleRow === true) {
5649
5722
  target = headerRows[0];
5650
5723
  }
5651
- else if (legacyTop === false) {
5724
+ else if (titleRow === false) {
5652
5725
  target = headerRows[ headerRows.length - 1 ];
5653
5726
  }
5727
+ else if (titleRow !== null) {
5728
+ target = headerRows[titleRow];
5729
+ }
5730
+ // else - all rows
5654
5731
 
5655
- _fnSortAttachListener(
5656
- settings,
5657
- target,
5658
- target === settings.nTHead
5659
- ? 'tr'+notSelector+' th'+notSelector+', tr'+notSelector+' td'+notSelector
5660
- : 'th'+notSelector+', td'+notSelector
5661
- );
5732
+ if (settings.orderHandler) {
5733
+ _fnSortAttachListener(
5734
+ settings,
5735
+ target,
5736
+ target === settings.nTHead
5737
+ ? 'tr'+notSelector+' th'+notSelector+', tr'+notSelector+' td'+notSelector
5738
+ : 'th'+notSelector+', td'+notSelector
5739
+ );
5740
+ }
5662
5741
 
5663
5742
  // Need to resolve the user input array into our internal structure
5664
5743
  var order = [];
@@ -5673,7 +5752,9 @@ function _fnSortAttachListener(settings, node, selector, column, callback) {
5673
5752
  var run = false;
5674
5753
  var columns = column === undefined
5675
5754
  ? _fnColumnsFromHeader( e.target )
5676
- : [column];
5755
+ : Array.isArray(column)
5756
+ ? column
5757
+ : [column];
5677
5758
 
5678
5759
  if ( columns.length ) {
5679
5760
  for ( var i=0, ien=columns.length ; i<ien ; i++ ) {
@@ -6296,16 +6377,19 @@ function _fnImplementState ( settings, s, callback) {
6296
6377
 
6297
6378
  // A column name was stored and should be used for restore
6298
6379
  if (typeof col[0] === 'string') {
6380
+ // Find the name from the current list of column names
6299
6381
  var idx = currentNames.indexOf(col[0]);
6300
6382
 
6301
- // Find the name from the current list of column names, or fallback to index 0
6302
- set[0] = idx >= 0
6303
- ? idx
6304
- : 0;
6383
+ if (idx < 0) {
6384
+ // If the column was not found ignore it and continue
6385
+ return;
6386
+ }
6387
+
6388
+ set[0] = idx;
6305
6389
  }
6306
6390
  else if (set[0] >= columns.length) {
6307
- // If a column name, but it is out of bounds, set to 0
6308
- set[0] = 0;
6391
+ // If the column index is out of bounds ignore it and continue
6392
+ return;
6309
6393
  }
6310
6394
 
6311
6395
  settings.aaSorting.push(set);
@@ -6718,6 +6802,23 @@ function _fnArrayApply(arr, data) {
6718
6802
  }
6719
6803
  }
6720
6804
 
6805
+ /**
6806
+ * Add one or more listeners to the table
6807
+ *
6808
+ * @param {*} that JQ for the table
6809
+ * @param {*} name Event name
6810
+ * @param {*} src Listener(s)
6811
+ */
6812
+ function _fnListener(that, name, src) {
6813
+ if (!Array.isArray(src)) {
6814
+ src = [src];
6815
+ }
6816
+
6817
+ for (i=0 ; i<src.length ; i++) {
6818
+ that.on(name + '.dt', src[i]);
6819
+ }
6820
+ }
6821
+
6721
6822
 
6722
6823
 
6723
6824
  /**
@@ -7374,12 +7475,24 @@ _api_register( 'table()', function ( selector ) {
7374
7475
  ['footer', 'aoFooter'],
7375
7476
  ].forEach(function (item) {
7376
7477
  _api_register( 'table().' + item[0] + '.structure()' , function (selector) {
7377
- var indexes = this.columns(selector).indexes().flatten();
7478
+ var indexes = this.columns(selector).indexes().flatten().toArray();
7378
7479
  var ctx = this.context[0];
7379
-
7380
- return _fnHeaderLayout(ctx, ctx[item[1]], indexes);
7381
- } );
7382
- })
7480
+ var structure = _fnHeaderLayout(ctx, ctx[item[1]], indexes);
7481
+
7482
+ // The structure is in column index order - but from this method we want the return to be
7483
+ // in the columns() selector API order. In order to do that we need to map from one form
7484
+ // to the other
7485
+ var orderedIndexes = indexes.slice().sort(function (a, b) {
7486
+ return a - b;
7487
+ });
7488
+
7489
+ return structure.map(function (row) {
7490
+ return indexes.map(function (colIdx) {
7491
+ return row[orderedIndexes.indexOf(colIdx)];
7492
+ });
7493
+ });
7494
+ });
7495
+ });
7383
7496
 
7384
7497
 
7385
7498
  _api_registerPlural( 'tables().containers()', 'table().container()' , function () {
@@ -7728,7 +7841,7 @@ var _selector_run = function ( type, selector, selectFn, settings, opts )
7728
7841
  {
7729
7842
  var
7730
7843
  out = [], res,
7731
- a, i, ien, j, jen,
7844
+ i, ien,
7732
7845
  selectorType = typeof selector;
7733
7846
 
7734
7847
  // Can't just check for isArray here, as an API or jQuery instance might be
@@ -7738,22 +7851,15 @@ var _selector_run = function ( type, selector, selectFn, settings, opts )
7738
7851
  }
7739
7852
 
7740
7853
  for ( i=0, ien=selector.length ; i<ien ; i++ ) {
7741
- // Only split on simple strings - complex expressions will be jQuery selectors
7742
- a = selector[i] && selector[i].split && ! selector[i].match(/[[(:]/) ?
7743
- selector[i].split(',') :
7744
- [ selector[i] ];
7745
-
7746
- for ( j=0, jen=a.length ; j<jen ; j++ ) {
7747
- res = selectFn( typeof a[j] === 'string' ? (a[j]).trim() : a[j] );
7854
+ res = selectFn( typeof selector[i] === 'string' ? selector[i].trim() : selector[i] );
7748
7855
 
7749
- // Remove empty items
7750
- res = res.filter( function (item) {
7751
- return item !== null && item !== undefined;
7752
- });
7856
+ // Remove empty items
7857
+ res = res.filter( function (item) {
7858
+ return item !== null && item !== undefined;
7859
+ });
7753
7860
 
7754
- if ( res && res.length ) {
7755
- out = out.concat( res );
7756
- }
7861
+ if ( res && res.length ) {
7862
+ out = out.concat( res );
7757
7863
  }
7758
7864
  }
7759
7865
 
@@ -7782,6 +7888,7 @@ var _selector_opts = function ( opts )
7782
7888
  }
7783
7889
 
7784
7890
  return $.extend( {
7891
+ columnOrder: 'implied',
7785
7892
  search: 'none',
7786
7893
  order: 'current',
7787
7894
  page: 'all'
@@ -8543,23 +8650,60 @@ var __columnData = function ( settings, column, r1, r2, rows, type ) {
8543
8650
 
8544
8651
  var __column_header = function ( settings, column, row ) {
8545
8652
  var header = settings.aoHeader;
8546
- var target = row !== undefined
8547
- ? row
8548
- : settings.bSortCellsTop // legacy support
8549
- ? 0
8550
- : header.length - 1;
8653
+ var titleRow = settings.titleRow;
8654
+ var target = null;
8655
+
8656
+ if (row !== undefined) {
8657
+ target = row;
8658
+ }
8659
+ else if (titleRow === true) { // legacy orderCellsTop support
8660
+ target = 0;
8661
+ }
8662
+ else if (titleRow === false) {
8663
+ target = header.length - 1;
8664
+ }
8665
+ else if (titleRow !== null) {
8666
+ target = titleRow;
8667
+ }
8668
+ else {
8669
+ // Automatic - find the _last_ unique cell from the top that is not empty (last for
8670
+ // backwards compatibility)
8671
+ for (var i=0 ; i<header.length ; i++) {
8672
+ if (header[i][column].unique && $('span.dt-column-title', header[i][column].cell).text()) {
8673
+ target = i;
8674
+ }
8675
+ }
8676
+
8677
+ if (target === null) {
8678
+ target = 0;
8679
+ }
8680
+ }
8551
8681
 
8552
8682
  return header[target][column].cell;
8553
8683
  };
8554
8684
 
8685
+ var __column_header_cells = function (header) {
8686
+ var out = [];
8687
+
8688
+ for (var i=0 ; i<header.length ; i++) {
8689
+ for (var j=0 ; j<header[i].length ; j++) {
8690
+ var cell = header[i][j].cell;
8691
+
8692
+ if (!out.includes(cell)) {
8693
+ out.push(cell);
8694
+ }
8695
+ }
8696
+ }
8697
+
8698
+ return out;
8699
+ }
8700
+
8555
8701
  var __column_selector = function ( settings, selector, opts )
8556
8702
  {
8557
8703
  var
8558
8704
  columns = settings.aoColumns,
8559
- names = _pluck( columns, 'sName' ),
8560
- titles = _pluck( columns, 'sTitle' ),
8561
- cells = DataTable.util.get('[].[].cell')(settings.aoHeader),
8562
- nodes = _unique( _flatten([], cells) );
8705
+ names, titles,
8706
+ nodes = __column_header_cells(settings.aoHeader);
8563
8707
 
8564
8708
  var run = function ( s ) {
8565
8709
  var selInt = _intVal( s );
@@ -8631,12 +8775,21 @@ var __column_selector = function ( settings, selector, opts )
8631
8775
  } );
8632
8776
 
8633
8777
  case 'name':
8778
+ // Don't get names, unless needed, and only get once if it is
8779
+ if (!names) {
8780
+ names = _pluck( columns, 'sName' );
8781
+ }
8782
+
8634
8783
  // match by name. `names` is column index complete and in order
8635
8784
  return names.map( function (name, i) {
8636
8785
  return name === match[1] ? i : null;
8637
8786
  } );
8638
8787
 
8639
8788
  case 'title':
8789
+ if (!titles) {
8790
+ titles = _pluck( columns, 'sTitle' );
8791
+ }
8792
+
8640
8793
  // match by column title
8641
8794
  return titles.map( function (title, i) {
8642
8795
  return title === match[1] ? i : null;
@@ -8675,7 +8828,11 @@ var __column_selector = function ( settings, selector, opts )
8675
8828
  [];
8676
8829
  };
8677
8830
 
8678
- return _selector_run( 'column', selector, run, settings, opts );
8831
+ var selected = _selector_run( 'column', selector, run, settings, opts );
8832
+
8833
+ return opts.columnOrder && opts.columnOrder === 'index'
8834
+ ? selected.sort(function (a, b) { return a - b; })
8835
+ : selected; // implied
8679
8836
  };
8680
8837
 
8681
8838
 
@@ -8799,6 +8956,12 @@ _api_registerPlural( 'columns().init()', 'column().init()', function () {
8799
8956
  }, 1 );
8800
8957
  } );
8801
8958
 
8959
+ _api_registerPlural( 'columns().names()', 'column().name()', function () {
8960
+ return this.iterator( 'column', function ( settings, column ) {
8961
+ return settings.aoColumns[column].sName;
8962
+ }, 1 );
8963
+ } );
8964
+
8802
8965
  _api_registerPlural( 'columns().nodes()', 'column().nodes()', function () {
8803
8966
  return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
8804
8967
  return _pluck_order( settings.aoData, rows, 'anCells', column ) ;
@@ -9225,7 +9388,10 @@ _api_register( 'order()', function ( order, dir ) {
9225
9388
  // otherwise a 2D array was passed in
9226
9389
 
9227
9390
  return this.iterator( 'table', function ( settings ) {
9228
- settings.aaSorting = Array.isArray(order) ? order.slice() : order;
9391
+ var resolved = [];
9392
+ _fnSortResolve(settings, resolved, order);
9393
+
9394
+ settings.aaSorting = resolved;
9229
9395
  } );
9230
9396
  } );
9231
9397
 
@@ -9351,7 +9517,7 @@ _api_register( 'search.fixed()', function ( name, search ) {
9351
9517
  var fixed = settings.searchFixed;
9352
9518
 
9353
9519
  if (! name) {
9354
- return Object.keys(fixed)
9520
+ return Object.keys(fixed);
9355
9521
  }
9356
9522
  else if (search === undefined) {
9357
9523
  return fixed[name];
@@ -9418,10 +9584,10 @@ _api_register([
9418
9584
  var fixed = settings.aoColumns[colIdx].searchFixed;
9419
9585
 
9420
9586
  if (! name) {
9421
- return Object.keys(fixed)
9587
+ return Object.keys(fixed);
9422
9588
  }
9423
9589
  else if (search === undefined) {
9424
- return fixed[name];
9590
+ return fixed[name] || null;
9425
9591
  }
9426
9592
  else if (search === null) {
9427
9593
  delete fixed[name];
@@ -9873,14 +10039,9 @@ _api_register( 'destroy()', function ( remove ) {
9873
10039
  jqTable.append( tfoot );
9874
10040
  }
9875
10041
 
9876
- // Clean up the header
9877
- $(thead).find('span.dt-column-order').remove();
9878
- $(thead).find('span.dt-column-title').each(function () {
9879
- var title = $(this).html();
9880
- $(this).parent().append(title);
9881
- $(this).remove();
9882
- });
9883
-
10042
+ // Clean up the header / footer
10043
+ cleanHeader(thead, 'header');
10044
+ cleanHeader(tfoot, 'footer');
9884
10045
  settings.colgroup.remove();
9885
10046
 
9886
10047
  settings.aaSorting = [];
@@ -9902,7 +10063,6 @@ _api_register( 'destroy()', function ( remove ) {
9902
10063
  orderClasses.isDesc
9903
10064
  )
9904
10065
  .css('width', '')
9905
- .removeAttr('data-dt-column')
9906
10066
  .removeAttr('aria-sort');
9907
10067
 
9908
10068
  // Add the TR elements back into the table in their original order
@@ -9983,6 +10143,19 @@ _api_register( 'i18n()', function ( token, def, plural ) {
9983
10143
  : resolved;
9984
10144
  } );
9985
10145
 
10146
+ // Needed for header and footer, so pulled into its own function
10147
+ function cleanHeader(node, className) {
10148
+ $(node).find('span.dt-column-order').remove();
10149
+ $(node).find('span.dt-column-title').each(function () {
10150
+ var title = $(this).html();
10151
+ $(this).parent().parent().append(title);
10152
+ $(this).remove();
10153
+ });
10154
+ $(node).find('div.dt-column-' + className).remove();
10155
+
10156
+ $('th, td', node).removeAttr('data-dt-column');
10157
+ }
10158
+
9986
10159
  /**
9987
10160
  * Version string for plug-ins to check compatibility. Allowed format is
9988
10161
  * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used
@@ -9991,7 +10164,7 @@ _api_register( 'i18n()', function ( token, def, plural ) {
9991
10164
  * @type string
9992
10165
  * @default Version number
9993
10166
  */
9994
- DataTable.version = "2.2.2";
10167
+ DataTable.version = "2.3.1";
9995
10168
 
9996
10169
  /**
9997
10170
  * Private data store, containing all of the settings objects that are
@@ -10598,6 +10771,10 @@ DataTable.defaults = {
10598
10771
  "bSortCellsTop": null,
10599
10772
 
10600
10773
 
10774
+ /** Specify which row is the title row in the header. Replacement for bSortCellsTop */
10775
+ titleRow: null,
10776
+
10777
+
10601
10778
  /**
10602
10779
  * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and
10603
10780
  * `sorting\_3` to the columns which are currently being sorted on. This is
@@ -10875,6 +11052,13 @@ DataTable.defaults = {
10875
11052
  1: "entry"
10876
11053
  },
10877
11054
 
11055
+ /**
11056
+ * Page length options
11057
+ */
11058
+ lengthLabels: {
11059
+ '-1': 'All'
11060
+ },
11061
+
10878
11062
  /**
10879
11063
  * This string is shown in preference to `zeroRecords` when the table is
10880
11064
  * empty of data (regardless of filtering). Note that this is an optional
@@ -11145,7 +11329,10 @@ DataTable.defaults = {
11145
11329
  /**
11146
11330
  * For server-side processing - use the data from the DOM for the first draw
11147
11331
  */
11148
- iDeferLoading: null
11332
+ iDeferLoading: null,
11333
+
11334
+ /** Event listeners */
11335
+ on: null
11149
11336
  };
11150
11337
 
11151
11338
  _fnHungarianMap( DataTable.defaults );
@@ -11972,10 +12159,7 @@ DataTable.models.oSettings = {
11972
12159
 
11973
12160
  /**
11974
12161
  * Indicate that if multiple rows are in the header and there is more than
11975
- * one unique cell per column, if the top one (true) or bottom one (false)
11976
- * should be used for sorting / title by DataTables.
11977
- * Note that this parameter will be set by the initialisation routine. To
11978
- * set a default use {@link DataTable.defaults}.
12162
+ * one unique cell per column. Replaced by titleRow
11979
12163
  */
11980
12164
  "bSortCellsTop": null,
11981
12165
 
@@ -12100,7 +12284,19 @@ DataTable.models.oSettings = {
12100
12284
  resizeObserver: null,
12101
12285
 
12102
12286
  /** Keep a record of the last size of the container, so we can skip duplicates */
12103
- containerWidth: -1
12287
+ containerWidth: -1,
12288
+
12289
+ /** Reverse the initial order of the data set on desc ordering */
12290
+ orderDescReverse: null,
12291
+
12292
+ /** Show / hide ordering indicators in headers */
12293
+ orderIndicators: true,
12294
+
12295
+ /** Default ordering listener */
12296
+ orderHandler: true,
12297
+
12298
+ /** Title row indicator */
12299
+ titleRow: null
12104
12300
  };
12105
12301
 
12106
12302
  /**
@@ -12930,7 +13126,7 @@ $.extend( true, DataTable.ext.renderer, {
12930
13126
  cell.addClass(classes.order.none);
12931
13127
  }
12932
13128
 
12933
- var legacyTop = settings.bSortCellsTop;
13129
+ var titleRow = settings.titleRow;
12934
13130
  var headerRows = cell.closest('thead').find('tr');
12935
13131
  var rowIdx = cell.parent().index();
12936
13132
 
@@ -12940,11 +13136,10 @@ $.extend( true, DataTable.ext.renderer, {
12940
13136
  cell.attr('data-dt-order') === 'disable' ||
12941
13137
  cell.parent().attr('data-dt-order') === 'disable' ||
12942
13138
 
12943
- // Legacy support for `orderCellsTop`. If it is set, then cells
12944
- // which are not in the top or bottom row of the header (depending
12945
- // on the value) do not get the sorting classes applied to them
12946
- (legacyTop === true && rowIdx !== 0) ||
12947
- (legacyTop === false && rowIdx !== headerRows.length - 1)
13139
+ // titleRow support, for defining a specific row in the header
13140
+ (titleRow === true && rowIdx !== 0) ||
13141
+ (titleRow === false && rowIdx !== headerRows.length - 1) ||
13142
+ (typeof titleRow === 'number' && rowIdx !== titleRow)
12948
13143
  ) {
12949
13144
  return;
12950
13145
  }
@@ -12954,7 +13149,7 @@ $.extend( true, DataTable.ext.renderer, {
12954
13149
  // `DT` namespace will allow the event to be removed automatically
12955
13150
  // on destroy, while the `dt` namespaced event is the one we are
12956
13151
  // listening for
12957
- $(settings.nTable).on( 'order.dt.DT column-visibility.dt.DT', function ( e, ctx ) {
13152
+ $(settings.nTable).on( 'order.dt.DT column-visibility.dt.DT', function ( e, ctx, column ) {
12958
13153
  if ( settings !== ctx ) { // need to check this this is the host
12959
13154
  return; // table, not a nested one
12960
13155
  }
@@ -12965,6 +13160,16 @@ $.extend( true, DataTable.ext.renderer, {
12965
13160
  return;
12966
13161
  }
12967
13162
 
13163
+ var orderedColumns = _pluck(sorting, 'col');
13164
+
13165
+ // This handler is only needed on column visibility if the column is part of the
13166
+ // ordering. If it isn't, then we can bail out to save performance. It could be a
13167
+ // separate event handler, but this is a balance between code reuse / size and performance
13168
+ // console.log(e, e.name, column, orderedColumns, orderedColumns.includes(column))
13169
+ if (e.type === 'column-visibility' && ! orderedColumns.includes(column)) {
13170
+ return;
13171
+ }
13172
+
12968
13173
  var i;
12969
13174
  var orderClasses = classes.order;
12970
13175
  var columns = ctx.api.columns( cell );
@@ -12973,8 +13178,8 @@ $.extend( true, DataTable.ext.renderer, {
12973
13178
  var ariaType = '';
12974
13179
  var indexes = columns.indexes();
12975
13180
  var sortDirs = columns.orderable(true).flatten();
12976
- var orderedColumns = _pluck(sorting, 'col');
12977
13181
  var tabIndex = settings.iTabIndex;
13182
+ var canOrder = ctx.orderHandler && orderable;
12978
13183
 
12979
13184
  cell
12980
13185
  .removeClass(
@@ -12982,8 +13187,8 @@ $.extend( true, DataTable.ext.renderer, {
12982
13187
  orderClasses.isDesc
12983
13188
  )
12984
13189
  .toggleClass( orderClasses.none, ! orderable )
12985
- .toggleClass( orderClasses.canAsc, orderable && sortDirs.includes('asc') )
12986
- .toggleClass( orderClasses.canDesc, orderable && sortDirs.includes('desc') );
13190
+ .toggleClass( orderClasses.canAsc, canOrder && sortDirs.includes('asc') )
13191
+ .toggleClass( orderClasses.canDesc, canOrder && sortDirs.includes('desc') );
12987
13192
 
12988
13193
  // Determine if all of the columns that this cell covers are included in the
12989
13194
  // current ordering
@@ -13742,12 +13947,17 @@ DataTable.feature.register( 'pageLength', function ( settings, opts ) {
13742
13947
  } );
13743
13948
 
13744
13949
  for ( i=0 ; i<lengths.length ; i++ ) {
13745
- select[0][ i ] = new Option(
13746
- typeof language[i] === 'number' ?
13950
+ // Attempt to look up the length from the i18n options
13951
+ var label = settings.api.i18n('lengthLabels.' + lengths[i], null);
13952
+
13953
+ if (label === null) {
13954
+ // If not present, fallback to old style
13955
+ label = typeof language[i] === 'number' ?
13747
13956
  settings.fnFormatNumber( language[i] ) :
13748
- language[i],
13749
- lengths[i]
13750
- );
13957
+ language[i];
13958
+ }
13959
+
13960
+ select[0][ i ] = new Option(label, lengths[i]);
13751
13961
  }
13752
13962
 
13753
13963
  // add for and id to label and input