datatables.net 2.1.7 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/js/dataTables.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! DataTables 2.1.7
1
+ /*! DataTables 2.2.0
2
2
  * © SpryMedia Ltd - datatables.net/license
3
3
  */
4
4
 
@@ -376,7 +376,6 @@ var DataTable = function ( selector, options )
376
376
  thead = $('<thead/>').appendTo($this);
377
377
  }
378
378
  oSettings.nTHead = thead[0];
379
- $('tr', thead).addClass(oClasses.thead.row);
380
379
 
381
380
  var tbody = $this.children('tbody');
382
381
  if ( tbody.length === 0 ) {
@@ -391,7 +390,6 @@ var DataTable = function ( selector, options )
391
390
  tfoot = $('<tfoot/>').appendTo($this);
392
391
  }
393
392
  oSettings.nTFoot = tfoot[0];
394
- $('tr', tfoot).addClass(oClasses.tfoot.row);
395
393
 
396
394
  // Copy the data index array
397
395
  oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
@@ -2108,6 +2106,10 @@ function _fnColumnSizes ( settings )
2108
2106
  var width = _fnColumnsSumWidth(settings, [i], false, false);
2109
2107
 
2110
2108
  cols[i].colEl.css('width', width);
2109
+
2110
+ if (settings.oScroll.sX) {
2111
+ cols[i].colEl.css('min-width', width);
2112
+ }
2111
2113
  }
2112
2114
  }
2113
2115
 
@@ -3175,9 +3177,13 @@ function _fnBuildHead( settings, side )
3175
3177
 
3176
3178
  // Add the number of cells needed to make up to the number of columns
3177
3179
  if (row.length === 1) {
3178
- var cells = $('td, th', row);
3180
+ var cellCount = 0;
3181
+
3182
+ $('td, th', row).each(function () {
3183
+ cellCount += this.colSpan;
3184
+ });
3179
3185
 
3180
- for ( i=cells.length, ien=columns.length ; i<ien ; i++ ) {
3186
+ for ( i=cellCount, ien=columns.length ; i<ien ; i++ ) {
3181
3187
  $('<th/>')
3182
3188
  .html( columns[i][titleProp] || '' )
3183
3189
  .appendTo( row );
@@ -3189,14 +3195,13 @@ function _fnBuildHead( settings, side )
3189
3195
 
3190
3196
  if (side === 'header') {
3191
3197
  settings.aoHeader = detected;
3198
+ $('tr', target).addClass(classes.thead.row);
3192
3199
  }
3193
3200
  else {
3194
3201
  settings.aoFooter = detected;
3202
+ $('tr', target).addClass(classes.tfoot.row);
3195
3203
  }
3196
3204
 
3197
- // ARIA role for the rows
3198
- $(target).children('tr').attr('role', 'row');
3199
-
3200
3205
  // Every cell needs to be passed through the renderer
3201
3206
  $(target).children('tr').children('th, td')
3202
3207
  .each( function () {
@@ -4457,7 +4462,7 @@ function _fnFilterCustom( settings )
4457
4462
  // So the array reference doesn't break set the results into the
4458
4463
  // existing array
4459
4464
  displayRows.length = 0;
4460
- displayRows.push.apply(displayRows, rows);
4465
+ _fnArrayApply(displayRows, rows);
4461
4466
  }
4462
4467
  }
4463
4468
 
@@ -5185,8 +5190,11 @@ function _fnScrollDraw ( settings )
5185
5190
  // [].find, but it wasn't supported in Chrome until Sept 2015, and DT has 10 year
5186
5191
  // browser support
5187
5192
  var firstTr = null;
5193
+ var start = _fnDataSource( settings ) !== 'ssp'
5194
+ ? settings._iDisplayStart
5195
+ : 0;
5188
5196
 
5189
- for (i=0 ; i<settings.aiDisplay.length ; i++) {
5197
+ for (i=start ; i<start + settings.aiDisplay.length ; i++) {
5190
5198
  var idx = settings.aiDisplay[i];
5191
5199
  var tr = settings.aoData[idx].nTr;
5192
5200
 
@@ -5201,7 +5209,7 @@ function _fnScrollDraw ( settings )
5201
5209
  return {
5202
5210
  idx: _fnVisibleToColumnIndex(settings, vis),
5203
5211
  width: $(this).outerWidth()
5204
- }
5212
+ };
5205
5213
  });
5206
5214
 
5207
5215
  // Check against what the colgroup > col is set to and correct if needed
@@ -5211,6 +5219,10 @@ function _fnScrollDraw ( settings )
5211
5219
 
5212
5220
  if (colWidth !== colSizes[i].width) {
5213
5221
  colEl.style.width = colSizes[i].width + 'px';
5222
+
5223
+ if (scroll.sX) {
5224
+ colEl.style.minWidth = colSizes[i].width + 'px';
5225
+ }
5214
5226
  }
5215
5227
  }
5216
5228
  }
@@ -5360,6 +5372,8 @@ function _fnCalculateColumnWidths ( settings )
5360
5372
  // browser will collapse it. If this width is smaller than the
5361
5373
  // width the column requires, then it will have no effect
5362
5374
  if ( scrollX ) {
5375
+ this.style.minWidth = width;
5376
+
5363
5377
  $( this ).append( $('<div/>').css( {
5364
5378
  width: width,
5365
5379
  margin: 0,
@@ -5469,15 +5483,52 @@ function _fnCalculateColumnWidths ( settings )
5469
5483
  }
5470
5484
 
5471
5485
  if ( (tableWidthAttr || scrollX) && ! settings._reszEvt ) {
5472
- var bindResize = function () {
5473
- $(window).on('resize.DT-'+settings.sInstance, DataTable.util.throttle( function () {
5474
- if (! settings.bDestroying) {
5475
- _fnAdjustColumnSizing( settings );
5486
+ var wrapperWidth = function () {
5487
+ return $(settings.nTableWrapper).is(':visible')
5488
+ ? $(settings.nTableWrapper).width()
5489
+ : 0;
5490
+ }
5491
+
5492
+ settings.containerWidth = wrapperWidth();
5493
+
5494
+ var resize = DataTable.util.throttle( function () {
5495
+ var newWidth = wrapperWidth();
5496
+
5497
+ // Don't do it if destroying, is the same size as last time, or the container
5498
+ // width is 0
5499
+ if (
5500
+ ! settings.bDestroying &&
5501
+ settings.containerWidth !== newWidth &&
5502
+ newWidth !== 0
5503
+ ) {
5504
+ // Do a resize
5505
+ _fnAdjustColumnSizing( settings );
5506
+ settings.containerWidth = newWidth;
5507
+ }
5508
+ } );
5509
+
5510
+ // For browsers that support it (~2020 onwards for wide support) we can watch for the
5511
+ // container changing width.
5512
+ if (window.ResizeObserver) {
5513
+ settings.resizeObserver = new ResizeObserver(function (e) {
5514
+ var box = e[0].contentBoxSize;
5515
+ var size = Array.isArray(box)
5516
+ ? box[0].inlineSize // Spec
5517
+ : box.inlineSize; // Old Firefox
5518
+
5519
+ // Under this condition a resize will trigger its own resize, causing an error.
5520
+ if (size < settings.containerWidth) {
5521
+ return;
5476
5522
  }
5477
- } ) );
5478
- };
5479
5523
 
5480
- bindResize();
5524
+ resize();
5525
+ });
5526
+ settings.resizeObserver.observe(settings.nTableWrapper);
5527
+ }
5528
+ else {
5529
+ // For old browsers, the best we can do is listen for a window resize
5530
+ $(window).on('resize.DT-'+settings.sInstance, resize);
5531
+ }
5481
5532
 
5482
5533
  settings._reszEvt = true;
5483
5534
  }
@@ -6091,15 +6142,26 @@ function _fnSaveState ( settings )
6091
6142
  return;
6092
6143
  }
6093
6144
 
6145
+ // Sort state saving uses [[idx, order]] structure.
6146
+ var sorting = [];
6147
+ _fnSortResolve(settings, sorting, settings.aaSorting );
6148
+
6094
6149
  /* Store the interesting variables */
6150
+ var columns = settings.aoColumns;
6095
6151
  var state = {
6096
6152
  time: +new Date(),
6097
6153
  start: settings._iDisplayStart,
6098
6154
  length: settings._iDisplayLength,
6099
- order: $.extend( true, [], settings.aaSorting ),
6155
+ order: sorting.map(function (sort) {
6156
+ // If a column name is available, use it
6157
+ return columns[sort[0]] && columns[sort[0]].sName
6158
+ ? [ columns[sort[0]].sName, sort[1] ]
6159
+ : sort.slice();
6160
+ } ),
6100
6161
  search: $.extend({}, settings.oPreviousSearch),
6101
6162
  columns: settings.aoColumns.map( function ( col, i ) {
6102
6163
  return {
6164
+ name: col.sName,
6103
6165
  visible: col.bVisible,
6104
6166
  search: $.extend({}, settings.aoPreSearchCols[i])
6105
6167
  };
@@ -6147,6 +6209,8 @@ function _fnLoadState ( settings, init, callback )
6147
6209
  function _fnImplementState ( settings, s, callback) {
6148
6210
  var i, ien;
6149
6211
  var columns = settings.aoColumns;
6212
+ var currentNames = _pluck(settings.aoColumns, 'sName');
6213
+
6150
6214
  settings._bLoadingState = true;
6151
6215
 
6152
6216
  // When StateRestore was introduced the state could now be implemented at any time
@@ -6176,13 +6240,6 @@ function _fnImplementState ( settings, s, callback) {
6176
6240
  return;
6177
6241
  }
6178
6242
 
6179
- // Number of columns have changed - all bets are off, no restore of settings
6180
- if ( s.columns && columns.length !== s.columns.length ) {
6181
- settings._bLoadingState = false;
6182
- callback();
6183
- return;
6184
- }
6185
-
6186
6243
  // Store the saved state so it might be accessed at any time
6187
6244
  settings.oLoadedState = $.extend( true, {}, s );
6188
6245
 
@@ -6216,10 +6273,23 @@ function _fnImplementState ( settings, s, callback) {
6216
6273
  if ( s.order !== undefined ) {
6217
6274
  settings.aaSorting = [];
6218
6275
  $.each( s.order, function ( i, col ) {
6219
- settings.aaSorting.push( col[0] >= columns.length ?
6220
- [ 0, col[1] ] :
6221
- col
6222
- );
6276
+ var set = [ col[0], col[1] ];
6277
+
6278
+ // A column name was stored and should be used for restore
6279
+ if (typeof col[0] === 'string') {
6280
+ var idx = currentNames.indexOf(col[0]);
6281
+
6282
+ // Find the name from the current list of column names, or fallback to index 0
6283
+ set[0] = idx >= 0
6284
+ ? idx
6285
+ : 0;
6286
+ }
6287
+ else if (set[0] >= columns.length) {
6288
+ // If a column name, but it is out of bounds, set to 0
6289
+ set[0] = 0;
6290
+ }
6291
+
6292
+ settings.aaSorting.push(set);
6223
6293
  } );
6224
6294
  }
6225
6295
 
@@ -6230,30 +6300,64 @@ function _fnImplementState ( settings, s, callback) {
6230
6300
 
6231
6301
  // Columns
6232
6302
  if ( s.columns ) {
6233
- for ( i=0, ien=s.columns.length ; i<ien ; i++ ) {
6234
- var col = s.columns[i];
6235
-
6236
- // Visibility
6237
- if ( col.visible !== undefined ) {
6238
- // If the api is defined, the table has been initialised so we need to use it rather than internal settings
6239
- if (api) {
6240
- // Don't redraw the columns on every iteration of this loop, we will do this at the end instead
6241
- api.column(i).visible(col.visible, false);
6303
+ var set = s.columns;
6304
+ var incoming = _pluck(s.columns, 'name');
6305
+
6306
+ // Check if it is a 2.2 style state object with a `name` property for the columns, and if
6307
+ // the name was defined. If so, then create a new array that will map the state object
6308
+ // given, to the current columns (don't bother if they are already matching tho).
6309
+ if (incoming.join('').length && incoming.join('') !== currentNames.join('')) {
6310
+ set = [];
6311
+
6312
+ // For each column, try to find the name in the incoming array
6313
+ for (i=0 ; i<currentNames.length ; i++) {
6314
+ if (currentNames[i] != '') {
6315
+ var idx = incoming.indexOf(currentNames[i]);
6316
+
6317
+ if (idx >= 0) {
6318
+ set.push(s.columns[idx]);
6319
+ }
6320
+ else {
6321
+ // No matching column name in the state's columns, so this might be a new
6322
+ // column and thus can't have a state already.
6323
+ set.push({});
6324
+ }
6242
6325
  }
6243
6326
  else {
6244
- columns[i].bVisible = col.visible;
6327
+ // If no name, but other columns did have a name, then there is no knowing
6328
+ // where this one came from originally so it can't be restored.
6329
+ set.push({});
6245
6330
  }
6246
6331
  }
6332
+ }
6333
+
6334
+ // If the number of columns to restore is different from current, then all bets are off.
6335
+ if (set.length === columns.length) {
6336
+ for ( i=0, ien=set.length ; i<ien ; i++ ) {
6337
+ var col = set[i];
6247
6338
 
6248
- // Search
6249
- if ( col.search !== undefined ) {
6250
- $.extend( settings.aoPreSearchCols[i], col.search );
6339
+ // Visibility
6340
+ if ( col.visible !== undefined ) {
6341
+ // If the api is defined, the table has been initialised so we need to use it rather than internal settings
6342
+ if (api) {
6343
+ // Don't redraw the columns on every iteration of this loop, we will do this at the end instead
6344
+ api.column(i).visible(col.visible, false);
6345
+ }
6346
+ else {
6347
+ columns[i].bVisible = col.visible;
6348
+ }
6349
+ }
6350
+
6351
+ // Search
6352
+ if ( col.search !== undefined ) {
6353
+ $.extend( settings.aoPreSearchCols[i], col.search );
6354
+ }
6355
+ }
6356
+
6357
+ // If the api is defined then we need to adjust the columns once the visibility has been changed
6358
+ if (api) {
6359
+ api.columns.adjust();
6251
6360
  }
6252
- }
6253
-
6254
- // If the api is defined then we need to adjust the columns once the visibility has been changed
6255
- if (api) {
6256
- api.columns.adjust();
6257
6361
  }
6258
6362
  }
6259
6363
 
@@ -6571,6 +6675,30 @@ function _fnMacros ( settings, str, entries )
6571
6675
  replace(/_ENTRIES-TOTAL_/g, settings.api.i18n('entries', '', vis) );
6572
6676
  }
6573
6677
 
6678
+ /**
6679
+ * Add elements to an array as quickly as possible, but stack stafe.
6680
+ *
6681
+ * @param {*} arr Array to add the data to
6682
+ * @param {*} data Data array that is to be added
6683
+ * @returns
6684
+ */
6685
+ function _fnArrayApply(arr, data) {
6686
+ if (! data) {
6687
+ return;
6688
+ }
6689
+
6690
+ // Chrome can throw a max stack error if apply is called with
6691
+ // too large an array, but apply is faster.
6692
+ if (data.length < 10000) {
6693
+ arr.push.apply(arr, data);
6694
+ }
6695
+ else {
6696
+ for (i=0 ; i<data.length ; i++) {
6697
+ arr.push(data[i]);
6698
+ }
6699
+ }
6700
+ }
6701
+
6574
6702
 
6575
6703
 
6576
6704
  /**
@@ -6763,18 +6891,7 @@ _Api = function ( context, data )
6763
6891
  : settings;
6764
6892
 
6765
6893
  // Initial data
6766
- if ( data ) {
6767
- // Chrome can throw a max stack error if apply is called with
6768
- // too large an array, but apply is faster.
6769
- if (data.length < 10000) {
6770
- this.push.apply(this, data);
6771
- }
6772
- else {
6773
- for (i=0 ; i<data.length ; i++) {
6774
- this.push(data[i]);
6775
- }
6776
- }
6777
- }
6894
+ _fnArrayApply(this, data);
6778
6895
 
6779
6896
  // selector
6780
6897
  this.selector = {
@@ -7155,7 +7272,7 @@ var __table_selector = function ( selector, a )
7155
7272
  selector.forEach(function (sel) {
7156
7273
  var inner = __table_selector(sel, a);
7157
7274
 
7158
- result.push.apply(result, inner);
7275
+ _fnArrayApply(result, inner);
7159
7276
  });
7160
7277
 
7161
7278
  return result.filter( function (item) {
@@ -8009,7 +8126,7 @@ _api_register( 'rows.add()', function ( rows ) {
8009
8126
  // Return an Api.rows() extended instance, so rows().nodes() etc can be used
8010
8127
  var modRows = this.rows( -1 );
8011
8128
  modRows.pop();
8012
- modRows.push.apply(modRows, newRows);
8129
+ _fnArrayApply(modRows, newRows);
8013
8130
 
8014
8131
  return modRows;
8015
8132
  } );
@@ -8522,7 +8639,10 @@ var __column_selector = function ( settings, selector, opts )
8522
8639
  .map( function () {
8523
8640
  return _fnColumnsFromHeader( this ); // `nodes` is column index complete and in order
8524
8641
  } )
8525
- .toArray();
8642
+ .toArray()
8643
+ .sort(function (a, b) {
8644
+ return a - b;
8645
+ });
8526
8646
 
8527
8647
  if ( jqResult.length || ! s.nodeName ) {
8528
8648
  return jqResult;
@@ -9341,6 +9461,10 @@ _api_register( 'state.save()', function () {
9341
9461
  } );
9342
9462
  } );
9343
9463
 
9464
+ // Can be assigned in DateTable.use() - note luxon and moment vars are in helpers.js
9465
+ var __bootstrap;
9466
+ var __foundation;
9467
+
9344
9468
  /**
9345
9469
  * Set the libraries that DataTables uses, or the global objects.
9346
9470
  * Note that the arguments can be either way around (legacy support)
@@ -9374,6 +9498,14 @@ DataTable.use = function (arg1, arg2) {
9374
9498
  case 'moment':
9375
9499
  return __moment;
9376
9500
 
9501
+ case 'bootstrap':
9502
+ // Use local if set, otherwise try window, which could be undefined
9503
+ return __bootstrap || window.bootstrap;
9504
+
9505
+ case 'foundation':
9506
+ // Ditto
9507
+ return __foundation || window.Foundation;
9508
+
9377
9509
  default:
9378
9510
  return null;
9379
9511
  }
@@ -9383,7 +9515,7 @@ DataTable.use = function (arg1, arg2) {
9383
9515
  if (type === 'lib' || type === 'jq' || (module && module.fn && module.fn.jquery)) {
9384
9516
  $ = module;
9385
9517
  }
9386
- else if (type == 'win' || (module && module.document)) {
9518
+ else if (type === 'win' || (module && module.document)) {
9387
9519
  window = module;
9388
9520
  document = module.document;
9389
9521
  }
@@ -9396,6 +9528,14 @@ DataTable.use = function (arg1, arg2) {
9396
9528
  else if (type === 'moment' || (module && module.isMoment)) {
9397
9529
  __moment = module;
9398
9530
  }
9531
+ else if (type === 'bootstrap' || (module && module.Modal && module.Modal.NAME === 'modal'))
9532
+ {
9533
+ // This is currently for BS5 only. BS3/4 attach to jQuery, so no need to use `.use()`
9534
+ __bootstrap = module;
9535
+ }
9536
+ else if (type === 'foundation' || (module && module.Reveal)) {
9537
+ __foundation = module;
9538
+ }
9399
9539
  }
9400
9540
 
9401
9541
  /**
@@ -9686,6 +9826,11 @@ _api_register( 'destroy()', function ( remove ) {
9686
9826
  new _Api( settings ).columns().visible( true );
9687
9827
  }
9688
9828
 
9829
+ // Container width change listener
9830
+ if (settings.resizeObserver) {
9831
+ settings.resizeObserver.disconnect();
9832
+ }
9833
+
9689
9834
  // Blitz all `DT` namespaced events (these are internal events, the
9690
9835
  // lowercase, `dt` events are user subscribed and they are responsible
9691
9836
  // for removing them
@@ -9804,7 +9949,7 @@ _api_register( 'i18n()', function ( token, def, plural ) {
9804
9949
  * @type string
9805
9950
  * @default Version number
9806
9951
  */
9807
- DataTable.version = "2.1.7";
9952
+ DataTable.version = "2.2.0";
9808
9953
 
9809
9954
  /**
9810
9955
  * Private data store, containing all of the settings objects that are
@@ -11907,7 +12052,13 @@ DataTable.models.oSettings = {
11907
12052
  deferLoading: null,
11908
12053
 
11909
12054
  /** Allow auto type detection */
11910
- typeDetect: true
12055
+ typeDetect: true,
12056
+
12057
+ /** ResizeObserver for the container div */
12058
+ resizeObserver: null,
12059
+
12060
+ /** Keep a record of the last size of the container, so we can skip duplicates */
12061
+ containerWidth: 0
11911
12062
  };
11912
12063
 
11913
12064
  /**
@@ -12049,8 +12200,8 @@ function __mld( dtLib, momentFn, luxonFn, dateFn, arg1 ) {
12049
12200
 
12050
12201
 
12051
12202
  var __mlWarning = false;
12052
- var __luxon; // Can be assigned in DateTeble.use()
12053
- var __moment; // Can be assigned in DateTeble.use()
12203
+ var __luxon; // Can be assigned in DateTable.use()
12204
+ var __moment; // Can be assigned in DateTable.use()
12054
12205
 
12055
12206
  /**
12056
12207
  *
@@ -12086,7 +12237,7 @@ function __mldObj (d, format, locale) {
12086
12237
  return null;
12087
12238
  }
12088
12239
 
12089
- dt.setLocale(locale);
12240
+ dt = dt.setLocale(locale);
12090
12241
  }
12091
12242
  else if (! format) {
12092
12243
  // No format given, must be ISO
@@ -12129,7 +12280,7 @@ function __mlHelper (localeString) {
12129
12280
 
12130
12281
  // Add type detection and sorting specific to this date format - we need to be able to identify
12131
12282
  // date type columns as such, rather than as numbers in extensions. Hence the need for this.
12132
- if (! DataTable.ext.type.order[typeName]) {
12283
+ if (! DataTable.ext.type.order[typeName + '-pre']) {
12133
12284
  DataTable.type(typeName, {
12134
12285
  detect: function (d) {
12135
12286
  // The renderer will give the value to type detect as the type!
@@ -12469,6 +12620,13 @@ var __diacriticSort = function (a, b) {
12469
12620
  });
12470
12621
  }
12471
12622
 
12623
+ var __diacriticHtmlSort = function (a, b) {
12624
+ a = _stripHtml(a);
12625
+ b = _stripHtml(b);
12626
+
12627
+ return __diacriticSort(a, b);
12628
+ }
12629
+
12472
12630
  //
12473
12631
  // Built in data types
12474
12632
  //
@@ -12539,6 +12697,31 @@ DataTable.type('html', {
12539
12697
  });
12540
12698
 
12541
12699
 
12700
+ DataTable.type('html-utf8', {
12701
+ detect: {
12702
+ allOf: function ( d ) {
12703
+ return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1);
12704
+ },
12705
+ oneOf: function ( d ) {
12706
+ // At least one data point must contain a `<` and a non-ASCII character
12707
+ // eslint-disable-next-line compat/compat
12708
+ return navigator.languages &&
12709
+ ! _empty( d ) &&
12710
+ typeof d === 'string' &&
12711
+ d.indexOf('<') !== -1 &&
12712
+ typeof d === 'string' && d.match(/[^\x00-\x7F]/);
12713
+ }
12714
+ },
12715
+ order: {
12716
+ asc: __diacriticHtmlSort,
12717
+ desc: function (a, b) {
12718
+ return __diacriticHtmlSort(a, b) * -1;
12719
+ }
12720
+ },
12721
+ search: _filterString(true, true)
12722
+ });
12723
+
12724
+
12542
12725
  DataTable.type('date', {
12543
12726
  className: 'dt-type-date',
12544
12727
  detect: {
@@ -12749,6 +12932,7 @@ $.extend( true, DataTable.ext.renderer, {
12749
12932
  var indexes = columns.indexes();
12750
12933
  var sortDirs = columns.orderable(true).flatten();
12751
12934
  var orderedColumns = _pluck(sorting, 'col');
12935
+ var tabIndex = settings.iTabIndex;
12752
12936
 
12753
12937
  cell
12754
12938
  .removeClass(
@@ -12813,8 +12997,13 @@ $.extend( true, DataTable.ext.renderer, {
12813
12997
 
12814
12998
  // Make the headers tab-able for keyboard navigation
12815
12999
  if (orderable) {
12816
- cell.find('.dt-column-title').attr('role', 'button');
12817
- cell.attr('tabindex', 0)
13000
+ var orderSpan = cell.find('.dt-column-order');
13001
+
13002
+ orderSpan.attr('role', 'button');
13003
+
13004
+ if (tabIndex !== -1) {
13005
+ orderSpan.attr('tabindex', tabIndex);
13006
+ }
12818
13007
  }
12819
13008
  } );
12820
13009
  }
@@ -12828,7 +13017,7 @@ $.extend( true, DataTable.ext.renderer, {
12828
13017
  .addClass(items.className || classes.row)
12829
13018
  .appendTo( container );
12830
13019
 
12831
- $.each( items, function (key, val) {
13020
+ DataTable.ext.renderer.layout._forLayoutRow(items, function (key, val) {
12832
13021
  if (key === 'id' || key === 'className') {
12833
13022
  return;
12834
13023
  }
@@ -12859,7 +13048,31 @@ $.extend( true, DataTable.ext.renderer, {
12859
13048
  })
12860
13049
  .append( val.contents )
12861
13050
  .appendTo( row );
12862
- } );
13051
+ });
13052
+ },
13053
+
13054
+ // Shared for use by the styling frameworks
13055
+ _forLayoutRow: function (items, fn) {
13056
+ // As we are inserting dom elements, we need start / end in a
13057
+ // specific order, this function is used for sorting the layout
13058
+ // keys.
13059
+ var layoutEnum = function (x) {
13060
+ switch (x) {
13061
+ case '': return 0;
13062
+ case 'start': return 1;
13063
+ case 'end': return 2;
13064
+ default: return 3;
13065
+ }
13066
+ };
13067
+
13068
+ Object
13069
+ .keys(items)
13070
+ .sort(function (a, b) {
13071
+ return layoutEnum(a) - layoutEnum(b);
13072
+ })
13073
+ .forEach(function (key) {
13074
+ fn(key, items[key]);
13075
+ });
12863
13076
  }
12864
13077
  }
12865
13078
  } );
@@ -13211,7 +13424,7 @@ function _pagingDraw(settings, host, opts) {
13211
13424
  'data-dt-idx': button,
13212
13425
  'tabIndex': btnInfo.disabled
13213
13426
  ? -1
13214
- : settings.iTabIndex
13427
+ : settings.iTabIndex && btn.clicker[0].nodeName.toLowerCase() !== 'span'
13215
13428
  ? settings.iTabIndex
13216
13429
  : null, // `0` doesn't need a tabIndex since it is the default
13217
13430
  });
@@ -13245,12 +13458,16 @@ function _pagingDraw(settings, host, opts) {
13245
13458
 
13246
13459
  // Responsive - check if the buttons are over two lines based on the
13247
13460
  // height of the buttons and the container.
13248
- if (
13249
- buttonEls.length && // any buttons
13250
- opts.buttons > 1 && // prevent infinite
13251
- $(host).height() >= ($(buttonEls[0]).outerHeight() * 2) - 10
13252
- ) {
13253
- _pagingDraw(settings, host, $.extend({}, opts, { buttons: opts.buttons - 2 }));
13461
+ if (buttonEls.length) {
13462
+ var outerHeight = $(buttonEls[0]).outerHeight();
13463
+
13464
+ if (
13465
+ opts.buttons > 1 && // prevent infinite
13466
+ outerHeight > 0 && // will be 0 if hidden
13467
+ $(host).height() >= (outerHeight * 2) - 10
13468
+ ) {
13469
+ _pagingDraw(settings, host, $.extend({}, opts, { buttons: opts.buttons - 2 }));
13470
+ }
13254
13471
  }
13255
13472
  }
13256
13473
 
@@ -13274,7 +13491,6 @@ function _pagingButtonInfo(settings, button, page, pages) {
13274
13491
  switch ( button ) {
13275
13492
  case 'ellipsis':
13276
13493
  o.display = '&#x2026;';
13277
- o.disabled = true;
13278
13494
  break;
13279
13495
 
13280
13496
  case 'first':
@@ -13461,7 +13677,7 @@ DataTable.feature.register( 'pageLength', function ( settings, opts ) {
13461
13677
 
13462
13678
  // Save text node content for macro updating
13463
13679
  var textNodes = [];
13464
- Array.from(div.find('label')[0].childNodes).forEach(function (el) {
13680
+ Array.prototype.slice.call(div.find('label')[0].childNodes).forEach(function (el) {
13465
13681
  if (el.nodeType === Node.TEXT_NODE) {
13466
13682
  textNodes.push({
13467
13683
  el: el,
@@ -13479,7 +13695,6 @@ DataTable.feature.register( 'pageLength', function ( settings, opts ) {
13479
13695
 
13480
13696
  // Next, the select itself, along with the options
13481
13697
  var select = $('<select/>', {
13482
- 'name': tableId+'_length',
13483
13698
  'aria-controls': tableId,
13484
13699
  'class': classes.select
13485
13700
  } );
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "main": "js/dataTables.js",
5
5
  "module": "js/dataTables.mjs",
6
6
  "types": "./types/types.d.ts",
7
- "version": "2.1.7",
7
+ "version": "2.2.0",
8
8
  "files": [
9
9
  "js/**/*.js",
10
10
  "js/**/*.mjs",