PluginProbe
Ultimate Store Kit – Store Builder Addons for Elementor, WooCommerce Store Builder, EDD Store Builder / 3.1.4
Ultimate Store Kit – Store Builder Addons for Elementor, WooCommerce Store Builder, EDD Store Builder v3.1.4
3.1.4 3.0.8 3.0.9 3.1.0 3.1.2 3.1.3 3.0.7 3.0.5 3.0.4 3.0.3 3.0.2 trunk 1.5.0 1.5.1 1.5.2 1.6.1 1.6.2 1.6.3 1.6.4 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 All 93 releases
ultimate-store-kit / src / vendor / js / datatables.js

datatables.js in Ultimate Store Kit – Store Builder Addons for Elementor, WooCommerce Store Builder, EDD Store Builder 3.1.4, at src/vendor/js/datatables.js

12,848 lines 427.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /*
2 * This combined file was created by the DataTables downloader builder:
3 * https://datatables.net/download
4 *
5 * To rebuild or modify this file with the latest versions of the included
6 * software please visit:
7 * https://datatables.net/download/#dt/dt-3.0.3
8 *
9 * Included libraries:
10 * DataTables 3.0.3
11 */
12
13 /*! DataTables 3.0.3
14 * Copyright (c) SpryMedia Ltd - datatables.net/license
15 */
16
17 (function(factory){
18 if (typeof define === 'function' && define.amd) {
19 // AMD
20 define([], function () {
21 return factory(window, document);
22 });
23 }
24 else if (typeof exports === 'object') {
25 // CommonJS
26 var cjsRequires = function (root) { };
27
28 if (typeof window === 'undefined') {
29 module.exports = function (root) {
30 if (! root) {
31 // CommonJS environments without a window global must pass a
32 // root. This will give an error otherwise
33 root = window;
34 }
35
36 cjsRequires(root);
37 return factory(root, root.document);
38 };
39 }
40 else {
41 cjsRequires(window);
42 module.exports = factory(window, window.document);
43 }
44 }
45 else {
46 // Browser
47 window.DataTable = factory(window, document);
48 }
49 }(function(window, document) {
50 'use strict';
51
52
53
54 // A collection of the regular expressions used throughout the code base. Not all are here
55 // just the ones that need to be reused - no need to dump single use expressions here.
56 // https://en.wikipedia.org/wiki/Foreign_exchange_market
57 // - \u20BD - Russian ruble.
58 // - \u20a9 - South Korean Won
59 // - \u20BA - Turkish Lira
60 // - \u20B9 - Indian Rupee
61 // - R - Brazil (R$) and South Africa
62 // - fr - Swiss Franc
63 // - kr - Swedish krona, Norwegian krone and Danish krone
64 // - \u2009 is thin space and \u202F is narrow no-break space, both used in many
65 // - Ƀ - Bitcoin
66 // - Ξ - Ethereum
67 // standards as thousands separators.
68 const reFormattedNumeric = /['\u00A0,$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfkɃΞ]/gi;
69 const reHtml = /<([^>]*>)/g;
70 // Escape regular expression special characters
71 const reRegexCharacters = new RegExp('(\\' +
72 [
73 '/',
74 '.',
75 '*',
76 '+',
77 '?',
78 '|',
79 '(',
80 ')',
81 '[',
82 ']',
83 '{',
84 '}',
85 '\\',
86 '$',
87 '^',
88 '-',
89 ].join('|\\') +
90 ')', 'g');
91 // This is not strict ISO8601 - Date.parse() is quite lax, although
92 // implementations differ between browsers.
93 const reDate = /^\d{2,4}[./-]\d{1,2}[./-]\d{1,2}([T ]{1}\d{1,2}[:.]\d{2}([.:]\d{2})?)?$/;
94 const reNewLines = /[\r\n\u2028]/g;
95 const isoTimezone = /[T\s]\d{2}.*?(Z|[+-]\d{2}(?::?\d{2})?)$/;
96
97 var regex = /*#__PURE__*/Object.freeze({
98 __proto__: null,
99 isoTimezone: isoTimezone,
100 reDate: reDate,
101 reFormattedNumeric: reFormattedNumeric,
102 reHtml: reHtml,
103 reNewLines: reNewLines,
104 reRegexCharacters: reRegexCharacters
105 });
106
107 const maxStrLen = Math.pow(2, 28);
108 /**
109 * DataTables default string normalisation. Remove diacritics from a string by
110 * decomposing it and then removing non-ascii characters.
111 *
112 * This function is replaceable if the user wishes to use a different library
113 * for normalising a string.
114 *
115 * @param val Value to normalise (if a string)
116 * @param both Include both the normalised and original in the return
117 * @returns Normalised string, or original value if not a string
118 */
119 let _normalize = function (val, both) {
120 if (typeof val !== 'string') {
121 return val;
122 }
123 // It is faster to just run `normalize` than it is to check if
124 // we need to with a regex! (Check as it isn't available in old
125 // Safari)
126 var res = val.normalize ? val.normalize('NFD') : val;
127 // Equally, here we check if a regex is needed or not
128 return res.length !== val.length
129 ? (both === true ? val + ' ' : '') + res.replace(/[\u0300-\u036f]/g, '')
130 : res;
131 };
132 /**
133 * DataTables default string HTML stripping from a string
134 *
135 * This function is replaceable if the user wishes to use a different library
136 * for stripping HTML from a string.
137 *
138 * @param input Value to strip HTML from
139 * @param replacement Value to replace the tags with
140 * @returns Stripped value
141 */
142 let _stripHtml = function (input, replacement = '') {
143 if (!input || typeof input !== 'string') {
144 return input;
145 }
146 // Irrelevant check to workaround CodeQL's false positive on the regex
147 if (input.length > maxStrLen) {
148 throw new Error('Exceeded max str len');
149 }
150 let previous;
151 let next = input.replace(reHtml, replacement); // Complete tags
152 // Safety for incomplete script tag - use do / while to ensure that
153 // we get all instances
154 do {
155 previous = next;
156 next = next.replace(/<script/i, '');
157 } while (next !== previous);
158 // T must be a string, but TS can't seem to figure that out
159 return previous;
160 };
161 /**
162 * DataTables default HTML entity escaping.
163 *
164 * This function is replaceable if the user wishes to use a different library
165 * for escaping HTML entities in a string.
166 *
167 * @param val Value to escape HTML in
168 * @returns Escaped value
169 */
170 let _escapeHtml = function (val) {
171 let d = Array.isArray(val) ? val.join(',') : val;
172 return typeof d === 'string'
173 ? d
174 .replace(/&/g, '&amp;')
175 .replace(/</g, '&lt;')
176 .replace(/>/g, '&gt;')
177 .replace(/"/g, '&quot;')
178 : d;
179 };
180 /**
181 * Escape regular expression characters in a string
182 *
183 * @param val String to escape
184 * @returns String with regex characters escaped
185 */
186 function escapeRegex(val) {
187 return val.replace(reRegexCharacters, '\\$1');
188 }
189 function escapeHtml(mixed) {
190 var type = typeof mixed;
191 if (type === 'function') {
192 _escapeHtml = mixed;
193 return;
194 }
195 else if (type === 'string' || Array.isArray(mixed)) {
196 return _escapeHtml(mixed);
197 }
198 return mixed;
199 }
200 function normalize(mixed, both) {
201 var type = typeof mixed;
202 if (type !== 'function') {
203 return _normalize(mixed, both);
204 }
205 _normalize = mixed;
206 }
207 function stripHtml(mixed, replacement) {
208 const type = typeof mixed;
209 if (type === 'function') {
210 _stripHtml = mixed;
211 return;
212 }
213 else if (type === 'string') {
214 return _stripHtml(mixed, replacement);
215 }
216 return mixed;
217 }
218
219 var string = /*#__PURE__*/Object.freeze({
220 __proto__: null,
221 escapeHtml: escapeHtml,
222 escapeRegex: escapeRegex,
223 normalize: normalize,
224 stripHtml: stripHtml
225 });
226
227 const _re_dic = {};
228 /**
229 * Get integer value
230 *
231 * @param s Value
232 * @returns Int, or null if not a number
233 */
234 function intVal(s) {
235 var integer = parseInt(s, 10);
236 return !isNaN(integer) && isFinite(s) ? integer : null;
237 }
238 // Convert from a formatted number with characters other than `.` as the
239 // decimal place, to a JavaScript number
240 function numToDecimal(num, decimalPoint) {
241 // Cache created regular expressions for speed as this function is called often
242 if (!_re_dic[decimalPoint]) {
243 _re_dic[decimalPoint] = new RegExp(escapeRegex(decimalPoint), 'g');
244 }
245 return typeof num === 'string' && decimalPoint !== '.'
246 ? num.replace(/\./g, '').replace(_re_dic[decimalPoint], '.')
247 : num;
248 }
249
250 var conv = /*#__PURE__*/Object.freeze({
251 __proto__: null,
252 intVal: intVal,
253 numToDecimal: numToDecimal
254 });
255
256 function arrayLike(test) {
257 return (test && // Exists
258 typeof test !== 'string' && // Is not a string
259 test.length !== undefined && // Has a length
260 test.nodeType === undefined // Is not a text node
261 );
262 }
263 /**
264 * Determine if the input is a Dom instance
265 *
266 * @param input Value to check
267 * @returns true if it is a Dom instance, false otherwise
268 */
269 function dom(input) {
270 return input && typeof input === 'object' && input._isDom;
271 }
272 /**
273 * Determine if the input is an HTML element
274 *
275 * @param input Value to check
276 * @returns true if an HTML element was passed in
277 */
278 function element(input) {
279 return typeof input === 'object' && input.nodeName;
280 }
281 /**
282 * Check if a value is empty or not. Note that a string with `-` is considered
283 * empty
284 *
285 * @param d Value to check
286 * @returns `true` if empty, `false` otherwise
287 */
288 function empty(d) {
289 return !d || d === true || d === '-' ? true : false;
290 }
291 /**
292 * Check if a string is HTML. Note that a string without HTML in it can be
293 * considered to be HTML still!
294 *
295 * @todo Can we drop this?
296 * @param d
297 * @returns
298 */
299 function html(d) {
300 return empty(d) || typeof d === 'string';
301 }
302 /**
303 * Is a string a number surrounded by HTML?
304 *
305 * @param d Value to check
306 * @param decimalPoint Decimal place character
307 * @param formatted Consider formatted numbers
308 * @param allowEmpty Allow empty to be considered as a number
309 * @returns True if a number, null otherwise
310 */
311 function htmlNum(d, decimalPoint, formatted, allowEmpty) {
312 if (allowEmpty && empty(d)) {
313 return true;
314 }
315 // input and select strings mean that this isn't just a number
316 if (typeof d === 'string' && d.match(/<(input|select)/i)) {
317 return null;
318 }
319 return !html(d)
320 ? null
321 : num$1(stripHtml(d), decimalPoint, formatted, allowEmpty)
322 ? true
323 : null;
324 }
325 /**
326 * Determine if an input is a jQuery instance
327 *
328 * @param input Value to check
329 * @returns true if it is a jQuery instance, false otherwise
330 */
331 function jquery(input) {
332 return input && typeof input.jquery === 'string';
333 }
334 /**
335 * Check if a given value is numeric, taking into account if it might be
336 * formatted or uses a decimal point that is not a period.
337 *
338 * @param d Value to check
339 * @param decimalPoint DP character
340 * @param formatted Allow the number to be formatted or not
341 * @param allowEmpty Allow an empty value to be considered a number
342 * @returns `true` if numeric
343 */
344 function num$1(d, decimalPoint, formatted, allowEmpty) {
345 let type = typeof d;
346 if (type === 'number' || type === 'bigint') {
347 return true;
348 }
349 // If empty return immediately so there must be a number if it is a
350 // formatted string (this stops the string "k", or "kr", etc being detected
351 // as a formatted number for currency
352 if (allowEmpty && empty(d)) {
353 return true;
354 }
355 if (decimalPoint && type === 'string') {
356 d = numToDecimal(d, decimalPoint);
357 }
358 if (formatted && type === 'string') {
359 d = d.replace(reFormattedNumeric, '');
360 }
361 return !isNaN(parseFloat(d)) && isFinite(d);
362 }
363 /**
364 * Determine if a value is a plain object or not
365 *
366 * @param value Value to check
367 * @returns true if is a plain object, otherwise false
368 */
369 function plainObject(value) {
370 if (typeof value !== 'object' || value === null) {
371 return false;
372 }
373 let proto = Object.getPrototypeOf(value);
374 return proto === null || proto === Object.prototype;
375 }
376
377 var is = /*#__PURE__*/Object.freeze({
378 __proto__: null,
379 arrayLike: arrayLike,
380 dom: dom,
381 element: element,
382 empty: empty,
383 html: html,
384 htmlNum: htmlNum,
385 jquery: jquery,
386 num: num$1,
387 plainObject: plainObject
388 });
389
390 /**
391 * Object iteration function, executing a callback for each key in the object
392 *
393 * @param input Input object
394 * @param fn Function to execute
395 */
396 function each(input, fn) {
397 if (!input) {
398 return;
399 }
400 let keys = Object.keys(input);
401 for (let i = 0; i < keys.length; i++) {
402 let key = keys[i];
403 fn(key, input[key], i);
404 }
405 }
406 /**
407 * Merge the contents of two or more objects into the first object.
408 *
409 * @param out Object to be assigned the properties
410 * @param inputs Objects to take the values from
411 * @returns The `output`, just for convenience - output === the return.
412 */
413 function assign(out, ...inputs) {
414 let output = Object(out);
415 // Can't just use `Object.assign` as it will assign `undefined` as a regular
416 // value to the target.
417 for (let i = 0; i < inputs.length; i++) {
418 let options = inputs[i];
419 // Filter inputs
420 if (options != null) {
421 // Extend the base object
422 for (let name in options) {
423 let copy = options[name];
424 // Prevent Object.prototype pollution
425 // Prevent never-ending loop
426 if (name === '__proto__' || output === copy) {
427 continue;
428 }
429 // Ignore undefined values (this is why we can't use Object.assign)
430 if (copy !== undefined) {
431 output[name] = copy;
432 }
433 }
434 }
435 }
436 return output;
437 }
438 /**
439 * Deep merge the contents of two or more objects into the first object. This
440 * breaks references for both objects and array.
441 *
442 * @param out Object to be assigned the properties
443 * @param inputs Objects to take the values from
444 * @returns The `output`, just for convenience - output === the return.
445 */
446 function assignDeep(out, ...inputs) {
447 if (!out) {
448 return {};
449 }
450 for (let i = 0; i < inputs.length; i++) {
451 let input = inputs[i];
452 if (!input) {
453 continue;
454 }
455 for (const [key, value] of Object.entries(input)) {
456 if (Array.isArray(value)) {
457 if (!Array.isArray(out[key])) {
458 out[key] = [];
459 }
460 assignDeep(out[key], value);
461 }
462 else if (plainObject(value)) {
463 if (!plainObject(out[key])) {
464 out[key] = {};
465 }
466 assignDeep(out[key], value);
467 }
468 else if (input[key] !== undefined) {
469 out[key] = input[key];
470 }
471 }
472 }
473 return out;
474 }
475 /**
476 * Deep merge objects, but shallow copy arrays. The reason we need to do this,
477 * is that we don't want to deep copy array init values (such as aaSorting)
478 * since the dev wouldn't be able to override them, but we do want to deep copy
479 * arrays.
480 *
481 * @param out Object to extend
482 * @param extender Object from which the properties will be applied to out
483 * @param breakRefs If true, then arrays will be sliced to take an independent
484 * copy with the exception of the `data` or `aaData` parameters if they are
485 * present. This is so you can pass in a collection to DataTables and have
486 * that used as your data source without breaking the references
487 * @returns out Reference, just for convenience - out === the return.
488 * @todo This doesn't take account of arrays inside the deep copied objects.
489 */
490 function assignDeepObjects(out, extender, breakRefs = false) {
491 let val;
492 for (let prop in extender) {
493 if (Object.prototype.hasOwnProperty.call(extender, prop)) {
494 val = extender[prop];
495 if (plainObject(val)) {
496 if (!plainObject(out[prop])) {
497 out[prop] = {};
498 }
499 assignDeep(out[prop], val);
500 }
501 else if (breakRefs &&
502 prop !== 'data' &&
503 prop !== 'aaData' &&
504 Array.isArray(val)) {
505 out[prop] = val.slice();
506 }
507 else {
508 out[prop] = val;
509 }
510 }
511 }
512 return out;
513 }
514 /**
515 * Map entries to an array
516 *
517 * @param obj In object
518 * @param fn Map transform function. Same signature as `each`
519 * @returns Result
520 */
521 function map$1(obj, fn) {
522 let out = [];
523 each(obj, (key, val) => {
524 out.push(fn(key, val));
525 });
526 return out;
527 }
528
529 var object = /*#__PURE__*/Object.freeze({
530 __proto__: null,
531 assign: assign,
532 assignDeep: assignDeep,
533 assignDeepObjects: assignDeepObjects,
534 each: each,
535 map: map$1
536 });
537
538 const defaults$5 = {
539 cache: true,
540 contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
541 headers: {},
542 traditional: false,
543 url: location.href
544 };
545 /**
546 * Trigger an Ajax call to the server based on the configuration parameters
547 * passed in.
548 *
549 * @param optionsIn Ajax options
550 * @returns The XHR request
551 */
552 function ajax(optionsIn) {
553 let xhr = new XMLHttpRequest();
554 let options = assign({}, defaults$5, optionsIn);
555 let urlParams = queryParams(options);
556 let method = httpMethod(options);
557 let sendData = null;
558 // Allow the data to be sent to the server as a simple JSON string -
559 // primarily to be used with POST / PUT
560 if (options.submitAs === 'json' && options.data) {
561 options.data = JSON.stringify(options.data);
562 if (!options.contentType) {
563 options.contentType = 'application/json; charset=utf-8';
564 }
565 }
566 xhr.open(method, options.url +
567 (urlParams
568 ? (options.url.includes('?') ? '&' : '?') + urlParams
569 : ''), true, options.username || null, options.password || null);
570 // Content type for FormData requests gets set by the browser.
571 if (options.contentType && !(options.data instanceof FormData)) {
572 xhr.setRequestHeader('Content-Type', options.contentType);
573 }
574 // Add a X-Request-With header, as jQuery does so and some server-side
575 // platforms look for it. Only for same domain though.
576 if (options.headers &&
577 !options.headers['X-Requested-With'] &&
578 !isCrossDomain(options.url)) {
579 options.headers['X-Requested-With'] = 'XMLHttpRequest';
580 }
581 // Add an accept header specifically for JSON data types, again to match
582 // how jQuery operates for this.
583 if (options.dataType === 'json' &&
584 options.headers &&
585 !options.headers['accepts']) {
586 options.headers['Accept'] =
587 'application/json, text/javascript, */*; q=0.01';
588 }
589 each(options.headers, (key, val) => {
590 xhr.setRequestHeader(key, val);
591 });
592 if (options.data instanceof FormData) {
593 sendData = options.data;
594 }
595 else if (method !== 'GET' && options.data) {
596 if (typeof options.data === 'string') {
597 sendData = options.data;
598 }
599 else {
600 sendData = serialize(options.data, options.traditional);
601 sendData = convertSpaces(sendData, options);
602 // So beforeSend matches how jQuery behaves
603 options.data = sendData;
604 }
605 }
606 xhr.onreadystatechange = function () {
607 if (xhr.readyState != 4) {
608 return;
609 }
610 let responseData = xhr.responseText;
611 let statusText = 'success';
612 if (xhr.status === 0) {
613 return; // aborted
614 }
615 else if (xhr.status === 204 || method === 'HEAD') {
616 statusText = 'nocontent';
617 }
618 else if (xhr.status === 304) {
619 statusText = 'notmodified';
620 }
621 else if (xhr.status >= 400) {
622 statusText = 'error';
623 }
624 // Return data type handling
625 if (options.dataType === 'json') {
626 try {
627 responseData = JSON.parse(responseData);
628 }
629 catch (e) {
630 statusText = 'parsererror';
631 }
632 }
633 else if (!options.dataType) {
634 // Data type is undefined, so attempt to parse as JSON if possible,
635 // but no error if it can't be
636 try {
637 responseData = JSON.parse(responseData);
638 }
639 catch (e) {
640 // noop
641 }
642 }
643 if (statusText === 'success') {
644 callback(options.success, responseData, statusText, xhr);
645 }
646 else {
647 callback(options.error, xhr, statusText, xhr.statusText);
648 }
649 callback(options.complete, xhr, statusText);
650 };
651 if (options.beforeSend) {
652 if (options.beforeSend.call(options, xhr, options) === false) {
653 xhr.abort();
654 return xhr;
655 }
656 }
657 xhr.send(sendData);
658 return xhr;
659 }
660 // Expose defaults and serialisation method
661 ajax.defaults = defaults$5;
662 ajax.serialize = serialize;
663 /**
664 * Run callback functions (allowing for none, one or array)
665 *
666 * @param fnIn Function(s) to run
667 * @param arg1 Parameters to pass to the function(s)
668 * @param arg2 Parameters to pass to the function(s)
669 * @param arg3 Parameters to pass to the function(s)
670 */
671 function callback(fnIn, arg1, arg2, arg3) {
672 if (!fnIn) {
673 return;
674 }
675 let fnArr = Array.isArray(fnIn) ? fnIn : [fnIn];
676 for (let i = 0; i < fnArr.length; i++) {
677 fnArr[i](arg1, arg2, arg3);
678 }
679 }
680 /**
681 * For form submission with x-www-form-urlencoded, spaces should be submitted as
682 * `+`. See the jQuery discussion on the topic here:
683 * https://github.com/jquery/jquery/issues/2658#issuecomment-149024872
684 *
685 * @param sendData Serialised form of the data to submit
686 * @param options Ajax options
687 * @returns Query string
688 */
689 function convertSpaces(sendData, options) {
690 return (options.contentType || '').indexOf('application/x-www-form-urlencoded') === 0
691 ? sendData.replace(/%20/g, '+')
692 : sendData;
693 }
694 /**
695 * Determine if a url is a cross domain request or not
696 *
697 * @param url URL to check
698 * @returns True if cross domain, false otherwise
699 */
700 function isCrossDomain(url) {
701 // Use the current page as the base to handle relative URLs correctly
702 const target = new URL(url, window.location.origin);
703 return target.origin !== window.location.origin;
704 }
705 /**
706 * Get the HTTP method from the Ajax request options
707 *
708 * @param options Ajax options
709 * @returns HTTP verb
710 */
711 function httpMethod(options) {
712 let method = 'GET';
713 if (options.type) {
714 method = options.type;
715 }
716 if (options.method) {
717 method = options.method;
718 }
719 return method.toUpperCase();
720 }
721 /**
722 * Get the query parameters based on the options (method, cache and data all
723 * need to be considered).
724 *
725 * @param options Ajax options
726 * @returns URL string
727 */
728 function queryParams(options) {
729 let requestParams = [];
730 if (httpMethod(options) === 'GET') {
731 // Construct URL parameters string
732 requestParams.push(serialize(options.data, options.traditional));
733 }
734 // If a DELETE method is used there are a number of servers which will
735 // reject the request if it has a body. So we need to append to the URL.
736 //
737 // http://stackoverflow.com/questions/15088955
738 // http://bugs.jquery.com/ticket/11586
739 if (httpMethod(options) === 'DELETE' &&
740 (options.deleteBody === undefined || options.deleteBody === true)) {
741 requestParams.push(serialize(options.data, options.traditional));
742 delete options.data;
743 }
744 if (options.cache === false) {
745 requestParams.push(serialize({ _: +new Date() }));
746 }
747 return convertSpaces(requestParams.filter(d => !!d).join('&'), options);
748 }
749 /**
750 * Convert an object into a list of parameters for a query request. Supports
751 * jQuery traditional option for legacy applications.
752 *
753 * @param obj Object to convert
754 * @param traditional If jQuery old style should be used
755 * @returns Parameter-ized string
756 */
757 function serialize(obj, traditional = false) {
758 var params = [];
759 if (obj === undefined || obj === null) {
760 return '';
761 }
762 serializeNested(params, obj, traditional);
763 return params.join('&');
764 }
765 /**
766 * Recursive serialisation function
767 *
768 * @param params Array to write the serialised parameters to
769 * @param obj Object / array to serialise
770 * @param traditional Traditional flag for legacy
771 * @param scope Recursive scope
772 */
773 function serializeNested(params, obj, traditional, scope = '') {
774 let array = Array.isArray(obj);
775 for (let key in obj) {
776 let value = obj[key];
777 let nestDown = Array.isArray(value) || (!traditional && plainObject(value));
778 if (scope) {
779 // Non-scalar values need the index set on the host
780 let index = !array || nestDown ? key : '';
781 key = traditional ? scope : scope + '[' + index + ']';
782 }
783 if (!scope && array) {
784 serializeAdd(params, value.name, value.value);
785 }
786 else if (nestDown) {
787 // Nest down
788 serializeNested(params, value, traditional, key);
789 }
790 else {
791 serializeAdd(params, key, value);
792 }
793 }
794 }
795 /**
796 * Add a name / value pair to the list of parameters
797 *
798 * @param params Parameter values
799 * @param name Parameter name
800 * @param value Parameter value
801 */
802 function serializeAdd(params, name, value) {
803 // Allow the input to be a function to match how jQuery operates
804 let strVal = typeof value === 'function' ? value() : value;
805 params.push(encodeURIComponent(name) +
806 '=' +
807 encodeURIComponent(strVal === null ? '' : strVal));
808 }
809
810 /**
811 * Determine if all values in the array are unique. This means we can short
812 * cut the _unique method at the cost of a single loop. A sorted array is used
813 * to easily check the values.
814 *
815 * @param src Source array
816 * @return true if all unique, false otherwise
817 */
818 function allUnique(src) {
819 if (src.length < 2) {
820 return true;
821 }
822 var sorted = src.slice().sort();
823 var last = sorted[0];
824 for (var i = 1, iLen = sorted.length; i < iLen; i++) {
825 if (sorted[i] === last) {
826 return false;
827 }
828 last = sorted[i];
829 }
830 return true;
831 }
832 /**
833 * Flatten an array
834 *
835 * Surprisingly this is faster than [].concat.apply
836 * https://jsperf.com/flatten-an-array-loop-vs-reduce/2
837 *
838 * @param out Array to write to
839 * @param val Source array, or single value
840 * @returns Flattened array
841 */
842 function flatten(out, val) {
843 if (Array.isArray(val) || arrayLike(val)) {
844 for (var i = 0; i < val.length; i++) {
845 flatten(out, val[i]);
846 }
847 }
848 else {
849 out.push(val);
850 }
851 return out;
852 }
853 function intersection(a1, a2) {
854 return a1.filter(item => a2.includes(item));
855 }
856 /**
857 * Pluck items from an array of objects, or from a nested array of objects
858 *
859 * @param a Array to get values from
860 * @param prop Property to read values from
861 * @param prop2 Inner property to get values from if a 2D array
862 * @returns Array of read values
863 */
864 function pluck(a, prop, prop2) {
865 let out = [], i = 0, iLen = a.length;
866 // Could have the test in the loop for slightly smaller code, but speed
867 // is essential here
868 if (prop2 !== undefined) {
869 for (; i < iLen; i++) {
870 if (a[i] && a[i][prop]) {
871 out.push(a[i][prop][prop2]);
872 }
873 }
874 }
875 else {
876 for (; i < iLen; i++) {
877 if (a[i]) {
878 out.push(a[i][prop]);
879 }
880 }
881 }
882 return out;
883 }
884 /**
885 * Basically the same as _pluck, but rather than looping over the source array we use `order`
886 * as the indexes to pick from the source array
887 *
888 * @param a Array to get values from
889 * @param order Indexes to pick
890 * @param prop Property to read values from
891 * @param prop2 Inner property to get values from if a 2D array
892 * @returns Array of read values
893 */
894 function pluckOrder(a, order, prop, prop2) {
895 let out = [], i = 0, iLen = order.length;
896 // Could have the test in the loop for slightly smaller code, but speed
897 // is essential here
898 if (prop2 !== undefined) {
899 for (; i < iLen; i++) {
900 if (a[order[i]] && a[order[i]][prop]) {
901 out.push(a[order[i]][prop][prop2]);
902 }
903 }
904 }
905 else {
906 for (; i < iLen; i++) {
907 if (a[order[i]]) {
908 out.push(a[order[i]][prop]);
909 }
910 }
911 }
912 return out;
913 }
914 function range(len, start) {
915 var out = [];
916 var end;
917 if (start === undefined) {
918 start = 0;
919 end = len;
920 }
921 else {
922 end = start;
923 start = len;
924 }
925 for (var i = start; i < end; i++) {
926 out.push(i);
927 }
928 return out;
929 }
930 /**
931 * Remove all falsy values from an array
932 *
933 * @param a Source array
934 * @returns A new array, with empty values removed
935 */
936 function removeEmpty(a) {
937 var out = [];
938 for (var i = 0, iLen = a.length; i < iLen; i++) {
939 if (a[i]) {
940 // careful - will remove all falsy values!
941 out.push(a[i]);
942 }
943 }
944 return out;
945 }
946 /**
947 * Join data from an array, but only for specific columns.
948 *
949 * Performance testing for this available here:
950 * https://jsperf.app/vejijo/2/preview.
951 *
952 * @param src Data source array to pick from
953 * @param use Indexes we want from the array
954 * @returns Joined string
955 */
956 function selectiveJoin(src, use) {
957 if (typeof use === 'number') {
958 return '' + src[use];
959 }
960 if (use.length === 0) {
961 return '';
962 }
963 let result = '' + src[use[0]];
964 for (let i = 1; i < use.length; i++) {
965 result += ' ' + src[use[i]];
966 }
967 return result;
968 }
969 /**
970 * Find the unique elements in a source array.
971 *
972 * @param src Source array
973 * @return Array of unique items
974 */
975 function unique(src) {
976 if (Array.from && Set) {
977 return Array.from(new Set(src));
978 }
979 if (allUnique(src)) {
980 return src.slice();
981 }
982 // A faster unique method is to use object keys to identify used values,
983 // but this doesn't work with arrays or objects, which we must also
984 // consider. See jsperf.app/compare-array-unique-versions/4 for more
985 // information.
986 var out = [], val, i, iLen = src.length, j, k = 0;
987 again: for (i = 0; i < iLen; i++) {
988 val = src[i];
989 for (j = 0; j < k; j++) {
990 if (out[j] === val) {
991 continue again;
992 }
993 }
994 out.push(val);
995 k++;
996 }
997 return out;
998 }
999
1000 var array = /*#__PURE__*/Object.freeze({
1001 __proto__: null,
1002 flatten: flatten,
1003 intersection: intersection,
1004 pluck: pluck,
1005 pluckOrder: pluckOrder,
1006 range: range,
1007 removeEmpty: removeEmpty,
1008 selectiveJoin: selectiveJoin,
1009 unique: unique
1010 });
1011
1012 // Private variable that is used to match action syntax in the data property object
1013 const __reArray = /\[.*?\]$/;
1014 const __reFn = /\(\)$/;
1015 /**
1016 * Split string on periods, taking into account escaped periods
1017 *
1018 * @param str String to split
1019 * @return Split string
1020 */
1021 function splitObjNotation(str) {
1022 const parts = str.match(/(\\.|[^.])+/g) || [''];
1023 return parts.map(function (s) {
1024 return s.replace(/\\\./g, '.');
1025 });
1026 }
1027 /**
1028 * Create a function that will read data a common data point from different (but same structure)
1029 * data objects. This is primarily used to get data for a specific cell in a single column, but it
1030 * can also be used in other places, such as when using JSON notation.
1031 *
1032 * @param dataPoint The data point to get
1033 * @returns Function to get a data point's value from a source.
1034 */
1035 function get$1(dataPoint) {
1036 if (dataPoint === null) {
1037 // Give an empty string for rendering / sorting etc
1038 return function (data) {
1039 // type, row and meta also passed, but not used
1040 return data;
1041 };
1042 }
1043 else if (typeof dataPoint === 'function') {
1044 return function (data, type, row, meta) {
1045 return dataPoint(data, type, row, meta);
1046 };
1047 }
1048 else if (typeof dataPoint === 'string' &&
1049 (dataPoint.indexOf('.') !== -1 ||
1050 dataPoint.indexOf('[') !== -1 ||
1051 dataPoint.indexOf('(') !== -1)) {
1052 /* If there is a . in the source string then the data source is in a
1053 * nested object so we loop over the data for each level to get the next
1054 * level down. On each loop we test for undefined, and if found immediately
1055 * return. This allows entire objects to be missing and sDefaultContent to
1056 * be used if defined, rather than throwing an error
1057 */
1058 let fetchData = function (data, type, src) {
1059 let arrayNotation, funcNotation, out, innerSrc;
1060 if (src !== '') {
1061 let a = splitObjNotation(src);
1062 for (let i = 0, iLen = a.length; i < iLen; i++) {
1063 // Check if we are dealing with special notation
1064 arrayNotation = a[i].match(__reArray);
1065 funcNotation = a[i].match(__reFn);
1066 if (arrayNotation) {
1067 // Array notation
1068 a[i] = a[i].replace(__reArray, '');
1069 // Condition allows simply [] to be passed in
1070 if (a[i] !== '') {
1071 data = data[a[i]];
1072 }
1073 out = [];
1074 // Get the remainder of the nested object to get
1075 a.splice(0, i + 1);
1076 innerSrc = a.join('.');
1077 // Traverse each entry in the array getting the properties requested
1078 if (Array.isArray(data)) {
1079 for (let j = 0, jLen = data.length; j < jLen; j++) {
1080 out.push(fetchData(data[j], type, innerSrc));
1081 }
1082 }
1083 // If a string is given in between the array notation indicators, that
1084 // is used to join the strings together, otherwise an array is returned
1085 let join = arrayNotation[0].substring(1, arrayNotation[0].length - 1);
1086 data = join === '' ? out : out.join(join);
1087 // The inner call to fetchData has already traversed through the remainder
1088 // of the source requested, so we exit from the loop
1089 break;
1090 }
1091 else if (funcNotation) {
1092 // Function call
1093 a[i] = a[i].replace(__reFn, '');
1094 data = data[a[i]]();
1095 continue;
1096 }
1097 if (data === null || data[a[i]] === null) {
1098 return null;
1099 }
1100 else if (data === undefined || data[a[i]] === undefined) {
1101 return undefined;
1102 }
1103 data = data[a[i]];
1104 }
1105 }
1106 return data;
1107 };
1108 return function (data, type) {
1109 // row and meta also passed, but not used
1110 return fetchData(data, type, dataPoint);
1111 };
1112 }
1113 else if (plainObject(dataPoint)) {
1114 // Build an object of get functions, and wrap them in a single call
1115 let o = {};
1116 each(dataPoint, function (key, val) {
1117 if (val) {
1118 o[key] = get$1(val);
1119 }
1120 });
1121 return function (data, type, row, meta) {
1122 let t = o[type] || o._;
1123 return t !== undefined ? t(data, type, row, meta) : data;
1124 };
1125 }
1126 else {
1127 // Array or flat object mapping
1128 return function (data) {
1129 // row and meta also passed, but not used
1130 return data[dataPoint];
1131 };
1132 }
1133 }
1134 /**
1135 * Write a value into an existing data store
1136 *
1137 * @param dataPoint The data point to write to
1138 */
1139 function set$1(dataPoint) {
1140 if (dataPoint === null) {
1141 // Nothing to do when the data source is null
1142 return function () { };
1143 }
1144 else if (typeof dataPoint === 'function') {
1145 return function (data, val, meta) {
1146 dataPoint(data, 'set', val, meta);
1147 };
1148 }
1149 else if (typeof dataPoint === 'string' &&
1150 (dataPoint.indexOf('.') !== -1 ||
1151 dataPoint.indexOf('[') !== -1 ||
1152 dataPoint.indexOf('(') !== -1)) {
1153 // Like the get, we need to get data from a nested object
1154 let setData = function (data, val, src) {
1155 let a = splitObjNotation(src), b;
1156 let aLast = a[a.length - 1];
1157 let arrayNotation, funcNotation, o, innerSrc;
1158 for (let i = 0, iLen = a.length - 1; i < iLen; i++) {
1159 // Protect against prototype pollution
1160 if (a[i] === '__proto__' || a[i] === 'constructor') {
1161 throw new Error('Cannot set prototype values');
1162 }
1163 // Check if we are dealing with an array notation request
1164 arrayNotation = a[i].match(__reArray);
1165 funcNotation = a[i].match(__reFn);
1166 if (arrayNotation) {
1167 a[i] = a[i].replace(__reArray, '');
1168 data[a[i]] = [];
1169 // Get the remainder of the nested object to set so we can recurse
1170 b = a.slice();
1171 b.splice(0, i + 1);
1172 innerSrc = b.join('.');
1173 // Traverse each entry in the array setting the properties requested
1174 if (Array.isArray(val)) {
1175 for (let j = 0, jLen = val.length; j < jLen; j++) {
1176 o = {};
1177 setData(o, val[j], innerSrc);
1178 data[a[i]].push(o);
1179 }
1180 }
1181 else {
1182 // We've been asked to save data to an array, but it
1183 // isn't array data to be saved. Best that can be done
1184 // is to just save the value.
1185 data[a[i]] = val;
1186 }
1187 // The inner call to setData has already traversed through the remainder
1188 // of the source and has set the data, thus we can exit here
1189 return;
1190 }
1191 else if (funcNotation) {
1192 // Function call
1193 a[i] = a[i].replace(__reFn, '');
1194 data = data[a[i]](val);
1195 }
1196 // If the nested object doesn't currently exist - since we are
1197 // trying to set the value - create it
1198 if (data[a[i]] === null || data[a[i]] === undefined) {
1199 data[a[i]] = {};
1200 }
1201 data = data[a[i]];
1202 }
1203 // Last item in the input - i.e, the actual set
1204 if (aLast.match(__reFn)) {
1205 // Function call
1206 data = data[aLast.replace(__reFn, '')](val);
1207 }
1208 else {
1209 // If array notation is used, we just want to strip it and use the property name
1210 // and assign the value. If it isn't used, then we get the result we want anyway
1211 data[aLast.replace(__reArray, '')] = val;
1212 }
1213 };
1214 return function (data, val) {
1215 // meta is also passed in, but not used
1216 return setData(data, val, dataPoint);
1217 };
1218 }
1219 else if (plainObject(dataPoint)) {
1220 /* Unlike get, only the underscore (global) option is used for for
1221 * setting data since we don't know the type here. This is why an object
1222 * option is not documented for `mData` (which is read/write), but it is
1223 * for `render` which is read only.
1224 */
1225 return set$1(dataPoint._);
1226 }
1227 else {
1228 // Array or flat object mapping
1229 return function (data, val) {
1230 // meta is also passed in, but not used
1231 data[dataPoint] = val;
1232 };
1233 }
1234 }
1235
1236 var data = /*#__PURE__*/Object.freeze({
1237 __proto__: null,
1238 get: get$1,
1239 set: set$1
1240 });
1241
1242 // Can be assigned in DateTable.use()
1243 var __bootstrap;
1244 var __foundation;
1245 var __luxon$1;
1246 var __moment$1;
1247 var __dateTime;
1248 var __dataTable;
1249 var __jquery;
1250 /**
1251 * Set the libraries that DataTables uses, or the global objects.
1252 * Note that the arguments can be either way around (legacy support)
1253 * and the second is optional. See docs.
1254 */
1255 function external (arg1, arg2) {
1256 // Reverse arguments for legacy support
1257 var module = typeof arg1 === 'string' ? arg2 : arg1;
1258 var type = typeof arg2 === 'string' ? arg2 : arg1;
1259 // Getter
1260 if (module === undefined && typeof type === 'string') {
1261 switch (type) {
1262 case 'lib':
1263 case 'jq':
1264 return __jquery !== undefined ? __jquery : window.jQuery || null;
1265 case 'win':
1266 return window;
1267 case 'datatable':
1268 return __dataTable;
1269 case 'datetime':
1270 return __dateTime;
1271 case 'luxon':
1272 return __luxon$1 || window.luxon || null;
1273 case 'moment':
1274 return __moment$1 || window.moment || null;
1275 case 'bootstrap':
1276 // Use local if set, otherwise try window, which could be undefined
1277 return __bootstrap || window.bootstrap || null;
1278 case 'foundation':
1279 // Ditto
1280 return __foundation || window.Foundation || null;
1281 default:
1282 return null;
1283 }
1284 }
1285 // Setter
1286 if (type === 'lib' ||
1287 type === 'jq' ||
1288 (module && module.fn && module.fn.jquery)) {
1289 __jquery = module;
1290 jQuerySetup();
1291 }
1292 else if (type === 'datatable' || (module && module.isDataTable)) {
1293 __dataTable = module;
1294 }
1295 else if (type === 'win' || (module && module.document)) {
1296 window = module;
1297 document = module.document;
1298 }
1299 else if (type === 'datetime' || (module && module.type === 'DateTime')) {
1300 __dateTime = module;
1301 }
1302 else if (type === 'luxon' || (module && module.FixedOffsetZone)) {
1303 __luxon$1 = module;
1304 }
1305 else if (type === 'moment' || (module && module.isMoment)) {
1306 __moment$1 = module;
1307 }
1308 else if (type === 'bootstrap' ||
1309 (module && module.Modal && module.Modal.NAME === 'modal')) {
1310 // This is currently for BS5 only. BS3/4 attach to jQuery, so no need to use `.use()`
1311 __bootstrap = module;
1312 }
1313 else if (type === 'foundation' || (module && module.Reveal)) {
1314 __foundation = module;
1315 }
1316 }
1317 /**
1318 * Attach jQuery to DataTables
1319 */
1320 function jQuerySetup() {
1321 if (!__dataTable || !__jquery) {
1322 return;
1323 }
1324 // Provide access to the host jQuery object (circular reference)
1325 __dataTable.$ = __jquery;
1326 // jQuery integration - expose the core function.
1327 __jquery.fn.dataTable = __dataTable;
1328 // jQuery wrapper - returning a DataTable instance
1329 __jquery.fn.DataTable = function (options) {
1330 let table = new __dataTable(this.toArray(), options);
1331 return table;
1332 };
1333 // Legacy aliases
1334 __jquery.fn.dataTableSettings = __dataTable.ext.settings;
1335 __jquery.fn.dataTableExt = __dataTable.ext;
1336 // All properties that are available to $.fn.dataTable should also be available
1337 // on $.fn.DataTable
1338 each(__dataTable, function (prop, val) {
1339 __jquery.fn.DataTable[prop] = val;
1340 });
1341 }
1342
1343 function debounce(fn, timeout = 250) {
1344 let timer;
1345 return function (...args) {
1346 clearTimeout(timer);
1347 timer = setTimeout(() => {
1348 fn.call(this, ...args);
1349 }, timeout);
1350 };
1351 }
1352 function throttle(fn, freq = 200) {
1353 let last, timer;
1354 return function (...args) {
1355 const now = +new Date();
1356 if (last && now < last + freq) {
1357 clearTimeout(timer);
1358 timer = setTimeout(() => {
1359 last = undefined;
1360 fn.call(this, ...args);
1361 }, freq);
1362 }
1363 else {
1364 last = now;
1365 fn.call(this, ...args);
1366 }
1367 };
1368 }
1369
1370 var timer = /*#__PURE__*/Object.freeze({
1371 __proto__: null,
1372 debounce: debounce,
1373 throttle: throttle
1374 });
1375
1376 /**
1377 * Provide a common method for plug-ins to check the version of DataTables being
1378 * used, in order to ensure compatibility.
1379 *
1380 * @param version1 Version string to check for, in the format "X.Y.Z". Note that
1381 * the formats "X" and "X.Y" are also acceptable.
1382 * @param version2 As above, but optional. If not given the current DataTables
1383 * version will be used.
1384 * @returns true if this version of DataTables is greater or equal to the
1385 * required version, or false if this version of DataTales is not suitable
1386 */
1387 function check$1(version1, version2) {
1388 let dt = external('datatable');
1389 var parts1 = version2 ? version2.split('.') : dt.ext.version.split('.');
1390 var parts2 = version1.split('.');
1391 var int1, int2;
1392 for (var i = 0, iLen = parts2.length; i < iLen; i++) {
1393 int1 = parseInt(parts1[i], 10) || 0;
1394 int2 = parseInt(parts2[i], 10) || 0;
1395 // Parts are the same, keep comparing
1396 if (int1 === int2) {
1397 continue;
1398 }
1399 // Parts are different, return immediately
1400 return int1 > int2;
1401 }
1402 return true;
1403 }
1404
1405 var version = /*#__PURE__*/Object.freeze({
1406 __proto__: null,
1407 check: check$1
1408 });
1409
1410 // Note that the aliased properties are for compatibility with DataTables 2-
1411 // which had a set of `util` functions.
1412 var util = {
1413 ajax,
1414 array,
1415 conv,
1416 data,
1417 /** @see timer.debounce */
1418 debounce: debounce,
1419 /** @see string.normalize */
1420 diacritics: normalize,
1421 /** @see string.escapeHtml */
1422 escapeHtml: escapeHtml,
1423 /** @see string.escapeRegex */
1424 escapeRegex: escapeRegex,
1425 external,
1426 /** @see data.get */
1427 get: get$1,
1428 is,
1429 object,
1430 regex,
1431 /** @see data.set */
1432 set: set$1,
1433 string,
1434 /** @see string.stripHtml */
1435 stripHtml: stripHtml,
1436 /** @see timer.throttle */
1437 throttle: throttle,
1438 timer,
1439 /** @see array.unique */
1440 unique: unique,
1441 version
1442 };
1443
1444 /** Each element with an event attached needs a unique id */
1445 let _uidCounter = 1;
1446 /**
1447 * All wrapped event handlers are stored in this array so we can refer back to
1448 * them for removal. Each entry in the array is for a unique element, using the
1449 * index to refer to it (the `uid` that is attached to the element).
1450 */
1451 const _eventStore = [];
1452 /**
1453 * Get a unique id that can be assigned to an element.
1454 *
1455 * @returns UID
1456 */
1457 function getUid(el) {
1458 if (!el._event_uid) {
1459 el._event_uid = _uidCounter++;
1460 }
1461 return el._event_uid;
1462 }
1463 /**
1464 * Get all event handlers that have been assigned to an element
1465 *
1466 * @param el Element
1467 * @returns Array of functions
1468 */
1469 function get(el) {
1470 let uid = el._event_uid;
1471 if (!uid || !_eventStore[uid]) {
1472 return null;
1473 }
1474 return _eventStore[uid];
1475 }
1476 /**
1477 * Store an event handler for an element (does not apply it)
1478 *
1479 * @param el Element
1480 * @param wrapper Function to set
1481 */
1482 function set(el, wrapper) {
1483 let uid = getUid(el);
1484 if (_eventStore[uid] === undefined) {
1485 _eventStore[uid] = [];
1486 }
1487 _eventStore[uid].push(wrapper);
1488 }
1489 /**
1490 * Remove an event handler from an element's store
1491 *
1492 * @param el Element
1493 * @param wrapper Function to set
1494 * @returns void
1495 */
1496 function remove$1(el, wrapper) {
1497 let store = get(el);
1498 if (!store) {
1499 return;
1500 }
1501 let idx = store.indexOf(wrapper);
1502 if (idx !== -1) {
1503 store.splice(idx, 1);
1504 }
1505 }
1506
1507 /*
1508 * We need an events library that has many of the features of jQuery's event
1509 * handling - this is for backwards compatibility. Developers who have used
1510 * jQuery to listen for events should not need to change their code!
1511 *
1512 * DataTables uses the following features of jQuery events, which need to be
1513 * fully supported:
1514 *
1515 * * Namespaces
1516 * * Custom events
1517 * * Custom event object properties
1518 * * Arguments for custom events
1519 *
1520 * Frustratingly while `dispatchEvent` will trigger all events listened to by
1521 * jQuery (including namespaces with a shim of `jQuery.event.fix` to add the
1522 * namespace and rnamespace properties), there is no way for `dispatchEvent` to
1523 * pass arguments to custom event handlers.
1524 *
1525 * Also the inverse doesn't hold - using `addEventListener` followed by
1526 * `$().trigger()` doesn't trigger the event handler. Therefore using both
1527 * `dispatchEvent` and `$().trigger()` to fire off both event handlers would
1528 * risk triggering some event handlers twice.
1529 *
1530 * Because of the backwards compatibility constraint and the complications given
1531 * above, this library will act as a simple proxy to jQuery, if jQuery is
1532 * present. If it is not, then it will implement the features described above
1533 * itself. While this is not ideal (I'd prefer to have a way to trigger jQuery
1534 * listened for events independently of triggering those added with this
1535 * library, or any other `addEventListener` call), it does ensure backwards
1536 * compatibility.
1537 */
1538 const _mouseEvents = [
1539 'click',
1540 'dblclick',
1541 'mousedown',
1542 'mouseenter',
1543 'mouseleave',
1544 'mousemove',
1545 'mouseout',
1546 'mouseover',
1547 'mouseup'
1548 ];
1549 /**
1550 * Add a property to an event object.
1551 *
1552 * @param event Event
1553 * @param name Property name
1554 * @param value Value to give the value
1555 */
1556 function setEventProp(event, name, value) {
1557 // Can't use do `event[name] =` - event object can't always have properties
1558 // added like that.
1559 Object.defineProperty(event, name, {
1560 configurable: true,
1561 get() {
1562 return value;
1563 }
1564 });
1565 }
1566 /**
1567 * Check that an element matches a given selector for a given event (ie its
1568 * target)
1569 *
1570 * @param el Root element
1571 * @param selector CSS selector
1572 * @param event Event that
1573 * @returns The matching element if there is one
1574 */
1575 function delegateTarget(el, selector, event) {
1576 // Its a delegate - get all elements that match the selector as
1577 // descendants from the element the event was triggered on
1578 let elements = Array.from(el.querySelectorAll(selector));
1579 let target = event.target;
1580 // The event might originate below our target, so need to climb the ladder
1581 for (; target && target !== this; target = target.parentNode) {
1582 // Needs to happen for all matched descendants
1583 for (let element of elements) {
1584 // And only call it when matched
1585 if (element !== target) {
1586 continue;
1587 }
1588 return target;
1589 }
1590 }
1591 }
1592 /**
1593 * Get the event name and namespaces from a string
1594 *
1595 * @param original event name and dot delimited namespaces
1596 * @returns Object with split parts
1597 */
1598 function parseEventName(original) {
1599 if (!original) {
1600 return {
1601 eventName: null,
1602 namespaces: []
1603 };
1604 }
1605 let parts = original.split('.');
1606 let name = parts.shift();
1607 let isHover = false;
1608 let isFocus = false;
1609 // mouse[enter|leave] and focus|blur don't bubble, but do have counterparts
1610 // which do, so we make use of them, as this allows event delegation on
1611 // those event names to work as expected.
1612 if (name === 'mouseenter') {
1613 name = 'mouseover';
1614 isHover = true;
1615 }
1616 else if (name === 'mouseleave') {
1617 name = 'mouseout';
1618 isHover = true;
1619 }
1620 else if (name === 'focus') {
1621 name = 'focusin';
1622 isFocus = true;
1623 }
1624 else if (name === 'blur') {
1625 name = 'focusout';
1626 isFocus = true;
1627 }
1628 else if (name === 'ready') {
1629 name = 'DOMContentLoaded';
1630 }
1631 return {
1632 eventName: name,
1633 isFocus,
1634 isHover,
1635 namespaces: parts
1636 };
1637 }
1638 /**
1639 * Add an event listener to a function
1640 *
1641 * @param el The element to add an event handler to
1642 * @param nameFull Event name. This can optionally be followed by a dot
1643 * separated list of namespaces, a la jQuery. This allows for easy event
1644 * removal and also matching triggering.
1645 * @param handler Callback function to execute
1646 * @param selector Delegate selector. `null` for non-delegate events
1647 * @param one Indicate if the event handler should execute only once and then be
1648 * removed.
1649 */
1650 function add(el, nameFull, handler, selector, one) {
1651 let jq = external('jq');
1652 if (jq) {
1653 let method = one ? 'one' : 'on';
1654 if (selector) {
1655 jq(el)[method](nameFull, selector, handler);
1656 }
1657 else {
1658 jq(el)[method](nameFull, handler);
1659 }
1660 return;
1661 }
1662 // No jQuery - add the event ourselves
1663 let { eventName, namespaces, isFocus, isHover } = parseEventName(nameFull);
1664 if (!eventName) {
1665 return;
1666 }
1667 // Special handling for the "ready" event - it will trigger when the content
1668 // is ready, but also if it is already ready, when added.
1669 if (el === document && eventName === 'DOMContentLoaded' && nameFull.includes('ready')) {
1670 if (document.readyState === 'complete') {
1671 handler(new Event('DOMContentLoaded'));
1672 return;
1673 }
1674 }
1675 // Create a function that will be the actual event handler, and performs any
1676 // logic we need, such as delegate handling and adding properties.
1677 let wrapped = function (event) {
1678 let callScope = el; // Scope for the callback function
1679 // If the event has a namespace (from a trigger), then the handler
1680 // should only be run if there is at least one namespace being listened
1681 // for that matches. This is an OR operation.
1682 if (event.namespace &&
1683 !intersection(namespaces, event.namespace.split('.')).length) {
1684 return;
1685 }
1686 // If a special (overridden) type, then we need to check that they apply
1687 if (!selector &&
1688 ((isFocus && event.target !== el) ||
1689 (isHover &&
1690 event.relatedTarget &&
1691 el.contains(event.relatedTarget)))) {
1692 return;
1693 }
1694 // For delegate events, determine if the target matches our selector
1695 if (selector) {
1696 let dTarget = delegateTarget(el, selector, event);
1697 if (!dTarget) {
1698 return;
1699 }
1700 if (isHover &&
1701 event.relatedTarget &&
1702 dTarget.contains(event.relatedTarget)) {
1703 return;
1704 }
1705 callScope = dTarget;
1706 }
1707 // Set the properties that jQuery adds to the event object
1708 setEventProp(event, 'currentTarget', callScope);
1709 setEventProp(event, 'delegateTarget', el);
1710 setEventProp(event, 'relatedTarget', event.relatedTarget);
1711 // If it was triggered, extra data can be passed through using the
1712 // arguments passed to trigger.
1713 let retVal = handler.apply(callScope, [event, ...(event._args || [])]);
1714 if (one) {
1715 remove(el, eventName, handler, selector);
1716 }
1717 if (retVal === false) {
1718 event.preventDefault();
1719 event.stopPropagation();
1720 }
1721 event.result = retVal;
1722 };
1723 wrapped.delegateSelector = selector;
1724 wrapped.original = handler;
1725 wrapped.one = one;
1726 wrapped.type = eventName;
1727 wrapped.namespaces = namespaces;
1728 set(el, wrapped);
1729 el.addEventListener(eventName, wrapped);
1730 }
1731 /**
1732 * Remove an event from an element
1733 *
1734 * @param el The element to remove the event(s) from
1735 * @param nameFull Event name and / or dot separated namespaces
1736 * @param handler The function to remove (optional)
1737 * @param selector Delegate selector (optional)
1738 */
1739 function remove(el, nameFull, handler, selector) {
1740 let jq = external('jq');
1741 if (jq) {
1742 if (selector) {
1743 jq(el).off(nameFull, selector, handler);
1744 }
1745 else {
1746 jq(el).off(nameFull, handler);
1747 }
1748 return;
1749 }
1750 // No jQuery - do it our way
1751 let { eventName, namespaces } = parseEventName(nameFull);
1752 let removeEvents = [];
1753 let stored = get(el);
1754 if (stored === null) {
1755 return;
1756 }
1757 if (eventName && selector && handler) {
1758 removeEvents = stored.filter(wrapped => wrapped.type === eventName &&
1759 wrapped.delegateSelector === selector &&
1760 wrapped.original === handler);
1761 }
1762 else if (eventName && selector) {
1763 removeEvents = stored.filter(wrapped => wrapped.type === eventName &&
1764 wrapped.delegateSelector === selector);
1765 }
1766 else if (eventName && handler) {
1767 removeEvents = stored.filter(wrapped => wrapped.type === eventName && wrapped.original === handler);
1768 }
1769 else if (eventName) {
1770 removeEvents = stored.filter(wrapped => wrapped.type === eventName);
1771 }
1772 else {
1773 // No name, use all events
1774 removeEvents = stored;
1775 }
1776 // If namespaces were given then we need to filter down to just those event
1777 // handlers which have the given namespaces
1778 if (namespaces.length) {
1779 removeEvents = removeEvents.filter(
1780 // The event needs to match all of the namespaces given in order to
1781 // be removed - this matches jQuery's behaviour. The event could
1782 // have other namespaces. Do this by filtering to just the filtering
1783 // namespaces and check that the length matches
1784 ev => ev.namespaces.filter(ns => namespaces.includes(ns)).length ===
1785 namespaces.length);
1786 }
1787 removeEvents.forEach(wrapped => {
1788 remove$1(el, wrapped);
1789 el.removeEventListener(wrapped.type, wrapped);
1790 });
1791 }
1792 /**
1793 * Trigger an event on an element. Can have extra data given, which is useful
1794 * for custom events.
1795 *
1796 * @param el Element to trigger the event on
1797 * @param nameFull Event name with optional dot separated namespaces
1798 * @param bubbles If the event should bubble up through the DOM or not
1799 * @param args Array of arguments to pass to the event handler
1800 * @param eventProps Object of extra parameters to attach to the event object
1801 * @param returnEvent Indicate if the return should be the event object (for
1802 * further processing) or the default prevented state.
1803 * @returns `true` if default was NOT prevented, `false` if default was
1804 * prevented. If `returnEvent` is `true` then the return will be the event
1805 * object.
1806 */
1807 function trigger(el, nameFull, bubbles = false, args = [], eventProps = null, returnEvent = false) {
1808 let jq = external('jq');
1809 if (jq) {
1810 let method = bubbles ? 'trigger' : 'triggerHandler';
1811 let ev = jq.Event(nameFull);
1812 each(eventProps, (key, val) => {
1813 setEventProp(ev, key, val);
1814 });
1815 jq(el)[method](ev, args || []);
1816 if (returnEvent) {
1817 ev.defaultPrevented = ev.isDefaultPrevented();
1818 return ev;
1819 }
1820 // See note below regarding the inversion
1821 return !ev.isDefaultPrevented();
1822 }
1823 // No jQuery
1824 let { eventName, namespaces } = parseEventName(nameFull);
1825 if (!eventName) {
1826 return false;
1827 }
1828 let isMouseEvent = _mouseEvents.includes(eventName.toLowerCase());
1829 let event = isMouseEvent
1830 ? new MouseEvent(eventName, { bubbles, cancelable: true })
1831 : new Event(eventName, { bubbles, cancelable: true });
1832 // Set the extra properties for the event
1833 setEventProp(event, 'namespace', namespaces.join('.'));
1834 setEventProp(event, '_args', args || []);
1835 each(eventProps, (key, val) => setEventProp(event, key, val));
1836 el.dispatchEvent(event);
1837 // A lot of the old DataTables stuff checks for a `false` return to prevent
1838 // the default action. To maintain compatibility we return an inverted
1839 // `defaultPrevented` here - i.e. it becomes `do default`.
1840 return returnEvent ? event : !event.defaultPrevented;
1841 }
1842
1843 // Window level functions
1844 var win = {
1845 /**
1846 * Get the height of the window, excluding a horizontal scrollbar if it is
1847 * present.
1848 *
1849 * @returns Height in pixels
1850 */
1851 height() {
1852 var _a;
1853 return ((_a = document.querySelector('html')) === null || _a === void 0 ? void 0 : _a.clientHeight) || 0;
1854 },
1855 /**
1856 * Remove an event handler from the window
1857 *
1858 * @param name Event name (can include or just be a namespace)
1859 * @param cb Event callback function
1860 */
1861 off(name, cb = null) {
1862 remove(window, name, cb, null);
1863 },
1864 /**
1865 * Add an event handler to the window
1866 *
1867 * @param name Event name (can include a namespace)
1868 * @param cb Event callback function
1869 */
1870 on(name, cb) {
1871 add(window, name, cb, null, false);
1872 },
1873 /**
1874 * Add an event handler to the window that will execute just once
1875 *
1876 * @param name Event name (can include a namespace)
1877 * @param cb Event callback function
1878 */
1879 one(name, cb) {
1880 add(window, name, cb, null, true);
1881 },
1882 /**
1883 * Get the left scroll offset of the window / document
1884 *
1885 * @param set Set the scroll position
1886 * @returns Window X scroll offset in pixels
1887 */
1888 scrollLeft(set) {
1889 if (set !== undefined) {
1890 window.scrollX = set;
1891 }
1892 return window.scrollX;
1893 },
1894 /**
1895 * Get the top scroll offset of the window / document
1896 *
1897 * @param set Set the scroll position
1898 * @returns Window Y scroll offset in pixels
1899 */
1900 scrollTop(set) {
1901 if (set !== undefined) {
1902 window.scrollY = set;
1903 }
1904 return window.scrollY;
1905 },
1906 /**
1907 * Get the width of the window, excluding a vertical scrollbar if it is
1908 * present.
1909 *
1910 * @returns Width in pixels
1911 */
1912 width() {
1913 var _a;
1914 return ((_a = document.querySelector('html')) === null || _a === void 0 ? void 0 : _a.clientWidth) || 0;
1915 }
1916 };
1917
1918 function create$3(name) {
1919 let el = document.createElement(name);
1920 return new Dom(el);
1921 }
1922 function select(selector) {
1923 return new Dom(selector);
1924 }
1925 /**
1926 * `Dom` is a class that provides a chaining UI for simple DOM manipulation and
1927 * selection.
1928 */
1929 class Dom {
1930 /**
1931 * `Dom` is used for selection and manipulation of the DOM elements in a
1932 * chaining interface.
1933 *
1934 * @param selector
1935 */
1936 constructor(selector) {
1937 /** Number of elements in the array */
1938 this.length = 0;
1939 /** Flag to indicate that this is a Dom instance */
1940 this._isDom = true;
1941 if (selector) {
1942 this.add(selector);
1943 }
1944 }
1945 /**
1946 * Add an element (or multiple) to the instance. Will ensure uniqueness.
1947 *
1948 * @param selector Element(s) to add
1949 * @param sort Indicate if the element should be added in document order.
1950 * @returns Self for chaining
1951 */
1952 add(selector, sort = true) {
1953 if (selector) {
1954 if (typeof selector === 'string') {
1955 let elements = Array.from(document.querySelectorAll(selector));
1956 addArray(this, elements);
1957 }
1958 else if (selector instanceof Dom) {
1959 addArray(this, selector.get());
1960 }
1961 else if (typeof selector === 'object' &&
1962 !selector.nodeName && // <select> has a length!
1963 selector.length !== undefined) {
1964 // Array-like - could be a jQuery instance or DataTables API
1965 // instance
1966 let arrayLike = selector;
1967 for (let i = 0; i < arrayLike.length; i++) {
1968 addArray(this, arrayLike[i]);
1969 }
1970 sort = false;
1971 }
1972 else {
1973 addArray(this, selector);
1974 }
1975 }
1976 if (sort) {
1977 this.sort();
1978 }
1979 return this;
1980 }
1981 /**
1982 * Insert the given content to each item in the result set.
1983 *
1984 * Limit your result set to a single item!
1985 *
1986 * @param content The content to append
1987 * @returns Self for chaining
1988 */
1989 append(content) {
1990 if (!content) {
1991 return this;
1992 }
1993 if (!arrayLike(content)) {
1994 content = [content];
1995 }
1996 // Generate a flat array of the content to be added, with nulls removed
1997 // this means it will be an array of nodes and / or strings
1998 let flatContent = flatten([], content).filter(c => !!c);
1999 /// Got a string somewhere in it, so need to use insertAdjacentHTML
2000 if (flatContent.find(val => typeof val === 'string')) {
2001 return this.each(el => {
2002 for (let i = 0; i < flatContent.length; i++) {
2003 if (typeof flatContent[i] === 'string') {
2004 el.insertAdjacentHTML('beforeend', flatContent[i]);
2005 }
2006 else {
2007 el.append(flatContent[i]);
2008 }
2009 }
2010 });
2011 }
2012 // Otherwise we can use a document fragment for a single mutation
2013 // on the main document
2014 return this.each(el => {
2015 let fragment = new DocumentFragment();
2016 for (let i = 0; i < flatContent.length; i++) {
2017 fragment.append(flatContent[i]);
2018 }
2019 el.append(fragment);
2020 });
2021 }
2022 /**
2023 * Append the current data set items to the element from the selector
2024 *
2025 * @param selector
2026 */
2027 appendTo(selector) {
2028 let inst = selector instanceof Dom ? selector : new Dom(selector);
2029 inst.append(this);
2030 return this;
2031 }
2032 attr(name, value) {
2033 if (typeof name === 'string' && value === undefined) {
2034 return this.count() ? this[0].getAttribute(name) : null;
2035 }
2036 return this.each(el => {
2037 if (typeof name === 'string') {
2038 if (value !== undefined && value !== null) {
2039 el.setAttribute(name, typeof value === 'string' ? value : value.toString());
2040 }
2041 }
2042 else {
2043 each(name, (key, val) => {
2044 if (val !== undefined && val !== null) {
2045 el.setAttribute(key, val);
2046 }
2047 });
2048 }
2049 });
2050 }
2051 /**
2052 * Remove an attribute on each element in the result set
2053 *
2054 * @param attr Attribute to remove
2055 * @returns Self for chaining
2056 */
2057 attrRemove(attr) {
2058 return this.each(el => el.removeAttribute(attr));
2059 }
2060 /**
2061 * Blur on the target elements
2062 *
2063 * @returns Self for chaining
2064 */
2065 blur() {
2066 return this.each(el => el.blur());
2067 }
2068 /**
2069 * Get the child from all elements in the result set
2070 *
2071 * @param selector Query string that the child much match to be selected
2072 * @returns New Dom instance with children as the result set
2073 */
2074 children(selector) {
2075 return this.map(el => {
2076 let children = Array.from(el.children);
2077 return selector
2078 ? children.filter(child => child.matches(selector))
2079 : children;
2080 });
2081 }
2082 /**
2083 * Add one or more class names to the result set
2084 *
2085 * @param name Class name(s) to set
2086 * @returns Self for chaining
2087 */
2088 classAdd(name) {
2089 if (!name) {
2090 return this;
2091 }
2092 let names = stringArrays(name);
2093 return this.each(el => {
2094 names.filter(n => n).forEach(n => el.classList.add(n));
2095 });
2096 }
2097 /**
2098 * Check if the first element in the result set has the given class
2099 *
2100 * @param name Class name to check for
2101 * @returns Self for chaining
2102 */
2103 classHas(name) {
2104 return this.count() ? this[0].classList.contains(name) : false;
2105 }
2106 /**
2107 * Remove the given class(s) from all elements in the result set
2108 *
2109 * @param name Class name to remove
2110 * @returns Self for chaining
2111 */
2112 classRemove(name) {
2113 if (!name) {
2114 return this;
2115 }
2116 let names = stringArrays(name);
2117 return this.each(el => {
2118 names.filter(n => n).forEach(n => el.classList.remove(n));
2119 });
2120 }
2121 /**
2122 * Toggle a class on all elements in the result set
2123 *
2124 * @param name Class name(s) to toggle - space separated
2125 * @param toggle Toggle on or off
2126 * @returns Self for chaining
2127 */
2128 classToggle(name, toggle) {
2129 let names = Array.isArray(name) ? name : name.split(' ');
2130 return this.each(el => {
2131 names.filter(n => n).forEach(n => el.classList.toggle(n, toggle));
2132 });
2133 }
2134 /**
2135 * Clone the nodes in the result set and return a new instance
2136 *
2137 * @param deep Include the subtree (`true`) or not (`false` - default)
2138 * @returns New Dom instance with new elements
2139 */
2140 clone(deep = false) {
2141 return this.map(el => el.cloneNode(deep));
2142 }
2143 /**
2144 * Find the closest ancestor for each element in the result set
2145 *
2146 * @param selector
2147 * @returns New Dom instance when the matching ancestors
2148 */
2149 closest(selector) {
2150 if (typeof selector === 'string') {
2151 return this.map(el => el.closest(selector));
2152 }
2153 return this.map(el => {
2154 // Traverse up the tree seeing if the element matches
2155 while (el.parentElement) {
2156 if (el.parentElement === selector) {
2157 return selector;
2158 }
2159 el = el.parentElement;
2160 }
2161 // Nothing found
2162 return null;
2163 });
2164 }
2165 /**
2166 * Determine if the result set contains the element specified. Shorthand for
2167 * .find().count()
2168 *
2169 * @param input Element / selector to look for
2170 * @returns true if it does contain, false otherwise
2171 */
2172 contains(input) {
2173 return this.find(input).count() !== 0;
2174 }
2175 /**
2176 * Get the number of elements in the current result set
2177 *
2178 * @returns Number of elements
2179 */
2180 count() {
2181 return this.length;
2182 }
2183 css(rule, value) {
2184 // String getter
2185 if (typeof rule === 'string' && value === undefined) {
2186 return this.length ? getComputedStyle(this[0])[rule] : null;
2187 }
2188 return this.each(el => {
2189 if (typeof rule === 'string') {
2190 // String setter
2191 el.style[rule] = value;
2192 }
2193 else {
2194 // Object set of rules
2195 Object.assign(el.style, rule);
2196 }
2197 });
2198 }
2199 data(name, value) {
2200 if (!name) {
2201 let out = {};
2202 if (!this.count()) {
2203 return out;
2204 }
2205 util.object.each(this[0].dataset, (key, val) => {
2206 out[key] = dataConvert(val);
2207 });
2208 return out;
2209 }
2210 if (typeof name === 'string' && value === undefined) {
2211 return this.length ? dataConvert(this[0].dataset[name]) : null;
2212 }
2213 if (typeof name === 'string') {
2214 this.each(el => (el.dataset[name] = JSON.stringify(value)));
2215 }
2216 else {
2217 each(name, (key, val) => {
2218 this.each(el => (el.dataset[key] = JSON.stringify(val)));
2219 });
2220 }
2221 return this;
2222 }
2223 /**
2224 * Remove the elements in the result set from the document. Does not remove
2225 * event listeners.
2226 *
2227 * @returns Self for chaining
2228 */
2229 detach() {
2230 return this.each(el => el.remove());
2231 }
2232 /**
2233 * Remove the child elements from each element in the result set from the
2234 * document. Does not remove event listeners.
2235 *
2236 * @returns Self for chaining
2237 */
2238 detachChildren() {
2239 return this.each(el => {
2240 el.replaceChildren();
2241 });
2242 }
2243 /**
2244 * Iterate over each item in the result set and perform an action
2245 *
2246 * @param callback Callback function
2247 * @returns Self for chaining
2248 */
2249 each(callback) {
2250 for (let i = 0; i < this.length; i++) {
2251 let el = this[i];
2252 callback.call(el, el, i);
2253 }
2254 return this;
2255 }
2256 /**
2257 * Inverse iteration over each item in the result set and perform an action
2258 *
2259 * @param callback Callback function
2260 * @returns Self for chaining
2261 */
2262 eachReverse(callback) {
2263 for (let i = this.length - 1; i >= 0; i--) {
2264 let el = this[i];
2265 callback.call(el, el, i);
2266 }
2267 return this;
2268 }
2269 /**
2270 * Remove all children
2271 *
2272 * @returns Self for chaining
2273 */
2274 empty() {
2275 // TODO should remove event listeners
2276 return this.each(el => {
2277 var _a;
2278 if (el.replaceChildren) {
2279 el.replaceChildren();
2280 }
2281 else {
2282 while (el.childNodes.length) {
2283 (_a = el.firstChild) === null || _a === void 0 ? void 0 : _a.remove();
2284 }
2285 }
2286 });
2287 }
2288 /**
2289 * Get a new Dom instance with just a specific element from the result set
2290 *
2291 * @param idx The element to use
2292 * @returns New Dom instance
2293 */
2294 eq(idx) {
2295 return idx < this.count() ? new Dom(this.get(idx)) : new Dom();
2296 }
2297 get(idx) {
2298 return idx !== undefined ? this[idx] : Array.from(this);
2299 }
2300 /**
2301 * Call focus on the target elements
2302 *
2303 * @returns Self for chaining
2304 */
2305 focus() {
2306 return this.each(el => el.focus());
2307 }
2308 /**
2309 * Reduce the result set based on a given filter, which can be a CSS
2310 * selector, an element or array of elements.
2311 *
2312 * @param filter Optional selector or function that the result set element
2313 * would need to match to be selected.
2314 * @returns New Dom instance containing the filters elements
2315 */
2316 filter(filter) {
2317 return this.map(el => {
2318 if (filter === undefined) {
2319 return el;
2320 }
2321 if (typeof filter === 'function') {
2322 return filter(el) ? el : null;
2323 }
2324 // Direct match - allows an element to be given as the filter
2325 if (typeof filter !== 'string') {
2326 if (arrayLike(filter)) {
2327 return Array.from(filter).includes(el) ? el : null;
2328 }
2329 return filter === el ? el : null;
2330 }
2331 // CSS selector
2332 if (!el.matches(filter)) {
2333 return null;
2334 }
2335 // If there is a pseudo child selector, want to check that the
2336 // element is actually in the document, if not, then
2337 // `:first-child` (etc) will match detached elements, which is
2338 // not desirable.
2339 if (!el.parentNode &&
2340 (filter.match(/:\w+-child/) || filter.match(/:\w+-of-type/))) {
2341 return null;
2342 }
2343 return el;
2344 });
2345 }
2346 /**
2347 * Get all matching descendants
2348 *
2349 * @param input Elements to find
2350 * @returns A new Dom instance with all matching elements
2351 */
2352 find(input) {
2353 if (input === null) {
2354 return new Dom();
2355 }
2356 // Text based selector - loop over each element in the result set, doing
2357 // the search on each and adding to a new instance.
2358 if (typeof input === 'string') {
2359 return this.map(el => Array.from(el.querySelectorAll(input)));
2360 }
2361 let selector = input instanceof Dom ? input.get() : input;
2362 // Otherwise its an element, that we need to see if one of the elements
2363 // in the result set is a parent of the given target
2364 let hasParent = false;
2365 this.each(el => {
2366 if (new Dom(selector).closest(el).count()) {
2367 hasParent = true;
2368 }
2369 });
2370 return new Dom(hasParent ? selector : []);
2371 }
2372 /**
2373 * Get the last element in the result set
2374 *
2375 * @returns New instance with just the selected item
2376 */
2377 first() {
2378 return new Dom(this.length ? this[0] : null);
2379 }
2380 height(include) {
2381 if (!this.count()) {
2382 return 0;
2383 }
2384 if (include === undefined ||
2385 include === 'withPadding' ||
2386 include === 'withBorder' ||
2387 include === 'withMargin' ||
2388 include === 'inner' ||
2389 include === 'outer') {
2390 let el = this[0];
2391 let computed = window.getComputedStyle(this[0]);
2392 let rectHeight = el.getBoundingClientRect().height;
2393 if (!include || include === 'content') {
2394 // Content. Minus scrollbar if there is one. This is basically
2395 // clientHeight minus padding, but that isn't fractional, so use
2396 // the bounding rect.
2397 let barHeight = el.offsetHeight -
2398 parseFloat(computed.borderTop) -
2399 parseFloat(computed.borderBottom) -
2400 el.clientHeight;
2401 return (rectHeight -
2402 parseFloat(computed.paddingTop) -
2403 parseFloat(computed.paddingBottom) -
2404 parseFloat(computed.borderTop) -
2405 parseFloat(computed.borderBottom) -
2406 barHeight);
2407 }
2408 else if (include === 'withPadding' || include === 'inner') {
2409 return (rectHeight -
2410 parseFloat(computed.borderTop) -
2411 parseFloat(computed.borderBottom));
2412 }
2413 else if (include === 'withBorder') {
2414 return rectHeight;
2415 }
2416 else {
2417 // withMargin
2418 return (rectHeight +
2419 parseFloat(computed.marginTop) +
2420 parseFloat(computed.marginBottom));
2421 }
2422 }
2423 else {
2424 // Setter
2425 return this.each(el => (el.style.height =
2426 typeof include === 'string' ? include : include + 'px'));
2427 }
2428 }
2429 /**
2430 * Hide an element by setting it to `display: none`
2431 *
2432 * @returns Self for chaining
2433 */
2434 hide() {
2435 return this.each(el => {
2436 el.style.display = 'none';
2437 });
2438 }
2439 html(data) {
2440 if (data !== undefined) {
2441 return this.each(el => {
2442 el.innerHTML = data;
2443 });
2444 }
2445 else {
2446 return this.count() ? this[0].innerHTML : null;
2447 }
2448 }
2449 /**
2450 * Boolean return check on if an item in the result set matches the selector
2451 * given. Only one need match.
2452 *
2453 * @param selector Selector to match against
2454 * @returns Boolean true if there is a match
2455 */
2456 is(selector) {
2457 return this.filter(selector).count() > 0;
2458 }
2459 /**
2460 * Determine if the first element in the result set is in the document or
2461 * not
2462 *
2463 * @returns true if is, false if detached
2464 */
2465 isAttached() {
2466 if (this.count() === 0) {
2467 return false;
2468 }
2469 return document.body.contains(this[0]);
2470 }
2471 /**
2472 * Determine if the first element in the result set is visible or not.
2473 *
2474 * @returns Visibility flag
2475 */
2476 isVisible() {
2477 if (this.count() === 0) {
2478 return false;
2479 }
2480 let el = this[0];
2481 return !!(el.offsetWidth ||
2482 el.offsetHeight ||
2483 el.getClientRects().length);
2484 }
2485 /**
2486 * Get the index of an element from among its siblings
2487 *
2488 * @returns Element index
2489 */
2490 index() {
2491 if (this.count()) {
2492 let el = this[0];
2493 return Array.from(el.parentNode.children).indexOf(el);
2494 }
2495 return -1;
2496 }
2497 /**
2498 * Insert each element in the result set after a target node
2499 *
2500 * @param target Element after which the insert should happen
2501 * @returns Self for chaining
2502 */
2503 insertAfter(target) {
2504 let nodes = elementArray(target);
2505 return this.eachReverse(el => {
2506 nodes.forEach(n => { var _a; return (_a = n === null || n === void 0 ? void 0 : n.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(el, n.nextSibling); });
2507 });
2508 }
2509 /**
2510 * Insert each element in the result set before a target node
2511 *
2512 * @param target Element before which the insert should happen
2513 * @returns Self for chaining
2514 */
2515 insertBefore(target) {
2516 let nodes = elementArray(target);
2517 return this.each(el => {
2518 nodes.forEach(n => { var _a; return (_a = n === null || n === void 0 ? void 0 : n.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(el, n); });
2519 });
2520 }
2521 /**
2522 * Get the last element in the result set
2523 *
2524 * @returns New instance with just the selected item
2525 */
2526 last() {
2527 let s = this;
2528 return new Dom(s.length ? s[s.length - 1] : null);
2529 }
2530 /**
2531 * Create a new Dom instance based on the results from a callback function
2532 * which is executed per element in the result set.
2533 *
2534 * @param fn Function to get the elements to add to the new instance
2535 * @returns New Dom instance with the results from the callback
2536 */
2537 map(fn) {
2538 let next = new Dom();
2539 this.each(el => {
2540 // Don't reorder the items
2541 next.add(fn(el), false);
2542 });
2543 return next;
2544 }
2545 /**
2546 * Create an array of any data type based on a function returning a value
2547 * from each element in the result set.
2548 *
2549 * @param fn Mapping function
2550 * @returns Array of returned objects.
2551 */
2552 mapTo(fn) {
2553 let result = [];
2554 this.each((el, idx) => result.push(fn(el, idx)));
2555 return result;
2556 }
2557 off(arg1, arg2, arg3) {
2558 let { handler, names, selector } = normaliseEventParams(arg1, arg2, arg3);
2559 return this.each(el => {
2560 names.forEach(name => {
2561 remove(el, name, handler, selector);
2562 });
2563 });
2564 }
2565 /**
2566 * Get the offset of the first element in the result set. The offset is the
2567 * coordinates of the element relative to the document.
2568 *
2569 * @returns Object with top and left offset
2570 */
2571 offset() {
2572 if (!this.count()) {
2573 return {
2574 top: 0,
2575 left: 0
2576 };
2577 }
2578 let box = this[0].getBoundingClientRect();
2579 let docElem = document.documentElement;
2580 return {
2581 top: box.top + window.pageYOffset - docElem.clientTop,
2582 left: box.left + window.pageXOffset - docElem.clientLeft
2583 };
2584 }
2585 /**
2586 * Get the offset parents of the elements in the result set.
2587 *
2588 * Departure from jQuery - it won't go up to `html`
2589 *
2590 * @returns Instance with the result set as the offset parents
2591 */
2592 offsetParent() {
2593 return this.map(el => el.offsetParent || document.body);
2594 }
2595 on(arg1, arg2, arg3) {
2596 let { handler, names, selector } = normaliseEventParams(arg1, arg2, arg3);
2597 return this.each(el => {
2598 names
2599 .filter(n => n !== null)
2600 .forEach(name => {
2601 add(el, name, handler, selector, false);
2602 });
2603 });
2604 }
2605 one(arg1, arg2, arg3) {
2606 let { handler, names, selector } = normaliseEventParams(arg1, arg2, arg3);
2607 return this.each(el => {
2608 names
2609 .filter(n => n !== null)
2610 .forEach(name => {
2611 add(el, name, handler, selector, true);
2612 });
2613 });
2614 }
2615 /**
2616 * Get the parent element for each element in the result set
2617 *
2618 * @param filter Optional selector that the parent element would need to
2619 * match to be selected.
2620 * @returns New Dom instance containing the parent elements
2621 */
2622 parent(filter) {
2623 return this.map(el => {
2624 let parent = el.parentElement;
2625 if (filter) {
2626 return (parent === null || parent === void 0 ? void 0 : parent.matches(filter)) ? parent : null;
2627 }
2628 return parent;
2629 });
2630 }
2631 /**
2632 * Get the position of the first element in the result set. The position is
2633 * the coordinates relative to the offset parent.
2634 *
2635 * @returns Object with top and left position coordinates
2636 */
2637 position() {
2638 if (!this.count()) {
2639 return {
2640 top: 0,
2641 left: 0
2642 };
2643 }
2644 let el = this[0];
2645 let { marginTop, marginLeft } = getComputedStyle(el);
2646 return {
2647 top: el.offsetTop - parseInt(marginTop),
2648 left: el.offsetLeft - parseInt(marginLeft)
2649 };
2650 }
2651 /**
2652 * Prepend the given content to each item in the result set.
2653 *
2654 * You should limit your result set to a single item!
2655 *
2656 * @param content Item(s) to prepend
2657 * @returns Self for chaining
2658 */
2659 prepend(content) {
2660 return this.each(el => {
2661 if (content instanceof Dom) {
2662 // Reverse the array, so if there are multiple elements, they
2663 // end up being added sequentially, just like jQuery
2664 let itemsReversed = Array.from(content).reverse();
2665 itemsReversed.forEach(item => el.prepend(item));
2666 }
2667 else if (typeof content === 'string') {
2668 el.insertAdjacentHTML('afterbegin', content);
2669 }
2670 else {
2671 el.prepend(content);
2672 }
2673 });
2674 }
2675 /**
2676 * Append the current data set items to the element from the selector
2677 *
2678 * @param selector Select item to insert result sets into
2679 * @returns Self for chaining
2680 */
2681 prependTo(selector) {
2682 if (selector instanceof Dom) {
2683 selector.prepend(this);
2684 }
2685 else {
2686 new Dom(selector).prepend(this);
2687 }
2688 return this;
2689 }
2690 prop(name, value) {
2691 if (typeof name === 'string' && value === undefined) {
2692 return this.count() ? this[0][name] : null;
2693 }
2694 return this.each(el => {
2695 el[name] = value;
2696 });
2697 }
2698 /**
2699 * Remove a property from all elements in the result set
2700 *
2701 * @param name Property name to remove
2702 * @returns Self for chaining
2703 */
2704 propRemove(name) {
2705 return this.each(el => {
2706 delete el[name];
2707 });
2708 }
2709 /**
2710 * Removed all nodes in the result set from the document
2711 *
2712 * @returns Self for chaining
2713 */
2714 remove() {
2715 // TODO this should remove event listeners
2716 return this.each(el => el.remove());
2717 }
2718 /**
2719 * Replace the elements in the result set with those given.
2720 *
2721 * @param replacer Element(s) to insert in place of the originals
2722 * @returns Self
2723 */
2724 replaceWith(replacer) {
2725 return this.each(el => {
2726 if (replacer instanceof Dom) {
2727 el.replaceWith(...replacer.get());
2728 }
2729 else {
2730 el.replaceWith(replacer);
2731 }
2732 });
2733 }
2734 scrollLeft(val) {
2735 if (val === undefined) {
2736 return this.count() ? this[0].scrollLeft : 0;
2737 }
2738 return this.each(el => (el.scrollLeft = val));
2739 }
2740 scrollTop(val) {
2741 if (val === undefined) {
2742 return this.count() ? this[0].scrollTop : 0;
2743 }
2744 return this.each(el => (el.scrollTop = val));
2745 }
2746 /**
2747 * Get the siblings of all elements in the result set
2748 *
2749 * @returns New Dom instance containing the sibling elements
2750 */
2751 siblings() {
2752 return this.map(el => {
2753 return el.parentElement
2754 ? Array.from(el.parentElement.children).filter(child => child !== el)
2755 : [];
2756 });
2757 }
2758 /**
2759 * Set the elements in the result set to display as blocks
2760 *
2761 * @returns Self for chaining
2762 * @todo Could be smarter with hide, since some elements might have been a
2763 * grid or flex before being hidden.
2764 */
2765 show() {
2766 return this.each(el => {
2767 el.style.display = 'block';
2768 });
2769 }
2770 /**
2771 * Sort the DOM elements into document order.
2772 *
2773 * This is normally not needed as elements selected with a DOM selector are
2774 * automatically sorted in document order. However, in the case of elements
2775 * being added as an array, their order will be retained. In such as case
2776 * you might wish to sort them in document order.
2777 */
2778 sort() {
2779 Array.prototype.sort.call(this, documentOrder);
2780 return this;
2781 }
2782 text(txt) {
2783 if (txt === undefined) {
2784 return this.count() ? this[0].textContent : null;
2785 }
2786 return this.each(el => {
2787 el.textContent = txt;
2788 });
2789 }
2790 /**
2791 * Perform a CSS transition - i.e. an animation. Note this isn't nearly as
2792 * comprehensive as an animation library, nor is it meant to be. It is for
2793 * simple transitions such as fading in only.
2794 *
2795 * To set up something like a fade in, do `dom.css({opacity:
2796 * 0}).transition({opacity: 1})`.
2797 *
2798 * @param css CSS properties to transition
2799 * @param duration Transition duration
2800 * @param ease CSS easing function name
2801 * @param cb Callback function
2802 * @returns Self for chaining
2803 */
2804 transition(css, duration, ease, cb) {
2805 if (!this.count()) {
2806 return this;
2807 }
2808 if (!duration && duration !== 0) {
2809 duration = 400;
2810 }
2811 if (!ease) {
2812 ease = '';
2813 }
2814 if (!cb) {
2815 cb = () => { };
2816 }
2817 if (Dom.transitions && duration !== 0) {
2818 let first = this[0];
2819 // If there was an existing transition, cancel its callback
2820 if (first._dom_tra) {
2821 clearTimeout(first._dom_tra);
2822 delete first._dom_tra;
2823 }
2824 setTimeout(() => {
2825 this.css('transition', 'all ' + duration + 'ms ' + ease);
2826 this.css(css);
2827 }, 0);
2828 first._dom_tra = setTimeout(() => {
2829 delete first._dom_tra;
2830 this.css('transition', '');
2831 cb.call(this);
2832 }, duration);
2833 }
2834 else {
2835 this.css(css);
2836 cb.call(this);
2837 }
2838 return this;
2839 }
2840 trigger(name, bubbles = true, args = null, props = null, returnEvent = false) {
2841 let { names } = normaliseEventParams(name);
2842 let ret = [];
2843 this.each(el => {
2844 names
2845 .filter(n => n !== null)
2846 .forEach(name => {
2847 ret.push(trigger(el, name, bubbles, args, props, returnEvent));
2848 });
2849 });
2850 return ret;
2851 }
2852 val(value) {
2853 if (value === undefined) {
2854 // Getter
2855 if (!this.count()) {
2856 return null;
2857 }
2858 let el = this[0];
2859 if (el.options && el.multiple) {
2860 return Array.from(el.options)
2861 .filter(opt => opt.selected)
2862 .map(opt => opt.value);
2863 }
2864 return el.value;
2865 }
2866 // Setter
2867 return this.each((el) => {
2868 if (el.options && el.multiple) {
2869 let valArr = Array.isArray(value) ? value : [value];
2870 Array.from(el.options).forEach(opt => (opt.selected = valArr.includes(opt.value)));
2871 }
2872 else {
2873 // This works for select elements as well in modern browsers
2874 el.value = value;
2875 }
2876 });
2877 }
2878 width(include) {
2879 if (!this.count()) {
2880 return 0;
2881 }
2882 if (include === undefined ||
2883 include === 'withPadding' ||
2884 include === 'withBorder' ||
2885 include === 'withMargin' ||
2886 include === 'inner' ||
2887 include === 'outer') {
2888 let el = this[0];
2889 let computed = window.getComputedStyle(el);
2890 let rectWidth = el.getBoundingClientRect().width;
2891 if (!include || include === 'content') {
2892 // Content. Minus scrollbar if there is one. This is basically
2893 // clientWidth minus padding, but that isn't fractional, so use
2894 // the bounding rect.
2895 let barWidth = el.offsetWidth -
2896 parseFloat(computed.borderLeft) -
2897 parseFloat(computed.borderRight) -
2898 el.clientWidth;
2899 return (rectWidth -
2900 parseFloat(computed.paddingLeft) -
2901 parseFloat(computed.paddingRight) -
2902 parseFloat(computed.borderLeft) -
2903 parseFloat(computed.borderRight) -
2904 barWidth);
2905 }
2906 else if (include === 'withPadding' || include === 'inner') {
2907 return (rectWidth -
2908 parseFloat(computed.borderLeft) -
2909 parseFloat(computed.borderRight));
2910 }
2911 else if (include === 'withBorder') {
2912 return rectWidth;
2913 }
2914 else {
2915 // withMargin
2916 return (rectWidth +
2917 parseFloat(computed.marginLeft) +
2918 parseFloat(computed.marginRight));
2919 }
2920 }
2921 else {
2922 // Setter
2923 return this.each(el => (el.style.width =
2924 typeof include === 'string' ? include : include + 'px'));
2925 }
2926 }
2927 }
2928 /**
2929 * Create a new element and wrap in a `Dom` instance (alias of `create`)
2930 *
2931 * @param name Element name to create
2932 * @returns Dom instance for manipulating the new element
2933 */
2934 Dom.c = create$3;
2935 /**
2936 * Create a new element and wrap in a `Dom` instance (alias of `c`)
2937 *
2938 * @param name Element name to create
2939 * @returns Dom instance for manipulating the new element
2940 */
2941 Dom.create = create$3;
2942 /**
2943 * Select items from the document and wrap in a `Dom` instance (alias of
2944 * `select`)
2945 *
2946 * @param selector Items to select
2947 * @returns Dom instance for manipulating the selected items
2948 */
2949 Dom.s = select;
2950 /**
2951 * Select items from the document and wrap in a `Dom` instance (alias of
2952 * `s`)
2953 *
2954 * @param selector Items to select
2955 * @returns Dom instance for manipulating the selected items
2956 */
2957 Dom.select = select;
2958 /**
2959 * Flag to indicate if transitions (animations) should be allowed. Set to
2960 * false to disable and have it jump to the end.
2961 */
2962 Dom.transitions = true;
2963 /**
2964 * Window object methods
2965 */
2966 Dom.w = win;
2967 // Aliases for jQuery-likeness. Not exposed via Typescript, but that might
2968 // change.
2969 Dom.prototype.addClass = Dom.prototype.classAdd;
2970 Dom.prototype.hasClass = Dom.prototype.classHas;
2971 Dom.prototype.removeClass = Dom.prototype.classRemove;
2972 /**
2973 * Convert a data value into a typed value
2974 *
2975 * @param val Data to convert
2976 * @returns Converted value
2977 */
2978 function dataConvert(val) {
2979 if (val === undefined) {
2980 return null;
2981 }
2982 try {
2983 return JSON.parse(val);
2984 }
2985 catch (e) {
2986 return val;
2987 }
2988 }
2989 function normaliseEventParams(name, arg2, arg3) {
2990 let selector;
2991 let handler;
2992 let names = name ? name.split(' ').map(str => str.trim()) : [null];
2993 if (typeof arg2 === 'string') {
2994 selector = arg2;
2995 handler = arg3;
2996 }
2997 else {
2998 selector = null;
2999 handler = arg2;
3000 }
3001 return {
3002 handler,
3003 names,
3004 selector
3005 };
3006 }
3007 function documentOrder(a, b) {
3008 if (a === b) {
3009 return 0;
3010 }
3011 let position = a.compareDocumentPosition(b);
3012 if (position & Node.DOCUMENT_POSITION_DISCONNECTED) {
3013 // One is disconnected - find which
3014 if (document.body.contains(a)) {
3015 return -1;
3016 }
3017 else if (document.body.contains(b)) {
3018 return 1;
3019 }
3020 return 0;
3021 }
3022 else if (position & Node.DOCUMENT_POSITION_FOLLOWING ||
3023 position & Node.DOCUMENT_POSITION_CONTAINED_BY) {
3024 return -1;
3025 }
3026 else if (position & Node.DOCUMENT_POSITION_PRECEDING ||
3027 position & Node.DOCUMENT_POSITION_CONTAINS) {
3028 return 1;
3029 }
3030 else {
3031 return 0;
3032 }
3033 }
3034 function elementArray(target) {
3035 return dom(target)
3036 ? target.get()
3037 : Array.isArray(target)
3038 ? target
3039 : [target];
3040 }
3041 function addArray(store, el) {
3042 if (util.is.arrayLike(el)) {
3043 for (var i = 0; i < el.length; i++) {
3044 let e = el[i];
3045 if (e !== null && e !== undefined) {
3046 store[store.length] = e;
3047 store.length++;
3048 }
3049 }
3050 }
3051 else if (el !== null && el !== undefined) {
3052 store[store.length] = el;
3053 store.length++;
3054 }
3055 }
3056 function stringArrays(name) {
3057 let names = [];
3058 let add = function (n) {
3059 names.push.apply(names, n.split(' '));
3060 };
3061 if (Array.isArray(name)) {
3062 name.forEach(n => add(n));
3063 }
3064 else {
3065 add(name);
3066 }
3067 return names;
3068 }
3069
3070 const features = {};
3071 const legacy = [];
3072 /**
3073 * Create a new feature that can be used for layout
3074 *
3075 * @param name The name of the new feature.
3076 * @param cb A function that will create the elements and event listeners for
3077 * the feature being added.
3078 * @param legacyChar
3079 */
3080 function register$2(name, cb, legacyChar = '') {
3081 features[name] = cb;
3082 if (legacyChar) {
3083 legacy.push({
3084 cFeature: legacyChar,
3085 fnInit: cb
3086 });
3087 }
3088 }
3089
3090 var classes$1 = {
3091 container: 'dt-container',
3092 empty: {
3093 row: 'dt-empty'
3094 },
3095 info: {
3096 container: 'dt-info'
3097 },
3098 layout: {
3099 row: 'dt-layout-row',
3100 cell: 'dt-layout-cell',
3101 tableRow: 'dt-layout-table',
3102 tableCell: '',
3103 start: 'dt-layout-start',
3104 end: 'dt-layout-end',
3105 full: 'dt-layout-full'
3106 },
3107 length: {
3108 container: 'dt-length',
3109 select: 'dt-input'
3110 },
3111 order: {
3112 canAsc: 'dt-orderable-asc',
3113 canDesc: 'dt-orderable-desc',
3114 isAsc: 'dt-ordering-asc',
3115 isDesc: 'dt-ordering-desc',
3116 none: 'dt-orderable-none',
3117 position: 'sorting_'
3118 },
3119 processing: {
3120 container: 'dt-processing'
3121 },
3122 scrolling: {
3123 body: 'dt-scroll-body',
3124 container: 'dt-scroll',
3125 footer: {
3126 self: 'dt-scroll-foot',
3127 inner: 'dt-scroll-footInner'
3128 },
3129 header: {
3130 self: 'dt-scroll-head',
3131 inner: 'dt-scroll-headInner'
3132 }
3133 },
3134 search: {
3135 container: 'dt-search',
3136 input: 'dt-input'
3137 },
3138 table: 'dataTable',
3139 tbody: {
3140 cell: '',
3141 row: ''
3142 },
3143 thead: {
3144 cell: '',
3145 row: ''
3146 },
3147 tfoot: {
3148 cell: '',
3149 row: ''
3150 },
3151 paging: {
3152 active: 'current',
3153 button: 'dt-paging-button',
3154 container: 'dt-paging',
3155 disabled: 'disabled',
3156 nav: ''
3157 }
3158 };
3159
3160 /**
3161 * Compute what number buttons to show in the paging control
3162 *
3163 * @param page Current page
3164 * @param pages Total number of pages
3165 * @param buttons Target number of number buttons
3166 * @param addFirstLast Indicate if page 1 and end should be included
3167 * @returns Buttons to show
3168 */
3169 function pagingNumbers(page, pages, buttons, addFirstLast) {
3170 let numbers = [], half = Math.floor(buttons / 2), before = addFirstLast ? 2 : 1, after = addFirstLast ? 1 : 0;
3171 if (pages <= buttons) {
3172 numbers = range(0, pages);
3173 }
3174 else if (buttons === 1) {
3175 // Single button - current page only
3176 numbers = [page];
3177 }
3178 else if (buttons === 3) {
3179 // Special logic for just three buttons
3180 if (page <= 1) {
3181 numbers = [0, 1, 'ellipsis'];
3182 }
3183 else if (page >= pages - 2) {
3184 numbers = range(pages - 2, pages);
3185 numbers.unshift('ellipsis');
3186 }
3187 else {
3188 numbers = ['ellipsis', page, 'ellipsis'];
3189 }
3190 }
3191 else if (page <= half) {
3192 numbers = range(0, buttons - before);
3193 numbers.push('ellipsis');
3194 if (addFirstLast) {
3195 numbers.push(pages - 1);
3196 }
3197 }
3198 else if (page >= pages - 1 - half) {
3199 numbers = range(pages - (buttons - before), pages);
3200 numbers.unshift('ellipsis');
3201 if (addFirstLast) {
3202 numbers.unshift(0);
3203 }
3204 }
3205 else {
3206 numbers = range(page - half + before, page + half - after);
3207 numbers.push('ellipsis');
3208 numbers.unshift('ellipsis');
3209 if (addFirstLast) {
3210 numbers.push(pages - 1);
3211 numbers.unshift(0);
3212 }
3213 }
3214 return numbers;
3215 }
3216 var pager = {
3217 simple: function () {
3218 return ['previous', 'next'];
3219 },
3220 full: function () {
3221 return ['first', 'previous', 'next', 'last'];
3222 },
3223 numbers: function () {
3224 return ['numbers'];
3225 },
3226 simple_numbers: function () {
3227 return ['previous', 'numbers', 'next'];
3228 },
3229 full_numbers: function () {
3230 return ['first', 'previous', 'numbers', 'next', 'last'];
3231 },
3232 first_last: function () {
3233 return ['first', 'last'];
3234 },
3235 first_last_numbers: function () {
3236 return ['first', 'numbers', 'last'];
3237 },
3238 // For testing and plug-ins to use
3239 _numbers: pagingNumbers,
3240 // Number of number buttons - legacy, use `numbers` option for paging feature
3241 numbers_length: 7
3242 };
3243
3244 const footer = (settings, cell, classes) => {
3245 cell.classAdd(classes.tfoot.cell);
3246 };
3247 const header = (settings, cell, classes) => {
3248 cell.classAdd(classes.thead.cell);
3249 if (!settings.features.ordering) {
3250 cell.classAdd(classes.order.none);
3251 }
3252 var titleRow = settings.titleRow;
3253 var headerRows = cell.closest('thead').find('tr');
3254 var rowIdx = cell.parent().index();
3255 // Conditions to not apply the ordering icons
3256 if (
3257 // Cells and rows which have the attribute to disable the icons
3258 cell.attr('data-dt-order') === 'disable' ||
3259 cell.parent().attr('data-dt-order') === 'disable' ||
3260 // titleRow support, for defining a specific row in the header
3261 (titleRow === true && rowIdx !== 0) ||
3262 (titleRow === false && rowIdx !== headerRows.count() - 1) ||
3263 (typeof titleRow === 'number' && rowIdx !== titleRow)) {
3264 return;
3265 }
3266 // No additional mark-up required. Attach a sort listener to update on sort
3267 // - note that using the `DT` namespace will allow the event to be removed
3268 // automatically on destroy, while the `dt` namespaced event is the one we
3269 // are listening for
3270 Dom.s(settings.table).on('order.dt.DT column-visibility.dt.DT', function (e, ctx, column) {
3271 if (settings !== ctx) {
3272 // need to check if this is the host
3273 return; // table, not a nested one
3274 }
3275 var sorting = ctx.sortDetails;
3276 if (!sorting) {
3277 return;
3278 }
3279 var orderedColumns = pluck(sorting, 'col');
3280 // This handler is only needed on column visibility if the column is
3281 // part of the ordering. If it isn't, then we can bail out to save
3282 // performance. It could be a separate event handler, but this is a
3283 // balance between code reuse / size and performance console.log(e,
3284 // e.name, column, orderedColumns, orderedColumns.includes(column))
3285 if (e.type === 'column-visibility' &&
3286 !orderedColumns.includes(column)) {
3287 return;
3288 }
3289 var i;
3290 var orderClasses = classes.order;
3291 var columns = ctx.api.columns(cell);
3292 var col = settings.columns[columns.flatten()[0]];
3293 var orderable = columns.orderable().includes(true);
3294 var ariaType = '';
3295 var indexes = columns.indexes();
3296 var sortDirs = columns.orderable(true).flatten();
3297 var tabIndex = settings.tabIndex;
3298 var canOrder = ctx.orderHandler && orderable;
3299 cell.classRemove(orderClasses.isAsc + ' ' + orderClasses.isDesc)
3300 .classToggle(orderClasses.none, !orderable)
3301 .classToggle(orderClasses.canAsc, canOrder && sortDirs.includes('asc'))
3302 .classToggle(orderClasses.canDesc, canOrder && sortDirs.includes('desc'));
3303 // Determine if all of the columns that this cell covers are
3304 // included in the current ordering
3305 var isOrdering = true;
3306 for (i = 0; i < indexes.length; i++) {
3307 if (!orderedColumns.includes(indexes[i])) {
3308 isOrdering = false;
3309 }
3310 }
3311 if (isOrdering) {
3312 // Get the ordering direction for the columns under this cell
3313 // Note that it is possible for a cell to be asc and desc
3314 // sorting (column spanning cells)
3315 var orderDirs = columns.order();
3316 cell.classAdd((orderDirs.includes('asc') ? orderClasses.isAsc : '') +
3317 (orderDirs.includes('desc') ? orderClasses.isDesc : ''));
3318 }
3319 // Find the first visible column that has ordering applied to it -
3320 // it get's the aria information, as the ARIA spec says that only
3321 // one column should be marked with aria-sort
3322 var firstVis = -1; // column index
3323 for (i = 0; i < orderedColumns.length; i++) {
3324 if (settings.columns[orderedColumns[i]].visible) {
3325 firstVis = orderedColumns[i];
3326 break;
3327 }
3328 }
3329 if (indexes[0] == firstVis) {
3330 var firstSort = sorting[0];
3331 var sortOrder = col.orderSequence;
3332 cell.attr('aria-sort', firstSort.dir === 'asc' ? 'ascending' : 'descending');
3333 // Determine if the next click will remove sorting or change the
3334 // sort
3335 ariaType =
3336 sortOrder && !sortOrder[firstSort.index + 1]
3337 ? 'Remove'
3338 : 'Reverse';
3339 }
3340 else {
3341 cell.attrRemove('aria-sort');
3342 }
3343 // Make the headers tab-able for keyboard navigation
3344 if (orderable) {
3345 var orderSpan = cell.find('.dt-column-order');
3346 orderSpan
3347 .attr('role', 'button')
3348 .attr('aria-label', orderable
3349 ? col.ariaTitle +
3350 ctx.api.i18n('aria.orderable' + ariaType)
3351 : col.ariaTitle);
3352 if (tabIndex !== -1) {
3353 orderSpan.attr('tabindex', tabIndex);
3354 }
3355 }
3356 });
3357 };
3358 const layout = (settings, container, items) => {
3359 let classes = settings.classes.layout;
3360 let row = Dom
3361 .c('div')
3362 .attr('id', items.id || null)
3363 .classAdd(items.className || classes.row)
3364 .appendTo(container);
3365 displayRowCells(items, function (key, val) {
3366 var klass = '';
3367 if (val.table) {
3368 row.classAdd(classes.tableRow);
3369 klass += classes.tableCell + ' ';
3370 }
3371 if (key === 'start') {
3372 klass += classes.start;
3373 }
3374 else if (key === 'end') {
3375 klass += classes.end;
3376 }
3377 else {
3378 klass += classes.full;
3379 }
3380 Dom.c('div')
3381 .attr({
3382 id: val.id || null,
3383 class: val.className
3384 ? val.className
3385 : classes.cell + ' ' + klass
3386 })
3387 .append(val.contents)
3388 .appendTo(row);
3389 });
3390 };
3391 const pagingButton = (settings, buttonType, content, active, disabled) => {
3392 var classes = settings.classes.paging;
3393 var btnClasses = [classes.button];
3394 var btn;
3395 if (active) {
3396 btnClasses.push(classes.active);
3397 }
3398 if (disabled) {
3399 btnClasses.push(classes.disabled);
3400 }
3401 if (buttonType === 'ellipsis') {
3402 btn = Dom.c('span').classAdd('ellipsis').html(content).get(0);
3403 }
3404 else {
3405 btn = Dom
3406 .c('button')
3407 .classAdd(btnClasses.join(' '))
3408 .attr('role', 'link')
3409 .attr('type', 'button')
3410 .html(content)
3411 .get(0);
3412 }
3413 return {
3414 display: btn,
3415 clicker: btn
3416 };
3417 };
3418 const pagingContainer = (settings, buttons) => {
3419 // No wrapping element - just append directly to the host
3420 return buttons;
3421 };
3422 function displayRowCells(items, fn) {
3423 if (items.start) {
3424 fn('start', items.start);
3425 }
3426 if (items.end) {
3427 fn('end', items.end);
3428 }
3429 if (items.full) {
3430 fn('full', items.full);
3431 }
3432 }
3433
3434 const store = {
3435 className: {},
3436 detect: [],
3437 render: {},
3438 search: {},
3439 order: {}
3440 };
3441 // Common function to remove new lines, strip HTML and diacritic control
3442 function _filterString(stripHtml, normalize) {
3443 return function (str) {
3444 if (util.is.empty(str) || typeof str !== 'string') {
3445 return str;
3446 }
3447 str = str.replace(util.regex.reNewLines, ' ');
3448 if (stripHtml) {
3449 str = util.stripHtml(str);
3450 }
3451 {
3452 str = util.diacritics(str, false);
3453 }
3454 return str;
3455 };
3456 }
3457 function __numericReplace(d, decimalPlace, re1, re2) {
3458 if (d !== 0 && (!d || d === '-')) {
3459 return -Infinity;
3460 }
3461 if (typeof d === 'number' || typeof d === 'bigint') {
3462 return d;
3463 }
3464 // If a decimal place other than `.` is used, it needs to be given to the
3465 // function so we can detect it and replace with a `.` which is the only
3466 // decimal place JavaScript recognises - it is not locale aware.
3467 if (decimalPlace) {
3468 d = util.conv.numToDecimal(d, decimalPlace);
3469 }
3470 if (typeof d === 'string') {
3471 if (re1) {
3472 d = d.replace(re1, '');
3473 }
3474 if (re2) {
3475 d = d.replace(re2, '');
3476 }
3477 }
3478 return d * 1;
3479 }
3480 function register$1(name, prop, val) {
3481 if (!prop) {
3482 return {
3483 className: store.className[name],
3484 detect: store.detect.find(function (fn) {
3485 return fn._name === name;
3486 }),
3487 order: {
3488 pre: store.order[name + '-pre'],
3489 asc: store.order[name + '-asc'],
3490 desc: store.order[name + '-desc']
3491 },
3492 render: store.render[name],
3493 search: store.search[name]
3494 };
3495 }
3496 var setProp = function (prop2, propVal) {
3497 store[prop2][name] = propVal;
3498 };
3499 var setDetect = function (detect) {
3500 // `detect` can be a function or an object - we set a name
3501 // property for either - that is used for the detection
3502 Object.defineProperty(detect, '_name', { value: name });
3503 var idx = store.detect.findIndex(function (item) {
3504 return item._name === name;
3505 });
3506 if (idx === -1) {
3507 store.detect.unshift(detect);
3508 }
3509 else {
3510 store.detect.splice(idx, 1, detect);
3511 }
3512 };
3513 var setOrder = function (obj) {
3514 store.order[name + '-pre'] = obj.pre; // can be undefined
3515 store.order[name + '-asc'] = obj.asc; // can be undefined
3516 store.order[name + '-desc'] = obj.desc; // can be undefined
3517 };
3518 // prop is optional
3519 if (val === undefined) {
3520 val = prop;
3521 prop = undefined;
3522 }
3523 if (prop === 'className') {
3524 setProp('className', val);
3525 }
3526 else if (prop === 'detect') {
3527 setDetect(val);
3528 }
3529 else if (prop === 'order') {
3530 setOrder(val);
3531 }
3532 else if (prop === 'render') {
3533 setProp('render', val);
3534 }
3535 else if (prop === 'search') {
3536 setProp('search', val);
3537 }
3538 else if (!prop) {
3539 if (val.className) {
3540 setProp('className', val.className);
3541 }
3542 if (val.detect !== undefined) {
3543 setDetect(val.detect);
3544 }
3545 if (val.order) {
3546 setOrder(val.order);
3547 }
3548 if (val.render !== undefined) {
3549 setProp('render', val.render);
3550 }
3551 if (val.search !== undefined) {
3552 setProp('search', val.search);
3553 }
3554 }
3555 }
3556 // Get a list of types
3557 function types() {
3558 return store.detect.map(function (detect) {
3559 return detect._name;
3560 });
3561 }
3562 var __diacriticSort = function (a, b) {
3563 a = a !== null && a !== undefined ? a.toString().toLowerCase() : '';
3564 b = b !== null && b !== undefined ? b.toString().toLowerCase() : '';
3565 // Checked for `navigator.languages` support in `oneOf` so this code can't execute in old
3566 // Safari and thus can disable this check
3567 // eslint-disable-next-line compat/compat
3568 return a.localeCompare(b, navigator.languages[0] || navigator.language, {
3569 numeric: true,
3570 ignorePunctuation: true
3571 });
3572 };
3573 var __diacriticHtmlSort = function (a, b) {
3574 a = util.stripHtml(a);
3575 b = util.stripHtml(b);
3576 return __diacriticSort(a, b);
3577 };
3578 //
3579 // Built in data types
3580 //
3581 register$1('string', {
3582 detect: function () {
3583 return 'string';
3584 },
3585 order: {
3586 pre: function (a) {
3587 // This is a little complex, but faster than always calling toString,
3588 // http://jsperf.com/tostring-v-check
3589 return util.is.empty(a) && typeof a !== 'boolean'
3590 ? ''
3591 : typeof a === 'string'
3592 ? a.toLowerCase()
3593 : !a.toString
3594 ? ''
3595 : a.toString();
3596 }
3597 },
3598 search: _filterString(false)
3599 });
3600 register$1('string-utf8', {
3601 detect: {
3602 allOf: function () {
3603 return true;
3604 },
3605 oneOf: function (d) {
3606 // At least one data point must contain a non-ASCII character
3607 // This line will also check if navigator.languages is supported or not. If not (Safari 10.0-)
3608 // this data type won't be supported.
3609 // eslint-disable-next-line compat/compat
3610 return (!util.is.empty(d) &&
3611 navigator.languages &&
3612 typeof d === 'string' &&
3613 !!d.match(/[^\x00-\x7F]/));
3614 }
3615 },
3616 order: {
3617 asc: __diacriticSort,
3618 desc: function (a, b) {
3619 return __diacriticSort(a, b) * -1;
3620 }
3621 },
3622 search: _filterString(false)
3623 });
3624 register$1('html', {
3625 detect: {
3626 allOf: function (d) {
3627 return (util.is.empty(d) ||
3628 (typeof d === 'string' && d.indexOf('<') !== -1));
3629 },
3630 oneOf: function (d) {
3631 // At least one data point must contain a `<`
3632 return (!util.is.empty(d) &&
3633 typeof d === 'string' &&
3634 d.indexOf('<') !== -1);
3635 }
3636 },
3637 order: {
3638 pre: function (a) {
3639 return util.is.empty(a)
3640 ? ''
3641 : a.replace
3642 ? util.stripHtml(a).trim().toLowerCase()
3643 : a + '';
3644 }
3645 },
3646 search: _filterString(true)
3647 });
3648 register$1('html-utf8', {
3649 detect: {
3650 allOf: function (d) {
3651 return (util.is.empty(d) ||
3652 (typeof d === 'string' && d.indexOf('<') !== -1));
3653 },
3654 oneOf: function (d) {
3655 // At least one data point must contain a `<` and a non-ASCII character
3656 // eslint-disable-next-line compat/compat
3657 return (navigator.languages &&
3658 !util.is.empty(d) &&
3659 typeof d === 'string' &&
3660 d.indexOf('<') !== -1 &&
3661 typeof d === 'string' &&
3662 !!d.match(/[^\x00-\x7F]/));
3663 }
3664 },
3665 order: {
3666 asc: __diacriticHtmlSort,
3667 desc: function (a, b) {
3668 return __diacriticHtmlSort(a, b) * -1;
3669 }
3670 },
3671 search: _filterString(true)
3672 });
3673 register$1('date', {
3674 className: 'dt-type-date',
3675 detect: {
3676 allOf: function (d) {
3677 // V8 tries _very_ hard to make a string passed into `Date.parse()`
3678 // valid, so we need to use a regex to restrict date formats. Use a
3679 // plug-in for anything other than ISO8601 style strings
3680 if (d && !(d instanceof Date) && !util.regex.reDate.test(d)) {
3681 return null;
3682 }
3683 var parsed = Date.parse(d);
3684 return (parsed !== null && !isNaN(parsed)) || util.is.empty(d);
3685 },
3686 oneOf: function (d) {
3687 // At least one entry must be a date or a string with a date
3688 return (d instanceof Date ||
3689 (typeof d === 'string' && util.regex.reDate.test(d)));
3690 }
3691 },
3692 order: {
3693 pre: function (d) {
3694 var ts = Date.parse(d);
3695 return isNaN(ts) ? -Infinity : ts;
3696 }
3697 }
3698 });
3699 register$1('html-num-fmt', {
3700 className: 'dt-type-numeric',
3701 detect: {
3702 allOf: function (d, settings) {
3703 var decimal = settings.language.decimal;
3704 return util.is.htmlNum(d, decimal, true, false);
3705 },
3706 oneOf: function (d, settings) {
3707 // At least one data point must contain a numeric value
3708 var decimal = settings.language.decimal;
3709 return util.is.htmlNum(d, decimal, true, false);
3710 }
3711 },
3712 order: {
3713 pre: function (d, s) {
3714 var dp = s.language.decimal;
3715 return __numericReplace(d, dp, util.regex.reHtml, util.regex.reFormattedNumeric);
3716 }
3717 },
3718 search: _filterString(true)
3719 });
3720 register$1('html-num', {
3721 className: 'dt-type-numeric',
3722 detect: {
3723 allOf: function (d, settings) {
3724 var decimal = settings.language.decimal;
3725 return util.is.htmlNum(d, decimal, false, true);
3726 },
3727 oneOf: function (d, settings) {
3728 // At least one data point must contain a numeric value
3729 var decimal = settings.language.decimal;
3730 return util.is.htmlNum(d, decimal, false, false);
3731 }
3732 },
3733 order: {
3734 pre: function (d, s) {
3735 var dp = s.language.decimal;
3736 return __numericReplace(d, dp, util.regex.reHtml);
3737 }
3738 },
3739 search: _filterString(true)
3740 });
3741 register$1('num-fmt', {
3742 className: 'dt-type-numeric',
3743 detect: {
3744 allOf: function (d, settings) {
3745 var decimal = settings.language.decimal;
3746 return util.is.num(d, decimal, true, true);
3747 },
3748 oneOf: function (d, settings) {
3749 // At least one data point must contain a numeric value
3750 var decimal = settings.language.decimal;
3751 return util.is.num(d, decimal, true, false);
3752 }
3753 },
3754 order: {
3755 pre: function (d, s) {
3756 var dp = s.language.decimal;
3757 return __numericReplace(d, dp, util.regex.reFormattedNumeric);
3758 }
3759 }
3760 });
3761 register$1('num', {
3762 className: 'dt-type-numeric',
3763 detect: {
3764 allOf: function (d, settings) {
3765 var decimal = settings.language.decimal;
3766 return util.is.num(d, decimal, false, true);
3767 },
3768 oneOf: function (d, settings) {
3769 // At least one data point must contain a numeric value
3770 var decimal = settings.language.decimal;
3771 return util.is.num(d, decimal, false, false);
3772 }
3773 },
3774 order: {
3775 pre: function (d, s) {
3776 var dp = s.language.decimal;
3777 return __numericReplace(d, dp);
3778 }
3779 }
3780 });
3781
3782 /**
3783 * DataTables extensions
3784 *
3785 * This namespace acts as a collection area for plug-ins that can be used to
3786 * extend DataTables capabilities. Indeed many of the build in methods
3787 * use this method to provide their own capabilities (sorting methods for
3788 * example).
3789 *
3790 * Note that this namespace is aliased to `jQuery.fn.dataTableExt` for legacy
3791 * reasons
3792 */
3793 const ext = {
3794 /**
3795 * DataTables build type (expanded by the download builder)
3796 */
3797 builder: 'dt/dt-3.0.3',
3798 /**
3799 * Buttons. For use with the Buttons extension for DataTables. This is
3800 * defined here so other extensions can define buttons regardless of load
3801 * order. It is _not_ used by DataTables core.
3802 */
3803 buttons: {},
3804 /**
3805 * ColumnControl buttons and content
3806 */
3807 ccContent: {},
3808 /**
3809 * Element class names
3810 */
3811 classes: classes$1,
3812 /**
3813 * Error reporting.
3814 *
3815 * How should DataTables report an error. Can take the value 'alert',
3816 * 'throw', 'none' or a function.
3817 */
3818 errMode: 'alert',
3819 /** HTML entity escaping */
3820 escape: {
3821 /** When reading data-* attributes for initialisation options */
3822 attributes: false
3823 },
3824 /**
3825 * Legacy so v1 plug-ins don't throw js errors on load
3826 */
3827 feature: legacy,
3828 /**
3829 * Feature plug-ins.
3830 *
3831 * This is an object of callbacks which provide the features for DataTables
3832 * to be initialised via the `layout` option.
3833 */
3834 features: features,
3835 /**
3836 * Row searching.
3837 *
3838 * This method of searching is complimentary to the default type based
3839 * searching, and a lot more comprehensive as it allows you complete control
3840 * over the searching logic. Each element in this array is a function
3841 * (parameters described below) that is called for every row in the table,
3842 * and your logic decides if it should be included in the searching data set
3843 * or not.
3844 */
3845 search: [],
3846 /**
3847 * Selector extensions
3848 *
3849 * The `selector` option can be used to extend the options available for the
3850 * selector modifier options (`selector-modifier` object data type) that
3851 * each of the three built in selector types offer (row, column and cell +
3852 * their plural counterparts). For example the Select extension uses this
3853 * mechanism to provide an option to select only rows, columns and cells
3854 * that have been marked as selected by the end user (`{selected: true}`),
3855 * which can be used in conjunction with the existing built in selector
3856 * options.
3857 */
3858 selector: {
3859 cell: [],
3860 column: [],
3861 row: []
3862 },
3863 settings: [],
3864 /**
3865 * Legacy configuration options. Enable and disable legacy options that
3866 * are available in DataTables.
3867 *
3868 * @type object
3869 */
3870 legacy: {
3871 /**
3872 * Enable / disable DataTables 1.9 compatible server-side processing
3873 * requests
3874 */
3875 ajax: null
3876 },
3877 /**
3878 * Pagination plug-in methods.
3879 *
3880 * Each entry in this object is a function and defines which buttons should
3881 * be shown by the pagination rendering method that is used for the table.
3882 * The renderer addresses how the buttons are displayed in the document,
3883 * while the functions here tell it what buttons to display. This is done by
3884 * returning an array of button descriptions (what each button will do).
3885 */
3886 pager: pager,
3887 renderer: {
3888 footer: {
3889 _: footer
3890 },
3891 header: {
3892 _: header
3893 },
3894 layout: {
3895 _: layout
3896 },
3897 pagingButton: {
3898 _: pagingButton
3899 },
3900 pagingContainer: {
3901 _: pagingContainer
3902 }
3903 },
3904 /**
3905 * Rendering helper function exposed for use by the styling integrations.
3906 */
3907 rendererDisplayRowCells: displayRowCells,
3908 /**
3909 * Ordering plug-ins - custom data source
3910 *
3911 * The extension options for ordering of data available here is
3912 * complimentary to the default type based ordering that DataTables
3913 * typically uses. It allows much greater control over the data that is
3914 * being used to order a column, but is necessarily therefore more complex.
3915 */
3916 order: {},
3917 /**
3918 * Type based plug-ins.
3919 *
3920 * Each column in DataTables has a type assigned to it, either by automatic
3921 * detection or by direct assignment using the `type` option for the column.
3922 * The type of a column will effect how it is ordering and search (plug-ins
3923 * can also make use of the column type if required).
3924 */
3925 type: store,
3926 /**
3927 * Unique DataTables instance counter
3928 *
3929 * @type int
3930 * @private
3931 */
3932 _unique: 0,
3933 //
3934 // Depreciated
3935 // The following properties are retained for backwards compatibility only.
3936 // The should not be used in new projects and will be removed in a future
3937 // version
3938 //
3939 /**
3940 * Software version
3941 * @type string
3942 */
3943 version: '3.0.3'
3944 };
3945 //
3946 // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts
3947 //
3948 Object.assign(ext, {
3949 afnFiltering: ext.search,
3950 aTypes: ext.type.detect,
3951 ofnSearch: ext.type.search,
3952 oSort: ext.type.order,
3953 afnSortData: ext.order,
3954 aoFeatures: ext.feature,
3955 oStdClasses: ext.classes,
3956 oPagination: ext.pager,
3957 sVersion: ext.version,
3958 fnVersionCheck: check$1
3959 });
3960
3961 /**
3962 * Log an error message
3963 *
3964 * @param ctx DataTables settings object
3965 * @param level log error messages, or display them to the user
3966 * @param msg error message
3967 * @param tn Technical note id to get more information about the error.
3968 */
3969 function log(ctx, level, msg, tn) {
3970 msg =
3971 'DataTables warning: ' +
3972 (ctx ? 'table id=' + ctx.tableId + ' - ' : '') +
3973 msg;
3974 if (tn) {
3975 msg +=
3976 '. For more information about this error, please see ' +
3977 'https://datatables.net/tn/' +
3978 tn;
3979 }
3980 {
3981 // Backwards compatibility pre 1.10
3982 var type = ext.sErrMode || ext.errMode;
3983 if (ctx) {
3984 callbackFire(ctx, null, 'dt-error', [ctx, tn, msg], true);
3985 }
3986 if (type == 'alert') {
3987 alert(msg);
3988 }
3989 else if (type == 'throw') {
3990 throw new Error(msg);
3991 }
3992 else if (typeof type == 'function') {
3993 type(ctx, tn, msg);
3994 }
3995 }
3996 }
3997 /**
3998 * See if a property is defined on one object, if so assign it to the other
3999 * object
4000 *
4001 * @param ret target object
4002 * @param src source object
4003 * @param name property
4004 * @param mappedName name to map too - optional, name used if not given
4005 */
4006 function map(ret, src, name, mappedName) {
4007 if (Array.isArray(name)) {
4008 for (let i = 0; i < name.length; i++) {
4009 let val = name[i];
4010 if (Array.isArray(val)) {
4011 map(ret, src, val[0], val[1]);
4012 }
4013 else {
4014 map(ret, src, val);
4015 }
4016 }
4017 return;
4018 }
4019 if (mappedName === undefined) {
4020 mappedName = name;
4021 }
4022 if (src[name] !== undefined) {
4023 ret[mappedName] = src[name];
4024 }
4025 }
4026 /**
4027 * Bind an event handler to allow a click or return key to activate the callback.
4028 * This is good for accessibility since a return on the keyboard will have the
4029 * same effect as a click, if the element has focus.
4030 *
4031 * @param n Element to bind the action to
4032 * @param selector Selector (for delegated events)
4033 * @param fn Callback function for when the event is triggered
4034 */
4035 function bindAction(n, selector, fn) {
4036 Dom.s(n)
4037 .on('click.DT', selector, function (e) {
4038 fn(e);
4039 })
4040 .on('keypress.DT', selector, function (e) {
4041 if (e.which === 13) {
4042 e.preventDefault();
4043 fn(e);
4044 }
4045 })
4046 .on('selectstart.DT', selector, function () {
4047 // Don't want a double click resulting in text selection
4048 return false;
4049 });
4050 }
4051 /**
4052 * Register a callback function. Easily allows a callback function to be added
4053 * to an array store of callback functions that can then all be called together.
4054 *
4055 * @param settings dataTables settings object
4056 * @param store Name of the array storage for the callbacks in settings
4057 * @param fn Function to be called back
4058 */
4059 function callbackReg(ctx, store, fn) {
4060 if (fn) {
4061 ctx.callbacks[store].push(fn);
4062 }
4063 }
4064 /**
4065 * Fire callback functions and trigger events. Note that the loop over the
4066 * callback array store is done backwards! Further note that you do not want to
4067 * fire off triggers in time sensitive applications (for example cell creation)
4068 * as its slow.
4069 *
4070 * @param ctx DataTables settings object
4071 * @param callbackArr Name of the array storage for the callbacks in the context
4072 * @param eventName Name of the custom event to trigger. If null no trigger is
4073 * fired
4074 * @param args Array of arguments to pass to the callback function / trigger
4075 * @param bubbles True if the event should bubble
4076 */
4077 function callbackFire(ctx, callbackArr, eventName, args, bubbles = false) {
4078 var ret = [];
4079 if (callbackArr) {
4080 ret = ctx.callbacks[callbackArr]
4081 .slice()
4082 .reverse()
4083 .map(function (val) {
4084 return val.apply(ctx.instance, args);
4085 });
4086 }
4087 if (eventName !== null) {
4088 let table = Dom.s(ctx.table);
4089 let result = table.trigger(eventName + '.dt', bubbles, args, {
4090 dt: ctx.api
4091 });
4092 // If not yet attached to the document, trigger the event
4093 // on the body directly to sort of simulate the bubble
4094 if (bubbles && table.closest('body').count() === 0) {
4095 Dom.s('body').trigger(eventName + '.dt', bubbles, args, {
4096 dt: ctx.api
4097 });
4098 }
4099 ret.push(result[0]);
4100 }
4101 return ret;
4102 }
4103 function lengthOverflow(ctx) {
4104 var start = ctx.displayStart, end = displayEnd(ctx), len = ctx.pageLength;
4105 // If we have space to show extra rows (backing up from the end point - then
4106 // do so
4107 if (start >= end) {
4108 start = end - len;
4109 }
4110 // Keep the start record on the current page
4111 start -= start % len;
4112 if (len === -1 || start < 0) {
4113 start = 0;
4114 }
4115 ctx.displayStart = start;
4116 }
4117 /**
4118 * Detect the data source being used for the table. Used to simplify the code a
4119 * little (ajax) and to make it compress a little smaller.
4120 *
4121 * @param ctx DataTables settings object
4122 * @returns Data source
4123 */
4124 function dataSource(ctx) {
4125 if (ctx.features.serverSide) {
4126 return 'ssp';
4127 }
4128 else if (ctx.ajax) {
4129 return 'ajax';
4130 }
4131 return 'dom';
4132 }
4133 /**
4134 * Common replacement for language strings
4135 *
4136 * @param ctx DataTables settings object
4137 * @param str String with values to replace
4138 * @param entries Plural number for _ENTRIES_ - can be undefined
4139 * @returns String
4140 */
4141 function macros(ctx, str, entries) {
4142 // When infinite scrolling, we are always starting at 1. _iDisplayStart is
4143 // used only internally
4144 var formatter = ctx.formatNumber, start = ctx.displayStart + 1, len = ctx.pageLength, vis = recordsDisplay(ctx), max = recordsTotal(ctx), all = len === -1;
4145 return str
4146 .replace(/_START_/g, formatter(start, ctx))
4147 .replace(/_END_/g, formatter(displayEnd(ctx), ctx))
4148 .replace(/_MAX_/g, formatter(max, ctx))
4149 .replace(/_TOTAL_/g, formatter(vis, ctx))
4150 .replace(/_PAGE_/g, formatter(all ? 1 : Math.ceil(start / len), ctx))
4151 .replace(/_PAGES_/g, formatter(all ? 1 : Math.ceil(vis / len), ctx))
4152 .replace(/_ENTRIES_/g, ctx.api.i18n('entries', '', entries))
4153 .replace(/_ENTRIES-MAX_/g, ctx.api.i18n('entries', '', max))
4154 .replace(/_ENTRIES-TOTAL_/g, ctx.api.i18n('entries', '', vis));
4155 }
4156 /**
4157 * Add elements to an array as quickly as possible, but stack safe.
4158 *
4159 * @param arr Array to add the data to
4160 * @param data Data array that is to be added
4161 */
4162 function arrayApply(arr, data) {
4163 if (!data) {
4164 return;
4165 }
4166 // Chrome can throw a max stack error if apply is called with
4167 // too large an array, but apply is faster.
4168 if (data.length < 10000) {
4169 arr.push.apply(arr, data);
4170 }
4171 else {
4172 for (var i = 0; i < data.length; i++) {
4173 arr.push(data[i]);
4174 }
4175 }
4176 }
4177 /**
4178 * Add one or more listeners to the table
4179 *
4180 * @param that JQ for the table
4181 * @param name Event name
4182 * @param src Listener(s)
4183 */
4184 function listener(that, name, src) {
4185 let srcArr = Array.isArray(src) ? src : [src];
4186 for (var i = 0; i < srcArr.length; i++) {
4187 that.on(name + '.dt.DT', srcArr[i]);
4188 }
4189 }
4190 /**
4191 * Escape HTML entities in strings, in an object
4192 */
4193 function escapeObject(obj) {
4194 if (ext.escape.attributes) {
4195 each(obj, function (key, val) {
4196 obj[key] = escapeHtml(val);
4197 });
4198 }
4199 return obj;
4200 }
4201
4202 /*
4203 * Public helper functions. These aren't used internally by DataTables, or
4204 * called by any of the options passed into DataTables, but they can be used
4205 * externally by developers working with DataTables. They are helper functions
4206 * to make working with DataTables a little bit easier.
4207 */
4208 /**
4209 * Common logic for moment, luxon or a date action.
4210 *
4211 * Happens after __mldObj, so don't need to call `resolveWindowsLibs` again
4212 */
4213 function __mld(dtLib, momentFn, luxonFn, dateFn, arg1) {
4214 if (__moment) {
4215 return dtLib[momentFn](arg1);
4216 }
4217 else if (__luxon) {
4218 return dtLib[luxonFn](arg1);
4219 }
4220 return dateFn ? dtLib[dateFn](arg1) : dtLib;
4221 }
4222 var __mlWarning = false;
4223 var __luxon;
4224 var __moment;
4225 /**
4226 *
4227 */
4228 function resolveWindowLibs() {
4229 __luxon = util.external('luxon');
4230 __moment = util.external('moment');
4231 }
4232 function __mldObj(d, format, locale) {
4233 var dt;
4234 resolveWindowLibs();
4235 if (__moment) {
4236 dt = __moment(d, format, locale, true);
4237 if (!dt.isValid()) {
4238 return null;
4239 }
4240 }
4241 else if (__luxon) {
4242 dt =
4243 format && typeof d === 'string'
4244 ? __luxon.DateTime.fromFormat(d, format)
4245 : __luxon.DateTime.fromISO(d);
4246 if (!dt.isValid) {
4247 return null;
4248 }
4249 dt = dt.setLocale(locale);
4250 }
4251 else if (!format) {
4252 // No format given, must be ISO
4253 dt = new Date(d);
4254 }
4255 else {
4256 if (!__mlWarning) {
4257 alert('DataTables warning: Formatted date without Moment.js or Luxon - https://datatables.net/tn/17');
4258 }
4259 __mlWarning = true;
4260 }
4261 return dt;
4262 }
4263 // Wrapper for date, datetime and time which all operate the same way with the
4264 // exception of the output string for auto locale support
4265 function __mlHelper(localeString) {
4266 return function (from, to, locale, def) {
4267 // Luxon and Moment support
4268 // Argument shifting
4269 if (arguments.length === 0) {
4270 locale = 'en';
4271 to = null; // means toLocaleString
4272 from = null; // means iso8601
4273 }
4274 else if (arguments.length === 1) {
4275 locale = 'en';
4276 to = from;
4277 from = null;
4278 }
4279 else if (arguments.length === 2) {
4280 locale = to;
4281 to = from;
4282 from = null;
4283 }
4284 var typeName = 'datetime' + (to ? '-' + to : '');
4285 // Add type detection and sorting specific to this date format - we need
4286 // to be able to identify date type columns as such, rather than as
4287 // numbers in extensions. Hence the need for this.
4288 if (!store.order[typeName + '-pre']) {
4289 register$1(typeName, {
4290 detect: function (d) {
4291 // The renderer will give the value to type detect as the
4292 // type!
4293 return d === typeName ? typeName : false;
4294 },
4295 order: {
4296 pre: function (d) {
4297 // The renderer gives us Moment, Luxon or Date objects
4298 // for the sorting, all of which have a `valueOf` which
4299 // gives milliseconds epoch
4300 return d.valueOf();
4301 }
4302 }
4303 });
4304 }
4305 if (!store.className[typeName]) {
4306 store.className[typeName] = 'dt-right';
4307 }
4308 return function (d, type) {
4309 // Allow for a default value
4310 if (d === null || d === undefined) {
4311 if (def === '--now') {
4312 // We treat everything as UTC further down, so no changes
4313 // are made, as such need to get the local date / time as if
4314 // it were UTC
4315 var local = new Date();
4316 d = new Date(Date.UTC(local.getFullYear(), local.getMonth(), local.getDate(), local.getHours(), local.getMinutes(), local.getSeconds()));
4317 }
4318 else {
4319 d = '';
4320 }
4321 }
4322 if (type === 'type') {
4323 // Typing uses the type name for fast matching
4324 return typeName;
4325 }
4326 if (d === '') {
4327 return type !== 'sort'
4328 ? ''
4329 : __mldObj('0000-01-01 00:00:00', null, locale);
4330 }
4331 // Shortcut. If `from` and `to` are the same, we are using the
4332 // renderer to format for ordering, not display - its already in the
4333 // display format.
4334 if (to !== null &&
4335 from === to &&
4336 type !== 'sort' &&
4337 type !== 'type' &&
4338 !(d instanceof Date)) {
4339 return d;
4340 }
4341 // Determine if there is a timezone. If there is, we want to reuse
4342 // it for the output, so the timezone doesn't change between the
4343 // input and output.
4344 let options = {};
4345 let tzMatch = typeof d === 'string' ? d.match(util.regex.isoTimezone) : null;
4346 if (tzMatch) {
4347 options.timeZone = tzMatch[1] === 'Z' ? 'UTC' : tzMatch[1];
4348 }
4349 // Get a Date object (Luxon, moment or Date)
4350 var dt = __mldObj(d, from, locale);
4351 if (dt === null) {
4352 return d;
4353 }
4354 if (type === 'sort') {
4355 return dt;
4356 }
4357 var formatted = to === null
4358 ? __mld(dt, 'toDate', 'toJSDate', '')[localeString](navigator.language, options)
4359 : __mld(dt, 'format', 'toFormat', 'toISOString', to);
4360 // XSS protection
4361 return type === 'display' ? util.escapeHtml(formatted) : formatted;
4362 };
4363 };
4364 }
4365 // Based on locale, determine standard number formatting
4366 // Fallback for legacy browsers is US English
4367 var __thousands = ',';
4368 var __decimal = '.';
4369 if (window.Intl !== undefined) {
4370 try {
4371 var num = new Intl.NumberFormat().formatToParts(100000.1);
4372 for (var i = 0; i < num.length; i++) {
4373 if (num[i].type === 'group') {
4374 __thousands = num[i].value;
4375 }
4376 else if (num[i].type === 'decimal') {
4377 __decimal = num[i].value;
4378 }
4379 }
4380 }
4381 catch (e) {
4382 // noop
4383 }
4384 }
4385 /**
4386 * Register a date / time format for DataTables to use.
4387 *
4388 * @param format The date / time format to detect data in. Please refer to the
4389 * Moment.js or Luxon document for the full list of tokens, depending on which
4390 * of the two libraries you are using.
4391 * @param locale The locale to pass to Moment.js / Luxon.
4392 */
4393 function datetime(format, locale) {
4394 var typeName = 'datetime-' + format;
4395 if (!locale) {
4396 locale = 'en';
4397 }
4398 if (!store.order[typeName]) {
4399 register$1(typeName, {
4400 detect: function (d) {
4401 var dt = __mldObj(d, format, locale);
4402 return d === '' || dt ? typeName : false;
4403 },
4404 order: {
4405 pre: function (d) {
4406 return __mldObj(d, format, locale) || 0;
4407 }
4408 }
4409 });
4410 }
4411 if (!store.className[typeName]) {
4412 store.className[typeName] = 'dt-right';
4413 }
4414 }
4415 /**
4416 * Helpers for `columns.render`.
4417 */
4418 var helpers = {
4419 date: __mlHelper('toLocaleDateString'),
4420 datetime: __mlHelper('toLocaleString'),
4421 time: __mlHelper('toLocaleTimeString'),
4422 number: function (thousands, decimal, precision, prefix, postfix) {
4423 // Auto locale detection
4424 if (thousands === null || thousands === undefined) {
4425 thousands = __thousands;
4426 }
4427 if (decimal === null || decimal === undefined) {
4428 decimal = __decimal;
4429 }
4430 return {
4431 display: function (d) {
4432 if (typeof d !== 'number' && typeof d !== 'string') {
4433 return d;
4434 }
4435 if (d === '' || d === null) {
4436 return d;
4437 }
4438 var flo = typeof d === 'number' ? d : parseFloat(d);
4439 var negative = flo < 0 ? '-' : '';
4440 var abs = Math.abs(flo);
4441 // Scientific notation for large and small numbers
4442 if (abs >= 100000000000 || (abs < 0.0001 && abs !== 0)) {
4443 var exp = flo.toExponential(precision).split(/e\+?/);
4444 return exp[0] + ' x 10<sup>' + exp[1] + '</sup>';
4445 }
4446 // If NaN then there isn't much formatting that we can do - just
4447 // return immediately, escaping any HTML (this was supposed to
4448 // be a number after all)
4449 if (isNaN(flo)) {
4450 return util.escapeHtml(d);
4451 }
4452 flo = flo.toFixed(precision);
4453 var absPart = Math.abs(flo);
4454 var intPart = Math.abs(parseInt(flo, 10));
4455 var floatPart = precision
4456 ? decimal +
4457 (absPart - intPart).toFixed(precision).substring(2)
4458 : '';
4459 // If zero, then can't have a negative prefix
4460 if (intPart === 0 && parseFloat(floatPart) === 0) {
4461 negative = '';
4462 }
4463 return (negative +
4464 (prefix || '') +
4465 intPart
4466 .toString()
4467 .replace(/\B(?=(\d{3})+(?!\d))/g, thousands) +
4468 floatPart +
4469 (postfix || ''));
4470 }
4471 };
4472 },
4473 text: function () {
4474 return {
4475 display: util.escapeHtml,
4476 filter: util.escapeHtml
4477 };
4478 }
4479 };
4480
4481 /**
4482 * Column options that can be given to DataTables at initialisation time.
4483 */
4484 const defaults$4 = {
4485 ariaTitle: '',
4486 cellType: 'td',
4487 className: '',
4488 contentPadding: '',
4489 createdCell: null,
4490 data: null,
4491 defaultContent: null,
4492 footer: null,
4493 name: '',
4494 orderable: true,
4495 orderData: null,
4496 orderDataType: 'std',
4497 orderSequence: ['asc', 'desc', ''],
4498 render: null,
4499 search: null,
4500 searchable: true,
4501 title: null,
4502 type: null,
4503 visible: true,
4504 width: null
4505 };
4506
4507 /**
4508 * Internal settings object used for individual columns. Instances are held in
4509 * the setting object's `columns` array and contains all the information that
4510 * DataTables needs about each individual column.
4511 *
4512 * Note that this object is related to the column defaults but this one is the
4513 * internal data store for DataTables's cache of columns. It should NOT be
4514 * manipulated outside of DataTables. Any configuration should be done through
4515 * the initialisation options.
4516 */
4517 class Settings {
4518 constructor() {
4519 /**
4520 * Flag to indicate if HTML5 data attributes should be used as the data
4521 * source for filtering or sorting. True is either are.
4522 */
4523 this.attrSrc = false;
4524 this.ariaTitle = '';
4525 /**
4526 * The class to apply to all cells in the table's `tbody`` for the column
4527 */
4528 this.className = null;
4529 /**
4530 * When DataTables calculates the column widths to assign to each column, it
4531 * finds the longest string in each column and then constructs a temporary
4532 * table and reads the widths from that. The problem with this is that "mmm"
4533 * is much wider then "iiii", but the latter is a longer string - thus the
4534 * calculation can go wrong (doing it properly and putting it into an DOM
4535 * object and measuring that is horribly(!) slow). Thus as a "work around"
4536 * we provide this option. It will append its value to the text that is
4537 * found to be the longest string for the column - i.e. padding.
4538 */
4539 this.contentPadding = null;
4540 /**
4541 * Property to read the value for the cells in the column from the data
4542 * source array / object. If null, then the default content is used, if a
4543 * function is given then the return from the function is used.
4544 */
4545 this.data = null;
4546 /**
4547 * Allows a default value to be given for a column's data, and will be used
4548 * whenever a null data source is encountered (this can be because mData is
4549 * set to null, or because the data source itself is null).
4550 */
4551 this.defaultContent = null;
4552 /**
4553 * Name for the column, allowing reference to the column by name as well as
4554 * by index (needs a lookup to work by name).
4555 */
4556 this.name = null;
4557 /**
4558 * A list of the columns that sorting should occur on when this column is
4559 * sorted. That this property is an array allows multi-column sorting to be
4560 * defined for a column (for example first name / last name columns would
4561 * benefit from this). The values are integers pointing to the columns to be
4562 * sorted on (typically it will be a single integer pointing at itself, but
4563 * that doesn't need to be the case).
4564 */
4565 this.orderData = [];
4566 /**
4567 * Custom sorting data type - defines which of the available plug-ins in
4568 * afnSortData the custom sorting will use - if any is defined.
4569 */
4570 this.orderDataType = 'std';
4571 /**
4572 * Class to be applied to the header element when sorting on this column
4573 */
4574 this.orderingClass = null;
4575 /**
4576 * Define the sorting directions that are applied to the column, in sequence
4577 * as the column is repeatedly sorted upon - i.e. the first value is used as
4578 * the sorting direction when the column if first sorted (clicked on). Sort
4579 * it again (click again) and it will move on to the next index. Repeat
4580 * until loop.
4581 */
4582 this.orderSequence = [];
4583 /**
4584 * Partner property to mData which is used (only when defined) to get the
4585 * data - i.e. it is basically the same as mData, but without the 'set'
4586 * option, and also the data fed to it is the result from mData. This is the
4587 * rendering method to match the data method of mData.
4588 */
4589 this.render = null;
4590 /**
4591 * Title of the column - what is seen in the TH element (nTh).
4592 */
4593 this.title = null;
4594 /**
4595 * Store for manual type assignment using the `column.type` option. This
4596 * is held in store so we can manipulate the column's `type` property.
4597 */
4598 this.typeManual = null;
4599 /** Cached longest strings from a column */
4600 this.wideStrings = null;
4601 /**
4602 * Width of the column
4603 */
4604 this.width = null;
4605 /**
4606 * Width of the column when it was first "encountered"
4607 */
4608 this.widthOrig = null;
4609 }
4610 }
4611
4612 const defaults$3 = {
4613 boundary: false,
4614 caseInsensitive: true,
4615 columns: null,
4616 exact: false,
4617 regex: false,
4618 return: false,
4619 search: '',
4620 smart: true
4621 };
4622 /**
4623 * Create a new search options object
4624 *
4625 * @param parts Values to assign, otherwise the defaults will be used
4626 * @returns New object
4627 */
4628 function create$2(parts = {}) {
4629 return util.object.assignDeep({}, defaults$3, parts);
4630 }
4631
4632 const browser = {
4633 barWidth: -1,
4634 scrollbarLeft: false
4635 };
4636 const hungarianToCamelRe = /^(a|aa|ai|ao|as|b|fn|i|m|o|s)([A-Z])([a-z].*$)/;
4637 /**
4638 * Take an object which has hungarian notation parameters and convert them to
4639 * camelCase style. This is to allow compatibility with DataTables 1.9 and
4640 * earlier which only used hungarian notation, and also with DataTables 1.10 - 2
4641 * which allowed it to be used.
4642 */
4643 function hungarianToCamel(user) {
4644 if (!user) {
4645 return user;
4646 }
4647 let userKeys = Object.keys(user);
4648 let userAny = user;
4649 for (let i = 0; i < userKeys.length; i++) {
4650 let userKey = userKeys[i];
4651 let match = userKey.match(hungarianToCamelRe);
4652 // Is the key in hungarian notation?
4653 if (match) {
4654 // If so map it down
4655 user[match[2].toLowerCase() + match[3]] = userAny[userKey];
4656 }
4657 // Recurse down through the object
4658 if (util.is.plainObject(userAny[userKey])) {
4659 hungarianToCamel(userAny[userKey]);
4660 }
4661 }
4662 return user;
4663 }
4664 /**
4665 * Map one parameter onto another
4666 *
4667 * @param o Object to map
4668 * @param newKey The new parameter name
4669 * @param oldKey The old parameter name
4670 */
4671 function compatMap(o, newKey, oldKey) {
4672 if (o[oldKey] !== undefined) {
4673 o[newKey] = o[oldKey];
4674 }
4675 }
4676 /**
4677 * Provide backwards compatibility for the main DT options. Note that the new
4678 * options are mapped onto the old parameters, so this is an external interface
4679 * change only.
4680 *
4681 * @param init Object to map
4682 */
4683 function compatOpts(init) {
4684 // Convert any old style parameters to camelCase
4685 hungarianToCamel(init);
4686 // Map old parameter names to new
4687 compatMap(init, 'ordering', 'sort');
4688 compatMap(init, 'orderMulti', 'sortMulti');
4689 compatMap(init, 'orderClasses', 'sortClasses');
4690 compatMap(init, 'orderCellsTop', 'sortCellsTop');
4691 compatMap(init, 'order', 'sorting');
4692 compatMap(init, 'orderFixed', 'sortingFixed');
4693 compatMap(init, 'paging', 'paginate');
4694 compatMap(init, 'pagingType', 'paginationType');
4695 compatMap(init, 'pageLength', 'displayLength');
4696 compatMap(init, 'searching', 'filter');
4697 compatMap(init, 'stateDuration', 'cookieDuration');
4698 // Boolean initialisation of x-scrolling
4699 if (typeof init.scrollX === 'boolean') {
4700 init.scrollX = init.scrollX ? '100%' : '';
4701 }
4702 // Objects for ordering
4703 if (typeof init.ordering === 'object') {
4704 init.orderIndicators =
4705 init.ordering.indicators !== undefined
4706 ? init.ordering.indicators
4707 : true;
4708 init.orderHandler =
4709 init.ordering.handler !== undefined ? init.ordering.handler : true;
4710 init.ordering = true;
4711 }
4712 else if (init.ordering === false) {
4713 init.orderIndicators = false;
4714 init.orderHandler = false;
4715 }
4716 else if (init.ordering === true) {
4717 init.orderIndicators = true;
4718 init.orderHandler = true;
4719 }
4720 // Which cells are the title cells?
4721 if (typeof init.orderCellsTop === 'boolean') {
4722 init.titleRow = init.orderCellsTop;
4723 }
4724 // Column search objects are in an array, so it needs to be converted
4725 // element by element
4726 var searchCols = init.searchCols;
4727 if (searchCols) {
4728 for (var i = 0, iLen = searchCols.length; i < iLen; i++) {
4729 if (searchCols[i]) {
4730 hungarianToCamel(searchCols[i]);
4731 }
4732 }
4733 }
4734 // Enable search delay if server-side processing is enabled
4735 if (init.serverSide && !init.searchDelay) {
4736 init.searchDelay = 400;
4737 }
4738 // Language
4739 if (init.language && init.language.url && !init.language.ajax) {
4740 init.language.ajax = init.language.url;
4741 }
4742 }
4743 /**
4744 * Provide backwards compatibility for column options. Note that the new options
4745 * are mapped onto the old parameters, so this is an external interface change
4746 * only.
4747 *
4748 * @param init Object to map
4749 */
4750 function compatCols(init) {
4751 // Convert any old style parameters to camelCase
4752 hungarianToCamel(init);
4753 // typeof columnDefaults
4754 compatMap(init, 'orderable', 'sortable');
4755 compatMap(init, 'orderData', 'dataSort');
4756 compatMap(init, 'orderSequence', 'sorting');
4757 compatMap(init, 'orderDataType', 'sortDataType');
4758 compatMap(init, 'className', 'class');
4759 // orderData can be given as an integer
4760 var dataSort = init.aDataSort;
4761 var orderData = init.orderData;
4762 if (typeof dataSort === 'number') {
4763 init.orderData = [dataSort];
4764 }
4765 if (typeof orderData === 'number') {
4766 init.orderData = [orderData];
4767 }
4768 // Backwards compatibility for mDataProp from 1.9-
4769 if (init.dataProp !== undefined && !init.data) {
4770 init.data = init.dataProp;
4771 }
4772 }
4773 /**
4774 * Browser feature detection for capabilities, quirks
4775 *
4776 * @param ctx DataTables settings object
4777 */
4778 function browserDetect(ctx) {
4779 // We don't need to do this every time DataTables is constructed, the values
4780 // calculated are specific to the browser and OS configuration which we
4781 // don't expect to change between initialisations
4782 if (browser.barWidth === -1) {
4783 // Scrolling feature / quirks detection
4784 var n = Dom
4785 .c('div')
4786 .css({
4787 position: 'fixed',
4788 top: '0',
4789 left: -1 * window.pageXOffset + 'px', // allow for scrolling
4790 height: '1px',
4791 width: '1px',
4792 overflow: 'hidden'
4793 })
4794 .append(Dom
4795 .c('div')
4796 .css({
4797 position: 'absolute',
4798 top: '1px',
4799 left: '1px',
4800 width: '100px',
4801 overflow: 'scroll'
4802 })
4803 .append(Dom.c('div').css({
4804 width: '100%',
4805 height: '10px'
4806 })))
4807 .appendTo('body');
4808 var outer = n.children();
4809 var inner = outer.children();
4810 browser.barWidth = outer.get(0).offsetWidth - outer.get(0).clientWidth;
4811 browser.scrollbarLeft = Math.round(inner.offset().left) !== 1;
4812 n.remove();
4813 }
4814 Object.assign(ctx.browser, browser);
4815 ctx.scroll.barWidth = browser.barWidth;
4816 }
4817
4818 const defaults$2 = {
4819 addedClasses: [],
4820 cells: [],
4821 data: [],
4822 details: undefined,
4823 detailsShow: undefined,
4824 displayData: null,
4825 idx: -1,
4826 orderCache: null,
4827 searchCellCache: null,
4828 searchRowCache: null,
4829 src: 'dom',
4830 tr: null
4831 };
4832 /**
4833 * Create a new object that is a row model
4834 *
4835 * @param parts Values to assign, otherwise the defaults will be used
4836 * @returns New object
4837 */
4838 function create$1(parts = {}) {
4839 return util.object.assignDeep({}, defaults$2, parts);
4840 }
4841
4842 /**
4843 * Add a data array to the table, creating DOM node etc. This is the parallel to
4844 * gatherData, but for adding rows from a JavaScript source, rather than a
4845 * DOM source.
4846 *
4847 * @param settings DataTables settings object
4848 * @param dataIn data array to be added
4849 * @param tr TR element to add to the table - optional. If not given, DataTables
4850 * will create a row automatically
4851 * @param tds Array of TD|TH elements for the row - must be given if tr is.
4852 * @returns >=0 if successful (index of new data entry), -1 if failed
4853 */
4854 function addData(settings, dataIn, tr, tds) {
4855 /* Create the object for storing information about this new row */
4856 var rowIdx = settings.data.length;
4857 var row = create$1({
4858 src: tr ? 'dom' : 'data',
4859 idx: rowIdx
4860 });
4861 row.data = dataIn;
4862 settings.data.push(row);
4863 var columns = settings.columns;
4864 for (var i = 0, iLen = columns.length; i < iLen; i++) {
4865 // Invalidate the column types as the new data needs to be revalidated
4866 columns[i].type = null;
4867 }
4868 /* Add to the display array */
4869 settings.displayMaster.push(rowIdx);
4870 var id = settings.rowIdFn(dataIn);
4871 if (id !== undefined) {
4872 settings.ids[id] = row;
4873 }
4874 /* Create the DOM information, or register it if already present */
4875 if (tr || !settings.features.deferRender) {
4876 createTr(settings, rowIdx, tr, tds);
4877 }
4878 return rowIdx;
4879 }
4880 /**
4881 * Add one or more TR elements to the table. Generally we'd expect to
4882 * use this for reading data from a DOM sourced table, but it could be
4883 * used for an TR element. Note that if a TR is given, it is used (i.e.
4884 * it is not cloned).
4885 *
4886 * @param settings DataTables settings object
4887 * @param rows The TR element(s) to add to the table
4888 * @returns Array of indexes for the added rows
4889 */
4890 function addTr(settings, rows) {
4891 return rows.mapTo(el => {
4892 let row = getRowElementsFromNode(settings, el);
4893 return addData(settings, row.data, el, row.cells);
4894 });
4895 }
4896 /**
4897 * Get the data for a given cell from the internal cache, taking into account
4898 * data mapping
4899 *
4900 * @param settings DataTables settings object
4901 * @param rowIdx data row id
4902 * @param colIdx Column index
4903 * @param type data get type ('display', 'type' 'filter|search' 'sort|order')
4904 * @returns Cell data
4905 */
4906 function getCellData(settings, rowIdx, colIdx, type) {
4907 if (type === 'search') {
4908 type = 'filter';
4909 }
4910 else if (type === 'order') {
4911 type = 'sort';
4912 }
4913 var row = settings.data[rowIdx];
4914 if (!row) {
4915 return undefined;
4916 }
4917 var draw = settings.drawCount;
4918 var col = settings.columns[colIdx];
4919 var rowData = row.data;
4920 var defaultContent = col.defaultContent;
4921 var cellData = col.dataGet(rowData, type, {
4922 settings: settings,
4923 row: rowIdx,
4924 col: colIdx
4925 });
4926 // Allow for a node being returned for non-display types
4927 if (type !== 'display' &&
4928 cellData &&
4929 typeof cellData === 'object' &&
4930 cellData.nodeName) {
4931 cellData = cellData.innerHTML;
4932 }
4933 if (cellData === undefined) {
4934 if (settings.drawError != draw && defaultContent === null) {
4935 log(settings, 0, 'Requested unknown parameter ' +
4936 (typeof col.data == 'function'
4937 ? '{function}'
4938 : "'" + col.data + "'") +
4939 ' for row ' +
4940 rowIdx +
4941 ', column ' +
4942 colIdx, 4);
4943 settings.drawError = draw;
4944 }
4945 return defaultContent;
4946 }
4947 // When the data source is null and a specific data type is requested (i.e.
4948 // not the original data), we can use default column data
4949 if ((cellData === rowData || cellData === null) &&
4950 defaultContent !== null &&
4951 type !== undefined) {
4952 cellData = defaultContent;
4953 }
4954 else if (typeof cellData === 'function') {
4955 // If the data source is a function, then we run it and use the return,
4956 // executing in the scope of the data object (for instances)
4957 return cellData.call(rowData);
4958 }
4959 if (cellData === null && type === 'display') {
4960 return '';
4961 }
4962 if (type === 'filter') {
4963 var formatters = ext.type.search;
4964 if (col.type && formatters[col.type]) {
4965 cellData = formatters[col.type](cellData);
4966 }
4967 }
4968 return cellData;
4969 }
4970 /**
4971 * Set the value for a specific cell, into the internal data cache
4972 *
4973 * @param settings DataTables settings object
4974 * @param rowIdx data row id
4975 * @param colIdx Column index
4976 * @param val Value to set
4977 */
4978 function setCellData(settings, rowIdx, colIdx, val) {
4979 let row = settings.data[rowIdx];
4980 if (row) {
4981 let col = settings.columns[colIdx];
4982 let rowData = row.data;
4983 col.dataSet(rowData, val, {
4984 settings: settings,
4985 row: rowIdx,
4986 col: colIdx
4987 });
4988 }
4989 }
4990 /**
4991 * Write a value to a cell
4992 *
4993 * @param td Cell
4994 * @param val Value
4995 */
4996 function writeCell(td, val) {
4997 let cell = Dom.s(td);
4998 if (val && typeof val === 'object' && val.nodeName) {
4999 cell.empty().append(val);
5000 }
5001 else {
5002 cell.html(val);
5003 }
5004 }
5005 /**
5006 * Return an array with the full table data
5007 *
5008 * @param settings DataTables settings object
5009 * @returns array {array} aData Master data array
5010 */
5011 function getDataMaster(settings) {
5012 return util.array.pluck(settings.data, 'data');
5013 }
5014 /**
5015 * Nuke the table
5016 *
5017 * @param settings DataTables settings object
5018 */
5019 function clearTable(settings) {
5020 settings.data.length = 0;
5021 settings.displayMaster.length = 0;
5022 settings.display.length = 0;
5023 settings.ids = {};
5024 }
5025 /**
5026 * Mark cached data as invalid such that a re-read of the data will occur when
5027 * the cached data is next requested. Also update from the data source object.
5028 *
5029 * @param settings DataTables settings object
5030 * @param rowIdx Row index to invalidate
5031 * @param src Source to invalidate from: undefined, 'auto', 'dom' or 'data'
5032 * @param colIdx Column index to invalidate. If undefined the whole row will be
5033 * invalidated
5034 */
5035 function invalidateRow(settings, rowIdx, src, colIdx) {
5036 var row = settings.data[rowIdx];
5037 var i, iLen;
5038 if (!row) {
5039 return;
5040 }
5041 // Remove the cached data for the row
5042 row.orderCache = null;
5043 row.searchCellCache = null;
5044 row.displayData = null;
5045 // Are we reading last data from DOM or the data object?
5046 if (src === 'dom' || ((!src || src === 'auto') && row.src === 'dom')) {
5047 // Read the data from the DOM
5048 row.data = getRowElementsFromModel(settings, row, colIdx).data;
5049 }
5050 else {
5051 // Reading from data object, update the DOM
5052 var cells = row.cells;
5053 var display = getRowDisplay(settings, rowIdx);
5054 if (cells.length) {
5055 if (colIdx !== undefined) {
5056 writeCell(cells[colIdx], display[colIdx]);
5057 }
5058 else {
5059 for (i = 0, iLen = cells.length; i < iLen; i++) {
5060 writeCell(cells[i], display[i]);
5061 }
5062 }
5063 }
5064 }
5065 invalidColumn(settings, colIdx);
5066 // Update DataTables special `DT_*` attributes for the row
5067 rowAttributes(settings, row);
5068 callbackFire(settings, null, 'rowInvalidate', [settings, rowIdx, colIdx], false);
5069 }
5070 /**
5071 * Column specific invalidation
5072 *
5073 * @param settings DataTables settings object
5074 * @param colIdx Column index to invalidate, or all columns if not given
5075 */
5076 function invalidColumn(settings, colIdx) {
5077 // Column specific invalidation
5078 var cols = settings.columns;
5079 if (colIdx !== undefined) {
5080 // Type - the data might have changed
5081 cols[colIdx].type = null;
5082 // Max length string. Its a fairly cheep recalculation, so not worth
5083 // something more complicated
5084 cols[colIdx].wideStrings = null;
5085 }
5086 else {
5087 for (let i = 0, iLen = cols.length; i < iLen; i++) {
5088 cols[i].type = null;
5089 cols[i].wideStrings = null;
5090 }
5091 }
5092 settings.containerWidth = -1;
5093 }
5094 /**
5095 * Get the cells and data for a given row - from a <tr> element
5096 *
5097 * @param settings DataTables settings object
5098 * @param row TR element from which to read data or existing row object from
5099 * which to re-read the data from the cells
5100 */
5101 function getRowElementsFromNode(settings, row) {
5102 let data = settings.rowReadObject ? {} : [];
5103 let cells = Dom.s(row).children('th, td');
5104 let id = row.getAttribute('id');
5105 cells.each((el, idx) => {
5106 readCellData(settings, el, data, idx);
5107 });
5108 if (id) {
5109 util.set(settings.rowId)(data, id);
5110 }
5111 return {
5112 data: data,
5113 cells: cells.get()
5114 };
5115 }
5116 /**
5117 * Get the cells and data for a given row - from an existing row model
5118 *
5119 * @param settings DataTables settings object
5120 * @param row Existing row object from which to re-read the data from the cells
5121 * @param colIdx Optional column index
5122 */
5123 function getRowElementsFromModel(settings, row, colIdx) {
5124 let tds = row.cells;
5125 for (let i = 0; i < tds.length; i++) {
5126 if (colIdx === undefined || colIdx === i) {
5127 readCellData(settings, tds[i], row.data, i);
5128 }
5129 }
5130 // Read the ID from the DOM if present
5131 if (row.tr) {
5132 let id = row.tr.getAttribute('id');
5133 if (id) {
5134 util.set(settings.rowId)(row.data, id);
5135 }
5136 }
5137 return {
5138 data: row.data,
5139 cells: tds
5140 };
5141 }
5142 /**
5143 * Read data from a cell into the data source object
5144 *
5145 * @param settings DataTables settings object
5146 * @param cell The HTML cell element to read from
5147 * @param data Data object / array to store data into
5148 * @param colIdx The column index for the cell
5149 */
5150 function readCellData(settings, cell, data, colIdx) {
5151 let column = settings.columns[colIdx];
5152 let contents = cell.innerHTML.trim();
5153 if (column.attrSrc) {
5154 // If we are working with attributes from the cell as values
5155 let dataPoint = column.data;
5156 let setter = util.set(dataPoint._);
5157 let attr = function (str, cell) {
5158 if (typeof str === 'string') {
5159 let idx = str.indexOf('@');
5160 if (idx !== -1) {
5161 let att = str.substring(idx + 1);
5162 let setter = util.set(str);
5163 setter(data, cell.getAttribute(att));
5164 }
5165 }
5166 };
5167 setter(data, contents);
5168 attr(dataPoint.sort, cell);
5169 attr(dataPoint.type, cell);
5170 attr(dataPoint.filter, cell);
5171 }
5172 else {
5173 if (!column.setter) {
5174 // Cache the setter function
5175 column.setter = util.set(column.data);
5176 }
5177 column.setter(data, contents);
5178 }
5179 }
5180
5181 /**
5182 * Recalculate the column widths, if needed (by a column having been
5183 * invalidated)
5184 *
5185 * @param settings DataTables settings object
5186 */
5187 function columnWidths(settings) {
5188 if (settings.columns.map(c => c.wideStrings).includes(null)) {
5189 calculateColumnWidths(settings);
5190 }
5191 }
5192 /**
5193 * Calculate the width of columns for the table
5194 *
5195 * @param settings DataTables settings object
5196 */
5197 function calculateColumnWidths(settings) {
5198 // Not interested in doing column width calculation if auto-width is disabled
5199 if (!settings.features.autoWidth) {
5200 return;
5201 }
5202 var table = settings.table, columns = settings.columns, scroll = settings.scroll, scrollY = scroll.y, scrollX = scroll.x, visibleColumns = getColumns(settings, 'visible'), tableWidthAttr = table.getAttribute('width'), // from DOM element
5203 tableContainer = table.parentElement, i, j, column, columnIdx;
5204 var styleWidth = table.style.width;
5205 var containerWidth = wrapperWidth(settings);
5206 // Don't re-run for the same width as the last time
5207 if (containerWidth === settings.containerWidth) {
5208 return false;
5209 }
5210 settings.containerWidth = containerWidth;
5211 // If there is no width applied as a CSS style or as an attribute, we assume that
5212 // the width is intended to be 100%, which is usually is in CSS, but it is very
5213 // difficult to correctly parse the rules to get the final result.
5214 if (!styleWidth && !tableWidthAttr) {
5215 table.style.width = '100%';
5216 styleWidth = '100%';
5217 }
5218 if (styleWidth && styleWidth.indexOf('%') !== -1) {
5219 tableWidthAttr = styleWidth;
5220 }
5221 // Let plug-ins know that we are doing a recalc, in case they have changed any of the
5222 // visible columns their own way (e.g. Responsive uses display:none).
5223 callbackFire(settings, null, 'column-calc', [{ visible: visibleColumns }], false);
5224 // Construct a worst case table with the widest, assign any user defined
5225 // widths, then insert it into the DOM and allow the browser to do all
5226 // the hard work of calculating table widths
5227 var tmpTable = Dom
5228 .s(table.cloneNode())
5229 .css('visibility', 'hidden')
5230 .css('margin', '0')
5231 .attrRemove('id');
5232 // Clean up the table body
5233 tmpTable.append(Dom.c('tbody'));
5234 // Clone the table header and footer - we can't use the header / footer
5235 // from the cloned table, since if scrolling is active, the table's
5236 // real header and footer are contained in different table tags
5237 tmpTable
5238 .append(settings.thead.cloneNode(true))
5239 .append(settings.tfoot.cloneNode(true));
5240 // Remove any assigned widths from the footer (from scrolling)
5241 tmpTable.find('tfoot th, tfoot td').css('width', '');
5242 // Apply custom sizing to the cloned header
5243 tmpTable.find('thead th, thead td').each(cell => {
5244 // Get the `width` from the header layout
5245 var width = columnsSumWidth(settings, cell, true);
5246 if (width) {
5247 cell.style.width = width;
5248 // For scrollX we need to force the column width otherwise the
5249 // browser will collapse it. If this width is smaller than the
5250 // width the column requires, then it will have no effect
5251 if (scrollX) {
5252 cell.style.minWidth = width;
5253 Dom.s(cell).append(Dom.c('div').css({
5254 width: width,
5255 margin: '0',
5256 padding: '0',
5257 border: '0',
5258 height: '1px'
5259 }));
5260 }
5261 }
5262 else {
5263 cell.style.width = '';
5264 }
5265 });
5266 // Get the widest strings for each of the visible columns and add them to
5267 // our table to create a "worst case"
5268 var longestData = [];
5269 for (i = 0; i < visibleColumns.length; i++) {
5270 longestData.push(getWideStrings(settings, visibleColumns[i]));
5271 }
5272 if (longestData.length) {
5273 for (i = 0; i < longestData[0].length; i++) {
5274 var tr = Dom.c('tr').appendTo(tmpTable.find('tbody'));
5275 for (j = 0; j < visibleColumns.length; j++) {
5276 columnIdx = visibleColumns[j];
5277 column = columns[columnIdx];
5278 var longest = longestData[j][i] || '';
5279 var autoClass = ext.type.className[column.type];
5280 var padding = column.contentPadding || (scrollX ? '-' : '');
5281 var text = longest + padding;
5282 var cell = Dom
5283 .c('td')
5284 .classAdd(autoClass)
5285 .classAdd(column.className)
5286 .appendTo(tr);
5287 if (longest.indexOf('<') === -1 &&
5288 longest.indexOf('&') === -1) {
5289 cell.text(text);
5290 }
5291 else {
5292 cell.html(text);
5293 }
5294 }
5295 }
5296 }
5297 // Tidy the temporary table - remove name attributes so there aren't
5298 // duplicated in the dom (radio elements for example)
5299 tmpTable.find('[name]').attrRemove('name');
5300 // Table has been built, attach to the document so we can work with it.
5301 // A holding element is used, positioned at the top of the container
5302 // with minimal height, so it has no effect on if the container scrolls
5303 // or not. Otherwise it might trigger scrolling when it actually isn't
5304 // needed
5305 var holder = Dom
5306 .c('div')
5307 .css(scrollX || scrollY
5308 ? {
5309 position: 'absolute',
5310 top: '0',
5311 left: '0',
5312 height: '1px',
5313 right: '0',
5314 overflow: 'hidden'
5315 }
5316 : {})
5317 .append(tmpTable)
5318 .appendTo(tableContainer);
5319 // When scrolling (X or Y) we want to set the width of the table as
5320 // appropriate. However, when not scrolling leave the table width as it
5321 // is. This results in slightly different, but I think correct behaviour
5322 if (scrollX) {
5323 tmpTable.css('width', 'auto').attrRemove('width');
5324 // If there is no width attribute or style, then allow the table to
5325 // collapse
5326 if (tmpTable.width() < tableContainer.clientWidth && tableWidthAttr) {
5327 tmpTable.width(tableContainer.clientWidth);
5328 }
5329 }
5330 else if (scrollY) {
5331 tmpTable.width(tableContainer.clientWidth);
5332 }
5333 else if (tableWidthAttr) {
5334 tmpTable.width(tableWidthAttr);
5335 }
5336 // Get the width of each column in the constructed table
5337 var total = 0;
5338 var bodyCells = tmpTable.find('tbody tr').eq(0).children();
5339 for (i = 0; i < visibleColumns.length; i++) {
5340 // Use getBounding for sub-pixel accuracy, which we then want to round
5341 // up!
5342 var bounding = bodyCells.get(i).getBoundingClientRect().width;
5343 // Total is tracked to remove any sub-pixel errors as the outerWidth
5344 // of the table might not equal the total given here
5345 total += bounding;
5346 // Width for each column to use
5347 columns[visibleColumns[i]].width = stringToCss(bounding);
5348 }
5349 table.style.width = stringToCss(total);
5350 // Finished with the table - ditch it
5351 holder.remove();
5352 // If there is a width attr, we want to attach an event listener which
5353 // allows the table sizing to automatically adjust when the window is
5354 // resized. Use the width attr rather than CSS, since we can't know if the
5355 // CSS is a relative value or absolute - DOM read is always px.
5356 if (tableWidthAttr) {
5357 table.style.width = stringToCss(tableWidthAttr);
5358 }
5359 if ((tableWidthAttr || scrollX) && !settings.reszEvt) {
5360 var resize = util.throttle(function () {
5361 var newWidth = wrapperWidth(settings);
5362 // Don't do it if destroying or the container width is 0
5363 if (!settings.destroying && newWidth !== 0) {
5364 adjustColumnSizing(settings);
5365 }
5366 });
5367 // For browsers that support it (~2020 onwards for wide support) we can watch for the
5368 // container changing width.
5369 if (window.ResizeObserver) {
5370 // This is a tricky beast - if the element is visible when `.observe()` is called,
5371 // then the callback is immediately run. Which we don't want. If the element isn't
5372 // visible, then it isn't run, but we want it to run when it is then made visible.
5373 // This flag allows the above to be satisfied.
5374 var first = Dom.s(settings.tableWrapper).isVisible();
5375 // Use an empty div to attach the observer so it isn't impacted by height changes
5376 var resizer = Dom
5377 .c('div')
5378 .css({
5379 width: '100%',
5380 height: '0'
5381 })
5382 .classAdd('dt-autosize')
5383 .appendTo(settings.tableWrapper);
5384 settings.resizeObserver = new ResizeObserver(function (e) {
5385 if (first) {
5386 first = false;
5387 }
5388 else {
5389 resize();
5390 }
5391 });
5392 settings.resizeObserver.observe(resizer.get(0));
5393 }
5394 else {
5395 // For old browsers, the best we can do is listen for a window
5396 // resize
5397 window.addEventListener('resize', resize);
5398 settings.windowResizeCb = resize; // For removal in `destroy`
5399 }
5400 settings.reszEvt = true;
5401 }
5402 }
5403 /**
5404 * Get the width of the DataTables wrapper element
5405 *
5406 * @param settings DataTables settings object
5407 * @returns Width
5408 */
5409 function wrapperWidth(settings) {
5410 let wrapper = Dom.s(settings.tableWrapper);
5411 return wrapper.isVisible() ? wrapper.width() : 0;
5412 }
5413 /**
5414 * Get the widest strings for each column.
5415 *
5416 * It is very difficult to determine what the widest string actually is due to variable character
5417 * width and kerning. Doing an exact calculation with the DOM or even Canvas would kill performance
5418 * and this is a critical point, so we use two techniques to determine a collection of the longest
5419 * strings from the column, which will likely contain the widest strings:
5420 *
5421 * 1) Get the top three longest strings from the column
5422 * 2) Get the top three widest words (i.e. an unbreakable phrase)
5423 *
5424 * @param settings DataTables settings object
5425 * @param colIdx column of interest
5426 * @returns Array of the longest strings
5427 */
5428 function getWideStrings(settings, colIdx) {
5429 var column = settings.columns[colIdx];
5430 // Do we need to recalculate (i.e. was invalidated), or just use the cached data?
5431 if (!column.wideStrings) {
5432 var allStrings = [];
5433 var collection = [];
5434 // Create an array with the string information for the column
5435 for (var i = 0, iLen = settings.displayMaster.length; i < iLen; i++) {
5436 var rowIdx = settings.displayMaster[i];
5437 var data = getRowDisplay(settings, rowIdx)[colIdx];
5438 var cellString = data && typeof data === 'object' && data.nodeType
5439 ? data.innerHTML
5440 : data + '';
5441 // Remove id / name attributes from elements so they
5442 // don't interfere with existing elements
5443 cellString = cellString
5444 .replace(/id=".*?"/g, '')
5445 .replace(/name=".*?"/g, '');
5446 // Don't want script, dialog or template tags in the width
5447 // calculations as they are hidden content
5448 cellString = cellString
5449 .replace(/<script[\s\S]*?<\/script(?:\s[^>]*)?>/gi, ' ')
5450 .replace(/<dialog[\s\S]*?<\/dialog(?:\s[^>]*)?>/gi, ' ')
5451 .replace(/<template[\s\S]*?<\/template(?:\s[^>]*)?>/gi, ' ');
5452 var noHtml = util.string
5453 .stripHtml(cellString, ' ')
5454 .replace(/&nbsp;/g, ' ');
5455 collection.push({
5456 str: cellString,
5457 len: noHtml.length
5458 });
5459 allStrings.push(noHtml);
5460 }
5461 // Order and then cut down to the size we need
5462 collection
5463 .sort(function (a, b) {
5464 return b.len - a.len;
5465 })
5466 .splice(3);
5467 column.wideStrings = collection.map(function (item) {
5468 return item.str;
5469 });
5470 // Longest unbroken string
5471 const parts = allStrings.join(' ').split(' ');
5472 parts.sort(function (a, b) {
5473 return b.length - a.length;
5474 });
5475 if (parts.length) {
5476 column.wideStrings.push(parts[0]);
5477 }
5478 if (parts.length > 1) {
5479 column.wideStrings.push(parts[1]);
5480 }
5481 if (parts.length > 2) {
5482 column.wideStrings.push(parts[3]);
5483 }
5484 }
5485 return column.wideStrings;
5486 }
5487 /**
5488 * Append a CSS unit (only if required) to a string
5489 *
5490 * @param s Value to css-ify
5491 * @returns Value with css unit
5492 */
5493 function stringToCss(s) {
5494 if (s === null) {
5495 return '0px';
5496 }
5497 if (typeof s == 'number') {
5498 return s < 0 ? '0px' : s + 'px';
5499 }
5500 // Check it has a unit character already
5501 return s.match(/\d$/) ? s + 'px' : s;
5502 }
5503 /**
5504 * Re-insert the `col` elements for current visibility
5505 *
5506 * @param settings DT settings
5507 */
5508 function colGroup(settings) {
5509 var cols = settings.columns;
5510 settings.colgroup.empty();
5511 for (var i = 0; i < cols.length; i++) {
5512 if (cols[i].visible) {
5513 settings.colgroup.append(cols[i].colEl);
5514 }
5515 }
5516 }
5517
5518 /**
5519 * Scrolling setup
5520 *
5521 * @param settings DataTables settings object
5522 * @returns Node to add to the DOM
5523 */
5524 function featureTable(settings) {
5525 let table = Dom.s(settings.table);
5526 let scroll = settings.scroll;
5527 let scrollX = scroll.x;
5528 let scrollY = scroll.y;
5529 // No scrolling or x-scrolling only
5530 if (scrollY === '' && scrollX === '') {
5531 return table.get(0);
5532 }
5533 let classes = settings.classes.scrolling;
5534 let caption = settings.captionNode;
5535 let captionSide = caption
5536 ? caption._captionSide
5537 : null;
5538 let tableCloneHeader = table.clone(false);
5539 let tableCloneFooter = table.clone(false);
5540 let footer = table.children('tfoot');
5541 let size = function (s) {
5542 return !s ? '100%' : stringToCss(s);
5543 };
5544 /*
5545 * The HTML structure that we want to generate in this function is:
5546 * div - scroller
5547 * div - scroll head
5548 * div - scroll head inner
5549 * table - scroll head table
5550 * thead - thead
5551 * div - scroll body
5552 * table - table (master table)
5553 * thead - thead clone for sizing
5554 * tbody - tbody
5555 * div - scroll foot
5556 * div - scroll foot inner
5557 * table - scroll foot table
5558 * tfoot - tfoot
5559 */
5560 let scroller = Dom.c('div')
5561 .classAdd(classes.container)
5562 .attr('role', 'table')
5563 .append(Dom.c('div')
5564 .classAdd(classes.header.self)
5565 .css({
5566 overflow: 'hidden',
5567 position: 'relative',
5568 border: '0',
5569 width: scrollX ? size(scrollX) : '100%'
5570 })
5571 .attr('role', 'none')
5572 .append(Dom.c('div')
5573 .classAdd(classes.header.inner)
5574 .css({
5575 'box-sizing': 'content-box',
5576 width: scroll.xInner || '100%'
5577 })
5578 .attr('role', 'none')
5579 .append(tableCloneHeader
5580 .attrRemove('id')
5581 .css('margin-left', '0')
5582 .append(captionSide === 'top' ? caption : null)
5583 .append(table.children('thead')))))
5584 .append(Dom.c('div')
5585 .classAdd(classes.body)
5586 .css({
5587 position: 'relative',
5588 overflow: 'auto',
5589 width: size(scrollX)
5590 })
5591 .attr('role', 'none')
5592 .append(table));
5593 if (footer.count()) {
5594 scroller.append(Dom.c('div')
5595 .classAdd(classes.footer.self)
5596 .css({
5597 overflow: 'hidden',
5598 border: '0',
5599 width: scrollX ? size(scrollX) : '100%'
5600 })
5601 .attr('role', 'none')
5602 .append(Dom.c('div')
5603 .classAdd(classes.footer.inner)
5604 .attr('role', 'none')
5605 .append(tableCloneFooter
5606 .attrRemove('id')
5607 .css('margin-left', '0')
5608 .append(captionSide === 'bottom' ? caption : null)
5609 .append(table.children('tfoot')))));
5610 }
5611 let children = scroller.children();
5612 let scrollHead = children.eq(0);
5613 let scrollBody = children.eq(1);
5614 let scrollFoot = children.eq(2);
5615 // When the body is scrolled, then we also want to scroll the header and
5616 // footer. Note that each element has its own scroll listener, and that in
5617 // turn sets the scroll for the other elements. However this doesn't lead to
5618 // an infinite loop as `scroll` is only triggered if the value changes.
5619 scrollBody.on('scroll.DT', () => {
5620 let scrollLeft = scrollBody.scrollLeft();
5621 scrollHead.scrollLeft(scrollLeft);
5622 scrollFoot.scrollLeft(scrollLeft);
5623 });
5624 scrollHead.on('scroll.DT', () => {
5625 let scrollLeft = scrollHead.scrollLeft();
5626 scrollBody.scrollLeft(scrollLeft);
5627 scrollFoot.scrollLeft(scrollLeft);
5628 });
5629 scrollFoot.on('scroll.DT', () => {
5630 let scrollLeft = scrollFoot.scrollLeft();
5631 scrollHead.scrollLeft(scrollLeft);
5632 scrollBody.scrollLeft(scrollLeft);
5633 });
5634 scrollBody.css('max-height', size(scrollY));
5635 if (!scroll.collapse) {
5636 scrollBody.css('height', size(scrollY));
5637 }
5638 settings.scrollHead = scrollHead;
5639 settings.scrollBody = scrollBody;
5640 settings.scrollFoot = scrollFoot;
5641 // On redraw - align columns
5642 settings.callbacks.draw.push(scrollDraw);
5643 // Aria roles - because we break the table up into parts we need to be very
5644 // explicit with the roles to create the accessability tree for the table,
5645 // otherwise browser's attempt to "fix" the tree by filling in what it
5646 // thinks are gaps. The static elements that we can assign roles to are done
5647 // here. Dynamic ones are done in the draw function below.
5648 table.attr('role', 'none');
5649 table.find('tbody').attr('role', 'rowgroup');
5650 tableCloneHeader.attr('role', 'none');
5651 tableCloneFooter.attr('role', 'none');
5652 settings.colgroup.find('colgroup').attr('role', 'none');
5653 // Move the info feature's aria desc by to the new "table"
5654 let describedBy = table.attr('aria-describedby');
5655 if (describedBy) {
5656 scroller.attr('aria-describedby', describedBy);
5657 table.attrRemove('aria-describedby');
5658 }
5659 return scroller.get(0);
5660 }
5661 /**
5662 * Update the header, footer and body tables for resizing - i.e. column
5663 * alignment.
5664 *
5665 * Welcome to the most horrible function DataTables. The process that this
5666 * function follows is basically:
5667 * 1. Re-create the table inside the scrolling div
5668 * 2. Correct colgroup > col values if needed
5669 * 3. Copy colgroup > col over to header and footer
5670 * 4. Clean up
5671 *
5672 * @param settings DataTables settings object
5673 */
5674 function scrollDraw(settings) {
5675 // Given that this is such a monster function, a lot of variables are use
5676 // to try and keep the minimised size as small as possible
5677 let scroll = settings.scroll, barWidth = scroll.barWidth, divHeader = settings.scrollHead, divHeaderInner = divHeader.children('div'), divHeaderTable = divHeaderInner.children('table'), divBodyEl = settings.scrollBody, divBody = divBodyEl, divFooter = settings.scrollFoot, divFooterInner = divFooter.children('div'), divFooterTable = divFooterInner.children('table'), header = Dom.s(settings.thead), table = Dom.s(settings.table), footer = Dom.s(settings.tfoot), browser = settings.browser, headerCopy, footerCopy;
5678 // If the scrollbar visibility has changed from the last draw, we need to
5679 // adjust the column sizes as the table width will have changed to account
5680 // for the scrollbar
5681 let scrollBarVis = divBodyEl.get(0).scrollHeight > divBodyEl.get(0).clientHeight;
5682 if (settings.scrollBarVis !== scrollBarVis &&
5683 settings.scrollBarVis !== undefined) {
5684 settings.scrollBarVis = scrollBarVis;
5685 adjustColumnSizing(settings);
5686 return; // adjust column sizing will call this function again
5687 }
5688 else {
5689 settings.scrollBarVis = scrollBarVis;
5690 }
5691 header.find('thead').attr('role', 'rowgroup');
5692 footer.find('tfoot').attr('role', 'rowgroup');
5693 // 1. Re-create the table inside the scrolling div
5694 // Remove the old minimised thead and tfoot elements in the inner table
5695 table.children('thead, tfoot').remove();
5696 // Clone the current header and footer elements and then place it into the
5697 // inner table
5698 headerCopy = header.clone(true).prependTo(table);
5699 headerCopy.find('th, td').attrRemove('tabindex');
5700 headerCopy.find('[id]').attrRemove('id');
5701 if (footer.count()) {
5702 footerCopy = footer.clone(true).prependTo(table);
5703 footerCopy.find('[id]').attrRemove('id');
5704 }
5705 // 2. Correct colgroup > col values if needed
5706 // It is possible that the cell sizes are smaller than the content, so we need to
5707 // correct colgroup>col for such cases. This can happen if the auto width detection
5708 // uses a cell which has a longer string, but isn't the widest! For example
5709 // "Chief Executive Officer (CEO)" is the longest string in the demo, but
5710 // "Systems Administrator" is actually the widest string since it doesn't collapse.
5711 // Note the use of translating into a column index to get the `col` element. This
5712 // is because of Responsive which might remove `col` elements, knocking the alignment
5713 // of the indexes out.
5714 if (settings.display.length) {
5715 // Get the column sizes from the first row in the table. This should really be a
5716 // [].find, but it wasn't supported in Chrome until Sept 2015, and DT has 10 year
5717 // browser support
5718 let firstTr = null;
5719 let start = dataSource(settings) !== 'ssp' ? settings.displayStart : 0;
5720 for (let i = start; i < start + settings.display.length; i++) {
5721 let idx = settings.display[i];
5722 let row = settings.data[idx];
5723 if (row) {
5724 let tr = row.tr;
5725 if (tr) {
5726 firstTr = tr;
5727 break;
5728 }
5729 }
5730 }
5731 if (firstTr) {
5732 let colSizes = Dom.s(firstTr)
5733 .children('th, td')
5734 .mapTo(function (cell, idx) {
5735 return {
5736 idx: visibleToColumnIndex(settings, idx),
5737 width: Dom.s(cell).width('outer')
5738 };
5739 });
5740 // Check against what the colgroup > col is set to and correct if needed
5741 for (let i = 0; i < colSizes.length; i++) {
5742 let colEl = settings.columns[colSizes[i].idx].colEl;
5743 colEl.css('width', colSizes[i].width + 'px');
5744 if (scroll.x) {
5745 colEl.css('minWidth', colSizes[i].width + 'px');
5746 }
5747 }
5748 }
5749 }
5750 // 3. Copy the colgroup over to the header and footer
5751 divHeaderTable.find('colgroup').remove();
5752 divHeaderTable.append(settings.colgroup.clone(true));
5753 if (footer) {
5754 divFooterTable.find('colgroup').remove();
5755 divFooterTable.append(settings.colgroup.clone(true));
5756 }
5757 // "Hide" the header and footer that we used for the sizing. We need to keep
5758 // the content of the cell so that the width applied to the header and body
5759 // both match, but we want to hide it completely.
5760 headerCopy.find('th, td').each(function (el) {
5761 Dom.c('div')
5762 .classAdd('dt-scroll-sizing')
5763 .append(Array.from(el.childNodes))
5764 .appendTo(el);
5765 });
5766 if (footerCopy) {
5767 footerCopy.find('th, td').each(function (el) {
5768 Dom.c('div')
5769 .classAdd('dt-scroll-sizing')
5770 .append(Array.from(el.childNodes))
5771 .appendTo(el);
5772 });
5773 }
5774 // 4. Clean up
5775 // Figure out if there are scrollbar present - if so then we need the header and footer to
5776 // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar)
5777 let isScrolling = Math.floor(table.height()) > divBodyEl.get(0).clientHeight ||
5778 divBody.css('overflow-y') == 'scroll';
5779 let paddingSide = 'padding' + (browser.scrollbarLeft ? 'Left' : 'Right');
5780 // Set the width's of the header and footer tables
5781 let outerWidth = table.width('withPadding');
5782 divHeaderTable.css('width', stringToCss(outerWidth));
5783 divHeaderInner
5784 .css('width', stringToCss(outerWidth))
5785 .css(paddingSide, isScrolling ? barWidth + 'px' : '0px');
5786 if (footer.count()) {
5787 divFooterTable.css('width', stringToCss(outerWidth));
5788 divFooterInner
5789 .css('width', stringToCss(outerWidth))
5790 .css(paddingSide, isScrolling ? barWidth + 'px' : '0px');
5791 }
5792 // Correct DOM ordering for colgroup - comes before the thead
5793 table.children('colgroup').prependTo(table);
5794 // Remove tabindex from the hidden row elements
5795 table.find('thead, tfoot').find('[tabindex]').attrRemove('tabindex');
5796 // Dynamic ARIA roles - see setup for details on why this is needed
5797 table
5798 .find('thead, tfoot')
5799 .attr('role', 'none')
5800 .find('[role]')
5801 .attrRemove('role');
5802 table.find('tbody tr:not([role])').attr('role', 'row');
5803 table.find('tbody td:not([role]), tbody th:not([role])').attr('role', 'cell');
5804 scrollAria(headerCopy);
5805 scrollAria(footerCopy);
5806 // Adjust the position of the header in case we loose the y-scrollbar
5807 divBody.trigger('scroll');
5808 // If sorting or filtering has occurred, jump the scrolling back to the top
5809 // only if we aren't holding the position
5810 if ((settings.wasOrdered || settings.wasFiltered) && !settings.drawHold) {
5811 divBodyEl.scrollTop(0);
5812 }
5813 }
5814 /**
5815 * Apply ARIA roles for the header / footer of a scrolling table
5816 * @param element
5817 */
5818 function scrollAria(element) {
5819 if (element) {
5820 element.find('tfoot:not([role])').attr('role', 'rowgroup');
5821 element.find('tr:not([role])').attr('role', 'row');
5822 element.find('th:not([role])').attr('role', 'columnheader');
5823 element.find('td:not([role])').attr('role', 'cell');
5824 }
5825 }
5826
5827 /**
5828 * Add a column to the list used for the table with default values
5829 *
5830 * @param settings DataTables settings object
5831 */
5832 function addColumn(settings) {
5833 // Add column to aoColumns array
5834 let columnIdx = settings.columns.length;
5835 let column = util.object.assign({}, new Settings(), defaults$4, {
5836 orderData: defaults$4.orderData
5837 ? defaults$4.orderData
5838 : [columnIdx],
5839 data: defaults$4.data ? defaults$4.data : columnIdx,
5840 idx: columnIdx,
5841 searchFixed: {},
5842 colEl: Dom
5843 .c('col')
5844 .attr('data-dt-column', columnIdx)
5845 });
5846 settings.columns.push(column);
5847 // Legacy support for `searchCols` property. If set, and there is a value
5848 // for this column, then it should be applied to the search. The new, column
5849 // specific `search` option is applied in `columnOptions`, but we always
5850 // want the search object for the column to exist.
5851 let searchCols = settings.searchCols;
5852 settings.searches[columnIdx] = create$2(searchCols[columnIdx]
5853 ? hungarianToCamel(searchCols[columnIdx])
5854 : {});
5855 settings.searches[columnIdx].columns = [columnIdx];
5856 }
5857 /**
5858 * Apply options for a column
5859 *
5860 * @param settings DataTables settings object
5861 * @param colIdx column index to consider
5862 * @param options Column configuration options
5863 */
5864 function columnOptions(settings, colIdx, options) {
5865 var column = settings.columns[colIdx];
5866 /* User specified column options */
5867 if (options !== undefined && options !== null) {
5868 // Backwards compatibility
5869 compatCols(options);
5870 if (options.type) {
5871 column.typeManual = options.type;
5872 }
5873 // `class` is a reserved word in JavaScript, so we need to provide
5874 // the ability to use a valid name for the camel case input
5875 if (options.className && !options.className) {
5876 options.className = options.className;
5877 }
5878 var origClass = column.className;
5879 util.object.assign(column, options);
5880 map(column, options, 'width', 'widthOrig');
5881 // Merge class from previously defined classes with this one, rather
5882 // than just overwriting it in the extend above
5883 if (origClass !== column.className) {
5884 column.className = origClass + ' ' + column.className;
5885 }
5886 map(column, options, 'orderData');
5887 // Search term specifically for this column
5888 if (options.search) {
5889 util.object.assign(settings.searches[colIdx], options.search);
5890 }
5891 }
5892 /* Cache the data get and set functions for speed */
5893 var dataSrc = column.data;
5894 var dataFn = util.get(dataSrc);
5895 // The `render` option can be given as an array to access the helper
5896 // rendering methods. The first element is the rendering method to use, the
5897 // rest are the parameters to pass
5898 if (column.render && Array.isArray(column.render)) {
5899 var copy = column.render.slice();
5900 var name = copy.shift();
5901 column.render = helpers[name].apply(window, copy);
5902 }
5903 column.renderer = column.render ? util.get(column.render) : null;
5904 var attrTest = function (src) {
5905 return typeof src === 'string' && src.indexOf('@') !== -1;
5906 };
5907 column.attrSrc =
5908 !!dataSrc &&
5909 util.is.plainObject(dataSrc) &&
5910 (attrTest(dataSrc.sort) ||
5911 attrTest(dataSrc.type) ||
5912 attrTest(dataSrc.filter));
5913 column.setter = null;
5914 column.dataGet = function (rowData, type, meta) {
5915 var innerData = dataFn(rowData, type, undefined, meta);
5916 return column.renderer && type
5917 ? column.renderer(innerData, type, rowData, meta)
5918 : innerData;
5919 };
5920 column.dataSet = function (rowData, val, meta) {
5921 return util.set(dataSrc)(rowData, val, meta);
5922 };
5923 // Indicate if DataTables should read DOM data as an object or array
5924 // Used in _fnGetRowElements
5925 if (typeof dataSrc !== 'number' && !column._isArrayHost) {
5926 settings.rowReadObject = true;
5927 }
5928 // Feature sorting overrides column specific when off
5929 if (!settings.features.ordering) {
5930 column.orderable = false;
5931 }
5932 }
5933 /**
5934 * Adjust the table column widths for new data. Note: you would probably want to
5935 * do a redraw after calling this function!
5936 *
5937 * @param settings DataTables settings object
5938 */
5939 function adjustColumnSizing(settings) {
5940 calculateColumnWidths(settings);
5941 columnSizes(settings);
5942 let scroll = settings.scroll;
5943 if (scroll.y !== '' || scroll.x !== '') {
5944 scrollDraw(settings);
5945 }
5946 callbackFire(settings, null, 'column-sizing', [settings]);
5947 }
5948 /**
5949 * Apply column sizes
5950 *
5951 * @param settings DataTables settings object
5952 */
5953 function columnSizes(settings) {
5954 let cols = settings.columns;
5955 for (let i = 0; i < cols.length; i++) {
5956 let width = columnsSumWidth(settings, [i], false);
5957 if (width) {
5958 cols[i].colEl.css('width', width);
5959 if (settings.scroll.x) {
5960 cols[i].colEl.css('min-width', width);
5961 }
5962 }
5963 }
5964 }
5965 /**
5966 * Convert the index of a visible column to the index in the data array (take
5967 * account of hidden columns)
5968 *
5969 * @param settings DataTables settings object
5970 * @param visIdx Visible column index to lookup
5971 * @returns i the data index
5972 */
5973 function visibleToColumnIndex(settings, visIdx) {
5974 let aiVis = getColumns(settings, 'visible');
5975 return typeof aiVis[visIdx] === 'number' ? aiVis[visIdx] : null;
5976 }
5977 /**
5978 * Convert the index of an index in the data array and convert it to the visible
5979 * column index (take account of hidden columns)
5980 *
5981 * @param settings DataTables settings object
5982 * @param match Column index to lookup
5983 * @returns The data index
5984 */
5985 function columnIndexToVisible(settings, match) {
5986 let aiVis = getColumns(settings, 'visible');
5987 let iPos = aiVis.indexOf(match);
5988 return iPos !== -1 ? iPos : null;
5989 }
5990 /**
5991 * Get the number of visible columns
5992 *
5993 * @param settings DataTables settings object
5994 * @returns i the number of visible columns
5995 */
5996 function visibleColumns(settings) {
5997 let layout = settings.header;
5998 let columns = settings.columns;
5999 let vis = 0;
6000 if (layout.length) {
6001 for (let i = 0, iLen = layout[0].length; i < iLen; i++) {
6002 if (columns[i].visible &&
6003 Dom.s(layout[0][i].cell).css('display') !== 'none') {
6004 vis++;
6005 }
6006 }
6007 }
6008 return vis;
6009 }
6010 /**
6011 * Get an array of column indexes that match a given property
6012 *
6013 * @param settings DataTables settings object
6014 * @param param Parameter in the columns array to look for
6015 * @returns Array of indexes with matched properties
6016 */
6017 function getColumns(settings, param) {
6018 let a = [];
6019 settings.columns.map(function (val, i) {
6020 if (val[param]) {
6021 a.push(i);
6022 }
6023 });
6024 return a;
6025 }
6026 /**
6027 * Allow the result from a type detection function to be `true` while
6028 * translating that into a string. Old type detection functions will return the
6029 * type name if it passes. An object store would be better, but not backwards
6030 * compatible.
6031 *
6032 * @param typeDetect Object or function for type detection
6033 * @param res Result from the type detection function
6034 * @returns Type name or false
6035 */
6036 function _typeResult(typeDetect, res) {
6037 return res === true ? typeDetect._name : res;
6038 }
6039 /**
6040 * Calculate the 'type' of a column
6041 * @param settings DataTables settings object
6042 */
6043 function columnTypes(settings, originalTypes = '') {
6044 var columns = settings.columns;
6045 var data = settings.data;
6046 var types = ext.type.detect;
6047 var i, iLen, j, jen, k, ken;
6048 var col, detectedType, cache;
6049 if (!originalTypes) {
6050 originalTypes = columns.map(c => c.type).join(',');
6051 }
6052 // For each column, spin over the data type detection functions, seeing if
6053 // one matches
6054 for (i = 0, iLen = columns.length; i < iLen; i++) {
6055 col = columns[i];
6056 cache = [];
6057 if (!col.type && col.typeManual) {
6058 col.type = col.typeManual;
6059 }
6060 else if (!col.type) {
6061 // With SSP type detection can be unreliable and error prone, so we
6062 // provide a way to turn it off.
6063 if (!settings.typeDetect) {
6064 return;
6065 }
6066 for (j = 0, jen = types.length; j < jen; j++) {
6067 let typeDetect = types[j];
6068 let oneOf;
6069 let allOf;
6070 let init;
6071 let one = false;
6072 // There can be either one, or three type detection functions
6073 if (typeof typeDetect === 'function') {
6074 allOf = typeDetect;
6075 }
6076 else {
6077 oneOf = typeDetect.oneOf;
6078 allOf = typeDetect.allOf;
6079 init = typeDetect.init;
6080 }
6081 detectedType = null;
6082 // Fast detect based on column assignment
6083 if (init) {
6084 detectedType = _typeResult(typeDetect, init(settings, col, i));
6085 if (detectedType) {
6086 col.type = detectedType;
6087 break;
6088 }
6089 }
6090 for (k = 0, ken = data.length; k < ken; k++) {
6091 if (!data[k]) {
6092 continue;
6093 }
6094 // Use a cache array so we only need to get the type data
6095 // from the formatter once (when using multiple detectors)
6096 if (cache[k] === undefined) {
6097 cache[k] = getCellData(settings, k, i, 'type');
6098 }
6099 // Only one data point in the column needs to match this
6100 // function
6101 if (oneOf && !one) {
6102 one = _typeResult(typeDetect, oneOf(cache[k], settings));
6103 }
6104 // All data points need to match this function
6105 detectedType = _typeResult(typeDetect, allOf(cache[k], settings));
6106 // If null, then this type can't apply to this column, so
6107 // rather than testing all cells, break out. There is an
6108 // exception for the last type which is `html`. We need to
6109 // scan all rows since it is possible to mix string and HTML
6110 // types
6111 if (!detectedType && j !== types.length - 3) {
6112 break;
6113 }
6114 // Only a single match is needed for html type since it is
6115 // bottom of the pile and very similar to string - but it
6116 // must not be empty
6117 if (detectedType === 'html' && !util.is.empty(cache[k])) {
6118 break;
6119 }
6120 }
6121 // Type is valid for all data points in the column - use this
6122 // type
6123 if ((oneOf && one && detectedType) ||
6124 (!oneOf && detectedType)) {
6125 col.type = detectedType;
6126 break;
6127 }
6128 }
6129 // Fall back - if no type was detected, always use string
6130 if (!col.type) {
6131 col.type = 'string';
6132 }
6133 }
6134 // Set class names for header / footer for auto type classes
6135 var autoClass = ext.type.className[col.type];
6136 if (autoClass) {
6137 _columnAutoClass(settings.header, i, autoClass);
6138 _columnAutoClass(settings.footer, i, autoClass);
6139 }
6140 var renderer = ext.type.render[col.type];
6141 // This can only happen once! There is no way to remove
6142 // a renderer. After the first time the renderer has
6143 // already been set so createTr will run the renderer itself.
6144 if (renderer && !col.renderer) {
6145 col.renderer = util.get(renderer);
6146 _columnAutoRender(settings, i);
6147 }
6148 }
6149 var newTypes = columns.map(c => c.type).join(',');
6150 if (newTypes !== originalTypes) {
6151 callbackFire(settings, null, 'columnTypes', [settings], false);
6152 }
6153 }
6154 /**
6155 * Apply an auto detected renderer to data which doesn't yet have a renderer
6156 */
6157 function _columnAutoRender(settings, colIdx) {
6158 let data = settings.data;
6159 for (let i = 0; i < data.length; i++) {
6160 let d = data[i];
6161 if (d && d.tr) {
6162 // We have to update the display here since there is no invalidation
6163 // check for the data
6164 let display = getCellData(settings, i, colIdx, 'display');
6165 d.displayData[colIdx] = display;
6166 writeCell(d.cells[colIdx], display);
6167 // No need to update sort / filter data since it has been
6168 // invalidated and will be re-read with the renderer now applied
6169 }
6170 }
6171 }
6172 /**
6173 * Apply a class name to a column's header cells
6174 *
6175 * @param container The header / footer structure array
6176 * @param colIdx Column index
6177 * @param className Class name to apply
6178 */
6179 function _columnAutoClass(container, colIdx, className) {
6180 container.forEach(function (row) {
6181 if (row[colIdx] && row[colIdx].unique) {
6182 Dom.s(row[colIdx].cell).classAdd(className);
6183 }
6184 });
6185 }
6186 /**
6187 * Take the column definitions and static columns arrays and calculate how they
6188 * relate to column indexes. The callback function will then apply the
6189 * definition found for a column to a suitable configuration object.
6190 *
6191 * @param settings DataTables settings object
6192 * @param aoColDefs The aoColumnDefs array that is to be applied
6193 * @param aoCols The aoColumns array that defines columns individually
6194 * @param headerLayout Layout for header as it was loaded
6195 * @param fn Callback function - takes two parameters, the calculated column
6196 * index and the definition for that column.
6197 */
6198 function applyColumnDefs(settings, aoColDefs, aoCols, headerLayout, fn) {
6199 var i, iLen, j, jLen, k, kLen;
6200 var columns = settings.columns;
6201 if (aoCols) {
6202 for (i = 0, iLen = aoCols.length; i < iLen; i++) {
6203 // Compat
6204 if (aoCols[i] && aoCols[i].name) {
6205 columns[i].name = aoCols[i].name;
6206 }
6207 }
6208 }
6209 // Column definitions with aTargets
6210 if (aoColDefs) {
6211 // Loop over the definitions array - loop in reverse so first instance
6212 // has priority
6213 for (i = aoColDefs.length - 1; i >= 0; i--) {
6214 let def = aoColDefs[i];
6215 /* Each definition can target multiple columns, as it is an array */
6216 let aTargets = def.target !== undefined
6217 ? def.target
6218 : def.targets !== undefined
6219 ? def.targets
6220 : def.aTargets; // legacy
6221 if (!Array.isArray(aTargets)) {
6222 aTargets = [aTargets];
6223 }
6224 for (j = 0, jLen = aTargets.length; j < jLen; j++) {
6225 var target = aTargets[j];
6226 if (typeof target === 'number' && target >= 0) {
6227 /* Add columns that we don't yet know about */
6228 while (columns.length <= target) {
6229 addColumn(settings);
6230 }
6231 /* Integer, basic index */
6232 fn(target, def);
6233 }
6234 else if (typeof target === 'number' && target < 0) {
6235 /* Negative integer, right to left column counting */
6236 fn(columns.length + target, def);
6237 }
6238 else if (typeof target === 'string') {
6239 for (k = 0, kLen = columns.length; k < kLen; k++) {
6240 if (target === '_all') {
6241 // Apply to all columns
6242 fn(k, def);
6243 }
6244 else if (target.indexOf(':name') !== -1) {
6245 // Column selector
6246 if (columns[k].name === target.replace(':name', '')) {
6247 fn(k, def);
6248 }
6249 }
6250 else {
6251 // Cell selector
6252 headerLayout.forEach(function (row) {
6253 if (row[k]) {
6254 var cell = row[k].cell;
6255 // Legacy support. Note that it means that
6256 // we don't support an element name selector
6257 // only, since they are treated as class
6258 // names for 1.x compat.
6259 if (target.match(/^[a-z][\w-]*$/i)) {
6260 target = '.' + target;
6261 }
6262 if (cell.matches(target)) {
6263 fn(k, def);
6264 }
6265 }
6266 });
6267 }
6268 }
6269 }
6270 }
6271 }
6272 }
6273 // Statically defined columns array
6274 if (aoCols) {
6275 for (i = 0, iLen = aoCols.length; i < iLen; i++) {
6276 fn(i, aoCols[i]);
6277 }
6278 }
6279 }
6280 /**
6281 * Get the width for a given set of columns
6282 *
6283 * @param settings DataTables settings object
6284 * @param targets Columns - comma separated string or array of numbers
6285 * @param original Use the original width (true) or calculated (false)
6286 * @param incVisible Include visible columns (true) or not (false)
6287 * @returns Combined CSS value
6288 */
6289 function columnsSumWidth(settings, targets, original, incVisible) {
6290 if (!Array.isArray(targets)) {
6291 targets = columnsFromHeader(targets);
6292 }
6293 let sum = 0;
6294 let unit = 'px';
6295 let columns = settings.columns;
6296 for (let i = 0, iLen = targets.length; i < iLen; i++) {
6297 let column = columns[targets[i]];
6298 let definedWidth = original ? column.widthOrig : column.width;
6299 if (column.visible === false) {
6300 continue;
6301 }
6302 if (definedWidth === null || definedWidth === undefined) {
6303 return null; // can't determine a defined width - browser defined
6304 }
6305 else if (typeof definedWidth === 'number') {
6306 sum += definedWidth;
6307 }
6308 else {
6309 let matched = definedWidth.match(/([\d\.]+)([^\d]*)/);
6310 if (matched) {
6311 sum += parseFloat(matched[1]);
6312 unit = matched.length === 3 ? matched[2] : 'px';
6313 }
6314 }
6315 }
6316 return sum + unit;
6317 }
6318 /**
6319 * Determine what columns a header cell covers (can be multiple for colspan
6320 * cases).
6321 *
6322 * @param cell The header cell in question
6323 * @returns An array of column indexes
6324 */
6325 function columnsFromHeader(cell) {
6326 let attr = Dom.s(cell).closest('[data-dt-column]').attr('data-dt-column');
6327 if (!attr) {
6328 return [];
6329 }
6330 return attr.split(',').map(function (val) {
6331 return parseInt(val);
6332 });
6333 }
6334
6335 /**
6336 * Generate the node required for the processing node
6337 *
6338 * @param ctx DataTables settings object
6339 */
6340 function processingHtml(ctx) {
6341 var table = ctx.table;
6342 var scrolling = ctx.scroll.x !== '' || ctx.scroll.y !== '';
6343 if (ctx.features.processing) {
6344 var n = Dom
6345 .c('div')
6346 .attr('id', ctx.tableId + '_processing')
6347 .attr('role', 'status')
6348 .classAdd(ctx.classes.processing.container)
6349 .html(ctx.language.processing)
6350 .append(Dom
6351 .c('div')
6352 .append(Dom.c('div'))
6353 .append(Dom.c('div'))
6354 .append(Dom.c('div'))
6355 .append(Dom.c('div')));
6356 // Different positioning depending on if scrolling is enabled or not
6357 if (scrolling) {
6358 n.prependTo(Dom.s(ctx.tableWrapper).find('div.dt-scroll').get(0));
6359 }
6360 else {
6361 n.insertBefore(table);
6362 }
6363 Dom.s(table).on('processing.dt.DT', (e, s, show) => {
6364 n.css('display', show ? 'block' : 'none');
6365 });
6366 }
6367 }
6368 /**
6369 * Display or hide the processing indicator
6370 *
6371 * @param ctx DataTables settings object
6372 * @param show Show the processing indicator (true) or not (false)
6373 */
6374 function processingDisplay(ctx, show) {
6375 // Ignore cases when we are still redrawing
6376 if (ctx.doingDraw && show === false) {
6377 return;
6378 }
6379 callbackFire(ctx, null, 'processing', [ctx, show]);
6380 }
6381 /**
6382 * Show the processing element if an action takes longer than a given time
6383 *
6384 * @param ctx DataTables settings object
6385 * @param enable Do (true) or not (false) async processing (local feature enablement)
6386 * @param run Function to run
6387 */
6388 function processingRun(ctx, enable, run) {
6389 if (!enable) {
6390 // Immediate execution, synchronous
6391 run();
6392 }
6393 else {
6394 processingDisplay(ctx, true);
6395 // Allow the processing display to show if needed
6396 setTimeout(function () {
6397 run();
6398 processingDisplay(ctx, false);
6399 }, 0);
6400 }
6401 }
6402
6403 function renderer(ctx, type) {
6404 var render = ctx.renderer;
6405 var host = ext.renderer[type];
6406 if (plainObject(render) && render[type]) {
6407 // Specific renderer for this type. If available use it, otherwise use
6408 // the default.
6409 return host[render[type]] || host._;
6410 }
6411 else if (typeof render === 'string') {
6412 // Common renderer - if there is one available for this type use it,
6413 // otherwise use the default
6414 return host[render] || host._;
6415 }
6416 // Use the default
6417 return host._;
6418 }
6419
6420 /**
6421 * Add the options to the page HTML for the table
6422 *
6423 * @param ctx DataTables context
6424 */
6425 function createLayout(ctx) {
6426 var classes = ctx.classes;
6427 // Wrapper div around everything DataTables controls
6428 var insert = Dom
6429 .c('div')
6430 .attr('id', ctx.tableId + '_wrapper')
6431 .classAdd(classes.container)
6432 .insertBefore(ctx.table);
6433 ctx.tableWrapper = insert.get(0);
6434 if (ctx.dom) {
6435 // Legacy
6436 legacyDom(ctx, ctx.dom, insert);
6437 }
6438 else {
6439 var top = convert(ctx, ctx.layout, 'top');
6440 var bottom = convert(ctx, ctx.layout, 'bottom');
6441 var render = renderer(ctx, 'layout');
6442 // Everything above - the renderer will actually insert the contents into the document
6443 top.forEach(function (item) {
6444 render(ctx, insert, item);
6445 });
6446 // The table - always the center of attention
6447 render(ctx, insert, {
6448 full: {
6449 contents: [featureTable(ctx)],
6450 items: [],
6451 table: true
6452 }
6453 });
6454 // Everything below
6455 bottom.forEach(function (item) {
6456 render(ctx, insert, item);
6457 });
6458 }
6459 // Processing floats on top, so it isn't an inserted feature
6460 processingHtml(ctx);
6461 }
6462 /**
6463 * Expand the layout items into an object for the rendering function
6464 */
6465 function layoutItems(row, align, items) {
6466 if (Array.isArray(items)) {
6467 for (var i = 0; i < items.length; i++) {
6468 layoutItems(row, align, items[i]);
6469 }
6470 return;
6471 }
6472 var rowCell = row[align]; // can't be undefined - will have been created by getRow
6473 // If it is an object, then there can be multiple features contained in it
6474 if (util.is.plainObject(items)) {
6475 // Is it an cell object already, with rowId, etc. A feature plugin cannot
6476 // be named "features" due to this check
6477 if (items.features) {
6478 if (items.rowId) {
6479 row.id = items.rowId;
6480 }
6481 if (items.rowClass) {
6482 row.className = items.rowClass;
6483 }
6484 rowCell.id = items.id;
6485 rowCell.className = items.className;
6486 layoutItems(row, align, items.features);
6487 }
6488 else {
6489 // An object of features and configuration options - e.g. `{paging: {startEnd: false}}`
6490 util.object.each(items, (key, val) => {
6491 rowCell.items.push({
6492 feature: key,
6493 opts: val
6494 });
6495 });
6496 }
6497 }
6498 else {
6499 // Otherwise, it is a function, node or Dom / jQuery instance and can just get added
6500 rowCell.items.push(items);
6501 }
6502 }
6503 /**
6504 * Find, or create a layout row and setup a target cell in it
6505 *
6506 * @param rows Rows array to search for the target row. Is mutated when a row is
6507 * added if not found.
6508 * @param rowNum Row index to get
6509 * @param align Where the cell position is
6510 * @returns The row
6511 */
6512 function getRow(rows, rowNum, align) {
6513 var row;
6514 // Find existing rows
6515 for (var i = 0; i < rows.length; i++) {
6516 row = rows[i];
6517 if (row.rowNum === rowNum) {
6518 // full is on its own, but start and end share a row
6519 if ((align === 'full' && row.full) ||
6520 ((align === 'start' || align === 'end') &&
6521 (row.start || row.end))) {
6522 if (!row[align]) {
6523 row[align] = {
6524 contents: [],
6525 items: []
6526 };
6527 }
6528 return row;
6529 }
6530 }
6531 }
6532 // If we get this far, then there was no match, create a new row
6533 row = {
6534 rowNum: rowNum
6535 };
6536 row[align] = {
6537 contents: [],
6538 items: []
6539 };
6540 rows.push(row);
6541 return row;
6542 }
6543 /**
6544 * Convert a `layout` object given by a user to the object structure needed
6545 * for the renderer. This is done twice, once for above and once for below
6546 * the table. Ordering must also be considered.
6547 *
6548 * @param settings DataTables settings object
6549 * @param layout Layout object to convert
6550 * @param side `top` or `bottom`
6551 * @returns Converted array structure - one item for each row.
6552 */
6553 function convert(settings, layout, side) {
6554 var rows = [];
6555 // Split out into an array
6556 util.object.each(layout, function (pos, items) {
6557 var parts = pos.match(/^([a-z]+)([0-9]*)([A-Za-z]*)$/);
6558 if (items === null || !parts) {
6559 return;
6560 }
6561 var rowNum = parts[2] ? parseInt(parts[2]) : 0;
6562 var align = parts[3] ? parts[3].toLowerCase() : 'full';
6563 // Filter out the side we aren't interested in
6564 if (parts[1] !== side) {
6565 return;
6566 }
6567 // Only really a type check
6568 if (align !== 'full' && align !== 'start' && align !== 'end') {
6569 return;
6570 }
6571 // Get or create the row we should attach to
6572 var row = getRow(rows, rowNum, align);
6573 layoutItems(row, align, items);
6574 });
6575 // Order by item identifier
6576 rows.sort(function (a, b) {
6577 var order1 = a.rowNum || 0;
6578 var order2 = b.rowNum || 0;
6579 // If both in the same row, then the row with `full` comes first
6580 if (order1 === order2) {
6581 var ret = a.full && !b.full ? -1 : 1;
6582 return side === 'bottom' ? ret * -1 : ret;
6583 }
6584 return order2 - order1;
6585 });
6586 // Invert for below the table
6587 if (side === 'bottom') {
6588 rows.reverse();
6589 }
6590 for (var row = 0; row < rows.length; row++) {
6591 delete rows[row].rowNum;
6592 resolve(settings, rows[row]);
6593 }
6594 return rows;
6595 }
6596 /**
6597 * Convert the contents of a row's layout object to nodes that can be inserted
6598 * into the document by a renderer. Execute functions, look up plug-ins, etc.
6599 *
6600 * @param settings DataTables settings object
6601 * @param row Layout object for this row
6602 */
6603 function resolve(settings, row) {
6604 var getFeature = function (feature, opts) {
6605 if (!ext.features[feature]) {
6606 log(settings, 0, 'Unknown feature: ' + feature);
6607 }
6608 return ext.features[feature].apply(this, [settings, opts]);
6609 };
6610 // Resolve items in the `contents` array from being an identifier, such as
6611 // the name of a feature, into the node to display.
6612 var resolve = function (item) {
6613 if (!row[item]) {
6614 return;
6615 }
6616 row[item].contents = row[item].items
6617 .filter(item => !!item)
6618 .map(item => {
6619 if (typeof item === 'string') {
6620 return getFeature(item, null);
6621 }
6622 else if (util.is.plainObject(item)) {
6623 // If it's an object, it just has feature and opts properties from
6624 // the transform in _layoutArray
6625 return getFeature(item.feature, item.opts);
6626 }
6627 else if (typeof item.node === 'function') {
6628 return item.node(settings);
6629 }
6630 else if (typeof item === 'function') {
6631 var inst = item(settings);
6632 return typeof inst.node === 'function' ? inst.node() : inst;
6633 }
6634 else if (item.nodeName) {
6635 // An HTML element
6636 return item;
6637 }
6638 else if (item instanceof Dom) {
6639 return item.get(0);
6640 }
6641 else if (item.length) {
6642 // Possibly jQuery
6643 return item[0];
6644 }
6645 });
6646 };
6647 resolve('start');
6648 resolve('end');
6649 resolve('full');
6650 }
6651 /**
6652 * Draw the table with the legacy DOM property
6653 *
6654 * @param settings DT settings instance
6655 * @param layout DOM string
6656 * @param insert Insert point
6657 */
6658 function legacyDom(settings, layout, insert) {
6659 let parts = layout.match(/(".*?")|('.*?')|./g);
6660 let featureNode, option, newNode, next, attr;
6661 if (!parts) {
6662 return;
6663 }
6664 for (let i = 0; i < parts.length; i++) {
6665 featureNode = null;
6666 option = parts[i];
6667 if (option == '<') {
6668 // New container div
6669 newNode = Dom.c('div');
6670 // Check to see if we should append an id and/or a class name to the container
6671 next = parts[i + 1];
6672 if (next[0] == "'" || next[0] == '"') {
6673 attr = next.replace(/['"]/g, '');
6674 let id = '', className;
6675 /* The attribute can be in the format of "#id.class", "#id" or "class" This logic
6676 * breaks the string into parts and applies them as needed
6677 */
6678 if (attr.indexOf('.') != -1) {
6679 let split = attr.split('.');
6680 id = split[0];
6681 className = split[1];
6682 }
6683 else if (attr[0] == '#') {
6684 id = attr;
6685 }
6686 else {
6687 className = attr;
6688 }
6689 newNode.attr('id', id.substring(1)).classAdd(className);
6690 i++; // Move along the position array
6691 }
6692 insert.append(newNode.get()); // TODO
6693 insert = newNode;
6694 }
6695 else if (option == '>') {
6696 // End container div
6697 insert = insert.parent();
6698 }
6699 else if (option == 't') {
6700 // Table
6701 featureNode = featureTable(settings);
6702 }
6703 else {
6704 ext.feature.forEach(function (feature) {
6705 if (option == feature.cFeature) {
6706 featureNode = feature.fnInit(settings);
6707 }
6708 });
6709 }
6710 // Add to the display
6711 if (featureNode) {
6712 // TODO when doing the full dom update, won't need this check
6713 insert.append(featureNode instanceof Dom ? featureNode.get() : featureNode);
6714 }
6715 }
6716 }
6717
6718 function sortInit(settings) {
6719 var target = settings.thead;
6720 var headerRows = target.querySelectorAll('tr');
6721 var titleRow = settings.titleRow;
6722 var notSelector = ':not([data-dt-order="disable"]):not([data-dt-order="icon-only"])';
6723 // Legacy support for `orderCellsTop`
6724 if (titleRow === true) {
6725 target = headerRows[0];
6726 }
6727 else if (titleRow === false) {
6728 target = headerRows[headerRows.length - 1];
6729 }
6730 else if (titleRow !== null) {
6731 target = headerRows[titleRow];
6732 }
6733 // else - all rows
6734 if (settings.orderHandler) {
6735 sortAttachListener(settings, target, target === settings.thead
6736 ? 'tr' +
6737 notSelector +
6738 ' th' +
6739 notSelector +
6740 ', tr' +
6741 notSelector +
6742 ' td' +
6743 notSelector
6744 : 'th' + notSelector + ', td' + notSelector);
6745 }
6746 // Need to resolve the user input array into our internal structure
6747 var order = [];
6748 sortResolve(settings, order, settings.order);
6749 settings.order = order;
6750 }
6751 /**
6752 * Attach event listeners to a node that will trigger ordering on a column
6753 *
6754 * @param settings DataTables context
6755 * @param node Node to attach to
6756 * @param selector Delegate selector
6757 * @param column Column index to target
6758 * @param callback Callback for when done
6759 */
6760 function sortAttachListener(settings, node, selector, column, callback) {
6761 bindAction(node, selector, function (e) {
6762 var run = false;
6763 var columns = column === undefined
6764 ? columnsFromHeader(e.target)
6765 : typeof column === 'function'
6766 ? column()
6767 : Array.isArray(column)
6768 ? column
6769 : [column];
6770 if (columns.length) {
6771 for (var i = 0, iLen = columns.length; i < iLen; i++) {
6772 var ret = sortAdd(settings, columns[i], i, e.shiftKey);
6773 if (ret !== false) {
6774 run = true;
6775 }
6776 // If the first entry is no sort, then subsequent
6777 // sort columns are ignored
6778 if (settings.order.length === 1 &&
6779 settings.order[0][1] === '') {
6780 break;
6781 }
6782 }
6783 if (run) {
6784 processingRun(settings, true, function () {
6785 sort(settings);
6786 sortDisplay(settings, settings.display);
6787 reDraw(settings, false, false);
6788 if (callback) {
6789 callback();
6790 }
6791 });
6792 }
6793 }
6794 });
6795 }
6796 /**
6797 * Sort the display array to match the master's order
6798 *
6799 * @param settings DataTables context
6800 * @param display The display array
6801 */
6802 function sortDisplay(settings, display) {
6803 if (display.length < 2) {
6804 return;
6805 }
6806 var master = settings.displayMaster;
6807 var masterMap = {};
6808 var map = {};
6809 var i;
6810 // Rather than needing an `indexOf` on master array, we can create a map
6811 for (i = 0; i < master.length; i++) {
6812 masterMap[master[i]] = i;
6813 }
6814 // And then cache what would be the indexOf from the display
6815 for (i = 0; i < display.length; i++) {
6816 map[display[i]] = masterMap[display[i]];
6817 }
6818 display.sort(function (a, b) {
6819 // Short version of this function is simply `master.indexOf(a) - master.indexOf(b);`
6820 return map[a] - map[b];
6821 });
6822 }
6823 /**
6824 * Convert the API variants that can be used for defining the order into our
6825 * internal OrderColumn array.
6826 *
6827 * @param settings DataTable context object
6828 * @param nestedSort Array to write the resolve values to
6829 * @param sortItem Source object / array from user (It is really an `Order`
6830 * but due to `aaSorting` being used for input and the internal structure
6831 * it is currently any).
6832 * @todo Split aaSorting into unresolved and resolved parameters (in state.ts as
6833 * well)
6834 */
6835 function sortResolve(settings, nestedSort, sortItem // TODO typing
6836 ) {
6837 var push = function (a) {
6838 if (plainObject(a)) {
6839 let orderIdx = a;
6840 let orderName = a;
6841 if (orderIdx.idx !== undefined) {
6842 // Index based ordering
6843 nestedSort.push([orderIdx.idx, orderIdx.dir]);
6844 }
6845 else if (orderName.name) {
6846 // Name based ordering
6847 var cols = pluck(settings.columns, 'name');
6848 var idx = cols.indexOf(orderName.name);
6849 if (idx !== -1) {
6850 nestedSort.push([idx, orderName.dir]);
6851 }
6852 }
6853 }
6854 else {
6855 // Plain column index and direction pair
6856 nestedSort.push(a);
6857 }
6858 };
6859 if (plainObject(sortItem)) {
6860 // Object
6861 push(sortItem);
6862 }
6863 else if (Array.isArray(sortItem) && typeof sortItem[0] === 'number') {
6864 // 1D array
6865 push(sortItem);
6866 }
6867 else if (Array.isArray(sortItem)) {
6868 // 2D array
6869 for (var z = 0; z < sortItem.length; z++) {
6870 push(sortItem[z]); // Object or array
6871 }
6872 }
6873 }
6874 function sortFlatten(settings) {
6875 var i, k, kLen, aSort = [], extSort = ext.type.order, aoColumns = settings.columns, dataSort, colIdx, type, srcCol, fixed = settings.orderFixed, fixedObj = plainObject(fixed), nestedSort = [];
6876 if (!settings.features.ordering) {
6877 return aSort;
6878 }
6879 // Build the sort array, with pre-fix and post-fix options if they have been
6880 // specified
6881 if (Array.isArray(fixed)) {
6882 sortResolve(settings, nestedSort, fixed);
6883 }
6884 if (fixedObj && fixed.pre) {
6885 sortResolve(settings, nestedSort, fixed.pre);
6886 }
6887 sortResolve(settings, nestedSort, settings.order);
6888 if (fixedObj && fixed.post) {
6889 sortResolve(settings, nestedSort, fixed.post);
6890 }
6891 for (i = 0; i < nestedSort.length; i++) {
6892 srcCol = nestedSort[i][0];
6893 if (aoColumns[srcCol]) {
6894 dataSort = aoColumns[srcCol].orderData;
6895 for (k = 0, kLen = dataSort.length; k < kLen; k++) {
6896 colIdx = dataSort[k];
6897 type = aoColumns[colIdx].type || 'string';
6898 if (nestedSort[i]._idx === undefined) {
6899 nestedSort[i]._idx = aoColumns[colIdx].orderSequence.indexOf(nestedSort[i][1]);
6900 }
6901 if (nestedSort[i][1]) {
6902 aSort.push({
6903 src: srcCol,
6904 col: colIdx,
6905 dir: nestedSort[i][1],
6906 index: nestedSort[i]._idx,
6907 type: type,
6908 formatter: extSort[type + '-pre'],
6909 sorter: extSort[type + '-' + nestedSort[i][1]]
6910 });
6911 }
6912 }
6913 }
6914 }
6915 return aSort;
6916 }
6917 /**
6918 * Change the order of the table
6919 *
6920 * @param ctx DataTables settings object
6921 * @param col Column to perform sort on
6922 * @param dir Direction to sort on
6923 */
6924 function sort(ctx, col, dir) {
6925 var i, iLen, aiOrig = [], extSort = ext.type.order, data = ctx.data, sortCol, displayMaster = ctx.displayMaster, aSort;
6926 // Make sure the columns all have types defined
6927 columnTypes(ctx);
6928 // Allow a specific column to be sorted, which will _not_ alter the display
6929 // master
6930 if (col !== undefined) {
6931 var srcCol = ctx.columns[col];
6932 aSort = [
6933 {
6934 src: col,
6935 col: col,
6936 dir: dir || '',
6937 index: 0,
6938 type: srcCol.type,
6939 formatter: extSort[srcCol.type + '-pre'],
6940 sorter: extSort[srcCol.type + '-' + dir]
6941 }
6942 ];
6943 displayMaster = displayMaster.slice();
6944 }
6945 else {
6946 aSort = sortFlatten(ctx);
6947 }
6948 for (i = 0, iLen = aSort.length; i < iLen; i++) {
6949 sortCol = aSort[i];
6950 // Load the data needed for the sort, for each cell
6951 sortData(ctx, sortCol.col);
6952 }
6953 /* No sorting required if server-side or no sorting array */
6954 if (dataSource(ctx) != 'ssp' && aSort.length !== 0) {
6955 // Reset the initial positions on each pass so we get a stable sort
6956 for (i = 0, iLen = displayMaster.length; i < iLen; i++) {
6957 aiOrig[i] = i;
6958 }
6959 // If the first sort is desc, then reverse the array to preserve original
6960 // order, just in reverse
6961 if (aSort.length && aSort[0].dir === 'desc' && ctx.orderDescReverse) {
6962 aiOrig.reverse();
6963 }
6964 /* Do the sort - here we want multi-column sorting based on a given data source (column)
6965 * and sorting function (from oSort) in a certain direction. It's reasonably complex to
6966 * follow on its own, but this is what we want (example two column sorting):
6967 * fnLocalSorting = function(a,b){
6968 * var test;
6969 * test = oSort['string-asc']('data11', 'data12');
6970 * if (test !== 0)
6971 * return test;
6972 * test = oSort['numeric-desc']('data21', 'data22');
6973 * if (test !== 0)
6974 * return test;
6975 * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
6976 * }
6977 * Basically we have a test for each sorting column, if the data in that column is equal,
6978 * test the next column. If all columns match, then we use a numeric sort on the row
6979 * positions in the original data array to provide a stable sort.
6980 */
6981 displayMaster.sort(function (a, b) {
6982 var _a, _b;
6983 var x, y, k, test, sortItem, len = aSort.length, dataA = (_a = data[a]) === null || _a === void 0 ? void 0 : _a.orderCache, dataB = (_b = data[b]) === null || _b === void 0 ? void 0 : _b.orderCache;
6984 for (k = 0; k < len; k++) {
6985 sortItem = aSort[k];
6986 // Data, which may have already been through a `-pre` function
6987 x = dataA[sortItem.col];
6988 y = dataB[sortItem.col];
6989 if (sortItem.sorter) {
6990 // If there is a custom sorter (`-asc` or `-desc`) for this
6991 // data type, use it
6992 test = sortItem.sorter(x, y);
6993 if (test !== 0) {
6994 return test;
6995 }
6996 }
6997 else {
6998 // Otherwise, use generic sorting
6999 test = x < y ? -1 : x > y ? 1 : 0;
7000 if (test !== 0) {
7001 return sortItem.dir === 'asc' ? test : -test;
7002 }
7003 }
7004 }
7005 x = aiOrig[a];
7006 y = aiOrig[b];
7007 return x < y ? -1 : x > y ? 1 : 0;
7008 });
7009 }
7010 else if (aSort.length === 0) {
7011 // Apply index order
7012 displayMaster.sort(function (x, y) {
7013 return x < y ? -1 : x > y ? 1 : 0;
7014 });
7015 }
7016 if (col === undefined) {
7017 // Tell the draw function that we have sorted the data
7018 ctx.wasOrdered = true;
7019 ctx.sortDetails = aSort;
7020 callbackFire(ctx, null, 'order', [ctx, aSort]);
7021 }
7022 return displayMaster;
7023 }
7024 /**
7025 * Function to run on user sort request
7026 *
7027 * @param settings dataTables settings object
7028 * @param colIdx column sorting index
7029 * @param addIndex Counter
7030 * @param shift Shift click add
7031 */
7032 function sortAdd(settings, colIdx, addIndex, shift) {
7033 var col = settings.columns[colIdx];
7034 var sorting = settings.order;
7035 var asSorting = col.orderSequence;
7036 var nextSortIdx;
7037 var next = function (a, overflow) {
7038 var idx = a._idx;
7039 if (idx === undefined) {
7040 idx = asSorting.indexOf(a[1]);
7041 }
7042 return idx + 1 < asSorting.length ? idx + 1 : overflow ? null : 0;
7043 };
7044 if (!col.orderable) {
7045 return false;
7046 }
7047 // Convert to 2D array if needed
7048 if (typeof sorting[0] === 'number') {
7049 sorting = settings.order = [sorting];
7050 }
7051 // If appending the sort then we are multi-column sorting
7052 if ((shift || addIndex) && settings.features.orderMulti) {
7053 // Are we already doing some kind of sort on this column?
7054 var sortIdx = pluck(sorting, '0').indexOf(colIdx);
7055 if (sortIdx !== -1) {
7056 // Yes, modify the sort
7057 nextSortIdx = next(sorting[sortIdx], true);
7058 if (nextSortIdx === null && sorting.length === 1) {
7059 nextSortIdx = 0; // can't remove sorting completely
7060 }
7061 if (nextSortIdx === null || asSorting[nextSortIdx] === '') {
7062 sorting.splice(sortIdx, 1);
7063 }
7064 else {
7065 sorting[sortIdx][1] = asSorting[nextSortIdx];
7066 sorting[sortIdx]._idx = nextSortIdx;
7067 }
7068 }
7069 else if (shift) {
7070 // No sort on this column yet, being added by shift click
7071 // add it as itself
7072 sorting.push([colIdx, asSorting[0], 0]);
7073 sorting[sorting.length - 1]._idx = 0;
7074 }
7075 else {
7076 // No sort on this column yet, being added from a colspan
7077 // so add with same direction as first column
7078 sorting.push([colIdx, sorting[0][1], 0]);
7079 sorting[sorting.length - 1]._idx = 0;
7080 }
7081 }
7082 else if (sorting.length && sorting[0][0] == colIdx) {
7083 // Single column - already sorting on this column, modify the sort
7084 nextSortIdx = next(sorting[0]);
7085 if (nextSortIdx) {
7086 sorting.length = 1;
7087 sorting[0][1] = asSorting[nextSortIdx];
7088 sorting[0]._idx = nextSortIdx;
7089 }
7090 else {
7091 sorting.length = 1;
7092 sorting[0][1] = asSorting[0];
7093 sorting[0]._idx = 0;
7094 }
7095 }
7096 else {
7097 // Single column - sort only on this column
7098 sorting.length = 0;
7099 sorting.push([colIdx, asSorting[0]]);
7100 sorting[0]._idx = 0;
7101 }
7102 }
7103 /**
7104 * Set the sorting classes on table's body, Note: it is safe to call this function
7105 * when bSort and bSortClasses are false
7106 *
7107 * @param settings DataTables settings object
7108 */
7109 function sortingClasses(settings) {
7110 var oldSort = settings.lastOrder;
7111 var sortClass = settings.classes.order.position;
7112 var sortFlat = sortFlatten(settings);
7113 var features = settings.features;
7114 var i, iLen, colIdx;
7115 if (features.ordering && features.orderClasses) {
7116 // Remove old sorting classes
7117 for (i = 0, iLen = oldSort.length; i < iLen; i++) {
7118 colIdx = oldSort[i].src;
7119 // Remove column sorting
7120 Dom.s(pluck(settings.data, 'cells', colIdx)).classRemove(sortClass + (i < 2 ? i + 1 : 3));
7121 }
7122 // Add new column sorting
7123 for (i = 0, iLen = sortFlat.length; i < iLen; i++) {
7124 colIdx = sortFlat[i].src;
7125 Dom.s(pluck(settings.data, 'cells', colIdx)).classAdd(sortClass + (i < 2 ? i + 1 : 3));
7126 }
7127 }
7128 settings.lastOrder = sortFlat;
7129 }
7130 /**
7131 * Get the data to sort a column, be it from cache, fresh (populating the
7132 * cache), or from a sort formatter
7133 *
7134 * @param settings DataTables settings object
7135 * @param colIdx Column index
7136 */
7137 function sortData(settings, colIdx) {
7138 // Custom sorting function - provided by the sort data type
7139 var column = settings.columns[colIdx];
7140 var customSort = ext.order[column.orderDataType];
7141 var customData;
7142 if (customSort) {
7143 customData = customSort.call(settings.instance, settings, colIdx, columnIndexToVisible(settings, colIdx));
7144 }
7145 // Use / populate cache
7146 var row, cellData;
7147 var formatter = ext.type.order[column.type + '-pre'];
7148 var data = settings.data;
7149 for (var rowIdx = 0; rowIdx < data.length; rowIdx++) {
7150 // Sparse array
7151 if (!data[rowIdx]) {
7152 continue;
7153 }
7154 row = data[rowIdx];
7155 if (row && !row.orderCache) {
7156 row.orderCache = [];
7157 }
7158 if (row && (!row.orderCache[colIdx] || customSort)) {
7159 cellData = customSort
7160 ? customData[rowIdx] // If there was a custom sort function, use data from there
7161 : getCellData(settings, rowIdx, colIdx, 'sort');
7162 row.orderCache[colIdx] = formatter
7163 ? formatter(cellData, settings)
7164 : cellData;
7165 }
7166 }
7167 }
7168
7169 /**
7170 * Alter the display settings to change the page
7171 *
7172 * @param settings DataTables settings object
7173 * @param action Paging action to take: "first", "previous", "next" or "last" or
7174 * page number to jump to (integer)
7175 * @param redraw Automatically draw the update or not
7176 * @returns true page has changed, false - no change
7177 */
7178 function pageChange(settings, action, redraw) {
7179 var start = settings.displayStart, len = settings.pageLength, records = recordsDisplay(settings);
7180 if (records === 0 || len === -1) {
7181 start = 0;
7182 }
7183 else if (typeof action === 'number') {
7184 start = action * len;
7185 if (start > records) {
7186 start = 0;
7187 }
7188 }
7189 else if (action == 'first') {
7190 start = 0;
7191 }
7192 else if (action == 'previous') {
7193 start = len >= 0 ? start - len : 0;
7194 if (start < 0) {
7195 start = 0;
7196 }
7197 }
7198 else if (action == 'next') {
7199 if (start + len < records) {
7200 start += len;
7201 }
7202 }
7203 else if (action == 'last') {
7204 start = Math.floor((records - 1) / len) * len;
7205 }
7206 else if (action === 'ellipsis') {
7207 return;
7208 }
7209 else {
7210 log(settings, 0, 'Unknown paging action: ' + action, 5);
7211 }
7212 var changed = settings.displayStart !== start;
7213 settings.displayStart = start;
7214 callbackFire(settings, null, changed ? 'page' : 'page-nc', [settings]);
7215 if (changed && redraw) {
7216 draw(settings);
7217 }
7218 return changed;
7219 }
7220
7221 /**
7222 * State information for a table
7223 *
7224 * @param settings DataTables settings object
7225 */
7226 function saveState(settings) {
7227 if (settings.loadingState) {
7228 return;
7229 }
7230 // Sort state saving uses [[idx, order]] structure.
7231 var sorting = [];
7232 sortResolve(settings, sorting, settings.order);
7233 /* Store the interesting variables */
7234 var columns = settings.columns;
7235 var state = {
7236 columns: settings.columns.map(function (col, i) {
7237 return {
7238 name: col.name,
7239 visible: col.visible,
7240 search: Object.assign({}, settings.searches[i])
7241 };
7242 }),
7243 length: settings.pageLength,
7244 order: sorting.map(function (sort) {
7245 // If a column name is available, use it
7246 return columns[sort[0]] && columns[sort[0]].name
7247 ? [columns[sort[0]].name, sort[1]]
7248 : sort.slice();
7249 }),
7250 search: Object.assign({}, settings.searches['*']),
7251 searchGroups: Object.keys(settings.searches)
7252 .filter(c => c.includes(',')) // Limit to only multi-column subsets
7253 .map(c => Object.assign({}, settings.searches[c])),
7254 start: settings.displayStart,
7255 time: +new Date()
7256 };
7257 settings.stateSaved = state;
7258 callbackFire(settings, 'stateSaveParams', 'stateSaveParams', [
7259 settings,
7260 state
7261 ]);
7262 if (settings.features.stateSave && !settings.destroying) {
7263 settings.stateSaveCallback.call(settings.instance, settings, state);
7264 }
7265 }
7266 /**
7267 * Attempt to load a saved table state
7268 *
7269 * @param settings dataTables settings object
7270 * @param callback Callback to execute when the state has been loaded
7271 */
7272 function loadState(settings, callback) {
7273 if (!settings.features.stateSave) {
7274 callback();
7275 return;
7276 }
7277 var loaded = function (state, ignoreTime = false) {
7278 implementState(settings, state, ignoreTime, callback);
7279 };
7280 var state = settings.stateLoadCallback.call(settings.instance, settings, loaded);
7281 if (state !== undefined) {
7282 implementState(settings, state, false, callback);
7283 }
7284 // otherwise, wait for the loaded callback to be executed
7285 return true;
7286 }
7287 function implementState(settings, s, ignoreTime, callback) {
7288 var i, iLen;
7289 var columns = settings.columns;
7290 var currentNames = pluck(settings.columns, 'name');
7291 settings.loadingState = true;
7292 // When StateRestore was introduced the state could now be implemented at
7293 // any time Not just initialisation. To do this an api instance is required
7294 // in some places
7295 var api = settings.initDone ? new Api(settings) : null;
7296 if (!ignoreTime) {
7297 if (!s || !s.time) {
7298 settings.loadingState = false;
7299 callback();
7300 return;
7301 }
7302 // Reject old data
7303 var duration = settings.stateDuration;
7304 if (duration > 0 && s.time < +new Date() - duration * 1000) {
7305 settings.loadingState = false;
7306 callback();
7307 return;
7308 }
7309 }
7310 // Allow custom and plug-in manipulation functions to alter the saved data
7311 // set and cancelling of loading by returning false
7312 var abStateLoad = callbackFire(settings, 'stateLoadParams', 'stateLoadParams', [settings, s]);
7313 if (abStateLoad.indexOf(false) !== -1) {
7314 settings.loadingState = false;
7315 callback();
7316 return;
7317 }
7318 // Store the saved state so it might be accessed at any time
7319 settings.stateLoaded = assignDeep({}, s);
7320 // This is needed for ColReorder, which has to happen first to allow all
7321 // the stored indexes to be usable. It is not publicly documented.
7322 callbackFire(settings, null, 'stateLoadInit', [settings, s], true);
7323 // Page Length
7324 if (s.length !== undefined) {
7325 // If already initialised just set the value directly so that the select
7326 // element is also updated
7327 if (api) {
7328 api.page.len(s.length);
7329 }
7330 else {
7331 settings.pageLength = s.length;
7332 }
7333 }
7334 // Restore key features
7335 if (s.start !== undefined) {
7336 if (api === null) {
7337 settings.displayStart = s.start;
7338 settings.displayStartInit = s.start;
7339 }
7340 else {
7341 pageChange(settings, s.start / settings.pageLength);
7342 }
7343 }
7344 // Order
7345 if (s.order !== undefined) {
7346 settings.order = [];
7347 for (let i = 0; i < s.order.length; i++) {
7348 let col = s.order[i];
7349 let set = [col[0], col[1]];
7350 // A column name was stored and should be used for restore
7351 if (typeof col[0] === 'string') {
7352 // Find the name from the current list of column names
7353 let idx = currentNames.indexOf(col[0]);
7354 if (idx < 0) {
7355 // If the column was not found ignore it and continue
7356 continue;
7357 }
7358 set[0] = idx;
7359 }
7360 else if (set[0] >= columns.length) {
7361 // If the column index is out of bounds ignore it and continue
7362 continue;
7363 }
7364 settings.order.push(set);
7365 }
7366 }
7367 // Search
7368 if (s.search !== undefined) {
7369 Object.assign(settings.searches['*'], s.search);
7370 }
7371 if (s.searchGroups) {
7372 s.searchGroups.forEach(group => {
7373 if (group.columns) {
7374 let index = group.columns.join(',');
7375 settings.searches[index] = create$2(group);
7376 }
7377 });
7378 }
7379 // Columns
7380 if (s.columns) {
7381 var set = s.columns;
7382 var incoming = pluck(s.columns, 'name');
7383 // Check if it is a 2.2 style state object with a `name` property for
7384 // the columns, and if the name was defined. If so, then create a new
7385 // array that will map the state object given, to the current columns
7386 // (don't bother if they are already matching tho).
7387 if (incoming.join('').length &&
7388 incoming.join('') !== currentNames.join('')) {
7389 set = [];
7390 // For each column, try to find the name in the incoming array
7391 for (i = 0; i < currentNames.length; i++) {
7392 if (currentNames[i] != '') {
7393 var idx = incoming.indexOf(currentNames[i]);
7394 if (idx >= 0) {
7395 set.push(s.columns[idx]);
7396 }
7397 else {
7398 // No matching column name in the state's columns, so
7399 // this might be a new column and thus can't have a
7400 // state already.
7401 set.push({});
7402 }
7403 }
7404 else {
7405 // If no name, but other columns did have a name, then there
7406 // is no knowing where this one came from originally so it
7407 // can't be restored.
7408 set.push({});
7409 }
7410 }
7411 }
7412 // If the number of columns to restore is different from current, then
7413 // all bets are off.
7414 if (set.length === columns.length) {
7415 for (i = 0, iLen = set.length; i < iLen; i++) {
7416 var col = set[i];
7417 // Visibility
7418 if (col.visible !== undefined) {
7419 // If the api is defined, the table has been initialised so
7420 // we need to use it rather than internal settings
7421 if (api) {
7422 // Don't redraw the columns on every iteration of this
7423 // loop, we will do this at the end instead
7424 api.column(i).visible(col.visible, false);
7425 }
7426 else {
7427 columns[i].visible = col.visible;
7428 }
7429 }
7430 // Search
7431 if (col.search !== undefined) {
7432 Object.assign(settings.searches[i], col.search);
7433 // If out of order due to a change in order from named
7434 // columns we need to make sure the index is correct
7435 settings.searches[i].columns = [i];
7436 }
7437 }
7438 // If the api is defined then we need to adjust the columns once the
7439 // visibility has been changed
7440 if (api) {
7441 api.one('draw', function () {
7442 api.columns.adjust();
7443 });
7444 }
7445 }
7446 }
7447 settings.loadingState = false;
7448 callbackFire(settings, 'stateLoaded', 'stateLoaded', [settings, s]);
7449 callback();
7450 }
7451
7452 /**
7453 * Draw the table for the first time, adding all required features
7454 *
7455 * @param settings DataTables settings object
7456 */
7457 function initialise(settings) {
7458 var i;
7459 var init = settings.init;
7460 var deferLoading = settings.deferLoading;
7461 var dataSrc = dataSource(settings);
7462 // Ensure that the table data is fully initialised
7463 if (!settings.initialised) {
7464 setTimeout(function () {
7465 initialise(settings);
7466 }, 200);
7467 return;
7468 }
7469 // Build the header / footer for the table
7470 buildHead(settings, 'header');
7471 buildHead(settings, 'footer');
7472 // Load the table's state (if needed) and then render around it and draw
7473 loadState(settings, function () {
7474 // Then draw the header / footer
7475 drawHead(settings, settings.header);
7476 drawHead(settings, settings.footer);
7477 // Cache the paging start point, as the first redraw will reset it
7478 var iAjaxStart = settings.displayStartInit;
7479 // Local data load
7480 // Check if there is data passing into the constructor
7481 if (init && init.data) {
7482 for (i = 0; i < init.data.length; i++) {
7483 addData(settings, init.data[i]);
7484 }
7485 }
7486 else if (deferLoading || dataSrc == 'dom') {
7487 // Grab the data from the page
7488 addTr(settings, Dom.s(settings.tbody).children('tr'));
7489 }
7490 // Filter not yet applied - copy the display master
7491 settings.display = settings.displayMaster.slice();
7492 // Enable features
7493 createLayout(settings);
7494 sortInit(settings);
7495 colGroup(settings);
7496 /* Okay to show that something is going on now */
7497 processingDisplay(settings, true);
7498 callbackFire(settings, null, 'preInit', [settings], true);
7499 // If there is default sorting required - let's do it. The sort function
7500 // will do the drawing for us. Otherwise we draw the table regardless of
7501 // the Ajax source - this allows the table to look initialised for Ajax
7502 // sourcing data (show 'loading' message possibly)
7503 reDraw(settings);
7504 // Server-side processing init complete is done by _fnAjaxUpdateDraw
7505 if (dataSrc != 'ssp' || deferLoading) {
7506 // if there is an ajax source load the data
7507 if (dataSrc == 'ajax') {
7508 buildAjax(settings, {}, function (json) {
7509 var aData = ajaxDataSrc(settings, json, false);
7510 // Got the data - add it to the table
7511 for (i = 0; i < aData.length; i++) {
7512 addData(settings, aData[i]);
7513 }
7514 // Reset the init display for cookie saving. We've already
7515 // done a filter, and therefore cleared it before. So we
7516 // need to make it appear 'fresh'
7517 settings.displayStartInit = iAjaxStart;
7518 reDraw(settings);
7519 processingDisplay(settings, false);
7520 initComplete(settings);
7521 });
7522 }
7523 else {
7524 initComplete(settings);
7525 processingDisplay(settings, false);
7526 }
7527 }
7528 });
7529 }
7530 /**
7531 * Draw the table for the first time, adding all required features
7532 *
7533 * @param settings DataTables settings object
7534 */
7535 function initComplete(settings) {
7536 if (settings.initDone) {
7537 return;
7538 }
7539 var args = [settings, settings.json];
7540 settings.initDone = true;
7541 // If the footer element is empty after initialisation, then remove it
7542 let tfoot = Dom.s(settings.tfoot);
7543 if (tfoot.children().count() === 0) {
7544 tfoot.remove();
7545 }
7546 // Table is fully set up and we have data, so calculate the
7547 // column widths
7548 adjustColumnSizing(settings);
7549 callbackFire(settings, null, 'plugin-init', args, true);
7550 callbackFire(settings, 'init', 'init', args, true);
7551 }
7552
7553 /**
7554 * Create an Ajax call based on the table's settings, taking into account that
7555 * parameters can have multiple forms, and backwards compatibility.
7556 *
7557 * @param settings DataTables settings object
7558 * @param data Data to send to the server, required by DataTables - may be
7559 * augmented by developer callbacks
7560 * @param fn Callback function to run when data is obtained
7561 */
7562 function buildAjax(settings, data, fn) {
7563 var ajaxData;
7564 var ajaxConfig = settings.ajax;
7565 var instance = settings.instance;
7566 var callback = function (json) {
7567 var status = settings.jqXHR ? settings.jqXHR.status : null;
7568 if (json === null || (typeof status === 'number' && status == 204)) {
7569 json = {};
7570 ajaxDataSrc(settings, json, []);
7571 }
7572 var error = json.error || json.sError;
7573 if (error) {
7574 log(settings, 0, error);
7575 }
7576 // Microsoft often wrap JSON as a string in another JSON object Let's
7577 // handle that automatically
7578 if (json.d && typeof json.d === 'string') {
7579 try {
7580 json = JSON.parse(json.d);
7581 }
7582 catch (e) {
7583 // noop
7584 }
7585 }
7586 settings.json = json;
7587 callbackFire(settings, null, 'xhr', [settings, json, settings.jqXHR], true);
7588 fn(json);
7589 };
7590 if (util.is.plainObject(ajaxConfig) && ajaxConfig.data) {
7591 ajaxData = ajaxConfig.data;
7592 var newData = typeof ajaxData === 'function'
7593 ? ajaxData(data, settings) // fn can manipulate data or return
7594 : ajaxData; // an object or array to merge
7595 // If the function returned something, use that alone
7596 data =
7597 typeof ajaxData === 'function' && newData
7598 ? newData
7599 : util.object.assignDeep(data, newData);
7600 // Remove the data property as we've resolved it already and don't want
7601 // jQuery to do it again (it is restored at the end of the function)
7602 delete ajaxConfig.data;
7603 }
7604 var baseAjax = {
7605 url: typeof ajaxConfig === 'string' ? ajaxConfig : '',
7606 data: data,
7607 success: callback,
7608 dataType: 'json',
7609 cache: false,
7610 type: settings.serverMethod,
7611 error: function (xhr, error) {
7612 var ret = callbackFire(settings, null, 'xhr', [settings, null, settings.jqXHR], true);
7613 if (ret.indexOf(false) === -1) {
7614 if (error == 'parsererror') {
7615 log(settings, 0, 'Invalid JSON response', 1);
7616 }
7617 else if (xhr.readyState === 4) {
7618 log(settings, 0, 'Ajax error', 7);
7619 }
7620 }
7621 processingDisplay(settings, false);
7622 }
7623 };
7624 // If `ajax` option is an object, extend and override our default base
7625 if (util.is.plainObject(ajaxConfig)) {
7626 util.object.assign(baseAjax, ajaxConfig);
7627 }
7628 // Store the data submitted for the API
7629 settings.ajaxData = data;
7630 // Allow plug-ins and external processes to modify the data
7631 callbackFire(settings, null, 'preXhr', [settings, data, baseAjax], true);
7632 if (typeof ajaxConfig === 'function') {
7633 // Is a function - let the caller define what needs to be done
7634 settings.jqXHR = ajaxConfig.call(instance, data, callback, settings);
7635 }
7636 else if (ajaxConfig &&
7637 typeof ajaxConfig !== 'string' &&
7638 ajaxConfig.url === '') {
7639 // No url, so don't load any data. Just apply an empty data array
7640 // to the object for the callback.
7641 var empty = {};
7642 ajaxDataSrc(settings, empty, []);
7643 callback(empty);
7644 }
7645 else {
7646 // Object to extend the base settings
7647 settings.jqXHR = util.ajax(baseAjax);
7648 }
7649 // Restore for next time around
7650 if (ajaxData) {
7651 ajaxConfig.data = ajaxData;
7652 }
7653 }
7654 /**
7655 * Update the table using an Ajax call
7656 *
7657 * @param settings DataTables settings object
7658 * @returns Block the table drawing or not
7659 */
7660 function ajaxUpdate(settings) {
7661 settings.drawCount++;
7662 processingDisplay(settings, true);
7663 buildAjax(settings, ajaxParameters(settings), function (json) {
7664 ajaxUpdateDraw(settings, json);
7665 });
7666 }
7667 function functionOrValue(val) {
7668 return typeof val === 'function' ? 'function' : val.toString();
7669 }
7670 /**
7671 * Build up the parameters in an object needed for a server-side processing
7672 * request.
7673 *
7674 * @param settings DataTables settings object
7675 * @returns Block the table drawing or not
7676 */
7677 function ajaxParameters(settings) {
7678 var columns = settings.columns, features = settings.features, searches = settings.searches, searchesFixed = settings.searchesFixed, colData = function (idx, prop) {
7679 return typeof columns[idx][prop] === 'function'
7680 ? 'function'
7681 : columns[idx][prop];
7682 };
7683 return {
7684 draw: settings.drawCount,
7685 columns: columns.map(function (column, i) {
7686 return {
7687 data: colData(i, 'data'),
7688 name: column.name,
7689 searchable: column.searchable,
7690 orderable: column.orderable,
7691 search: {
7692 value: searches[i]
7693 ? functionOrValue(searches[i].search)
7694 : '',
7695 regex: searches[i] ? searches[i].regex : false,
7696 fixed: searchesFixed[i]
7697 ? Object.keys(searchesFixed[i]).map(name => ({
7698 name: name,
7699 term: functionOrValue(searchesFixed[i][name].search)
7700 }))
7701 : []
7702 }
7703 };
7704 }),
7705 order: sortFlatten(settings).map(function (val) {
7706 return {
7707 column: val.col,
7708 dir: val.dir,
7709 name: colData(val.col, 'name')
7710 };
7711 }),
7712 start: settings.displayStart,
7713 length: features.paging ? settings.pageLength : -1,
7714 search: {
7715 value: functionOrValue(searches['*'].search),
7716 regex: searches['*'].regex,
7717 fixed: Object.keys(settings.searchesFixed['*']).map(name => ({
7718 name: name,
7719 term: functionOrValue(settings.searchesFixed['*'][name].search)
7720 })),
7721 groups: Object.keys(settings.searches)
7722 .filter(c => c.includes(',')) // Limit to only multi-column subsets
7723 .map(c => ({
7724 columns: settings.searches[c].columns || [],
7725 term: functionOrValue(settings.searches[c].search)
7726 })),
7727 groupsFixed: Object.keys(settings.searchesFixed)
7728 .filter(c => c.includes(',')) // Limit to only multi-column subsets
7729 .map(c => {
7730 let searches = settings.searchesFixed[c];
7731 return Object.keys(searches).map(n => ({
7732 columns: searches[n].columns || [],
7733 name: n,
7734 term: functionOrValue(searches[n].search)
7735 }));
7736 })
7737 .flat()
7738 }
7739 };
7740 }
7741 /**
7742 * Data the data from the server (nuking the old) and redraw the table
7743 *
7744 * @param settings DataTables settings object
7745 * @param json json data return from the server.
7746 */
7747 function ajaxUpdateDraw(settings, json) {
7748 var data = ajaxDataSrc(settings, json, false);
7749 var drawUnique = ajaxDataSrcParam(settings, 'draw', json);
7750 var recordsTotal = ajaxDataSrcParam(settings, 'recordsTotal', json);
7751 var recordsFiltered = ajaxDataSrcParam(settings, 'recordsFiltered', json);
7752 var existingTypes = settings.columns.map(c => c.type).join(',');
7753 if (drawUnique !== undefined) {
7754 // Protect against out of sequence returns
7755 if (drawUnique * 1 < settings.drawCount) {
7756 return;
7757 }
7758 settings.drawCount = drawUnique * 1;
7759 }
7760 // No data in returned object, so rather than an array, we show an empty
7761 // table
7762 if (!data) {
7763 data = [];
7764 }
7765 clearTable(settings);
7766 settings.recordsTotal = parseInt(recordsTotal, 10);
7767 settings.recordsDisplay = parseInt(recordsFiltered, 10);
7768 for (var i = 0, iLen = data.length; i < iLen; i++) {
7769 addData(settings, data[i]);
7770 }
7771 settings.display = settings.displayMaster.slice();
7772 columnTypes(settings, existingTypes);
7773 draw(settings, true);
7774 initComplete(settings);
7775 processingDisplay(settings, false);
7776 }
7777 /**
7778 * Get the data from the JSON data source to use for drawing a table.
7779 *
7780 * @param settings DataTables settings object
7781 * @param json Data source object / array from the server
7782 * @param write Array or object to write the data to
7783 * @return Array of data to use
7784 */
7785 function ajaxDataSrc(settings, json, write) {
7786 var dataProp = 'data';
7787 if (util.is.plainObject(settings.ajax) &&
7788 settings.ajax.dataSrc !== undefined) {
7789 // Could in inside a `dataSrc` object, or not!
7790 var dataSrc = settings.ajax.dataSrc;
7791 // string, function and object are valid types
7792 if (typeof dataSrc === 'string' || typeof dataSrc === 'function') {
7793 dataProp = dataSrc;
7794 }
7795 else if (dataSrc.data !== undefined) {
7796 dataProp = dataSrc.data;
7797 }
7798 }
7799 if (!write) {
7800 if (dataProp === 'data') {
7801 // If the default, then we still want to support the old style, and
7802 // safely ignore it if possible
7803 return json.aaData || json[dataProp];
7804 }
7805 return dataProp !== '' ? util.get(dataProp)(json) : json;
7806 }
7807 // set
7808 util.set(dataProp)(json, write);
7809 }
7810 /**
7811 * Very similar to ajaxDataSrc, but for the other SSP properties
7812 *
7813 * @param settings DataTables settings object
7814 * @param param Target parameter
7815 * @param json JSON data
7816 * @returns Resolved value
7817 */
7818 function ajaxDataSrcParam(settings, param, json) {
7819 var dataSrc = util.is.plainObject(settings.ajax)
7820 ? settings.ajax.dataSrc // TODO
7821 : null;
7822 if (dataSrc && dataSrc[param]) {
7823 // Get from custom location
7824 return util.data.get(dataSrc[param])(json);
7825 }
7826 // else - Default behaviour
7827 var old = '';
7828 // Legacy support
7829 if (param === 'draw') {
7830 old = 'sEcho';
7831 }
7832 else if (param === 'recordsTotal') {
7833 old = 'iTotalRecords';
7834 }
7835 else if (param === 'recordsFiltered') {
7836 old = 'iTotalDisplayRecords';
7837 }
7838 return json[old] !== undefined ? json[old] : json[param];
7839 }
7840
7841 const __filter_div = Dom.c('div').get(0);
7842 const __filter_div_textContent = __filter_div.textContent !== undefined;
7843 /**
7844 * Filter the table using both the global filter and column based filtering
7845 *
7846 * @param settings DataTables settings object
7847 */
7848 function filterComplete(settings) {
7849 settings.columns;
7850 // In server-side processing all filtering is done by the server, so no
7851 // point hanging around here
7852 if (dataSource(settings) != 'ssp') {
7853 // Check if any of the rows were invalidated
7854 filterData(settings);
7855 // Start from the full data set
7856 settings.display = settings.displayMaster.slice();
7857 // Column set filters first
7858 util.object.each(settings.searches, (key, s) => {
7859 filter(settings.display, settings, s.search, s);
7860 });
7861 // Fixed (named) filters next
7862 util.object.each(settings.searchesFixed, function (columns) {
7863 util.object.each(settings.searchesFixed[columns], function (name, s) {
7864 filter(settings.display, settings, s.search, s);
7865 });
7866 });
7867 // And finally legacy global filtering
7868 filterCustom(settings);
7869 }
7870 // Tell the draw function we have been filtering
7871 settings.wasFiltered = true;
7872 callbackFire(settings, null, 'search', [settings]);
7873 }
7874 /**
7875 * Apply custom filtering functions
7876 *
7877 * This is legacy now that we have named functions, but it is widely used
7878 * from 1.x, so it is not yet deprecated.
7879 *
7880 * @param settings DataTables settings object
7881 */
7882 function filterCustom(settings) {
7883 let filters = ext.search;
7884 let displayRows = settings.display;
7885 let row, rowIdx;
7886 for (let i = 0, iLen = filters.length; i < iLen; i++) {
7887 let rows = [];
7888 // Loop over each row and see if it should be included
7889 for (let j = 0, jen = displayRows.length; j < jen; j++) {
7890 rowIdx = displayRows[j];
7891 row = settings.data[rowIdx];
7892 if (row &&
7893 filters[i](settings, row.searchCellCache, rowIdx, row.data, j)) {
7894 rows.push(rowIdx);
7895 }
7896 }
7897 // So the array reference doesn't break set the results into the
7898 // existing array
7899 displayRows.length = 0;
7900 arrayApply(displayRows, rows);
7901 }
7902 }
7903 /**
7904 * Filter the data table based on user input and draw the table
7905 *
7906 * @param searchRows
7907 * @param settings
7908 * @param input
7909 * @param options
7910 * @returns
7911 */
7912 function filter(searchRows, settings, input, options) {
7913 if (input === '') {
7914 return;
7915 }
7916 let i = 0;
7917 let matched = [];
7918 // Search term can be a function, regex or string - if a string we apply our
7919 // smart filtering regex (assuming the options require that)
7920 let searchFunc = typeof input === 'function' ? input : null;
7921 let rpSearch = input instanceof RegExp
7922 ? input
7923 : searchFunc
7924 ? null
7925 : filterCreateSearch(input, options);
7926 let columns = options.columns
7927 ? options.columns
7928 : util.array.range(settings.columns.length);
7929 // Then for each row, does the test pass. If not, lop the row from the array
7930 for (i = 0; i < searchRows.length; i++) {
7931 let row = settings.data[searchRows[i]];
7932 if (row) {
7933 // Get the data array based on the columns to include in the search
7934 let data = util.array.selectiveJoin(row.searchCellCache, columns);
7935 // Run the search action
7936 if ((searchFunc &&
7937 searchFunc(data, row.data, searchRows[i], columns.length === 1 ? columns[0] : columns // compat
7938 )) ||
7939 (rpSearch && typeof data === 'string' && rpSearch.test(data))) {
7940 matched.push(searchRows[i]);
7941 }
7942 }
7943 }
7944 // Mutate the searchRows array
7945 searchRows.length = matched.length;
7946 for (i = 0; i < matched.length; i++) {
7947 searchRows[i] = matched[i];
7948 }
7949 }
7950 /**
7951 * Build a regular expression object suitable for searching a table
7952 */
7953 function filterCreateSearch(searchIn, inOpts) {
7954 let not = [];
7955 let options = Object.assign({}, {
7956 boundary: false,
7957 caseInsensitive: true,
7958 exact: false,
7959 regex: false,
7960 smart: true
7961 }, inOpts);
7962 let search = typeof searchIn !== 'string' ? searchIn.toString() : searchIn;
7963 // Remove diacritics if normalize is set up to do so
7964 search = util.diacritics(search);
7965 if (options.exact) {
7966 return new RegExp('^' + util.escapeRegex(search) + '$', options.caseInsensitive ? 'i' : '');
7967 }
7968 search = options.regex ? search : util.escapeRegex(search);
7969 if (options.smart) {
7970 /* For smart filtering we want to allow the search to work regardless of
7971 * word order. We also want double quoted text to be preserved, so word
7972 * order is important - a la google. And a negative look around for
7973 * finding rows which don't contain a given string.
7974 *
7975 * So this is the sort of thing we want to generate:
7976 *
7977 * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$
7978 */
7979 let parts = search.match(/!?["\u201C][^"\u201D]+["\u201D]|[^ ]+/g) || [
7980 ''
7981 ];
7982 let a = parts.map(function (word) {
7983 let negative = false;
7984 let m;
7985 // Determine if it is a "does not include"
7986 if (word.charAt(0) === '!') {
7987 negative = true;
7988 word = word.substring(1);
7989 }
7990 // Strip the quotes from around matched phrases
7991 if (word.charAt(0) === '"') {
7992 m = word.match(/^"(.*)"$/);
7993 word = m ? m[1] : word;
7994 }
7995 else if (word.charAt(0) === '\u201C') {
7996 // Smart quote match (iPhone users)
7997 m = word.match(/^\u201C(.*)\u201D$/);
7998 word = m ? m[1] : word;
7999 }
8000 // For our "not" case, we need to modify the string that is
8001 // allowed to match at the end of the expression.
8002 if (negative) {
8003 if (word.length > 1) {
8004 not.push('(?!' + word + ')');
8005 }
8006 word = '';
8007 }
8008 return word.replace(/"/g, '');
8009 });
8010 let match = not.length ? not.join('') : '';
8011 let boundary = options.boundary ? '\\b' : '';
8012 search =
8013 '^(?=.*?' +
8014 boundary +
8015 a.join(')(?=.*?' + boundary) +
8016 ')(' +
8017 match +
8018 '.)*$';
8019 }
8020 return new RegExp(search, options.caseInsensitive ? 'i' : '');
8021 }
8022 // Update the filtering data for each row if needed (by invalidation or first
8023 // run)
8024 function filterData(settings) {
8025 let columns = settings.columns;
8026 let data = settings.data;
8027 let column;
8028 let j, jen, cellData, row;
8029 let wasInvalidated = false;
8030 for (let rowIdx = 0; rowIdx < data.length; rowIdx++) {
8031 if (!data[rowIdx]) {
8032 continue;
8033 }
8034 row = data[rowIdx];
8035 if (row && !row.searchCellCache) {
8036 const rowFilterData = [];
8037 for (j = 0, jen = columns.length; j < jen; j++) {
8038 column = columns[j];
8039 if (column.searchable) {
8040 cellData = getCellData(settings, rowIdx, j, 'filter');
8041 // Search in DataTables is string based
8042 if (cellData === null) {
8043 cellData = '';
8044 }
8045 if (typeof cellData !== 'string' && cellData.toString) {
8046 cellData = cellData.toString();
8047 }
8048 }
8049 else {
8050 cellData = '';
8051 }
8052 // If it looks like there is an HTML entity in the string,
8053 // attempt to decode it so sorting works as expected. Note that
8054 // we could use a single line of jQuery to do this, but the DOM
8055 // method used here is much faster
8056 // https://jsperf.com/html-decode
8057 if (cellData.indexOf && cellData.indexOf('&') !== -1) {
8058 __filter_div.innerHTML = cellData;
8059 cellData = __filter_div_textContent
8060 ? __filter_div.textContent
8061 : __filter_div.innerText;
8062 }
8063 if (cellData.replace) {
8064 cellData = cellData.replace(/[\r\n\u2028]/g, '');
8065 }
8066 rowFilterData.push(cellData);
8067 }
8068 row.searchCellCache = rowFilterData;
8069 row.searchRowCache = rowFilterData.join(' ');
8070 wasInvalidated = true;
8071 }
8072 }
8073 return wasInvalidated;
8074 }
8075
8076 /**
8077 * Render and cache a row's display data for the columns, if required
8078 *
8079 * @param settings DataTables settings object
8080 * @param rowIdx Row index
8081 * @returns Array with display information
8082 */
8083 function getRowDisplay(settings, rowIdx) {
8084 var rowModal = settings.data[rowIdx];
8085 var columns = settings.columns;
8086 if (!rowModal) {
8087 return [];
8088 }
8089 if (!rowModal.displayData) {
8090 // Need to render and cache
8091 rowModal.displayData = [];
8092 for (var colIdx = 0, len = columns.length; colIdx < len; colIdx++) {
8093 rowModal.displayData.push(getCellData(settings, rowIdx, colIdx, 'display'));
8094 }
8095 }
8096 return rowModal.displayData;
8097 }
8098 /**
8099 * Create a new TR element (and it's TD children) for a row
8100 *
8101 * @param settings DataTables settings object
8102 * @param rowIdx Row to consider
8103 * @param trIn TR element to add to the table - optional. If not given,
8104 * DataTables will create a row automatically
8105 * @param tds Array of TD|TH elements for the row - must be given if trIn is.
8106 */
8107 function createTr(settings, rowIdx, trIn, tds) {
8108 var row = settings.data[rowIdx], cells = [], tr, td, column, i, iLen, create, trClass = settings.classes.tbody.row;
8109 if (row && row.tr === null) {
8110 let rowData = row.data;
8111 tr = trIn || document.createElement('tr');
8112 row.tr = tr;
8113 row.cells = cells;
8114 Dom.s(tr).classAdd(trClass);
8115 // Use a private property on the node to allow reserve mapping from the node
8116 // to the aoData array for fast look up
8117 tr._DT_RowIndex = rowIdx;
8118 // Special parameters can be given by the data source to be used on the
8119 // row
8120 rowAttributes(settings, row);
8121 /* Process each column */
8122 for (i = 0, iLen = settings.columns.length; i < iLen; i++) {
8123 column = settings.columns[i];
8124 create = trIn && tds && tds[i] ? false : true;
8125 td = create
8126 ? document.createElement(column.cellType)
8127 : tds[i];
8128 if (!td) {
8129 log(settings, 0, 'Incorrect column count', 18);
8130 }
8131 td._DT_CellIndex = {
8132 row: rowIdx,
8133 column: i
8134 };
8135 cells.push(td);
8136 var display = getRowDisplay(settings, rowIdx);
8137 // Need to create the HTML if new, or if a rendering function is
8138 // defined
8139 if (create ||
8140 ((column.render || column.data !== i) &&
8141 (!util.is.plainObject(column.data) ||
8142 (column.data &&
8143 column.data._ !== i + '.display')))) {
8144 writeCell(td, display[i]);
8145 }
8146 // column class
8147 Dom.s(td).classAdd(column.className);
8148 // Visibility - add or remove as required
8149 if (column.visible && create) {
8150 tr.appendChild(td);
8151 }
8152 else if (!column.visible && !create) {
8153 td.parentNode.removeChild(td);
8154 }
8155 if (column.createdCell) {
8156 column.createdCell.call(settings.instance, td, getCellData(settings, rowIdx, i), rowData, rowIdx, i);
8157 }
8158 }
8159 callbackFire(settings, 'rowCreated', 'row-created', [
8160 tr,
8161 rowData,
8162 rowIdx,
8163 cells
8164 ]);
8165 }
8166 else if (row) {
8167 Dom.s(row.tr).classAdd(trClass);
8168 }
8169 }
8170 /**
8171 * Add attributes to a row based on the special `DT_*` parameters in a data
8172 * source object.
8173 *
8174 * @param settings DataTables settings object
8175 * @param row Row object for the row to be modified
8176 */
8177 function rowAttributes(settings, row) {
8178 var tr = row.tr;
8179 var data = row.data;
8180 if (tr) {
8181 var id = settings.rowIdFn(data);
8182 if (id) {
8183 tr.id = id;
8184 }
8185 if (data.DT_RowClass) {
8186 // Remove any classes added by DT_RowClass before
8187 var a = data.DT_RowClass.split(' ');
8188 row.addedClasses = row.addedClasses
8189 ? util.unique(row.addedClasses.concat(a))
8190 : a;
8191 Dom.s(tr)
8192 .classRemove(row.addedClasses.join(' '))
8193 .classAdd(data.DT_RowClass);
8194 }
8195 if (data.DT_RowAttr) {
8196 Dom.s(tr).attr(data.DT_RowAttr);
8197 }
8198 if (data.DT_RowData) {
8199 Dom.s(tr).data(data.DT_RowData);
8200 }
8201 }
8202 }
8203 /**
8204 * Create the HTML header for the table
8205 *
8206 * @param settings DataTable instance
8207 * @param side If the header or footer should be used
8208 * @returns
8209 */
8210 function buildHead(settings, side) {
8211 let classes = settings.classes;
8212 let columns = settings.columns;
8213 let i, iLen, row;
8214 let target = Dom.s(side === 'header' ? settings.thead : settings.tfoot);
8215 let titleProp = side === 'header' ? 'title' : side;
8216 // Footer might be defined
8217 if (!target) {
8218 return;
8219 }
8220 // If no cells yet and we have content for them, then create
8221 if (side === 'header' ||
8222 util.array.pluck(settings.columns, titleProp).join('')) {
8223 row = target.find('tr');
8224 // Add a row if needed
8225 if (!row.count()) {
8226 row = Dom.c('tr').appendTo(target);
8227 }
8228 // Add the number of cells needed to make up to the number of columns
8229 if (row.count() === 1) {
8230 let cellCount = 0;
8231 row.find('td, th').each(el => {
8232 cellCount += el.colSpan;
8233 });
8234 for (i = cellCount, iLen = columns.length; i < iLen; i++) {
8235 Dom.c('th')
8236 .html(columns[i][titleProp] || '')
8237 .appendTo(row);
8238 }
8239 }
8240 }
8241 let detected = detectHeader(settings, target.get(0), true);
8242 if (side === 'header') {
8243 settings.header = detected;
8244 target.find('tr').classAdd(classes.thead.row);
8245 }
8246 else {
8247 settings.footer = detected;
8248 target.find('tr').classAdd(classes.tfoot.row);
8249 }
8250 // Every cell needs to be passed through the renderer
8251 target
8252 .children('tr')
8253 .children('th, td')
8254 .each(el => {
8255 // Should just be able to do `renderer(settings, side)` here but
8256 // Typescript doesn't like it, despite it already being constrained!
8257 let runner = side === 'header'
8258 ? renderer(settings, 'header')
8259 : renderer(settings, 'footer');
8260 runner(settings, Dom.s(el), classes);
8261 });
8262 }
8263 /**
8264 * Build a layout structure for a header or footer
8265 *
8266 * @param settings DataTables settings
8267 * @param source Source layout array
8268 * @param incColumns What columns should be included
8269 * @returns Layout array in column index order
8270 */
8271 function headerLayout(settings, source, incColumns) {
8272 var row, column, cell;
8273 var local = [];
8274 var structure = [];
8275 var columns = settings.columns;
8276 var columnCount = columns.length;
8277 var rowspan, colspan;
8278 if (!source) {
8279 return;
8280 }
8281 // Default is to work on only visible columns
8282 if (!incColumns) {
8283 incColumns = util.array.range(columnCount).filter(function (idx) {
8284 return columns[idx].visible;
8285 });
8286 }
8287 // Make a copy of the master layout array, but with only the columns we want
8288 for (row = 0; row < source.length; row++) {
8289 // Remove any columns we haven't selected
8290 local[row] = source[row].slice().filter(function (c, i) {
8291 return incColumns.includes(i);
8292 });
8293 // Prep the structure array - it needs an element for each row
8294 structure.push([]);
8295 }
8296 for (row = 0; row < local.length; row++) {
8297 for (column = 0; column < local[row].length; column++) {
8298 rowspan = 1;
8299 colspan = 1;
8300 // Check to see if there is already a cell (row/colspan) covering
8301 // our target insert point. If there is, then there is nothing to
8302 // do.
8303 if (structure[row][column] === undefined) {
8304 cell = local[row][column].cell;
8305 // Expand for rowspan
8306 while (local[row + rowspan] !== undefined &&
8307 local[row][column].cell == local[row + rowspan][column].cell) {
8308 structure[row + rowspan][column] = null;
8309 rowspan++;
8310 }
8311 // And for colspan
8312 while (local[row][column + colspan] !== undefined &&
8313 local[row][column].cell == local[row][column + colspan].cell) {
8314 // Which also needs to go over rows
8315 for (var k = 0; k < rowspan; k++) {
8316 structure[row + k][column + colspan] = null;
8317 }
8318 colspan++;
8319 }
8320 var titleSpan = Dom.s(cell).find('.dt-column-title');
8321 structure[row][column] = {
8322 cell: cell,
8323 colspan: colspan,
8324 rowspan: rowspan,
8325 title: titleSpan.count()
8326 ? titleSpan.html()
8327 : Dom.s(cell).html()
8328 };
8329 }
8330 }
8331 }
8332 return structure;
8333 }
8334 /**
8335 * Draw the header (or footer) element based on the column visibility states.
8336 *
8337 * @param settings DataTables settings object
8338 * @param source Layout array from detectHeader
8339 */
8340 function drawHead(settings, source) {
8341 let layout = headerLayout(settings, source);
8342 let tr;
8343 if (!layout) {
8344 return;
8345 }
8346 for (let row = 0; row < source.length; row++) {
8347 tr = source[row].row;
8348 // All cells are going to be replaced, so empty out the row
8349 if (tr) {
8350 Dom.s(tr).detachChildren();
8351 }
8352 for (let column = 0; column < layout[row].length; column++) {
8353 let point = layout[row][column];
8354 if (point) {
8355 Dom.s(point.cell)
8356 .appendTo(tr)
8357 .attr('rowspan', point.rowspan)
8358 .attr('colspan', point.colspan);
8359 }
8360 }
8361 }
8362 }
8363 /**
8364 * Insert the required TR nodes into the table for display
8365 *
8366 * @param settings DataTables settings object
8367 * @param ajaxComplete true after ajax call to complete rendering
8368 */
8369 function draw(settings, ajaxComplete) {
8370 // Allow for state saving and a custom start position
8371 setStartPosition(settings);
8372 // Provide a pre-callback function which can be used to cancel the draw is
8373 // false is returned
8374 var aPreDraw = callbackFire(settings, 'preDraw', 'preDraw', [settings]);
8375 if (aPreDraw.indexOf(false) !== -1) {
8376 processingDisplay(settings, false);
8377 return;
8378 }
8379 var rowEls = [];
8380 var rowCount = 0;
8381 var isServerSide = dataSource(settings) == 'ssp';
8382 var display = settings.display;
8383 var start = settings.displayStart;
8384 var end = displayEnd(settings);
8385 var columns = settings.columns;
8386 var body = Dom.s(settings.tbody);
8387 settings.doingDraw = true;
8388 /* Server-side processing draw intercept */
8389 if (settings.deferLoading) {
8390 settings.deferLoading = false;
8391 settings.drawCount++;
8392 processingDisplay(settings, false);
8393 }
8394 else if (!isServerSide) {
8395 settings.drawCount++;
8396 }
8397 else if (!settings.destroying && !ajaxComplete) {
8398 // Show loading message for server-side processing
8399 if (settings.drawCount === 0) {
8400 body.empty().append(_emptyRow(settings));
8401 }
8402 ajaxUpdate(settings);
8403 return;
8404 }
8405 if (display.length !== 0) {
8406 var iStart = isServerSide ? 0 : start;
8407 var iEnd = isServerSide ? settings.data.length : end;
8408 for (var j = iStart; j < iEnd; j++) {
8409 var dataIdx = display[j];
8410 var data = settings.data[dataIdx];
8411 // Row has been deleted - can't be displayed
8412 if (data === null) {
8413 continue;
8414 }
8415 // Row node hasn't been created yet
8416 if (data.tr === null) {
8417 createTr(settings, dataIdx);
8418 }
8419 var nRow = data.tr;
8420 // Add various classes as needed
8421 for (var i = 0; i < columns.length; i++) {
8422 var col = columns[i];
8423 var td = data.cells[i];
8424 Dom.s(td)
8425 .classAdd(col.type ? ext.type.className[col.type] : null) // auto class
8426 .classAdd(settings.classes.tbody.cell); // all cells
8427 }
8428 // Row callback functions - might want to manipulate the row
8429 // rowCount and j are not currently documented. Are they at all
8430 // useful?
8431 callbackFire(settings, 'row', null, [
8432 nRow,
8433 data.data,
8434 rowCount,
8435 j,
8436 dataIdx
8437 ]);
8438 rowEls.push(nRow);
8439 rowCount++;
8440 }
8441 }
8442 else {
8443 rowEls[0] = _emptyRow(settings);
8444 }
8445 /* Header and footer callbacks */
8446 callbackFire(settings, 'header', 'header', [
8447 Dom.s(settings.thead).children('tr').get(0),
8448 getDataMaster(settings),
8449 start,
8450 end,
8451 display
8452 ]);
8453 callbackFire(settings, 'footer', 'footer', [
8454 Dom.s(settings.tfoot).children('tr').get(0),
8455 getDataMaster(settings),
8456 start,
8457 end,
8458 display
8459 ]);
8460 body.detachChildren().append(rowEls);
8461 // Empty table needs a specific class
8462 Dom.s(settings.tableWrapper).classToggle('dt-empty-footer', Dom.s(settings.tfoot).find('tr').count() === 0);
8463 // Call all required callback functions for the end of a draw
8464 callbackFire(settings, 'draw', 'draw', [settings], true);
8465 // Draw is complete, sorting and filtering must be as well
8466 settings.wasOrdered = false;
8467 settings.wasFiltered = false;
8468 settings.doingDraw = false;
8469 }
8470 /**
8471 * Redraw the table - taking account of the various features which are enabled
8472 *
8473 * @param settings DataTables settings object
8474 * @param holdPosition Keep the current paging position. By default the paging
8475 * is reset to the first page
8476 * @param recompute Indicate if a rebuild of sort and filter should happen
8477 */
8478 function reDraw(settings, holdPosition, recompute) {
8479 let features = settings.features, doSort = features.ordering, doFilter = features.searching;
8480 if (recompute === undefined || recompute === true) {
8481 // Resolve any column types that are unknown due to addition or
8482 // invalidation
8483 columnTypes(settings);
8484 columnWidths(settings);
8485 if (doSort) {
8486 sort(settings);
8487 }
8488 if (doFilter) {
8489 filterComplete(settings);
8490 }
8491 else {
8492 // No filtering, so we want to just use the display master
8493 settings.display = settings.displayMaster.slice();
8494 }
8495 }
8496 if (holdPosition !== true) {
8497 settings.displayStart = 0;
8498 }
8499 else {
8500 // Keep position, but make sure that there is actually data to display,
8501 // otherwise we need to rewind a bit (e.g. if rows were deleted)
8502 lengthOverflow(settings);
8503 }
8504 // Let any modules know about the draw hold position state (used by
8505 // scrolling internally)
8506 settings.drawHold = holdPosition;
8507 draw(settings);
8508 settings.api.one('draw', function () {
8509 settings.drawHold = false;
8510 });
8511 }
8512 /**
8513 * Table is empty - create a row with an empty message in it
8514 *
8515 * @param settings DataTables context
8516 */
8517 function _emptyRow(settings) {
8518 let lang = settings.language;
8519 let zero = lang.zeroRecords;
8520 let dataSrc = dataSource(settings);
8521 // Make use of the fact that settings.json is only set once the initial data
8522 // has been loaded. Show loading when that isn't the case
8523 if ((dataSrc === 'ssp' || dataSrc === 'ajax') && !settings.json) {
8524 zero = lang.loadingRecords;
8525 }
8526 else if (lang.emptyTable && recordsTotal(settings) === 0) {
8527 zero = lang.emptyTable;
8528 }
8529 return Dom
8530 .c('tr')
8531 .append(Dom
8532 .c('td')
8533 .attr('colSpan', visibleColumns(settings))
8534 .classAdd(settings.classes.empty.row)
8535 .html(zero))
8536 .get(0);
8537 }
8538 /**
8539 * Use the DOM source to create up an array of header cells. The idea here is to
8540 * create a layout grid (array) of rows x columns, which contains a reference to
8541 * the cell at that point in the grid (regardless of col/rowspan), such that any
8542 * column / row could be removed and the new grid constructed.
8543 *
8544 * @param settings DataTables context
8545 * @param thead thead / tbody element
8546 * @param write If cells should be written (if required)
8547 * @returns Calculated layout array
8548 */
8549 function detectHeader(settings, thead, write) {
8550 let columns = settings.columns;
8551 let rows = Dom.s(thead).children('tr');
8552 let row, loopCell;
8553 let i, k, l, len, shifted, column, colspan, rowspan;
8554 let titleRow = settings.titleRow;
8555 let isHeader = thead && thead.nodeName.toLowerCase() === 'thead';
8556 let layout = [];
8557 let isUnique;
8558 let shift = function (a, b, j) {
8559 let d = a[b];
8560 while (d[j]) {
8561 j++;
8562 }
8563 return j;
8564 };
8565 // We know how many rows there are in the layout - so prep it
8566 for (i = 0, len = rows.count(); i < len; i++) {
8567 layout.push([]);
8568 }
8569 for (i = 0, len = rows.count(); i < len; i++) {
8570 row = rows.get(i);
8571 column = 0;
8572 // For every cell in the row..
8573 loopCell = row.firstChild;
8574 while (loopCell) {
8575 if (loopCell.nodeName.toUpperCase() == 'TD' ||
8576 loopCell.nodeName.toUpperCase() == 'TH') {
8577 let cell = Dom.s(loopCell);
8578 let cols = [];
8579 // Get the col and rowspan attributes from the DOM and sanitise
8580 // them
8581 colspan = parseInt(cell.attr('colspan') || '1') || 1;
8582 rowspan = parseInt(cell.attr('rowspan') || '1') || 1;
8583 colspan =
8584 !colspan || colspan === 0 || colspan === 1 ? 1 : colspan;
8585 rowspan =
8586 !rowspan || rowspan === 0 || rowspan === 1 ? 1 : rowspan;
8587 // There might be colspan cells already in this row, so shift
8588 // our target accordingly
8589 shifted = shift(layout, i, column);
8590 // Cache calculation for unique columns
8591 isUnique = colspan === 1 ? true : false;
8592 // Perform header setup
8593 if (write) {
8594 if (isUnique) {
8595 // Allow column options to be set from HTML attributes
8596 columnOptions(settings, shifted, escapeObject(cell.data()));
8597 // Get the width for the column. This can be defined
8598 // from the width attribute, style attribute or
8599 // `columns.width` option
8600 let columnDef = columns[shifted];
8601 let width = cell.attr('width') || null;
8602 let t = cell
8603 .get(0)
8604 .style.width.match(/width:\s*(\d+[pxem%]+)/);
8605 if (t) {
8606 width = t[1];
8607 }
8608 columnDef.widthOrig = columnDef.width || width;
8609 if (isHeader) {
8610 // Column title handling - can be user set, or read
8611 // from the DOM This happens before the render, so
8612 // the original is still in place
8613 if (columnDef.title !== null &&
8614 !columnDef.autoTitle) {
8615 if ((titleRow === true && i === 0) || // top row
8616 (titleRow === false &&
8617 i === rows.count() - 1) || // bottom row
8618 titleRow === i || // specific row
8619 titleRow === null) {
8620 cell.html(columnDef.title);
8621 }
8622 }
8623 if (!columnDef.title && isUnique) {
8624 columnDef.title = util.string.stripHtml(cell.html());
8625 columnDef.autoTitle = true;
8626 }
8627 }
8628 else {
8629 // Footer specific operations
8630 if (columnDef.footer) {
8631 cell.html(columnDef.footer);
8632 }
8633 }
8634 // Fall back to the aria-label attribute on the table
8635 // header if no ariaTitle is provided.
8636 if (!columnDef.ariaTitle) {
8637 columnDef.ariaTitle =
8638 cell.attr('aria-label') || columnDef.title;
8639 }
8640 // Column specific class names
8641 if (columnDef.className) {
8642 cell.classAdd(columnDef.className);
8643 }
8644 }
8645 // Wrap the column title so we can write to it in future
8646 if (cell.find('div.dt-column-title').count() === 0) {
8647 Dom.c('div')
8648 .classAdd('dt-column-title')
8649 .append(Array.from(cell.get(0).childNodes))
8650 .appendTo(cell);
8651 }
8652 if (settings.orderIndicators &&
8653 isHeader &&
8654 cell.filter(':not([data-dt-order=disable])').count() !==
8655 0 &&
8656 cell.parent(':not([data-dt-order=disable])').count() !==
8657 0 &&
8658 cell.find('div.dt-column-order').count() === 0) {
8659 Dom.c('div')
8660 .classAdd('dt-column-order')
8661 .appendTo(cell);
8662 }
8663 // We need to wrap the elements in the header in another
8664 // element to use flexbox layout for those elements
8665 var headerFooter = isHeader ? 'header' : 'footer';
8666 if (cell.find('div.dt-column-' + headerFooter).count() ===
8667 0) {
8668 Dom.c('div')
8669 .classAdd('dt-column-' + headerFooter)
8670 .append(Array.from(cell.get(0).childNodes))
8671 .appendTo(cell);
8672 }
8673 }
8674 // If there is col / rowspan, copy the information into the
8675 // layout grid
8676 for (l = 0; l < colspan; l++) {
8677 for (k = 0; k < rowspan; k++) {
8678 layout[i + k][shifted + l] = {
8679 cell: cell.get(0),
8680 unique: isUnique
8681 };
8682 layout[i + k].row = row;
8683 }
8684 cols.push(shifted + l);
8685 }
8686 // Assign an attribute so spanning cells can still be identified
8687 // as belonging to a column
8688 cell.attr('data-dt-column', util.unique(cols).join(','));
8689 }
8690 loopCell = loopCell.nextSibling;
8691 }
8692 }
8693 return layout;
8694 }
8695 /**
8696 * Set the start position for draw
8697 *
8698 * @param settings DataTables settings object
8699 */
8700 function setStartPosition(settings) {
8701 var bServerSide = dataSource(settings) == 'ssp';
8702 var iInitDisplayStart = settings.displayStartInit;
8703 // Check and see if we have an initial draw position from state saving
8704 if (iInitDisplayStart !== undefined && iInitDisplayStart !== -1) {
8705 settings.displayStart = bServerSide
8706 ? iInitDisplayStart
8707 : iInitDisplayStart >= recordsDisplay(settings)
8708 ? 0
8709 : iInitDisplayStart;
8710 settings.displayStartInit = -1;
8711 }
8712 }
8713 /**
8714 * Get the number of records in the current record set, before filtering
8715 *
8716 * @param ctx DataTables settings object
8717 */
8718 function recordsTotal(ctx) {
8719 return dataSource(ctx) == 'ssp'
8720 ? ctx.recordsTotal * 1
8721 : ctx.displayMaster.length;
8722 }
8723 /**
8724 * Get the number of records in the current record set, after filtering
8725 *
8726 * @param ctx DataTables settings object
8727 */
8728 function recordsDisplay(ctx) {
8729 return dataSource(ctx) == 'ssp'
8730 ? ctx.recordsDisplay * 1
8731 : ctx.display.length;
8732 }
8733 /**
8734 * Get the display end point - display index
8735 *
8736 * @param ctx DataTables settings object
8737 */
8738 function displayEnd(ctx) {
8739 var len = ctx.pageLength, start = ctx.displayStart, calc = start + len, records = ctx.display.length, features = ctx.features, paginate = features.paging;
8740 if (features.serverSide) {
8741 return paginate === false || len === -1
8742 ? start + records
8743 : Math.min(start + len, ctx.recordsDisplay);
8744 }
8745 else {
8746 return !paginate || calc > records || len === -1 ? records : calc;
8747 }
8748 }
8749
8750 /**
8751 * Common run function for selector types
8752 */
8753 function selectorRun(type, selector, selectFn, settings, opts) {
8754 var out = [], res, i, iLen, selectorType = typeof selector;
8755 // If a Dom instance, then get the underlying elements
8756 if (selector instanceof Dom) {
8757 selector = selector.get();
8758 }
8759 // Can't just check for isArray here, as an API or jQuery instance might be
8760 // given with their array like look
8761 if (!selector ||
8762 selectorType === 'string' ||
8763 selectorType === 'function' ||
8764 selector.length === undefined) {
8765 selector = [selector];
8766 }
8767 for (i = 0, iLen = selector.length; i < iLen; i++) {
8768 res = selectFn(typeof selector[i] === 'string' ? selector[i].trim() : selector[i]);
8769 // Remove empty items
8770 res = res.filter(function (item) {
8771 return item !== null && item !== undefined;
8772 });
8773 if (res && res.length) {
8774 out = out.concat(res);
8775 }
8776 }
8777 // selector extensions
8778 var extSelectors = ext.selector[type];
8779 if (extSelectors.length) {
8780 for (i = 0, iLen = extSelectors.length; i < iLen; i++) {
8781 out = extSelectors[i](settings, opts, out);
8782 }
8783 }
8784 return unique(out);
8785 }
8786 function selectorOpts(opts) {
8787 if (!opts) {
8788 opts = {};
8789 }
8790 // Backwards compatibility for 1.9- which used the terminology filter rather
8791 // than search
8792 if (opts.filter && opts.search === undefined) {
8793 opts.search = opts.filter;
8794 }
8795 return assign({}, {
8796 columnOrder: 'implied',
8797 search: 'none',
8798 order: 'current',
8799 page: 'all'
8800 }, opts);
8801 }
8802 // Reduce the API instance to the first item found
8803 function selectorFirst(old) {
8804 // Need to specify the target class as singular since `old` has the context
8805 // of the plural
8806 var inst = old.inst(old.context[0], null, old._newClass.replace(/s$/, ''));
8807 // Use a push rather than passing to the constructor, since it will
8808 // merge arrays down automatically, which isn't what is wanted here
8809 if (old.length) {
8810 inst.push(old[0]);
8811 }
8812 inst.selector = old.selector;
8813 // Limit to a single row / column / cell
8814 if (inst.length && inst[0].length > 1) {
8815 inst[0].splice(1);
8816 }
8817 return inst;
8818 }
8819 function selectorRowIndexes(settings, opts) {
8820 var i, iLen, tmp, a = [], displayFiltered = settings.display, displayMaster = settings.displayMaster;
8821 var search = opts.search, // none, applied, removed
8822 order = opts.order, // applied, current, index (original)
8823 page = opts.page; // all, current
8824 if (dataSource(settings) == 'ssp') {
8825 // In server-side processing mode, most options are irrelevant since
8826 // rows not shown don't exist and the index order is the applied order
8827 // Removed is a special case - for consistency just return an empty
8828 // array
8829 return search === 'removed' ? [] : range(0, displayMaster.length);
8830 }
8831 if (page == 'current') {
8832 // Current page implies that order=current and filter=applied, since it
8833 // is fairly senseless otherwise, regardless of what order and search
8834 // actually are
8835 for (i = settings.displayStart, iLen = displayEnd(settings); i < iLen; i++) {
8836 a.push(displayFiltered[i]);
8837 }
8838 }
8839 else if (order == 'current' || order == 'applied') {
8840 if (search == 'none') {
8841 a = displayMaster.slice();
8842 }
8843 else if (search == 'applied') {
8844 a = displayFiltered.slice();
8845 }
8846 else if (search == 'removed') {
8847 // O(n+m) solution by creating a hash map
8848 var displayFilteredMap = {};
8849 for (i = 0, iLen = displayFiltered.length; i < iLen; i++) {
8850 displayFilteredMap[displayFiltered[i]] = null;
8851 }
8852 displayMaster.forEach(function (item) {
8853 if (!Object.prototype.hasOwnProperty.call(displayFilteredMap, item)) {
8854 a.push(item);
8855 }
8856 });
8857 }
8858 }
8859 else if (order == 'index' || order == 'original') {
8860 for (i = 0, iLen = settings.data.length; i < iLen; i++) {
8861 if (!settings.data[i]) {
8862 continue;
8863 }
8864 if (search == 'none') {
8865 a.push(i);
8866 }
8867 else {
8868 // applied | removed
8869 tmp = displayFiltered.indexOf(i);
8870 if ((tmp === -1 && search == 'removed') ||
8871 (tmp >= 0 && search == 'applied')) {
8872 a.push(i);
8873 }
8874 }
8875 }
8876 }
8877 else if (typeof order === 'number') {
8878 // Order the rows by the given column
8879 var ordered = sort(settings, order, 'asc');
8880 if (search === 'none') {
8881 a = ordered;
8882 }
8883 else {
8884 // applied | removed
8885 for (i = 0; i < ordered.length; i++) {
8886 tmp = displayFiltered.indexOf(ordered[i]);
8887 if ((tmp === -1 && search == 'removed') ||
8888 (tmp >= 0 && search == 'applied')) {
8889 a.push(ordered[i]);
8890 }
8891 }
8892 }
8893 }
8894 return a;
8895 }
8896
8897 /**
8898 * `Array.prototype` reference as methods from it are used in the array-like
8899 * methods of the API.
8900 */
8901 const __arrayProto = Array.prototype;
8902 const Api = function (context, data) {
8903 // Allow the API to be initialised without specifying `new`
8904 if (!(this instanceof Api)) {
8905 return new Api(context, data);
8906 }
8907 this.context = toContextArray(context);
8908 // Initial data
8909 arrayApply(this, data);
8910 // Add properties which will still execute in this scope
8911 extendApi(this, 'Api');
8912 };
8913 // And the private parameters
8914 util.object.assign(Api.prototype, {
8915 _newClass: 'Api',
8916 isDataTableApi: true,
8917 any() {
8918 return this.count() !== 0;
8919 },
8920 context: [], // array of table settings objects
8921 count() {
8922 return this.flatten().length;
8923 },
8924 each(fn) {
8925 for (var i = 0, iLen = this.length; i < iLen; i++) {
8926 fn.call(this, this[i], i, this);
8927 }
8928 return this;
8929 },
8930 eq(idx) {
8931 var ctx = this.context;
8932 // Note that `eq` returns an API instance, not a nested class instance
8933 return ctx.length > idx ? this.inst(ctx[idx], this[idx], 'Api') : null;
8934 },
8935 filter(fn) {
8936 var a = __arrayProto.filter.call(this, fn, this);
8937 return this.inst(this.context, a);
8938 },
8939 flatten() {
8940 var a = [];
8941 return this.inst(this.context, a.concat.apply(a, this.toArray()));
8942 },
8943 get(idx) {
8944 return this[idx];
8945 },
8946 join: __arrayProto.join,
8947 includes(find) {
8948 return this.indexOf(find) === -1 ? false : true;
8949 },
8950 indexOf: __arrayProto.indexOf,
8951 inst(context, data, newClass) {
8952 let name = newClass || this._newClass;
8953 let inst = Api;
8954 if (classes[name]) {
8955 inst = classes[name];
8956 }
8957 return new inst(context, data);
8958 },
8959 iterator(flatten, type, fn, alwaysNew) {
8960 var a = [], ret, i, iLen, j, jen, context = this.context, rows, items, item, selector = this.selector;
8961 // Argument shifting
8962 if (typeof flatten === 'string') {
8963 alwaysNew = fn;
8964 fn = type;
8965 type = flatten;
8966 flatten = false;
8967 }
8968 for (i = 0, iLen = context.length; i < iLen; i++) {
8969 var apiInst = this.inst(context[i]);
8970 if (type === 'table') {
8971 ret = fn.call(apiInst, context[i], i);
8972 if (ret !== undefined) {
8973 a.push(ret);
8974 }
8975 }
8976 else if (type === 'columns' || type === 'rows') {
8977 // this has same length as context - one entry for each table
8978 ret = fn.call(apiInst, context[i], this[i], i);
8979 if (ret !== undefined) {
8980 a.push(ret);
8981 }
8982 }
8983 else if (type === 'every' ||
8984 type === 'column' ||
8985 type === 'column-rows' ||
8986 type === 'row' ||
8987 type === 'cell') {
8988 // columns and rows share the same structure.
8989 // 'this' is an array of column indexes for each context
8990 items = this[i];
8991 if (type === 'column-rows') {
8992 rows = selectorRowIndexes(context[i], selector.opts);
8993 }
8994 for (j = 0, jen = items.length; j < jen; j++) {
8995 item = items[j];
8996 if (type === 'cell') {
8997 ret = fn.call(apiInst, context[i], item.row, item.column, i, j);
8998 }
8999 else {
9000 ret = fn.call(apiInst, context[i], item, i, j, rows);
9001 }
9002 if (ret !== undefined) {
9003 a.push(ret);
9004 }
9005 }
9006 }
9007 }
9008 if (a.length || alwaysNew) {
9009 var api = this.inst(context, flatten ? a.concat.apply([], a) : a);
9010 var apiSelector = api.selector;
9011 if (apiSelector) {
9012 apiSelector.rows = selector.rows;
9013 apiSelector.cols = selector.cols;
9014 apiSelector.opts = selector.opts;
9015 }
9016 return api;
9017 }
9018 return this;
9019 },
9020 lastIndexOf: __arrayProto.lastIndexOf,
9021 length: 0,
9022 map(fn) {
9023 var a = __arrayProto.map.call(this, fn, this);
9024 return this.inst(this.context, a);
9025 },
9026 pluck(prop) {
9027 var fn = util.get(prop);
9028 return this.map((src) => fn(src));
9029 },
9030 pop: __arrayProto.pop,
9031 push: __arrayProto.push,
9032 reduce: __arrayProto.reduce,
9033 reduceRight: __arrayProto.reduceRight,
9034 reverse: __arrayProto.reverse,
9035 // Object with rows, columns and opts
9036 selector: {
9037 rows: undefined,
9038 cols: undefined,
9039 opts: undefined
9040 },
9041 shift: __arrayProto.shift,
9042 slice() {
9043 return this.inst(this.context, this);
9044 },
9045 sort: __arrayProto.sort,
9046 splice: __arrayProto.splice,
9047 toArray() {
9048 return __arrayProto.slice.call(this);
9049 },
9050 to$() {
9051 let jq = util.external('jq');
9052 return jq(this);
9053 },
9054 toDom() {
9055 return new Dom(this.toArray());
9056 },
9057 toJQuery: function () {
9058 let jq = util.external('jq');
9059 return jq(this);
9060 },
9061 unique: function () {
9062 return this.inst(this.context, util.array.unique(this.toArray()));
9063 },
9064 unshift: __arrayProto.unshift
9065 });
9066 function register(name, func) {
9067 if (Array.isArray(name)) {
9068 for (let i = 0; i < name.length; i++) {
9069 Api.register(name[i], func);
9070 }
9071 return;
9072 }
9073 let names = getPrototypeNames(name);
9074 // Has the parent already been defined or not?
9075 if (!classes[names.hostClass]) {
9076 // Create a new "class"
9077 createApiClass(names.hostClass);
9078 }
9079 if (names.property) {
9080 if (!properties[names.propertyHost]) {
9081 properties[names.propertyHost] = [];
9082 }
9083 properties[names.propertyHost].push({
9084 couldReturn: names.couldReturn,
9085 property: names.property,
9086 method: names.methodName,
9087 fn: func
9088 });
9089 }
9090 else {
9091 let wrapped = function () {
9092 // If a new instance (.inst) is created while the function is being
9093 // executed, we want to allow it to return its target class. But we
9094 // also need to keep hold of our own, so it can be used in the end.
9095 let previousCould = this._newClass;
9096 this._newClass = names.couldReturn;
9097 let result = func.apply(this, arguments);
9098 this._newClass = previousCould;
9099 return result;
9100 };
9101 // Create the new method on the host class
9102 classes[names.hostClass].prototype[names.methodName] = wrapped;
9103 // If the method is on the top level, it needs to be applied to other
9104 // classes which have already been defined to allow the circular
9105 // chaining of the API (e.g. `row().data(...).draw())`.
9106 if (names.hostClass === 'Api') {
9107 util.object.each(classes, (className, klass) => {
9108 if (!klass.prototype[names.methodName]) {
9109 klass.prototype[names.methodName] = wrapped;
9110 }
9111 });
9112 }
9113 }
9114 }
9115 function registerPlural(pluralName, singularName, func) {
9116 Api.register(pluralName, func);
9117 Api.register(singularName, function () {
9118 var ret = func.apply(this, arguments);
9119 if (ret === this) {
9120 // Returned item is the API instance that was passed in, return it
9121 return this;
9122 }
9123 else if (ret && ret.isDataTableApi) {
9124 // New API instance returned, want the value from the first item
9125 // in the returned array for the singular result.
9126 return ret.length
9127 ? Array.isArray(ret[0])
9128 ? this.inst(ret.context, ret[0]) // Array results are 'enhanced'
9129 : ret[0]
9130 : undefined;
9131 }
9132 // Non-API return - just fire it back
9133 return ret;
9134 });
9135 }
9136 Api.register = register;
9137 Api.registerPlural = registerPlural;
9138 /** A collection of properties to apply to the classes as they are constructed */
9139 const properties = {};
9140 /** Collection of API classes */
9141 const classes = {
9142 Api
9143 };
9144 // Upstream DataTables ships a leftover debug block here that assigns the very
9145 // generic globals `window.classes` and `window.properties`. Nothing reads them
9146 // (verified in this file and across the plugin), so they are removed to avoid
9147 // polluting the global namespace on every page carrying a Product Table.
9148 // RE-APPLY THIS REMOVAL after any future DataTables upgrade.
9149 /**
9150 * Create a new API "class" (function), used for nested levels of the API - e.g.
9151 * `ApiRows` and `ApiColumn`.
9152 *
9153 * @param name
9154 */
9155 function createApiClass(name) {
9156 let newClass = function (context, data) {
9157 // Same as the main API constructor
9158 this.context = toContextArray(context);
9159 arrayApply(this, data);
9160 // Extend the API with properties that execute in this scope, both for
9161 // this level and for the top level to allow looped chaining
9162 extendApi(this, 'Api');
9163 extendApi(this, this._newClass);
9164 };
9165 newClass.prototype = Object.create(Api.prototype);
9166 Object.defineProperty(newClass, 'name', {
9167 value: name,
9168 writable: false
9169 });
9170 newClass.prototype._newClass = name;
9171 classes[name] = newClass;
9172 }
9173 /**
9174 * When an instance is created it needs to be extended with properties (since
9175 * these cannot be given a scope from the prototype due to the nesting).
9176 *
9177 * @param api API instance to extend
9178 * @param className The name of the instance to extend
9179 * @returns void
9180 */
9181 function extendApi(api, className) {
9182 let props = properties[className];
9183 if (!props) {
9184 return;
9185 }
9186 for (let i = 0; i < props.length; i++) {
9187 let def = props[i];
9188 if (!api[def.property]) {
9189 // Instance doesn't yet have this property, so need to create an
9190 // object to hold the methods.
9191 api[def.property] = {};
9192 }
9193 else if (!api.hasOwnProperty(def.property)) {
9194 // Its a prototype function, which is a problem since it is shared
9195 // between all instances, and thus scope is whichever is last setup.
9196 // As such we need to make it an independent function and wrap it.
9197 let fn = api[def.property];
9198 api[def.property] = function () {
9199 return fn.apply(api, arguments);
9200 };
9201 }
9202 // Wrap the function so we can keep scope and set the return class
9203 api[def.property][def.method] = function () {
9204 let previousCould = api._newClass;
9205 api._newClass = def.couldReturn;
9206 let result = def.fn.apply(api, arguments);
9207 api._newClass = previousCould;
9208 return result;
9209 };
9210 }
9211 }
9212 /**
9213 * Based on an API method name, construct the class, property, etc names that
9214 * are used to store and construct the API.
9215 *
9216 * @param name API function name
9217 * @returns Name components
9218 */
9219 function getPrototypeNames(name) {
9220 let parts = name.split('.');
9221 let property = null;
9222 let hostClass = 'Api';
9223 let returnClass = 'Api';
9224 let methodName = '';
9225 let propertyHost = '';
9226 let lastPart = '';
9227 for (let i = 0; i < parts.length; i++) {
9228 let part = parts[i];
9229 let partNoParen = part.replace('()', '');
9230 hostClass = returnClass; // from previous loop
9231 returnClass +=
9232 partNoParen.charAt(0).toUpperCase() +
9233 partNoParen.slice(1).toLowerCase();
9234 if (part.includes('()')) {
9235 methodName = partNoParen;
9236 // If the previous part was a method rather than a property, then we
9237 // remove the property host
9238 if (lastPart.includes('()')) {
9239 property = null;
9240 propertyHost = '';
9241 }
9242 }
9243 else {
9244 // Is a property
9245 property = part;
9246 propertyHost = hostClass;
9247 }
9248 lastPart = part;
9249 }
9250 return {
9251 couldReturn: returnClass,
9252 hostClass,
9253 property,
9254 propertyHost,
9255 methodName
9256 };
9257 }
9258 /**
9259 * Abstraction for `context` parameter of the `Api` constructor to allow it to
9260 * take several different forms for ease of use.
9261 *
9262 * Each of the input parameter types will be converted to a DataTables settings
9263 * object where possible.
9264 *
9265 * @param mixedIn DataTable identifier. Can be one of:
9266 * * `string` - jQuery selector. Any DataTables' matching the given selector
9267 * with be found and used.
9268 * * `node` - `TABLE` node which has already been formed into a DataTable.
9269 * * `jQuery` - A jQuery object of `TABLE` nodes.
9270 * * `object` - DataTables settings object
9271 * * `DataTables.Api` - API instance
9272 * @return Matching DataTables settings objects. `null` or `undefined` is
9273 * returned if no matching DataTable is found.
9274 */
9275 function toContext(mixedIn) {
9276 var mixed = mixedIn;
9277 var idx, nodes = null;
9278 var settings = ext.settings;
9279 var tables = util.array.pluck(settings, 'table');
9280 if (!mixed) {
9281 return [];
9282 }
9283 else if (mixed.table && mixed.features) {
9284 // DataTables settings object
9285 return [mixed];
9286 }
9287 else if (mixed.nodeName && mixed.nodeName.toLowerCase() === 'table') {
9288 // Table node
9289 idx = tables.indexOf(mixed);
9290 return idx !== -1 ? [settings[idx]] : null;
9291 }
9292 else if (mixed && typeof mixed.settings === 'function') {
9293 return mixed.settings().toArray();
9294 }
9295 else if (typeof mixed === 'string') {
9296 // jQuery selector
9297 nodes = Dom.s(mixed).get();
9298 }
9299 else if (util.is.jquery(mixed)) {
9300 // jQuery object
9301 nodes = mixed.get();
9302 }
9303 else if (util.is.dom(mixed)) {
9304 // DOM object
9305 nodes = mixed.get();
9306 }
9307 if (nodes) {
9308 return settings.filter(function (v, i) {
9309 return nodes.includes(tables[i]);
9310 });
9311 }
9312 }
9313 /**
9314 * Create the context array for an instance
9315 *
9316 * @param mixed The passed in options to convert to context
9317 * @returns Context array
9318 */
9319 function toContextArray(mixed) {
9320 var i;
9321 var settings = [];
9322 var ctxSettings = function (o) {
9323 var a = toContext(o);
9324 if (a) {
9325 settings.push.apply(settings, a);
9326 }
9327 };
9328 if (Array.isArray(mixed)) {
9329 for (i = 0; i < mixed.length; i++) {
9330 ctxSettings(mixed[i]);
9331 }
9332 }
9333 else {
9334 ctxSettings(mixed);
9335 }
9336 // Remove duplicates
9337 return settings.length > 1 ? util.unique(settings) : settings;
9338 }
9339
9340 register('$()', function (selector, opts) {
9341 let jq = util.external('jq');
9342 if (!jq) {
9343 log(this.context[0], 0, 'No jQuery available. Use `.dom()` or register jQuery');
9344 }
9345 let rows = this.rows(opts).nodes(), // Get all rows
9346 jqRows = jq(rows);
9347 return jq([].concat(jqRows.filter(selector).toArray(), jqRows.find(selector).toArray()));
9348 });
9349 // jQuery functions to operate on the tables
9350 ['on', 'one', 'off'].forEach(key => {
9351 register(key + '()', function ( /* event, handler */) {
9352 var args = Array.prototype.slice.call(arguments);
9353 // Add the `dt` namespace automatically if it isn't already present
9354 args[0] = args[0]
9355 .split(/\s/)
9356 .map(function (e) {
9357 return !e.match(/\.dt\b/) ? e + '.dt' : e;
9358 })
9359 .join(' ');
9360 var inst = Dom.s(this.tables().nodes());
9361 inst[key].apply(inst, args);
9362 return this;
9363 });
9364 });
9365 register('clear()', function () {
9366 return this.iterator('table', function (settings) {
9367 clearTable(settings);
9368 });
9369 });
9370 register('error()', function (msg) {
9371 return this.iterator('table', function (settings) {
9372 log(settings, 0, msg);
9373 });
9374 });
9375 register('settings()', function () {
9376 return new Api(this.context, this.context);
9377 });
9378 register('init()', function () {
9379 var ctx = this.context;
9380 return ctx.length ? ctx[0].init : null;
9381 });
9382 register('data()', function () {
9383 return this.iterator('table', function (settings) {
9384 return util.array.pluck(settings.data, 'data');
9385 }).flatten();
9386 });
9387 register('trigger()', function (name, args, bubbles) {
9388 return this.iterator('table', function (settings) {
9389 return callbackFire(settings, null, name, args, bubbles);
9390 }).flatten();
9391 });
9392 register('ready()', function (fn) {
9393 var ctx = this.context;
9394 // Get status of first table
9395 if (!fn) {
9396 return ctx.length ? ctx[0].initDone || false : false;
9397 }
9398 // Function to run either once the table becomes ready or
9399 // immediately if it is already ready.
9400 return this.tables().every(function () {
9401 var api = this;
9402 if (this.context[0].initDone) {
9403 fn.call(api);
9404 }
9405 else {
9406 this.on('init.dt.DT', function () {
9407 fn.call(api);
9408 });
9409 }
9410 });
9411 });
9412 register('destroy()', function (remove) {
9413 remove = remove || false;
9414 return this.iterator('table', function (settings) {
9415 var classes = settings.classes;
9416 var table = settings.table;
9417 var tbody = settings.tbody;
9418 var thead = settings.thead;
9419 var tfoot = settings.tfoot;
9420 var jqTable = Dom.s(table);
9421 var jqTbody = Dom.s(tbody);
9422 var jqWrapper = Dom.s(settings.tableWrapper);
9423 var rows = settings.data
9424 .map(function (r) {
9425 return r ? r.tr : null;
9426 })
9427 .filter(r => !!r);
9428 var orderClasses = classes.order;
9429 // Flag to note that the table is currently being destroyed - no action
9430 // should be taken
9431 settings.destroying = true;
9432 // Fire off the destroy callbacks for plug-ins etc
9433 callbackFire(settings, 'destroy', 'destroy', [settings], true);
9434 // If not being removed from the document, make all columns visible
9435 if (!remove) {
9436 new Api(settings).columns().visible();
9437 }
9438 // Container width change listener
9439 if (settings.resizeObserver) {
9440 settings.resizeObserver.disconnect();
9441 }
9442 // Blitz all `DT` namespaced events (these are internal events, the
9443 // lowercase, `dt` events are user subscribed and they are responsible
9444 // for removing them
9445 jqWrapper.off('.DT').find(':not(tbody *)').off('.DT');
9446 if (settings.windowResizeCb) {
9447 window.removeEventListener('resize', settings.windowResizeCb);
9448 }
9449 // When scrolling we had to break the table up - restore it
9450 if (table != thead.parentNode) {
9451 jqTable.children('thead').detach();
9452 jqTable.append(thead);
9453 }
9454 if (tfoot && table != tfoot.parentNode) {
9455 jqTable.children('tfoot').detach();
9456 jqTable.append(tfoot);
9457 }
9458 // Clean up the header / footer
9459 cleanHeader(thead, 'header');
9460 cleanHeader(tfoot, 'footer');
9461 settings.colgroup.remove();
9462 settings.order = [];
9463 settings.orderFixed = [];
9464 sortingClasses(settings);
9465 jqTable
9466 .find('th, td')
9467 .classRemove(Object.values(ext.type.className).join(' '));
9468 Dom.s(thead)
9469 .find('th, td')
9470 .classRemove(orderClasses.none +
9471 ' ' +
9472 orderClasses.canAsc +
9473 ' ' +
9474 orderClasses.canDesc +
9475 ' ' +
9476 orderClasses.isAsc +
9477 ' ' +
9478 orderClasses.isDesc)
9479 .css('width', '')
9480 .attrRemove('aria-sort');
9481 // Add the TR elements back into the table in their original order
9482 jqTbody.children().detach();
9483 jqTbody.append(rows);
9484 var orig = settings.tableWrapper.parentNode;
9485 var insertBefore = settings.tableWrapper.nextSibling;
9486 // Remove the DataTables generated nodes, events and classes
9487 var removedMethod = remove ? 'remove' : 'detach';
9488 jqTable[removedMethod]();
9489 jqWrapper[removedMethod]();
9490 // If we need to reattach the table to the document
9491 if (!remove && orig) {
9492 // insertBefore acts like appendChild if !arg[1]
9493 orig.insertBefore(table, insertBefore);
9494 // Restore the width of the original table - was read from the style property,
9495 // so we can restore directly to that
9496 jqTable.css('width', settings + 'px').classRemove(classes.table);
9497 }
9498 /* Remove the settings object from the settings array */
9499 var idx = ext.settings.indexOf(settings);
9500 if (idx !== -1) {
9501 ext.settings.splice(idx, 1);
9502 }
9503 });
9504 });
9505 // i18n method for extensions to be able to use the language object from the
9506 // DataTable
9507 register('i18n()', function (token, def, plural) {
9508 var ctx = this.context[0];
9509 var resolved = util.get(token)(ctx.language);
9510 if (resolved === undefined) {
9511 resolved = def;
9512 }
9513 if (util.is.plainObject(resolved)) {
9514 if (plural !== false) {
9515 resolved =
9516 plural !== undefined && resolved[plural] !== undefined
9517 ? resolved[plural]
9518 : resolved._;
9519 }
9520 }
9521 return typeof resolved === 'string'
9522 ? resolved.replace('%d', plural) // nb: plural might be undefined,
9523 : resolved;
9524 });
9525 // Needed for header and footer, so pulled into its own function
9526 function cleanHeader(node, className) {
9527 let headerCell = Dom.s(node);
9528 headerCell.find('.dt-column-order').remove();
9529 headerCell.find('.dt-column-title').each(function (el) {
9530 let cell = Dom.s(el);
9531 var title = cell.html();
9532 cell.parent().parent().html(title);
9533 cell.remove();
9534 });
9535 headerCell.find('div.dt-column-' + className).remove();
9536 headerCell.find('th, td').attrRemove('data-dt-column');
9537 }
9538
9539 const __reload = function (settings, holdPosition, callback) {
9540 // Use the draw event to trigger a callback
9541 if (callback) {
9542 var api = new Api(settings);
9543 api.one('draw', function () {
9544 callback(api.ajax.json());
9545 });
9546 }
9547 if (dataSource(settings) == 'ssp') {
9548 reDraw(settings, holdPosition);
9549 }
9550 else {
9551 processingDisplay(settings, true);
9552 // Cancel an existing request
9553 var xhr = settings.jqXHR;
9554 if (xhr && xhr.readyState !== 4 && typeof xhr.abort === 'function') {
9555 xhr.abort();
9556 }
9557 // Trigger xhr
9558 buildAjax(settings, {}, function (json) {
9559 clearTable(settings);
9560 var data = ajaxDataSrc(settings, json, false);
9561 for (var i = 0, iLen = data.length; i < iLen; i++) {
9562 addData(settings, data[i]);
9563 }
9564 reDraw(settings, holdPosition);
9565 initComplete(settings);
9566 processingDisplay(settings, false);
9567 });
9568 }
9569 };
9570 register('ajax.json()', function () {
9571 var ctx = this.context;
9572 if (ctx.length > 0) {
9573 return ctx[0].json;
9574 }
9575 // else return undefined;
9576 });
9577 register('ajax.params()', function () {
9578 var ctx = this.context;
9579 if (ctx.length > 0) {
9580 return ctx[0].ajaxData;
9581 }
9582 // else return undefined;
9583 });
9584 register('ajax.reload()', function (callback, resetPaging) {
9585 return this.iterator('table', function (settings) {
9586 __reload(settings, resetPaging === false, callback);
9587 });
9588 });
9589 register('ajax.url()', function (url) {
9590 var ctx = this.context;
9591 if (url === undefined) {
9592 // get
9593 if (ctx.length === 0) {
9594 return undefined;
9595 }
9596 let context = ctx[0];
9597 return util.is.plainObject(context.ajax)
9598 ? context.ajax.url
9599 : context.ajax;
9600 }
9601 // set
9602 return this.iterator('table', function (settings) {
9603 if (util.is.plainObject(settings.ajax)) {
9604 settings.ajax.url = url;
9605 }
9606 else {
9607 settings.ajax = url;
9608 }
9609 }, true);
9610 });
9611 register('ajax.url().load()', function (callback, resetPaging) {
9612 // Same as a reload, but makes sense to present it for easy access after
9613 // a url change
9614 return this.iterator('table', function (ctx) {
9615 __reload(ctx, resetPaging === false, callback);
9616 });
9617 });
9618
9619 function selectCells(settings, selector, opts) {
9620 var data = settings.data;
9621 var rows = selectorRowIndexes(settings, opts);
9622 var allCells;
9623 var row;
9624 var columns = settings.columns.length;
9625 var a, i, iLen, j, o, host;
9626 var run = function (s) {
9627 var fnSelector = typeof s === 'function';
9628 if (s === null || s === undefined || fnSelector) {
9629 // All cells and function selectors
9630 a = [];
9631 for (i = 0, iLen = rows.length; i < iLen; i++) {
9632 row = rows[i];
9633 for (j = 0; j < columns; j++) {
9634 o = {
9635 row: row,
9636 column: j
9637 };
9638 if (fnSelector) {
9639 // Selector - function
9640 host = data[row];
9641 if (s(o, getCellData(settings, row, j), host && host.cells ? host.cells[j] : null)) {
9642 a.push(o);
9643 }
9644 }
9645 else {
9646 // Selector - all
9647 a.push(o);
9648 }
9649 }
9650 }
9651 return a;
9652 }
9653 // Selector - index
9654 if (plainObject(s)) {
9655 // Valid cell index and its in the array of selectable rows
9656 return s.column !== undefined &&
9657 s.row !== undefined &&
9658 rows.indexOf(s.row) !== -1
9659 ? [s]
9660 : [];
9661 }
9662 // Only get the nodes if we get these far in the selector and need to
9663 // actually work with the cell nodes.
9664 if (!allCells) {
9665 let cells = removeEmpty(pluckOrder(data, rows, 'cells'));
9666 allCells = Dom.s(flatten([], cells));
9667 }
9668 // Selector - jQuery filtered cells
9669 let jqResult = allCells.filter(s).mapTo((el) => {
9670 return {
9671 // use a new object, in case someone changes the values
9672 row: el._DT_CellIndex.row,
9673 column: el._DT_CellIndex.column
9674 };
9675 });
9676 if (jqResult.length || !s.nodeName) {
9677 return jqResult;
9678 }
9679 // Otherwise the selector is a node, and there is one last option - the
9680 // element might be a child of an element which has dt-row and dt-column
9681 // data attributes
9682 let rowHost = Dom.s(s).closest('*[data-dt-row]');
9683 let columnHost = Dom.s(s).closest('*[data-dt-column]');
9684 return rowHost.count()
9685 ? [
9686 {
9687 row: parseInt(rowHost.attr('data-dt-row')),
9688 column: parseInt(columnHost.attr('data-dt-column'))
9689 }
9690 ]
9691 : [];
9692 };
9693 return selectorRun('cell', selector, run, settings, opts);
9694 }
9695 register('cells()', function (arg1, arg2, arg3) {
9696 // // Argument shifting
9697 let rowSelector = null;
9698 let columnSelector = null;
9699 let cellSelector;
9700 let opts;
9701 // Argument shifting
9702 if (plainObject(arg1)) {
9703 if (arg1.row === undefined) {
9704 // Selector modifier only overload
9705 opts = arg1;
9706 }
9707 else {
9708 // Cell selector as an index object
9709 cellSelector = arg1;
9710 opts = arg2;
9711 }
9712 }
9713 else if (plainObject(arg2) || arg2 === undefined) {
9714 // Cell selector overload
9715 cellSelector = arg1;
9716 opts = arg2;
9717 }
9718 else if (arg1 !== undefined) {
9719 // Row + column selector overload
9720 rowSelector = arg1;
9721 columnSelector = arg2;
9722 opts = arg3;
9723 }
9724 // Cell selector (if there is no column selector, then it must be)
9725 if (columnSelector === null) {
9726 return this.iterator('table', function (settings) {
9727 return selectCells(settings, cellSelector, selectorOpts(opts));
9728 });
9729 }
9730 // The default built in options need to apply to row and columns
9731 let internalOpts = opts
9732 ? {
9733 page: opts.page,
9734 order: opts.order,
9735 search: opts.search
9736 }
9737 : {};
9738 // Row + column selector
9739 let columns = this.columns(columnSelector, internalOpts);
9740 let rows = this.rows(rowSelector, internalOpts);
9741 let i, iLen, j, jen;
9742 let cellsNoOpts = this.iterator('table', function (settings, idx) {
9743 let a = [];
9744 for (i = 0, iLen = rows[idx].length; i < iLen; i++) {
9745 for (j = 0, jen = columns[idx].length; j < jen; j++) {
9746 a.push({
9747 row: rows[idx][i],
9748 column: columns[idx][j]
9749 });
9750 }
9751 }
9752 return a;
9753 }, true);
9754 // There is currently only one extension which uses a cell selector
9755 // extension It is a _major_ performance drag to run this if it isn't
9756 // needed, so this is an extension specific check at the moment
9757 let cells = opts && opts.selected
9758 ? this.cells(cellsNoOpts.toArray(), opts)
9759 : cellsNoOpts;
9760 assign(cells.selector, {
9761 cols: columnSelector,
9762 rows: rowSelector,
9763 opts: opts
9764 });
9765 return cells;
9766 });
9767 register('cells().every()', function (fn) {
9768 var opts = this.selector.opts;
9769 var counter = 0;
9770 return this.iterator('every', (settings, selectedIdx, tableIdx) => {
9771 let inst = this.cell(selectedIdx, opts);
9772 fn.call(inst, inst[0][0].row, inst[0][0].column, tableIdx, counter);
9773 counter++;
9774 });
9775 });
9776 registerPlural('cells().nodes()', 'cell().node()', function () {
9777 return this.iterator('cell', function (settings, row, column) {
9778 var data = settings.data[row];
9779 return data && data.cells ? data.cells[column] : undefined;
9780 }, true);
9781 });
9782 register('cells().data()', function () {
9783 return this.iterator('cell', function (settings, row, column) {
9784 return getCellData(settings, row, column);
9785 }, true);
9786 });
9787 registerPlural('cells().render()', 'cell().render()', function (type) {
9788 return this.iterator('cell', function (settings, row, column) {
9789 return getCellData(settings, row, column, type);
9790 }, true);
9791 });
9792 registerPlural('cells().indexes()', 'cell().index()', function () {
9793 return this.iterator('cell', function (settings, row, column) {
9794 return {
9795 row: row,
9796 column: column,
9797 columnVisible: columnIndexToVisible(settings, column)
9798 };
9799 }, true);
9800 });
9801 registerPlural('cells().invalidate()', 'cell().invalidate()', function (src) {
9802 return this.iterator('cell', function (settings, row, column) {
9803 invalidateRow(settings, row, src, column);
9804 });
9805 });
9806 register('cell()', function (rowSelector, columnSelector, opts) {
9807 return selectorFirst(this.cells(rowSelector, columnSelector, opts));
9808 });
9809 register('cell().data()', function (data) {
9810 var ctx = this.context;
9811 var cell = this[0];
9812 if (data === undefined) {
9813 // Get
9814 return ctx.length && cell.length
9815 ? getCellData(ctx[0], cell[0].row, cell[0].column)
9816 : undefined;
9817 }
9818 // Set
9819 setCellData(ctx[0], cell[0].row, cell[0].column, data);
9820 invalidateRow(ctx[0], cell[0].row, 'data', cell[0].column);
9821 return this;
9822 });
9823
9824 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9825 * Columns
9826 *
9827 * {integer} - column index (>=0 count from left, <0 count from right)
9828 * "{integer}:visIdx" - visible column index (i.e. translate to column index) (>=0 count from left, <0 count from right)
9829 * "{integer}:visible" - alias for {integer}:visIdx (>=0 count from left, <0 count from right)
9830 * "{string}:name" - column name
9831 * "{string}" - jQuery selector on column header nodes
9832 *
9833 */
9834 // can be an array of these items, comma separated list, or an array of comma
9835 // separated lists
9836 const __re_column_selector = /^(.*?):(name|title|visIdx|visible)$/;
9837 // r1 and r2 are redundant - but it means that the parameters match for the
9838 // iterator callback in columns().data()
9839 function columnData(settings, column, r1, r2, rows, type) {
9840 let a = [];
9841 for (let row = 0, iLen = rows.length; row < iLen; row++) {
9842 a.push(getCellData(settings, rows[row], column, type));
9843 }
9844 return a;
9845 }
9846 function columnHeader(settings, column, row) {
9847 var header = settings.header;
9848 var titleRow = settings.titleRow;
9849 var target = 0;
9850 if (row !== undefined) {
9851 target = row;
9852 }
9853 else if (titleRow === true) {
9854 // legacy orderCellsTop support
9855 target = 0;
9856 }
9857 else if (titleRow === false) {
9858 target = header.length - 1;
9859 }
9860 else if (titleRow !== null) {
9861 target = titleRow;
9862 }
9863 else {
9864 // Automatic - find the _last_ unique cell from the top that is not empty (last for
9865 // backwards compatibility)
9866 for (var i = 0; i < header.length; i++) {
9867 if (header[i][column].unique &&
9868 Dom
9869 .s(header[i][column].cell)
9870 .find('.dt-column-title')
9871 .text()) {
9872 target = i;
9873 }
9874 }
9875 if (target === null) {
9876 target = 0;
9877 }
9878 }
9879 return header[target][column].cell;
9880 }
9881 function columnHeaderCells(header) {
9882 var out = [];
9883 for (var i = 0; i < header.length; i++) {
9884 for (var j = 0; j < header[i].length; j++) {
9885 var cell = header[i][j].cell;
9886 if (!out.includes(cell)) {
9887 out.push(cell);
9888 }
9889 }
9890 }
9891 return out;
9892 }
9893 function selectColumns(settings, selector, opts) {
9894 var columns = settings.columns, names, titles, nodes = columnHeaderCells(settings.header);
9895 var run = function (s) {
9896 var selInt = intVal(s);
9897 // Selector - all
9898 if (s === '') {
9899 return range(columns.length);
9900 }
9901 // Selector - index
9902 if (selInt !== null) {
9903 return [
9904 selInt >= 0
9905 ? selInt // Count from left
9906 : columns.length + selInt // Count from right (+ because its a negative value)
9907 ];
9908 }
9909 // Selector = function
9910 if (typeof s === 'function') {
9911 var rows = selectorRowIndexes(settings, opts);
9912 return columns.map(function (col, idx) {
9913 return s(idx, columnData(settings, idx, 0, 0, rows), columnHeader(settings, idx))
9914 ? idx
9915 : null;
9916 });
9917 }
9918 // String selector
9919 var match = typeof s === 'string' ? s.match(__re_column_selector) : '';
9920 if (match) {
9921 switch (match[2]) {
9922 case 'visIdx':
9923 case 'visible':
9924 // Selector is a column index
9925 if (match[1] && match[1].match(/^\d+$/)) {
9926 var idx = parseInt(match[1], 10);
9927 // Visible index given, convert to column index
9928 if (idx < 0) {
9929 // Counting from the right
9930 var visColumns = columns.map(function (col, i) {
9931 return col.visible ? i : null;
9932 });
9933 return [visColumns[visColumns.length + idx]];
9934 }
9935 // Counting from the left
9936 return [visibleToColumnIndex(settings, idx)];
9937 }
9938 return columns.map(function (col, mapIdx) {
9939 // Not visible, can't match
9940 if (!col.visible) {
9941 return null;
9942 }
9943 if (col.responsiveVisible === false) {
9944 return null;
9945 }
9946 // Selector
9947 if (match && match[1]) {
9948 return Dom
9949 .s(nodes[mapIdx])
9950 .filter(match[1])
9951 .count() > 0
9952 ? mapIdx
9953 : null;
9954 }
9955 // `:visible` on its own
9956 return mapIdx;
9957 });
9958 case 'name':
9959 // Don't get names, unless needed, and only get once if it is
9960 if (!names) {
9961 names = pluck(columns, 'name');
9962 }
9963 // match by name. `names` is column index complete and in
9964 // order
9965 return names.map(function (name, i) {
9966 return match && name === match[1] ? i : null;
9967 });
9968 case 'title':
9969 if (!titles) {
9970 titles = pluck(columns, 'title');
9971 }
9972 // match by column title
9973 return titles.map(function (title, i) {
9974 return match && title === match[1] ? i : null;
9975 });
9976 default:
9977 return [];
9978 }
9979 }
9980 // Cell in the table body
9981 if (s.nodeName && s._DT_CellIndex) {
9982 return [s._DT_CellIndex.column];
9983 }
9984 // Selector on the TH elements for the columns
9985 var result = Dom
9986 .s(nodes)
9987 .filter(s)
9988 .mapTo(el => {
9989 return columnsFromHeader(el); // `nodes` is column index complete and in order
9990 })
9991 .flat()
9992 .sort(function (a, b) {
9993 return a - b;
9994 });
9995 if (result.length || !s.nodeName) {
9996 return result;
9997 }
9998 // Otherwise a node which might have a `dt-column` data attribute, or be
9999 // a child or such an element
10000 var host = Dom.s(s).closest('*[data-dt-column]');
10001 return host.count() ? [parseInt(host.attr('data-dt-column'))] : [];
10002 };
10003 var selected = selectorRun('column', selector, run, settings, opts);
10004 return opts.columnOrder && opts.columnOrder === 'index'
10005 ? selected.sort(function (a, b) {
10006 return a - b;
10007 })
10008 : selected; // implied
10009 }
10010 function setColumnVis(settings, column, vis) {
10011 var cols = settings.columns, col = cols[column], data = settings.data, cells, i, iLen, tr;
10012 // Get
10013 if (vis === undefined) {
10014 return col.visible;
10015 }
10016 // Set
10017 // No change
10018 if (col.visible === vis) {
10019 return false;
10020 }
10021 if (vis) {
10022 // Insert column
10023 // Need to decide if we should use appendChild or insertBefore
10024 var insertBefore = pluck(cols, 'visible').indexOf(true, column + 1);
10025 for (i = 0, iLen = data.length; i < iLen; i++) {
10026 let row = data[i];
10027 if (row) {
10028 tr = row.tr;
10029 cells = row.cells;
10030 if (tr) {
10031 // insertBefore can act like appendChild if 2nd arg is null
10032 tr.insertBefore(cells[column], cells[insertBefore] || null);
10033 }
10034 }
10035 }
10036 }
10037 else {
10038 // Remove column
10039 Dom.s(removeEmpty(pluck(settings.data, 'cells', column))).detach();
10040 }
10041 // Common actions
10042 col.visible = vis;
10043 colGroup(settings);
10044 return true;
10045 }
10046 register('columns()', function (arg1, arg2) {
10047 let selector;
10048 let opts;
10049 // argument shifting
10050 if (arg1 === undefined) {
10051 selector = '';
10052 }
10053 else if (plainObject(arg1)) {
10054 selector = '';
10055 arg2 = arg1;
10056 }
10057 else {
10058 selector = arg1;
10059 }
10060 opts = selectorOpts(arg2);
10061 let inst = this.iterator('table', settings => selectColumns(settings, selector, opts), true);
10062 // Want argument shifting here and in _row_selector?
10063 inst.selector.cols = selector;
10064 inst.selector.opts = opts;
10065 return inst;
10066 });
10067 register('columns().every()', function (fn) {
10068 var opts = this.selector.opts;
10069 var counter = 0;
10070 return this.iterator('every', (settings, selectedIdx, tableIdx) => {
10071 let inst = this.column(selectedIdx, opts);
10072 fn.call(inst, selectedIdx, tableIdx, counter);
10073 counter++;
10074 });
10075 });
10076 registerPlural('columns().header()', 'column().header()', function (row) {
10077 return this.iterator('column', function (settings, column) {
10078 return columnHeader(settings, column, row);
10079 }, true);
10080 });
10081 registerPlural('columns().footer()', 'column().footer()', function (row) {
10082 return this.iterator('column', function (settings, column) {
10083 var footer = settings.footer;
10084 if (!footer.length) {
10085 return null;
10086 }
10087 return settings.footer[row !== undefined ? row : 0][column]
10088 .cell;
10089 }, true);
10090 });
10091 registerPlural('columns().data()', 'column().data()', function () {
10092 return this.iterator('column-rows', columnData, true);
10093 });
10094 registerPlural('columns().render()', 'column().render()', function (type) {
10095 return this.iterator('column-rows', function (settings, column, i, j, rows) {
10096 return columnData(settings, column, i, j, rows, type);
10097 }, true);
10098 });
10099 registerPlural('columns().dataSrc()', 'column().dataSrc()', function () {
10100 return this.iterator('column', function (settings, column) {
10101 return settings.columns[column].data;
10102 }, true);
10103 });
10104 registerPlural('columns().init()', 'column().init()', function () {
10105 return this.iterator('column', function (settings, column) {
10106 return settings.columns[column];
10107 }, true);
10108 });
10109 registerPlural('columns().names()', 'column().name()', function () {
10110 return this.iterator('column', function (settings, column) {
10111 return settings.columns[column].name;
10112 }, true);
10113 });
10114 registerPlural('columns().nodes()', 'column().nodes()', function () {
10115 return this.iterator('column-rows', function (settings, column, i, j, rows) {
10116 return removeEmpty(pluckOrder(settings.data, rows, 'cells', column));
10117 }, true);
10118 });
10119 registerPlural('columns().titles()', 'column().title()', function (title, row) {
10120 return this.iterator('column', function (settings, column) {
10121 // Argument shifting
10122 if (typeof title === 'number') {
10123 row = title;
10124 title = undefined;
10125 }
10126 var span = Dom
10127 .s(this.column(column).header(row))
10128 .find('.dt-column-title');
10129 if (title !== undefined) {
10130 span.html(title);
10131 return this;
10132 }
10133 return span.html();
10134 }, true);
10135 });
10136 registerPlural('columns().types()', 'column().type()', function () {
10137 return this.iterator('column', function (settings, column) {
10138 var colObj = settings.columns[column];
10139 var type = colObj.type;
10140 // If the type was invalidated, then resolve it. This actually
10141 // does all columns at the moment. Would only happen once if
10142 // getting all column's data types.
10143 if (!type) {
10144 columnTypes(settings);
10145 type = colObj.type;
10146 }
10147 return type;
10148 }, true);
10149 });
10150 registerPlural('columns().visible()', 'column().visible()', function (vis, calc) {
10151 var that = this;
10152 var changed = [];
10153 var ret = this.iterator('column', function (settings, column) {
10154 if (vis === undefined) {
10155 return settings.columns[column].visible;
10156 } // else
10157 if (setColumnVis(settings, column, vis)) {
10158 changed.push(column);
10159 }
10160 });
10161 // Group the column visibility changes
10162 if (vis !== undefined) {
10163 this.iterator('table', function (settings) {
10164 // Redraw the header after changes
10165 drawHead(settings, settings.header);
10166 drawHead(settings, settings.footer);
10167 // Update colspan for no records display. Child rows and
10168 // extensions will use their own listeners to do this - only
10169 // need to update the empty table item here
10170 if (!settings.display.length) {
10171 Dom.s(settings.tbody)
10172 .find('td[colspan]')
10173 .attr('colspan', visibleColumns(settings));
10174 }
10175 saveState(settings);
10176 // Second loop once the first is done for events
10177 that.iterator('column', function (ctx, column) {
10178 if (changed.includes(column)) {
10179 callbackFire(ctx, null, 'column-visibility', [
10180 ctx,
10181 column,
10182 vis,
10183 calc
10184 ]);
10185 }
10186 });
10187 if (changed.length && (calc === undefined || calc)) {
10188 that.columns.adjust();
10189 }
10190 });
10191 }
10192 return ret;
10193 });
10194 registerPlural('columns().widths()', 'column().width()', function () {
10195 // Injects a fake row into the table for just a moment so the widths can
10196 // be read, regardless of colspan in the header and rows being present
10197 // in the body
10198 var columns = this.columns(':visible');
10199 var row = Dom
10200 .c('tr')
10201 .html('<td>' + Array(columns.count()).join('</td><td>') + '</td>');
10202 Dom.s(this.table().body()).append(row);
10203 var widths = [];
10204 var indexes = columns.indexes();
10205 row.children().each((el, idx) => {
10206 widths[indexes[idx]] = Dom.s(el).width('outer');
10207 });
10208 row.remove();
10209 return this.iterator('column', (settings, column) => {
10210 return widths[column] || 0;
10211 }, true);
10212 });
10213 registerPlural('columns().indexes()', 'column().index()', function (type) {
10214 return this.iterator('column', function (settings, column) {
10215 return type === 'visible'
10216 ? columnIndexToVisible(settings, column)
10217 : column;
10218 }, true);
10219 });
10220 register('columns.adjust()', function () {
10221 return this.iterator('table', function (settings) {
10222 // Force a column sizing to happen with a manual call - otherwise it
10223 // can skip if the size hasn't changed
10224 settings.containerWidth = -1;
10225 adjustColumnSizing(settings);
10226 }, true);
10227 });
10228 register('column.index()', function (type, idx) {
10229 if (this.context.length !== 0) {
10230 var ctx = this.context[0];
10231 if (type === 'fromVisible' || type === 'toData') {
10232 return visibleToColumnIndex(ctx, idx);
10233 }
10234 else if (type === 'fromData' || type === 'toVisible') {
10235 return columnIndexToVisible(ctx, idx);
10236 }
10237 }
10238 return -1;
10239 });
10240 register('column()', function (selector, opts) {
10241 return selectorFirst(this.columns(selector, opts));
10242 });
10243
10244 /**
10245 * Redraw the tables in the current context.
10246 */
10247 Api.register('draw()', function (paging) {
10248 return this.iterator('table', function (settings) {
10249 if (paging === 'page') {
10250 draw(settings);
10251 }
10252 else {
10253 if (typeof paging === 'string') {
10254 paging = paging === 'full-hold' ? false : true;
10255 }
10256 reDraw(settings, paging === false);
10257 }
10258 });
10259 });
10260
10261 register('order()', function (order, dir) {
10262 let ctx = this.context;
10263 let args = Array.prototype.slice.call(arguments);
10264 if (order === undefined) {
10265 // get
10266 return ctx.length !== 0 ? ctx[0].order : undefined;
10267 }
10268 // set
10269 if (typeof order === 'number' && typeof dir === 'string') {
10270 // Simple column / direction passed in
10271 order = [[order, dir]];
10272 }
10273 else if (args.length > 1) {
10274 // Arguments passed in (list of 1D arrays)
10275 order = args;
10276 }
10277 // otherwise a 2D array was passed in
10278 return this.iterator('table', function (settings) {
10279 let resolved = [];
10280 sortResolve(settings, resolved, order);
10281 settings.order = resolved;
10282 });
10283 });
10284 register('order.listener()', function (node, column, callback) {
10285 return this.iterator('table', function (settings) {
10286 sortAttachListener(settings, node, '', column, callback);
10287 });
10288 });
10289 register('order.fixed()', function (set) {
10290 if (!set) {
10291 var ctx = this.context;
10292 var fixed = ctx.length ? ctx[0].orderFixed : undefined;
10293 return Array.isArray(fixed) ? { pre: fixed } : fixed;
10294 }
10295 return this.iterator('table', function (settings) {
10296 settings.orderFixed = assignDeep({}, set);
10297 });
10298 });
10299 // Order by the selected column(s)
10300 register(['columns().order()', 'column().order()'], function (dir) {
10301 var that = this;
10302 if (!dir) {
10303 return this.iterator('column', function (settings, idx) {
10304 var sort = sortFlatten(settings);
10305 for (var i = 0, iLen = sort.length; i < iLen; i++) {
10306 if (sort[i].col === idx) {
10307 return sort[i].dir;
10308 }
10309 }
10310 return null;
10311 }, true);
10312 }
10313 else {
10314 return this.iterator('table', function (settings, i) {
10315 settings.order = that[i].map(function (col) {
10316 return [col, dir];
10317 });
10318 });
10319 }
10320 });
10321 registerPlural('columns().orderable()', 'column().orderable()', function (directions) {
10322 return this.iterator('column', function (settings, idx) {
10323 var col = settings.columns[idx];
10324 return directions ? col.orderSequence : col.orderable;
10325 }, true);
10326 });
10327
10328 /**
10329 * Set the page length
10330 *
10331 * @param ctx DataTables context
10332 * @param val Value to change to
10333 */
10334 function lengthChange(ctx, val) {
10335 let len = typeof val === 'string' ? parseInt(val, 10) : val;
10336 ctx.pageLength = len;
10337 lengthOverflow(ctx);
10338 // Fire length change event
10339 callbackFire(ctx, null, 'length', [ctx, len]);
10340 }
10341
10342 register('page()', function (action) {
10343 if (action === undefined) {
10344 return this.page.info().page; // not an expensive call
10345 }
10346 // else, have an action to take on all tables
10347 return this.iterator('table', function (settings) {
10348 pageChange(settings, action);
10349 });
10350 });
10351 register('page.info()', function () {
10352 var settings = this.context[0], start = settings.displayStart, len = settings.features.paging ? settings.pageLength : -1, visRecords = recordsDisplay(settings), all = len === -1;
10353 return {
10354 page: all ? 0 : Math.floor(start / len),
10355 pages: all ? 1 : Math.ceil(visRecords / len),
10356 start: start,
10357 end: displayEnd(settings),
10358 length: len,
10359 recordsTotal: recordsTotal(settings),
10360 recordsDisplay: visRecords,
10361 serverSide: dataSource(settings) === 'ssp'
10362 };
10363 });
10364 register('page.len()', function (len) {
10365 // Note that we can't call this function 'length()' because `length` is a
10366 // JavaScript property of functions which defines how many arguments the
10367 // function expects.
10368 if (len === undefined || len === null) {
10369 return this.context.length !== 0
10370 ? this.context[0].pageLength
10371 : undefined;
10372 }
10373 // else, set the page length
10374 return this.iterator('table', function (settings) {
10375 lengthChange(settings, len);
10376 });
10377 });
10378
10379 register('processing()', function (show) {
10380 return this.iterator('table', ctx => processingDisplay(ctx, show));
10381 });
10382
10383 // Add the state event handler in time for the initial draw to save state
10384 Dom.s(document).on('preInit.dt', function (e, context) {
10385 var api = new Api(context);
10386 api.on('stateSaveParams.DT', function (ev, settings, d) {
10387 // This could be more compact with the API, but it is a lot faster as a
10388 // simple internal loop
10389 var idFn = settings.rowIdFn;
10390 var rows = settings.displayMaster;
10391 var ids = [];
10392 for (var i = 0; i < rows.length; i++) {
10393 var rowIdx = rows[i];
10394 var row = settings.data[rowIdx];
10395 if (row.detailsShow) {
10396 ids.push('#' + idFn(row.data));
10397 }
10398 }
10399 d.childRows = ids;
10400 });
10401 // For future state loads (e.g. with StateRestore)
10402 api.on('stateLoaded.DT', function (ev, settings, state) {
10403 detailsStateLoad(api, state);
10404 });
10405 });
10406 // But initial details can wait until the end
10407 Dom.s(document).on('plugin-init.dt', function (e, context) {
10408 var api = context.api;
10409 // And the initial load state
10410 detailsStateLoad(api, api.state.loaded());
10411 });
10412 function detailsStateLoad(api, state) {
10413 if (state && state.childRows) {
10414 api.rows(state.childRows.map(function (id) {
10415 // Escape any `:` characters from the row id. Accounts for
10416 // already escaped characters.
10417 return id.replace(/([^:\\]*(?:\\.[^:\\]*)*):/g, '$1\\:');
10418 })).every(function () {
10419 callbackFire(api.settings()[0], null, 'requestChild', [this]);
10420 });
10421 }
10422 }
10423 function detailsAdd(ctx, row, data, klass) {
10424 if (!row) {
10425 return;
10426 }
10427 // Convert to array of TR elements
10428 var rows = [];
10429 var addRow = function (r, k) {
10430 // Recursion to allow for arrays of jQuery objects
10431 if (Array.isArray(r) || util.is.jquery(r)) {
10432 for (var i = 0, iLen = r.length; i < iLen; i++) {
10433 addRow(r[i], k);
10434 }
10435 return;
10436 }
10437 // If we get a TR element, then just add it directly - up to the dev
10438 // to add the correct number of columns etc
10439 if (r.nodeName && r.nodeName.toLowerCase() === 'tr') {
10440 r.setAttribute('data-dt-row', row.idx);
10441 rows.push(r);
10442 }
10443 else {
10444 // Otherwise create a row with a wrapper
10445 let td = Dom.c('td').classAdd(k);
10446 let created = Dom
10447 .c('tr')
10448 .append(td)
10449 .attr('data-dt-row', row.idx)
10450 .classAdd(k);
10451 if (r.nodeName) {
10452 td.append(r);
10453 }
10454 else {
10455 td.html(r);
10456 }
10457 td.get(0).colSpan = visibleColumns(ctx);
10458 rows.push(created.get(0));
10459 }
10460 };
10461 addRow(data, klass);
10462 if (row.details) {
10463 row.details.detach();
10464 }
10465 row.details = Dom.s(rows);
10466 // If the children were already shown, that state should be retained
10467 if (row.detailsShow && row.tr) {
10468 row.details.insertAfter(row.tr);
10469 }
10470 }
10471 // Make state saving of child row details async to allow them to be batch
10472 // processed
10473 var detailsState = util.throttle(function (ctx) {
10474 saveState(ctx[0]);
10475 }, 500);
10476 function detailsRemove(api, idx) {
10477 var ctx = api.context;
10478 if (ctx.length) {
10479 var row = ctx[0].data[idx !== undefined ? idx : api[0]];
10480 if (row && row.details) {
10481 row.details.detach();
10482 row.detailsShow = undefined;
10483 row.details = undefined;
10484 Dom.s(row.tr).classRemove('dt-hasChild');
10485 detailsState(ctx);
10486 }
10487 }
10488 }
10489 function detailsDisplay(api, show) {
10490 var ctx = api.context;
10491 if (ctx.length && api.length) {
10492 var row = ctx[0].data[api[0]];
10493 if (row && row.details) {
10494 row.detailsShow = show;
10495 if (show && row.tr) {
10496 row.details.insertAfter(row.tr);
10497 Dom.s(row.tr).classAdd('dt-hasChild');
10498 }
10499 else if (!show) {
10500 row.details.detach();
10501 Dom.s(row.tr).classRemove('dt-hasChild');
10502 }
10503 callbackFire(ctx[0], null, 'childRow', [show, api.row(api[0])]);
10504 detailsEvents(ctx[0]);
10505 detailsState(ctx);
10506 }
10507 }
10508 }
10509 function detailsEvents(settings) {
10510 var api = new Api(settings);
10511 var namespace = '.dt.DT_details';
10512 var drawEvent = 'draw' + namespace;
10513 var colvisEvent = 'column-sizing' + namespace;
10514 var destroyEvent = 'destroy' + namespace;
10515 var data = settings.data;
10516 api.off(drawEvent + ' ' + colvisEvent + ' ' + destroyEvent);
10517 if (util.array.pluck(data, 'details').length > 0) {
10518 // On each draw, insert the required elements into the document
10519 api.on(drawEvent, function (e, ctx) {
10520 if (settings !== ctx) {
10521 return;
10522 }
10523 api.rows({ page: 'current' })
10524 .eq(0)
10525 .each(function (idx) {
10526 // Internal data grab
10527 var row = data[idx];
10528 if (row && row.detailsShow && row.details && row.tr) {
10529 row.details.insertAfter(row.tr);
10530 }
10531 });
10532 });
10533 // Column visibility change - update the colspan
10534 api.on(colvisEvent, function (e, ctx) {
10535 if (settings !== ctx) {
10536 return;
10537 }
10538 // Update the colspan for the details rows (note, only if it already
10539 // has a colspan)
10540 var row, visible = visibleColumns(ctx);
10541 for (var i = 0, iLen = data.length; i < iLen; i++) {
10542 row = data[i];
10543 if (row && row.details) {
10544 row.details.each(function (el) {
10545 var td = Dom.s(el).children('td');
10546 if (td.count() == 1) {
10547 td.attr('colspan', visible);
10548 }
10549 });
10550 }
10551 }
10552 });
10553 // Table destroyed - nuke any child rows
10554 api.on(destroyEvent, function (e, ctx) {
10555 if (settings !== ctx) {
10556 return;
10557 }
10558 for (var i = 0, iLen = data.length; i < iLen; i++) {
10559 let d = data[i];
10560 if (d && d.details) {
10561 detailsRemove(api, i);
10562 }
10563 }
10564 });
10565 }
10566 }
10567 // Strings for the method names to help minification
10568 var _emp = '';
10569 var _child_obj = _emp + 'row().child';
10570 var _child_mth = _child_obj + '()';
10571 // data can be:
10572 // tr
10573 // string
10574 // jQuery or array of any of the above
10575 Api.register(_child_mth, function (data, klass) {
10576 var _a;
10577 var ctx = this.context;
10578 if (data === undefined) {
10579 // get
10580 let details = ctx.length && this.length && ctx[0].data[this[0]]
10581 ? (_a = ctx[0].data[this[0]]) === null || _a === void 0 ? void 0 : _a.details
10582 : undefined;
10583 return details;
10584 }
10585 else if (data === true) {
10586 // show
10587 this.child.show();
10588 }
10589 else if (data === false) {
10590 // remove
10591 detailsRemove(this);
10592 }
10593 else if (ctx.length && this.length) {
10594 // set
10595 detailsAdd(ctx[0], ctx[0].data[this[0]], data, klass);
10596 }
10597 return this.inst(this.context, this);
10598 });
10599 Api.register([
10600 _child_obj + '.show()',
10601 _child_mth + '.show()' // only when `child()` was called with parameters
10602 ], function () {
10603 // it returns an object and this method is not executed)
10604 detailsDisplay(this, true);
10605 return this;
10606 });
10607 Api.register([
10608 _child_obj + '.hide()',
10609 _child_mth + '.hide()' // only when `child()` was called with parameters
10610 ], function () {
10611 // it returns an object and this method is not executed)
10612 detailsDisplay(this, false);
10613 return this;
10614 });
10615 Api.register([
10616 _child_obj + '.remove()',
10617 _child_mth + '.remove()' // only when `child()` was called with parameters
10618 ], function () {
10619 // it returns an object and this method is not executed)
10620 detailsRemove(this);
10621 return this;
10622 });
10623 Api.register(_child_obj + '.isShown()', function () {
10624 var ctx = this.context;
10625 if (ctx.length && this.length && ctx[0].data[this[0]]) {
10626 // detailsShown as false or undefined will fall through to return false
10627 return ctx[0].data[this[0]].detailsShow || false;
10628 }
10629 return false;
10630 });
10631
10632 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10633 * Rows
10634 *
10635 * {} - no selector - use all available rows
10636 * {integer} - row data index
10637 * {node} - TR node
10638 * {string} - jQuery selector to apply to the TR elements
10639 * {array} - jQuery array of nodes, or simply an array of TR nodes
10640 *
10641 */
10642 function selectRows(settings, selector, opts) {
10643 var rows;
10644 var run = function (sel) {
10645 var selInt = util.conv.intVal(sel);
10646 var data = settings.data;
10647 // Short cut - selector is a number and no options provided (default is
10648 // all records, so no need to check if the index is in there, since it
10649 // must be - dev error if the index doesn't exist).
10650 if (selInt !== null && !opts) {
10651 return [selInt];
10652 }
10653 if (!rows) {
10654 rows = selectorRowIndexes(settings, opts);
10655 }
10656 if (selInt !== null && rows.indexOf(selInt) !== -1) {
10657 // Selector - integer
10658 return [selInt];
10659 }
10660 else if (sel === null || sel === undefined || sel === '') {
10661 // Selector - none
10662 return rows;
10663 }
10664 // Selector - function
10665 if (typeof sel === 'function') {
10666 return rows.map(function (idx) {
10667 var row = data[idx];
10668 return row && sel(idx, row.data, row.tr) ? idx : null;
10669 });
10670 }
10671 // Selector - node
10672 if (sel.nodeName) {
10673 var rowIdx = sel._DT_RowIndex; // Property added by DT for fast lookup
10674 var cellIdx = sel._DT_CellIndex;
10675 var row;
10676 if (rowIdx !== undefined) {
10677 // Make sure that the row is actually still present in the table
10678 row = data[rowIdx];
10679 return row && row.tr === sel ? [rowIdx] : [];
10680 }
10681 else if (cellIdx) {
10682 row = data[cellIdx.row];
10683 return row && row.tr === sel.parentNode ? [cellIdx.row] : [];
10684 }
10685 else {
10686 var host = Dom.s(sel).closest('*[data-dt-row]');
10687 return host.count() ? [parseInt(host.attr('data-dt-row'))] : [];
10688 }
10689 }
10690 // ID selector. Want to always be able to select rows by id, regardless
10691 // of if the tr element has been created or not, so can't rely upon
10692 // jQuery here - hence a custom implementation. This does not match
10693 // Sizzle's fast selector or HTML4 - in HTML5 the ID can be anything,
10694 // but to select it using a CSS selector engine (like Sizzle or
10695 // querySelect) it would need to need to be escaped for some characters.
10696 // DataTables simplifies this for row selectors since you can select
10697 // only a row. A # indicates an id any anything that follows is the id -
10698 // unescaped.
10699 if (typeof sel === 'string') {
10700 if (sel.charAt(0) === '#') {
10701 // get row index from id
10702 var rowObj = settings.ids[sel.replace(/^#/, '')];
10703 if (rowObj !== undefined) {
10704 return [rowObj.idx];
10705 }
10706 // need to fall through to selector in case there is DOM id that
10707 // matches
10708 }
10709 else if (sel.match(/^(tr)?:eq\(\d+\)$/)) {
10710 // :eq() selector to get a row based on its position in the
10711 // selectable rows.
10712 let idx = parseInt(sel.replace(/[^\d]/g, ''));
10713 return rows[idx] !== undefined ? [rows[idx]] : [];
10714 }
10715 }
10716 // Get nodes in the order from the `rows` array with null values removed
10717 var nodes = util.array.removeEmpty(util.array.pluckOrder(settings.data, rows, 'tr'));
10718 // Selector - selector string, array of nodes or jQuery object.
10719 return Dom.s(nodes)
10720 .filter(sel)
10721 .mapTo((el) => el._DT_RowIndex);
10722 };
10723 var matched = selectorRun('row', selector, run, settings, opts);
10724 if (opts.order === 'current' || opts.order === 'applied') {
10725 sortDisplay(settings, matched);
10726 }
10727 return matched;
10728 }
10729 register('rows()', function (arg1, arg2) {
10730 let opts;
10731 let selector;
10732 // argument shifting
10733 if (arg1 === undefined) {
10734 // All rows - no selector or modifier
10735 selector = '';
10736 }
10737 else if (util.is.plainObject(arg1)) {
10738 // Arg1 is modifier overload
10739 selector = '';
10740 opts = arg1;
10741 }
10742 else {
10743 selector = arg1;
10744 opts = arg2;
10745 }
10746 opts = selectorOpts(opts);
10747 var inst = this.iterator('table', function (settings) {
10748 return selectRows(settings, selector, opts);
10749 }, true);
10750 // Want argument shifting here and in row_selector?
10751 inst.selector.rows = selector;
10752 inst.selector.opts = opts;
10753 return inst;
10754 });
10755 register('rows().every()', function (fn) {
10756 var opts = this.selector.opts;
10757 var counter = 0;
10758 return this.iterator('every', (settings, selectedIdx, tableIdx) => {
10759 let inst = this.row(selectedIdx, opts);
10760 fn.call(inst, selectedIdx, tableIdx, counter);
10761 counter++;
10762 });
10763 });
10764 register('rows().nodes()', function () {
10765 return this.iterator('row', function (settings, row) {
10766 var _a;
10767 return ((_a = settings.data[row]) === null || _a === void 0 ? void 0 : _a.tr) || undefined;
10768 }, true);
10769 });
10770 register('rows().data()', function () {
10771 return this.iterator(true, 'rows', function (settings, rows) {
10772 return util.array.pluckOrder(settings.data, rows, 'data');
10773 }, true);
10774 });
10775 registerPlural('rows().invalidate()', 'row().invalidate()', function (src) {
10776 return this.iterator('row', function (settings, row) {
10777 invalidateRow(settings, row, src);
10778 });
10779 });
10780 registerPlural('rows().indexes()', 'row().index()', function () {
10781 return this.iterator('row', function (settings, row) {
10782 return row;
10783 }, true);
10784 });
10785 registerPlural('rows().ids()', 'row().id()', function (hash) {
10786 var _a;
10787 var a = [];
10788 var context = this.context;
10789 // `iterator` will drop undefined values, but in this case we want them
10790 for (var i = 0, iLen = context.length; i < iLen; i++) {
10791 for (var j = 0, jen = this[i].length; j < jen; j++) {
10792 var id = context[i].rowIdFn((_a = context[i].data[this[i][j]]) === null || _a === void 0 ? void 0 : _a.data);
10793 a.push((hash === true ? '#' : '') + id);
10794 }
10795 }
10796 return this.inst(context, a);
10797 });
10798 registerPlural('rows().remove()', 'row().remove()', function () {
10799 this.iterator('row', function (settings, row) {
10800 var data = settings.data;
10801 var rowData = data[row];
10802 // Delete from the display arrays
10803 var idx = settings.displayMaster.indexOf(row);
10804 if (idx !== -1) {
10805 settings.displayMaster.splice(idx, 1);
10806 }
10807 // For server-side processing tables - subtract the deleted row from
10808 // the count
10809 if (settings.recordsDisplay > 0) {
10810 settings.recordsDisplay--;
10811 }
10812 // Check for an 'overflow' they case for displaying the table
10813 lengthOverflow(settings);
10814 // Remove the row's ID reference if there is one
10815 var id = settings.rowIdFn(rowData === null || rowData === void 0 ? void 0 : rowData.data);
10816 if (id !== undefined) {
10817 delete settings.ids[id];
10818 }
10819 data[row] = null;
10820 });
10821 return this;
10822 });
10823 register('rows.add()', function (rows) {
10824 var newRows = this.iterator('table', function (settings) {
10825 var row, i, iLen;
10826 var out = [];
10827 for (i = 0, iLen = rows.length; i < iLen; i++) {
10828 row = rows[i];
10829 if (row.nodeName && row.nodeName.toUpperCase() === 'TR') {
10830 out.push(addTr(settings, Dom.s(row))[0]);
10831 }
10832 else {
10833 out.push(addData(settings, row));
10834 }
10835 }
10836 return out;
10837 }, true);
10838 // Return an Api.rows() extended instance, so rows().nodes() etc can be used
10839 var modRows = this.rows(-1);
10840 modRows.pop();
10841 arrayApply(modRows, newRows);
10842 return modRows;
10843 });
10844 register('row()', function (selector, opts) {
10845 return selectorFirst(this.rows(selector, opts));
10846 });
10847 register('row().data()', function (data) {
10848 var _a;
10849 var ctx = this.context;
10850 if (data === undefined) {
10851 // Get
10852 return ctx.length && this.length && this[0].length
10853 ? (_a = ctx[0].data[this[0]]) === null || _a === void 0 ? void 0 : _a.data
10854 : undefined;
10855 }
10856 // Set
10857 var row = ctx[0].data[this[0]];
10858 row.data = data;
10859 // If the DOM has an id, and the data source is an array
10860 if (Array.isArray(data) && row.tr && row.tr.id) {
10861 util.set(ctx[0].rowId)(data, row.tr.id);
10862 }
10863 // Automatically invalidate
10864 invalidateRow(ctx[0], this[0][0], 'data');
10865 return this;
10866 });
10867 register('row().node()', function () {
10868 var ctx = this.context;
10869 if (ctx.length && this.length && this[0].length) {
10870 var row = ctx[0].data[this[0]];
10871 if (row && row.tr) {
10872 return row.tr;
10873 }
10874 }
10875 return null;
10876 });
10877 register('row.add()', function (row) {
10878 // Allow an array-like object to be passed in - only a single row is added
10879 // from it though - the first element in the set
10880 if (row && row.fn && row.length) {
10881 row = row[0];
10882 }
10883 var rows = this.iterator('table', function (settings) {
10884 // New column could cause a change in the cached column properties such
10885 // as type and width.
10886 invalidColumn(settings);
10887 if (row.nodeName && row.nodeName.toUpperCase() === 'TR') {
10888 return addTr(settings, Dom.s(row))[0];
10889 }
10890 return addData(settings, row);
10891 });
10892 // Return an Api.rows() extended instance, with the newly added row selected
10893 return this.row(rows[0]);
10894 });
10895
10896 register('search()', function (input, regex, smart, caseInsen) {
10897 if (input === undefined) {
10898 let ctx = this.context;
10899 // get
10900 if (ctx.length === 0) {
10901 return;
10902 }
10903 return ctx[0].searches['*'].search;
10904 }
10905 // set
10906 return this.iterator('table', function (ctx) {
10907 if (!ctx.features.searching) {
10908 return;
10909 }
10910 let target = ctx.searches['*'];
10911 if (!target) {
10912 target = create$2();
10913 }
10914 if (typeof regex === 'object') {
10915 // New style object of options
10916 assign(target, regex);
10917 }
10918 else {
10919 // Compat for the old options
10920 assign(target, {
10921 regex: regex === null ? false : regex,
10922 smart: smart === null ? true : smart,
10923 caseInsensitive: caseInsen === null ? true : caseInsen
10924 });
10925 }
10926 target.search = input;
10927 ctx.searches['*'] = target;
10928 filterComplete(ctx);
10929 });
10930 });
10931 register('search.fixed()', function (name, search, options) {
10932 var ret = this.iterator(true, 'table', function (settings) {
10933 var _a;
10934 var fixed = settings.searchesFixed['*'];
10935 if (!name) {
10936 return Object.keys(fixed);
10937 }
10938 else if (search === undefined) {
10939 return (_a = fixed[name]) === null || _a === void 0 ? void 0 : _a.search;
10940 }
10941 else if (search === null) {
10942 delete fixed[name];
10943 }
10944 else {
10945 let target = fixed[name];
10946 if (!target || !util.is.plainObject(target)) {
10947 target = create$2();
10948 }
10949 if (options) {
10950 assign(target, options);
10951 }
10952 target.search = search;
10953 fixed[name] = target;
10954 }
10955 return this;
10956 });
10957 return name !== undefined && search === undefined ? ret[0] : ret;
10958 });
10959 register(['columns().search()', 'column().search()'], function (input, regex, smart, caseInsen) {
10960 var _a;
10961 if (input === undefined) {
10962 let name = this[0].join(',');
10963 return this.context.length
10964 ? ((_a = this.context[0].searches[name]) === null || _a === void 0 ? void 0 : _a.search) || ''
10965 : '';
10966 }
10967 return this.iterator('columns', function (ctx, columns) {
10968 let colIdxs = columns.join(',');
10969 let target = ctx.searches[colIdxs];
10970 if (!target) {
10971 target = create$2();
10972 }
10973 // Delete the search for custom grouping types if removing
10974 if ((input === '' || input === null) && columns.length > 1) {
10975 delete ctx.searches[colIdxs];
10976 return;
10977 }
10978 if (typeof regex === 'object') {
10979 // New style object of options
10980 assign(target, regex);
10981 }
10982 else {
10983 // Compat for the old options
10984 assign(target, {
10985 regex: regex === null ? false : regex,
10986 smart: smart === null ? true : smart,
10987 caseInsensitive: caseInsen === null ? true : caseInsen
10988 });
10989 }
10990 target.search = input;
10991 target.columns = columns.slice();
10992 ctx.searches[colIdxs] = target;
10993 filterComplete(ctx);
10994 });
10995 });
10996 register(['columns().search.fixed()', 'column().search.fixed()'], function (name, search, options) {
10997 // No name, just return the names of the fixed searches applied to these
10998 // columns
10999 if (!name) {
11000 return this.iterator(true, 'columns', function (settings, columns) {
11001 let colIdxs = columns.join(',');
11002 let fixed = settings.searchesFixed[colIdxs];
11003 return fixed ? Object.keys(fixed) : [];
11004 });
11005 }
11006 // Get the value from an existing search
11007 if (search === undefined) {
11008 if (!this.context.length) {
11009 return undefined;
11010 }
11011 else {
11012 let colIdxs = this[0].join(',');
11013 let fixed = this.context[0].searchesFixed[colIdxs];
11014 return fixed && fixed[name] ? fixed[name].search : undefined;
11015 }
11016 }
11017 // Set a search, possibly with options
11018 return this.iterator(true, 'columns', function (settings, columns) {
11019 let colIdxs = columns.join(',');
11020 let fixed = settings.searchesFixed[colIdxs];
11021 if (!fixed) {
11022 fixed = {};
11023 settings.searchesFixed[colIdxs] = fixed;
11024 }
11025 if (search === null) {
11026 delete fixed[name];
11027 }
11028 else {
11029 let target = fixed[name];
11030 if (!target || !util.is.plainObject(target)) {
11031 target = create$2();
11032 }
11033 if (options) {
11034 assign(target, options);
11035 }
11036 target.search = search;
11037 target.columns = columns;
11038 fixed[name] = target;
11039 }
11040 return this;
11041 });
11042 });
11043
11044 register('state()', function (set, ignoreTime = true) {
11045 // getter
11046 if (!set) {
11047 return this.context.length ? this.context[0].stateSaved : null;
11048 }
11049 let setMutate = assignDeep({}, set);
11050 // setter
11051 return this.iterator('table', function (settings) {
11052 implementState(settings, setMutate, ignoreTime, function () { });
11053 });
11054 });
11055 register('state.clear()', function () {
11056 return this.iterator('table', function (settings) {
11057 // Save an empty object
11058 settings.stateSaveCallback.call(settings.instance, settings, {});
11059 });
11060 });
11061 register('state.loaded()', function () {
11062 return this.context.length ? this.context[0].stateLoaded : null;
11063 });
11064 register('state.save()', function () {
11065 return this.iterator('table', function (settings) {
11066 saveState(settings);
11067 });
11068 });
11069
11070 /**
11071 * Selector for HTML tables. Apply the given selector to the give array of
11072 * DataTables settings objects.
11073 *
11074 * @param selector Selector string or integer
11075 * @param a Array of DataTables settings objects to be filtered
11076 * @return Selected table notes
11077 */
11078 function table_selector(selector, a) {
11079 if (Array.isArray(selector)) {
11080 var result = [];
11081 selector.forEach(function (sel) {
11082 var inner = table_selector(sel, a);
11083 arrayApply(result, inner);
11084 });
11085 return result.filter(item => !!item);
11086 }
11087 // Integer is used to pick out a table by index
11088 if (typeof selector === 'number') {
11089 return [a[selector]];
11090 }
11091 // Perform a selector on the table nodes
11092 var nodes = a.map(function (el) {
11093 return el.table;
11094 });
11095 return Dom
11096 .s(nodes)
11097 .filter(selector)
11098 .mapTo(el => {
11099 // Need to translate back from the table node to the settings
11100 var idx = nodes.indexOf(el);
11101 return a[idx];
11102 });
11103 }
11104 register('tables()', function (selector) {
11105 // A new instance is created if there was a selector specified
11106 return selector !== undefined && selector !== null
11107 ? this.inst(table_selector(selector, this.context))
11108 : this.inst(this.context);
11109 });
11110 register('table()', function (selector) {
11111 return selectorFirst(this.tables(selector));
11112 });
11113 // Common methods, combined to reduce size
11114 [
11115 ['nodes', 'node', 'table'],
11116 ['body', 'body', 'tbody'],
11117 ['header', 'header', 'thead'],
11118 ['footer', 'footer', 'tfoot']
11119 ].forEach(function (item) {
11120 registerPlural('tables().' + item[0] + '()', 'table().' + item[1] + '()', function () {
11121 return this.iterator('table', ctx => ctx[item[2]], true);
11122 });
11123 });
11124 // Structure methods
11125 ['header', 'footer'].forEach(function (item) {
11126 register('table().' + item + '.structure()', function (selector) {
11127 var indexes = this.columns(selector).indexes().flatten().toArray();
11128 var ctx = this.context[0];
11129 var structure = headerLayout(ctx, ctx[item], indexes);
11130 // The structure is in column index order - but from this method we
11131 // want the return to be in the columns() selector API order. In
11132 // order to do that we need to map from one form to the other
11133 var orderedIndexes = indexes.slice().sort(function (a, b) {
11134 return a - b;
11135 });
11136 return structure.map(function (row) {
11137 return indexes.map(function (colIdx) {
11138 return row[orderedIndexes.indexOf(colIdx)];
11139 });
11140 });
11141 });
11142 });
11143 registerPlural('tables().containers()', 'table().container()', function () {
11144 return this.iterator('table', function (ctx) {
11145 return ctx.tableWrapper;
11146 }, true);
11147 });
11148 register('tables().every()', function (fn) {
11149 return this.iterator('table', (s, i) => {
11150 fn.call(this.table(i), i);
11151 });
11152 });
11153 register('caption()', function (value, side) {
11154 var context = this.context;
11155 // Getter - return existing node's content
11156 if (value === undefined) {
11157 var node = context[0].captionNode;
11158 return node && context.length ? node.innerHTML : null;
11159 }
11160 return this.iterator('table', function (ctx) {
11161 var table = Dom.s(ctx.table);
11162 var caption = Dom.s(ctx.captionNode);
11163 var container = Dom.s(ctx.tableWrapper);
11164 // Create the node if it doesn't exist yet
11165 if (!caption.count()) {
11166 caption = Dom.c('caption').html(value);
11167 ctx.captionNode = caption.get(0);
11168 // If side isn't set, we need to insert into the document to let
11169 // the CSS decide so we can read it back, otherwise there is no
11170 // way to know if the CSS would put it top or bottom for
11171 // scrolling
11172 if (!side) {
11173 table.prepend(caption);
11174 side = caption.css('caption-side');
11175 }
11176 }
11177 caption.html(value);
11178 if (side) {
11179 caption.css('caption-side', side);
11180 caption.get(0)._captionSide = side;
11181 }
11182 if (container.find('div.dt-scroll').count()) {
11183 var selector = side === 'top' ? 'head' : 'foot';
11184 container
11185 .find('div.dt-scroll-' + selector + ' table')
11186 .prepend(caption);
11187 }
11188 else {
11189 table.prepend(caption);
11190 }
11191 }, true);
11192 });
11193 register('caption.node()', function () {
11194 var ctx = this.context;
11195 return ctx.length ? ctx[0].captionNode : null;
11196 });
11197
11198 /**
11199 * What's this!? "DataTables Plus" is a commercial set of extensions for
11200 * DataTables, such as Editor, and the functions in this file allow a license
11201 * key to be provided (`DataTable.key(...)`) to unlock those features.
11202 *
11203 * This is the modal that I've selected to make DataTables sustainable, open
11204 * source core, with some commercial extensions available.
11205 *
11206 * Please support DataTables and open source by purchasing a Plus license from
11207 * https://datatables.net/plus .
11208 */
11209 let _ready = false;
11210 let _notice;
11211 let _processingKey = false;
11212 let _delayedReleaseDate = null;
11213 let _delayedSoftware = null;
11214 const _licenseInfo = {
11215 developers: 0,
11216 type: null,
11217 expires: null,
11218 valid: null
11219 };
11220 const _wm = Dom.c('div');
11221 const _publicKey = 'BE1A9w9D9U/4s4/TogY+1sW/dLJ8IquzK1PmV70J93ZTIvXMZ0eV2NAb52ntpgwVFySSB2fOI7geLNO737rQAyo=';
11222 /**
11223 * Convert a base64 string to a binary array
11224 *
11225 * @param b64 Source string
11226 * @returns Array
11227 */
11228 function b64ToBuf(b64) {
11229 return Uint8Array.from(atob(b64), c => c.charCodeAt(0));
11230 }
11231 /**
11232 * Logic to check the trial and plus license expiry and display messages if
11233 * needed. There is particular consideration for checking a release date of
11234 * software, as the license for DataTables Plus is perpetual for the version
11235 * purchased, and it shouldn't show a message for the purchased version ever.
11236 *
11237 * @param releaseDate The date the software was released on.
11238 * @param software The software name being validated. Can be null for a general
11239 * "Plus" check.
11240 * @returns true if valid, false otherwise
11241 */
11242 function check(releaseDate, software) {
11243 let expires = _licenseInfo.expires;
11244 if (!getSubtle()) {
11245 noticePrep('Unable to validate license key');
11246 noticeDisplay();
11247 }
11248 else if (_licenseInfo.valid === false) {
11249 noticePrep('License key invalid');
11250 noticeDisplay();
11251 }
11252 else if (_licenseInfo.type === 'trial') {
11253 // Trail is for plus, so the software type isn't taken into account
11254 let remaining = expires
11255 ? Math.ceil((expires.getTime() - new Date().getTime()) / 86400000)
11256 : -1;
11257 if (remaining < 0) {
11258 // Trial expires
11259 consoleMsg('Your trial has now expired - https://datatables.net/plus', 'warn');
11260 noticePrep('Trial expired');
11261 noticeDisplay();
11262 return false;
11263 }
11264 else {
11265 // Let the user know when it is going to expire with a console
11266 // message.
11267 consoleMsg('Your trial expires in ' +
11268 remaining +
11269 ' day' +
11270 (remaining === 1 ? '' : 's'));
11271 return true;
11272 }
11273 }
11274 else if (software === null) {
11275 // Validating the key only - there hasn't been any specific software
11276 // calling the `plus` parameter yet. The key is good, so carry on.
11277 return true;
11278 }
11279 else if (
11280 // Checking if the build version can be used with this key
11281 _licenseInfo.type === 'plus' ||
11282 (_licenseInfo.type === 'editor' && software === 'editor')) {
11283 if (!expires || new Date(releaseDate) > expires) {
11284 noticePrep('Upgrade required for this version');
11285 noticeDisplay();
11286 return false;
11287 }
11288 return true;
11289 }
11290 else if (_licenseInfo.type === 'editor' && software !== 'editor') {
11291 // Editor specific license
11292 noticePrep('License for Editor only. Upgrade for Plus');
11293 noticeDisplay();
11294 return false;
11295 }
11296 noticePrep();
11297 noticeDisplay();
11298 return false;
11299 }
11300 /**
11301 * Common log message handling
11302 *
11303 * @param msg Message to show
11304 * @param level Log level
11305 */
11306 function consoleMsg(msg, level = 'log') {
11307 let fn = level === 'log' ? console.log : console.warn;
11308 fn('%cDataTables Plus%c ' + msg, 'background: #007bff; color: #fff; padding: 2px 5px;', 'color: inherit;');
11309 }
11310 /**
11311 * Set the DataTables Plus key to use
11312 *
11313 * @param key DataTables Plus key - obtain from https://datatables.net/account .
11314 */
11315 const key = function (key) {
11316 _processingKey = true;
11317 // Run the verification of the key
11318 verify(key)
11319 .then(result => {
11320 _processingKey = false;
11321 check(_delayedReleaseDate, _delayedSoftware);
11322 })
11323 .catch(() => {
11324 _processingKey = false;
11325 check(_delayedReleaseDate, _delayedSoftware);
11326 });
11327 };
11328 /**
11329 * Build the notice
11330 *
11331 * @returns
11332 */
11333 function noticePrep(text) {
11334 if (!_ready) {
11335 let shadow = _wm[0].attachShadow({ mode: 'closed' });
11336 let notice = Dom.c('div').css({
11337 position: 'fixed',
11338 bottom: '1em',
11339 right: '1em',
11340 border: '1px solid #ffc107',
11341 background: '#fff3cd',
11342 color: '#856404',
11343 padding: '0.5em 1em',
11344 'font-family': 'sans-serif',
11345 'font-size': '12px',
11346 'border-radius': '4px',
11347 'z-index': '10000',
11348 'box-shadow': '0 2px 5px rgba(0,0,0,0.2)'
11349 });
11350 Dom.c('a')
11351 .attr('href', 'https://datatables.net/tn/25')
11352 .attr('target', '_blank')
11353 .css({
11354 color: 'inherit',
11355 'text-decoration': 'none'
11356 })
11357 .appendTo(notice);
11358 if (!text) {
11359 text = 'License key required';
11360 }
11361 shadow.appendChild(notice[0]);
11362 _notice = notice;
11363 _ready = true;
11364 }
11365 if (text) {
11366 _notice
11367 .find('a')
11368 .html('DataTables Plus: ' + text + ' - learn more &#187;');
11369 }
11370 }
11371 /**
11372 * Display the license notice
11373 */
11374 function noticeDisplay() {
11375 if (!_processingKey && document.body && !document.body.contains(_wm[0])) {
11376 document.body.appendChild(_wm[0]);
11377 }
11378 }
11379 /**
11380 * Validate the license string, which is in two parts - the first is a payload
11381 * that provides a small amount of information about the license, and the second
11382 * which is the license key.
11383 *
11384 * @param licenseString Key to validate
11385 * @returns Promise with validation information
11386 */
11387 function verify(licenseString) {
11388 return new Promise(function (resolve) {
11389 try {
11390 var parts = licenseString.split(':');
11391 if (parts.length !== 2) {
11392 _licenseInfo.valid = false;
11393 return resolve();
11394 }
11395 var payload = parts[0];
11396 var signatureB64 = parts[1];
11397 // Extract the payload to be useful
11398 var payloadParts = payload.match(/(plus|trial|editor)_(\d+)_(\d{4})(\d{2})(\d{2})/);
11399 if (!payloadParts || payloadParts.length !== 6) {
11400 _licenseInfo.valid = false;
11401 return resolve();
11402 }
11403 _licenseInfo.type = payloadParts[1];
11404 _licenseInfo.developers = parseInt(payloadParts[2]);
11405 _licenseInfo.expires = new Date(payloadParts[3] + '-' + payloadParts[4] + '-' + payloadParts[5]);
11406 var subtle = getSubtle();
11407 var rawKey = b64ToBuf(_publicKey);
11408 var rawSig = b64ToBuf(signatureB64);
11409 var data = new TextEncoder().encode(payload);
11410 // Non-secure environments don't have cryptographic verification
11411 // available.
11412 if (!subtle) {
11413 _licenseInfo.valid = false;
11414 resolve();
11415 return;
11416 }
11417 subtle
11418 .importKey('raw', rawKey, { name: 'ECDSA', namedCurve: 'P-256' }, false, ['verify'])
11419 .then(function (key) {
11420 return subtle.verify({ name: 'ECDSA', hash: { name: 'SHA-256' } }, key, rawSig, data);
11421 })
11422 .then(function (isValid) {
11423 _licenseInfo.valid = isValid;
11424 resolve();
11425 })
11426 .catch(function () {
11427 _licenseInfo.valid = false;
11428 resolve();
11429 });
11430 }
11431 catch (e) {
11432 _licenseInfo.valid = false;
11433 resolve();
11434 }
11435 });
11436 }
11437 /**
11438 * Create the `plus` function on `DataTable` which Plus extensions can call to
11439 * determine if the license key is valid and in date for the release. The
11440 * resulting function is called like this: `DataTable.plus('2026-12-25')` and
11441 * will return `true` or `false` depending on the key that was given (or not).
11442 *
11443 * @param DataTable The DataTable host object
11444 */
11445 function plus (DataTable) {
11446 Object.defineProperty(DataTable, 'plus', {
11447 value: function (releaseDate, software = '') {
11448 // Unsecure sites are only useful for development, so allow there
11449 // and on the site.
11450 let host = window.location.hostname;
11451 let isDev = host === '192.168.234.234' ||
11452 host.endsWith('.datatables.net') ||
11453 host === 'datatables.net';
11454 if (isDev) {
11455 return true;
11456 }
11457 if (_processingKey) {
11458 // The validation of the key is async, so there is a chance that
11459 // it could still be happening when this runs. We just queue the
11460 // last one if that is the case.
11461 _delayedReleaseDate = releaseDate;
11462 _delayedSoftware = software;
11463 return true;
11464 }
11465 return check(releaseDate, software);
11466 },
11467 configurable: false,
11468 enumerable: false,
11469 writable: false
11470 });
11471 }
11472 function getSubtle() {
11473 // Backwards compat for old browsers
11474 let cryptoObj = window.crypto || window.msCrypto;
11475 let subtle = cryptoObj.subtle || cryptoObj.webkitSubtle;
11476 return subtle;
11477 }
11478
11479 /**
11480 * CommonJS factory function pass through. This will check if the arguments
11481 * given are a window object or a jQuery object. If so they are set accordingly.
11482 *
11483 * @param root Window
11484 * @param jq jQuery
11485 * @returns Indicator
11486 */
11487 function factory(root, jq) {
11488 var is = false;
11489 // Test if the first parameter is a window object
11490 if (root && root.document) {
11491 window = root;
11492 document = root.document;
11493 }
11494 // Test if the second parameter is a jQuery object
11495 if (jq && jq.fn && jq.fn.jquery) {
11496 is = true;
11497 }
11498 return is;
11499 }
11500 /**
11501 * Check if a `<table>` node is a DataTable table already or not.
11502 *
11503 * @param table Table node or selector for the table to test. Note that if more
11504 * than more than one table is passed on, only the first will be checked
11505 * @returns true the table given is a DataTable, or false otherwise
11506 */
11507 const isDataTable = function (table) {
11508 if (table instanceof Api) {
11509 return true;
11510 }
11511 else if (arrayLike(table)) {
11512 // jQuery compatibility
11513 table = Array.from(table);
11514 }
11515 var t = Dom.s(table).get(0);
11516 var is = false;
11517 for (let i = 0; i < ext.settings.length; i++) {
11518 let ctx = ext.settings[i];
11519 var head = ctx.scrollHead ? ctx.scrollHead.find('table').get(0) : null;
11520 var foot = ctx.scrollFoot ? ctx.scrollFoot.find('table').get(0) : null;
11521 if (ctx.table === t || head === t || foot === t) {
11522 is = true;
11523 }
11524 }
11525 return is;
11526 };
11527 /**
11528 * Get all DataTable tables that have been initialised - optionally you can
11529 * select to get only currently visible tables.
11530 *
11531 * @param visible Flag to indicate if you want all (default) or visible tables
11532 * only.
11533 * @returns Array of `table` nodes (not DataTable instances) which are
11534 * DataTables
11535 */
11536 const tables = function (visible) {
11537 var api = false;
11538 if (visible && typeof visible !== 'boolean') {
11539 api = visible.api || false;
11540 visible = visible.visible || false;
11541 }
11542 var a = ext.settings
11543 .filter(function (o) {
11544 return !visible || (visible && Dom.s(o.table).isVisible())
11545 ? true
11546 : false;
11547 })
11548 .map(function (o) {
11549 return o.table;
11550 });
11551 return api ? new Api(a) : a;
11552 };
11553
11554 function _divProp(el, prop, val) {
11555 if (val) {
11556 el[prop] = val;
11557 }
11558 }
11559 register$2('div', function (settings, opts) {
11560 var n = document.createElement('div');
11561 if (opts) {
11562 _divProp(n, 'className', opts.className);
11563 _divProp(n, 'id', opts.id);
11564 _divProp(n, 'innerHTML', opts.html);
11565 _divProp(n, 'textContent', opts.text);
11566 }
11567 return n;
11568 });
11569
11570 register$2('info', function (settings, optsIn) {
11571 // For compatibility with the legacy `info` top level option
11572 if (!settings.features.info) {
11573 return null;
11574 }
11575 let lang = settings.language, tid = settings.tableId, n = Dom.c('div').classAdd(settings.classes.info.container);
11576 let opts = Object.assign({
11577 callback: lang.infoCallback,
11578 empty: lang.infoEmpty,
11579 postfix: lang.infoPostFix,
11580 search: lang.infoFiltered,
11581 text: lang.info
11582 }, optsIn);
11583 // Update display on each draw
11584 settings.callbacks.draw.push(function (s) {
11585 updateInfo(s, opts, n);
11586 });
11587 // For the first info display in the table, we add a callback and aria
11588 // information.
11589 if (!settings.infoEl) {
11590 n.attr({
11591 'aria-live': 'polite',
11592 id: tid + '_info',
11593 role: 'status'
11594 });
11595 // Table is described by our info div
11596 Dom.s(settings.table).attr('aria-describedby', tid + '_info');
11597 settings.infoEl = n;
11598 }
11599 return n;
11600 }, 'i');
11601 /**
11602 * Update the information elements in the display
11603 * @param settings DataTables settings object
11604 * @param opts
11605 * @param node
11606 */
11607 function updateInfo(settings, opts, node) {
11608 var start = settings.displayStart + 1, end = displayEnd(settings), max = recordsTotal(settings), total = recordsDisplay(settings), out = total ? opts.text : opts.empty;
11609 if (total !== max) {
11610 // Record set after filtering
11611 out += ' ' + opts.search;
11612 }
11613 // Convert the macros
11614 out += opts.postfix;
11615 out = macros(settings, out);
11616 if (opts.callback) {
11617 out = opts.callback.call(settings.instance, settings, start, end, max, total, out);
11618 }
11619 node.html(out);
11620 callbackFire(settings, null, 'info', [settings, node.get(0), out]);
11621 }
11622
11623 // opts
11624 // - type - button configuration
11625 // - buttons - number of buttons to show - must be odd
11626 register$2('paging', function (settings, optsIn) {
11627 // Don't show the paging input if the table doesn't have paging enabled
11628 if (!settings.features.paging) {
11629 return null;
11630 }
11631 let opts = Object.assign({
11632 buttons: ext.pager.numbers_length,
11633 type: settings.pagingType,
11634 boundaryNumbers: true,
11635 firstLast: true,
11636 previousNext: true,
11637 numbers: true
11638 }, optsIn);
11639 let host = Dom
11640 .c('div')
11641 .classAdd(settings.classes.paging.container +
11642 (opts.type ? ' paging_' + opts.type : ''))
11643 .append(Dom
11644 .c('nav')
11645 .attr('aria-label', 'pagination')
11646 .classAdd(settings.classes.paging.nav));
11647 let draw = function () {
11648 _pagingDraw(settings, host.children(), opts);
11649 };
11650 settings.callbacks.draw.push(draw);
11651 // Responsive redraw of paging control
11652 Dom.s(settings.table).on('column-sizing.dt.DT', draw);
11653 return host;
11654 }, 'p');
11655 /**
11656 * Dynamically create the button type array based on the configuration options.
11657 * This will only happen if the paging type is not defined.
11658 */
11659 function _pagingDynamic(opts) {
11660 let out = [];
11661 if (opts.numbers) {
11662 out.push('numbers');
11663 }
11664 if (opts.previousNext) {
11665 out.unshift('previous');
11666 out.push('next');
11667 }
11668 if (opts.firstLast) {
11669 out.unshift('first');
11670 out.push('last');
11671 }
11672 return out;
11673 }
11674 function _pagingDraw(settings, host, opts) {
11675 if (!settings.initDone) {
11676 return;
11677 }
11678 let plugin = opts.type ? ext.pager[opts.type] : _pagingDynamic, aria = settings.language.aria.paginate || {}, start = settings.displayStart, len = settings.pageLength, visRecords = recordsDisplay(settings), all = len === -1, page = all ? 0 : Math.ceil(start / len), pages = all ? (visRecords ? 1 : 0) : Math.ceil(visRecords / len), buttons = [], buttonEls = [], buttonsNested = plugin(opts).map(function (val) {
11679 return val === 'numbers'
11680 ? pagingNumbers(page, pages, opts.buttons, opts.boundaryNumbers)
11681 : val;
11682 });
11683 // .flat() would be better, but not supported in old Safari
11684 buttons = buttons.concat.apply(buttons, buttonsNested);
11685 for (let i = 0; i < buttons.length; i++) {
11686 let button = buttons[i];
11687 let btnInfo = _pagingButtonInfo(settings, button, page, pages);
11688 let btn = renderer(settings, 'pagingButton')(settings, button, btnInfo.display, btnInfo.active, btnInfo.disabled);
11689 let ariaLabel = typeof button === 'string'
11690 ? aria[button]
11691 : aria.number
11692 ? aria.number + (button + 1)
11693 : null;
11694 // Common attributes
11695 Dom.s(btn.clicker).attr({
11696 'aria-controls': settings.tableId,
11697 'aria-disabled': btnInfo.disabled ? 'true' : null,
11698 'aria-current': btnInfo.active ? 'page' : null,
11699 'aria-label': ariaLabel,
11700 'data-dt-idx': button,
11701 tabIndex: btnInfo.disabled
11702 ? -1
11703 : settings.tabIndex &&
11704 btn.clicker.nodeName.toLowerCase() !== 'span'
11705 ? settings.tabIndex
11706 : null // `0` doesn't need a tabIndex since it is the default
11707 });
11708 if (typeof button !== 'number') {
11709 Dom.s(btn.clicker).classAdd(button);
11710 }
11711 bindAction(btn.clicker, '', function (e) {
11712 e.preventDefault();
11713 pageChange(settings, button, true);
11714 });
11715 buttonEls.push(btn.display);
11716 }
11717 let wrapped = renderer(settings, 'pagingContainer')(settings, buttonEls);
11718 let activeEl = host.find(document.activeElement).attr('data-dt-idx');
11719 host.empty().append(wrapped);
11720 if (activeEl) {
11721 host.find('[data-dt-idx="' + activeEl + '"]').trigger('focus');
11722 }
11723 // Responsive - check if the buttons are over two lines based on the
11724 // height of the buttons and the container.
11725 if (buttonEls.length) {
11726 let outerHeight = Dom.s(buttonEls[0]).height('withBorder');
11727 if (opts.buttons > 1 && // prevent infinite
11728 outerHeight > 0 && // will be 0 if hidden
11729 host.height() >= outerHeight * 2 - 10) {
11730 _pagingDraw(settings, host, Object.assign({}, opts, { buttons: opts.buttons - 2 }));
11731 }
11732 }
11733 }
11734 /**
11735 * Get properties for a button based on the current paging state of the table
11736 *
11737 * @param settings DT settings object
11738 * @param button The button type in question
11739 * @param page Table's current page
11740 * @param pages Number of pages
11741 * @returns Info object
11742 */
11743 function _pagingButtonInfo(settings, button, page, pages) {
11744 let lang = settings.language.paginate;
11745 let o = {
11746 display: '',
11747 active: false,
11748 disabled: false
11749 };
11750 switch (button) {
11751 case 'ellipsis':
11752 o.display = '&#x2026;';
11753 break;
11754 case 'first':
11755 o.display = lang.first;
11756 if (page === 0) {
11757 o.disabled = true;
11758 }
11759 break;
11760 case 'previous':
11761 o.display = lang.previous;
11762 if (page === 0) {
11763 o.disabled = true;
11764 }
11765 break;
11766 case 'next':
11767 o.display = lang.next;
11768 if (pages === 0 || page === pages - 1) {
11769 o.disabled = true;
11770 }
11771 break;
11772 case 'last':
11773 o.display = lang.last;
11774 if (pages === 0 || page === pages - 1) {
11775 o.disabled = true;
11776 }
11777 break;
11778 default:
11779 if (typeof button === 'number') {
11780 o.display = settings.formatNumber(button + 1, settings);
11781 if (page === button) {
11782 o.active = true;
11783 }
11784 }
11785 break;
11786 }
11787 return o;
11788 }
11789
11790 var __lengthCounter = 0;
11791 // opts
11792 // - menu
11793 // - text
11794 register$2('pageLength', function (settings, optsIn) {
11795 var features = settings.features;
11796 // For compatibility with the legacy `pageLength` top level option
11797 if (!features.paging || !features.lengthChange) {
11798 return null;
11799 }
11800 let opts = Object.assign({
11801 menu: settings.lengthMenu,
11802 text: settings.language.lengthMenu
11803 }, optsIn);
11804 let classes = settings.classes.length, tableId = settings.tableId, menu = opts.menu, lengths = [], language = [], i;
11805 // Options can be given in a number of ways
11806 if (Array.isArray(menu[0])) {
11807 // Old 1.x style - 2D array
11808 lengths = menu[0];
11809 language = menu[1];
11810 }
11811 else {
11812 for (i = 0; i < menu.length; i++) {
11813 // An object with different label and value
11814 if (plainObject(menu[i])) {
11815 lengths.push(menu[i].value);
11816 language.push(menu[i].label);
11817 }
11818 else {
11819 // Or just a number to display and use
11820 lengths.push(menu[i]);
11821 language.push(menu[i]);
11822 }
11823 }
11824 }
11825 // We can put the <select> outside of the label if it is at the start or
11826 // end which helps improve accessability (not all screen readers like
11827 // implicit for elements).
11828 var end = opts.text.match(/_MENU_$/);
11829 var start = opts.text.match(/^_MENU_/);
11830 var removed = opts.text.replace(/_MENU_/, '');
11831 var str = '<label>' + opts.text + '</label>';
11832 if (start) {
11833 str = '_MENU_<label>' + removed + '</label>';
11834 }
11835 else if (end) {
11836 str = '<label>' + removed + '</label>_MENU_';
11837 }
11838 // Wrapper element - use a span as a holder for where the select will go
11839 var tmpId = 'tmp-' + +new Date();
11840 var div = Dom.c('div')
11841 .classAdd(classes.container)
11842 .html(str.replace('_MENU_', '<span id="' + tmpId + '"></span>'));
11843 // Save text node content for macro updating
11844 var textNodes = [];
11845 Array.prototype.slice
11846 .call(div.find('label').get(0).childNodes)
11847 .forEach(function (el) {
11848 if (el.nodeType === Node.TEXT_NODE) {
11849 textNodes.push({
11850 el: el,
11851 text: el.textContent
11852 });
11853 }
11854 });
11855 // Update the label text in case it has an entries value
11856 var updateEntries = function (len) {
11857 textNodes.forEach(function (node) {
11858 node.el.textContent = macros(settings, node.text, len);
11859 });
11860 };
11861 // Next, the select itself, along with the options
11862 var select = Dom.c('select')
11863 .attr('aria-controls', tableId)
11864 .attr('autocomplete', 'off')
11865 .classAdd(classes.select);
11866 for (i = 0; i < lengths.length; i++) {
11867 // Attempt to look up the length from the i18n options
11868 var label = settings.api.i18n('lengthLabels.' + lengths[i], null);
11869 if (label === null) {
11870 // If not present, fallback to old style
11871 label =
11872 typeof language[i] === 'number'
11873 ? settings.formatNumber(language[i], settings)
11874 : language[i];
11875 }
11876 select.get(0)[i] = new Option(label, lengths[i]);
11877 }
11878 // Swap in the select list
11879 div.find('#' + tmpId).replaceWith(select);
11880 // Can't use `select` variable as user might provide their own and the
11881 // reference is broken by the use of outerHTML
11882 div.find('select')
11883 .attr('id', 'dt-length-' + __lengthCounter)
11884 .val(settings.pageLength)
11885 .on('change.DT', function () {
11886 lengthChange(settings, select.val());
11887 draw(settings);
11888 });
11889 // add for and id to label and input
11890 div.find('label').attr('for', 'dt-length-' + __lengthCounter);
11891 __lengthCounter++;
11892 // Update node value whenever anything changes the table's length
11893 Dom.s(settings.table).on('length.dt.DT', function (e, s, len) {
11894 if (settings === s) {
11895 let localSelect = div.find('select');
11896 // Remove any temporary values
11897 localSelect.find('option[data-dt-len-tmp]').remove();
11898 let option = localSelect.find('option[value="' + len + '"]');
11899 // If the select list doesn't have the target value, then we
11900 // need to add it for display.
11901 if (!option.length) {
11902 let after = findInsertBeforePoint(select, len);
11903 let tempOption = Dom.c('option')
11904 .val(len)
11905 .text(len)
11906 .attr('data-dt-len-tmp', true);
11907 if (after && after.length) {
11908 tempOption.insertBefore(after);
11909 }
11910 else {
11911 localSelect.append(tempOption);
11912 }
11913 }
11914 localSelect.val(len);
11915 // Resolve plurals in the text for the new length
11916 updateEntries(len);
11917 }
11918 });
11919 updateEntries(settings.pageLength);
11920 return div;
11921 }, 'l');
11922 /**
11923 * Find the element to insert the temporary option before to keep the sequence.
11924 *
11925 * @param select Select element
11926 * @param insertValue Page length value
11927 * @returns Target option or null if not found
11928 */
11929 function findInsertBeforePoint(select, insertValue) {
11930 let options = select.find('option');
11931 let values = options.mapTo(el => parseInt(el.value));
11932 let idx = values.findIndex(val => val > insertValue);
11933 return idx < -1 ? null : options.eq(idx);
11934 }
11935
11936 let __searchCounter = 0;
11937 register$2('search', function (settings, optsIn) {
11938 // Don't show the input if filtering isn't available on the table
11939 if (!settings.features.searching) {
11940 return null;
11941 }
11942 let classes = settings.classes.search;
11943 let tableId = settings.tableId;
11944 let language = settings.language;
11945 let input = '<input type="search" class="' +
11946 classes.input +
11947 '" autocomplete="off"/>';
11948 let opts = util.object.assignDeep({
11949 columns: '*',
11950 placeholder: language.searchPlaceholder,
11951 processing: false,
11952 text: language.search
11953 }, optsIn);
11954 // The _INPUT_ is optional - is appended if not present
11955 if (opts.text.indexOf('_INPUT_') === -1) {
11956 opts.text += '_INPUT_';
11957 }
11958 opts.text = macros(settings, opts.text);
11959 let indexes = settings.api.columns(opts.columns).indexes().toArray();
11960 let searchName = opts.columns === '*' ? '*' : indexes.join(',');
11961 let appliedSearch = settings.searches[searchName];
11962 if (!appliedSearch) {
11963 appliedSearch = create$2();
11964 settings.searches[searchName] = appliedSearch;
11965 }
11966 appliedSearch.columns = indexes;
11967 // We can put the <input> outside of the label if it is at the start or
11968 // end which helps improve accessability (not all screen readers like
11969 // implicit for elements).
11970 let end = opts.text.match(/_INPUT_$/);
11971 let start = opts.text.match(/^_INPUT_/);
11972 let removed = opts.text.replace(/_INPUT_/, '');
11973 let str = '<label>' + opts.text + '</label>';
11974 if (start) {
11975 str = '_INPUT_<label>' + removed + '</label>';
11976 }
11977 else if (end) {
11978 str = '<label>' + removed + '</label>_INPUT_';
11979 }
11980 let filter = Dom.c('div')
11981 .classAdd(classes.container)
11982 .html(str.replace(/_INPUT_/, input));
11983 // add for and id to label and input
11984 filter.find('label').attr('for', 'dt-search-' + __searchCounter);
11985 filter.find('input').attr('id', 'dt-search-' + __searchCounter);
11986 __searchCounter++;
11987 let searchFn = function (event) {
11988 let val = this.value;
11989 if (appliedSearch.return && event.key !== 'Enter') {
11990 return;
11991 }
11992 /* Now do the filter */
11993 if (val != appliedSearch.search) {
11994 processingRun(settings, opts.processing, function () {
11995 appliedSearch.search = val;
11996 filterComplete(settings);
11997 // Need to redraw, without resorting
11998 settings.displayStart = 0;
11999 draw(settings);
12000 });
12001 }
12002 };
12003 let searchDelay = settings.searchDelay;
12004 let filterEl = filter
12005 .find('input')
12006 .val(textValue(appliedSearch.search))
12007 .attr('placeholder', opts.placeholder)
12008 .on('keyup.DT search.DT input.DT paste.DT cut.DT', searchDelay ? util.debounce(searchFn, searchDelay) : searchFn)
12009 .on('mouseup.DT', function (e) {
12010 // Edge fix! Edge 17 does not trigger anything other than mouse
12011 // events when clicking on the clear icon (Edge bug 17584515).
12012 // This is safe in other browsers as `searchFn` checks the value
12013 // to see if it has changed. In other browsers it won't have.
12014 setTimeout(function () {
12015 searchFn.call(filterEl.get(0), e);
12016 }, 10);
12017 })
12018 .on('keypress.DT', function (e) {
12019 /* Prevent form submission */
12020 if (e.keyCode == 13) {
12021 return false;
12022 }
12023 })
12024 .attr('aria-controls', tableId);
12025 // Update the input elements whenever the table is filtered
12026 Dom.s(settings.table).on('search.dt.DT', function (ev, s) {
12027 if (settings === s && filterEl.get(0) !== document.activeElement) {
12028 let host = settings.searches[searchName];
12029 filterEl.val(textValue(host.search));
12030 }
12031 });
12032 return filter;
12033 }, 'f');
12034 /**
12035 * Convert a search input into a plain string value for display. This is needed
12036 * as the value could be a function or regex, which can't be displayed in the
12037 * input element.
12038 *
12039 * @param val Search term
12040 * @returns String version
12041 */
12042 function textValue(val) {
12043 if (val instanceof RegExp) {
12044 return val.toString();
12045 }
12046 else if (typeof val !== 'function') {
12047 return val;
12048 }
12049 return '';
12050 }
12051
12052 const defaults$1 = {
12053 ajax: null,
12054 ajaxDataGet: false,
12055 api: null,
12056 browser: {
12057 barWidth: 0,
12058 scrollbarLeft: false
12059 },
12060 callbacks: {
12061 destroy: [],
12062 draw: [],
12063 footer: [],
12064 header: [],
12065 init: [],
12066 preDraw: [],
12067 row: [],
12068 rowCreated: [],
12069 stateLoadParams: [],
12070 stateLoaded: [],
12071 stateSaveParams: []
12072 },
12073 caption: '',
12074 captionNode: null,
12075 classes: {},
12076 columns: [],
12077 containerWidth: -1,
12078 data: [],
12079 deferLoading: false,
12080 destroyWidth: 0,
12081 destroying: false,
12082 display: [],
12083 displayMaster: [],
12084 displayStart: 0,
12085 displayStartInit: -1,
12086 doingDraw: false,
12087 dom: null,
12088 drawCount: 0,
12089 drawError: -1,
12090 drawHold: false,
12091 features: {
12092 autoWidth: false,
12093 deferRender: false,
12094 info: false,
12095 lengthChange: false,
12096 orderClasses: false,
12097 orderMulti: false,
12098 ordering: false,
12099 paging: false,
12100 processing: false,
12101 searching: false,
12102 serverSide: false,
12103 stateSave: false
12104 },
12105 footer: [],
12106 header: [],
12107 ids: {},
12108 init: {},
12109 initDone: false,
12110 initialised: false,
12111 language: {
12112 ajax: '',
12113 aria: {
12114 orderable: '',
12115 orderableRemove: '',
12116 orderableReverse: '',
12117 paginate: {
12118 first: '',
12119 last: '',
12120 next: '',
12121 number: '',
12122 previous: ''
12123 }
12124 },
12125 decimal: '',
12126 emptyTable: '',
12127 entries: { _: '' },
12128 info: '',
12129 infoEmpty: '',
12130 infoFiltered: '',
12131 infoPostFix: '',
12132 lengthMenu: '',
12133 lengthLabels: {},
12134 loadingRecords: '',
12135 paginate: {
12136 first: '',
12137 last: '',
12138 next: '',
12139 previous: ''
12140 },
12141 processing: '',
12142 search: '',
12143 searchPlaceholder: '',
12144 thousands: '',
12145 url: '',
12146 zeroRecords: ''
12147 },
12148 lastOrder: [],
12149 layout: {},
12150 loadingState: false,
12151 order: [],
12152 orderCellsTop: null,
12153 orderDescReverse: false,
12154 orderFixed: [],
12155 orderHandler: true,
12156 orderIndicators: true,
12157 pageLength: 10,
12158 pagingControls: 0,
12159 pagingType: 'two_button',
12160 searchCols: [],
12161 recordsDisplay: 0,
12162 recordsTotal: 0,
12163 renderer: null,
12164 resizeObserver: null,
12165 reszEvt: false,
12166 rowId: '',
12167 rowReadObject: false,
12168 scroll: {
12169 barWidth: 0,
12170 collapse: null,
12171 x: '',
12172 xInner: '',
12173 y: ''
12174 },
12175 scrollBarVis: false,
12176 searchDelay: 0,
12177 searches: {},
12178 searchesFixed: {
12179 '*': {}
12180 },
12181 serverMethod: null,
12182 sortDetails: [],
12183 stateDuration: 0,
12184 stateLoadCallback: () => {
12185 return {};
12186 },
12187 stateLoaded: null,
12188 stateSaveCallback: () => { },
12189 stateSaved: null,
12190 tabIndex: 0,
12191 tableId: '',
12192 titleRow: null,
12193 typeDetect: true,
12194 unique: '',
12195 wasFiltered: false,
12196 wasOrdered: false,
12197 windowResizeCb: () => { }
12198 };
12199 /**
12200 * Create a new context object
12201 *
12202 * @param parts Values to assign, otherwise the defaults will be used
12203 * @returns New object
12204 */
12205 function create(parts = {}) {
12206 return util.object.assignDeep({}, defaults$1, parts);
12207 }
12208
12209 var models = {
12210 Column: Settings,
12211 Row: create$1,
12212 Search: create$2,
12213 Settings: create
12214 };
12215
12216 /**
12217 * Initialisation options that can be given to DataTables at initialisation
12218 * time.
12219 */
12220 const defaults = {
12221 ajax: null,
12222 autoWidth: true,
12223 caption: '',
12224 classes: {},
12225 column: defaults$4,
12226 columnDefs: null,
12227 columns: null,
12228 createdRow: null,
12229 data: null,
12230 deferLoading: null,
12231 deferRender: true,
12232 destroy: false,
12233 displayStart: 0,
12234 dom: null,
12235 drawCallback: null,
12236 footerCallback: null,
12237 formatNumber: function (toFormat, ctx) {
12238 return toFormat
12239 .toString()
12240 .replace(/\B(?=(\d{3})+(?!\d))/g, ctx.language.thousands);
12241 },
12242 headerCallback: null,
12243 info: true,
12244 infoCallback: null,
12245 initComplete: null,
12246 language: {
12247 ajax: '',
12248 aria: {
12249 orderable: ': Activate to sort',
12250 orderableRemove: ': Activate to remove sorting',
12251 orderableReverse: ': Activate to invert sorting',
12252 paginate: {
12253 first: 'First',
12254 last: 'Last',
12255 next: 'Next',
12256 number: '',
12257 previous: 'Previous'
12258 }
12259 },
12260 decimal: '',
12261 emptyTable: 'No data available in table',
12262 entries: {
12263 _: 'entries',
12264 1: 'entry'
12265 },
12266 info: 'Showing _START_ to _END_ of _TOTAL_ _ENTRIES-TOTAL_',
12267 infoEmpty: 'Showing 0 to 0 of 0 _ENTRIES-TOTAL_',
12268 infoFiltered: '(filtered from _MAX_ total _ENTRIES-MAX_)',
12269 infoPostFix: '',
12270 lengthLabels: {
12271 '-1': 'All'
12272 },
12273 lengthMenu: '_MENU_ _ENTRIES_ per page',
12274 loadingRecords: 'Loading...',
12275 paginate: {
12276 first: '\u00AB',
12277 last: '\u00BB',
12278 next: '\u203A',
12279 previous: '\u2039'
12280 },
12281 processing: '',
12282 search: 'Search:',
12283 searchPlaceholder: '',
12284 thousands: ',',
12285 url: '',
12286 zeroRecords: 'No matching records found'
12287 },
12288 layout: {
12289 bottomEnd: 'paging',
12290 bottomStart: 'info',
12291 topEnd: 'search',
12292 topStart: 'pageLength'
12293 },
12294 lengthChange: true,
12295 lengthMenu: [10, 25, 50, 100],
12296 on: {},
12297 order: [[0, 'asc']],
12298 orderCellsTop: null,
12299 orderClasses: true,
12300 orderDescReverse: true,
12301 orderFixed: [],
12302 orderMulti: true,
12303 ordering: true,
12304 pageLength: 10,
12305 paging: true,
12306 pagingType: '',
12307 preDrawCallback: null,
12308 processing: false,
12309 renderer: null,
12310 retrieve: false,
12311 rowCallback: null,
12312 rowId: 'DT_RowId',
12313 scrollCollapse: false,
12314 scrollX: '',
12315 scrollY: '',
12316 search: defaults$3,
12317 searchCols: [],
12318 searchDelay: 0,
12319 searching: true,
12320 serverMethod: 'GET',
12321 serverSide: false,
12322 stateDuration: 7200,
12323 stateLoadCallback: function (settings) {
12324 try {
12325 const state = (settings.stateDuration === -1 ? sessionStorage : localStorage).getItem('DataTables_' + settings.unique + '_' + location.pathname);
12326 return state ? JSON.parse(state) : {};
12327 }
12328 catch (e) {
12329 return {};
12330 }
12331 },
12332 stateLoadParams: null,
12333 stateLoaded: null,
12334 stateSave: false,
12335 stateSaveCallback: function (settings, data) {
12336 try {
12337 (settings.stateDuration === -1
12338 ? sessionStorage
12339 : localStorage).setItem('DataTables_' + settings.unique + '_' + location.pathname, JSON.stringify(data));
12340 }
12341 catch (e) {
12342 // noop
12343 }
12344 },
12345 stateSaveParams: null,
12346 tabIndex: 0,
12347 titleRow: null,
12348 typeDetect: true
12349 };
12350
12351 const DataTable = function (selector, options) {
12352 // Check if called with a window or jQuery object for DOM less applications
12353 // This is for backwards compatibility
12354 if (factory(selector, options)) {
12355 return DataTable;
12356 }
12357 // Allow access to the API from the core class
12358 this.api = () => {
12359 return new Api(selector);
12360 };
12361 // Backwards compatibility with this "class" being exposed as
12362 // `$().dataTable()`. We can't simply provide that as a wrapper, as there
12363 // are properties on this class which are expected to be exposed.
12364 if (typeof this.jquery === 'string') {
12365 // Typescript doesn't like the `return api` from the constructor, but is
12366 // it valid Javascript, and allows backwards compatibility, hence the any
12367 new DataTable(this.toArray(), selector); // note argument shift
12368 return this;
12369 }
12370 var emptyInit = options === undefined;
12371 let tableEls = Dom.s(selector);
12372 let len = tableEls.count();
12373 if (emptyInit) {
12374 options = {};
12375 }
12376 tableEls.each(tableEl => {
12377 // For each initialisation we want to give it a clean initialisation
12378 // object that can be bashed around
12379 var o = {};
12380 var init = len > 1 // optimisation for single table case
12381 ? util.object.assignDeepObjects(o, options, true)
12382 : options;
12383 var i = 0, iLen;
12384 var id = tableEl.getAttribute('id');
12385 var table = Dom.s(tableEl);
12386 // Sanity check
12387 if (tableEl.nodeName.toLowerCase() != 'table') {
12388 log(null, 0, 'Non-table node initialisation (' + tableEl.nodeName + ')', 2);
12389 return;
12390 }
12391 // Special case for options
12392 if (init.on && init.on.options) {
12393 listener(table, 'options', init.on.options);
12394 }
12395 table.trigger('options.dt', true, [init]);
12396 // Backwards compatibility parameter mapping
12397 compatOpts(defaults);
12398 compatCols(defaults$4);
12399 // Allow data properties on the table element to be used as
12400 // initialisation options
12401 util.object.assign(init, escapeObject(table.data()));
12402 compatOpts(init);
12403 /* Check to see if we are re-initialising a table */
12404 var allSettings = ext.settings;
12405 for (i = 0, iLen = allSettings.length; i < iLen; i++) {
12406 var s = allSettings[i];
12407 /* Base check on table node */
12408 if (s.table == tableEl ||
12409 (s.thead && s.thead.parentNode == tableEl) ||
12410 (s.tfoot && s.tfoot.parentNode == tableEl)) {
12411 var retrieve = init.retrieve || false;
12412 var destroy = init.destroy || false;
12413 if (emptyInit || retrieve) {
12414 return s.instance;
12415 }
12416 else if (destroy) {
12417 new Api(s).destroy();
12418 break;
12419 }
12420 else {
12421 log(s, 0, 'Cannot reinitialise DataTable', 3);
12422 return;
12423 }
12424 }
12425 /* If the element we are initialising has the same ID as a table
12426 * which was previously initialised, but the table nodes don't match
12427 * (from before) then we destroy the old instance by simply deleting
12428 * it. This is under the assumption that the table has been
12429 * destroyed by other methods. Anyone using non-id selectors will
12430 * need to do this manually
12431 */
12432 if (s.tableId == tableEl.id) {
12433 allSettings.splice(i, 1);
12434 break;
12435 }
12436 }
12437 /* Ensure the table has an ID - required for accessibility */
12438 if (id === null || id === '') {
12439 id = 'DataTables_Table_' + ext._unique++;
12440 tableEl.id = id;
12441 }
12442 // Replacing an existing colgroup with our own. Not ideal, but a merge
12443 // could take a lot of code
12444 table.children('colgroup').remove();
12445 // Create the settings object for this table and set some of the default
12446 // parameters
12447 var settings = create({
12448 destroyWidth: table.width(),
12449 unique: id,
12450 tableId: id,
12451 colgroup: Dom.c('colgroup'),
12452 fastData: function (row, column, type) {
12453 return getCellData(settings, row, column, type);
12454 }
12455 });
12456 settings.table = tableEl;
12457 settings.init = init;
12458 allSettings.push(settings);
12459 // Make a single API instance available for internal handling
12460 settings.api = new Api(settings);
12461 // Need to add the instance after the instance after the settings object
12462 // has been added to the settings array, so we can self reference the
12463 // table instance if more than one
12464 settings.instance = Dom.s(tableEl); // any until we add the api
12465 settings.instance.api = () => settings.api;
12466 // If the length menu is given, but the init display length is not, use
12467 // the length menu
12468 if (init.lengthMenu && !init.pageLength) {
12469 init.pageLength =
12470 typeof init.lengthMenu[0] === 'number'
12471 ? init.lengthMenu[0]
12472 : Array.isArray(init.lengthMenu[0])
12473 ? init.lengthMenu[0][0]
12474 : init.lengthMenu[0].value;
12475 }
12476 // Apply the defaults and init options to make a single init object will
12477 // all options defined from defaults and instance options.
12478 let config = util.object.assignDeepObjects(util.object.assignDeep({}, defaults), init);
12479 // Map the initialisation options onto the context object
12480 map(settings.features, config, [
12481 'autoWidth',
12482 'deferRender',
12483 'info',
12484 'lengthChange',
12485 'orderClasses',
12486 'ordering',
12487 'orderMulti',
12488 'paging',
12489 'processing',
12490 'searching',
12491 'serverSide'
12492 ]);
12493 map(settings, config, [
12494 'ajax',
12495 'formatNumber',
12496 'serverMethod',
12497 'order',
12498 'orderFixed',
12499 'lengthMenu',
12500 'pagingType',
12501 'stateDuration',
12502 'orderCellsTop',
12503 'tabIndex',
12504 'dom',
12505 'stateLoadCallback',
12506 'stateSaveCallback',
12507 'renderer',
12508 'searchDelay',
12509 'rowId',
12510 'caption',
12511 'layout',
12512 'orderDescReverse',
12513 'orderIndicators',
12514 'orderHandler',
12515 'titleRow',
12516 'typeDetect',
12517 'pageLength',
12518 'searchCols'
12519 ]);
12520 map(settings.scroll, config, [
12521 ['scrollX', 'x'],
12522 ['scrollY', 'y'],
12523 ['scrollCollapse', 'collapse']
12524 ]);
12525 map(settings.language, config, 'infoCallback');
12526 // Setup global search
12527 settings.searches['*'] = create$2(config.search);
12528 /* Callback functions which are array driven */
12529 callbackReg(settings, 'draw', config.drawCallback);
12530 callbackReg(settings, 'stateSaveParams', config.stateSaveParams);
12531 callbackReg(settings, 'stateLoadParams', config.stateLoadParams);
12532 callbackReg(settings, 'stateLoaded', config.stateLoaded);
12533 callbackReg(settings, 'row', config.rowCallback);
12534 callbackReg(settings, 'rowCreated', config.createdRow);
12535 callbackReg(settings, 'header', config.headerCallback);
12536 callbackReg(settings, 'footer', config.footerCallback);
12537 callbackReg(settings, 'init', config.initComplete);
12538 callbackReg(settings, 'preDraw', config.preDrawCallback);
12539 settings.rowIdFn = util.get(settings.rowId);
12540 // Add event listeners
12541 if (config.on) {
12542 Object.keys(config.on).forEach(function (key) {
12543 listener(table, key, config.on[key]);
12544 });
12545 }
12546 // Browser support detection
12547 browserDetect(settings);
12548 var classes = settings.classes;
12549 util.object.assignDeep(classes, ext.classes, config.classes);
12550 table.classAdd(classes.table);
12551 if (!settings.features.paging) {
12552 config.displayStart = 0;
12553 }
12554 if (settings.displayStartInit === -1) {
12555 // Display start point, taking into account the save saving
12556 settings.displayStartInit = config.displayStart;
12557 settings.displayStart = config.displayStart; // TODO remove !
12558 }
12559 var defer = config.deferLoading;
12560 if (defer !== null) {
12561 settings.deferLoading = true;
12562 if (Array.isArray(defer)) {
12563 settings.recordsDisplay = defer[0];
12564 settings.recordsTotal = defer[1];
12565 }
12566 else {
12567 settings.recordsDisplay = defer; // TODO remove !
12568 settings.recordsTotal = defer;
12569 }
12570 }
12571 /*
12572 * Columns
12573 * See if we should load columns automatically or use defined ones
12574 */
12575 var columnsInit = [];
12576 var thead = table.children('thead');
12577 var initHeaderLayout = detectHeader(settings, thead.get(0), false);
12578 // If we don't have a columns array, then generate one with nulls
12579 if (config.columns) {
12580 columnsInit = config.columns;
12581 }
12582 else if (initHeaderLayout.length) {
12583 for (i = 0, iLen = initHeaderLayout[0].length; i < iLen; i++) {
12584 columnsInit.push(null);
12585 }
12586 }
12587 // Add the columns
12588 for (i = 0, iLen = columnsInit.length; i < iLen; i++) {
12589 addColumn(settings);
12590 }
12591 // Apply the column definitions
12592 applyColumnDefs(settings, config.columnDefs, columnsInit, initHeaderLayout, function (idx, def) {
12593 columnOptions(settings, idx, def);
12594 });
12595 /* HTML5 attribute detection - build an mData object automatically if
12596 * the attributes are found
12597 */
12598 var rowOne = table.children('tbody').find('tr:first-child').eq(0);
12599 if (rowOne.count()) {
12600 var a = function (cell, name) {
12601 return cell.getAttribute('data-' + name) !== null ? name : null;
12602 };
12603 rowOne
12604 .eq(0)
12605 .children('th, td')
12606 .each(function (cell, loop) {
12607 var col = settings.columns[loop];
12608 if (!col) {
12609 log(settings, 0, 'Incorrect column count', 18);
12610 }
12611 if (col.data === loop) {
12612 var sort = a(cell, 'sort') || a(cell, 'order');
12613 var filter = a(cell, 'filter') || a(cell, 'search');
12614 if (sort !== null || filter !== null) {
12615 col.data = {
12616 _: loop + '.display',
12617 sort: sort !== null
12618 ? loop + '.@data-' + sort
12619 : undefined,
12620 type: sort !== null
12621 ? loop + '.@data-' + sort
12622 : undefined,
12623 filter: filter !== null
12624 ? loop + '.@data-' + filter
12625 : undefined
12626 };
12627 col._isArrayHost = true;
12628 columnOptions(settings, loop);
12629 }
12630 }
12631 });
12632 }
12633 // Must be done after everything which can be overridden by the state
12634 // saving!
12635 callbackReg(settings, 'draw', saveState);
12636 var features = settings.features;
12637 if (config.stateSave) {
12638 features.stateSave = true;
12639 }
12640 // If aaSorting is not defined, then we use the first indicator in
12641 // asSorting in case that has been altered, so the default sort reflects
12642 // that option
12643 if (config.order === undefined) {
12644 var sorting = settings.order;
12645 for (i = 0, iLen = sorting.length; i < iLen; i++) {
12646 sorting[i][1] = settings.columns[i].orderSequence[0];
12647 }
12648 }
12649 // Do a first pass on the sorting classes (allows any size changes to be
12650 // taken into account, and also will apply sorting disabled classes if
12651 // disabled
12652 sortingClasses(settings);
12653 callbackReg(settings, 'draw', function () {
12654 if (settings.wasOrdered ||
12655 dataSource(settings) === 'ssp' ||
12656 features.deferRender) {
12657 sortingClasses(settings);
12658 }
12659 });
12660 /*
12661 * Table HTML init Cache the header, body and footer as required,
12662 * creating them if needed
12663 */
12664 var caption = table.children('caption');
12665 if (settings.caption) {
12666 if (caption.count() === 0) {
12667 caption = Dom.c('caption').prependTo(table);
12668 }
12669 caption.html(settings.caption);
12670 }
12671 // Store the caption side, so we can remove the element from the
12672 // document when creating the element
12673 if (caption.count()) {
12674 caption.get(0)._captionSide = caption.css('caption-side');
12675 settings.captionNode = caption.get(0);
12676 }
12677 // Place the colgroup element in the correct location for the HTML
12678 // structure
12679 if (caption.count()) {
12680 settings.colgroup.insertAfter(caption.get(0));
12681 }
12682 else {
12683 settings.colgroup.prependTo(tableEl);
12684 }
12685 if (thead.count() === 0) {
12686 thead = Dom.c('thead').appendTo(table);
12687 }
12688 settings.thead = thead.get(0);
12689 var tbody = table.children('tbody');
12690 if (tbody.count() === 0) {
12691 tbody = Dom.c('tbody').insertAfter(thead.get(0));
12692 }
12693 settings.tbody = tbody.get(0);
12694 var tfoot = table.children('tfoot');
12695 if (tfoot.count() === 0) {
12696 // If we are a scrolling table, and no footer has been given, then
12697 // we need to create a tfoot element for the caption element to be
12698 // appended to
12699 tfoot = Dom.c('tfoot').appendTo(tableEl);
12700 }
12701 settings.tfoot = tfoot.get(0);
12702 // Copy the data index array
12703 settings.display = settings.displayMaster.slice();
12704 // Initialisation complete - table can be drawn
12705 settings.initialised = true;
12706 // Language definitions
12707 var language = settings.language;
12708 if (config.language) {
12709 util.object.assignDeep(language, config.language);
12710 }
12711 if (language.ajax) {
12712 let languageLoaded = function (json) {
12713 hungarianToCamel(json);
12714 util.object.assignDeep(language, json, settings.init.language);
12715 callbackFire(settings, null, 'i18n', [settings], true);
12716 initialise(settings);
12717 };
12718 // Get the language definitions from a remote
12719 if (typeof language.ajax === 'function') {
12720 language.ajax(settings, languageLoaded);
12721 }
12722 else {
12723 let ajaxBase = {
12724 dataType: 'json',
12725 url: '',
12726 success: languageLoaded,
12727 error: function () {
12728 // Error occurred loading language file
12729 log(settings, 0, 'i18n file loading error', 21);
12730 // Continue on as best we can
12731 initialise(settings);
12732 }
12733 };
12734 if (typeof language.ajax === 'string') {
12735 ajaxBase.url = language.ajax;
12736 }
12737 else {
12738 ajaxBase = util.object.assign(ajaxBase, language.ajax);
12739 }
12740 util.ajax(ajaxBase);
12741 }
12742 }
12743 else {
12744 callbackFire(settings, null, 'i18n', [settings], true);
12745 initialise(settings);
12746 }
12747 });
12748 // This is unusual, but we want the return from the exposed `DataTable`
12749 // function to be an API instance, rather than the core, which is not
12750 // publicly exposed. This is also the reason for the `as unknown` below - TS
12751 // doesn't like the return with a different object.
12752 return this.api();
12753 };
12754 DataTable.type = register$1;
12755 DataTable.types = types;
12756 DataTable.render = helpers;
12757 DataTable.ext = ext;
12758 DataTable.use = util.external;
12759 DataTable.factory = factory;
12760 DataTable.versionCheck = util.version.check;
12761 DataTable.version = ext.version;
12762 DataTable.isDataTable = isDataTable;
12763 DataTable.tables = tables;
12764 DataTable.util = util;
12765 DataTable.Api = Api;
12766 DataTable.datetime = datetime;
12767 DataTable.__browser = browser;
12768 DataTable.Dom = Dom;
12769 DataTable.ajax = util.ajax;
12770 DataTable.key = key;
12771 plus(DataTable);
12772 /**
12773 * Private data store, containing all of the settings objects that are created
12774 * for the tables on a given page.
12775 */
12776 DataTable.settings = ext.settings;
12777 /**
12778 * Object models container, for the various models that DataTables has available
12779 * to it. These models define the objects that are used to hold the active state
12780 * and configuration of the table.
12781 */
12782 DataTable.models = models;
12783 DataTable.defaults = defaults;
12784 DataTable.feature = {
12785 register: register$2
12786 };
12787 // Register the libraries
12788 util.external(DataTable);
12789 if (window.jQuery) {
12790 util.external(window.jQuery);
12791 }
12792
12793
12794 return DataTable;
12795 }));
12796
12797
12798 /*! DataTables styling integration
12799 * © SpryMedia Ltd - datatables.net/license
12800 */
12801
12802 (function(factory){
12803 if (typeof define === 'function' && define.amd) {
12804 // AMD
12805 define(['datatables.net'], function (dt) {
12806 return factory(window, document, dt);
12807 });
12808 }
12809 else if (typeof exports === 'object') {
12810 // CommonJS
12811 var cjsRequires = function (root) {
12812 if (! root.DataTable) {
12813 require('datatables.net')(root);
12814 }
12815 };
12816
12817 if (typeof window === 'undefined') {
12818 module.exports = function (root) {
12819 if (! root) {
12820 // CommonJS environments without a window global must pass a
12821 // root. This will give an error otherwise
12822 root = window;
12823 }
12824
12825 cjsRequires(root);
12826 return factory(root, root.document, root.DataTable);
12827 };
12828 }
12829 else {
12830 cjsRequires(window);
12831 module.exports = factory(window, window.document, window.DataTable);
12832 }
12833 }
12834 else {
12835 // Browser
12836 factory(window, document, window.DataTable);
12837 }
12838 }(function(window, document, DataTable) {
12839 'use strict';
12840
12841
12842
12843
12844 return DataTable;
12845 }));
12846
12847
12848