datatables.net 3.0.0-beta.2 → 3.0.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 3.0.0-beta.2
1
+ /*! DataTables
2
2
  * Copyright (c) SpryMedia Ltd - datatables.net/license
3
3
  */
4
4
 
@@ -535,11 +535,16 @@ function ajax(optionsIn) {
535
535
  if (options.data instanceof FormData) {
536
536
  sendData = options.data;
537
537
  }
538
- else if (method !== 'GET' && options.data && typeof options.data !== 'string') {
539
- sendData = serialize(options.data, options.traditional);
540
- sendData = convertSpaces(sendData, options);
541
- // So beforeSend matches how jQuery behaves
542
- options.data = sendData;
538
+ else if (method !== 'GET' && options.data) {
539
+ if (typeof options.data === 'string') {
540
+ sendData = options.data;
541
+ }
542
+ else {
543
+ sendData = serialize(options.data, options.traditional);
544
+ sendData = convertSpaces(sendData, options);
545
+ // So beforeSend matches how jQuery behaves
546
+ options.data = sendData;
547
+ }
543
548
  }
544
549
  xhr.onreadystatechange = function () {
545
550
  if (xhr.readyState != 4) {
@@ -589,6 +594,7 @@ function ajax(optionsIn) {
589
594
  if (options.beforeSend) {
590
595
  if (options.beforeSend.call(options, xhr, options) === false) {
591
596
  xhr.abort();
597
+ return xhr;
592
598
  }
593
599
  }
594
600
  xhr.send(sendData);
@@ -775,7 +781,7 @@ function allUnique(src) {
775
781
  * @returns Flattened array
776
782
  */
777
783
  function flatten(out, val) {
778
- if (Array.isArray(val)) {
784
+ if (Array.isArray(val) || arrayLike(val)) {
779
785
  for (var i = 0; i < val.length; i++) {
780
786
  flatten(out, val[i]);
781
787
  }
@@ -1318,7 +1324,7 @@ var timer = /*#__PURE__*/Object.freeze({
1318
1324
  * @returns true if this version of DataTables is greater or equal to the
1319
1325
  * required version, or false if this version of DataTales is not suitable
1320
1326
  */
1321
- function check(version1, version2) {
1327
+ function check$1(version1, version2) {
1322
1328
  let dt = external('datatable');
1323
1329
  var parts1 = version2 ? version2.split('.') : dt.ext.version.split('.');
1324
1330
  var parts2 = version1.split('.');
@@ -1338,7 +1344,7 @@ function check(version1, version2) {
1338
1344
 
1339
1345
  var version = /*#__PURE__*/Object.freeze({
1340
1346
  __proto__: null,
1341
- check: check
1347
+ check: check$1
1342
1348
  });
1343
1349
 
1344
1350
  // Note that the aliased properties are for compatibility with DataTables 2-
@@ -1556,9 +1562,12 @@ function parseEventName(original) {
1556
1562
  isFocus = true;
1557
1563
  }
1558
1564
  else if (name === 'blur') {
1559
- name = 'blurout';
1565
+ name = 'focusout';
1560
1566
  isFocus = true;
1561
1567
  }
1568
+ else if (name === 'ready') {
1569
+ name = 'DOMContentLoaded';
1570
+ }
1562
1571
  return {
1563
1572
  eventName: name,
1564
1573
  isFocus,
@@ -1595,6 +1604,14 @@ function add(el, nameFull, handler, selector, one) {
1595
1604
  if (!eventName) {
1596
1605
  return;
1597
1606
  }
1607
+ // Special handling for the "ready" event - it will trigger when the content
1608
+ // is ready, but also if it is already ready, when added.
1609
+ if (el === document && eventName === 'DOMContentLoaded' && nameFull.includes('ready')) {
1610
+ if (document.readyState === 'complete') {
1611
+ handler(new Event('DOMContentLoaded'));
1612
+ return;
1613
+ }
1614
+ }
1598
1615
  // Create a function that will be the actual event handler, and performs any
1599
1616
  // logic we need, such as delegate handling and adding properties.
1600
1617
  let wrapped = function (event) {
@@ -1620,6 +1637,11 @@ function add(el, nameFull, handler, selector, one) {
1620
1637
  if (!dTarget) {
1621
1638
  return;
1622
1639
  }
1640
+ if (isHover &&
1641
+ event.relatedTarget &&
1642
+ dTarget.contains(event.relatedTarget)) {
1643
+ return;
1644
+ }
1623
1645
  callScope = dTarget;
1624
1646
  }
1625
1647
  // Set the properties that jQuery adds to the event object
@@ -1677,7 +1699,7 @@ function remove(el, nameFull, handler, selector) {
1677
1699
  wrapped.delegateSelector === selector &&
1678
1700
  wrapped.original === handler);
1679
1701
  }
1680
- if (eventName && selector) {
1702
+ else if (eventName && selector) {
1681
1703
  removeEvents = stored.filter(wrapped => wrapped.type === eventName &&
1682
1704
  wrapped.delegateSelector === selector);
1683
1705
  }
@@ -1908,29 +1930,33 @@ class Dom {
1908
1930
  if (!content) {
1909
1931
  return this;
1910
1932
  }
1911
- if (Array.isArray(content)) {
1912
- content.forEach(c => this.append(c));
1913
- return this;
1933
+ if (!arrayLike(content)) {
1934
+ content = [content];
1914
1935
  }
1915
- return this.each(el => {
1916
- if (content instanceof Dom) {
1917
- content.each(item => {
1918
- el.append(item);
1919
- });
1920
- }
1921
- else if (typeof content === 'string') {
1922
- el.insertAdjacentHTML('beforeend', content);
1923
- }
1924
- else if (arrayLike(content)) {
1925
- // Allow for a jQuery object being passed
1926
- let arrayLike = content;
1927
- for (let i = 0; i < arrayLike.length; i++) {
1928
- el.append(arrayLike[i]);
1936
+ // Generate a flat array of the content to be added, with nulls removed
1937
+ // this means it will be an array of nodes and / or strings
1938
+ let flatContent = flatten([], content).filter(c => !!c);
1939
+ /// Got a string somewhere in it, so need to use insertAdjacentHTML
1940
+ if (flatContent.find(val => typeof val === 'string')) {
1941
+ return this.each(el => {
1942
+ for (let i = 0; i < flatContent.length; i++) {
1943
+ if (typeof flatContent[i] === 'string') {
1944
+ el.insertAdjacentHTML('beforeend', flatContent[i]);
1945
+ }
1946
+ else {
1947
+ el.append(flatContent[i]);
1948
+ }
1929
1949
  }
1950
+ });
1951
+ }
1952
+ // Otherwise we can use a document fragment for a single mutation
1953
+ // on the main document
1954
+ return this.each(el => {
1955
+ let fragment = new DocumentFragment();
1956
+ for (let i = 0; i < flatContent.length; i++) {
1957
+ fragment.append(flatContent[i]);
1930
1958
  }
1931
- else {
1932
- el.append(content);
1933
- }
1959
+ el.append(fragment);
1934
1960
  });
1935
1961
  }
1936
1962
  /**
@@ -2097,9 +2123,7 @@ class Dom {
2097
2123
  css(rule, value) {
2098
2124
  // String getter
2099
2125
  if (typeof rule === 'string' && value === undefined) {
2100
- return this.length
2101
- ? getComputedStyle(this[0])[rule]
2102
- : null;
2126
+ return this.length ? getComputedStyle(this[0])[rule] : null;
2103
2127
  }
2104
2128
  return this.each(el => {
2105
2129
  if (typeof rule === 'string') {
@@ -2127,11 +2151,11 @@ class Dom {
2127
2151
  return this.length ? dataConvert(this[0].dataset[name]) : null;
2128
2152
  }
2129
2153
  if (typeof name === 'string') {
2130
- this.each(el => el.dataset[name] = JSON.stringify(value));
2154
+ this.each(el => (el.dataset[name] = JSON.stringify(value)));
2131
2155
  }
2132
2156
  else {
2133
2157
  each(name, (key, val) => {
2134
- this.each(el => el.dataset[key] = JSON.stringify(val));
2158
+ this.each(el => (el.dataset[key] = JSON.stringify(val)));
2135
2159
  });
2136
2160
  }
2137
2161
  return this;
@@ -3855,7 +3879,7 @@ const ext = {
3855
3879
  * Software version
3856
3880
  * @type string
3857
3881
  */
3858
- version: '3.0.0-beta.2'
3882
+ version: '3.0.0'
3859
3883
  };
3860
3884
  //
3861
3885
  // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts
@@ -3870,7 +3894,7 @@ Object.assign(ext, {
3870
3894
  oStdClasses: ext.classes,
3871
3895
  oPagination: ext.pager,
3872
3896
  sVersion: ext.version,
3873
- fnVersionCheck: check
3897
+ fnVersionCheck: check$1
3874
3898
  });
3875
3899
 
3876
3900
  /**
@@ -4099,7 +4123,7 @@ function arrayApply(arr, data) {
4099
4123
  function listener(that, name, src) {
4100
4124
  let srcArr = Array.isArray(src) ? src : [src];
4101
4125
  for (var i = 0; i < srcArr.length; i++) {
4102
- that.on(name + '.dt', srcArr[i]);
4126
+ that.on(name + '.dt.DT', srcArr[i]);
4103
4127
  }
4104
4128
  }
4105
4129
  /**
@@ -4214,10 +4238,12 @@ function __mlHelper(localeString) {
4214
4238
  // gives milliseconds epoch
4215
4239
  return d.valueOf();
4216
4240
  }
4217
- },
4218
- className: 'dt-right'
4241
+ }
4219
4242
  });
4220
4243
  }
4244
+ if (!store.className[typeName]) {
4245
+ store.className[typeName] = 'dt-right';
4246
+ }
4221
4247
  return function (d, type) {
4222
4248
  // Allow for a default value
4223
4249
  if (d === null || d === undefined) {
@@ -4318,10 +4344,12 @@ function datetime(format, locale) {
4318
4344
  pre: function (d) {
4319
4345
  return __mldObj(d, format, locale) || 0;
4320
4346
  }
4321
- },
4322
- className: 'dt-right'
4347
+ }
4323
4348
  });
4324
4349
  }
4350
+ if (!store.className[typeName]) {
4351
+ store.className[typeName] = 'dt-right';
4352
+ }
4325
4353
  }
4326
4354
  /**
4327
4355
  * Helpers for `columns.render`.
@@ -4990,6 +5018,7 @@ function invalidate(settings, rowIdx, src, colIdx) {
4990
5018
  // Update DataTables special `DT_*` attributes for the row
4991
5019
  rowAttributes(settings, row);
4992
5020
  }
5021
+ callbackFire(settings, null, 'rowInvalidate', [settings, rowIdx, colIdx], false);
4993
5022
  }
4994
5023
  /**
4995
5024
  * Get the cells and data for a given row - from a <tr> element
@@ -5335,9 +5364,9 @@ function getWideStrings(settings, colIdx) {
5335
5364
  // Don't want script, dialog or template tags in the width
5336
5365
  // calculations as they are hidden content
5337
5366
  cellString = cellString
5338
- .replace(/<script[\s\S]*?<\/script>/gi, ' ')
5339
- .replace(/<dialog[\s\S]*?<\/dialog>/gi, ' ')
5340
- .replace(/<template[\s\S]*?<\/template>/gi, ' ');
5367
+ .replace(/<script[\s\S]*?<\/script(?:\s[^>]*)?>/gi, ' ')
5368
+ .replace(/<dialog[\s\S]*?<\/dialog(?:\s[^>]*)?>/gi, ' ')
5369
+ .replace(/<template[\s\S]*?<\/template(?:\s[^>]*)?>/gi, ' ');
5341
5370
  var noHtml = util.string
5342
5371
  .stripHtml(cellString, ' ')
5343
5372
  .replace(/&nbsp;/g, ' ');
@@ -5938,6 +5967,7 @@ function columnTypes(settings) {
5938
5967
  var types = ext.type.detect;
5939
5968
  var i, iLen, j, jen, k, ken;
5940
5969
  var col, detectedType, cache;
5970
+ var originalTypes = columns.map(c => c.type).join(',');
5941
5971
  // For each column, spin over the data type detection functions, seeing if
5942
5972
  // one matches
5943
5973
  for (i = 0, iLen = columns.length; i < iLen; i++) {
@@ -6035,6 +6065,10 @@ function columnTypes(settings) {
6035
6065
  _columnAutoRender(settings, i);
6036
6066
  }
6037
6067
  }
6068
+ var newTypes = columns.map(c => c.type).join(',');
6069
+ if (newTypes !== originalTypes) {
6070
+ callbackFire(settings, null, 'columnTypes', [settings], false);
6071
+ }
6038
6072
  }
6039
6073
  /**
6040
6074
  * Apply an auto detected renderer to data which doesn't yet have a renderer
@@ -7236,13 +7270,13 @@ function implementState(settings, s, callback) {
7236
7270
  let idx = currentNames.indexOf(col[0]);
7237
7271
  if (idx < 0) {
7238
7272
  // If the column was not found ignore it and continue
7239
- return;
7273
+ continue;
7240
7274
  }
7241
7275
  set[0] = idx;
7242
7276
  }
7243
7277
  else if (set[0] >= columns.length) {
7244
7278
  // If the column index is out of bounds ignore it and continue
7245
- return;
7279
+ continue;
7246
7280
  }
7247
7281
  settings.order.push(set);
7248
7282
  }
@@ -9560,12 +9594,13 @@ function selectCells(settings, selector, opts) {
9560
9594
  // Otherwise the selector is a node, and there is one last option - the
9561
9595
  // element might be a child of an element which has dt-row and dt-column
9562
9596
  // data attributes
9563
- host = Dom.s(s).closest('*[data-dt-row]');
9564
- return host.count()
9597
+ let rowHost = Dom.s(s).closest('*[data-dt-row]');
9598
+ let columnHost = Dom.s(s).closest('*[data-dt-column]');
9599
+ return rowHost.count()
9565
9600
  ? [
9566
9601
  {
9567
- row: host.data('dt-row'),
9568
- column: host.data('dt-column')
9602
+ row: parseInt(rowHost.attr('data-dt-row')),
9603
+ column: parseInt(columnHost.attr('data-dt-column'))
9569
9604
  }
9570
9605
  ]
9571
9606
  : [];
@@ -9878,7 +9913,7 @@ function selectColumns(settings, selector, opts) {
9878
9913
  // Otherwise a node which might have a `dt-column` data attribute, or be
9879
9914
  // a child or such an element
9880
9915
  var host = Dom.s(s).closest('*[data-dt-column]');
9881
- return host.count() ? [host.data('dt-column')] : [];
9916
+ return host.count() ? [parseInt(host.attr('data-dt-column'))] : [];
9882
9917
  };
9883
9918
  var selected = selectorRun('column', selector, run, settings, opts);
9884
9919
  return opts.columnOrder && opts.columnOrder === 'index'
@@ -10075,18 +10110,19 @@ registerPlural('columns().widths()', 'column().width()', function () {
10075
10110
  // Injects a fake row into the table for just a moment so the widths can
10076
10111
  // be read, regardless of colspan in the header and rows being present
10077
10112
  // in the body
10078
- var columns = this.columns(':visible').count();
10113
+ var columns = this.columns(':visible');
10079
10114
  var row = Dom
10080
10115
  .c('tr')
10081
- .html('<td>' + Array(columns).join('</td><td>') + '</td>');
10116
+ .html('<td>' + Array(columns.count()).join('</td><td>') + '</td>');
10082
10117
  Dom.s(this.table().body()).append(row);
10083
- var widths = row.children().mapTo(el => {
10084
- return Dom.s(el).width('outer');
10118
+ var widths = [];
10119
+ var indexes = columns.indexes();
10120
+ row.children().each((el, idx) => {
10121
+ widths[indexes[idx]] = Dom.s(el).width('outer');
10085
10122
  });
10086
10123
  row.remove();
10087
- return this.iterator('column', function (settings, column) {
10088
- var visIdx = columnIndexToVisible(settings, column);
10089
- return visIdx !== null ? widths[visIdx] : 0;
10124
+ return this.iterator('column', (settings, column) => {
10125
+ return widths[column] || 0;
10090
10126
  }, true);
10091
10127
  });
10092
10128
  registerPlural('columns().indexes()', 'column().index()', function (type) {
@@ -10558,7 +10594,7 @@ function selectRows(settings, selector, opts) {
10558
10594
  }
10559
10595
  else {
10560
10596
  var host = Dom.s(sel).closest('*[data-dt-row]');
10561
- return host.count() ? [host.data('dt-row')] : [];
10597
+ return host.count() ? [parseInt(host.attr('data-dt-row'))] : [];
10562
10598
  }
10563
10599
  }
10564
10600
  // ID selector. Want to always be able to select rows by id, regardless
@@ -10590,8 +10626,7 @@ function selectRows(settings, selector, opts) {
10590
10626
  // Get nodes in the order from the `rows` array with null values removed
10591
10627
  var nodes = util.array.removeEmpty(util.array.pluckOrder(settings.data, rows, 'tr'));
10592
10628
  // Selector - selector string, array of nodes or jQuery object.
10593
- return Dom
10594
- .s(nodes)
10629
+ return Dom.s(nodes)
10595
10630
  .filter(sel)
10596
10631
  .mapTo((el) => el._DT_RowIndex);
10597
10632
  };
@@ -10736,7 +10771,7 @@ register('row().data()', function (data) {
10736
10771
  util.set(ctx[0].rowId)(data, row.tr.id);
10737
10772
  }
10738
10773
  // Automatically invalidate
10739
- invalidate(ctx[0], this[0], 'data');
10774
+ invalidate(ctx[0], this[0][0], 'data');
10740
10775
  return this;
10741
10776
  });
10742
10777
  register('row().node()', function () {
@@ -11054,10 +11089,10 @@ register('caption()', function (value, side) {
11054
11089
  caption.css('caption-side', side);
11055
11090
  caption.get(0)._captionSide = side;
11056
11091
  }
11057
- if (container.find('div.dataTables_scroll').count()) {
11058
- var selector = side === 'top' ? 'Head' : 'Foot';
11092
+ if (container.find('div.dt-scroll').count()) {
11093
+ var selector = side === 'top' ? 'head' : 'foot';
11059
11094
  container
11060
- .find('div.dataTables_scroll' + selector + ' table')
11095
+ .find('div.dt-scroll-' + selector + ' table')
11061
11096
  .prepend(caption);
11062
11097
  }
11063
11098
  else {
@@ -11070,6 +11105,272 @@ register('caption.node()', function () {
11070
11105
  return ctx.length ? ctx[0].captionNode : null;
11071
11106
  });
11072
11107
 
11108
+ /**
11109
+ * What's this!? "DataTables Plus" is a commercial set of extensions for
11110
+ * DataTables, such as Editor, and the functions in this file allow a license
11111
+ * key to be provided (`DataTable.key(...)`) to unlock those features.
11112
+ *
11113
+ * This is the modal that I've selected to make DataTables sustainable, open
11114
+ * source core, with some commercial extensions available.
11115
+ *
11116
+ * Please support DataTables and open source by purchasing a Plus license from
11117
+ * https://datatables.net/plus .
11118
+ */
11119
+ let _ready = false;
11120
+ let _notice;
11121
+ let _processingKey = false;
11122
+ let _delayedReleaseDate = null;
11123
+ let _delayedSoftware = null;
11124
+ const _licenseInfo = {
11125
+ developers: 0,
11126
+ type: null,
11127
+ expires: null,
11128
+ valid: null
11129
+ };
11130
+ const _wm = Dom.c('div');
11131
+ const _publicKey = 'BE1A9w9D9U/4s4/TogY+1sW/dLJ8IquzK1PmV70J93ZTIvXMZ0eV2NAb52ntpgwVFySSB2fOI7geLNO737rQAyo=';
11132
+ /**
11133
+ * Convert a base64 string to a binary array
11134
+ *
11135
+ * @param b64 Source string
11136
+ * @returns Array
11137
+ */
11138
+ function b64ToBuf(b64) {
11139
+ return Uint8Array.from(atob(b64), c => c.charCodeAt(0));
11140
+ }
11141
+ /**
11142
+ * Logic to check the trial and plus license expiry and display messages if
11143
+ * needed. There is particular consideration for checking a release date of
11144
+ * software, as the license for DataTables Plus is perpetual for the version
11145
+ * purchased, and it shouldn't show a message for the purchased version ever.
11146
+ *
11147
+ * @param releaseDate The date the software was released on.
11148
+ * @param software The software name being validated. Can be null for a general
11149
+ * "Plus" check.
11150
+ * @returns true if valid, false otherwise
11151
+ */
11152
+ function check(releaseDate, software) {
11153
+ let expires = _licenseInfo.expires;
11154
+ if (_licenseInfo.valid === false) {
11155
+ noticePrep('License key invalid');
11156
+ noticeDisplay();
11157
+ }
11158
+ else if (_licenseInfo.type === 'trial') {
11159
+ // Trail is for plus, so the software type isn't taken into account
11160
+ let remaining = expires
11161
+ ? Math.ceil((expires.getTime() - new Date().getTime()) / 86400000)
11162
+ : -1;
11163
+ if (remaining < 0) {
11164
+ // Trial expires
11165
+ consoleMsg('Your trial has now expired - https://datatables.net/plus', 'warn');
11166
+ noticePrep('Trial expired');
11167
+ noticeDisplay();
11168
+ return false;
11169
+ }
11170
+ else {
11171
+ // Let the user know when it is going to expire with a console
11172
+ // message.
11173
+ consoleMsg('Your trial expires in ' +
11174
+ remaining +
11175
+ ' day' +
11176
+ (remaining === 1 ? '' : 's'));
11177
+ return true;
11178
+ }
11179
+ }
11180
+ else if (_licenseInfo.type === 'plus' ||
11181
+ (_licenseInfo.type === 'editor' && software === 'editor')) {
11182
+ if (!expires || new Date(releaseDate) > expires) {
11183
+ noticePrep('Upgrade required for this version');
11184
+ noticeDisplay();
11185
+ return false;
11186
+ }
11187
+ return true;
11188
+ }
11189
+ else if (_licenseInfo.type === 'editor' && software !== 'editor') {
11190
+ noticePrep('License for Editor only. Upgrade for Plus');
11191
+ noticeDisplay();
11192
+ return false;
11193
+ }
11194
+ noticePrep();
11195
+ noticeDisplay();
11196
+ return false;
11197
+ }
11198
+ /**
11199
+ * Common log message handling
11200
+ *
11201
+ * @param msg Message to show
11202
+ * @param level Log level
11203
+ */
11204
+ function consoleMsg(msg, level = 'log') {
11205
+ let fn = level === 'log' ? console.log : console.warn;
11206
+ fn('%cDataTables Plus%c ' + msg, 'background: #007bff; color: #fff; padding: 2px 5px;', 'color: inherit;');
11207
+ }
11208
+ /**
11209
+ * Set the DataTables Plus key to use
11210
+ *
11211
+ * @param key DataTables Plus key - obtain from https://datatables.net/account .
11212
+ */
11213
+ const key = function (key) {
11214
+ _processingKey = true;
11215
+ // Run the verification of the key
11216
+ verify(key)
11217
+ .then(result => {
11218
+ _processingKey = false;
11219
+ check(_delayedReleaseDate, _delayedSoftware);
11220
+ })
11221
+ .catch(() => {
11222
+ _processingKey = false;
11223
+ check(_delayedReleaseDate, _delayedSoftware);
11224
+ });
11225
+ };
11226
+ /**
11227
+ * Build the notice
11228
+ *
11229
+ * @returns
11230
+ */
11231
+ function noticePrep(text) {
11232
+ if (!_ready) {
11233
+ let shadow = _wm[0].attachShadow({ mode: 'closed' });
11234
+ let notice = Dom.c('div').css({
11235
+ position: 'fixed',
11236
+ bottom: '1em',
11237
+ right: '1em',
11238
+ border: '1px solid #ffc107',
11239
+ background: '#fff3cd',
11240
+ color: '#856404',
11241
+ padding: '0.5em 1em',
11242
+ 'font-family': 'sans-serif',
11243
+ 'font-size': '12px',
11244
+ 'border-radius': '4px',
11245
+ 'z-index': '10000',
11246
+ 'box-shadow': '0 2px 5px rgba(0,0,0,0.2)'
11247
+ });
11248
+ Dom.c('a')
11249
+ .attr('href', 'https://datatables.net/tn/25')
11250
+ .attr('target', '_blank')
11251
+ .css({
11252
+ color: 'inherit',
11253
+ 'text-decoration': 'none'
11254
+ })
11255
+ .appendTo(notice);
11256
+ if (!text) {
11257
+ text = 'License key required';
11258
+ }
11259
+ shadow.appendChild(notice[0]);
11260
+ _notice = notice;
11261
+ _ready = true;
11262
+ }
11263
+ if (text) {
11264
+ _notice
11265
+ .find('a')
11266
+ .html('DataTables Plus: ' + text + ' - learn more &#187;');
11267
+ }
11268
+ }
11269
+ /**
11270
+ * Display the license notice
11271
+ */
11272
+ function noticeDisplay() {
11273
+ if (!_processingKey && !document.body.contains(_wm[0])) {
11274
+ document.body.appendChild(_wm[0]);
11275
+ }
11276
+ }
11277
+ /**
11278
+ * Validate the license string, which is in two parts - the first is a payload
11279
+ * that provides a small amount of information about the license, and the second
11280
+ * which is the license key.
11281
+ *
11282
+ * @param licenseString Key to validate
11283
+ * @returns Promise with validation information
11284
+ */
11285
+ function verify(licenseString) {
11286
+ return new Promise(function (resolve) {
11287
+ try {
11288
+ var parts = licenseString.split(':');
11289
+ if (parts.length !== 2) {
11290
+ _licenseInfo.valid = false;
11291
+ return resolve();
11292
+ }
11293
+ var payload = parts[0];
11294
+ var signatureB64 = parts[1];
11295
+ // Backwards compat for old browsers
11296
+ var cryptoObj = window.crypto || window.msCrypto;
11297
+ var subtle = cryptoObj.subtle || cryptoObj.webkitSubtle;
11298
+ var rawKey = b64ToBuf(_publicKey);
11299
+ var rawSig = b64ToBuf(signatureB64);
11300
+ var data = new TextEncoder().encode(payload);
11301
+ subtle
11302
+ .importKey('raw', rawKey, { name: 'ECDSA', namedCurve: 'P-256' }, false, ['verify'])
11303
+ .then(function (key) {
11304
+ return subtle.verify({ name: 'ECDSA', hash: { name: 'SHA-256' } }, key, rawSig, data);
11305
+ })
11306
+ .then(function (isValid) {
11307
+ if (!isValid) {
11308
+ _licenseInfo.valid = false;
11309
+ return resolve();
11310
+ }
11311
+ // Extract the payload to be useful
11312
+ var payloadParts = payload.match(/(plus|trial|editor)_(\d+)_(\d{4})(\d{2})(\d{2})/);
11313
+ if (!payloadParts || payloadParts.length !== 6) {
11314
+ _licenseInfo.valid = false;
11315
+ return resolve();
11316
+ }
11317
+ _licenseInfo.valid = true;
11318
+ _licenseInfo.type = payloadParts[1];
11319
+ _licenseInfo.developers = parseInt(payloadParts[2]);
11320
+ _licenseInfo.expires = new Date(payloadParts[3] +
11321
+ '-' +
11322
+ payloadParts[4] +
11323
+ '-' +
11324
+ payloadParts[5]);
11325
+ resolve();
11326
+ })
11327
+ .catch(function () {
11328
+ _licenseInfo.valid = false;
11329
+ resolve();
11330
+ });
11331
+ }
11332
+ catch (e) {
11333
+ _licenseInfo.valid = false;
11334
+ resolve();
11335
+ }
11336
+ });
11337
+ }
11338
+ /**
11339
+ * Create the `plus` function on `DataTable` which Plus extensions can call to
11340
+ * determine if the license key is valid and in date for the release. The
11341
+ * resulting function is called like this: `DataTable.plus('2026-12-25')` and
11342
+ * will return `true` or `false` depending on the key that was given (or not).
11343
+ *
11344
+ * @param DataTable The DataTable host object
11345
+ */
11346
+ function plus (DataTable) {
11347
+ Object.defineProperty(DataTable, 'plus', {
11348
+ value: function (releaseDate, software = null) {
11349
+ // Unsecure sites are only useful for development, so allow there
11350
+ // and on the site.
11351
+ let host = window.location.hostname;
11352
+ let isDev = host === '192.168.234.234' ||
11353
+ host.endsWith('.datatables.net') ||
11354
+ host === 'datatables.net';
11355
+ if (isDev) {
11356
+ return true;
11357
+ }
11358
+ if (_processingKey) {
11359
+ // The validation of the key is async, so there is a chance that
11360
+ // it could still be happening when this runs. We just queue the
11361
+ // last one if that is the case.
11362
+ _delayedReleaseDate = releaseDate;
11363
+ _delayedSoftware = software;
11364
+ return true;
11365
+ }
11366
+ return check(releaseDate, software);
11367
+ },
11368
+ configurable: false,
11369
+ enumerable: false,
11370
+ writable: false
11371
+ });
11372
+ }
11373
+
11073
11374
  /**
11074
11375
  * CommonJS factory function pass through. This will check if the arguments
11075
11376
  * given are a window object or a jQuery object. If so they are set accordingly.
@@ -11429,8 +11730,7 @@ register$2('pageLength', function (settings, optsIn) {
11429
11730
  }
11430
11731
  // Wrapper element - use a span as a holder for where the select will go
11431
11732
  var tmpId = 'tmp-' + +new Date();
11432
- var div = Dom
11433
- .c('div')
11733
+ var div = Dom.c('div')
11434
11734
  .classAdd(classes.container)
11435
11735
  .html(str.replace('_MENU_', '<span id="' + tmpId + '"></span>'));
11436
11736
  // Save text node content for macro updating
@@ -11452,9 +11752,9 @@ register$2('pageLength', function (settings, optsIn) {
11452
11752
  });
11453
11753
  };
11454
11754
  // Next, the select itself, along with the options
11455
- var select = Dom
11456
- .c('select')
11755
+ var select = Dom.c('select')
11457
11756
  .attr('aria-controls', tableId)
11757
+ .attr('autocomplete', 'off')
11458
11758
  .classAdd(classes.select);
11459
11759
  for (i = 0; i < lengths.length; i++) {
11460
11760
  // Attempt to look up the length from the i18n options
@@ -11485,7 +11785,26 @@ register$2('pageLength', function (settings, optsIn) {
11485
11785
  // Update node value whenever anything changes the table's length
11486
11786
  Dom.s(settings.table).on('length.dt.DT', function (e, s, len) {
11487
11787
  if (settings === s) {
11488
- div.find('select').val(len);
11788
+ let localSelect = div.find('select');
11789
+ // Remove any temporary values
11790
+ localSelect.find('option[data-dt-len-tmp]').remove();
11791
+ let option = localSelect.find('option[value="' + len + '"]');
11792
+ // If the select list doesn't have the target value, then we
11793
+ // need to add it for display.
11794
+ if (!option.length) {
11795
+ let after = findInsertBeforePoint(select, len);
11796
+ let tempOption = Dom.c('option')
11797
+ .val(len)
11798
+ .text(len)
11799
+ .attr('data-dt-len-tmp', true);
11800
+ if (after && after.length) {
11801
+ tempOption.insertBefore(after);
11802
+ }
11803
+ else {
11804
+ localSelect.append(tempOption);
11805
+ }
11806
+ }
11807
+ localSelect.val(len);
11489
11808
  // Resolve plurals in the text for the new length
11490
11809
  updateEntries(len);
11491
11810
  }
@@ -11493,6 +11812,19 @@ register$2('pageLength', function (settings, optsIn) {
11493
11812
  updateEntries(settings.pageLength);
11494
11813
  return div;
11495
11814
  }, 'l');
11815
+ /**
11816
+ * Find the element to insert the temporary option before to keep the sequence.
11817
+ *
11818
+ * @param select Select element
11819
+ * @param insertValue Page length value
11820
+ * @returns Target option or null if not found
11821
+ */
11822
+ function findInsertBeforePoint(select, insertValue) {
11823
+ let options = select.find('option');
11824
+ let values = options.mapTo(el => parseInt(el.value));
11825
+ let idx = values.findIndex(val => val > insertValue);
11826
+ return idx < -1 ? null : options.eq(idx);
11827
+ }
11496
11828
 
11497
11829
  let __searchCounter = 0;
11498
11830
  register$2('search', function (settings, optsIn) {
@@ -11503,7 +11835,9 @@ register$2('search', function (settings, optsIn) {
11503
11835
  let classes = settings.classes.search;
11504
11836
  let tableId = settings.tableId;
11505
11837
  let language = settings.language;
11506
- let input = '<input type="search" class="' + classes.input + '"/>';
11838
+ let input = '<input type="search" class="' +
11839
+ classes.input +
11840
+ '" autocomplete="off"/>';
11507
11841
  let opts = util.object.assignDeep({
11508
11842
  columns: '*',
11509
11843
  placeholder: language.searchPlaceholder,
@@ -12325,6 +12659,8 @@ DataTable.datetime = datetime;
12325
12659
  DataTable.__browser = browser;
12326
12660
  DataTable.Dom = Dom;
12327
12661
  DataTable.ajax = util.ajax;
12662
+ DataTable.key = key;
12663
+ plus(DataTable);
12328
12664
  /**
12329
12665
  * Private data store, containing all of the settings objects that are created
12330
12666
  * for the tables on a given page.