datatables.net 2.2.1 → 2.3.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,4 +1,4 @@
1
- /*! DataTables 2.2.1
1
+ /*! DataTables 2.3.0
2
2
  * © SpryMedia Ltd - datatables.net/license
3
3
  */
4
4
 
@@ -89,15 +89,19 @@
89
89
  var defaults = DataTable.defaults;
90
90
  var $this = $(this);
91
91
 
92
-
93
- /* Sanity check */
92
+ // Sanity check
94
93
  if ( this.nodeName.toLowerCase() != 'table' )
95
94
  {
96
95
  _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 );
97
96
  return;
98
97
  }
99
98
 
100
- $(this).trigger( 'options.dt', oInit );
99
+ // Special case for options
100
+ if (oInit.on && oInit.on.options) {
101
+ _fnListener($this, 'options', oInit.on.options);
102
+ }
103
+
104
+ $this.trigger( 'options.dt', oInit );
101
105
 
102
106
  /* Backwards compatibility for the defaults */
103
107
  _fnCompatOpts( defaults );
@@ -236,6 +240,9 @@
236
240
  "caption",
237
241
  "layout",
238
242
  "orderDescReverse",
243
+ "orderIndicators",
244
+ "orderHandler",
245
+ "titleRow",
239
246
  "typeDetect",
240
247
  [ "iCookieDuration", "iStateDuration" ], // backwards compat
241
248
  [ "oSearch", "oPreviousSearch" ],
@@ -264,6 +271,13 @@
264
271
 
265
272
  oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId );
266
273
 
274
+ // Add event listeners
275
+ if (oInit.on) {
276
+ Object.keys(oInit.on).forEach(function (key) {
277
+ _fnListener($this, key, oInit.on[key]);
278
+ });
279
+ }
280
+
267
281
  /* Browser support detection */
268
282
  _fnBrowserDetect( oSettings );
269
283
 
@@ -324,7 +338,7 @@
324
338
  /* HTML5 attribute detection - build an mData object automatically if the
325
339
  * attributes are found
326
340
  */
327
- var rowOne = $this.children('tbody').find('tr').eq(0);
341
+ var rowOne = $this.children('tbody').find('tr:first-child').eq(0);
328
342
 
329
343
  if ( rowOne.length ) {
330
344
  var a = function ( cell, name ) {
@@ -482,6 +496,13 @@
482
496
  * @namespace
483
497
  */
484
498
  DataTable.ext = _ext = {
499
+ /**
500
+ * DataTables build type (expanded by the download builder)
501
+ *
502
+ * @type string
503
+ */
504
+ builder: "-source-",
505
+
485
506
  /**
486
507
  * Buttons. For use with the Buttons extension for DataTables. This is
487
508
  * defined here so other extensions can define buttons regardless of load
@@ -494,20 +515,20 @@
494
515
 
495
516
 
496
517
  /**
497
- * Element class names
518
+ * ColumnControl buttons and content
498
519
  *
499
520
  * @type object
500
- * @default {}
501
521
  */
502
- classes: {},
522
+ ccContent: {},
503
523
 
504
524
 
505
525
  /**
506
- * DataTables build type (expanded by the download builder)
526
+ * Element class names
507
527
  *
508
- * @type string
528
+ * @type object
529
+ * @default {}
509
530
  */
510
- builder: "-source-",
531
+ classes: {},
511
532
 
512
533
 
513
534
  /**
@@ -1875,6 +1896,22 @@
1875
1896
  init.scrollX = init.scrollX ? '100%' : '';
1876
1897
  }
1877
1898
 
1899
+ // Objects for ordering
1900
+ if ( typeof init.bSort === 'object' ) {
1901
+ init.orderIndicators = init.bSort.indicators !== undefined ? init.bSort.indicators : true;
1902
+ init.orderHandler = init.bSort.handler !== undefined ? init.bSort.handler : true;
1903
+ init.bSort = true;
1904
+ }
1905
+ else if (init.bSort === false) {
1906
+ init.orderIndicators = false;
1907
+ init.orderHandler = false;
1908
+ }
1909
+
1910
+ // Which cells are the title cells?
1911
+ if (typeof init.bSortCellsTop === 'boolean') {
1912
+ init.titleRow = init.bSortCellsTop;
1913
+ }
1914
+
1878
1915
  // Column search objects are in an array, so it needs to be converted
1879
1916
  // element by element
1880
1917
  var searchCols = init.aoSearchCols;
@@ -3252,7 +3289,7 @@
3252
3289
  * @param {*} settings DataTables settings
3253
3290
  * @param {*} source Source layout array
3254
3291
  * @param {*} incColumns What columns should be included
3255
- * @returns Layout array
3292
+ * @returns Layout array in column index order
3256
3293
  */
3257
3294
  function _fnHeaderLayout( settings, source, incColumns )
3258
3295
  {
@@ -3548,10 +3585,9 @@
3548
3585
  var zero = oLang.sZeroRecords;
3549
3586
  var dataSrc = _fnDataSource( settings );
3550
3587
 
3551
- if (
3552
- (settings.iDraw < 1 && dataSrc === 'ssp') ||
3553
- (settings.iDraw <= 1 && dataSrc === 'ajax')
3554
- ) {
3588
+ // Make use of the fact that settings.json is only set once the initial data has
3589
+ // been loaded. Show loading when that isn't the case
3590
+ if ((dataSrc === 'ssp' || dataSrc === 'ajax') && ! settings.json) {
3555
3591
  zero = oLang.sLoadingRecords;
3556
3592
  }
3557
3593
  else if ( oLang.sEmptyTable && settings.fnRecordsTotal() === 0 )
@@ -3921,6 +3957,7 @@
3921
3957
  var rows = $(thead).children('tr');
3922
3958
  var row, cell;
3923
3959
  var i, k, l, iLen, shifted, column, colspan, rowspan;
3960
+ var titleRow = settings.titleRow;
3924
3961
  var isHeader = thead && thead.nodeName.toLowerCase() === 'thead';
3925
3962
  var layout = [];
3926
3963
  var unique;
@@ -3949,6 +3986,7 @@
3949
3986
  cell.nodeName.toUpperCase() == 'TH'
3950
3987
  ) {
3951
3988
  var cols = [];
3989
+ var jqCell = $(cell);
3952
3990
 
3953
3991
  // Get the col and rowspan attributes from the DOM and sanitise them
3954
3992
  colspan = cell.getAttribute('colspan') * 1;
@@ -3969,7 +4007,7 @@
3969
4007
  if ( write ) {
3970
4008
  if (unique) {
3971
4009
  // Allow column options to be set from HTML attributes
3972
- _fnColumnOptions( settings, shifted, $(cell).data() );
4010
+ _fnColumnOptions( settings, shifted, jqCell.data() );
3973
4011
 
3974
4012
  // Get the width for the column. This can be defined from the
3975
4013
  // width attribute, style attribute or `columns.width` option
@@ -3986,7 +4024,14 @@
3986
4024
  // Column title handling - can be user set, or read from the DOM
3987
4025
  // This happens before the render, so the original is still in place
3988
4026
  if ( columnDef.sTitle !== null && ! columnDef.autoTitle ) {
3989
- cell.innerHTML = columnDef.sTitle;
4027
+ if (
4028
+ (titleRow === true && i === 0) || // top row
4029
+ (titleRow === false && i === rows.length -1) || // bottom row
4030
+ (titleRow === i) || // specific row
4031
+ (titleRow === null)
4032
+ ) {
4033
+ cell.innerHTML = columnDef.sTitle;
4034
+ }
3990
4035
  }
3991
4036
 
3992
4037
  if (! columnDef.sTitle && unique) {
@@ -4004,12 +4049,12 @@
4004
4049
  // Fall back to the aria-label attribute on the table header if no ariaTitle is
4005
4050
  // provided.
4006
4051
  if (! columnDef.ariaTitle) {
4007
- columnDef.ariaTitle = $(cell).attr("aria-label") || columnDef.sTitle;
4052
+ columnDef.ariaTitle = jqCell.attr("aria-label") || columnDef.sTitle;
4008
4053
  }
4009
4054
 
4010
4055
  // Column specific class names
4011
4056
  if ( columnDef.className ) {
4012
- $(cell).addClass( columnDef.className );
4057
+ jqCell.addClass( columnDef.className );
4013
4058
  }
4014
4059
  }
4015
4060
 
@@ -4021,11 +4066,28 @@
4021
4066
  .appendTo(cell);
4022
4067
  }
4023
4068
 
4024
- if ( isHeader && $('span.dt-column-order', cell).length === 0) {
4069
+ if (
4070
+ settings.orderIndicators &&
4071
+ isHeader &&
4072
+ jqCell.filter(':not([data-dt-order=disable])').length !== 0 &&
4073
+ jqCell.parent(':not([data-dt-order=disable])').length !== 0 &&
4074
+ $('span.dt-column-order', cell).length === 0
4075
+ ) {
4025
4076
  $('<span>')
4026
4077
  .addClass('dt-column-order')
4027
4078
  .appendTo(cell);
4028
4079
  }
4080
+
4081
+ // We need to wrap the elements in the header in another element to use flexbox
4082
+ // layout for those elements
4083
+ var headerFooter = isHeader ? 'header' : 'footer';
4084
+
4085
+ if ( $('span.dt-column-' + headerFooter, cell).length === 0) {
4086
+ $('<div>')
4087
+ .addClass('dt-column-' + headerFooter)
4088
+ .append(cell.childNodes)
4089
+ .appendTo(cell);
4090
+ }
4029
4091
  }
4030
4092
 
4031
4093
  // If there is col / rowspan, copy the information into the layout grid
@@ -4176,6 +4238,11 @@
4176
4238
  // Allow plug-ins and external processes to modify the data
4177
4239
  _fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data, baseAjax], true );
4178
4240
 
4241
+ // Custom Ajax option to submit the parameters as a JSON string
4242
+ if (baseAjax.submitAs === 'json' && typeof data === 'object') {
4243
+ baseAjax.data = JSON.stringify(data);
4244
+ }
4245
+
4179
4246
  if ( typeof ajax === 'function' )
4180
4247
  {
4181
4248
  // Is a function - let the caller define what needs to be done
@@ -5544,6 +5611,15 @@
5544
5611
  // This flag allows the above to be satisfied.
5545
5612
  var first = $(settings.nTableWrapper).is(':visible');
5546
5613
 
5614
+ // Use an empty div to attach the observer so it isn't impacted by height changes
5615
+ var resizer = $('<div>')
5616
+ .css({
5617
+ width: '100%',
5618
+ height: 0
5619
+ })
5620
+ .addClass('dt-autosize')
5621
+ .appendTo(settings.nTableWrapper);
5622
+
5547
5623
  settings.resizeObserver = new ResizeObserver(function (e) {
5548
5624
  if (first) {
5549
5625
  first = false;
@@ -5553,7 +5629,7 @@
5553
5629
  }
5554
5630
  });
5555
5631
 
5556
- settings.resizeObserver.observe(settings.nTableWrapper);
5632
+ settings.resizeObserver.observe(resizer[0]);
5557
5633
  }
5558
5634
  else {
5559
5635
  // For old browsers, the best we can do is listen for a window resize
@@ -5667,24 +5743,30 @@
5667
5743
  function _fnSortInit( settings ) {
5668
5744
  var target = settings.nTHead;
5669
5745
  var headerRows = target.querySelectorAll('tr');
5670
- var legacyTop = settings.bSortCellsTop;
5746
+ var titleRow = settings.titleRow;
5671
5747
  var notSelector = ':not([data-dt-order="disable"]):not([data-dt-order="icon-only"])';
5672
5748
 
5673
5749
  // Legacy support for `orderCellsTop`
5674
- if (legacyTop === true) {
5750
+ if (titleRow === true) {
5675
5751
  target = headerRows[0];
5676
5752
  }
5677
- else if (legacyTop === false) {
5753
+ else if (titleRow === false) {
5678
5754
  target = headerRows[ headerRows.length - 1 ];
5679
5755
  }
5756
+ else if (titleRow !== null) {
5757
+ target = headerRows[titleRow];
5758
+ }
5759
+ // else - all rows
5680
5760
 
5681
- _fnSortAttachListener(
5682
- settings,
5683
- target,
5684
- target === settings.nTHead
5685
- ? 'tr'+notSelector+' th'+notSelector+', tr'+notSelector+' td'+notSelector
5686
- : 'th'+notSelector+', td'+notSelector
5687
- );
5761
+ if (settings.orderHandler) {
5762
+ _fnSortAttachListener(
5763
+ settings,
5764
+ target,
5765
+ target === settings.nTHead
5766
+ ? 'tr'+notSelector+' th'+notSelector+', tr'+notSelector+' td'+notSelector
5767
+ : 'th'+notSelector+', td'+notSelector
5768
+ );
5769
+ }
5688
5770
 
5689
5771
  // Need to resolve the user input array into our internal structure
5690
5772
  var order = [];
@@ -5699,7 +5781,9 @@
5699
5781
  var run = false;
5700
5782
  var columns = column === undefined
5701
5783
  ? _fnColumnsFromHeader( e.target )
5702
- : [column];
5784
+ : Array.isArray(column)
5785
+ ? column
5786
+ : [column];
5703
5787
 
5704
5788
  if ( columns.length ) {
5705
5789
  for ( var i=0, ien=columns.length ; i<ien ; i++ ) {
@@ -5885,10 +5969,14 @@
5885
5969
  displayMaster = oSettings.aiDisplayMaster,
5886
5970
  aSort;
5887
5971
 
5972
+ // Make sure the columns all have types defined
5973
+ _fnColumnTypes(oSettings);
5974
+
5888
5975
  // Allow a specific column to be sorted, which will _not_ alter the display
5889
5976
  // master
5890
5977
  if (col !== undefined) {
5891
5978
  var srcCol = oSettings.aoColumns[col];
5979
+
5892
5980
  aSort = [{
5893
5981
  src: col,
5894
5982
  col: col,
@@ -6318,16 +6406,19 @@
6318
6406
 
6319
6407
  // A column name was stored and should be used for restore
6320
6408
  if (typeof col[0] === 'string') {
6409
+ // Find the name from the current list of column names
6321
6410
  var idx = currentNames.indexOf(col[0]);
6322
6411
 
6323
- // Find the name from the current list of column names, or fallback to index 0
6324
- set[0] = idx >= 0
6325
- ? idx
6326
- : 0;
6412
+ if (idx < 0) {
6413
+ // If the column was not found ignore it and continue
6414
+ return;
6415
+ }
6416
+
6417
+ set[0] = idx;
6327
6418
  }
6328
6419
  else if (set[0] >= columns.length) {
6329
- // If a column name, but it is out of bounds, set to 0
6330
- set[0] = 0;
6420
+ // If the column index is out of bounds ignore it and continue
6421
+ return;
6331
6422
  }
6332
6423
 
6333
6424
  settings.aaSorting.push(set);
@@ -6740,6 +6831,23 @@
6740
6831
  }
6741
6832
  }
6742
6833
 
6834
+ /**
6835
+ * Add one or more listeners to the table
6836
+ *
6837
+ * @param {*} that JQ for the table
6838
+ * @param {*} name Event name
6839
+ * @param {*} src Listener(s)
6840
+ */
6841
+ function _fnListener(that, name, src) {
6842
+ if (!Array.isArray(src)) {
6843
+ src = [src];
6844
+ }
6845
+
6846
+ for (i=0 ; i<src.length ; i++) {
6847
+ that.on(name + '.dt', src[i]);
6848
+ }
6849
+ }
6850
+
6743
6851
 
6744
6852
 
6745
6853
  /**
@@ -7396,12 +7504,24 @@
7396
7504
  ['footer', 'aoFooter'],
7397
7505
  ].forEach(function (item) {
7398
7506
  _api_register( 'table().' + item[0] + '.structure()' , function (selector) {
7399
- var indexes = this.columns(selector).indexes().flatten();
7507
+ var indexes = this.columns(selector).indexes().flatten().toArray();
7400
7508
  var ctx = this.context[0];
7401
-
7402
- return _fnHeaderLayout(ctx, ctx[item[1]], indexes);
7403
- } );
7404
- })
7509
+ var structure = _fnHeaderLayout(ctx, ctx[item[1]], indexes);
7510
+
7511
+ // The structure is in column index order - but from this method we want the return to be
7512
+ // in the columns() selector API order. In order to do that we need to map from one form
7513
+ // to the other
7514
+ var orderedIndexes = indexes.slice().sort(function (a, b) {
7515
+ return a - b;
7516
+ });
7517
+
7518
+ return structure.map(function (row) {
7519
+ return indexes.map(function (colIdx) {
7520
+ return row[orderedIndexes.indexOf(colIdx)];
7521
+ });
7522
+ });
7523
+ });
7524
+ });
7405
7525
 
7406
7526
 
7407
7527
  _api_registerPlural( 'tables().containers()', 'table().container()' , function () {
@@ -7750,7 +7870,7 @@
7750
7870
  {
7751
7871
  var
7752
7872
  out = [], res,
7753
- a, i, ien, j, jen,
7873
+ i, ien,
7754
7874
  selectorType = typeof selector;
7755
7875
 
7756
7876
  // Can't just check for isArray here, as an API or jQuery instance might be
@@ -7760,22 +7880,15 @@
7760
7880
  }
7761
7881
 
7762
7882
  for ( i=0, ien=selector.length ; i<ien ; i++ ) {
7763
- // Only split on simple strings - complex expressions will be jQuery selectors
7764
- a = selector[i] && selector[i].split && ! selector[i].match(/[[(:]/) ?
7765
- selector[i].split(',') :
7766
- [ selector[i] ];
7883
+ res = selectFn( typeof selector[i] === 'string' ? selector[i].trim() : selector[i] );
7767
7884
 
7768
- for ( j=0, jen=a.length ; j<jen ; j++ ) {
7769
- res = selectFn( typeof a[j] === 'string' ? (a[j]).trim() : a[j] );
7770
-
7771
- // Remove empty items
7772
- res = res.filter( function (item) {
7773
- return item !== null && item !== undefined;
7774
- });
7885
+ // Remove empty items
7886
+ res = res.filter( function (item) {
7887
+ return item !== null && item !== undefined;
7888
+ });
7775
7889
 
7776
- if ( res && res.length ) {
7777
- out = out.concat( res );
7778
- }
7890
+ if ( res && res.length ) {
7891
+ out = out.concat( res );
7779
7892
  }
7780
7893
  }
7781
7894
 
@@ -7804,6 +7917,7 @@
7804
7917
  }
7805
7918
 
7806
7919
  return $.extend( {
7920
+ columnOrder: 'implied',
7807
7921
  search: 'none',
7808
7922
  order: 'current',
7809
7923
  page: 'all'
@@ -8565,23 +8679,60 @@
8565
8679
 
8566
8680
  var __column_header = function ( settings, column, row ) {
8567
8681
  var header = settings.aoHeader;
8568
- var target = row !== undefined
8569
- ? row
8570
- : settings.bSortCellsTop // legacy support
8571
- ? 0
8572
- : header.length - 1;
8682
+ var titleRow = settings.titleRow;
8683
+ var target = null;
8684
+
8685
+ if (row !== undefined) {
8686
+ target = row;
8687
+ }
8688
+ else if (titleRow === true) { // legacy orderCellsTop support
8689
+ target = 0;
8690
+ }
8691
+ else if (titleRow === false) {
8692
+ target = header.length - 1;
8693
+ }
8694
+ else if (titleRow !== null) {
8695
+ target = titleRow;
8696
+ }
8697
+ else {
8698
+ // Automatic - find the _last_ unique cell from the top that is not empty (last for
8699
+ // backwards compatibility)
8700
+ for (var i=0 ; i<header.length ; i++) {
8701
+ if (header[i][column].unique && $('span.dt-column-title', header[i][column].cell).text()) {
8702
+ target = i;
8703
+ }
8704
+ }
8705
+
8706
+ if (target === null) {
8707
+ target = 0;
8708
+ }
8709
+ }
8573
8710
 
8574
8711
  return header[target][column].cell;
8575
8712
  };
8576
8713
 
8714
+ var __column_header_cells = function (header) {
8715
+ var out = [];
8716
+
8717
+ for (var i=0 ; i<header.length ; i++) {
8718
+ for (var j=0 ; j<header[i].length ; j++) {
8719
+ var cell = header[i][j].cell;
8720
+
8721
+ if (!out.includes(cell)) {
8722
+ out.push(cell);
8723
+ }
8724
+ }
8725
+ }
8726
+
8727
+ return out;
8728
+ }
8729
+
8577
8730
  var __column_selector = function ( settings, selector, opts )
8578
8731
  {
8579
8732
  var
8580
8733
  columns = settings.aoColumns,
8581
- names = _pluck( columns, 'sName' ),
8582
- titles = _pluck( columns, 'sTitle' ),
8583
- cells = DataTable.util.get('[].[].cell')(settings.aoHeader),
8584
- nodes = _unique( _flatten([], cells) );
8734
+ names, titles,
8735
+ nodes = __column_header_cells(settings.aoHeader);
8585
8736
 
8586
8737
  var run = function ( s ) {
8587
8738
  var selInt = _intVal( s );
@@ -8653,12 +8804,21 @@
8653
8804
  } );
8654
8805
 
8655
8806
  case 'name':
8807
+ // Don't get names, unless needed, and only get once if it is
8808
+ if (!names) {
8809
+ names = _pluck( columns, 'sName' );
8810
+ }
8811
+
8656
8812
  // match by name. `names` is column index complete and in order
8657
8813
  return names.map( function (name, i) {
8658
8814
  return name === match[1] ? i : null;
8659
8815
  } );
8660
8816
 
8661
8817
  case 'title':
8818
+ if (!titles) {
8819
+ titles = _pluck( columns, 'sTitle' );
8820
+ }
8821
+
8662
8822
  // match by column title
8663
8823
  return titles.map( function (title, i) {
8664
8824
  return title === match[1] ? i : null;
@@ -8697,7 +8857,11 @@
8697
8857
  [];
8698
8858
  };
8699
8859
 
8700
- return _selector_run( 'column', selector, run, settings, opts );
8860
+ var selected = _selector_run( 'column', selector, run, settings, opts );
8861
+
8862
+ return opts.columnOrder && opts.columnOrder === 'index'
8863
+ ? selected.sort(function (a, b) { return a - b; })
8864
+ : selected; // implied
8701
8865
  };
8702
8866
 
8703
8867
 
@@ -8821,6 +8985,12 @@
8821
8985
  }, 1 );
8822
8986
  } );
8823
8987
 
8988
+ _api_registerPlural( 'columns().names()', 'column().name()', function () {
8989
+ return this.iterator( 'column', function ( settings, column ) {
8990
+ return settings.aoColumns[column].sName;
8991
+ }, 1 );
8992
+ } );
8993
+
8824
8994
  _api_registerPlural( 'columns().nodes()', 'column().nodes()', function () {
8825
8995
  return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
8826
8996
  return _pluck_order( settings.aoData, rows, 'anCells', column ) ;
@@ -9247,7 +9417,10 @@
9247
9417
  // otherwise a 2D array was passed in
9248
9418
 
9249
9419
  return this.iterator( 'table', function ( settings ) {
9250
- settings.aaSorting = Array.isArray(order) ? order.slice() : order;
9420
+ var resolved = [];
9421
+ _fnSortResolve(settings, resolved, order);
9422
+
9423
+ settings.aaSorting = resolved;
9251
9424
  } );
9252
9425
  } );
9253
9426
 
@@ -9373,7 +9546,7 @@
9373
9546
  var fixed = settings.searchFixed;
9374
9547
 
9375
9548
  if (! name) {
9376
- return Object.keys(fixed)
9549
+ return Object.keys(fixed);
9377
9550
  }
9378
9551
  else if (search === undefined) {
9379
9552
  return fixed[name];
@@ -9440,10 +9613,10 @@
9440
9613
  var fixed = settings.aoColumns[colIdx].searchFixed;
9441
9614
 
9442
9615
  if (! name) {
9443
- return Object.keys(fixed)
9616
+ return Object.keys(fixed);
9444
9617
  }
9445
9618
  else if (search === undefined) {
9446
- return fixed[name];
9619
+ return fixed[name] || null;
9447
9620
  }
9448
9621
  else if (search === null) {
9449
9622
  delete fixed[name];
@@ -9832,12 +10005,14 @@
9832
10005
  // Function to run either once the table becomes ready or
9833
10006
  // immediately if it is already ready.
9834
10007
  return this.tables().every(function () {
10008
+ var api = this;
10009
+
9835
10010
  if (this.context[0]._bInitComplete) {
9836
- fn.call(this);
10011
+ fn.call(api);
9837
10012
  }
9838
10013
  else {
9839
10014
  this.on('init.dt.DT', function () {
9840
- fn.call(this);
10015
+ fn.call(api);
9841
10016
  });
9842
10017
  }
9843
10018
  } );
@@ -9893,20 +10068,31 @@
9893
10068
  jqTable.append( tfoot );
9894
10069
  }
9895
10070
 
10071
+ // Clean up the header / footer
10072
+ cleanHeader(thead, 'header');
10073
+ cleanHeader(tfoot, 'footer');
9896
10074
  settings.colgroup.remove();
9897
10075
 
9898
10076
  settings.aaSorting = [];
9899
10077
  settings.aaSortingFixed = [];
9900
10078
  _fnSortingClasses( settings );
9901
10079
 
10080
+ $(jqTable).find('th, td').removeClass(
10081
+ $.map(DataTable.ext.type.className, function (v) {
10082
+ return v;
10083
+ }).join(' ')
10084
+ );
10085
+
9902
10086
  $('th, td', thead)
9903
10087
  .removeClass(
10088
+ orderClasses.none + ' ' +
9904
10089
  orderClasses.canAsc + ' ' +
9905
10090
  orderClasses.canDesc + ' ' +
9906
10091
  orderClasses.isAsc + ' ' +
9907
10092
  orderClasses.isDesc
9908
10093
  )
9909
- .css('width', '');
10094
+ .css('width', '')
10095
+ .removeAttr('aria-sort');
9910
10096
 
9911
10097
  // Add the TR elements back into the table in their original order
9912
10098
  jqTbody.children().detach();
@@ -9986,6 +10172,19 @@
9986
10172
  : resolved;
9987
10173
  } );
9988
10174
 
10175
+ // Needed for header and footer, so pulled into its own function
10176
+ function cleanHeader(node, className) {
10177
+ $(node).find('span.dt-column-order').remove();
10178
+ $(node).find('span.dt-column-title').each(function () {
10179
+ var title = $(this).html();
10180
+ $(this).parent().parent().append(title);
10181
+ $(this).remove();
10182
+ });
10183
+ $(node).find('div.dt-column-' + className).remove();
10184
+
10185
+ $('th, td', node).removeAttr('data-dt-column');
10186
+ }
10187
+
9989
10188
  /**
9990
10189
  * Version string for plug-ins to check compatibility. Allowed format is
9991
10190
  * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used
@@ -9994,7 +10193,7 @@
9994
10193
  * @type string
9995
10194
  * @default Version number
9996
10195
  */
9997
- DataTable.version = "2.2.1";
10196
+ DataTable.version = "2.3.0";
9998
10197
 
9999
10198
  /**
10000
10199
  * Private data store, containing all of the settings objects that are
@@ -10601,6 +10800,10 @@
10601
10800
  "bSortCellsTop": null,
10602
10801
 
10603
10802
 
10803
+ /** Specify which row is the title row in the header. Replacement for bSortCellsTop */
10804
+ titleRow: null,
10805
+
10806
+
10604
10807
  /**
10605
10808
  * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and
10606
10809
  * `sorting\_3` to the columns which are currently being sorted on. This is
@@ -10878,6 +11081,13 @@
10878
11081
  1: "entry"
10879
11082
  },
10880
11083
 
11084
+ /**
11085
+ * Page length options
11086
+ */
11087
+ lengthLabels: {
11088
+ '-1': 'All'
11089
+ },
11090
+
10881
11091
  /**
10882
11092
  * This string is shown in preference to `zeroRecords` when the table is
10883
11093
  * empty of data (regardless of filtering). Note that this is an optional
@@ -11148,7 +11358,10 @@
11148
11358
  /**
11149
11359
  * For server-side processing - use the data from the DOM for the first draw
11150
11360
  */
11151
- iDeferLoading: null
11361
+ iDeferLoading: null,
11362
+
11363
+ /** Event listeners */
11364
+ on: null
11152
11365
  };
11153
11366
 
11154
11367
  _fnHungarianMap( DataTable.defaults );
@@ -11975,10 +12188,7 @@
11975
12188
 
11976
12189
  /**
11977
12190
  * Indicate that if multiple rows are in the header and there is more than
11978
- * one unique cell per column, if the top one (true) or bottom one (false)
11979
- * should be used for sorting / title by DataTables.
11980
- * Note that this parameter will be set by the initialisation routine. To
11981
- * set a default use {@link DataTable.defaults}.
12191
+ * one unique cell per column. Replaced by titleRow
11982
12192
  */
11983
12193
  "bSortCellsTop": null,
11984
12194
 
@@ -12103,7 +12313,19 @@
12103
12313
  resizeObserver: null,
12104
12314
 
12105
12315
  /** Keep a record of the last size of the container, so we can skip duplicates */
12106
- containerWidth: -1
12316
+ containerWidth: -1,
12317
+
12318
+ /** Reverse the initial order of the data set on desc ordering */
12319
+ orderDescReverse: null,
12320
+
12321
+ /** Show / hide ordering indicators in headers */
12322
+ orderIndicators: true,
12323
+
12324
+ /** Default ordering listener */
12325
+ orderHandler: true,
12326
+
12327
+ /** Title row indicator */
12328
+ titleRow: null
12107
12329
  };
12108
12330
 
12109
12331
  /**
@@ -12933,7 +13155,7 @@
12933
13155
  cell.addClass(classes.order.none);
12934
13156
  }
12935
13157
 
12936
- var legacyTop = settings.bSortCellsTop;
13158
+ var titleRow = settings.titleRow;
12937
13159
  var headerRows = cell.closest('thead').find('tr');
12938
13160
  var rowIdx = cell.parent().index();
12939
13161
 
@@ -12943,11 +13165,10 @@
12943
13165
  cell.attr('data-dt-order') === 'disable' ||
12944
13166
  cell.parent().attr('data-dt-order') === 'disable' ||
12945
13167
 
12946
- // Legacy support for `orderCellsTop`. If it is set, then cells
12947
- // which are not in the top or bottom row of the header (depending
12948
- // on the value) do not get the sorting classes applied to them
12949
- (legacyTop === true && rowIdx !== 0) ||
12950
- (legacyTop === false && rowIdx !== headerRows.length - 1)
13168
+ // titleRow support, for defining a specific row in the header
13169
+ (titleRow === true && rowIdx !== 0) ||
13170
+ (titleRow === false && rowIdx !== headerRows.length - 1) ||
13171
+ (typeof titleRow === 'number' && rowIdx !== titleRow)
12951
13172
  ) {
12952
13173
  return;
12953
13174
  }
@@ -12957,7 +13178,7 @@
12957
13178
  // `DT` namespace will allow the event to be removed automatically
12958
13179
  // on destroy, while the `dt` namespaced event is the one we are
12959
13180
  // listening for
12960
- $(settings.nTable).on( 'order.dt.DT column-visibility.dt.DT', function ( e, ctx ) {
13181
+ $(settings.nTable).on( 'order.dt.DT column-visibility.dt.DT', function ( e, ctx, column ) {
12961
13182
  if ( settings !== ctx ) { // need to check this this is the host
12962
13183
  return; // table, not a nested one
12963
13184
  }
@@ -12968,6 +13189,16 @@
12968
13189
  return;
12969
13190
  }
12970
13191
 
13192
+ var orderedColumns = _pluck(sorting, 'col');
13193
+
13194
+ // This handler is only needed on column visibility if the column is part of the
13195
+ // ordering. If it isn't, then we can bail out to save performance. It could be a
13196
+ // separate event handler, but this is a balance between code reuse / size and performance
13197
+ // console.log(e, e.name, column, orderedColumns, orderedColumns.includes(column))
13198
+ if (e.type === 'column-visibility' && ! orderedColumns.includes(column)) {
13199
+ return;
13200
+ }
13201
+
12971
13202
  var i;
12972
13203
  var orderClasses = classes.order;
12973
13204
  var columns = ctx.api.columns( cell );
@@ -12976,8 +13207,8 @@
12976
13207
  var ariaType = '';
12977
13208
  var indexes = columns.indexes();
12978
13209
  var sortDirs = columns.orderable(true).flatten();
12979
- var orderedColumns = _pluck(sorting, 'col');
12980
13210
  var tabIndex = settings.iTabIndex;
13211
+ var canOrder = ctx.orderHandler && orderable;
12981
13212
 
12982
13213
  cell
12983
13214
  .removeClass(
@@ -12985,8 +13216,8 @@
12985
13216
  orderClasses.isDesc
12986
13217
  )
12987
13218
  .toggleClass( orderClasses.none, ! orderable )
12988
- .toggleClass( orderClasses.canAsc, orderable && sortDirs.includes('asc') )
12989
- .toggleClass( orderClasses.canDesc, orderable && sortDirs.includes('desc') );
13219
+ .toggleClass( orderClasses.canAsc, canOrder && sortDirs.includes('asc') )
13220
+ .toggleClass( orderClasses.canDesc, canOrder && sortDirs.includes('desc') );
12990
13221
 
12991
13222
  // Determine if all of the columns that this cell covers are included in the
12992
13223
  // current ordering
@@ -13035,16 +13266,16 @@
13035
13266
  cell.removeAttr('aria-sort');
13036
13267
  }
13037
13268
 
13038
- cell.attr('aria-label', orderable
13039
- ? col.ariaTitle + ctx.api.i18n('oAria.orderable' + ariaType)
13040
- : col.ariaTitle
13041
- );
13042
-
13043
13269
  // Make the headers tab-able for keyboard navigation
13044
13270
  if (orderable) {
13045
13271
  var orderSpan = cell.find('.dt-column-order');
13046
13272
 
13047
- orderSpan.attr('role', 'button');
13273
+ orderSpan
13274
+ .attr('role', 'button')
13275
+ .attr('aria-label', orderable
13276
+ ? col.ariaTitle + ctx.api.i18n('oAria.orderable' + ariaType)
13277
+ : col.ariaTitle
13278
+ );
13048
13279
 
13049
13280
  if (tabIndex !== -1) {
13050
13281
  orderSpan.attr('tabindex', tabIndex);
@@ -13745,12 +13976,17 @@
13745
13976
  } );
13746
13977
 
13747
13978
  for ( i=0 ; i<lengths.length ; i++ ) {
13748
- select[0][ i ] = new Option(
13749
- typeof language[i] === 'number' ?
13979
+ // Attempt to look up the length from the i18n options
13980
+ var label = settings.api.i18n('lengthLabels.' + lengths[i], null);
13981
+
13982
+ if (label === null) {
13983
+ // If not present, fallback to old style
13984
+ label = typeof language[i] === 'number' ?
13750
13985
  settings.fnFormatNumber( language[i] ) :
13751
- language[i],
13752
- lengths[i]
13753
- );
13986
+ language[i];
13987
+ }
13988
+
13989
+ select[0][ i ] = new Option(label, lengths[i]);
13754
13990
  }
13755
13991
 
13756
13992
  // add for and id to label and input