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.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! DataTables
|
|
1
|
+
/*! DataTables
|
|
2
2
|
* Copyright (c) SpryMedia Ltd - datatables.net/license
|
|
3
3
|
*/
|
|
4
4
|
|
|
@@ -46,9 +46,11 @@ const reRegexCharacters = new RegExp('(\\' +
|
|
|
46
46
|
// implementations differ between browsers.
|
|
47
47
|
const reDate = /^\d{2,4}[./-]\d{1,2}[./-]\d{1,2}([T ]{1}\d{1,2}[:.]\d{2}([.:]\d{2})?)?$/;
|
|
48
48
|
const reNewLines = /[\r\n\u2028]/g;
|
|
49
|
+
const isoTimezone = /[T\s]\d{2}.*?(Z|[+-]\d{2}(?::?\d{2})?)$/;
|
|
49
50
|
|
|
50
51
|
var regex = /*#__PURE__*/Object.freeze({
|
|
51
52
|
__proto__: null,
|
|
53
|
+
isoTimezone: isoTimezone,
|
|
52
54
|
reDate: reDate,
|
|
53
55
|
reFormattedNumeric: reFormattedNumeric,
|
|
54
56
|
reHtml: reHtml,
|
|
@@ -520,17 +522,29 @@ function ajax(optionsIn) {
|
|
|
520
522
|
if (options.contentType && !(options.data instanceof FormData)) {
|
|
521
523
|
xhr.setRequestHeader('Content-Type', options.contentType);
|
|
522
524
|
}
|
|
525
|
+
// Add a X-Request-With header, as jQuery does so and some server-side
|
|
526
|
+
// platforms look for it. Only for same domain though.
|
|
527
|
+
if (options.headers &&
|
|
528
|
+
!options.headers['X-Requested-With'] &&
|
|
529
|
+
!isCrossDomain(options.url)) {
|
|
530
|
+
options.headers['X-Requested-With'] = 'XMLHttpRequest';
|
|
531
|
+
}
|
|
523
532
|
each(options.headers, (key, val) => {
|
|
524
533
|
xhr.setRequestHeader(key, val);
|
|
525
534
|
});
|
|
526
535
|
if (options.data instanceof FormData) {
|
|
527
536
|
sendData = options.data;
|
|
528
537
|
}
|
|
529
|
-
else if (method !== 'GET' && options.data
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
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
|
+
}
|
|
534
548
|
}
|
|
535
549
|
xhr.onreadystatechange = function () {
|
|
536
550
|
if (xhr.readyState != 4) {
|
|
@@ -580,6 +594,7 @@ function ajax(optionsIn) {
|
|
|
580
594
|
if (options.beforeSend) {
|
|
581
595
|
if (options.beforeSend.call(options, xhr, options) === false) {
|
|
582
596
|
xhr.abort();
|
|
597
|
+
return xhr;
|
|
583
598
|
}
|
|
584
599
|
}
|
|
585
600
|
xhr.send(sendData);
|
|
@@ -617,6 +632,17 @@ function convertSpaces(sendData, options) {
|
|
|
617
632
|
? sendData.replace(/%20/g, '+')
|
|
618
633
|
: sendData;
|
|
619
634
|
}
|
|
635
|
+
/**
|
|
636
|
+
* Determine if a url is a cross domain request or not
|
|
637
|
+
*
|
|
638
|
+
* @param url URL to check
|
|
639
|
+
* @returns True if cross domain, false otherwise
|
|
640
|
+
*/
|
|
641
|
+
function isCrossDomain(url) {
|
|
642
|
+
// Use the current page as the base to handle relative URLs correctly
|
|
643
|
+
const target = new URL(url, window.location.origin);
|
|
644
|
+
return target.origin !== window.location.origin;
|
|
645
|
+
}
|
|
620
646
|
/**
|
|
621
647
|
* Get the HTTP method from the Ajax request options
|
|
622
648
|
*
|
|
@@ -755,7 +781,7 @@ function allUnique(src) {
|
|
|
755
781
|
* @returns Flattened array
|
|
756
782
|
*/
|
|
757
783
|
function flatten(out, val) {
|
|
758
|
-
if (Array.isArray(val)) {
|
|
784
|
+
if (Array.isArray(val) || arrayLike(val)) {
|
|
759
785
|
for (var i = 0; i < val.length; i++) {
|
|
760
786
|
flatten(out, val[i]);
|
|
761
787
|
}
|
|
@@ -1298,7 +1324,7 @@ var timer = /*#__PURE__*/Object.freeze({
|
|
|
1298
1324
|
* @returns true if this version of DataTables is greater or equal to the
|
|
1299
1325
|
* required version, or false if this version of DataTales is not suitable
|
|
1300
1326
|
*/
|
|
1301
|
-
function check(version1, version2) {
|
|
1327
|
+
function check$1(version1, version2) {
|
|
1302
1328
|
let dt = external('datatable');
|
|
1303
1329
|
var parts1 = version2 ? version2.split('.') : dt.ext.version.split('.');
|
|
1304
1330
|
var parts2 = version1.split('.');
|
|
@@ -1318,7 +1344,7 @@ function check(version1, version2) {
|
|
|
1318
1344
|
|
|
1319
1345
|
var version = /*#__PURE__*/Object.freeze({
|
|
1320
1346
|
__proto__: null,
|
|
1321
|
-
check: check
|
|
1347
|
+
check: check$1
|
|
1322
1348
|
});
|
|
1323
1349
|
|
|
1324
1350
|
// Note that the aliased properties are for compatibility with DataTables 2-
|
|
@@ -1536,9 +1562,12 @@ function parseEventName(original) {
|
|
|
1536
1562
|
isFocus = true;
|
|
1537
1563
|
}
|
|
1538
1564
|
else if (name === 'blur') {
|
|
1539
|
-
name = '
|
|
1565
|
+
name = 'focusout';
|
|
1540
1566
|
isFocus = true;
|
|
1541
1567
|
}
|
|
1568
|
+
else if (name === 'ready') {
|
|
1569
|
+
name = 'DOMContentLoaded';
|
|
1570
|
+
}
|
|
1542
1571
|
return {
|
|
1543
1572
|
eventName: name,
|
|
1544
1573
|
isFocus,
|
|
@@ -1575,6 +1604,14 @@ function add(el, nameFull, handler, selector, one) {
|
|
|
1575
1604
|
if (!eventName) {
|
|
1576
1605
|
return;
|
|
1577
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
|
+
}
|
|
1578
1615
|
// Create a function that will be the actual event handler, and performs any
|
|
1579
1616
|
// logic we need, such as delegate handling and adding properties.
|
|
1580
1617
|
let wrapped = function (event) {
|
|
@@ -1600,6 +1637,11 @@ function add(el, nameFull, handler, selector, one) {
|
|
|
1600
1637
|
if (!dTarget) {
|
|
1601
1638
|
return;
|
|
1602
1639
|
}
|
|
1640
|
+
if (isHover &&
|
|
1641
|
+
event.relatedTarget &&
|
|
1642
|
+
dTarget.contains(event.relatedTarget)) {
|
|
1643
|
+
return;
|
|
1644
|
+
}
|
|
1603
1645
|
callScope = dTarget;
|
|
1604
1646
|
}
|
|
1605
1647
|
// Set the properties that jQuery adds to the event object
|
|
@@ -1657,7 +1699,7 @@ function remove(el, nameFull, handler, selector) {
|
|
|
1657
1699
|
wrapped.delegateSelector === selector &&
|
|
1658
1700
|
wrapped.original === handler);
|
|
1659
1701
|
}
|
|
1660
|
-
if (eventName && selector) {
|
|
1702
|
+
else if (eventName && selector) {
|
|
1661
1703
|
removeEvents = stored.filter(wrapped => wrapped.type === eventName &&
|
|
1662
1704
|
wrapped.delegateSelector === selector);
|
|
1663
1705
|
}
|
|
@@ -1872,8 +1914,7 @@ class Dom {
|
|
|
1872
1914
|
}
|
|
1873
1915
|
}
|
|
1874
1916
|
if (sort) {
|
|
1875
|
-
|
|
1876
|
-
// this.sort(documentOrder);
|
|
1917
|
+
this.sort();
|
|
1877
1918
|
}
|
|
1878
1919
|
return this;
|
|
1879
1920
|
}
|
|
@@ -1889,29 +1930,33 @@ class Dom {
|
|
|
1889
1930
|
if (!content) {
|
|
1890
1931
|
return this;
|
|
1891
1932
|
}
|
|
1892
|
-
if (
|
|
1893
|
-
content
|
|
1894
|
-
return this;
|
|
1933
|
+
if (!arrayLike(content)) {
|
|
1934
|
+
content = [content];
|
|
1895
1935
|
}
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
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
|
+
}
|
|
1910
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]);
|
|
1911
1958
|
}
|
|
1912
|
-
|
|
1913
|
-
el.append(content);
|
|
1914
|
-
}
|
|
1959
|
+
el.append(fragment);
|
|
1915
1960
|
});
|
|
1916
1961
|
}
|
|
1917
1962
|
/**
|
|
@@ -2078,9 +2123,7 @@ class Dom {
|
|
|
2078
2123
|
css(rule, value) {
|
|
2079
2124
|
// String getter
|
|
2080
2125
|
if (typeof rule === 'string' && value === undefined) {
|
|
2081
|
-
return this.length
|
|
2082
|
-
? getComputedStyle(this[0])[rule]
|
|
2083
|
-
: null;
|
|
2126
|
+
return this.length ? getComputedStyle(this[0])[rule] : null;
|
|
2084
2127
|
}
|
|
2085
2128
|
return this.each(el => {
|
|
2086
2129
|
if (typeof rule === 'string') {
|
|
@@ -2108,11 +2151,11 @@ class Dom {
|
|
|
2108
2151
|
return this.length ? dataConvert(this[0].dataset[name]) : null;
|
|
2109
2152
|
}
|
|
2110
2153
|
if (typeof name === 'string') {
|
|
2111
|
-
this.each(el => el.dataset[name] = JSON.stringify(value));
|
|
2154
|
+
this.each(el => (el.dataset[name] = JSON.stringify(value)));
|
|
2112
2155
|
}
|
|
2113
2156
|
else {
|
|
2114
2157
|
each(name, (key, val) => {
|
|
2115
|
-
this.each(el => el.dataset[key] = JSON.stringify(val));
|
|
2158
|
+
this.each(el => (el.dataset[key] = JSON.stringify(val)));
|
|
2116
2159
|
});
|
|
2117
2160
|
}
|
|
2118
2161
|
return this;
|
|
@@ -2664,6 +2707,18 @@ class Dom {
|
|
|
2664
2707
|
el.style.display = 'block';
|
|
2665
2708
|
});
|
|
2666
2709
|
}
|
|
2710
|
+
/**
|
|
2711
|
+
* Sort the DOM elements into document order.
|
|
2712
|
+
*
|
|
2713
|
+
* This is normally not needed as elements selected with a DOM selector are
|
|
2714
|
+
* automatically sorted in document order. However, in the case of elements
|
|
2715
|
+
* being added as an array, their order will be retained. In such as case
|
|
2716
|
+
* you might wish to sort them in document order.
|
|
2717
|
+
*/
|
|
2718
|
+
sort() {
|
|
2719
|
+
Array.prototype.sort.call(this, documentOrder);
|
|
2720
|
+
return this;
|
|
2721
|
+
}
|
|
2667
2722
|
text(txt) {
|
|
2668
2723
|
if (txt === undefined) {
|
|
2669
2724
|
return this.count() ? this[0].textContent : null;
|
|
@@ -3824,7 +3879,7 @@ const ext = {
|
|
|
3824
3879
|
* Software version
|
|
3825
3880
|
* @type string
|
|
3826
3881
|
*/
|
|
3827
|
-
version: '3.0.0
|
|
3882
|
+
version: '3.0.0'
|
|
3828
3883
|
};
|
|
3829
3884
|
//
|
|
3830
3885
|
// Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts
|
|
@@ -3839,7 +3894,7 @@ Object.assign(ext, {
|
|
|
3839
3894
|
oStdClasses: ext.classes,
|
|
3840
3895
|
oPagination: ext.pager,
|
|
3841
3896
|
sVersion: ext.version,
|
|
3842
|
-
fnVersionCheck: check
|
|
3897
|
+
fnVersionCheck: check$1
|
|
3843
3898
|
});
|
|
3844
3899
|
|
|
3845
3900
|
/**
|
|
@@ -4068,7 +4123,7 @@ function arrayApply(arr, data) {
|
|
|
4068
4123
|
function listener(that, name, src) {
|
|
4069
4124
|
let srcArr = Array.isArray(src) ? src : [src];
|
|
4070
4125
|
for (var i = 0; i < srcArr.length; i++) {
|
|
4071
|
-
that.on(name + '.dt', srcArr[i]);
|
|
4126
|
+
that.on(name + '.dt.DT', srcArr[i]);
|
|
4072
4127
|
}
|
|
4073
4128
|
}
|
|
4074
4129
|
/**
|
|
@@ -4117,7 +4172,7 @@ function __mldObj(d, format, locale) {
|
|
|
4117
4172
|
var dt;
|
|
4118
4173
|
resolveWindowLibs();
|
|
4119
4174
|
if (__moment) {
|
|
4120
|
-
dt = __moment
|
|
4175
|
+
dt = __moment(d, format, locale, true);
|
|
4121
4176
|
if (!dt.isValid()) {
|
|
4122
4177
|
return null;
|
|
4123
4178
|
}
|
|
@@ -4183,10 +4238,12 @@ function __mlHelper(localeString) {
|
|
|
4183
4238
|
// gives milliseconds epoch
|
|
4184
4239
|
return d.valueOf();
|
|
4185
4240
|
}
|
|
4186
|
-
}
|
|
4187
|
-
className: 'dt-right'
|
|
4241
|
+
}
|
|
4188
4242
|
});
|
|
4189
4243
|
}
|
|
4244
|
+
if (!store.className[typeName]) {
|
|
4245
|
+
store.className[typeName] = 'dt-right';
|
|
4246
|
+
}
|
|
4190
4247
|
return function (d, type) {
|
|
4191
4248
|
// Allow for a default value
|
|
4192
4249
|
if (d === null || d === undefined) {
|
|
@@ -4220,6 +4277,15 @@ function __mlHelper(localeString) {
|
|
|
4220
4277
|
!(d instanceof Date)) {
|
|
4221
4278
|
return d;
|
|
4222
4279
|
}
|
|
4280
|
+
// Determine if there is a timezone. If there is, we want to reuse
|
|
4281
|
+
// it for the output, so the timezone doesn't change between the
|
|
4282
|
+
// input and output.
|
|
4283
|
+
let options = {};
|
|
4284
|
+
let tzMatch = typeof d === 'string' ? d.match(util.regex.isoTimezone) : null;
|
|
4285
|
+
if (tzMatch) {
|
|
4286
|
+
options.timeZone = tzMatch[1] === 'Z' ? 'UTC' : tzMatch[1];
|
|
4287
|
+
}
|
|
4288
|
+
// Get a Date object (Luxon, moment or Date)
|
|
4223
4289
|
var dt = __mldObj(d, from, locale);
|
|
4224
4290
|
if (dt === null) {
|
|
4225
4291
|
return d;
|
|
@@ -4228,7 +4294,7 @@ function __mlHelper(localeString) {
|
|
|
4228
4294
|
return dt;
|
|
4229
4295
|
}
|
|
4230
4296
|
var formatted = to === null
|
|
4231
|
-
? __mld(dt, 'toDate', 'toJSDate', '')[localeString](navigator.language,
|
|
4297
|
+
? __mld(dt, 'toDate', 'toJSDate', '')[localeString](navigator.language, options)
|
|
4232
4298
|
: __mld(dt, 'format', 'toFormat', 'toISOString', to);
|
|
4233
4299
|
// XSS protection
|
|
4234
4300
|
return type === 'display' ? util.escapeHtml(formatted) : formatted;
|
|
@@ -4278,10 +4344,12 @@ function datetime(format, locale) {
|
|
|
4278
4344
|
pre: function (d) {
|
|
4279
4345
|
return __mldObj(d, format, locale) || 0;
|
|
4280
4346
|
}
|
|
4281
|
-
}
|
|
4282
|
-
className: 'dt-right'
|
|
4347
|
+
}
|
|
4283
4348
|
});
|
|
4284
4349
|
}
|
|
4350
|
+
if (!store.className[typeName]) {
|
|
4351
|
+
store.className[typeName] = 'dt-right';
|
|
4352
|
+
}
|
|
4285
4353
|
}
|
|
4286
4354
|
/**
|
|
4287
4355
|
* Helpers for `columns.render`.
|
|
@@ -4950,6 +5018,7 @@ function invalidate(settings, rowIdx, src, colIdx) {
|
|
|
4950
5018
|
// Update DataTables special `DT_*` attributes for the row
|
|
4951
5019
|
rowAttributes(settings, row);
|
|
4952
5020
|
}
|
|
5021
|
+
callbackFire(settings, null, 'rowInvalidate', [settings, rowIdx, colIdx], false);
|
|
4953
5022
|
}
|
|
4954
5023
|
/**
|
|
4955
5024
|
* Get the cells and data for a given row - from a <tr> element
|
|
@@ -5295,9 +5364,9 @@ function getWideStrings(settings, colIdx) {
|
|
|
5295
5364
|
// Don't want script, dialog or template tags in the width
|
|
5296
5365
|
// calculations as they are hidden content
|
|
5297
5366
|
cellString = cellString
|
|
5298
|
-
.replace(/<script[\s\S]*?<\/script
|
|
5299
|
-
.replace(/<dialog[\s\S]*?<\/dialog
|
|
5300
|
-
.replace(/<template[\s\S]*?<\/template
|
|
5367
|
+
.replace(/<script[\s\S]*?<\/script(?:\s[^>]*)?>/gi, ' ')
|
|
5368
|
+
.replace(/<dialog[\s\S]*?<\/dialog(?:\s[^>]*)?>/gi, ' ')
|
|
5369
|
+
.replace(/<template[\s\S]*?<\/template(?:\s[^>]*)?>/gi, ' ');
|
|
5301
5370
|
var noHtml = util.string
|
|
5302
5371
|
.stripHtml(cellString, ' ')
|
|
5303
5372
|
.replace(/ /g, ' ');
|
|
@@ -5384,8 +5453,8 @@ function featureTable(settings) {
|
|
|
5384
5453
|
let captionSide = caption
|
|
5385
5454
|
? caption._captionSide
|
|
5386
5455
|
: null;
|
|
5387
|
-
let
|
|
5388
|
-
let
|
|
5456
|
+
let tableCloneHeader = table.clone(false);
|
|
5457
|
+
let tableCloneFooter = table.clone(false);
|
|
5389
5458
|
let footer = table.children('tfoot');
|
|
5390
5459
|
let size = function (s) {
|
|
5391
5460
|
return !s ? '100%' : stringToCss(s);
|
|
@@ -5406,11 +5475,10 @@ function featureTable(settings) {
|
|
|
5406
5475
|
* table - scroll foot table
|
|
5407
5476
|
* tfoot - tfoot
|
|
5408
5477
|
*/
|
|
5409
|
-
let scroller = Dom
|
|
5410
|
-
.c('div')
|
|
5478
|
+
let scroller = Dom.c('div')
|
|
5411
5479
|
.classAdd(classes.container)
|
|
5412
|
-
.
|
|
5413
|
-
.c('div')
|
|
5480
|
+
.attr('role', 'table')
|
|
5481
|
+
.append(Dom.c('div')
|
|
5414
5482
|
.classAdd(classes.header.self)
|
|
5415
5483
|
.css({
|
|
5416
5484
|
overflow: 'hidden',
|
|
@@ -5418,40 +5486,41 @@ function featureTable(settings) {
|
|
|
5418
5486
|
border: '0',
|
|
5419
5487
|
width: scrollX ? size(scrollX) : '100%'
|
|
5420
5488
|
})
|
|
5421
|
-
.
|
|
5422
|
-
.c('div')
|
|
5489
|
+
.attr('role', 'none')
|
|
5490
|
+
.append(Dom.c('div')
|
|
5423
5491
|
.classAdd(classes.header.inner)
|
|
5424
5492
|
.css({
|
|
5425
5493
|
'box-sizing': 'content-box',
|
|
5426
5494
|
width: scroll.xInner || '100%'
|
|
5427
5495
|
})
|
|
5428
|
-
.
|
|
5496
|
+
.attr('role', 'none')
|
|
5497
|
+
.append(tableCloneHeader
|
|
5429
5498
|
.attrRemove('id')
|
|
5430
5499
|
.css('margin-left', '0')
|
|
5431
5500
|
.append(captionSide === 'top' ? caption : null)
|
|
5432
5501
|
.append(table.children('thead')))))
|
|
5433
|
-
.append(Dom
|
|
5434
|
-
.c('div')
|
|
5502
|
+
.append(Dom.c('div')
|
|
5435
5503
|
.classAdd(classes.body)
|
|
5436
5504
|
.css({
|
|
5437
5505
|
position: 'relative',
|
|
5438
5506
|
overflow: 'auto',
|
|
5439
5507
|
width: size(scrollX)
|
|
5440
5508
|
})
|
|
5509
|
+
.attr('role', 'none')
|
|
5441
5510
|
.append(table));
|
|
5442
5511
|
if (footer.count()) {
|
|
5443
|
-
scroller.append(Dom
|
|
5444
|
-
.c('div')
|
|
5512
|
+
scroller.append(Dom.c('div')
|
|
5445
5513
|
.classAdd(classes.footer.self)
|
|
5446
5514
|
.css({
|
|
5447
5515
|
overflow: 'hidden',
|
|
5448
5516
|
border: '0',
|
|
5449
5517
|
width: scrollX ? size(scrollX) : '100%'
|
|
5450
5518
|
})
|
|
5451
|
-
.
|
|
5452
|
-
.c('div')
|
|
5519
|
+
.attr('role', 'none')
|
|
5520
|
+
.append(Dom.c('div')
|
|
5453
5521
|
.classAdd(classes.footer.inner)
|
|
5454
|
-
.
|
|
5522
|
+
.attr('role', 'none')
|
|
5523
|
+
.append(tableCloneFooter
|
|
5455
5524
|
.attrRemove('id')
|
|
5456
5525
|
.css('margin-left', '0')
|
|
5457
5526
|
.append(captionSide === 'bottom' ? caption : null)
|
|
@@ -5489,6 +5558,22 @@ function featureTable(settings) {
|
|
|
5489
5558
|
settings.scrollFoot = scrollFoot;
|
|
5490
5559
|
// On redraw - align columns
|
|
5491
5560
|
settings.callbacks.draw.push(scrollDraw);
|
|
5561
|
+
// Aria roles - because we break the table up into parts we need to be very
|
|
5562
|
+
// explicit with the roles to create the accessability tree for the table,
|
|
5563
|
+
// otherwise browser's attempt to "fix" the tree by filling in what it
|
|
5564
|
+
// thinks are gaps. The static elements that we can assign roles to are done
|
|
5565
|
+
// here. Dynamic ones are done in the draw function below.
|
|
5566
|
+
table.attr('role', 'none');
|
|
5567
|
+
table.find('tbody').attr('role', 'rowgroup');
|
|
5568
|
+
tableCloneHeader.attr('role', 'none');
|
|
5569
|
+
tableCloneFooter.attr('role', 'none');
|
|
5570
|
+
settings.colgroup.find('colgroup').attr('role', 'none');
|
|
5571
|
+
// Move the info feature's aria desc by to the new "table"
|
|
5572
|
+
let describedBy = table.attr('aria-describedby');
|
|
5573
|
+
if (describedBy) {
|
|
5574
|
+
scroller.attr('aria-describedby', describedBy);
|
|
5575
|
+
table.attrRemove('aria-describedby');
|
|
5576
|
+
}
|
|
5492
5577
|
return scroller.get(0);
|
|
5493
5578
|
}
|
|
5494
5579
|
/**
|
|
@@ -5521,10 +5606,13 @@ function scrollDraw(settings) {
|
|
|
5521
5606
|
else {
|
|
5522
5607
|
settings.scrollBarVis = scrollBarVis;
|
|
5523
5608
|
}
|
|
5609
|
+
header.find('thead').attr('role', 'rowgroup');
|
|
5610
|
+
footer.find('tfoot').attr('role', 'rowgroup');
|
|
5524
5611
|
// 1. Re-create the table inside the scrolling div
|
|
5525
5612
|
// Remove the old minimised thead and tfoot elements in the inner table
|
|
5526
5613
|
table.children('thead, tfoot').remove();
|
|
5527
|
-
// Clone the current header and footer elements and then place it into the
|
|
5614
|
+
// Clone the current header and footer elements and then place it into the
|
|
5615
|
+
// inner table
|
|
5528
5616
|
headerCopy = header.clone(true).prependTo(table);
|
|
5529
5617
|
headerCopy.find('th, td').attrRemove('tabindex');
|
|
5530
5618
|
headerCopy.find('[id]').attrRemove('id');
|
|
@@ -5559,8 +5647,7 @@ function scrollDraw(settings) {
|
|
|
5559
5647
|
}
|
|
5560
5648
|
}
|
|
5561
5649
|
if (firstTr) {
|
|
5562
|
-
let colSizes = Dom
|
|
5563
|
-
.s(firstTr)
|
|
5650
|
+
let colSizes = Dom.s(firstTr)
|
|
5564
5651
|
.children('th, td')
|
|
5565
5652
|
.mapTo(function (cell, idx) {
|
|
5566
5653
|
return {
|
|
@@ -5625,6 +5712,18 @@ function scrollDraw(settings) {
|
|
|
5625
5712
|
}
|
|
5626
5713
|
// Correct DOM ordering for colgroup - comes before the thead
|
|
5627
5714
|
table.children('colgroup').prependTo(table);
|
|
5715
|
+
// Remove tabindex from the hidden row elements
|
|
5716
|
+
table.find('thead, tfoot').find('[tabindex]').attrRemove('tabindex');
|
|
5717
|
+
// Dynamic ARIA roles - see setup for details on why this is needed
|
|
5718
|
+
table
|
|
5719
|
+
.find('thead, tfoot')
|
|
5720
|
+
.attr('role', 'none')
|
|
5721
|
+
.find('[role]')
|
|
5722
|
+
.attrRemove('role');
|
|
5723
|
+
table.find('tbody tr:not([role])').attr('role', 'row');
|
|
5724
|
+
table.find('tbody td:not([role]), tbody th:not([role])').attr('role', 'cell');
|
|
5725
|
+
scrollAria(headerCopy);
|
|
5726
|
+
scrollAria(footerCopy);
|
|
5628
5727
|
// Adjust the position of the header in case we loose the y-scrollbar
|
|
5629
5728
|
divBody.trigger('scroll');
|
|
5630
5729
|
// If sorting or filtering has occurred, jump the scrolling back to the top
|
|
@@ -5633,6 +5732,18 @@ function scrollDraw(settings) {
|
|
|
5633
5732
|
divBodyEl.scrollTop(0);
|
|
5634
5733
|
}
|
|
5635
5734
|
}
|
|
5735
|
+
/**
|
|
5736
|
+
* Apply ARIA roles for the header / footer of a scrolling table
|
|
5737
|
+
* @param element
|
|
5738
|
+
*/
|
|
5739
|
+
function scrollAria(element) {
|
|
5740
|
+
if (element) {
|
|
5741
|
+
element.find('tfoot:not([role])').attr('role', 'rowgroup');
|
|
5742
|
+
element.find('tr:not([role])').attr('role', 'row');
|
|
5743
|
+
element.find('th:not([role])').attr('role', 'columnheader');
|
|
5744
|
+
element.find('td:not([role])').attr('role', 'cell');
|
|
5745
|
+
}
|
|
5746
|
+
}
|
|
5636
5747
|
|
|
5637
5748
|
/**
|
|
5638
5749
|
* Add a column to the list used for the table with default values
|
|
@@ -5856,6 +5967,7 @@ function columnTypes(settings) {
|
|
|
5856
5967
|
var types = ext.type.detect;
|
|
5857
5968
|
var i, iLen, j, jen, k, ken;
|
|
5858
5969
|
var col, detectedType, cache;
|
|
5970
|
+
var originalTypes = columns.map(c => c.type).join(',');
|
|
5859
5971
|
// For each column, spin over the data type detection functions, seeing if
|
|
5860
5972
|
// one matches
|
|
5861
5973
|
for (i = 0, iLen = columns.length; i < iLen; i++) {
|
|
@@ -5953,6 +6065,10 @@ function columnTypes(settings) {
|
|
|
5953
6065
|
_columnAutoRender(settings, i);
|
|
5954
6066
|
}
|
|
5955
6067
|
}
|
|
6068
|
+
var newTypes = columns.map(c => c.type).join(',');
|
|
6069
|
+
if (newTypes !== originalTypes) {
|
|
6070
|
+
callbackFire(settings, null, 'columnTypes', [settings], false);
|
|
6071
|
+
}
|
|
5956
6072
|
}
|
|
5957
6073
|
/**
|
|
5958
6074
|
* Apply an auto detected renderer to data which doesn't yet have a renderer
|
|
@@ -7154,13 +7270,13 @@ function implementState(settings, s, callback) {
|
|
|
7154
7270
|
let idx = currentNames.indexOf(col[0]);
|
|
7155
7271
|
if (idx < 0) {
|
|
7156
7272
|
// If the column was not found ignore it and continue
|
|
7157
|
-
|
|
7273
|
+
continue;
|
|
7158
7274
|
}
|
|
7159
7275
|
set[0] = idx;
|
|
7160
7276
|
}
|
|
7161
7277
|
else if (set[0] >= columns.length) {
|
|
7162
7278
|
// If the column index is out of bounds ignore it and continue
|
|
7163
|
-
|
|
7279
|
+
continue;
|
|
7164
7280
|
}
|
|
7165
7281
|
settings.order.push(set);
|
|
7166
7282
|
}
|
|
@@ -8297,6 +8413,11 @@ function reDraw(settings, holdPosition, recompute) {
|
|
|
8297
8413
|
if (holdPosition !== true) {
|
|
8298
8414
|
settings.displayStart = 0;
|
|
8299
8415
|
}
|
|
8416
|
+
else {
|
|
8417
|
+
// Keep position, but make sure that there is actually data to display,
|
|
8418
|
+
// otherwise we need to rewind a bit (e.g. if rows were deleted)
|
|
8419
|
+
lengthOverflow(settings);
|
|
8420
|
+
}
|
|
8300
8421
|
// Let any modules know about the draw hold position state (used by
|
|
8301
8422
|
// scrolling internally)
|
|
8302
8423
|
settings.drawHold = holdPosition;
|
|
@@ -9473,12 +9594,13 @@ function selectCells(settings, selector, opts) {
|
|
|
9473
9594
|
// Otherwise the selector is a node, and there is one last option - the
|
|
9474
9595
|
// element might be a child of an element which has dt-row and dt-column
|
|
9475
9596
|
// data attributes
|
|
9476
|
-
|
|
9477
|
-
|
|
9597
|
+
let rowHost = Dom.s(s).closest('*[data-dt-row]');
|
|
9598
|
+
let columnHost = Dom.s(s).closest('*[data-dt-column]');
|
|
9599
|
+
return rowHost.count()
|
|
9478
9600
|
? [
|
|
9479
9601
|
{
|
|
9480
|
-
row:
|
|
9481
|
-
column:
|
|
9602
|
+
row: parseInt(rowHost.attr('data-dt-row')),
|
|
9603
|
+
column: parseInt(columnHost.attr('data-dt-column'))
|
|
9482
9604
|
}
|
|
9483
9605
|
]
|
|
9484
9606
|
: [];
|
|
@@ -9791,7 +9913,7 @@ function selectColumns(settings, selector, opts) {
|
|
|
9791
9913
|
// Otherwise a node which might have a `dt-column` data attribute, or be
|
|
9792
9914
|
// a child or such an element
|
|
9793
9915
|
var host = Dom.s(s).closest('*[data-dt-column]');
|
|
9794
|
-
return host.count() ? [host.
|
|
9916
|
+
return host.count() ? [parseInt(host.attr('data-dt-column'))] : [];
|
|
9795
9917
|
};
|
|
9796
9918
|
var selected = selectorRun('column', selector, run, settings, opts);
|
|
9797
9919
|
return opts.columnOrder && opts.columnOrder === 'index'
|
|
@@ -9988,18 +10110,19 @@ registerPlural('columns().widths()', 'column().width()', function () {
|
|
|
9988
10110
|
// Injects a fake row into the table for just a moment so the widths can
|
|
9989
10111
|
// be read, regardless of colspan in the header and rows being present
|
|
9990
10112
|
// in the body
|
|
9991
|
-
var columns = this.columns(':visible')
|
|
10113
|
+
var columns = this.columns(':visible');
|
|
9992
10114
|
var row = Dom
|
|
9993
10115
|
.c('tr')
|
|
9994
|
-
.html('<td>' + Array(columns).join('</td><td>') + '</td>');
|
|
10116
|
+
.html('<td>' + Array(columns.count()).join('</td><td>') + '</td>');
|
|
9995
10117
|
Dom.s(this.table().body()).append(row);
|
|
9996
|
-
var widths =
|
|
9997
|
-
|
|
10118
|
+
var widths = [];
|
|
10119
|
+
var indexes = columns.indexes();
|
|
10120
|
+
row.children().each((el, idx) => {
|
|
10121
|
+
widths[indexes[idx]] = Dom.s(el).width('outer');
|
|
9998
10122
|
});
|
|
9999
10123
|
row.remove();
|
|
10000
|
-
return this.iterator('column',
|
|
10001
|
-
|
|
10002
|
-
return visIdx !== null ? widths[visIdx] : 0;
|
|
10124
|
+
return this.iterator('column', (settings, column) => {
|
|
10125
|
+
return widths[column] || 0;
|
|
10003
10126
|
}, true);
|
|
10004
10127
|
});
|
|
10005
10128
|
registerPlural('columns().indexes()', 'column().index()', function (type) {
|
|
@@ -10471,7 +10594,7 @@ function selectRows(settings, selector, opts) {
|
|
|
10471
10594
|
}
|
|
10472
10595
|
else {
|
|
10473
10596
|
var host = Dom.s(sel).closest('*[data-dt-row]');
|
|
10474
|
-
return host.count() ? [host.
|
|
10597
|
+
return host.count() ? [parseInt(host.attr('data-dt-row'))] : [];
|
|
10475
10598
|
}
|
|
10476
10599
|
}
|
|
10477
10600
|
// ID selector. Want to always be able to select rows by id, regardless
|
|
@@ -10503,8 +10626,7 @@ function selectRows(settings, selector, opts) {
|
|
|
10503
10626
|
// Get nodes in the order from the `rows` array with null values removed
|
|
10504
10627
|
var nodes = util.array.removeEmpty(util.array.pluckOrder(settings.data, rows, 'tr'));
|
|
10505
10628
|
// Selector - selector string, array of nodes or jQuery object.
|
|
10506
|
-
return Dom
|
|
10507
|
-
.s(nodes)
|
|
10629
|
+
return Dom.s(nodes)
|
|
10508
10630
|
.filter(sel)
|
|
10509
10631
|
.mapTo((el) => el._DT_RowIndex);
|
|
10510
10632
|
};
|
|
@@ -10649,7 +10771,7 @@ register('row().data()', function (data) {
|
|
|
10649
10771
|
util.set(ctx[0].rowId)(data, row.tr.id);
|
|
10650
10772
|
}
|
|
10651
10773
|
// Automatically invalidate
|
|
10652
|
-
invalidate(ctx[0], this[0], 'data');
|
|
10774
|
+
invalidate(ctx[0], this[0][0], 'data');
|
|
10653
10775
|
return this;
|
|
10654
10776
|
});
|
|
10655
10777
|
register('row().node()', function () {
|
|
@@ -10967,10 +11089,10 @@ register('caption()', function (value, side) {
|
|
|
10967
11089
|
caption.css('caption-side', side);
|
|
10968
11090
|
caption.get(0)._captionSide = side;
|
|
10969
11091
|
}
|
|
10970
|
-
if (container.find('div.
|
|
10971
|
-
var selector = side === 'top' ? '
|
|
11092
|
+
if (container.find('div.dt-scroll').count()) {
|
|
11093
|
+
var selector = side === 'top' ? 'head' : 'foot';
|
|
10972
11094
|
container
|
|
10973
|
-
.find('div.
|
|
11095
|
+
.find('div.dt-scroll-' + selector + ' table')
|
|
10974
11096
|
.prepend(caption);
|
|
10975
11097
|
}
|
|
10976
11098
|
else {
|
|
@@ -10983,6 +11105,272 @@ register('caption.node()', function () {
|
|
|
10983
11105
|
return ctx.length ? ctx[0].captionNode : null;
|
|
10984
11106
|
});
|
|
10985
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 »');
|
|
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
|
+
|
|
10986
11374
|
/**
|
|
10987
11375
|
* CommonJS factory function pass through. This will check if the arguments
|
|
10988
11376
|
* given are a window object or a jQuery object. If so they are set accordingly.
|
|
@@ -11342,8 +11730,7 @@ register$2('pageLength', function (settings, optsIn) {
|
|
|
11342
11730
|
}
|
|
11343
11731
|
// Wrapper element - use a span as a holder for where the select will go
|
|
11344
11732
|
var tmpId = 'tmp-' + +new Date();
|
|
11345
|
-
var div = Dom
|
|
11346
|
-
.c('div')
|
|
11733
|
+
var div = Dom.c('div')
|
|
11347
11734
|
.classAdd(classes.container)
|
|
11348
11735
|
.html(str.replace('_MENU_', '<span id="' + tmpId + '"></span>'));
|
|
11349
11736
|
// Save text node content for macro updating
|
|
@@ -11365,9 +11752,9 @@ register$2('pageLength', function (settings, optsIn) {
|
|
|
11365
11752
|
});
|
|
11366
11753
|
};
|
|
11367
11754
|
// Next, the select itself, along with the options
|
|
11368
|
-
var select = Dom
|
|
11369
|
-
.c('select')
|
|
11755
|
+
var select = Dom.c('select')
|
|
11370
11756
|
.attr('aria-controls', tableId)
|
|
11757
|
+
.attr('autocomplete', 'off')
|
|
11371
11758
|
.classAdd(classes.select);
|
|
11372
11759
|
for (i = 0; i < lengths.length; i++) {
|
|
11373
11760
|
// Attempt to look up the length from the i18n options
|
|
@@ -11398,7 +11785,26 @@ register$2('pageLength', function (settings, optsIn) {
|
|
|
11398
11785
|
// Update node value whenever anything changes the table's length
|
|
11399
11786
|
Dom.s(settings.table).on('length.dt.DT', function (e, s, len) {
|
|
11400
11787
|
if (settings === s) {
|
|
11401
|
-
div.find('select')
|
|
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);
|
|
11402
11808
|
// Resolve plurals in the text for the new length
|
|
11403
11809
|
updateEntries(len);
|
|
11404
11810
|
}
|
|
@@ -11406,6 +11812,19 @@ register$2('pageLength', function (settings, optsIn) {
|
|
|
11406
11812
|
updateEntries(settings.pageLength);
|
|
11407
11813
|
return div;
|
|
11408
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
|
+
}
|
|
11409
11828
|
|
|
11410
11829
|
let __searchCounter = 0;
|
|
11411
11830
|
register$2('search', function (settings, optsIn) {
|
|
@@ -11416,7 +11835,9 @@ register$2('search', function (settings, optsIn) {
|
|
|
11416
11835
|
let classes = settings.classes.search;
|
|
11417
11836
|
let tableId = settings.tableId;
|
|
11418
11837
|
let language = settings.language;
|
|
11419
|
-
let input = '<input type="search" class="' +
|
|
11838
|
+
let input = '<input type="search" class="' +
|
|
11839
|
+
classes.input +
|
|
11840
|
+
'" autocomplete="off"/>';
|
|
11420
11841
|
let opts = util.object.assignDeep({
|
|
11421
11842
|
columns: '*',
|
|
11422
11843
|
placeholder: language.searchPlaceholder,
|
|
@@ -12238,6 +12659,8 @@ DataTable.datetime = datetime;
|
|
|
12238
12659
|
DataTable.__browser = browser;
|
|
12239
12660
|
DataTable.Dom = Dom;
|
|
12240
12661
|
DataTable.ajax = util.ajax;
|
|
12662
|
+
DataTable.key = key;
|
|
12663
|
+
plus(DataTable);
|
|
12241
12664
|
/**
|
|
12242
12665
|
* Private data store, containing all of the settings objects that are created
|
|
12243
12666
|
* for the tables on a given page.
|