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.js CHANGED
@@ -1,25 +1,7 @@
1
- /*! DataTables 2.1.7
1
+ /*! DataTables 2.2.0
2
2
  * © SpryMedia Ltd - datatables.net/license
3
3
  */
4
4
 
5
- /**
6
- * @summary DataTables
7
- * @description Paginate, search and order HTML tables
8
- * @version 2.1.7
9
- * @author SpryMedia Ltd
10
- * @contact www.datatables.net
11
- * @copyright SpryMedia Ltd.
12
- *
13
- * This source file is free software, available under the following license:
14
- * MIT license - https://datatables.net/license
15
- *
16
- * This source file is distributed in the hope that it will be useful, but
17
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18
- * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
19
- *
20
- * For details please refer to: https://www.datatables.net
21
- */
22
-
23
5
  (function( factory ) {
24
6
  "use strict";
25
7
 
@@ -429,7 +411,6 @@
429
411
  thead = $('<thead/>').appendTo($this);
430
412
  }
431
413
  oSettings.nTHead = thead[0];
432
- $('tr', thead).addClass(oClasses.thead.row);
433
414
 
434
415
  var tbody = $this.children('tbody');
435
416
  if ( tbody.length === 0 ) {
@@ -444,7 +425,6 @@
444
425
  tfoot = $('<tfoot/>').appendTo($this);
445
426
  }
446
427
  oSettings.nTFoot = tfoot[0];
447
- $('tr', tfoot).addClass(oClasses.tfoot.row);
448
428
 
449
429
  // Copy the data index array
450
430
  oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
@@ -2161,6 +2141,10 @@
2161
2141
  var width = _fnColumnsSumWidth(settings, [i], false, false);
2162
2142
 
2163
2143
  cols[i].colEl.css('width', width);
2144
+
2145
+ if (settings.oScroll.sX) {
2146
+ cols[i].colEl.css('min-width', width);
2147
+ }
2164
2148
  }
2165
2149
  }
2166
2150
 
@@ -3228,9 +3212,13 @@
3228
3212
 
3229
3213
  // Add the number of cells needed to make up to the number of columns
3230
3214
  if (row.length === 1) {
3231
- var cells = $('td, th', row);
3215
+ var cellCount = 0;
3216
+
3217
+ $('td, th', row).each(function () {
3218
+ cellCount += this.colSpan;
3219
+ });
3232
3220
 
3233
- for ( i=cells.length, ien=columns.length ; i<ien ; i++ ) {
3221
+ for ( i=cellCount, ien=columns.length ; i<ien ; i++ ) {
3234
3222
  $('<th/>')
3235
3223
  .html( columns[i][titleProp] || '' )
3236
3224
  .appendTo( row );
@@ -3242,14 +3230,13 @@
3242
3230
 
3243
3231
  if (side === 'header') {
3244
3232
  settings.aoHeader = detected;
3233
+ $('tr', target).addClass(classes.thead.row);
3245
3234
  }
3246
3235
  else {
3247
3236
  settings.aoFooter = detected;
3237
+ $('tr', target).addClass(classes.tfoot.row);
3248
3238
  }
3249
3239
 
3250
- // ARIA role for the rows
3251
- $(target).children('tr').attr('role', 'row');
3252
-
3253
3240
  // Every cell needs to be passed through the renderer
3254
3241
  $(target).children('tr').children('th, td')
3255
3242
  .each( function () {
@@ -4510,7 +4497,7 @@
4510
4497
  // So the array reference doesn't break set the results into the
4511
4498
  // existing array
4512
4499
  displayRows.length = 0;
4513
- displayRows.push.apply(displayRows, rows);
4500
+ _fnArrayApply(displayRows, rows);
4514
4501
  }
4515
4502
  }
4516
4503
 
@@ -5238,8 +5225,11 @@
5238
5225
  // [].find, but it wasn't supported in Chrome until Sept 2015, and DT has 10 year
5239
5226
  // browser support
5240
5227
  var firstTr = null;
5228
+ var start = _fnDataSource( settings ) !== 'ssp'
5229
+ ? settings._iDisplayStart
5230
+ : 0;
5241
5231
 
5242
- for (i=0 ; i<settings.aiDisplay.length ; i++) {
5232
+ for (i=start ; i<start + settings.aiDisplay.length ; i++) {
5243
5233
  var idx = settings.aiDisplay[i];
5244
5234
  var tr = settings.aoData[idx].nTr;
5245
5235
 
@@ -5254,7 +5244,7 @@
5254
5244
  return {
5255
5245
  idx: _fnVisibleToColumnIndex(settings, vis),
5256
5246
  width: $(this).outerWidth()
5257
- }
5247
+ };
5258
5248
  });
5259
5249
 
5260
5250
  // Check against what the colgroup > col is set to and correct if needed
@@ -5264,6 +5254,10 @@
5264
5254
 
5265
5255
  if (colWidth !== colSizes[i].width) {
5266
5256
  colEl.style.width = colSizes[i].width + 'px';
5257
+
5258
+ if (scroll.sX) {
5259
+ colEl.style.minWidth = colSizes[i].width + 'px';
5260
+ }
5267
5261
  }
5268
5262
  }
5269
5263
  }
@@ -5413,6 +5407,8 @@
5413
5407
  // browser will collapse it. If this width is smaller than the
5414
5408
  // width the column requires, then it will have no effect
5415
5409
  if ( scrollX ) {
5410
+ this.style.minWidth = width;
5411
+
5416
5412
  $( this ).append( $('<div/>').css( {
5417
5413
  width: width,
5418
5414
  margin: 0,
@@ -5522,15 +5518,52 @@
5522
5518
  }
5523
5519
 
5524
5520
  if ( (tableWidthAttr || scrollX) && ! settings._reszEvt ) {
5525
- var bindResize = function () {
5526
- $(window).on('resize.DT-'+settings.sInstance, DataTable.util.throttle( function () {
5527
- if (! settings.bDestroying) {
5528
- _fnAdjustColumnSizing( settings );
5521
+ var wrapperWidth = function () {
5522
+ return $(settings.nTableWrapper).is(':visible')
5523
+ ? $(settings.nTableWrapper).width()
5524
+ : 0;
5525
+ }
5526
+
5527
+ settings.containerWidth = wrapperWidth();
5528
+
5529
+ var resize = DataTable.util.throttle( function () {
5530
+ var newWidth = wrapperWidth();
5531
+
5532
+ // Don't do it if destroying, is the same size as last time, or the container
5533
+ // width is 0
5534
+ if (
5535
+ ! settings.bDestroying &&
5536
+ settings.containerWidth !== newWidth &&
5537
+ newWidth !== 0
5538
+ ) {
5539
+ // Do a resize
5540
+ _fnAdjustColumnSizing( settings );
5541
+ settings.containerWidth = newWidth;
5542
+ }
5543
+ } );
5544
+
5545
+ // For browsers that support it (~2020 onwards for wide support) we can watch for the
5546
+ // container changing width.
5547
+ if (window.ResizeObserver) {
5548
+ settings.resizeObserver = new ResizeObserver(function (e) {
5549
+ var box = e[0].contentBoxSize;
5550
+ var size = Array.isArray(box)
5551
+ ? box[0].inlineSize // Spec
5552
+ : box.inlineSize; // Old Firefox
5553
+
5554
+ // Under this condition a resize will trigger its own resize, causing an error.
5555
+ if (size < settings.containerWidth) {
5556
+ return;
5529
5557
  }
5530
- } ) );
5531
- };
5532
5558
 
5533
- bindResize();
5559
+ resize();
5560
+ });
5561
+ settings.resizeObserver.observe(settings.nTableWrapper);
5562
+ }
5563
+ else {
5564
+ // For old browsers, the best we can do is listen for a window resize
5565
+ $(window).on('resize.DT-'+settings.sInstance, resize);
5566
+ }
5534
5567
 
5535
5568
  settings._reszEvt = true;
5536
5569
  }
@@ -6144,15 +6177,26 @@
6144
6177
  return;
6145
6178
  }
6146
6179
 
6180
+ // Sort state saving uses [[idx, order]] structure.
6181
+ var sorting = [];
6182
+ _fnSortResolve(settings, sorting, settings.aaSorting );
6183
+
6147
6184
  /* Store the interesting variables */
6185
+ var columns = settings.aoColumns;
6148
6186
  var state = {
6149
6187
  time: +new Date(),
6150
6188
  start: settings._iDisplayStart,
6151
6189
  length: settings._iDisplayLength,
6152
- order: $.extend( true, [], settings.aaSorting ),
6190
+ order: sorting.map(function (sort) {
6191
+ // If a column name is available, use it
6192
+ return columns[sort[0]] && columns[sort[0]].sName
6193
+ ? [ columns[sort[0]].sName, sort[1] ]
6194
+ : sort.slice();
6195
+ } ),
6153
6196
  search: $.extend({}, settings.oPreviousSearch),
6154
6197
  columns: settings.aoColumns.map( function ( col, i ) {
6155
6198
  return {
6199
+ name: col.sName,
6156
6200
  visible: col.bVisible,
6157
6201
  search: $.extend({}, settings.aoPreSearchCols[i])
6158
6202
  };
@@ -6200,6 +6244,8 @@
6200
6244
  function _fnImplementState ( settings, s, callback) {
6201
6245
  var i, ien;
6202
6246
  var columns = settings.aoColumns;
6247
+ var currentNames = _pluck(settings.aoColumns, 'sName');
6248
+
6203
6249
  settings._bLoadingState = true;
6204
6250
 
6205
6251
  // When StateRestore was introduced the state could now be implemented at any time
@@ -6229,13 +6275,6 @@
6229
6275
  return;
6230
6276
  }
6231
6277
 
6232
- // Number of columns have changed - all bets are off, no restore of settings
6233
- if ( s.columns && columns.length !== s.columns.length ) {
6234
- settings._bLoadingState = false;
6235
- callback();
6236
- return;
6237
- }
6238
-
6239
6278
  // Store the saved state so it might be accessed at any time
6240
6279
  settings.oLoadedState = $.extend( true, {}, s );
6241
6280
 
@@ -6269,10 +6308,23 @@
6269
6308
  if ( s.order !== undefined ) {
6270
6309
  settings.aaSorting = [];
6271
6310
  $.each( s.order, function ( i, col ) {
6272
- settings.aaSorting.push( col[0] >= columns.length ?
6273
- [ 0, col[1] ] :
6274
- col
6275
- );
6311
+ var set = [ col[0], col[1] ];
6312
+
6313
+ // A column name was stored and should be used for restore
6314
+ if (typeof col[0] === 'string') {
6315
+ var idx = currentNames.indexOf(col[0]);
6316
+
6317
+ // Find the name from the current list of column names, or fallback to index 0
6318
+ set[0] = idx >= 0
6319
+ ? idx
6320
+ : 0;
6321
+ }
6322
+ else if (set[0] >= columns.length) {
6323
+ // If a column name, but it is out of bounds, set to 0
6324
+ set[0] = 0;
6325
+ }
6326
+
6327
+ settings.aaSorting.push(set);
6276
6328
  } );
6277
6329
  }
6278
6330
 
@@ -6283,30 +6335,64 @@
6283
6335
 
6284
6336
  // Columns
6285
6337
  if ( s.columns ) {
6286
- for ( i=0, ien=s.columns.length ; i<ien ; i++ ) {
6287
- var col = s.columns[i];
6288
-
6289
- // Visibility
6290
- if ( col.visible !== undefined ) {
6291
- // If the api is defined, the table has been initialised so we need to use it rather than internal settings
6292
- if (api) {
6293
- // Don't redraw the columns on every iteration of this loop, we will do this at the end instead
6294
- api.column(i).visible(col.visible, false);
6338
+ var set = s.columns;
6339
+ var incoming = _pluck(s.columns, 'name');
6340
+
6341
+ // Check if it is a 2.2 style state object with a `name` property for the columns, and if
6342
+ // the name was defined. If so, then create a new array that will map the state object
6343
+ // given, to the current columns (don't bother if they are already matching tho).
6344
+ if (incoming.join('').length && incoming.join('') !== currentNames.join('')) {
6345
+ set = [];
6346
+
6347
+ // For each column, try to find the name in the incoming array
6348
+ for (i=0 ; i<currentNames.length ; i++) {
6349
+ if (currentNames[i] != '') {
6350
+ var idx = incoming.indexOf(currentNames[i]);
6351
+
6352
+ if (idx >= 0) {
6353
+ set.push(s.columns[idx]);
6354
+ }
6355
+ else {
6356
+ // No matching column name in the state's columns, so this might be a new
6357
+ // column and thus can't have a state already.
6358
+ set.push({});
6359
+ }
6295
6360
  }
6296
6361
  else {
6297
- columns[i].bVisible = col.visible;
6362
+ // If no name, but other columns did have a name, then there is no knowing
6363
+ // where this one came from originally so it can't be restored.
6364
+ set.push({});
6298
6365
  }
6299
6366
  }
6367
+ }
6300
6368
 
6301
- // Search
6302
- if ( col.search !== undefined ) {
6303
- $.extend( settings.aoPreSearchCols[i], col.search );
6369
+ // If the number of columns to restore is different from current, then all bets are off.
6370
+ if (set.length === columns.length) {
6371
+ for ( i=0, ien=set.length ; i<ien ; i++ ) {
6372
+ var col = set[i];
6373
+
6374
+ // Visibility
6375
+ if ( col.visible !== undefined ) {
6376
+ // If the api is defined, the table has been initialised so we need to use it rather than internal settings
6377
+ if (api) {
6378
+ // Don't redraw the columns on every iteration of this loop, we will do this at the end instead
6379
+ api.column(i).visible(col.visible, false);
6380
+ }
6381
+ else {
6382
+ columns[i].bVisible = col.visible;
6383
+ }
6384
+ }
6385
+
6386
+ // Search
6387
+ if ( col.search !== undefined ) {
6388
+ $.extend( settings.aoPreSearchCols[i], col.search );
6389
+ }
6390
+ }
6391
+
6392
+ // If the api is defined then we need to adjust the columns once the visibility has been changed
6393
+ if (api) {
6394
+ api.columns.adjust();
6304
6395
  }
6305
- }
6306
-
6307
- // If the api is defined then we need to adjust the columns once the visibility has been changed
6308
- if (api) {
6309
- api.columns.adjust();
6310
6396
  }
6311
6397
  }
6312
6398
 
@@ -6624,6 +6710,30 @@
6624
6710
  replace(/_ENTRIES-TOTAL_/g, settings.api.i18n('entries', '', vis) );
6625
6711
  }
6626
6712
 
6713
+ /**
6714
+ * Add elements to an array as quickly as possible, but stack stafe.
6715
+ *
6716
+ * @param {*} arr Array to add the data to
6717
+ * @param {*} data Data array that is to be added
6718
+ * @returns
6719
+ */
6720
+ function _fnArrayApply(arr, data) {
6721
+ if (! data) {
6722
+ return;
6723
+ }
6724
+
6725
+ // Chrome can throw a max stack error if apply is called with
6726
+ // too large an array, but apply is faster.
6727
+ if (data.length < 10000) {
6728
+ arr.push.apply(arr, data);
6729
+ }
6730
+ else {
6731
+ for (i=0 ; i<data.length ; i++) {
6732
+ arr.push(data[i]);
6733
+ }
6734
+ }
6735
+ }
6736
+
6627
6737
 
6628
6738
 
6629
6739
  /**
@@ -6816,18 +6926,7 @@
6816
6926
  : settings;
6817
6927
 
6818
6928
  // Initial data
6819
- if ( data ) {
6820
- // Chrome can throw a max stack error if apply is called with
6821
- // too large an array, but apply is faster.
6822
- if (data.length < 10000) {
6823
- this.push.apply(this, data);
6824
- }
6825
- else {
6826
- for (i=0 ; i<data.length ; i++) {
6827
- this.push(data[i]);
6828
- }
6829
- }
6830
- }
6929
+ _fnArrayApply(this, data);
6831
6930
 
6832
6931
  // selector
6833
6932
  this.selector = {
@@ -7208,7 +7307,7 @@
7208
7307
  selector.forEach(function (sel) {
7209
7308
  var inner = __table_selector(sel, a);
7210
7309
 
7211
- result.push.apply(result, inner);
7310
+ _fnArrayApply(result, inner);
7212
7311
  });
7213
7312
 
7214
7313
  return result.filter( function (item) {
@@ -8062,7 +8161,7 @@
8062
8161
  // Return an Api.rows() extended instance, so rows().nodes() etc can be used
8063
8162
  var modRows = this.rows( -1 );
8064
8163
  modRows.pop();
8065
- modRows.push.apply(modRows, newRows);
8164
+ _fnArrayApply(modRows, newRows);
8066
8165
 
8067
8166
  return modRows;
8068
8167
  } );
@@ -8575,7 +8674,10 @@
8575
8674
  .map( function () {
8576
8675
  return _fnColumnsFromHeader( this ); // `nodes` is column index complete and in order
8577
8676
  } )
8578
- .toArray();
8677
+ .toArray()
8678
+ .sort(function (a, b) {
8679
+ return a - b;
8680
+ });
8579
8681
 
8580
8682
  if ( jqResult.length || ! s.nodeName ) {
8581
8683
  return jqResult;
@@ -9394,6 +9496,10 @@
9394
9496
  } );
9395
9497
  } );
9396
9498
 
9499
+ // Can be assigned in DateTable.use() - note luxon and moment vars are in helpers.js
9500
+ var __bootstrap;
9501
+ var __foundation;
9502
+
9397
9503
  /**
9398
9504
  * Set the libraries that DataTables uses, or the global objects.
9399
9505
  * Note that the arguments can be either way around (legacy support)
@@ -9427,6 +9533,14 @@
9427
9533
  case 'moment':
9428
9534
  return __moment;
9429
9535
 
9536
+ case 'bootstrap':
9537
+ // Use local if set, otherwise try window, which could be undefined
9538
+ return __bootstrap || window.bootstrap;
9539
+
9540
+ case 'foundation':
9541
+ // Ditto
9542
+ return __foundation || window.Foundation;
9543
+
9430
9544
  default:
9431
9545
  return null;
9432
9546
  }
@@ -9436,7 +9550,7 @@
9436
9550
  if (type === 'lib' || type === 'jq' || (module && module.fn && module.fn.jquery)) {
9437
9551
  $ = module;
9438
9552
  }
9439
- else if (type == 'win' || (module && module.document)) {
9553
+ else if (type === 'win' || (module && module.document)) {
9440
9554
  window = module;
9441
9555
  document = module.document;
9442
9556
  }
@@ -9449,6 +9563,14 @@
9449
9563
  else if (type === 'moment' || (module && module.isMoment)) {
9450
9564
  __moment = module;
9451
9565
  }
9566
+ else if (type === 'bootstrap' || (module && module.Modal && module.Modal.NAME === 'modal'))
9567
+ {
9568
+ // This is currently for BS5 only. BS3/4 attach to jQuery, so no need to use `.use()`
9569
+ __bootstrap = module;
9570
+ }
9571
+ else if (type === 'foundation' || (module && module.Reveal)) {
9572
+ __foundation = module;
9573
+ }
9452
9574
  }
9453
9575
 
9454
9576
  /**
@@ -9739,6 +9861,11 @@
9739
9861
  new _Api( settings ).columns().visible( true );
9740
9862
  }
9741
9863
 
9864
+ // Container width change listener
9865
+ if (settings.resizeObserver) {
9866
+ settings.resizeObserver.disconnect();
9867
+ }
9868
+
9742
9869
  // Blitz all `DT` namespaced events (these are internal events, the
9743
9870
  // lowercase, `dt` events are user subscribed and they are responsible
9744
9871
  // for removing them
@@ -9857,7 +9984,7 @@
9857
9984
  * @type string
9858
9985
  * @default Version number
9859
9986
  */
9860
- DataTable.version = "2.1.7";
9987
+ DataTable.version = "2.2.0";
9861
9988
 
9862
9989
  /**
9863
9990
  * Private data store, containing all of the settings objects that are
@@ -11960,7 +12087,13 @@
11960
12087
  deferLoading: null,
11961
12088
 
11962
12089
  /** Allow auto type detection */
11963
- typeDetect: true
12090
+ typeDetect: true,
12091
+
12092
+ /** ResizeObserver for the container div */
12093
+ resizeObserver: null,
12094
+
12095
+ /** Keep a record of the last size of the container, so we can skip duplicates */
12096
+ containerWidth: 0
11964
12097
  };
11965
12098
 
11966
12099
  /**
@@ -12102,8 +12235,8 @@
12102
12235
 
12103
12236
 
12104
12237
  var __mlWarning = false;
12105
- var __luxon; // Can be assigned in DateTeble.use()
12106
- var __moment; // Can be assigned in DateTeble.use()
12238
+ var __luxon; // Can be assigned in DateTable.use()
12239
+ var __moment; // Can be assigned in DateTable.use()
12107
12240
 
12108
12241
  /**
12109
12242
  *
@@ -12139,7 +12272,7 @@
12139
12272
  return null;
12140
12273
  }
12141
12274
 
12142
- dt.setLocale(locale);
12275
+ dt = dt.setLocale(locale);
12143
12276
  }
12144
12277
  else if (! format) {
12145
12278
  // No format given, must be ISO
@@ -12182,7 +12315,7 @@
12182
12315
 
12183
12316
  // Add type detection and sorting specific to this date format - we need to be able to identify
12184
12317
  // date type columns as such, rather than as numbers in extensions. Hence the need for this.
12185
- if (! DataTable.ext.type.order[typeName]) {
12318
+ if (! DataTable.ext.type.order[typeName + '-pre']) {
12186
12319
  DataTable.type(typeName, {
12187
12320
  detect: function (d) {
12188
12321
  // The renderer will give the value to type detect as the type!
@@ -12522,6 +12655,13 @@
12522
12655
  });
12523
12656
  }
12524
12657
 
12658
+ var __diacriticHtmlSort = function (a, b) {
12659
+ a = _stripHtml(a);
12660
+ b = _stripHtml(b);
12661
+
12662
+ return __diacriticSort(a, b);
12663
+ }
12664
+
12525
12665
  //
12526
12666
  // Built in data types
12527
12667
  //
@@ -12592,6 +12732,31 @@
12592
12732
  });
12593
12733
 
12594
12734
 
12735
+ DataTable.type('html-utf8', {
12736
+ detect: {
12737
+ allOf: function ( d ) {
12738
+ return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1);
12739
+ },
12740
+ oneOf: function ( d ) {
12741
+ // At least one data point must contain a `<` and a non-ASCII character
12742
+ // eslint-disable-next-line compat/compat
12743
+ return navigator.languages &&
12744
+ ! _empty( d ) &&
12745
+ typeof d === 'string' &&
12746
+ d.indexOf('<') !== -1 &&
12747
+ typeof d === 'string' && d.match(/[^\x00-\x7F]/);
12748
+ }
12749
+ },
12750
+ order: {
12751
+ asc: __diacriticHtmlSort,
12752
+ desc: function (a, b) {
12753
+ return __diacriticHtmlSort(a, b) * -1;
12754
+ }
12755
+ },
12756
+ search: _filterString(true, true)
12757
+ });
12758
+
12759
+
12595
12760
  DataTable.type('date', {
12596
12761
  className: 'dt-type-date',
12597
12762
  detect: {
@@ -12802,6 +12967,7 @@
12802
12967
  var indexes = columns.indexes();
12803
12968
  var sortDirs = columns.orderable(true).flatten();
12804
12969
  var orderedColumns = _pluck(sorting, 'col');
12970
+ var tabIndex = settings.iTabIndex;
12805
12971
 
12806
12972
  cell
12807
12973
  .removeClass(
@@ -12866,8 +13032,13 @@
12866
13032
 
12867
13033
  // Make the headers tab-able for keyboard navigation
12868
13034
  if (orderable) {
12869
- cell.find('.dt-column-title').attr('role', 'button');
12870
- cell.attr('tabindex', 0)
13035
+ var orderSpan = cell.find('.dt-column-order');
13036
+
13037
+ orderSpan.attr('role', 'button');
13038
+
13039
+ if (tabIndex !== -1) {
13040
+ orderSpan.attr('tabindex', tabIndex);
13041
+ }
12871
13042
  }
12872
13043
  } );
12873
13044
  }
@@ -12881,7 +13052,7 @@
12881
13052
  .addClass(items.className || classes.row)
12882
13053
  .appendTo( container );
12883
13054
 
12884
- $.each( items, function (key, val) {
13055
+ DataTable.ext.renderer.layout._forLayoutRow(items, function (key, val) {
12885
13056
  if (key === 'id' || key === 'className') {
12886
13057
  return;
12887
13058
  }
@@ -12912,7 +13083,31 @@
12912
13083
  })
12913
13084
  .append( val.contents )
12914
13085
  .appendTo( row );
12915
- } );
13086
+ });
13087
+ },
13088
+
13089
+ // Shared for use by the styling frameworks
13090
+ _forLayoutRow: function (items, fn) {
13091
+ // As we are inserting dom elements, we need start / end in a
13092
+ // specific order, this function is used for sorting the layout
13093
+ // keys.
13094
+ var layoutEnum = function (x) {
13095
+ switch (x) {
13096
+ case '': return 0;
13097
+ case 'start': return 1;
13098
+ case 'end': return 2;
13099
+ default: return 3;
13100
+ }
13101
+ };
13102
+
13103
+ Object
13104
+ .keys(items)
13105
+ .sort(function (a, b) {
13106
+ return layoutEnum(a) - layoutEnum(b);
13107
+ })
13108
+ .forEach(function (key) {
13109
+ fn(key, items[key]);
13110
+ });
12916
13111
  }
12917
13112
  }
12918
13113
  } );
@@ -13264,7 +13459,7 @@
13264
13459
  'data-dt-idx': button,
13265
13460
  'tabIndex': btnInfo.disabled
13266
13461
  ? -1
13267
- : settings.iTabIndex
13462
+ : settings.iTabIndex && btn.clicker[0].nodeName.toLowerCase() !== 'span'
13268
13463
  ? settings.iTabIndex
13269
13464
  : null, // `0` doesn't need a tabIndex since it is the default
13270
13465
  });
@@ -13298,12 +13493,16 @@
13298
13493
 
13299
13494
  // Responsive - check if the buttons are over two lines based on the
13300
13495
  // height of the buttons and the container.
13301
- if (
13302
- buttonEls.length && // any buttons
13303
- opts.buttons > 1 && // prevent infinite
13304
- $(host).height() >= ($(buttonEls[0]).outerHeight() * 2) - 10
13305
- ) {
13306
- _pagingDraw(settings, host, $.extend({}, opts, { buttons: opts.buttons - 2 }));
13496
+ if (buttonEls.length) {
13497
+ var outerHeight = $(buttonEls[0]).outerHeight();
13498
+
13499
+ if (
13500
+ opts.buttons > 1 && // prevent infinite
13501
+ outerHeight > 0 && // will be 0 if hidden
13502
+ $(host).height() >= (outerHeight * 2) - 10
13503
+ ) {
13504
+ _pagingDraw(settings, host, $.extend({}, opts, { buttons: opts.buttons - 2 }));
13505
+ }
13307
13506
  }
13308
13507
  }
13309
13508
 
@@ -13327,7 +13526,6 @@
13327
13526
  switch ( button ) {
13328
13527
  case 'ellipsis':
13329
13528
  o.display = '&#x2026;';
13330
- o.disabled = true;
13331
13529
  break;
13332
13530
 
13333
13531
  case 'first':
@@ -13514,7 +13712,7 @@
13514
13712
 
13515
13713
  // Save text node content for macro updating
13516
13714
  var textNodes = [];
13517
- Array.from(div.find('label')[0].childNodes).forEach(function (el) {
13715
+ Array.prototype.slice.call(div.find('label')[0].childNodes).forEach(function (el) {
13518
13716
  if (el.nodeType === Node.TEXT_NODE) {
13519
13717
  textNodes.push({
13520
13718
  el: el,
@@ -13532,7 +13730,6 @@
13532
13730
 
13533
13731
  // Next, the select itself, along with the options
13534
13732
  var select = $('<select/>', {
13535
- 'name': tableId+'_length',
13536
13733
  'aria-controls': tableId,
13537
13734
  'class': classes.select
13538
13735
  } );