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.js 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
 
@@ -569,11 +569,16 @@ function ajax(optionsIn) {
569
569
  if (options.data instanceof FormData) {
570
570
  sendData = options.data;
571
571
  }
572
- else if (method !== 'GET' && options.data && typeof options.data !== 'string') {
573
- sendData = serialize(options.data, options.traditional);
574
- sendData = convertSpaces(sendData, options);
575
- // So beforeSend matches how jQuery behaves
576
- options.data = sendData;
572
+ else if (method !== 'GET' && options.data) {
573
+ if (typeof options.data === 'string') {
574
+ sendData = options.data;
575
+ }
576
+ else {
577
+ sendData = serialize(options.data, options.traditional);
578
+ sendData = convertSpaces(sendData, options);
579
+ // So beforeSend matches how jQuery behaves
580
+ options.data = sendData;
581
+ }
577
582
  }
578
583
  xhr.onreadystatechange = function () {
579
584
  if (xhr.readyState != 4) {
@@ -623,6 +628,7 @@ function ajax(optionsIn) {
623
628
  if (options.beforeSend) {
624
629
  if (options.beforeSend.call(options, xhr, options) === false) {
625
630
  xhr.abort();
631
+ return xhr;
626
632
  }
627
633
  }
628
634
  xhr.send(sendData);
@@ -809,7 +815,7 @@ function allUnique(src) {
809
815
  * @returns Flattened array
810
816
  */
811
817
  function flatten(out, val) {
812
- if (Array.isArray(val)) {
818
+ if (Array.isArray(val) || arrayLike(val)) {
813
819
  for (var i = 0; i < val.length; i++) {
814
820
  flatten(out, val[i]);
815
821
  }
@@ -1352,7 +1358,7 @@ var timer = /*#__PURE__*/Object.freeze({
1352
1358
  * @returns true if this version of DataTables is greater or equal to the
1353
1359
  * required version, or false if this version of DataTales is not suitable
1354
1360
  */
1355
- function check(version1, version2) {
1361
+ function check$1(version1, version2) {
1356
1362
  let dt = external('datatable');
1357
1363
  var parts1 = version2 ? version2.split('.') : dt.ext.version.split('.');
1358
1364
  var parts2 = version1.split('.');
@@ -1372,7 +1378,7 @@ function check(version1, version2) {
1372
1378
 
1373
1379
  var version = /*#__PURE__*/Object.freeze({
1374
1380
  __proto__: null,
1375
- check: check
1381
+ check: check$1
1376
1382
  });
1377
1383
 
1378
1384
  // Note that the aliased properties are for compatibility with DataTables 2-
@@ -1590,9 +1596,12 @@ function parseEventName(original) {
1590
1596
  isFocus = true;
1591
1597
  }
1592
1598
  else if (name === 'blur') {
1593
- name = 'blurout';
1599
+ name = 'focusout';
1594
1600
  isFocus = true;
1595
1601
  }
1602
+ else if (name === 'ready') {
1603
+ name = 'DOMContentLoaded';
1604
+ }
1596
1605
  return {
1597
1606
  eventName: name,
1598
1607
  isFocus,
@@ -1629,6 +1638,14 @@ function add(el, nameFull, handler, selector, one) {
1629
1638
  if (!eventName) {
1630
1639
  return;
1631
1640
  }
1641
+ // Special handling for the "ready" event - it will trigger when the content
1642
+ // is ready, but also if it is already ready, when added.
1643
+ if (el === document && eventName === 'DOMContentLoaded' && nameFull.includes('ready')) {
1644
+ if (document.readyState === 'complete') {
1645
+ handler(new Event('DOMContentLoaded'));
1646
+ return;
1647
+ }
1648
+ }
1632
1649
  // Create a function that will be the actual event handler, and performs any
1633
1650
  // logic we need, such as delegate handling and adding properties.
1634
1651
  let wrapped = function (event) {
@@ -1654,6 +1671,11 @@ function add(el, nameFull, handler, selector, one) {
1654
1671
  if (!dTarget) {
1655
1672
  return;
1656
1673
  }
1674
+ if (isHover &&
1675
+ event.relatedTarget &&
1676
+ dTarget.contains(event.relatedTarget)) {
1677
+ return;
1678
+ }
1657
1679
  callScope = dTarget;
1658
1680
  }
1659
1681
  // Set the properties that jQuery adds to the event object
@@ -1711,7 +1733,7 @@ function remove(el, nameFull, handler, selector) {
1711
1733
  wrapped.delegateSelector === selector &&
1712
1734
  wrapped.original === handler);
1713
1735
  }
1714
- if (eventName && selector) {
1736
+ else if (eventName && selector) {
1715
1737
  removeEvents = stored.filter(wrapped => wrapped.type === eventName &&
1716
1738
  wrapped.delegateSelector === selector);
1717
1739
  }
@@ -1942,29 +1964,33 @@ class Dom {
1942
1964
  if (!content) {
1943
1965
  return this;
1944
1966
  }
1945
- if (Array.isArray(content)) {
1946
- content.forEach(c => this.append(c));
1947
- return this;
1967
+ if (!arrayLike(content)) {
1968
+ content = [content];
1948
1969
  }
1949
- return this.each(el => {
1950
- if (content instanceof Dom) {
1951
- content.each(item => {
1952
- el.append(item);
1953
- });
1954
- }
1955
- else if (typeof content === 'string') {
1956
- el.insertAdjacentHTML('beforeend', content);
1957
- }
1958
- else if (arrayLike(content)) {
1959
- // Allow for a jQuery object being passed
1960
- let arrayLike = content;
1961
- for (let i = 0; i < arrayLike.length; i++) {
1962
- el.append(arrayLike[i]);
1970
+ // Generate a flat array of the content to be added, with nulls removed
1971
+ // this means it will be an array of nodes and / or strings
1972
+ let flatContent = flatten([], content).filter(c => !!c);
1973
+ /// Got a string somewhere in it, so need to use insertAdjacentHTML
1974
+ if (flatContent.find(val => typeof val === 'string')) {
1975
+ return this.each(el => {
1976
+ for (let i = 0; i < flatContent.length; i++) {
1977
+ if (typeof flatContent[i] === 'string') {
1978
+ el.insertAdjacentHTML('beforeend', flatContent[i]);
1979
+ }
1980
+ else {
1981
+ el.append(flatContent[i]);
1982
+ }
1963
1983
  }
1984
+ });
1985
+ }
1986
+ // Otherwise we can use a document fragment for a single mutation
1987
+ // on the main document
1988
+ return this.each(el => {
1989
+ let fragment = new DocumentFragment();
1990
+ for (let i = 0; i < flatContent.length; i++) {
1991
+ fragment.append(flatContent[i]);
1964
1992
  }
1965
- else {
1966
- el.append(content);
1967
- }
1993
+ el.append(fragment);
1968
1994
  });
1969
1995
  }
1970
1996
  /**
@@ -2131,9 +2157,7 @@ class Dom {
2131
2157
  css(rule, value) {
2132
2158
  // String getter
2133
2159
  if (typeof rule === 'string' && value === undefined) {
2134
- return this.length
2135
- ? getComputedStyle(this[0])[rule]
2136
- : null;
2160
+ return this.length ? getComputedStyle(this[0])[rule] : null;
2137
2161
  }
2138
2162
  return this.each(el => {
2139
2163
  if (typeof rule === 'string') {
@@ -2161,11 +2185,11 @@ class Dom {
2161
2185
  return this.length ? dataConvert(this[0].dataset[name]) : null;
2162
2186
  }
2163
2187
  if (typeof name === 'string') {
2164
- this.each(el => el.dataset[name] = JSON.stringify(value));
2188
+ this.each(el => (el.dataset[name] = JSON.stringify(value)));
2165
2189
  }
2166
2190
  else {
2167
2191
  each(name, (key, val) => {
2168
- this.each(el => el.dataset[key] = JSON.stringify(val));
2192
+ this.each(el => (el.dataset[key] = JSON.stringify(val)));
2169
2193
  });
2170
2194
  }
2171
2195
  return this;
@@ -3889,7 +3913,7 @@ const ext = {
3889
3913
  * Software version
3890
3914
  * @type string
3891
3915
  */
3892
- version: '3.0.0-beta.2'
3916
+ version: '3.0.0'
3893
3917
  };
3894
3918
  //
3895
3919
  // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts
@@ -3904,7 +3928,7 @@ Object.assign(ext, {
3904
3928
  oStdClasses: ext.classes,
3905
3929
  oPagination: ext.pager,
3906
3930
  sVersion: ext.version,
3907
- fnVersionCheck: check
3931
+ fnVersionCheck: check$1
3908
3932
  });
3909
3933
 
3910
3934
  /**
@@ -4133,7 +4157,7 @@ function arrayApply(arr, data) {
4133
4157
  function listener(that, name, src) {
4134
4158
  let srcArr = Array.isArray(src) ? src : [src];
4135
4159
  for (var i = 0; i < srcArr.length; i++) {
4136
- that.on(name + '.dt', srcArr[i]);
4160
+ that.on(name + '.dt.DT', srcArr[i]);
4137
4161
  }
4138
4162
  }
4139
4163
  /**
@@ -4248,10 +4272,12 @@ function __mlHelper(localeString) {
4248
4272
  // gives milliseconds epoch
4249
4273
  return d.valueOf();
4250
4274
  }
4251
- },
4252
- className: 'dt-right'
4275
+ }
4253
4276
  });
4254
4277
  }
4278
+ if (!store.className[typeName]) {
4279
+ store.className[typeName] = 'dt-right';
4280
+ }
4255
4281
  return function (d, type) {
4256
4282
  // Allow for a default value
4257
4283
  if (d === null || d === undefined) {
@@ -4352,10 +4378,12 @@ function datetime(format, locale) {
4352
4378
  pre: function (d) {
4353
4379
  return __mldObj(d, format, locale) || 0;
4354
4380
  }
4355
- },
4356
- className: 'dt-right'
4381
+ }
4357
4382
  });
4358
4383
  }
4384
+ if (!store.className[typeName]) {
4385
+ store.className[typeName] = 'dt-right';
4386
+ }
4359
4387
  }
4360
4388
  /**
4361
4389
  * Helpers for `columns.render`.
@@ -5024,6 +5052,7 @@ function invalidate(settings, rowIdx, src, colIdx) {
5024
5052
  // Update DataTables special `DT_*` attributes for the row
5025
5053
  rowAttributes(settings, row);
5026
5054
  }
5055
+ callbackFire(settings, null, 'rowInvalidate', [settings, rowIdx, colIdx], false);
5027
5056
  }
5028
5057
  /**
5029
5058
  * Get the cells and data for a given row - from a <tr> element
@@ -5369,9 +5398,9 @@ function getWideStrings(settings, colIdx) {
5369
5398
  // Don't want script, dialog or template tags in the width
5370
5399
  // calculations as they are hidden content
5371
5400
  cellString = cellString
5372
- .replace(/<script[\s\S]*?<\/script>/gi, ' ')
5373
- .replace(/<dialog[\s\S]*?<\/dialog>/gi, ' ')
5374
- .replace(/<template[\s\S]*?<\/template>/gi, ' ');
5401
+ .replace(/<script[\s\S]*?<\/script(?:\s[^>]*)?>/gi, ' ')
5402
+ .replace(/<dialog[\s\S]*?<\/dialog(?:\s[^>]*)?>/gi, ' ')
5403
+ .replace(/<template[\s\S]*?<\/template(?:\s[^>]*)?>/gi, ' ');
5375
5404
  var noHtml = util.string
5376
5405
  .stripHtml(cellString, ' ')
5377
5406
  .replace(/&nbsp;/g, ' ');
@@ -5972,6 +6001,7 @@ function columnTypes(settings) {
5972
6001
  var types = ext.type.detect;
5973
6002
  var i, iLen, j, jen, k, ken;
5974
6003
  var col, detectedType, cache;
6004
+ var originalTypes = columns.map(c => c.type).join(',');
5975
6005
  // For each column, spin over the data type detection functions, seeing if
5976
6006
  // one matches
5977
6007
  for (i = 0, iLen = columns.length; i < iLen; i++) {
@@ -6069,6 +6099,10 @@ function columnTypes(settings) {
6069
6099
  _columnAutoRender(settings, i);
6070
6100
  }
6071
6101
  }
6102
+ var newTypes = columns.map(c => c.type).join(',');
6103
+ if (newTypes !== originalTypes) {
6104
+ callbackFire(settings, null, 'columnTypes', [settings], false);
6105
+ }
6072
6106
  }
6073
6107
  /**
6074
6108
  * Apply an auto detected renderer to data which doesn't yet have a renderer
@@ -7270,13 +7304,13 @@ function implementState(settings, s, callback) {
7270
7304
  let idx = currentNames.indexOf(col[0]);
7271
7305
  if (idx < 0) {
7272
7306
  // If the column was not found ignore it and continue
7273
- return;
7307
+ continue;
7274
7308
  }
7275
7309
  set[0] = idx;
7276
7310
  }
7277
7311
  else if (set[0] >= columns.length) {
7278
7312
  // If the column index is out of bounds ignore it and continue
7279
- return;
7313
+ continue;
7280
7314
  }
7281
7315
  settings.order.push(set);
7282
7316
  }
@@ -9594,12 +9628,13 @@ function selectCells(settings, selector, opts) {
9594
9628
  // Otherwise the selector is a node, and there is one last option - the
9595
9629
  // element might be a child of an element which has dt-row and dt-column
9596
9630
  // data attributes
9597
- host = Dom.s(s).closest('*[data-dt-row]');
9598
- return host.count()
9631
+ let rowHost = Dom.s(s).closest('*[data-dt-row]');
9632
+ let columnHost = Dom.s(s).closest('*[data-dt-column]');
9633
+ return rowHost.count()
9599
9634
  ? [
9600
9635
  {
9601
- row: host.data('dt-row'),
9602
- column: host.data('dt-column')
9636
+ row: parseInt(rowHost.attr('data-dt-row')),
9637
+ column: parseInt(columnHost.attr('data-dt-column'))
9603
9638
  }
9604
9639
  ]
9605
9640
  : [];
@@ -9912,7 +9947,7 @@ function selectColumns(settings, selector, opts) {
9912
9947
  // Otherwise a node which might have a `dt-column` data attribute, or be
9913
9948
  // a child or such an element
9914
9949
  var host = Dom.s(s).closest('*[data-dt-column]');
9915
- return host.count() ? [host.data('dt-column')] : [];
9950
+ return host.count() ? [parseInt(host.attr('data-dt-column'))] : [];
9916
9951
  };
9917
9952
  var selected = selectorRun('column', selector, run, settings, opts);
9918
9953
  return opts.columnOrder && opts.columnOrder === 'index'
@@ -10109,18 +10144,19 @@ registerPlural('columns().widths()', 'column().width()', function () {
10109
10144
  // Injects a fake row into the table for just a moment so the widths can
10110
10145
  // be read, regardless of colspan in the header and rows being present
10111
10146
  // in the body
10112
- var columns = this.columns(':visible').count();
10147
+ var columns = this.columns(':visible');
10113
10148
  var row = Dom
10114
10149
  .c('tr')
10115
- .html('<td>' + Array(columns).join('</td><td>') + '</td>');
10150
+ .html('<td>' + Array(columns.count()).join('</td><td>') + '</td>');
10116
10151
  Dom.s(this.table().body()).append(row);
10117
- var widths = row.children().mapTo(el => {
10118
- return Dom.s(el).width('outer');
10152
+ var widths = [];
10153
+ var indexes = columns.indexes();
10154
+ row.children().each((el, idx) => {
10155
+ widths[indexes[idx]] = Dom.s(el).width('outer');
10119
10156
  });
10120
10157
  row.remove();
10121
- return this.iterator('column', function (settings, column) {
10122
- var visIdx = columnIndexToVisible(settings, column);
10123
- return visIdx !== null ? widths[visIdx] : 0;
10158
+ return this.iterator('column', (settings, column) => {
10159
+ return widths[column] || 0;
10124
10160
  }, true);
10125
10161
  });
10126
10162
  registerPlural('columns().indexes()', 'column().index()', function (type) {
@@ -10592,7 +10628,7 @@ function selectRows(settings, selector, opts) {
10592
10628
  }
10593
10629
  else {
10594
10630
  var host = Dom.s(sel).closest('*[data-dt-row]');
10595
- return host.count() ? [host.data('dt-row')] : [];
10631
+ return host.count() ? [parseInt(host.attr('data-dt-row'))] : [];
10596
10632
  }
10597
10633
  }
10598
10634
  // ID selector. Want to always be able to select rows by id, regardless
@@ -10624,8 +10660,7 @@ function selectRows(settings, selector, opts) {
10624
10660
  // Get nodes in the order from the `rows` array with null values removed
10625
10661
  var nodes = util.array.removeEmpty(util.array.pluckOrder(settings.data, rows, 'tr'));
10626
10662
  // Selector - selector string, array of nodes or jQuery object.
10627
- return Dom
10628
- .s(nodes)
10663
+ return Dom.s(nodes)
10629
10664
  .filter(sel)
10630
10665
  .mapTo((el) => el._DT_RowIndex);
10631
10666
  };
@@ -10770,7 +10805,7 @@ register('row().data()', function (data) {
10770
10805
  util.set(ctx[0].rowId)(data, row.tr.id);
10771
10806
  }
10772
10807
  // Automatically invalidate
10773
- invalidate(ctx[0], this[0], 'data');
10808
+ invalidate(ctx[0], this[0][0], 'data');
10774
10809
  return this;
10775
10810
  });
10776
10811
  register('row().node()', function () {
@@ -11088,10 +11123,10 @@ register('caption()', function (value, side) {
11088
11123
  caption.css('caption-side', side);
11089
11124
  caption.get(0)._captionSide = side;
11090
11125
  }
11091
- if (container.find('div.dataTables_scroll').count()) {
11092
- var selector = side === 'top' ? 'Head' : 'Foot';
11126
+ if (container.find('div.dt-scroll').count()) {
11127
+ var selector = side === 'top' ? 'head' : 'foot';
11093
11128
  container
11094
- .find('div.dataTables_scroll' + selector + ' table')
11129
+ .find('div.dt-scroll-' + selector + ' table')
11095
11130
  .prepend(caption);
11096
11131
  }
11097
11132
  else {
@@ -11104,6 +11139,272 @@ register('caption.node()', function () {
11104
11139
  return ctx.length ? ctx[0].captionNode : null;
11105
11140
  });
11106
11141
 
11142
+ /**
11143
+ * What's this!? "DataTables Plus" is a commercial set of extensions for
11144
+ * DataTables, such as Editor, and the functions in this file allow a license
11145
+ * key to be provided (`DataTable.key(...)`) to unlock those features.
11146
+ *
11147
+ * This is the modal that I've selected to make DataTables sustainable, open
11148
+ * source core, with some commercial extensions available.
11149
+ *
11150
+ * Please support DataTables and open source by purchasing a Plus license from
11151
+ * https://datatables.net/plus .
11152
+ */
11153
+ let _ready = false;
11154
+ let _notice;
11155
+ let _processingKey = false;
11156
+ let _delayedReleaseDate = null;
11157
+ let _delayedSoftware = null;
11158
+ const _licenseInfo = {
11159
+ developers: 0,
11160
+ type: null,
11161
+ expires: null,
11162
+ valid: null
11163
+ };
11164
+ const _wm = Dom.c('div');
11165
+ const _publicKey = 'BE1A9w9D9U/4s4/TogY+1sW/dLJ8IquzK1PmV70J93ZTIvXMZ0eV2NAb52ntpgwVFySSB2fOI7geLNO737rQAyo=';
11166
+ /**
11167
+ * Convert a base64 string to a binary array
11168
+ *
11169
+ * @param b64 Source string
11170
+ * @returns Array
11171
+ */
11172
+ function b64ToBuf(b64) {
11173
+ return Uint8Array.from(atob(b64), c => c.charCodeAt(0));
11174
+ }
11175
+ /**
11176
+ * Logic to check the trial and plus license expiry and display messages if
11177
+ * needed. There is particular consideration for checking a release date of
11178
+ * software, as the license for DataTables Plus is perpetual for the version
11179
+ * purchased, and it shouldn't show a message for the purchased version ever.
11180
+ *
11181
+ * @param releaseDate The date the software was released on.
11182
+ * @param software The software name being validated. Can be null for a general
11183
+ * "Plus" check.
11184
+ * @returns true if valid, false otherwise
11185
+ */
11186
+ function check(releaseDate, software) {
11187
+ let expires = _licenseInfo.expires;
11188
+ if (_licenseInfo.valid === false) {
11189
+ noticePrep('License key invalid');
11190
+ noticeDisplay();
11191
+ }
11192
+ else if (_licenseInfo.type === 'trial') {
11193
+ // Trail is for plus, so the software type isn't taken into account
11194
+ let remaining = expires
11195
+ ? Math.ceil((expires.getTime() - new Date().getTime()) / 86400000)
11196
+ : -1;
11197
+ if (remaining < 0) {
11198
+ // Trial expires
11199
+ consoleMsg('Your trial has now expired - https://datatables.net/plus', 'warn');
11200
+ noticePrep('Trial expired');
11201
+ noticeDisplay();
11202
+ return false;
11203
+ }
11204
+ else {
11205
+ // Let the user know when it is going to expire with a console
11206
+ // message.
11207
+ consoleMsg('Your trial expires in ' +
11208
+ remaining +
11209
+ ' day' +
11210
+ (remaining === 1 ? '' : 's'));
11211
+ return true;
11212
+ }
11213
+ }
11214
+ else if (_licenseInfo.type === 'plus' ||
11215
+ (_licenseInfo.type === 'editor' && software === 'editor')) {
11216
+ if (!expires || new Date(releaseDate) > expires) {
11217
+ noticePrep('Upgrade required for this version');
11218
+ noticeDisplay();
11219
+ return false;
11220
+ }
11221
+ return true;
11222
+ }
11223
+ else if (_licenseInfo.type === 'editor' && software !== 'editor') {
11224
+ noticePrep('License for Editor only. Upgrade for Plus');
11225
+ noticeDisplay();
11226
+ return false;
11227
+ }
11228
+ noticePrep();
11229
+ noticeDisplay();
11230
+ return false;
11231
+ }
11232
+ /**
11233
+ * Common log message handling
11234
+ *
11235
+ * @param msg Message to show
11236
+ * @param level Log level
11237
+ */
11238
+ function consoleMsg(msg, level = 'log') {
11239
+ let fn = level === 'log' ? console.log : console.warn;
11240
+ fn('%cDataTables Plus%c ' + msg, 'background: #007bff; color: #fff; padding: 2px 5px;', 'color: inherit;');
11241
+ }
11242
+ /**
11243
+ * Set the DataTables Plus key to use
11244
+ *
11245
+ * @param key DataTables Plus key - obtain from https://datatables.net/account .
11246
+ */
11247
+ const key = function (key) {
11248
+ _processingKey = true;
11249
+ // Run the verification of the key
11250
+ verify(key)
11251
+ .then(result => {
11252
+ _processingKey = false;
11253
+ check(_delayedReleaseDate, _delayedSoftware);
11254
+ })
11255
+ .catch(() => {
11256
+ _processingKey = false;
11257
+ check(_delayedReleaseDate, _delayedSoftware);
11258
+ });
11259
+ };
11260
+ /**
11261
+ * Build the notice
11262
+ *
11263
+ * @returns
11264
+ */
11265
+ function noticePrep(text) {
11266
+ if (!_ready) {
11267
+ let shadow = _wm[0].attachShadow({ mode: 'closed' });
11268
+ let notice = Dom.c('div').css({
11269
+ position: 'fixed',
11270
+ bottom: '1em',
11271
+ right: '1em',
11272
+ border: '1px solid #ffc107',
11273
+ background: '#fff3cd',
11274
+ color: '#856404',
11275
+ padding: '0.5em 1em',
11276
+ 'font-family': 'sans-serif',
11277
+ 'font-size': '12px',
11278
+ 'border-radius': '4px',
11279
+ 'z-index': '10000',
11280
+ 'box-shadow': '0 2px 5px rgba(0,0,0,0.2)'
11281
+ });
11282
+ Dom.c('a')
11283
+ .attr('href', 'https://datatables.net/tn/25')
11284
+ .attr('target', '_blank')
11285
+ .css({
11286
+ color: 'inherit',
11287
+ 'text-decoration': 'none'
11288
+ })
11289
+ .appendTo(notice);
11290
+ if (!text) {
11291
+ text = 'License key required';
11292
+ }
11293
+ shadow.appendChild(notice[0]);
11294
+ _notice = notice;
11295
+ _ready = true;
11296
+ }
11297
+ if (text) {
11298
+ _notice
11299
+ .find('a')
11300
+ .html('DataTables Plus: ' + text + ' - learn more &#187;');
11301
+ }
11302
+ }
11303
+ /**
11304
+ * Display the license notice
11305
+ */
11306
+ function noticeDisplay() {
11307
+ if (!_processingKey && !document.body.contains(_wm[0])) {
11308
+ document.body.appendChild(_wm[0]);
11309
+ }
11310
+ }
11311
+ /**
11312
+ * Validate the license string, which is in two parts - the first is a payload
11313
+ * that provides a small amount of information about the license, and the second
11314
+ * which is the license key.
11315
+ *
11316
+ * @param licenseString Key to validate
11317
+ * @returns Promise with validation information
11318
+ */
11319
+ function verify(licenseString) {
11320
+ return new Promise(function (resolve) {
11321
+ try {
11322
+ var parts = licenseString.split(':');
11323
+ if (parts.length !== 2) {
11324
+ _licenseInfo.valid = false;
11325
+ return resolve();
11326
+ }
11327
+ var payload = parts[0];
11328
+ var signatureB64 = parts[1];
11329
+ // Backwards compat for old browsers
11330
+ var cryptoObj = window.crypto || window.msCrypto;
11331
+ var subtle = cryptoObj.subtle || cryptoObj.webkitSubtle;
11332
+ var rawKey = b64ToBuf(_publicKey);
11333
+ var rawSig = b64ToBuf(signatureB64);
11334
+ var data = new TextEncoder().encode(payload);
11335
+ subtle
11336
+ .importKey('raw', rawKey, { name: 'ECDSA', namedCurve: 'P-256' }, false, ['verify'])
11337
+ .then(function (key) {
11338
+ return subtle.verify({ name: 'ECDSA', hash: { name: 'SHA-256' } }, key, rawSig, data);
11339
+ })
11340
+ .then(function (isValid) {
11341
+ if (!isValid) {
11342
+ _licenseInfo.valid = false;
11343
+ return resolve();
11344
+ }
11345
+ // Extract the payload to be useful
11346
+ var payloadParts = payload.match(/(plus|trial|editor)_(\d+)_(\d{4})(\d{2})(\d{2})/);
11347
+ if (!payloadParts || payloadParts.length !== 6) {
11348
+ _licenseInfo.valid = false;
11349
+ return resolve();
11350
+ }
11351
+ _licenseInfo.valid = true;
11352
+ _licenseInfo.type = payloadParts[1];
11353
+ _licenseInfo.developers = parseInt(payloadParts[2]);
11354
+ _licenseInfo.expires = new Date(payloadParts[3] +
11355
+ '-' +
11356
+ payloadParts[4] +
11357
+ '-' +
11358
+ payloadParts[5]);
11359
+ resolve();
11360
+ })
11361
+ .catch(function () {
11362
+ _licenseInfo.valid = false;
11363
+ resolve();
11364
+ });
11365
+ }
11366
+ catch (e) {
11367
+ _licenseInfo.valid = false;
11368
+ resolve();
11369
+ }
11370
+ });
11371
+ }
11372
+ /**
11373
+ * Create the `plus` function on `DataTable` which Plus extensions can call to
11374
+ * determine if the license key is valid and in date for the release. The
11375
+ * resulting function is called like this: `DataTable.plus('2026-12-25')` and
11376
+ * will return `true` or `false` depending on the key that was given (or not).
11377
+ *
11378
+ * @param DataTable The DataTable host object
11379
+ */
11380
+ function plus (DataTable) {
11381
+ Object.defineProperty(DataTable, 'plus', {
11382
+ value: function (releaseDate, software = null) {
11383
+ // Unsecure sites are only useful for development, so allow there
11384
+ // and on the site.
11385
+ let host = window.location.hostname;
11386
+ let isDev = host === '192.168.234.234' ||
11387
+ host.endsWith('.datatables.net') ||
11388
+ host === 'datatables.net';
11389
+ if (isDev) {
11390
+ return true;
11391
+ }
11392
+ if (_processingKey) {
11393
+ // The validation of the key is async, so there is a chance that
11394
+ // it could still be happening when this runs. We just queue the
11395
+ // last one if that is the case.
11396
+ _delayedReleaseDate = releaseDate;
11397
+ _delayedSoftware = software;
11398
+ return true;
11399
+ }
11400
+ return check(releaseDate, software);
11401
+ },
11402
+ configurable: false,
11403
+ enumerable: false,
11404
+ writable: false
11405
+ });
11406
+ }
11407
+
11107
11408
  /**
11108
11409
  * CommonJS factory function pass through. This will check if the arguments
11109
11410
  * given are a window object or a jQuery object. If so they are set accordingly.
@@ -11463,8 +11764,7 @@ register$2('pageLength', function (settings, optsIn) {
11463
11764
  }
11464
11765
  // Wrapper element - use a span as a holder for where the select will go
11465
11766
  var tmpId = 'tmp-' + +new Date();
11466
- var div = Dom
11467
- .c('div')
11767
+ var div = Dom.c('div')
11468
11768
  .classAdd(classes.container)
11469
11769
  .html(str.replace('_MENU_', '<span id="' + tmpId + '"></span>'));
11470
11770
  // Save text node content for macro updating
@@ -11486,9 +11786,9 @@ register$2('pageLength', function (settings, optsIn) {
11486
11786
  });
11487
11787
  };
11488
11788
  // Next, the select itself, along with the options
11489
- var select = Dom
11490
- .c('select')
11789
+ var select = Dom.c('select')
11491
11790
  .attr('aria-controls', tableId)
11791
+ .attr('autocomplete', 'off')
11492
11792
  .classAdd(classes.select);
11493
11793
  for (i = 0; i < lengths.length; i++) {
11494
11794
  // Attempt to look up the length from the i18n options
@@ -11519,7 +11819,26 @@ register$2('pageLength', function (settings, optsIn) {
11519
11819
  // Update node value whenever anything changes the table's length
11520
11820
  Dom.s(settings.table).on('length.dt.DT', function (e, s, len) {
11521
11821
  if (settings === s) {
11522
- div.find('select').val(len);
11822
+ let localSelect = div.find('select');
11823
+ // Remove any temporary values
11824
+ localSelect.find('option[data-dt-len-tmp]').remove();
11825
+ let option = localSelect.find('option[value="' + len + '"]');
11826
+ // If the select list doesn't have the target value, then we
11827
+ // need to add it for display.
11828
+ if (!option.length) {
11829
+ let after = findInsertBeforePoint(select, len);
11830
+ let tempOption = Dom.c('option')
11831
+ .val(len)
11832
+ .text(len)
11833
+ .attr('data-dt-len-tmp', true);
11834
+ if (after && after.length) {
11835
+ tempOption.insertBefore(after);
11836
+ }
11837
+ else {
11838
+ localSelect.append(tempOption);
11839
+ }
11840
+ }
11841
+ localSelect.val(len);
11523
11842
  // Resolve plurals in the text for the new length
11524
11843
  updateEntries(len);
11525
11844
  }
@@ -11527,6 +11846,19 @@ register$2('pageLength', function (settings, optsIn) {
11527
11846
  updateEntries(settings.pageLength);
11528
11847
  return div;
11529
11848
  }, 'l');
11849
+ /**
11850
+ * Find the element to insert the temporary option before to keep the sequence.
11851
+ *
11852
+ * @param select Select element
11853
+ * @param insertValue Page length value
11854
+ * @returns Target option or null if not found
11855
+ */
11856
+ function findInsertBeforePoint(select, insertValue) {
11857
+ let options = select.find('option');
11858
+ let values = options.mapTo(el => parseInt(el.value));
11859
+ let idx = values.findIndex(val => val > insertValue);
11860
+ return idx < -1 ? null : options.eq(idx);
11861
+ }
11530
11862
 
11531
11863
  let __searchCounter = 0;
11532
11864
  register$2('search', function (settings, optsIn) {
@@ -11537,7 +11869,9 @@ register$2('search', function (settings, optsIn) {
11537
11869
  let classes = settings.classes.search;
11538
11870
  let tableId = settings.tableId;
11539
11871
  let language = settings.language;
11540
- let input = '<input type="search" class="' + classes.input + '"/>';
11872
+ let input = '<input type="search" class="' +
11873
+ classes.input +
11874
+ '" autocomplete="off"/>';
11541
11875
  let opts = util.object.assignDeep({
11542
11876
  columns: '*',
11543
11877
  placeholder: language.searchPlaceholder,
@@ -12359,6 +12693,8 @@ DataTable.datetime = datetime;
12359
12693
  DataTable.__browser = browser;
12360
12694
  DataTable.Dom = Dom;
12361
12695
  DataTable.ajax = util.ajax;
12696
+ DataTable.key = key;
12697
+ plus(DataTable);
12362
12698
  /**
12363
12699
  * Private data store, containing all of the settings objects that are created
12364
12700
  * for the tables on a given page.