/*! DataTables 3.1.3
* Copyright (c) SpryMedia Ltd - datatables.net/license
*/
(function(factory){
if (typeof define === 'function' && define.amd) {
// AMD
define([], function () {
return factory(window, document);
});
}
else if (typeof exports === 'object') {
// CommonJS
var cjsRequires = function (root) { };
if (typeof window === 'undefined') {
module.exports = function (root) {
if (! root) {
// CommonJS environments without a window global must pass a
// root. This will give an error otherwise
root = window;
}
cjsRequires(root);
return factory(root, root.document);
};
}
else {
cjsRequires(window);
module.exports = factory(window, window.document);
}
}
else {
// Browser
window.DataTable = factory(window, document);
}
}(function(window, document) {
'use strict';
// A collection of the regular expressions used throughout the code base. Not all are here
// just the ones that need to be reused - no need to dump single use expressions here.
// https://en.wikipedia.org/wiki/Foreign_exchange_market
// - \u20BD - Russian ruble.
// - \u20a9 - South Korean Won
// - \u20BA - Turkish Lira
// - \u20B9 - Indian Rupee
// - R - Brazil (R$) and South Africa
// - fr - Swiss Franc
// - kr - Swedish krona, Norwegian krone and Danish krone
// - \u2009 is thin space and \u202F is narrow no-break space, both used in many
// - Ƀ - Bitcoin
// - Ξ - Ethereum
// standards as thousands separators.
const reFormattedNumeric = /['\u00A0,$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfkɃΞ]/gi;
const reHtml = /<([^>]*>)/g;
// Escape regular expression special characters
const reRegexCharacters = new RegExp('(\\' +
[
'/',
'.',
'*',
'+',
'?',
'|',
'(',
')',
'[',
']',
'{',
'}',
'\\',
'$',
'^',
'-',
].join('|\\') +
')', 'g');
// This is not strict ISO8601 - Date.parse() is quite lax, although
// implementations differ between browsers.
const reDate = /^\d{2,4}[./-]\d{1,2}[./-]\d{1,2}([T ]{1}\d{1,2}[:.]\d{2}([.:]\d{2})?)?$/;
const reNewLines = /[\r\n\u2028]/g;
const isoTimezone = /[T\s]\d{2}.*?(Z|[+-]\d{2}(?::?\d{2})?)$/;
var regex = /*#__PURE__*/Object.freeze({
__proto__: null,
isoTimezone: isoTimezone,
reDate: reDate,
reFormattedNumeric: reFormattedNumeric,
reHtml: reHtml,
reNewLines: reNewLines,
reRegexCharacters: reRegexCharacters
});
const maxStrLen = Math.pow(2, 28);
/**
* DataTables default string normalisation. Remove diacritics from a string by
* decomposing it and then removing non-ascii characters.
*
* This function is replaceable if the user wishes to use a different library
* for normalising a string.
*
* @param val Value to normalise (if a string)
* @param both Include both the normalised and original in the return
* @returns Normalised string, or original value if not a string
*/
let _normalize = function (val, both) {
if (typeof val !== 'string') {
return val;
}
// It is faster to just run `normalize` than it is to check if
// we need to with a regex! (Check as it isn't available in old
// Safari)
var res = val.normalize ? val.normalize('NFD') : val;
// Equally, here we check if a regex is needed or not
return res.length !== val.length
? (both === true ? val + ' ' : '') + res.replace(/[\u0300-\u036f]/g, '')
: res;
};
/**
* DataTables default string HTML stripping from a string
*
* This function is replaceable if the user wishes to use a different library
* for stripping HTML from a string.
*
* @param input Value to strip HTML from
* @param replacement Value to replace the tags with
* @returns Stripped value
*/
let _stripHtml = function (input, replacement = '') {
if (!input || typeof input !== 'string') {
return input;
}
// Irrelevant check to workaround CodeQL's false positive on the regex
if (input.length > maxStrLen) {
throw new Error('Exceeded max str len');
}
let previous;
let next = input.replace(reHtml, replacement); // Complete tags
// Safety for incomplete script tag - use do / while to ensure that
// we get all instances
do {
previous = next;
next = next.replace(//g, '>')
.replace(/"/g, '"')
: d;
};
/**
* Escape regular expression characters in a string
*
* @param val String to escape
* @returns String with regex characters escaped
*/
function escapeRegex(val) {
return val.replace(reRegexCharacters, '\\$1');
}
function escapeHtml(mixed) {
var type = typeof mixed;
if (type === 'function') {
_escapeHtml = mixed;
return;
}
else if (type === 'string' || Array.isArray(mixed)) {
return _escapeHtml(mixed);
}
return mixed;
}
function normalize(mixed, both) {
var type = typeof mixed;
if (type !== 'function') {
return _normalize(mixed, both);
}
_normalize = mixed;
}
function stripHtml(mixed, replacement) {
const type = typeof mixed;
if (type === 'function') {
_stripHtml = mixed;
return;
}
else if (type === 'string') {
return _stripHtml(mixed, replacement);
}
return mixed;
}
var string = /*#__PURE__*/Object.freeze({
__proto__: null,
escapeHtml: escapeHtml,
escapeRegex: escapeRegex,
normalize: normalize,
stripHtml: stripHtml
});
const _re_dic = {};
/**
* Get integer value
*
* @param s Value
* @returns Int, or null if not a number
*/
function intVal(s) {
var integer = parseInt(s, 10);
return !isNaN(integer) && isFinite(s) ? integer : null;
}
// Convert from a formatted number with characters other than `.` as the
// decimal place, to a JavaScript number
function numToDecimal(num, decimalPoint) {
// Cache created regular expressions for speed as this function is called often
if (!_re_dic[decimalPoint]) {
_re_dic[decimalPoint] = new RegExp(escapeRegex(decimalPoint), 'g');
}
return typeof num === 'string' && decimalPoint !== '.'
? num.replace(/\./g, '').replace(_re_dic[decimalPoint], '.')
: num;
}
var conv = /*#__PURE__*/Object.freeze({
__proto__: null,
intVal: intVal,
numToDecimal: numToDecimal
});
function arrayLike(test) {
return (test && // Exists
typeof test !== 'string' && // Is not a string
test.length !== undefined && // Has a length
test.nodeType === undefined // Is not a text node
);
}
/**
* Determine if the input is a Dom instance
*
* @param input Value to check
* @returns true if it is a Dom instance, false otherwise
*/
function dom(input) {
return input && typeof input === 'object' && input._isDom;
}
/**
* Determine if the input is an HTML element
*
* @param input Value to check
* @returns true if an HTML element was passed in
*/
function element(input) {
return typeof input === 'object' && input.nodeName;
}
/**
* Check if a value is empty or not. Note that a string with `-` is considered
* empty
*
* @param d Value to check
* @returns `true` if empty, `false` otherwise
*/
function empty(d) {
return !d || d === true || d === '-' ? true : false;
}
/**
* Check if a string is HTML. Note that a string without HTML in it can be
* considered to be HTML still!
*
* @todo Can we drop this?
* @param d
* @returns
*/
function html(d) {
return empty(d) || typeof d === 'string';
}
/**
* Is a string a number surrounded by HTML?
*
* @param d Value to check
* @param decimalPoint Decimal place character
* @param formatted Consider formatted numbers
* @param allowEmpty Allow empty to be considered as a number
* @returns True if a number, null otherwise
*/
function htmlNum(d, decimalPoint, formatted, allowEmpty) {
if (allowEmpty && empty(d)) {
return true;
}
// input and select strings mean that this isn't just a number
if (typeof d === 'string' && d.match(/<(input|select)/i)) {
return null;
}
return !html(d)
? null
: num(stripHtml(d), decimalPoint, formatted, allowEmpty)
? true
: null;
}
/**
* Determine if an input is a jQuery instance
*
* @param input Value to check
* @returns true if it is a jQuery instance, false otherwise
*/
function jquery(input) {
return input && typeof input.jquery === 'string';
}
/**
* Check if a given value is numeric, taking into account if it might be
* formatted or uses a decimal point that is not a period.
*
* @param d Value to check
* @param decimalPoint DP character
* @param formatted Allow the number to be formatted or not
* @param allowEmpty Allow an empty value to be considered a number
* @returns `true` if numeric
*/
function num(d, decimalPoint, formatted, allowEmpty) {
let type = typeof d;
if (type === 'number' || type === 'bigint') {
return true;
}
// If empty return immediately so there must be a number if it is a
// formatted string (this stops the string "k", or "kr", etc being detected
// as a formatted number for currency
if (allowEmpty && empty(d)) {
return true;
}
if (decimalPoint && type === 'string') {
d = numToDecimal(d, decimalPoint);
}
if (formatted && type === 'string') {
d = d.replace(reFormattedNumeric, '');
}
return !isNaN(parseFloat(d)) && isFinite(d);
}
/**
* Determine if a value is a plain object or not
*
* @param value Value to check
* @returns true if is a plain object, otherwise false
*/
function plainObject(value) {
if (typeof value !== 'object' || value === null) {
return false;
}
let proto = Object.getPrototypeOf(value);
return proto === null || proto === Object.prototype;
}
var is = /*#__PURE__*/Object.freeze({
__proto__: null,
arrayLike: arrayLike,
dom: dom,
element: element,
empty: empty,
html: html,
htmlNum: htmlNum,
jquery: jquery,
num: num,
plainObject: plainObject
});
/**
* Object iteration function, executing a callback for each key in the object
*
* @param input Input object
* @param fn Function to execute
*/
function each(input, fn) {
if (!input) {
return;
}
let keys = Object.keys(input);
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
fn(key, input[key], i);
}
}
/**
* Merge the contents of two or more objects into the first object.
*
* @param out Object to be assigned the properties
* @param inputs Objects to take the values from
* @returns The `output`, just for convenience - output === the return.
*/
function assign(out, ...inputs) {
let output = Object(out);
// Can't just use `Object.assign` as it will assign `undefined` as a regular
// value to the target.
for (let i = 0; i < inputs.length; i++) {
let options = inputs[i];
// Filter inputs
if (options != null) {
// Extend the base object
for (let name in options) {
let copy = options[name];
// Prevent Object.prototype pollution
// Prevent never-ending loop
if (name === '__proto__' || output === copy) {
continue;
}
// Ignore undefined values (this is why we can't use Object.assign)
if (copy !== undefined) {
output[name] = copy;
}
}
}
}
return output;
}
/**
* Deep merge the contents of two or more objects into the first object. This
* breaks references for both objects and array.
*
* @param out Object to be assigned the properties
* @param inputs Objects to take the values from
* @returns The `output`, just for convenience - output === the return.
*/
function assignDeep(out, ...inputs) {
if (!out) {
return {};
}
for (let i = 0; i < inputs.length; i++) {
let input = inputs[i];
if (!input) {
continue;
}
for (const [key, value] of Object.entries(input)) {
if (Array.isArray(value)) {
if (!Array.isArray(out[key])) {
out[key] = [];
}
assignDeep(out[key], value);
}
else if (plainObject(value)) {
if (!plainObject(out[key])) {
out[key] = {};
}
assignDeep(out[key], value);
}
else if (input[key] !== undefined) {
out[key] = input[key];
}
}
}
return out;
}
/**
* Deep merge objects, but shallow copy arrays. The reason we need to do this,
* is that we don't want to deep copy array init values (such as aaSorting)
* since the dev wouldn't be able to override them, but we do want to deep copy
* objects.
*
* @param out Object to extend
* @param extender Object from which the properties will be applied to out
* @param breakRefs If true, then arrays will be sliced to take an independent
* copy with the exception of the `data` or `aaData` parameters if they are
* present. This is so you can pass in a collection to DataTables and have
* that used as your data source without breaking the references
* @returns out Reference, just for convenience - out === the return.
*/
function assignDeepObjects(out, extender, breakRefs = false) {
let val;
for (let prop in extender) {
if (Object.prototype.hasOwnProperty.call(extender, prop)) {
val = extender[prop];
if (plainObject(val)) {
if (!plainObject(out[prop])) {
out[prop] = {};
}
assignDeepObjects(out[prop], val, breakRefs);
}
else if (breakRefs &&
prop !== 'data' &&
prop !== 'aaData' &&
Array.isArray(val)) {
out[prop] = val.slice();
}
else {
out[prop] = val;
}
}
}
return out;
}
/**
* Map entries to an array
*
* @param obj In object
* @param fn Map transform function. Same signature as `each`
* @returns Result
*/
function map$1(obj, fn) {
let out = [];
each(obj, (key, val) => {
out.push(fn(key, val));
});
return out;
}
var object = /*#__PURE__*/Object.freeze({
__proto__: null,
assign: assign,
assignDeep: assignDeep,
assignDeepObjects: assignDeepObjects,
each: each,
map: map$1
});
// Can be assigned in DateTable.use()
var __win;
var __bootstrap;
var __foundation;
var __luxon$1;
var __moment$1;
var __dateTime;
var __dataTable;
var __jquery;
function getWin() {
if (__win) {
return __win;
}
if (typeof globalThis !== 'undefined' && globalThis.window) {
return globalThis.window;
}
if (typeof window !== 'undefined') {
return window;
}
return {};
}
/**
* Set the libraries that DataTables uses, or the global objects.
* Note that the arguments can be either way around (legacy support)
* and the second is optional. See docs.
*/
function external (arg1, arg2) {
// Reverse arguments for legacy support
var module = typeof arg1 === 'string' ? arg2 : arg1;
var type = typeof arg2 === 'string' ? arg2 : arg1;
// Getter
if (module === undefined && typeof type === 'string') {
switch (type) {
case 'lib':
case 'jq':
if (__jquery) {
return __jquery;
}
let local = getWin().jQuery;
if (local && local.fn) {
return local;
}
return null;
case 'win':
return getWin();
case 'doc':
return getWin().document;
case 'datatable':
return __dataTable;
case 'datetime':
return __dateTime;
case 'luxon':
return __luxon$1 || getWin().luxon || null;
case 'moment':
return __moment$1 || getWin().moment || null;
case 'bootstrap':
// Use local if set, otherwise try window, which could be undefined
return __bootstrap || getWin().bootstrap || null;
case 'foundation':
// Ditto
return __foundation || getWin().Foundation || null;
default:
return null;
}
}
// Setter
if (type === 'lib' ||
type === 'jq' ||
(module && module.fn && module.fn.jquery)) {
__jquery = module;
jQuerySetup();
}
else if (type === 'datatable' || (module && module.isDataTable)) {
__dataTable = module;
}
else if (type === 'win' || (module && module.document)) {
__win = module;
}
else if (type === 'datetime' || (module && module.type === 'DateTime')) {
__dateTime = module;
}
else if (type === 'luxon' || (module && module.FixedOffsetZone)) {
__luxon$1 = module;
}
else if (type === 'moment' || (module && module.isMoment)) {
__moment$1 = module;
}
else if (type === 'bootstrap' ||
(module && module.Modal && module.Modal.NAME === 'modal')) {
// This is currently for BS5 only. BS3/4 attach to jQuery, so no need to use `.use()`
__bootstrap = module;
}
else if (type === 'foundation' || (module && module.Reveal)) {
__foundation = module;
}
}
/**
* Attach jQuery to DataTables
*/
function jQuerySetup() {
if (!__dataTable || !__jquery) {
return;
}
// Provide access to the host jQuery object (circular reference)
__dataTable.$ = __jquery;
// jQuery integration - expose the core function.
__jquery.fn.dataTable = __dataTable;
// jQuery wrapper - returning a DataTable instance
__jquery.fn.DataTable = function (options) {
let table = new __dataTable(this.toArray(), options);
return table;
};
// Legacy aliases
__jquery.fn.dataTableSettings = __dataTable.ext.settings;
__jquery.fn.dataTableExt = __dataTable.ext;
// All properties that are available to $.fn.dataTable should also be available
// on $.fn.DataTable
each(__dataTable, function (prop, val) {
__jquery.fn.DataTable[prop] = val;
});
}
const defaults$5 = {
cache: true,
contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
headers: {},
traditional: false,
url: ''
};
/**
* Trigger an Ajax call to the server based on the configuration parameters
* passed in.
*
* @param optionsIn Ajax options
* @returns The XHR request
*/
function ajax(optionsIn) {
let xhr = new XMLHttpRequest();
let options = assign({}, defaults$5, optionsIn);
let urlParams = queryParams(options);
let method = httpMethod(options);
let sendData = null;
// Allow the data to be sent to the server as a simple JSON string -
// primarily to be used with POST / PUT
if (options.submitAs === 'json' && options.data) {
options.data = JSON.stringify(options.data);
if (!options.contentType) {
options.contentType = 'application/json; charset=utf-8';
}
}
xhr.open(method, options.url +
(urlParams
? (options.url.includes('?') ? '&' : '?') + urlParams
: ''), true, options.username || null, options.password || null);
// Content type for FormData requests gets set by the browser.
if (options.contentType && !(options.data instanceof FormData)) {
xhr.setRequestHeader('Content-Type', options.contentType);
}
// Add a X-Request-With header, as jQuery does so and some server-side
// platforms look for it. Only for same domain though.
if (options.headers &&
!options.headers['X-Requested-With'] &&
!isCrossDomain(options.url)) {
options.headers['X-Requested-With'] = 'XMLHttpRequest';
}
// Add an accept header specifically for JSON data types, again to match
// how jQuery operates for this.
if (options.dataType === 'json' &&
options.headers &&
!options.headers['accepts']) {
options.headers['Accept'] =
'application/json, text/javascript, */*; q=0.01';
}
each(options.headers, (key, val) => {
xhr.setRequestHeader(key, val);
});
if (options.data instanceof FormData) {
sendData = options.data;
}
else if (method !== 'GET' && options.data) {
if (typeof options.data === 'string') {
sendData = options.data;
}
else {
sendData = serialize(options.data, options.traditional);
sendData = convertSpaces(sendData, options);
// So beforeSend matches how jQuery behaves
options.data = sendData;
}
}
xhr.onreadystatechange = function () {
if (xhr.readyState != 4) {
return;
}
let responseData = xhr.responseText;
let statusText = 'success';
if (xhr.status === 0) {
return; // aborted
}
else if (xhr.status === 204 || method === 'HEAD') {
statusText = 'nocontent';
}
else if (xhr.status === 304) {
statusText = 'notmodified';
}
else if (xhr.status >= 400) {
statusText = 'error';
}
// Return data type handling
if (options.dataType === 'json') {
try {
responseData = JSON.parse(responseData);
}
catch (e) {
statusText = 'parsererror';
}
}
else if (!options.dataType) {
// Data type is undefined, so attempt to parse as JSON if possible,
// but no error if it can't be
try {
responseData = JSON.parse(responseData);
}
catch (e) {
// noop
}
}
if (statusText === 'success') {
callback(options.success, responseData, statusText, xhr);
}
else {
callback(options.error, xhr, statusText, xhr.statusText);
}
callback(options.complete, xhr, statusText);
};
if (options.beforeSend) {
if (options.beforeSend.call(options, xhr, options) === false) {
xhr.abort();
return xhr;
}
}
xhr.send(sendData);
return xhr;
}
// Expose defaults and serialisation method
ajax.defaults = defaults$5;
ajax.serialize = serialize;
/**
* Run callback functions (allowing for none, one or array)
*
* @param fnIn Function(s) to run
* @param arg1 Parameters to pass to the function(s)
* @param arg2 Parameters to pass to the function(s)
* @param arg3 Parameters to pass to the function(s)
*/
function callback(fnIn, arg1, arg2, arg3) {
if (!fnIn) {
return;
}
let fnArr = Array.isArray(fnIn) ? fnIn : [fnIn];
for (let i = 0; i < fnArr.length; i++) {
fnArr[i](arg1, arg2, arg3);
}
}
/**
* For form submission with x-www-form-urlencoded, spaces should be submitted as
* `+`. See the jQuery discussion on the topic here:
* https://github.com/jquery/jquery/issues/2658#issuecomment-149024872
*
* @param sendData Serialised form of the data to submit
* @param options Ajax options
* @returns Query string
*/
function convertSpaces(sendData, options) {
return (options.contentType || '').indexOf('application/x-www-form-urlencoded') === 0
? sendData.replace(/%20/g, '+')
: sendData;
}
/**
* Determine if a url is a cross domain request or not
*
* @param url URL to check
* @returns True if cross domain, false otherwise
*/
function isCrossDomain(url) {
// Use the current page as the base to handle relative URLs correctly
const win = external('win');
const target = new URL(url, win.location.origin);
return target.origin !== win.location.origin;
}
/**
* Get the HTTP method from the Ajax request options
*
* @param options Ajax options
* @returns HTTP verb
*/
function httpMethod(options) {
let method = 'GET';
if (options.type) {
method = options.type;
}
if (options.method) {
method = options.method;
}
return method.toUpperCase();
}
/**
* Get the query parameters based on the options (method, cache and data all
* need to be considered).
*
* @param options Ajax options
* @returns URL string
*/
function queryParams(options) {
let requestParams = [];
if (httpMethod(options) === 'GET') {
// Construct URL parameters string
requestParams.push(serialize(options.data, options.traditional));
}
// If a DELETE method is used there are a number of servers which will
// reject the request if it has a body. So we need to append to the URL.
//
// http://stackoverflow.com/questions/15088955
// http://bugs.jquery.com/ticket/11586
if (httpMethod(options) === 'DELETE' &&
(options.deleteBody === undefined || options.deleteBody === true)) {
requestParams.push(serialize(options.data, options.traditional));
delete options.data;
}
if (options.cache === false) {
requestParams.push(serialize({ _: +new Date() }));
}
return convertSpaces(requestParams.filter(d => !!d).join('&'), options);
}
/**
* Convert an object into a list of parameters for a query request. Supports
* jQuery traditional option for legacy applications.
*
* @param obj Object to convert
* @param traditional If jQuery old style should be used
* @returns Parameter-ized string
*/
function serialize(obj, traditional = false) {
var params = [];
if (obj === undefined || obj === null) {
return '';
}
serializeNested(params, obj, traditional);
return params.join('&');
}
/**
* Recursive serialisation function
*
* @param params Array to write the serialised parameters to
* @param obj Object / array to serialise
* @param traditional Traditional flag for legacy
* @param scope Recursive scope
*/
function serializeNested(params, obj, traditional, scope = '') {
let array = Array.isArray(obj);
for (let key in obj) {
let value = obj[key];
let nestDown = Array.isArray(value) || (!traditional && plainObject(value));
if (scope) {
// Non-scalar values need the index set on the host
let index = !array || nestDown ? key : '';
key = traditional ? scope : scope + '[' + index + ']';
}
if (!scope && array) {
serializeAdd(params, value.name, value.value);
}
else if (nestDown) {
// Nest down
serializeNested(params, value, traditional, key);
}
else {
serializeAdd(params, key, value);
}
}
}
/**
* Add a name / value pair to the list of parameters
*
* @param params Parameter values
* @param name Parameter name
* @param value Parameter value
*/
function serializeAdd(params, name, value) {
// Allow the input to be a function to match how jQuery operates
let strVal = typeof value === 'function' ? value() : value;
params.push(encodeURIComponent(name) +
'=' +
encodeURIComponent(strVal === null ? '' : strVal));
}
/**
* Determine if all values in the array are unique. This means we can short
* cut the _unique method at the cost of a single loop. A sorted array is used
* to easily check the values.
*
* @param src Source array
* @return true if all unique, false otherwise
*/
function allUnique(src) {
if (src.length < 2) {
return true;
}
var sorted = src.slice().sort();
var last = sorted[0];
for (var i = 1, iLen = sorted.length; i < iLen; i++) {
if (sorted[i] === last) {
return false;
}
last = sorted[i];
}
return true;
}
/**
* Flatten an array
*
* Surprisingly this is faster than [].concat.apply
* https://jsperf.com/flatten-an-array-loop-vs-reduce/2
*
* @param out Array to write to
* @param val Source array, or single value
* @returns Flattened array
*/
function flatten(out, val) {
if (Array.isArray(val) || arrayLike(val)) {
for (var i = 0; i < val.length; i++) {
flatten(out, val[i]);
}
}
else {
out.push(val);
}
return out;
}
function intersection(a1, a2) {
return a1.filter(item => a2.includes(item));
}
/**
* Pluck items from an array of objects, or from a nested array of objects
*
* @param a Array to get values from
* @param prop Property to read values from
* @param prop2 Inner property to get values from if a 2D array
* @returns Array of read values
*/
function pluck(a, prop, prop2) {
let out = [], i = 0, iLen = a.length;
// Could have the test in the loop for slightly smaller code, but speed
// is essential here
if (prop2 !== undefined) {
for (; i < iLen; i++) {
if (a[i] && a[i][prop]) {
out.push(a[i][prop][prop2]);
}
}
}
else {
for (; i < iLen; i++) {
if (a[i]) {
out.push(a[i][prop]);
}
}
}
return out;
}
/**
* Basically the same as _pluck, but rather than looping over the source array we use `order`
* as the indexes to pick from the source array
*
* @param a Array to get values from
* @param order Indexes to pick
* @param prop Property to read values from
* @param prop2 Inner property to get values from if a 2D array
* @returns Array of read values
*/
function pluckOrder(a, order, prop, prop2) {
let out = [], i = 0, iLen = order.length;
// Could have the test in the loop for slightly smaller code, but speed
// is essential here
if (prop2 !== undefined) {
for (; i < iLen; i++) {
if (a[order[i]] && a[order[i]][prop]) {
out.push(a[order[i]][prop][prop2]);
}
}
}
else {
for (; i < iLen; i++) {
if (a[order[i]]) {
out.push(a[order[i]][prop]);
}
}
}
return out;
}
function range(len, start) {
var out = [];
var end;
if (start === undefined) {
start = 0;
end = len;
}
else {
end = start;
start = len;
}
for (var i = start; i < end; i++) {
out.push(i);
}
return out;
}
/**
* Remove all falsy values from an array
*
* @param a Source array
* @returns A new array, with empty values removed
*/
function removeEmpty(a) {
var out = [];
for (var i = 0, iLen = a.length; i < iLen; i++) {
if (a[i]) {
// careful - will remove all falsy values!
out.push(a[i]);
}
}
return out;
}
/**
* Join data from an array, but only for specific columns.
*
* Performance testing for this available here:
* https://jsperf.app/vejijo/2/preview.
*
* @param src Data source array to pick from
* @param use Indexes we want from the array
* @returns Joined string
*/
function selectiveJoin(src, use) {
if (typeof use === 'number') {
return '' + src[use];
}
if (use.length === 0) {
return '';
}
let result = '' + src[use[0]];
for (let i = 1; i < use.length; i++) {
result += ' ' + src[use[i]];
}
return result;
}
/**
* Find the unique elements in a source array.
*
* @param src Source array
* @return Array of unique items
*/
function unique(src) {
if (Array.from && Set) {
return Array.from(new Set(src));
}
if (allUnique(src)) {
return src.slice();
}
// A faster unique method is to use object keys to identify used values,
// but this doesn't work with arrays or objects, which we must also
// consider. See jsperf.app/compare-array-unique-versions/4 for more
// information.
var out = [], val, i, iLen = src.length, j, k = 0;
again: for (i = 0; i < iLen; i++) {
val = src[i];
for (j = 0; j < k; j++) {
if (out[j] === val) {
continue again;
}
}
out.push(val);
k++;
}
return out;
}
var array = /*#__PURE__*/Object.freeze({
__proto__: null,
flatten: flatten,
intersection: intersection,
pluck: pluck,
pluckOrder: pluckOrder,
range: range,
removeEmpty: removeEmpty,
selectiveJoin: selectiveJoin,
unique: unique
});
// Private variable that is used to match action syntax in the data property object
const __reArray = /\[.*?\]$/;
const __reFn = /\(\)$/;
/**
* Split string on periods, taking into account escaped periods
*
* @param str String to split
* @return Split string
*/
function splitObjNotation(str) {
const parts = str.match(/(\\.|[^.])+/g) || [''];
return parts.map(function (s) {
return s.replace(/\\\./g, '.');
});
}
/**
* Create a function that will read data a common data point from different (but same structure)
* data objects. This is primarily used to get data for a specific cell in a single column, but it
* can also be used in other places, such as when using JSON notation.
*
* @param dataPoint The data point to get
* @returns Function to get a data point's value from a source.
*/
function get$1(dataPoint) {
if (dataPoint === null) {
// Give an empty string for rendering / sorting etc
return function (data) {
// type, row and meta also passed, but not used
return data;
};
}
else if (typeof dataPoint === 'function') {
return function (data, type, row, meta) {
return dataPoint(data, type, row, meta);
};
}
else if (typeof dataPoint === 'string' &&
(dataPoint.indexOf('.') !== -1 ||
dataPoint.indexOf('[') !== -1 ||
dataPoint.indexOf('(') !== -1)) {
/* If there is a . in the source string then the data source is in a
* nested object so we loop over the data for each level to get the next
* level down. On each loop we test for undefined, and if found immediately
* return. This allows entire objects to be missing and sDefaultContent to
* be used if defined, rather than throwing an error
*/
let fetchData = function (data, type, src) {
let arrayNotation, funcNotation, out, innerSrc;
if (src !== '') {
let a = splitObjNotation(src);
for (let i = 0, iLen = a.length; i < iLen; i++) {
// Check if we are dealing with special notation
arrayNotation = a[i].match(__reArray);
funcNotation = a[i].match(__reFn);
if (arrayNotation) {
// Array notation
a[i] = a[i].replace(__reArray, '');
// Condition allows simply [] to be passed in
if (a[i] !== '') {
data = data[a[i]];
}
out = [];
// Get the remainder of the nested object to get
a.splice(0, i + 1);
innerSrc = a.join('.');
// Traverse each entry in the array getting the properties requested
if (Array.isArray(data)) {
for (let j = 0, jLen = data.length; j < jLen; j++) {
out.push(fetchData(data[j], type, innerSrc));
}
}
// If a string is given in between the array notation indicators, that
// is used to join the strings together, otherwise an array is returned
let join = arrayNotation[0].substring(1, arrayNotation[0].length - 1);
data = join === '' ? out : out.join(join);
// The inner call to fetchData has already traversed through the remainder
// of the source requested, so we exit from the loop
break;
}
else if (funcNotation) {
// Function call
a[i] = a[i].replace(__reFn, '');
data = data[a[i]]();
continue;
}
if (data === null || data[a[i]] === null) {
return null;
}
else if (data === undefined || data[a[i]] === undefined) {
return undefined;
}
data = data[a[i]];
}
}
return data;
};
return function (data, type) {
// row and meta also passed, but not used
return fetchData(data, type, dataPoint);
};
}
else if (plainObject(dataPoint)) {
// Build an object of get functions, and wrap them in a single call
let o = {};
each(dataPoint, function (key, val) {
if (val) {
o[key] = get$1(val);
}
});
return function (data, type, row, meta) {
let t = o[type] || o._;
return t !== undefined ? t(data, type, row, meta) : data;
};
}
else {
// Array or flat object mapping
return function (data) {
// row and meta also passed, but not used
return data[dataPoint];
};
}
}
/**
* Write a value into an existing data store
*
* @param dataPoint The data point to write to
*/
function set$1(dataPoint) {
if (dataPoint === null) {
// Nothing to do when the data source is null
return function () { };
}
else if (typeof dataPoint === 'function') {
return function (data, val, meta) {
dataPoint(data, 'set', val, meta);
};
}
else if (typeof dataPoint === 'string' &&
(dataPoint.indexOf('.') !== -1 ||
dataPoint.indexOf('[') !== -1 ||
dataPoint.indexOf('(') !== -1)) {
// Like the get, we need to get data from a nested object
let setData = function (data, val, src) {
let a = splitObjNotation(src), b;
let aLast = a[a.length - 1];
let arrayNotation, funcNotation, o, innerSrc;
for (let i = 0, iLen = a.length - 1; i < iLen; i++) {
// Protect against prototype pollution
if (a[i] === '__proto__' || a[i] === 'constructor') {
throw new Error('Cannot set prototype values');
}
// Check if we are dealing with an array notation request
arrayNotation = a[i].match(__reArray);
funcNotation = a[i].match(__reFn);
if (arrayNotation) {
a[i] = a[i].replace(__reArray, '');
data[a[i]] = [];
// Get the remainder of the nested object to set so we can recurse
b = a.slice();
b.splice(0, i + 1);
innerSrc = b.join('.');
// Traverse each entry in the array setting the properties requested
if (Array.isArray(val)) {
for (let j = 0, jLen = val.length; j < jLen; j++) {
o = {};
setData(o, val[j], innerSrc);
data[a[i]].push(o);
}
}
else {
// We've been asked to save data to an array, but it
// isn't array data to be saved. Best that can be done
// is to just save the value.
data[a[i]] = val;
}
// The inner call to setData has already traversed through the remainder
// of the source and has set the data, thus we can exit here
return;
}
else if (funcNotation) {
// Function call
a[i] = a[i].replace(__reFn, '');
data = data[a[i]](val);
}
// If the nested object doesn't currently exist - since we are
// trying to set the value - create it
if (data[a[i]] === null || data[a[i]] === undefined) {
data[a[i]] = {};
}
data = data[a[i]];
}
// Last item in the input - i.e, the actual set
if (aLast.match(__reFn)) {
// Function call
data = data[aLast.replace(__reFn, '')](val);
}
else {
// If array notation is used, we just want to strip it and use the property name
// and assign the value. If it isn't used, then we get the result we want anyway
data[aLast.replace(__reArray, '')] = val;
}
};
return function (data, val) {
// meta is also passed in, but not used
return setData(data, val, dataPoint);
};
}
else if (plainObject(dataPoint)) {
/* Unlike get, only the underscore (global) option is used for for
* setting data since we don't know the type here. This is why an object
* option is not documented for `mData` (which is read/write), but it is
* for `render` which is read only.
*/
return set$1(dataPoint._);
}
else {
// Array or flat object mapping
return function (data, val) {
// meta is also passed in, but not used
data[dataPoint] = val;
};
}
}
var data = /*#__PURE__*/Object.freeze({
__proto__: null,
get: get$1,
set: set$1
});
function debounce(fn, timeout = 250) {
let timer;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => {
fn.call(this, ...args);
}, timeout);
};
}
function throttle(fn, freq = 200) {
let last, timer;
return function (...args) {
const now = +new Date();
if (last && now < last + freq) {
clearTimeout(timer);
timer = setTimeout(() => {
last = undefined;
fn.call(this, ...args);
}, freq);
}
else {
last = now;
fn.call(this, ...args);
}
};
}
var timer = /*#__PURE__*/Object.freeze({
__proto__: null,
debounce: debounce,
throttle: throttle
});
/**
* Provide a common method for plug-ins to check the version of DataTables being
* used, in order to ensure compatibility.
*
* @param version1 Version string to check for, in the format "X.Y.Z". Note that
* the formats "X" and "X.Y" are also acceptable.
* @param version2 As above, but optional. If not given the current DataTables
* version will be used.
* @returns true if this version of DataTables is greater or equal to the
* required version, or false if this version of DataTales is not suitable
*/
function check$1(version1, version2) {
let dt = external('datatable');
var parts1 = version2 ? version2.split('.') : dt.ext.version.split('.');
var parts2 = version1.split('.');
var int1, int2;
for (var i = 0, iLen = parts2.length; i < iLen; i++) {
int1 = parseInt(parts1[i], 10) || 0;
int2 = parseInt(parts2[i], 10) || 0;
// Parts are the same, keep comparing
if (int1 === int2) {
continue;
}
// Parts are different, return immediately
return int1 > int2;
}
return true;
}
var version = /*#__PURE__*/Object.freeze({
__proto__: null,
check: check$1
});
// Note that the aliased properties are for compatibility with DataTables 2-
// which had a set of `util` functions.
var util = {
ajax,
array,
conv,
data,
/** @see timer.debounce */
debounce: debounce,
/** @see string.normalize */
diacritics: normalize,
/** @see string.escapeHtml */
escapeHtml: escapeHtml,
/** @see string.escapeRegex */
escapeRegex: escapeRegex,
external,
/** @see data.get */
get: get$1,
is,
object,
regex,
/** @see data.set */
set: set$1,
string,
/** @see string.stripHtml */
stripHtml: stripHtml,
/** @see timer.throttle */
throttle: throttle,
timer,
/** @see array.unique */
unique: unique,
version
};
/** Each element with an event attached needs a unique id */
let _uidCounter = 1;
/**
* All wrapped event handlers are stored in this array so we can refer back to
* them for removal. Each entry in the array is for a unique element, using the
* index to refer to it (the `uid` that is attached to the element).
*/
const _eventStore = [];
/**
* Get a unique id that can be assigned to an element.
*
* @returns UID
*/
function getUid(el) {
if (!el._event_uid) {
el._event_uid = _uidCounter++;
}
return el._event_uid;
}
/**
* Get all event handlers that have been assigned to an element
*
* @param el Element
* @returns Array of functions
*/
function get(el) {
let uid = el._event_uid;
if (!uid || !_eventStore[uid]) {
return null;
}
return _eventStore[uid];
}
/**
* Store an event handler for an element (does not apply it)
*
* @param el Element
* @param wrapper Function to set
*/
function set(el, wrapper) {
let uid = getUid(el);
if (_eventStore[uid] === undefined) {
_eventStore[uid] = [];
}
_eventStore[uid].push(wrapper);
}
/**
* Remove an event handler from an element's store
*
* @param el Element
* @param wrapper Function to set
* @returns void
*/
function remove$1(el, wrapper) {
let store = get(el);
if (!store) {
return;
}
let idx = store.indexOf(wrapper);
if (idx !== -1) {
store.splice(idx, 1);
}
}
/*
* We need an events library that has many of the features of jQuery's event
* handling - this is for backwards compatibility. Developers who have used
* jQuery to listen for events should not need to change their code!
*
* DataTables uses the following features of jQuery events, which need to be
* fully supported:
*
* * Namespaces
* * Custom events
* * Custom event object properties
* * Arguments for custom events
*
* Frustratingly while `dispatchEvent` will trigger all events listened to by
* jQuery (including namespaces with a shim of `jQuery.event.fix` to add the
* namespace and rnamespace properties), there is no way for `dispatchEvent` to
* pass arguments to custom event handlers.
*
* Also the inverse doesn't hold - using `addEventListener` followed by
* `$().trigger()` doesn't trigger the event handler. Therefore using both
* `dispatchEvent` and `$().trigger()` to fire off both event handlers would
* risk triggering some event handlers twice.
*
* Because of the backwards compatibility constraint and the complications given
* above, this library will act as a simple proxy to jQuery, if jQuery is
* present. If it is not, then it will implement the features described above
* itself. While this is not ideal (I'd prefer to have a way to trigger jQuery
* listened for events independently of triggering those added with this
* library, or any other `addEventListener` call), it does ensure backwards
* compatibility.
*/
const _mouseEvents = [
'click',
'dblclick',
'mousedown',
'mouseenter',
'mouseleave',
'mousemove',
'mouseout',
'mouseover',
'mouseup'
];
/**
* Add a property to an event object.
*
* @param event Event
* @param name Property name
* @param value Value to give the value
*/
function setEventProp(event, name, value) {
// Can't use do `event[name] =` - event object can't always have properties
// added like that.
Object.defineProperty(event, name, {
configurable: true,
get() {
return value;
}
});
}
/**
* Check that an element matches a given selector for a given event (ie its
* target)
*
* @param el Root element
* @param selector CSS selector
* @param event Event that
* @returns The matching element if there is one
*/
function delegateTarget(el, selector, event) {
// Its a delegate - get all elements that match the selector as
// descendants from the element the event was triggered on
let elements = Array.from(el.querySelectorAll(selector));
let target = event.target;
// The event might originate below our target, so need to climb the ladder
for (; target && target !== this; target = target.parentNode) {
// Needs to happen for all matched descendants
for (let element of elements) {
// And only call it when matched
if (element !== target) {
continue;
}
return target;
}
}
}
/**
* Get the event name and namespaces from a string
*
* @param original event name and dot delimited namespaces
* @returns Object with split parts
*/
function parseEventName(original) {
if (!original) {
return {
eventName: null,
namespaces: []
};
}
let parts = original.split('.');
let name = parts.shift();
let isHover = false;
let isFocus = false;
// mouse[enter|leave] and focus|blur don't bubble, but do have counterparts
// which do, so we make use of them, as this allows event delegation on
// those event names to work as expected.
if (name === 'mouseenter') {
name = 'mouseover';
isHover = true;
}
else if (name === 'mouseleave') {
name = 'mouseout';
isHover = true;
}
else if (name === 'focus') {
name = 'focusin';
isFocus = true;
}
else if (name === 'blur') {
name = 'focusout';
isFocus = true;
}
else if (name === 'ready') {
name = 'DOMContentLoaded';
}
return {
eventName: name,
isFocus,
isHover,
namespaces: parts
};
}
/**
* Add an event listener to a function
*
* @param el The element to add an event handler to
* @param nameFull Event name. This can optionally be followed by a dot
* separated list of namespaces, a la jQuery. This allows for easy event
* removal and also matching triggering.
* @param handler Callback function to execute
* @param selector Delegate selector. `null` for non-delegate events
* @param one Indicate if the event handler should execute only once and then be
* removed.
*/
function add(el, nameFull, handler, selector, one) {
let jq = external('jq');
let doc = external('doc');
if (jq && el.constructor !== EventTarget) {
let method = one ? 'one' : 'on';
if (selector) {
jq(el)[method](nameFull, selector, handler);
}
else {
jq(el)[method](nameFull, handler);
}
return;
}
// No jQuery - add the event ourselves
let { eventName, namespaces, isFocus, isHover } = parseEventName(nameFull);
if (!eventName) {
return;
}
// Special handling for the "ready" event - it will trigger when the content
// is ready, but also if it is already ready, when added.
if (el === doc && eventName === 'DOMContentLoaded' && nameFull.includes('ready')) {
if (doc.readyState === 'complete') {
handler(new Event('DOMContentLoaded'));
return;
}
}
// Create a function that will be the actual event handler, and performs any
// logic we need, such as delegate handling and adding properties.
let wrapped = function (event) {
let callScope = el; // Scope for the callback function
// If the event has a namespace (from a trigger), then the handler
// should only be run if there is at least one namespace being listened
// for that matches. This is an OR operation.
if (event.namespace &&
!intersection(namespaces, event.namespace.split('.')).length) {
return;
}
// If a special (overridden) type, then we need to check that they apply
if (!selector &&
((isFocus && event.target !== el) ||
(isHover &&
event.relatedTarget &&
el.contains(event.relatedTarget)))) {
return;
}
// For delegate events, determine if the target matches our selector
if (selector) {
let dTarget = delegateTarget(el, selector, event);
if (!dTarget) {
return;
}
if (isHover &&
event.relatedTarget &&
dTarget.contains(event.relatedTarget)) {
return;
}
callScope = dTarget;
}
// Set the properties that jQuery adds to the event object
setEventProp(event, 'currentTarget', callScope);
setEventProp(event, 'delegateTarget', el);
setEventProp(event, 'relatedTarget', event.relatedTarget);
// If it was triggered, extra data can be passed through using the
// arguments passed to trigger.
let retVal = handler.apply(callScope, [event, ...(event._args || [])]);
if (one) {
remove(el, eventName, handler, selector);
}
if (retVal === false) {
event.preventDefault();
event.stopPropagation();
}
event.result = retVal;
};
wrapped.delegateSelector = selector;
wrapped.original = handler;
wrapped.one = one;
wrapped.type = eventName;
wrapped.namespaces = namespaces;
set(el, wrapped);
el.addEventListener(eventName, wrapped);
}
/**
* Remove an event from an element
*
* @param el The element to remove the event(s) from
* @param nameFull Event name and / or dot separated namespaces
* @param handler The function to remove (optional)
* @param selector Delegate selector (optional)
*/
function remove(el, nameFull, handler, selector) {
let jq = external('jq');
if (jq && el.constructor !== EventTarget) {
if (selector) {
jq(el).off(nameFull, selector, handler);
}
else {
jq(el).off(nameFull, handler);
}
return;
}
// No jQuery - do it our way
let { eventName, namespaces } = parseEventName(nameFull);
let removeEvents = [];
let stored = get(el);
if (stored === null) {
return;
}
if (eventName && selector && handler) {
removeEvents = stored.filter(wrapped => wrapped.type === eventName &&
wrapped.delegateSelector === selector &&
wrapped.original === handler);
}
else if (eventName && selector) {
removeEvents = stored.filter(wrapped => wrapped.type === eventName &&
wrapped.delegateSelector === selector);
}
else if (eventName && handler) {
removeEvents = stored.filter(wrapped => wrapped.type === eventName && wrapped.original === handler);
}
else if (eventName) {
removeEvents = stored.filter(wrapped => wrapped.type === eventName);
}
else {
// No name, use all events
removeEvents = stored;
}
// If namespaces were given then we need to filter down to just those event
// handlers which have the given namespaces
if (namespaces.length) {
removeEvents = removeEvents.filter(
// The event needs to match all of the namespaces given in order to
// be removed - this matches jQuery's behaviour. The event could
// have other namespaces. Do this by filtering to just the filtering
// namespaces and check that the length matches
ev => ev.namespaces.filter(ns => namespaces.includes(ns)).length ===
namespaces.length);
}
removeEvents.forEach(wrapped => {
remove$1(el, wrapped);
el.removeEventListener(wrapped.type, wrapped);
});
}
/**
* Trigger an event on an element. Can have extra data given, which is useful
* for custom events.
*
* @param el Element to trigger the event on
* @param nameFull Event name with optional dot separated namespaces
* @param bubbles If the event should bubble up through the DOM or not
* @param args Array of arguments to pass to the event handler
* @param eventProps Object of extra parameters to attach to the event object
* @param returnEvent Indicate if the return should be the event object (for
* further processing) or the default prevented state.
* @returns `true` if default was NOT prevented, `false` if default was
* prevented. If `returnEvent` is `true` then the return will be the event
* object.
*/
function trigger(el, nameFull, bubbles = false, args = [], eventProps = null, returnEvent = false) {
let jq = external('jq');
let win = external('win');
if (jq && el.constructor !== EventTarget) {
let method = bubbles ? 'trigger' : 'triggerHandler';
let ev = jq.Event(nameFull);
each(eventProps, (key, val) => {
setEventProp(ev, key, val);
});
jq(el)[method](ev, args || []);
if (returnEvent) {
ev.defaultPrevented = ev.isDefaultPrevented();
return ev;
}
// See note below regarding the inversion
return !ev.isDefaultPrevented();
}
// No jQuery
let { eventName, namespaces } = parseEventName(nameFull);
if (!eventName) {
return false;
}
let event;
// If running in Node, we might be using JSDom which has its own event
// classes. The EventTarget is always the global, separate from the window
// events. 99% of the time, they will be the same.
if (el.constructor === EventTarget) {
event = new Event(eventName, { bubbles, cancelable: true });
}
else {
let isMouseEvent = _mouseEvents.includes(eventName.toLowerCase());
event = isMouseEvent
? new win.MouseEvent(eventName, { bubbles, cancelable: true })
: new win.Event(eventName, { bubbles, cancelable: true });
}
// Set the extra properties for the event
setEventProp(event, 'namespace', namespaces.join('.'));
setEventProp(event, '_args', args || []);
each(eventProps, (key, val) => setEventProp(event, key, val));
el.dispatchEvent(event);
// A lot of the old DataTables stuff checks for a `false` return to prevent
// the default action. To maintain compatibility we return an inverted
// `defaultPrevented` here - i.e. it becomes `do default`.
return returnEvent ? event : !event.defaultPrevented;
}
// Window level functions
var win = {
/**
* Get the height of the window, excluding a horizontal scrollbar if it is
* present.
*
* @returns Height in pixels
*/
height() {
var _a;
return ((_a = external('doc').querySelector('html')) === null || _a === void 0 ? void 0 : _a.clientHeight) || 0;
},
/**
* Remove an event handler from the window
*
* @param name Event name (can include or just be a namespace)
* @param cb Event callback function
*/
off(name, cb = null) {
remove(window, name, cb, null);
},
/**
* Add an event handler to the window
*
* @param name Event name (can include a namespace)
* @param cb Event callback function
*/
on(name, cb) {
add(window, name, cb, null, false);
},
/**
* Add an event handler to the window that will execute just once
*
* @param name Event name (can include a namespace)
* @param cb Event callback function
*/
one(name, cb) {
add(window, name, cb, null, true);
},
/**
* Get the left scroll offset of the window / document
*
* @param set Set the scroll position
* @returns Window X scroll offset in pixels
*/
scrollLeft(set) {
if (set !== undefined) {
window.scrollX = set;
}
return window.scrollX;
},
/**
* Get the top scroll offset of the window / document
*
* @param set Set the scroll position
* @returns Window Y scroll offset in pixels
*/
scrollTop(set) {
if (set !== undefined) {
window.scrollY = set;
}
return window.scrollY;
},
/**
* Get the width of the window, excluding a vertical scrollbar if it is
* present.
*
* @returns Width in pixels
*/
width() {
var _a;
return ((_a = external('doc').querySelector('html')) === null || _a === void 0 ? void 0 : _a.clientWidth) || 0;
}
};
function create$3(name) {
let el = external('doc').createElement(name);
return new Dom(el);
}
function select(selector) {
return new Dom(selector);
}
/**
* Event target for events which don't use the document
*/
const _staticEventTarget = new EventTarget();
/**
* `Dom` is a class that provides a chaining UI for simple DOM manipulation and
* selection.
*/
class Dom {
/**
* `Dom` is used for selection and manipulation of the DOM elements in a
* chaining interface.
*
* @param selector
*/
constructor(selector) {
/** Number of elements in the array */
this.length = 0;
/** Flag to indicate that this is a Dom instance */
this._isDom = true;
if (selector) {
this.add(selector);
}
}
/**
* Add an element (or multiple) to the instance. Will ensure uniqueness.
*
* @param selector Element(s) to add
* @param sort Indicate if the element should be added in document order.
* @returns Self for chaining
*/
add(selector, sort = true) {
if (selector) {
if (typeof selector === 'string') {
let elements = Array.from(external('doc').querySelectorAll(selector));
addArray(this, elements);
}
else if (selector instanceof Dom) {
addArray(this, selector.get());
}
else if (typeof selector === 'object' &&
!selector.nodeName && //