@coreui/coreui-pro
Version:
The most popular front-end framework for developing responsive, mobile-first projects on the web rewritten by the CoreUI Team
1,492 lines (1,389 loc) • 378 kB
JavaScript
/*!
* CoreUI v5.14.2 (https://coreui.io)
* Copyright 2025 The CoreUI Team (https://github.com/orgs/coreui/people)
* Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE)
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('@popperjs/core')) :
typeof define === 'function' && define.amd ? define(['@popperjs/core'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.bootstrap = factory(global.Popper));
})(this, (function (Popper) { 'use strict';
function _interopNamespaceDefault(e) {
const n = Object.create(null, { [Symbol.toStringTag]: { value: 'Module' } });
if (e) {
for (const k in e) {
if (k !== 'default') {
const d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: () => e[k]
});
}
}
}
n.default = e;
return Object.freeze(n);
}
const Popper__namespace = /*#__PURE__*/_interopNamespaceDefault(Popper);
/**
* --------------------------------------------------------------------------
* CoreUI dom/data.js
* Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE)
*
* This is a modified version of the Bootstrap's dom/data.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
/**
* Constants
*/
const elementMap = new Map();
const Data = {
set(element, key, instance) {
if (!elementMap.has(element)) {
elementMap.set(element, new Map());
}
const instanceMap = elementMap.get(element);
// make it clear we only want one instance per element
// can be removed later when multiple key/instances are fine to be used
if (!instanceMap.has(key) && instanceMap.size !== 0) {
// eslint-disable-next-line no-console
console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`);
return;
}
instanceMap.set(key, instance);
},
get(element, key) {
if (elementMap.has(element)) {
return elementMap.get(element).get(key) || null;
}
return null;
},
remove(element, key) {
if (!elementMap.has(element)) {
return;
}
const instanceMap = elementMap.get(element);
instanceMap.delete(key);
// free up element references if there are no instances left for an element
if (instanceMap.size === 0) {
elementMap.delete(element);
}
}
};
/**
* --------------------------------------------------------------------------
* CoreUI util/index.js
* Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE)
*
* This is a modified version of the Bootstrap's util/index.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
const MAX_UID = 1000000;
const MILLISECONDS_MULTIPLIER = 1000;
const TRANSITION_END = 'transitionend';
/**
* Properly escape IDs selectors to handle weird IDs
* @param {string} selector
* @returns {string}
*/
const parseSelector = selector => {
if (selector && window.CSS && window.CSS.escape) {
// document.querySelector needs escaping to handle IDs (html5+) containing for instance /
selector = selector.replace(/#([^\s"#']+)/g, (match, id) => `#${CSS.escape(id)}`);
}
return selector;
};
// Shout-out Angus Croll (https://goo.gl/pxwQGp)
const toType = object => {
if (object === null || object === undefined) {
return `${object}`;
}
return Object.prototype.toString.call(object).match(/\s([a-z]+)/i)[1].toLowerCase();
};
/**
* Public Util API
*/
const getUID = prefix => {
do {
prefix += Math.floor(Math.random() * MAX_UID);
} while (document.getElementById(prefix));
return prefix;
};
const getTransitionDurationFromElement = element => {
if (!element) {
return 0;
}
// Get transition-duration of the element
let {
transitionDuration,
transitionDelay
} = window.getComputedStyle(element);
const floatTransitionDuration = Number.parseFloat(transitionDuration);
const floatTransitionDelay = Number.parseFloat(transitionDelay);
// Return 0 if element or transition duration is not found
if (!floatTransitionDuration && !floatTransitionDelay) {
return 0;
}
// If multiple durations are defined, take the first
transitionDuration = transitionDuration.split(',')[0];
transitionDelay = transitionDelay.split(',')[0];
return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER;
};
const triggerTransitionEnd = element => {
element.dispatchEvent(new Event(TRANSITION_END));
};
const isElement = object => {
if (!object || typeof object !== 'object') {
return false;
}
if (typeof object.jquery !== 'undefined') {
object = object[0];
}
return typeof object.nodeType !== 'undefined';
};
const getElement = object => {
// it's a jQuery object or a node element
if (isElement(object)) {
return object.jquery ? object[0] : object;
}
if (typeof object === 'string' && object.length > 0) {
return document.querySelector(parseSelector(object));
}
return null;
};
const isVisible = element => {
if (!isElement(element) || element.getClientRects().length === 0) {
return false;
}
const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible';
// Handle `details` element as its content may falsie appear visible when it is closed
const closedDetails = element.closest('details:not([open])');
if (!closedDetails) {
return elementIsVisible;
}
if (closedDetails !== element) {
const summary = element.closest('summary');
if (summary && summary.parentNode !== closedDetails) {
return false;
}
if (summary === null) {
return false;
}
}
return elementIsVisible;
};
const isDisabled = element => {
if (!element || element.nodeType !== Node.ELEMENT_NODE) {
return true;
}
if (element.classList.contains('disabled')) {
return true;
}
if (typeof element.disabled !== 'undefined') {
return element.disabled;
}
return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false';
};
const findShadowRoot = element => {
if (!document.documentElement.attachShadow) {
return null;
}
// Can find the shadow root otherwise it'll return the document
if (typeof element.getRootNode === 'function') {
const root = element.getRootNode();
return root instanceof ShadowRoot ? root : null;
}
if (element instanceof ShadowRoot) {
return element;
}
// when we don't find a shadow root
if (!element.parentNode) {
return null;
}
return findShadowRoot(element.parentNode);
};
const noop = () => {};
/**
* Trick to restart an element's animation
*
* @param {HTMLElement} element
* @return void
*
* @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation
*/
const reflow = element => {
element.offsetHeight; // eslint-disable-line no-unused-expressions
};
const getjQuery = () => {
if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
return window.jQuery;
}
return null;
};
const DOMContentLoadedCallbacks = [];
const onDOMContentLoaded = callback => {
if (document.readyState === 'loading') {
// add listener on the first call when the document is in loading state
if (!DOMContentLoadedCallbacks.length) {
document.addEventListener('DOMContentLoaded', () => {
for (const callback of DOMContentLoadedCallbacks) {
callback();
}
});
}
DOMContentLoadedCallbacks.push(callback);
} else {
callback();
}
};
const isRTL = () => document.documentElement.dir === 'rtl';
const defineJQueryPlugin = plugin => {
onDOMContentLoaded(() => {
const $ = getjQuery();
/* istanbul ignore if */
if ($) {
const name = plugin.NAME;
const JQUERY_NO_CONFLICT = $.fn[name];
$.fn[name] = plugin.jQueryInterface;
$.fn[name].Constructor = plugin;
$.fn[name].noConflict = () => {
$.fn[name] = JQUERY_NO_CONFLICT;
return plugin.jQueryInterface;
};
}
});
};
const execute = (possibleCallback, args = [], defaultValue = possibleCallback) => {
return typeof possibleCallback === 'function' ? possibleCallback.call(...args) : defaultValue;
};
const executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {
if (!waitForTransition) {
execute(callback);
return;
}
const durationPadding = 5;
const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding;
let called = false;
const handler = ({
target
}) => {
if (target !== transitionElement) {
return;
}
called = true;
transitionElement.removeEventListener(TRANSITION_END, handler);
execute(callback);
};
transitionElement.addEventListener(TRANSITION_END, handler);
setTimeout(() => {
if (!called) {
triggerTransitionEnd(transitionElement);
}
}, emulatedDuration);
};
/**
* Return the previous/next element of a list.
*
* @param {array} list The list of elements
* @param activeElement The active element
* @param shouldGetNext Choose to get next or previous element
* @param isCycleAllowed
* @return {Element|elem} The proper element
*/
const getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {
const listLength = list.length;
let index = list.indexOf(activeElement);
// if the element does not exist in the list return an element
// depending on the direction and if cycle is allowed
if (index === -1) {
return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0];
}
index += shouldGetNext ? 1 : -1;
if (isCycleAllowed) {
index = (index + listLength) % listLength;
}
return list[Math.max(0, Math.min(index, listLength - 1))];
};
/**
* --------------------------------------------------------------------------
* CoreUI dom/event-handler.js
* Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE)
*
* This is a modified version of the Bootstrap's dom/event-handler.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
/**
* Constants
*/
const namespaceRegex = /[^.]*(?=\..*)\.|.*/;
const stripNameRegex = /\..*/;
const stripUidRegex = /::\d+$/;
const eventRegistry = {}; // Events storage
let uidEvent = 1;
const customEvents = {
mouseenter: 'mouseover',
mouseleave: 'mouseout'
};
const nativeEvents = new Set(['click', 'dblclick', 'mouseup', 'mousedown', 'contextmenu', 'mousewheel', 'DOMMouseScroll', 'mouseover', 'mouseout', 'mousemove', 'selectstart', 'selectend', 'keydown', 'keypress', 'keyup', 'orientationchange', 'touchstart', 'touchmove', 'touchend', 'touchcancel', 'pointerdown', 'pointermove', 'pointerup', 'pointerleave', 'pointercancel', 'gesturestart', 'gesturechange', 'gestureend', 'focus', 'blur', 'change', 'reset', 'select', 'submit', 'focusin', 'focusout', 'load', 'unload', 'beforeunload', 'resize', 'move', 'DOMContentLoaded', 'readystatechange', 'error', 'abort', 'scroll']);
/**
* Private methods
*/
function makeEventUid(element, uid) {
return uid && `${uid}::${uidEvent++}` || element.uidEvent || uidEvent++;
}
function getElementEvents(element) {
const uid = makeEventUid(element);
element.uidEvent = uid;
eventRegistry[uid] = eventRegistry[uid] || {};
return eventRegistry[uid];
}
function bootstrapHandler(element, fn) {
return function handler(event) {
hydrateObj(event, {
delegateTarget: element
});
if (handler.oneOff) {
EventHandler.off(element, event.type, fn);
}
return fn.apply(element, [event]);
};
}
function bootstrapDelegationHandler(element, selector, fn) {
return function handler(event) {
const domElements = element.querySelectorAll(selector);
for (let {
target
} = event; target && target !== this; target = target.parentNode) {
for (const domElement of domElements) {
if (domElement !== target) {
continue;
}
hydrateObj(event, {
delegateTarget: target
});
if (handler.oneOff) {
EventHandler.off(element, event.type, selector, fn);
}
return fn.apply(target, [event]);
}
}
};
}
function findHandler(events, callable, delegationSelector = null) {
return Object.values(events).find(event => event.callable === callable && event.delegationSelector === delegationSelector);
}
function normalizeParameters(originalTypeEvent, handler, delegationFunction) {
const isDelegated = typeof handler === 'string';
// TODO: tooltip passes `false` instead of selector, so we need to check
const callable = isDelegated ? delegationFunction : handler || delegationFunction;
let typeEvent = getTypeEvent(originalTypeEvent);
if (!nativeEvents.has(typeEvent)) {
typeEvent = originalTypeEvent;
}
return [isDelegated, callable, typeEvent];
}
function addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) {
if (typeof originalTypeEvent !== 'string' || !element) {
return;
}
let [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction);
// in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position
// this prevents the handler from being dispatched the same way as mouseover or mouseout does
if (originalTypeEvent in customEvents) {
const wrapFunction = fn => {
return function (event) {
if (!event.relatedTarget || event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget)) {
return fn.call(this, event);
}
};
};
callable = wrapFunction(callable);
}
const events = getElementEvents(element);
const handlers = events[typeEvent] || (events[typeEvent] = {});
const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null);
if (previousFunction) {
previousFunction.oneOff = previousFunction.oneOff && oneOff;
return;
}
const uid = makeEventUid(callable, originalTypeEvent.replace(namespaceRegex, ''));
const fn = isDelegated ? bootstrapDelegationHandler(element, handler, callable) : bootstrapHandler(element, callable);
fn.delegationSelector = isDelegated ? handler : null;
fn.callable = callable;
fn.oneOff = oneOff;
fn.uidEvent = uid;
handlers[uid] = fn;
element.addEventListener(typeEvent, fn, isDelegated);
}
function removeHandler(element, events, typeEvent, handler, delegationSelector) {
const fn = findHandler(events[typeEvent], handler, delegationSelector);
if (!fn) {
return;
}
element.removeEventListener(typeEvent, fn, Boolean(delegationSelector));
delete events[typeEvent][fn.uidEvent];
}
function removeNamespacedHandlers(element, events, typeEvent, namespace) {
const storeElementEvent = events[typeEvent] || {};
for (const [handlerKey, event] of Object.entries(storeElementEvent)) {
if (handlerKey.includes(namespace)) {
removeHandler(element, events, typeEvent, event.callable, event.delegationSelector);
}
}
}
function getTypeEvent(event) {
// allow to get the native events from namespaced events ('click.bs.button' --> 'click')
event = event.replace(stripNameRegex, '');
return customEvents[event] || event;
}
const EventHandler = {
on(element, event, handler, delegationFunction) {
addHandler(element, event, handler, delegationFunction, false);
},
one(element, event, handler, delegationFunction) {
addHandler(element, event, handler, delegationFunction, true);
},
off(element, originalTypeEvent, handler, delegationFunction) {
if (typeof originalTypeEvent !== 'string' || !element) {
return;
}
const [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction);
const inNamespace = typeEvent !== originalTypeEvent;
const events = getElementEvents(element);
const storeElementEvent = events[typeEvent] || {};
const isNamespace = originalTypeEvent.startsWith('.');
if (typeof callable !== 'undefined') {
// Simplest case: handler is passed, remove that listener ONLY.
if (!Object.keys(storeElementEvent).length) {
return;
}
removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null);
return;
}
if (isNamespace) {
for (const elementEvent of Object.keys(events)) {
removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1));
}
}
for (const [keyHandlers, event] of Object.entries(storeElementEvent)) {
const handlerKey = keyHandlers.replace(stripUidRegex, '');
if (!inNamespace || originalTypeEvent.includes(handlerKey)) {
removeHandler(element, events, typeEvent, event.callable, event.delegationSelector);
}
}
},
trigger(element, event, args) {
if (typeof event !== 'string' || !element) {
return null;
}
const $ = getjQuery();
const typeEvent = getTypeEvent(event);
const inNamespace = event !== typeEvent;
let jQueryEvent = null;
let bubbles = true;
let nativeDispatch = true;
let defaultPrevented = false;
if (inNamespace && $) {
jQueryEvent = $.Event(event, args);
$(element).trigger(jQueryEvent);
bubbles = !jQueryEvent.isPropagationStopped();
nativeDispatch = !jQueryEvent.isImmediatePropagationStopped();
defaultPrevented = jQueryEvent.isDefaultPrevented();
}
const evt = hydrateObj(new Event(event, {
bubbles,
cancelable: true
}), args);
if (defaultPrevented) {
evt.preventDefault();
}
if (nativeDispatch) {
element.dispatchEvent(evt);
}
if (evt.defaultPrevented && jQueryEvent) {
jQueryEvent.preventDefault();
}
return evt;
}
};
function hydrateObj(obj, meta = {}) {
for (const [key, value] of Object.entries(meta)) {
try {
obj[key] = value;
} catch (_unused) {
Object.defineProperty(obj, key, {
configurable: true,
get() {
return value;
}
});
}
}
return obj;
}
/**
* --------------------------------------------------------------------------
* CoreUI dom/manipulator.js
* Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE)
*
* This is a modified version of the Bootstrap's dom/manipulator.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
function normalizeData(value) {
if (value === 'true') {
return true;
}
if (value === 'false') {
return false;
}
if (value === Number(value).toString()) {
return Number(value);
}
if (value === '' || value === 'null') {
return null;
}
if (typeof value !== 'string') {
return value;
}
try {
return JSON.parse(decodeURIComponent(value));
} catch (_unused) {
return value;
}
}
function normalizeDataKey(key) {
return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`);
}
const Manipulator = {
setDataAttribute(element, key, value) {
element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value);
},
removeDataAttribute(element, key) {
element.removeAttribute(`data-bs-${normalizeDataKey(key)}`);
},
getDataAttributes(element) {
if (!element) {
return {};
}
const attributes = {};
const bsKeys = Object.keys(element.dataset).filter(key => key.startsWith('bs') && !key.startsWith('bsConfig'));
for (const key of bsKeys) {
let pureKey = key.replace(/^bs/, '');
pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1);
attributes[pureKey] = normalizeData(element.dataset[key]);
}
return attributes;
},
getDataAttribute(element, key) {
return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`));
}
};
/**
* --------------------------------------------------------------------------
* CoreUI util/config.js
* Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE)
*
* This is a modified version of the Bootstrap's util/config.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
/**
* Class definition
*/
class Config {
// Getters
static get Default() {
return {};
}
static get DefaultType() {
return {};
}
static get NAME() {
throw new Error('You have to implement the static method "NAME", for each component!');
}
_getConfig(config) {
config = this._mergeConfigObj(config);
config = this._configAfterMerge(config);
this._typeCheckConfig(config);
return config;
}
_configAfterMerge(config) {
return config;
}
_mergeConfigObj(config, element) {
const jsonConfig = isElement(element) ? Manipulator.getDataAttribute(element, 'config') : {}; // try to parse
return {
...this.constructor.Default,
...(typeof jsonConfig === 'object' ? jsonConfig : {}),
...(isElement(element) ? Manipulator.getDataAttributes(element) : {}),
...(typeof config === 'object' ? config : {})
};
}
_typeCheckConfig(config, configTypes = this.constructor.DefaultType) {
for (const [property, expectedTypes] of Object.entries(configTypes)) {
const value = config[property];
const valueType = isElement(value) ? 'element' : toType(value);
if (!new RegExp(expectedTypes).test(valueType)) {
throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`);
}
}
}
}
/**
* --------------------------------------------------------------------------
* CoreUI base-component.js
* Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE)
*
* This component is a modified version of the Bootstrap's base-component.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
/**
* Constants
*/
const VERSION = '5.14.2';
/**
* Class definition
*/
class BaseComponent extends Config {
constructor(element, config) {
super();
element = getElement(element);
if (!element) {
return;
}
this._element = element;
this._config = this._getConfig(config);
Data.set(this._element, this.constructor.DATA_KEY, this);
}
// Public
dispose() {
Data.remove(this._element, this.constructor.DATA_KEY);
EventHandler.off(this._element, this.constructor.EVENT_KEY);
for (const propertyName of Object.getOwnPropertyNames(this)) {
this[propertyName] = null;
}
}
// Private
_queueCallback(callback, element, isAnimated = true) {
executeAfterTransition(callback, element, isAnimated);
}
_getConfig(config) {
config = this._mergeConfigObj(config, this._element);
config = this._configAfterMerge(config);
this._typeCheckConfig(config);
return config;
}
// Static
static getInstance(element) {
return Data.get(getElement(element), this.DATA_KEY);
}
static getOrCreateInstance(element, config = {}) {
return this.getInstance(element) || new this(element, typeof config === 'object' ? config : null);
}
static get VERSION() {
return VERSION;
}
static get DATA_KEY() {
return `bs.${this.NAME}`;
}
static get EVENT_KEY() {
return `.${this.DATA_KEY}`;
}
static eventName(name) {
return `${name}${this.EVENT_KEY}`;
}
}
/**
* --------------------------------------------------------------------------
* CoreUI dom/selector-engine.js
* Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE)
*
* This is a modified version of the Bootstrap's dom/selector-engine.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
const getSelector = element => {
let selector = element.getAttribute('data-bs-target');
if (!selector || selector === '#') {
let hrefAttribute = element.getAttribute('href');
// The only valid content that could double as a selector are IDs or classes,
// so everything starting with `#` or `.`. If a "real" URL is used as the selector,
// `document.querySelector` will rightfully complain it is invalid.
// See https://github.com/twbs/bootstrap/issues/32273
if (!hrefAttribute || !hrefAttribute.includes('#') && !hrefAttribute.startsWith('.')) {
return null;
}
// Just in case some CMS puts out a full URL with the anchor appended
if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) {
hrefAttribute = `#${hrefAttribute.split('#')[1]}`;
}
selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null;
}
return selector ? selector.split(',').map(sel => parseSelector(sel)).join(',') : null;
};
const SelectorEngine = {
find(selector, element = document.documentElement) {
return [].concat(...Element.prototype.querySelectorAll.call(element, selector));
},
findOne(selector, element = document.documentElement) {
return Element.prototype.querySelector.call(element, selector);
},
children(element, selector) {
return [].concat(...element.children).filter(child => child.matches(selector));
},
parents(element, selector) {
const parents = [];
let ancestor = element.parentNode.closest(selector);
while (ancestor) {
parents.push(ancestor);
ancestor = ancestor.parentNode.closest(selector);
}
return parents;
},
prev(element, selector) {
let previous = element.previousElementSibling;
while (previous) {
if (previous.matches(selector)) {
return [previous];
}
previous = previous.previousElementSibling;
}
return [];
},
// TODO: this is now unused; remove later along with prev()
next(element, selector) {
let next = element.nextElementSibling;
while (next) {
if (next.matches(selector)) {
return [next];
}
next = next.nextElementSibling;
}
return [];
},
focusableChildren(element) {
const focusables = ['a', 'button', 'input', 'textarea', 'select', 'details', '[tabindex]', '[contenteditable="true"]'].map(selector => `${selector}:not([tabindex^="-"])`).join(',');
return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el));
},
getSelectorFromElement(element) {
const selector = getSelector(element);
if (selector) {
return SelectorEngine.findOne(selector) ? selector : null;
}
return null;
},
getElementFromSelector(element) {
const selector = getSelector(element);
return selector ? SelectorEngine.findOne(selector) : null;
},
getMultipleElementsFromSelector(element) {
const selector = getSelector(element);
return selector ? SelectorEngine.find(selector) : [];
}
};
/**
* --------------------------------------------------------------------------
* CoreUI util/component-functions.js
* Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE)
*
* This is a modified version of the Bootstrap's util/component-functions.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
const enableDismissTrigger = (component, method = 'hide') => {
const clickEvent = `click.dismiss${component.EVENT_KEY}`;
const name = component.NAME;
EventHandler.on(document, clickEvent, `[data-bs-dismiss="${name}"]`, function (event) {
if (['A', 'AREA'].includes(this.tagName)) {
event.preventDefault();
}
if (isDisabled(this)) {
return;
}
const target = SelectorEngine.getElementFromSelector(this) || this.closest(`.${name}`);
const instance = component.getOrCreateInstance(target);
// Method argument is left, for Alert and only, as it doesn't implement the 'hide' method
instance[method]();
});
};
/**
* --------------------------------------------------------------------------
* CoreUI alert.js
* Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE)
*
* This component is a modified version of the Bootstrap's alert.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
/**
* Constants
*/
const NAME$r = 'alert';
const DATA_KEY$m = 'bs.alert';
const EVENT_KEY$n = `.${DATA_KEY$m}`;
const EVENT_CLOSE = `close${EVENT_KEY$n}`;
const EVENT_CLOSED = `closed${EVENT_KEY$n}`;
const CLASS_NAME_FADE$5 = 'fade';
const CLASS_NAME_SHOW$f = 'show';
/**
* Class definition
*/
class Alert extends BaseComponent {
// Getters
static get NAME() {
return NAME$r;
}
// Public
close() {
const closeEvent = EventHandler.trigger(this._element, EVENT_CLOSE);
if (closeEvent.defaultPrevented) {
return;
}
this._element.classList.remove(CLASS_NAME_SHOW$f);
const isAnimated = this._element.classList.contains(CLASS_NAME_FADE$5);
this._queueCallback(() => this._destroyElement(), this._element, isAnimated);
}
// Private
_destroyElement() {
this._element.remove();
EventHandler.trigger(this._element, EVENT_CLOSED);
this.dispose();
}
// Static
static jQueryInterface(config) {
return this.each(function () {
const data = Alert.getOrCreateInstance(this);
if (typeof config !== 'string') {
return;
}
if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {
throw new TypeError(`No method named "${config}"`);
}
data[config](this);
});
}
}
/**
* Data API implementation
*/
enableDismissTrigger(Alert, 'close');
/**
* jQuery
*/
defineJQueryPlugin(Alert);
/**
* --------------------------------------------------------------------------
* CoreUI button.js
* Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE)
*
* This component is a modified version of the Bootstrap's button.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
/**
* Constants
*/
const NAME$q = 'button';
const DATA_KEY$l = 'bs.button';
const EVENT_KEY$m = `.${DATA_KEY$l}`;
const DATA_API_KEY$h = '.data-api';
const CLASS_NAME_ACTIVE$6 = 'active';
const SELECTOR_DATA_TOGGLE$f = '[data-bs-toggle="button"]';
const EVENT_CLICK_DATA_API$g = `click${EVENT_KEY$m}${DATA_API_KEY$h}`;
/**
* Class definition
*/
class Button extends BaseComponent {
// Getters
static get NAME() {
return NAME$q;
}
// Public
toggle() {
// Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method
this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE$6));
}
// Static
static jQueryInterface(config) {
return this.each(function () {
const data = Button.getOrCreateInstance(this);
if (config === 'toggle') {
data[config]();
}
});
}
}
/**
* Data API implementation
*/
EventHandler.on(document, EVENT_CLICK_DATA_API$g, SELECTOR_DATA_TOGGLE$f, event => {
event.preventDefault();
const button = event.target.closest(SELECTOR_DATA_TOGGLE$f);
const data = Button.getOrCreateInstance(button);
data.toggle();
});
/**
* jQuery
*/
defineJQueryPlugin(Button);
/**
* Converts an ISO week string to a Date object representing the Monday of that week.
* @param isoWeek - The ISO week string (e.g., "2023W05" or "2023w05").
* @returns The Date object for the Monday of the specified week, or null if invalid.
*/
const convertIsoWeekToDate = isoWeek => {
const [year, week] = isoWeek.split(/[Ww]/);
const date = new Date(Number(year), 0, 4); // 4th Jan is always in week 1
date.setDate(date.getDate() - (date.getDay() || 7) + 1 + (Number(week) - 1) * 7);
return date;
};
/**
* Converts a date string or Date object to a Date object based on selection type.
* @param date - The date to convert.
* @param selectionType - The type of selection ('day', 'week', 'month', 'year').
* @returns The corresponding Date object or null if invalid.
*/
const convertToDateObject = (date, selectionType) => {
if (date === null) {
return null;
}
if (date instanceof Date) {
return date;
}
if (selectionType === 'week') {
return convertIsoWeekToDate(date);
}
if (selectionType === 'month' || selectionType === 'year') {
const _date = new Date(Date.parse(date));
const userTimezoneOffset = _date.getTimezoneOffset() * 60000;
return new Date(_date.getTime() + userTimezoneOffset);
}
return new Date(Date.parse(date));
};
/**
* Creates groups from an array.
* @param arr - The array to group.
* @param numberOfGroups - Number of groups to create.
* @returns An array of grouped arrays.
*/
const createGroupsInArray = (arr, numberOfGroups) => {
const perGroup = Math.ceil(arr.length / numberOfGroups);
return Array.from({
length: numberOfGroups
}).fill('').map((_, i) => arr.slice(i * perGroup, (i + 1) * perGroup));
};
/**
* Adjusts the calendar date based on order and view type.
* @param calendarDate - The current calendar date.
* @param order - The order to adjust by.
* @param view - The current view type.
* @returns The adjusted Date object.
*/
const getCalendarDate = (calendarDate, order, view) => {
if (order !== 0 && view === 'days') {
return new Date(calendarDate.getFullYear(), calendarDate.getMonth() + order, 1);
}
if (order !== 0 && view === 'months') {
return new Date(calendarDate.getFullYear() + order, calendarDate.getMonth(), 1);
}
if (order !== 0 && view === 'years') {
return new Date(calendarDate.getFullYear() + 12 * order, calendarDate.getMonth(), 1);
}
return calendarDate;
};
/**
* Formats a date based on the selection type.
* @param date - The date to format.
* @param selectionType - The type of selection ('day', 'week', 'month', 'year').
* @returns A formatted date string or the original Date object.
*/
const getDateBySelectionType = (date, selectionType) => {
if (date === null) {
return null;
}
if (selectionType === 'week') {
return `${date.getFullYear()}W${getWeekNumber(date)}`;
}
if (selectionType === 'month') {
const monthNumber = `0${date.getMonth() + 1}`.slice(-2);
return `${date.getFullYear()}-${monthNumber}`;
}
if (selectionType === 'year') {
return `${date.getFullYear()}`;
}
return date;
};
/**
* Retrieves an array of month names based on locale and format.
* @param locale - The locale string (e.g., 'en-US').
* @param format - The format of the month names ('short' or 'long').
* @returns An array of month names.
*/
const getMonthsNames = (locale, format = 'short') => {
return Array.from({
length: 12
}, (_, i) => {
return new Date(2000, i, 1).toLocaleString(locale, {
month: format
});
});
};
/**
* Generates an array of years centered around a given year.
* @param year - The central year.
* @param range - The number of years before and after the central year.
* @returns An array of years.
*/
const getYears = (year, range = 6) => {
return Array.from({
length: range * 2
}, (_, i) => year - range + i);
};
/**
* Retrieves leading days (from the previous month) for a calendar view.
* @param year - The year.
* @param month - The month (0-11).
* @param firstDayOfWeek - The first day of the week (0-6, where 0 is Sunday).
* @returns An array of leading day objects.
*/
const getLeadingDays = (year, month, firstDayOfWeek) => {
// 0: sunday
// 1: monday
const dates = [];
const d = new Date(year, month);
const y = d.getFullYear();
const m = d.getMonth();
const firstWeekday = new Date(y, m, 1).getDay();
let leadingDays = 6 - (6 - firstWeekday) - firstDayOfWeek;
if (firstDayOfWeek) {
leadingDays = leadingDays < 0 ? 7 + leadingDays : leadingDays;
}
for (let i = leadingDays * -1; i < 0; i++) {
dates.push({
date: new Date(y, m, i + 1),
month: 'previous'
});
}
return dates;
};
/**
* Retrieves all days within a specific month.
* @param year - The year.
* @param month - The month (0-11).
* @returns An array of day objects.
*/
const getMonthDays = (year, month) => {
const dates = [];
const lastDay = new Date(year, month + 1, 0).getDate();
for (let i = 1; i <= lastDay; i++) {
dates.push({
date: new Date(year, month, i),
month: 'current'
});
}
return dates;
};
/**
* Retrieves trailing days (from the next month) for a calendar view.
* @param year - The year.
* @param month - The month (0-11).
* @param leadingDays - Array of leading day objects.
* @param monthDays - Array of current month day objects.
* @returns An array of trailing day objects.
*/
const getTrailingDays = (year, month, leadingDays, monthDays) => {
const dates = [];
const days = 42 - (leadingDays.length + monthDays.length);
for (let i = 1; i <= days; i++) {
dates.push({
date: new Date(year, month + 1, i),
month: 'next'
});
}
return dates;
};
/**
* Calculates the ISO week number for a given date.
* @param date - The date to calculate the week number for.
* @returns The ISO week number.
*/
const getWeekNumber = date => {
const tempDate = new Date(date);
tempDate.setHours(0, 0, 0, 0);
// Thursday in current week decides the year
tempDate.setDate(tempDate.getDate() + 3 - (tempDate.getDay() + 6) % 7);
const week1 = new Date(tempDate.getFullYear(), 0, 4);
// Calculate full weeks to the date
const weekNumber = 1 + Math.round((tempDate.getTime() - week1.getTime()) / 86400000 / 7);
return weekNumber;
};
/**
* Retrieves detailed information about each week in a month for calendar rendering.
* @param year - The year.
* @param month - The month (0-11).
* @param firstDayOfWeek - The first day of the week (0-6, where 0 is Sunday).
* @returns An array of week objects containing week numbers and day details.
*/
const getMonthDetails = (year, month, firstDayOfWeek) => {
const daysPrevMonth = getLeadingDays(year, month, firstDayOfWeek);
const daysThisMonth = getMonthDays(year, month);
const daysNextMonth = getTrailingDays(year, month, daysPrevMonth, daysThisMonth);
const days = [...daysPrevMonth, ...daysThisMonth, ...daysNextMonth];
const weeks = [];
for (const [index, day] of days.entries()) {
if (index % 7 === 0 || weeks.length === 0) {
weeks.push({
days: []
});
}
if ((index + 1) % 7 === 0) {
weeks[weeks.length - 1].weekNumber = getWeekNumber(day.date);
}
weeks[weeks.length - 1].days.push(day);
}
return weeks;
};
/**
* Checks if a date is disabled based on the 'date' period type.
* @param date - The date to check.
* @param min - Minimum allowed date.
* @param max - Maximum allowed date.
* @param disabledDates - Criteria for disabled dates.
* @returns True if the date is disabled, false otherwise.
*/
const isDateDisabled = (date, min, max, disabledDates) => {
if (min && date < min) {
return true;
}
if (max && date > max) {
return true;
}
if (disabledDates === undefined) {
return false;
}
if (typeof disabledDates === 'function') {
return disabledDates(date);
}
if (disabledDates instanceof Date && isSameDateAs(date, disabledDates)) {
return true;
}
if (Array.isArray(disabledDates) && disabledDates) {
for (const _date of disabledDates) {
if (typeof _date === 'function' && _date(date)) {
return true;
}
if (Array.isArray(_date) && isDateInRange(date, _date[0], _date[1])) {
return true;
}
if (_date instanceof Date && isSameDateAs(date, _date)) {
return true;
}
}
}
return false;
};
/**
* Checks if a date is within a specified range.
* @param date - The date to check.
* @param start - Start date of the range.
* @param end - End date of the range.
* @returns True if the date is within the range, false otherwise.
*/
const isDateInRange = (date, start, end) => {
const _date = removeTimeFromDate(date);
const _start = start ? removeTimeFromDate(start) : null;
const _end = end ? removeTimeFromDate(end) : null;
return Boolean(_start && _end && _start <= _date && _date <= _end);
};
/**
* Checks if a date is selected based on start and end dates.
* @param date - The date to check.
* @param start - Start date.
* @param end - End date.
* @returns True if the date is selected, false otherwise.
*/
const isDateSelected = (date, start, end) => {
if (start !== null && isSameDateAs(start, date)) {
return true;
}
if (end !== null && isSameDateAs(end, date)) {
return true;
}
return false;
};
/**
* Determines if any date within a range is disabled.
* @param startDate - Start date of the range.
* @param endDate - End date of the range.
* @param disabledDates - Criteria for disabled dates.
* @returns True if any date in the range is disabled, false otherwise.
*/
const isDisableDateInRange = (startDate, endDate, disabledDates) => {
if (startDate && endDate) {
const date = new Date(startDate);
let disabled = false;
// eslint-disable-next-line no-unmodified-loop-condition
while (date < endDate) {
date.setDate(date.getDate() + 1);
if (isDateDisabled(date, null, null, disabledDates)) {
disabled = true;
break;
}
}
return disabled;
}
return false;
};
/**
* Checks if a month is disabled based on the 'month' period type.
* @param date - The date representing the month to check.
* @param min - Minimum allowed date.
* @param max - Maximum allowed date.
* @param disabledDates - Criteria for disabled dates.
* @returns True if the month is disabled, false otherwise.
*/
const isMonthDisabled = (date, min, max, disabledDates) => {
const current = date.getFullYear() * 12 + date.getMonth();
const _min = min ? min.getFullYear() * 12 + min.getMonth() : null;
const _max = max ? max.getFullYear() * 12 + max.getMonth() : null;
if (_min && current < _min) {
return true;
}
if (_max && current > _max) {
return true;
}
if (disabledDates === undefined) {
return false;
}
const start = min ? Math.max(date.getTime(), min.getTime()) : date;
const end = max ? Math.min(date.getTime(), max.getTime()) : new Date(new Date().getFullYear(), 11, 31);
for (const currentDate = new Date(start);
// eslint-disable-next-line no-unmodified-loop-condition
currentDate <= end; currentDate.setDate(currentDate.getDate() + 1)) {
if (!isDateDisabled(currentDate, min, max, disabledDates)) {
return false;
}
}
return false;
};
/**
* Checks if a month is selected based on start and end dates.
* @param date - The date representing the month.
* @param start - Start date.
* @param end - End date.
* @returns True if the month is selected, false otherwise.
*/
const isMonthSelected = (date, start, end) => {
const year = date.getFullYear();
const month = date.getMonth();
if (start !== null && year === start.getFullYear() && month === start.getMonth()) {
return true;
}
if (end !== null && year === end.getFullYear() && month === end.getMonth()) {
return true;
}
return false;
};
/**
* Checks if a month is within a specified range.
* @param date - The date representing the month.
* @param start - Start date.
* @param end - End date.
* @returns True if the month is within the range, false otherwise.
*/
const isMonthInRange = (date, start, end) => {
const year = date.getFullYear();
const month = date.getMonth();
const _start = start ? start.getFullYear() * 12 + start.getMonth() : null;
const _end = end ? end.getFullYear() * 12 + end.getMonth() : null;
const _date = year * 12 + month;
return Boolean(_start && _end && _start <= _date && _date <= _end);
};
/**
* Checks if two dates are the same calendar date.
* @param date - First date.
* @param date2 - Second date.
* @returns True if both dates are the same, false otherwise.
*/
const isSameDateAs = (date, date2) => {
if (date instanceof Date && date2 instanceof Date) {
return date.getDate() === date2.getDate() && date.getMonth() === date2.getMonth() && date.getFullYear() === date2.getFullYear();
}
if (date === null && date2 === null) {
return true;
}
return false;
};
/**
* Checks if a date is today.
* @param date - The date to check.
* @returns True if the date is today, false otherwise.
*/
const isToday = date => {
const today = new Date();
return isSameDateAs(date, today);
};
/**
* Checks if a year is disabled based on the 'year' period type.
* @param date - The date representing the year to check.
* @param min - Minimum allowed date.
* @param max - Maximum allowed date.
* @param disabledDates - Criteria for disabled dates.
* @returns True if the year is disabled, false otherwise.
*/
const isYearDisabled = (date, min, max, disabledDates) => {
const year = date.getFullYear();
const minYear = min ? min.getFullYear() : null;
const maxYear = max ? max.getFullYear() : null;
if (minYear && year < minYear) {
return true;
}
if (maxYear && year > maxYear) {
return true;
}
if (disabledDates === undefined) {
return false;
}
const start = min ? Math.max(date.getTime(), min.getTime()) : date;
const end = max ? Math.min(date.getTime(), max.getTime()) : new Date(new Date().getFullYear(), 11, 31);
for (const currentDate = new Date(start);
// eslint-disable-next-line no-unmodified-loop-condition
currentDate <= end; currentDate.setDate(currentDate.getDate() + 1)) {
if (!isDateDisabled(currentDate, min, max, disabledDates)) {
return false;
}
}
return false;
};
/**
* Checks if a year is selected based on start and end dates.
* @param date - The date representing the year.
* @param start - Start date.
* @param end - End date.
* @returns True if the year matches the start's or end's year, false otherwise.
*/
const isYearSelected = (date, start, end) => {
const year = date.getFullYear();
if (start !== null && year === start.getFullYear()) {
return true;
}
if (end !== null && year === end.getFullYear()) {
return true;
}
return false;
};
/**
* Checks if a year is within a specified range.
* @param date - The date representing the year.
* @param start - Start date.
* @param end - End date.
* @returns True if the year's value lies between start's year and end's year, false otherwise.
*/
const isYearInRange = (date, start, end) => {
const year = date.getFullYear();
cons