datatables.net 3.0.0-beta.1 → 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 +519 -96
- package/js/dataTables.min.js +2 -2
- package/js/dataTables.min.mjs +2 -2
- package/js/dataTables.mjs +519 -96
- package/package.json +3 -2
- package/types/types.d.ts +245 -228
package/js/dataTables.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! DataTables
|
|
1
|
+
/*! DataTables
|
|
2
2
|
* Copyright (c) SpryMedia Ltd - datatables.net/license
|
|
3
3
|
*/
|
|
4
4
|
|
|
@@ -80,9 +80,11 @@ const reRegexCharacters = new RegExp('(\\' +
|
|
|
80
80
|
// implementations differ between browsers.
|
|
81
81
|
const reDate = /^\d{2,4}[./-]\d{1,2}[./-]\d{1,2}([T ]{1}\d{1,2}[:.]\d{2}([.:]\d{2})?)?$/;
|
|
82
82
|
const reNewLines = /[\r\n\u2028]/g;
|
|
83
|
+
const isoTimezone = /[T\s]\d{2}.*?(Z|[+-]\d{2}(?::?\d{2})?)$/;
|
|
83
84
|
|
|
84
85
|
var regex = /*#__PURE__*/Object.freeze({
|
|
85
86
|
__proto__: null,
|
|
87
|
+
isoTimezone: isoTimezone,
|
|
86
88
|
reDate: reDate,
|
|
87
89
|
reFormattedNumeric: reFormattedNumeric,
|
|
88
90
|
reHtml: reHtml,
|
|
@@ -554,17 +556,29 @@ function ajax(optionsIn) {
|
|
|
554
556
|
if (options.contentType && !(options.data instanceof FormData)) {
|
|
555
557
|
xhr.setRequestHeader('Content-Type', options.contentType);
|
|
556
558
|
}
|
|
559
|
+
// Add a X-Request-With header, as jQuery does so and some server-side
|
|
560
|
+
// platforms look for it. Only for same domain though.
|
|
561
|
+
if (options.headers &&
|
|
562
|
+
!options.headers['X-Requested-With'] &&
|
|
563
|
+
!isCrossDomain(options.url)) {
|
|
564
|
+
options.headers['X-Requested-With'] = 'XMLHttpRequest';
|
|
565
|
+
}
|
|
557
566
|
each(options.headers, (key, val) => {
|
|
558
567
|
xhr.setRequestHeader(key, val);
|
|
559
568
|
});
|
|
560
569
|
if (options.data instanceof FormData) {
|
|
561
570
|
sendData = options.data;
|
|
562
571
|
}
|
|
563
|
-
else if (method !== 'GET' && options.data
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
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
|
+
}
|
|
568
582
|
}
|
|
569
583
|
xhr.onreadystatechange = function () {
|
|
570
584
|
if (xhr.readyState != 4) {
|
|
@@ -614,6 +628,7 @@ function ajax(optionsIn) {
|
|
|
614
628
|
if (options.beforeSend) {
|
|
615
629
|
if (options.beforeSend.call(options, xhr, options) === false) {
|
|
616
630
|
xhr.abort();
|
|
631
|
+
return xhr;
|
|
617
632
|
}
|
|
618
633
|
}
|
|
619
634
|
xhr.send(sendData);
|
|
@@ -651,6 +666,17 @@ function convertSpaces(sendData, options) {
|
|
|
651
666
|
? sendData.replace(/%20/g, '+')
|
|
652
667
|
: sendData;
|
|
653
668
|
}
|
|
669
|
+
/**
|
|
670
|
+
* Determine if a url is a cross domain request or not
|
|
671
|
+
*
|
|
672
|
+
* @param url URL to check
|
|
673
|
+
* @returns True if cross domain, false otherwise
|
|
674
|
+
*/
|
|
675
|
+
function isCrossDomain(url) {
|
|
676
|
+
// Use the current page as the base to handle relative URLs correctly
|
|
677
|
+
const target = new URL(url, window.location.origin);
|
|
678
|
+
return target.origin !== window.location.origin;
|
|
679
|
+
}
|
|
654
680
|
/**
|
|
655
681
|
* Get the HTTP method from the Ajax request options
|
|
656
682
|
*
|
|
@@ -789,7 +815,7 @@ function allUnique(src) {
|
|
|
789
815
|
* @returns Flattened array
|
|
790
816
|
*/
|
|
791
817
|
function flatten(out, val) {
|
|
792
|
-
if (Array.isArray(val)) {
|
|
818
|
+
if (Array.isArray(val) || arrayLike(val)) {
|
|
793
819
|
for (var i = 0; i < val.length; i++) {
|
|
794
820
|
flatten(out, val[i]);
|
|
795
821
|
}
|
|
@@ -1332,7 +1358,7 @@ var timer = /*#__PURE__*/Object.freeze({
|
|
|
1332
1358
|
* @returns true if this version of DataTables is greater or equal to the
|
|
1333
1359
|
* required version, or false if this version of DataTales is not suitable
|
|
1334
1360
|
*/
|
|
1335
|
-
function check(version1, version2) {
|
|
1361
|
+
function check$1(version1, version2) {
|
|
1336
1362
|
let dt = external('datatable');
|
|
1337
1363
|
var parts1 = version2 ? version2.split('.') : dt.ext.version.split('.');
|
|
1338
1364
|
var parts2 = version1.split('.');
|
|
@@ -1352,7 +1378,7 @@ function check(version1, version2) {
|
|
|
1352
1378
|
|
|
1353
1379
|
var version = /*#__PURE__*/Object.freeze({
|
|
1354
1380
|
__proto__: null,
|
|
1355
|
-
check: check
|
|
1381
|
+
check: check$1
|
|
1356
1382
|
});
|
|
1357
1383
|
|
|
1358
1384
|
// Note that the aliased properties are for compatibility with DataTables 2-
|
|
@@ -1570,9 +1596,12 @@ function parseEventName(original) {
|
|
|
1570
1596
|
isFocus = true;
|
|
1571
1597
|
}
|
|
1572
1598
|
else if (name === 'blur') {
|
|
1573
|
-
name = '
|
|
1599
|
+
name = 'focusout';
|
|
1574
1600
|
isFocus = true;
|
|
1575
1601
|
}
|
|
1602
|
+
else if (name === 'ready') {
|
|
1603
|
+
name = 'DOMContentLoaded';
|
|
1604
|
+
}
|
|
1576
1605
|
return {
|
|
1577
1606
|
eventName: name,
|
|
1578
1607
|
isFocus,
|
|
@@ -1609,6 +1638,14 @@ function add(el, nameFull, handler, selector, one) {
|
|
|
1609
1638
|
if (!eventName) {
|
|
1610
1639
|
return;
|
|
1611
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
|
+
}
|
|
1612
1649
|
// Create a function that will be the actual event handler, and performs any
|
|
1613
1650
|
// logic we need, such as delegate handling and adding properties.
|
|
1614
1651
|
let wrapped = function (event) {
|
|
@@ -1634,6 +1671,11 @@ function add(el, nameFull, handler, selector, one) {
|
|
|
1634
1671
|
if (!dTarget) {
|
|
1635
1672
|
return;
|
|
1636
1673
|
}
|
|
1674
|
+
if (isHover &&
|
|
1675
|
+
event.relatedTarget &&
|
|
1676
|
+
dTarget.contains(event.relatedTarget)) {
|
|
1677
|
+
return;
|
|
1678
|
+
}
|
|
1637
1679
|
callScope = dTarget;
|
|
1638
1680
|
}
|
|
1639
1681
|
// Set the properties that jQuery adds to the event object
|
|
@@ -1691,7 +1733,7 @@ function remove(el, nameFull, handler, selector) {
|
|
|
1691
1733
|
wrapped.delegateSelector === selector &&
|
|
1692
1734
|
wrapped.original === handler);
|
|
1693
1735
|
}
|
|
1694
|
-
if (eventName && selector) {
|
|
1736
|
+
else if (eventName && selector) {
|
|
1695
1737
|
removeEvents = stored.filter(wrapped => wrapped.type === eventName &&
|
|
1696
1738
|
wrapped.delegateSelector === selector);
|
|
1697
1739
|
}
|
|
@@ -1906,8 +1948,7 @@ class Dom {
|
|
|
1906
1948
|
}
|
|
1907
1949
|
}
|
|
1908
1950
|
if (sort) {
|
|
1909
|
-
|
|
1910
|
-
// this.sort(documentOrder);
|
|
1951
|
+
this.sort();
|
|
1911
1952
|
}
|
|
1912
1953
|
return this;
|
|
1913
1954
|
}
|
|
@@ -1923,29 +1964,33 @@ class Dom {
|
|
|
1923
1964
|
if (!content) {
|
|
1924
1965
|
return this;
|
|
1925
1966
|
}
|
|
1926
|
-
if (
|
|
1927
|
-
content
|
|
1928
|
-
return this;
|
|
1967
|
+
if (!arrayLike(content)) {
|
|
1968
|
+
content = [content];
|
|
1929
1969
|
}
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
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
|
+
}
|
|
1944
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]);
|
|
1945
1992
|
}
|
|
1946
|
-
|
|
1947
|
-
el.append(content);
|
|
1948
|
-
}
|
|
1993
|
+
el.append(fragment);
|
|
1949
1994
|
});
|
|
1950
1995
|
}
|
|
1951
1996
|
/**
|
|
@@ -2112,9 +2157,7 @@ class Dom {
|
|
|
2112
2157
|
css(rule, value) {
|
|
2113
2158
|
// String getter
|
|
2114
2159
|
if (typeof rule === 'string' && value === undefined) {
|
|
2115
|
-
return this.length
|
|
2116
|
-
? getComputedStyle(this[0])[rule]
|
|
2117
|
-
: null;
|
|
2160
|
+
return this.length ? getComputedStyle(this[0])[rule] : null;
|
|
2118
2161
|
}
|
|
2119
2162
|
return this.each(el => {
|
|
2120
2163
|
if (typeof rule === 'string') {
|
|
@@ -2142,11 +2185,11 @@ class Dom {
|
|
|
2142
2185
|
return this.length ? dataConvert(this[0].dataset[name]) : null;
|
|
2143
2186
|
}
|
|
2144
2187
|
if (typeof name === 'string') {
|
|
2145
|
-
this.each(el => el.dataset[name] = JSON.stringify(value));
|
|
2188
|
+
this.each(el => (el.dataset[name] = JSON.stringify(value)));
|
|
2146
2189
|
}
|
|
2147
2190
|
else {
|
|
2148
2191
|
each(name, (key, val) => {
|
|
2149
|
-
this.each(el => el.dataset[key] = JSON.stringify(val));
|
|
2192
|
+
this.each(el => (el.dataset[key] = JSON.stringify(val)));
|
|
2150
2193
|
});
|
|
2151
2194
|
}
|
|
2152
2195
|
return this;
|
|
@@ -2698,6 +2741,18 @@ class Dom {
|
|
|
2698
2741
|
el.style.display = 'block';
|
|
2699
2742
|
});
|
|
2700
2743
|
}
|
|
2744
|
+
/**
|
|
2745
|
+
* Sort the DOM elements into document order.
|
|
2746
|
+
*
|
|
2747
|
+
* This is normally not needed as elements selected with a DOM selector are
|
|
2748
|
+
* automatically sorted in document order. However, in the case of elements
|
|
2749
|
+
* being added as an array, their order will be retained. In such as case
|
|
2750
|
+
* you might wish to sort them in document order.
|
|
2751
|
+
*/
|
|
2752
|
+
sort() {
|
|
2753
|
+
Array.prototype.sort.call(this, documentOrder);
|
|
2754
|
+
return this;
|
|
2755
|
+
}
|
|
2701
2756
|
text(txt) {
|
|
2702
2757
|
if (txt === undefined) {
|
|
2703
2758
|
return this.count() ? this[0].textContent : null;
|
|
@@ -3858,7 +3913,7 @@ const ext = {
|
|
|
3858
3913
|
* Software version
|
|
3859
3914
|
* @type string
|
|
3860
3915
|
*/
|
|
3861
|
-
version: '3.0.0
|
|
3916
|
+
version: '3.0.0'
|
|
3862
3917
|
};
|
|
3863
3918
|
//
|
|
3864
3919
|
// Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts
|
|
@@ -3873,7 +3928,7 @@ Object.assign(ext, {
|
|
|
3873
3928
|
oStdClasses: ext.classes,
|
|
3874
3929
|
oPagination: ext.pager,
|
|
3875
3930
|
sVersion: ext.version,
|
|
3876
|
-
fnVersionCheck: check
|
|
3931
|
+
fnVersionCheck: check$1
|
|
3877
3932
|
});
|
|
3878
3933
|
|
|
3879
3934
|
/**
|
|
@@ -4102,7 +4157,7 @@ function arrayApply(arr, data) {
|
|
|
4102
4157
|
function listener(that, name, src) {
|
|
4103
4158
|
let srcArr = Array.isArray(src) ? src : [src];
|
|
4104
4159
|
for (var i = 0; i < srcArr.length; i++) {
|
|
4105
|
-
that.on(name + '.dt', srcArr[i]);
|
|
4160
|
+
that.on(name + '.dt.DT', srcArr[i]);
|
|
4106
4161
|
}
|
|
4107
4162
|
}
|
|
4108
4163
|
/**
|
|
@@ -4151,7 +4206,7 @@ function __mldObj(d, format, locale) {
|
|
|
4151
4206
|
var dt;
|
|
4152
4207
|
resolveWindowLibs();
|
|
4153
4208
|
if (__moment) {
|
|
4154
|
-
dt = __moment
|
|
4209
|
+
dt = __moment(d, format, locale, true);
|
|
4155
4210
|
if (!dt.isValid()) {
|
|
4156
4211
|
return null;
|
|
4157
4212
|
}
|
|
@@ -4217,10 +4272,12 @@ function __mlHelper(localeString) {
|
|
|
4217
4272
|
// gives milliseconds epoch
|
|
4218
4273
|
return d.valueOf();
|
|
4219
4274
|
}
|
|
4220
|
-
}
|
|
4221
|
-
className: 'dt-right'
|
|
4275
|
+
}
|
|
4222
4276
|
});
|
|
4223
4277
|
}
|
|
4278
|
+
if (!store.className[typeName]) {
|
|
4279
|
+
store.className[typeName] = 'dt-right';
|
|
4280
|
+
}
|
|
4224
4281
|
return function (d, type) {
|
|
4225
4282
|
// Allow for a default value
|
|
4226
4283
|
if (d === null || d === undefined) {
|
|
@@ -4254,6 +4311,15 @@ function __mlHelper(localeString) {
|
|
|
4254
4311
|
!(d instanceof Date)) {
|
|
4255
4312
|
return d;
|
|
4256
4313
|
}
|
|
4314
|
+
// Determine if there is a timezone. If there is, we want to reuse
|
|
4315
|
+
// it for the output, so the timezone doesn't change between the
|
|
4316
|
+
// input and output.
|
|
4317
|
+
let options = {};
|
|
4318
|
+
let tzMatch = typeof d === 'string' ? d.match(util.regex.isoTimezone) : null;
|
|
4319
|
+
if (tzMatch) {
|
|
4320
|
+
options.timeZone = tzMatch[1] === 'Z' ? 'UTC' : tzMatch[1];
|
|
4321
|
+
}
|
|
4322
|
+
// Get a Date object (Luxon, moment or Date)
|
|
4257
4323
|
var dt = __mldObj(d, from, locale);
|
|
4258
4324
|
if (dt === null) {
|
|
4259
4325
|
return d;
|
|
@@ -4262,7 +4328,7 @@ function __mlHelper(localeString) {
|
|
|
4262
4328
|
return dt;
|
|
4263
4329
|
}
|
|
4264
4330
|
var formatted = to === null
|
|
4265
|
-
? __mld(dt, 'toDate', 'toJSDate', '')[localeString](navigator.language,
|
|
4331
|
+
? __mld(dt, 'toDate', 'toJSDate', '')[localeString](navigator.language, options)
|
|
4266
4332
|
: __mld(dt, 'format', 'toFormat', 'toISOString', to);
|
|
4267
4333
|
// XSS protection
|
|
4268
4334
|
return type === 'display' ? util.escapeHtml(formatted) : formatted;
|
|
@@ -4312,10 +4378,12 @@ function datetime(format, locale) {
|
|
|
4312
4378
|
pre: function (d) {
|
|
4313
4379
|
return __mldObj(d, format, locale) || 0;
|
|
4314
4380
|
}
|
|
4315
|
-
}
|
|
4316
|
-
className: 'dt-right'
|
|
4381
|
+
}
|
|
4317
4382
|
});
|
|
4318
4383
|
}
|
|
4384
|
+
if (!store.className[typeName]) {
|
|
4385
|
+
store.className[typeName] = 'dt-right';
|
|
4386
|
+
}
|
|
4319
4387
|
}
|
|
4320
4388
|
/**
|
|
4321
4389
|
* Helpers for `columns.render`.
|
|
@@ -4984,6 +5052,7 @@ function invalidate(settings, rowIdx, src, colIdx) {
|
|
|
4984
5052
|
// Update DataTables special `DT_*` attributes for the row
|
|
4985
5053
|
rowAttributes(settings, row);
|
|
4986
5054
|
}
|
|
5055
|
+
callbackFire(settings, null, 'rowInvalidate', [settings, rowIdx, colIdx], false);
|
|
4987
5056
|
}
|
|
4988
5057
|
/**
|
|
4989
5058
|
* Get the cells and data for a given row - from a <tr> element
|
|
@@ -5329,9 +5398,9 @@ function getWideStrings(settings, colIdx) {
|
|
|
5329
5398
|
// Don't want script, dialog or template tags in the width
|
|
5330
5399
|
// calculations as they are hidden content
|
|
5331
5400
|
cellString = cellString
|
|
5332
|
-
.replace(/<script[\s\S]*?<\/script
|
|
5333
|
-
.replace(/<dialog[\s\S]*?<\/dialog
|
|
5334
|
-
.replace(/<template[\s\S]*?<\/template
|
|
5401
|
+
.replace(/<script[\s\S]*?<\/script(?:\s[^>]*)?>/gi, ' ')
|
|
5402
|
+
.replace(/<dialog[\s\S]*?<\/dialog(?:\s[^>]*)?>/gi, ' ')
|
|
5403
|
+
.replace(/<template[\s\S]*?<\/template(?:\s[^>]*)?>/gi, ' ');
|
|
5335
5404
|
var noHtml = util.string
|
|
5336
5405
|
.stripHtml(cellString, ' ')
|
|
5337
5406
|
.replace(/ /g, ' ');
|
|
@@ -5418,8 +5487,8 @@ function featureTable(settings) {
|
|
|
5418
5487
|
let captionSide = caption
|
|
5419
5488
|
? caption._captionSide
|
|
5420
5489
|
: null;
|
|
5421
|
-
let
|
|
5422
|
-
let
|
|
5490
|
+
let tableCloneHeader = table.clone(false);
|
|
5491
|
+
let tableCloneFooter = table.clone(false);
|
|
5423
5492
|
let footer = table.children('tfoot');
|
|
5424
5493
|
let size = function (s) {
|
|
5425
5494
|
return !s ? '100%' : stringToCss(s);
|
|
@@ -5440,11 +5509,10 @@ function featureTable(settings) {
|
|
|
5440
5509
|
* table - scroll foot table
|
|
5441
5510
|
* tfoot - tfoot
|
|
5442
5511
|
*/
|
|
5443
|
-
let scroller = Dom
|
|
5444
|
-
.c('div')
|
|
5512
|
+
let scroller = Dom.c('div')
|
|
5445
5513
|
.classAdd(classes.container)
|
|
5446
|
-
.
|
|
5447
|
-
.c('div')
|
|
5514
|
+
.attr('role', 'table')
|
|
5515
|
+
.append(Dom.c('div')
|
|
5448
5516
|
.classAdd(classes.header.self)
|
|
5449
5517
|
.css({
|
|
5450
5518
|
overflow: 'hidden',
|
|
@@ -5452,40 +5520,41 @@ function featureTable(settings) {
|
|
|
5452
5520
|
border: '0',
|
|
5453
5521
|
width: scrollX ? size(scrollX) : '100%'
|
|
5454
5522
|
})
|
|
5455
|
-
.
|
|
5456
|
-
.c('div')
|
|
5523
|
+
.attr('role', 'none')
|
|
5524
|
+
.append(Dom.c('div')
|
|
5457
5525
|
.classAdd(classes.header.inner)
|
|
5458
5526
|
.css({
|
|
5459
5527
|
'box-sizing': 'content-box',
|
|
5460
5528
|
width: scroll.xInner || '100%'
|
|
5461
5529
|
})
|
|
5462
|
-
.
|
|
5530
|
+
.attr('role', 'none')
|
|
5531
|
+
.append(tableCloneHeader
|
|
5463
5532
|
.attrRemove('id')
|
|
5464
5533
|
.css('margin-left', '0')
|
|
5465
5534
|
.append(captionSide === 'top' ? caption : null)
|
|
5466
5535
|
.append(table.children('thead')))))
|
|
5467
|
-
.append(Dom
|
|
5468
|
-
.c('div')
|
|
5536
|
+
.append(Dom.c('div')
|
|
5469
5537
|
.classAdd(classes.body)
|
|
5470
5538
|
.css({
|
|
5471
5539
|
position: 'relative',
|
|
5472
5540
|
overflow: 'auto',
|
|
5473
5541
|
width: size(scrollX)
|
|
5474
5542
|
})
|
|
5543
|
+
.attr('role', 'none')
|
|
5475
5544
|
.append(table));
|
|
5476
5545
|
if (footer.count()) {
|
|
5477
|
-
scroller.append(Dom
|
|
5478
|
-
.c('div')
|
|
5546
|
+
scroller.append(Dom.c('div')
|
|
5479
5547
|
.classAdd(classes.footer.self)
|
|
5480
5548
|
.css({
|
|
5481
5549
|
overflow: 'hidden',
|
|
5482
5550
|
border: '0',
|
|
5483
5551
|
width: scrollX ? size(scrollX) : '100%'
|
|
5484
5552
|
})
|
|
5485
|
-
.
|
|
5486
|
-
.c('div')
|
|
5553
|
+
.attr('role', 'none')
|
|
5554
|
+
.append(Dom.c('div')
|
|
5487
5555
|
.classAdd(classes.footer.inner)
|
|
5488
|
-
.
|
|
5556
|
+
.attr('role', 'none')
|
|
5557
|
+
.append(tableCloneFooter
|
|
5489
5558
|
.attrRemove('id')
|
|
5490
5559
|
.css('margin-left', '0')
|
|
5491
5560
|
.append(captionSide === 'bottom' ? caption : null)
|
|
@@ -5523,6 +5592,22 @@ function featureTable(settings) {
|
|
|
5523
5592
|
settings.scrollFoot = scrollFoot;
|
|
5524
5593
|
// On redraw - align columns
|
|
5525
5594
|
settings.callbacks.draw.push(scrollDraw);
|
|
5595
|
+
// Aria roles - because we break the table up into parts we need to be very
|
|
5596
|
+
// explicit with the roles to create the accessability tree for the table,
|
|
5597
|
+
// otherwise browser's attempt to "fix" the tree by filling in what it
|
|
5598
|
+
// thinks are gaps. The static elements that we can assign roles to are done
|
|
5599
|
+
// here. Dynamic ones are done in the draw function below.
|
|
5600
|
+
table.attr('role', 'none');
|
|
5601
|
+
table.find('tbody').attr('role', 'rowgroup');
|
|
5602
|
+
tableCloneHeader.attr('role', 'none');
|
|
5603
|
+
tableCloneFooter.attr('role', 'none');
|
|
5604
|
+
settings.colgroup.find('colgroup').attr('role', 'none');
|
|
5605
|
+
// Move the info feature's aria desc by to the new "table"
|
|
5606
|
+
let describedBy = table.attr('aria-describedby');
|
|
5607
|
+
if (describedBy) {
|
|
5608
|
+
scroller.attr('aria-describedby', describedBy);
|
|
5609
|
+
table.attrRemove('aria-describedby');
|
|
5610
|
+
}
|
|
5526
5611
|
return scroller.get(0);
|
|
5527
5612
|
}
|
|
5528
5613
|
/**
|
|
@@ -5555,10 +5640,13 @@ function scrollDraw(settings) {
|
|
|
5555
5640
|
else {
|
|
5556
5641
|
settings.scrollBarVis = scrollBarVis;
|
|
5557
5642
|
}
|
|
5643
|
+
header.find('thead').attr('role', 'rowgroup');
|
|
5644
|
+
footer.find('tfoot').attr('role', 'rowgroup');
|
|
5558
5645
|
// 1. Re-create the table inside the scrolling div
|
|
5559
5646
|
// Remove the old minimised thead and tfoot elements in the inner table
|
|
5560
5647
|
table.children('thead, tfoot').remove();
|
|
5561
|
-
// Clone the current header and footer elements and then place it into the
|
|
5648
|
+
// Clone the current header and footer elements and then place it into the
|
|
5649
|
+
// inner table
|
|
5562
5650
|
headerCopy = header.clone(true).prependTo(table);
|
|
5563
5651
|
headerCopy.find('th, td').attrRemove('tabindex');
|
|
5564
5652
|
headerCopy.find('[id]').attrRemove('id');
|
|
@@ -5593,8 +5681,7 @@ function scrollDraw(settings) {
|
|
|
5593
5681
|
}
|
|
5594
5682
|
}
|
|
5595
5683
|
if (firstTr) {
|
|
5596
|
-
let colSizes = Dom
|
|
5597
|
-
.s(firstTr)
|
|
5684
|
+
let colSizes = Dom.s(firstTr)
|
|
5598
5685
|
.children('th, td')
|
|
5599
5686
|
.mapTo(function (cell, idx) {
|
|
5600
5687
|
return {
|
|
@@ -5659,6 +5746,18 @@ function scrollDraw(settings) {
|
|
|
5659
5746
|
}
|
|
5660
5747
|
// Correct DOM ordering for colgroup - comes before the thead
|
|
5661
5748
|
table.children('colgroup').prependTo(table);
|
|
5749
|
+
// Remove tabindex from the hidden row elements
|
|
5750
|
+
table.find('thead, tfoot').find('[tabindex]').attrRemove('tabindex');
|
|
5751
|
+
// Dynamic ARIA roles - see setup for details on why this is needed
|
|
5752
|
+
table
|
|
5753
|
+
.find('thead, tfoot')
|
|
5754
|
+
.attr('role', 'none')
|
|
5755
|
+
.find('[role]')
|
|
5756
|
+
.attrRemove('role');
|
|
5757
|
+
table.find('tbody tr:not([role])').attr('role', 'row');
|
|
5758
|
+
table.find('tbody td:not([role]), tbody th:not([role])').attr('role', 'cell');
|
|
5759
|
+
scrollAria(headerCopy);
|
|
5760
|
+
scrollAria(footerCopy);
|
|
5662
5761
|
// Adjust the position of the header in case we loose the y-scrollbar
|
|
5663
5762
|
divBody.trigger('scroll');
|
|
5664
5763
|
// If sorting or filtering has occurred, jump the scrolling back to the top
|
|
@@ -5667,6 +5766,18 @@ function scrollDraw(settings) {
|
|
|
5667
5766
|
divBodyEl.scrollTop(0);
|
|
5668
5767
|
}
|
|
5669
5768
|
}
|
|
5769
|
+
/**
|
|
5770
|
+
* Apply ARIA roles for the header / footer of a scrolling table
|
|
5771
|
+
* @param element
|
|
5772
|
+
*/
|
|
5773
|
+
function scrollAria(element) {
|
|
5774
|
+
if (element) {
|
|
5775
|
+
element.find('tfoot:not([role])').attr('role', 'rowgroup');
|
|
5776
|
+
element.find('tr:not([role])').attr('role', 'row');
|
|
5777
|
+
element.find('th:not([role])').attr('role', 'columnheader');
|
|
5778
|
+
element.find('td:not([role])').attr('role', 'cell');
|
|
5779
|
+
}
|
|
5780
|
+
}
|
|
5670
5781
|
|
|
5671
5782
|
/**
|
|
5672
5783
|
* Add a column to the list used for the table with default values
|
|
@@ -5890,6 +6001,7 @@ function columnTypes(settings) {
|
|
|
5890
6001
|
var types = ext.type.detect;
|
|
5891
6002
|
var i, iLen, j, jen, k, ken;
|
|
5892
6003
|
var col, detectedType, cache;
|
|
6004
|
+
var originalTypes = columns.map(c => c.type).join(',');
|
|
5893
6005
|
// For each column, spin over the data type detection functions, seeing if
|
|
5894
6006
|
// one matches
|
|
5895
6007
|
for (i = 0, iLen = columns.length; i < iLen; i++) {
|
|
@@ -5987,6 +6099,10 @@ function columnTypes(settings) {
|
|
|
5987
6099
|
_columnAutoRender(settings, i);
|
|
5988
6100
|
}
|
|
5989
6101
|
}
|
|
6102
|
+
var newTypes = columns.map(c => c.type).join(',');
|
|
6103
|
+
if (newTypes !== originalTypes) {
|
|
6104
|
+
callbackFire(settings, null, 'columnTypes', [settings], false);
|
|
6105
|
+
}
|
|
5990
6106
|
}
|
|
5991
6107
|
/**
|
|
5992
6108
|
* Apply an auto detected renderer to data which doesn't yet have a renderer
|
|
@@ -7188,13 +7304,13 @@ function implementState(settings, s, callback) {
|
|
|
7188
7304
|
let idx = currentNames.indexOf(col[0]);
|
|
7189
7305
|
if (idx < 0) {
|
|
7190
7306
|
// If the column was not found ignore it and continue
|
|
7191
|
-
|
|
7307
|
+
continue;
|
|
7192
7308
|
}
|
|
7193
7309
|
set[0] = idx;
|
|
7194
7310
|
}
|
|
7195
7311
|
else if (set[0] >= columns.length) {
|
|
7196
7312
|
// If the column index is out of bounds ignore it and continue
|
|
7197
|
-
|
|
7313
|
+
continue;
|
|
7198
7314
|
}
|
|
7199
7315
|
settings.order.push(set);
|
|
7200
7316
|
}
|
|
@@ -8331,6 +8447,11 @@ function reDraw(settings, holdPosition, recompute) {
|
|
|
8331
8447
|
if (holdPosition !== true) {
|
|
8332
8448
|
settings.displayStart = 0;
|
|
8333
8449
|
}
|
|
8450
|
+
else {
|
|
8451
|
+
// Keep position, but make sure that there is actually data to display,
|
|
8452
|
+
// otherwise we need to rewind a bit (e.g. if rows were deleted)
|
|
8453
|
+
lengthOverflow(settings);
|
|
8454
|
+
}
|
|
8334
8455
|
// Let any modules know about the draw hold position state (used by
|
|
8335
8456
|
// scrolling internally)
|
|
8336
8457
|
settings.drawHold = holdPosition;
|
|
@@ -9507,12 +9628,13 @@ function selectCells(settings, selector, opts) {
|
|
|
9507
9628
|
// Otherwise the selector is a node, and there is one last option - the
|
|
9508
9629
|
// element might be a child of an element which has dt-row and dt-column
|
|
9509
9630
|
// data attributes
|
|
9510
|
-
|
|
9511
|
-
|
|
9631
|
+
let rowHost = Dom.s(s).closest('*[data-dt-row]');
|
|
9632
|
+
let columnHost = Dom.s(s).closest('*[data-dt-column]');
|
|
9633
|
+
return rowHost.count()
|
|
9512
9634
|
? [
|
|
9513
9635
|
{
|
|
9514
|
-
row:
|
|
9515
|
-
column:
|
|
9636
|
+
row: parseInt(rowHost.attr('data-dt-row')),
|
|
9637
|
+
column: parseInt(columnHost.attr('data-dt-column'))
|
|
9516
9638
|
}
|
|
9517
9639
|
]
|
|
9518
9640
|
: [];
|
|
@@ -9825,7 +9947,7 @@ function selectColumns(settings, selector, opts) {
|
|
|
9825
9947
|
// Otherwise a node which might have a `dt-column` data attribute, or be
|
|
9826
9948
|
// a child or such an element
|
|
9827
9949
|
var host = Dom.s(s).closest('*[data-dt-column]');
|
|
9828
|
-
return host.count() ? [host.
|
|
9950
|
+
return host.count() ? [parseInt(host.attr('data-dt-column'))] : [];
|
|
9829
9951
|
};
|
|
9830
9952
|
var selected = selectorRun('column', selector, run, settings, opts);
|
|
9831
9953
|
return opts.columnOrder && opts.columnOrder === 'index'
|
|
@@ -10022,18 +10144,19 @@ registerPlural('columns().widths()', 'column().width()', function () {
|
|
|
10022
10144
|
// Injects a fake row into the table for just a moment so the widths can
|
|
10023
10145
|
// be read, regardless of colspan in the header and rows being present
|
|
10024
10146
|
// in the body
|
|
10025
|
-
var columns = this.columns(':visible')
|
|
10147
|
+
var columns = this.columns(':visible');
|
|
10026
10148
|
var row = Dom
|
|
10027
10149
|
.c('tr')
|
|
10028
|
-
.html('<td>' + Array(columns).join('</td><td>') + '</td>');
|
|
10150
|
+
.html('<td>' + Array(columns.count()).join('</td><td>') + '</td>');
|
|
10029
10151
|
Dom.s(this.table().body()).append(row);
|
|
10030
|
-
var widths =
|
|
10031
|
-
|
|
10152
|
+
var widths = [];
|
|
10153
|
+
var indexes = columns.indexes();
|
|
10154
|
+
row.children().each((el, idx) => {
|
|
10155
|
+
widths[indexes[idx]] = Dom.s(el).width('outer');
|
|
10032
10156
|
});
|
|
10033
10157
|
row.remove();
|
|
10034
|
-
return this.iterator('column',
|
|
10035
|
-
|
|
10036
|
-
return visIdx !== null ? widths[visIdx] : 0;
|
|
10158
|
+
return this.iterator('column', (settings, column) => {
|
|
10159
|
+
return widths[column] || 0;
|
|
10037
10160
|
}, true);
|
|
10038
10161
|
});
|
|
10039
10162
|
registerPlural('columns().indexes()', 'column().index()', function (type) {
|
|
@@ -10505,7 +10628,7 @@ function selectRows(settings, selector, opts) {
|
|
|
10505
10628
|
}
|
|
10506
10629
|
else {
|
|
10507
10630
|
var host = Dom.s(sel).closest('*[data-dt-row]');
|
|
10508
|
-
return host.count() ? [host.
|
|
10631
|
+
return host.count() ? [parseInt(host.attr('data-dt-row'))] : [];
|
|
10509
10632
|
}
|
|
10510
10633
|
}
|
|
10511
10634
|
// ID selector. Want to always be able to select rows by id, regardless
|
|
@@ -10537,8 +10660,7 @@ function selectRows(settings, selector, opts) {
|
|
|
10537
10660
|
// Get nodes in the order from the `rows` array with null values removed
|
|
10538
10661
|
var nodes = util.array.removeEmpty(util.array.pluckOrder(settings.data, rows, 'tr'));
|
|
10539
10662
|
// Selector - selector string, array of nodes or jQuery object.
|
|
10540
|
-
return Dom
|
|
10541
|
-
.s(nodes)
|
|
10663
|
+
return Dom.s(nodes)
|
|
10542
10664
|
.filter(sel)
|
|
10543
10665
|
.mapTo((el) => el._DT_RowIndex);
|
|
10544
10666
|
};
|
|
@@ -10683,7 +10805,7 @@ register('row().data()', function (data) {
|
|
|
10683
10805
|
util.set(ctx[0].rowId)(data, row.tr.id);
|
|
10684
10806
|
}
|
|
10685
10807
|
// Automatically invalidate
|
|
10686
|
-
invalidate(ctx[0], this[0], 'data');
|
|
10808
|
+
invalidate(ctx[0], this[0][0], 'data');
|
|
10687
10809
|
return this;
|
|
10688
10810
|
});
|
|
10689
10811
|
register('row().node()', function () {
|
|
@@ -11001,10 +11123,10 @@ register('caption()', function (value, side) {
|
|
|
11001
11123
|
caption.css('caption-side', side);
|
|
11002
11124
|
caption.get(0)._captionSide = side;
|
|
11003
11125
|
}
|
|
11004
|
-
if (container.find('div.
|
|
11005
|
-
var selector = side === 'top' ? '
|
|
11126
|
+
if (container.find('div.dt-scroll').count()) {
|
|
11127
|
+
var selector = side === 'top' ? 'head' : 'foot';
|
|
11006
11128
|
container
|
|
11007
|
-
.find('div.
|
|
11129
|
+
.find('div.dt-scroll-' + selector + ' table')
|
|
11008
11130
|
.prepend(caption);
|
|
11009
11131
|
}
|
|
11010
11132
|
else {
|
|
@@ -11017,6 +11139,272 @@ register('caption.node()', function () {
|
|
|
11017
11139
|
return ctx.length ? ctx[0].captionNode : null;
|
|
11018
11140
|
});
|
|
11019
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 »');
|
|
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
|
+
|
|
11020
11408
|
/**
|
|
11021
11409
|
* CommonJS factory function pass through. This will check if the arguments
|
|
11022
11410
|
* given are a window object or a jQuery object. If so they are set accordingly.
|
|
@@ -11376,8 +11764,7 @@ register$2('pageLength', function (settings, optsIn) {
|
|
|
11376
11764
|
}
|
|
11377
11765
|
// Wrapper element - use a span as a holder for where the select will go
|
|
11378
11766
|
var tmpId = 'tmp-' + +new Date();
|
|
11379
|
-
var div = Dom
|
|
11380
|
-
.c('div')
|
|
11767
|
+
var div = Dom.c('div')
|
|
11381
11768
|
.classAdd(classes.container)
|
|
11382
11769
|
.html(str.replace('_MENU_', '<span id="' + tmpId + '"></span>'));
|
|
11383
11770
|
// Save text node content for macro updating
|
|
@@ -11399,9 +11786,9 @@ register$2('pageLength', function (settings, optsIn) {
|
|
|
11399
11786
|
});
|
|
11400
11787
|
};
|
|
11401
11788
|
// Next, the select itself, along with the options
|
|
11402
|
-
var select = Dom
|
|
11403
|
-
.c('select')
|
|
11789
|
+
var select = Dom.c('select')
|
|
11404
11790
|
.attr('aria-controls', tableId)
|
|
11791
|
+
.attr('autocomplete', 'off')
|
|
11405
11792
|
.classAdd(classes.select);
|
|
11406
11793
|
for (i = 0; i < lengths.length; i++) {
|
|
11407
11794
|
// Attempt to look up the length from the i18n options
|
|
@@ -11432,7 +11819,26 @@ register$2('pageLength', function (settings, optsIn) {
|
|
|
11432
11819
|
// Update node value whenever anything changes the table's length
|
|
11433
11820
|
Dom.s(settings.table).on('length.dt.DT', function (e, s, len) {
|
|
11434
11821
|
if (settings === s) {
|
|
11435
|
-
div.find('select')
|
|
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);
|
|
11436
11842
|
// Resolve plurals in the text for the new length
|
|
11437
11843
|
updateEntries(len);
|
|
11438
11844
|
}
|
|
@@ -11440,6 +11846,19 @@ register$2('pageLength', function (settings, optsIn) {
|
|
|
11440
11846
|
updateEntries(settings.pageLength);
|
|
11441
11847
|
return div;
|
|
11442
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
|
+
}
|
|
11443
11862
|
|
|
11444
11863
|
let __searchCounter = 0;
|
|
11445
11864
|
register$2('search', function (settings, optsIn) {
|
|
@@ -11450,7 +11869,9 @@ register$2('search', function (settings, optsIn) {
|
|
|
11450
11869
|
let classes = settings.classes.search;
|
|
11451
11870
|
let tableId = settings.tableId;
|
|
11452
11871
|
let language = settings.language;
|
|
11453
|
-
let input = '<input type="search" class="' +
|
|
11872
|
+
let input = '<input type="search" class="' +
|
|
11873
|
+
classes.input +
|
|
11874
|
+
'" autocomplete="off"/>';
|
|
11454
11875
|
let opts = util.object.assignDeep({
|
|
11455
11876
|
columns: '*',
|
|
11456
11877
|
placeholder: language.searchPlaceholder,
|
|
@@ -12272,6 +12693,8 @@ DataTable.datetime = datetime;
|
|
|
12272
12693
|
DataTable.__browser = browser;
|
|
12273
12694
|
DataTable.Dom = Dom;
|
|
12274
12695
|
DataTable.ajax = util.ajax;
|
|
12696
|
+
DataTable.key = key;
|
|
12697
|
+
plus(DataTable);
|
|
12275
12698
|
/**
|
|
12276
12699
|
* Private data store, containing all of the settings objects that are created
|
|
12277
12700
|
* for the tables on a given page.
|