blob: f83d69c147965e3c0d43abb9f6bef7dab7166403 [file] [edit]
/*!
* Bootstrap event-handler.js v6.0.0-alpha1 (https://getbootstrap.com/)
* Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
//#region js/src/dom/event-handler.ts
/**
* Constants
*/
const namespaceRegex = /[^.]*(?=\..*)\.|.*/;
const stripNameRegex = /\..*/;
const stripUidRegex = /::\d+$/;
const eventRegistry = {};
let uidEvent = 1;
const customEvents = {
mouseenter: "mouseover",
mouseleave: "mouseout"
};
const nativeEvents = /* @__PURE__ */ 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",
"scrollend"
]);
/**
* 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 isMouseEventWithinTarget(event) {
const { delegateTarget, relatedTarget } = event;
return Boolean(relatedTarget && delegateTarget.contains(relatedTarget));
}
function bootstrapHandler(element, fn, handlerTypeEvent) {
const isCustomMouseEvent = handlerTypeEvent in customEvents;
return function handler(event) {
const bootstrapEvent = hydrateObj(event, { delegateTarget: element });
if (isCustomMouseEvent && isMouseEventWithinTarget(bootstrapEvent)) return;
if (handler.oneOff) EventHandler.off(element, handlerTypeEvent, fn);
return fn.apply(element, [bootstrapEvent]);
};
}
function bootstrapDelegationHandler(element, selector, fn, handlerTypeEvent) {
const isCustomMouseEvent = handlerTypeEvent in customEvents;
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;
const bootstrapEvent = hydrateObj(event, { delegateTarget: target });
if (isCustomMouseEvent && isMouseEventWithinTarget(bootstrapEvent)) return;
if (handler.oneOff) EventHandler.off(element, handlerTypeEvent, selector, fn);
return fn.apply(target, [bootstrapEvent]);
}
};
}
function findHandler(events, callable, handlerTypeEvent, delegationSelector = null) {
return Object.values(events).find((event) => event.callable === callable && event.handlerTypeEvent === handlerTypeEvent && event.delegationSelector === delegationSelector);
}
function normalizeParameters(originalTypeEvent, handler, delegationFunction) {
const isDelegated = typeof handler === "string";
const callable = isDelegated ? delegationFunction : handler || delegationFunction;
const baseTypeEvent = originalTypeEvent.replace(stripNameRegex, "");
let typeEvent = customEvents[baseTypeEvent] || baseTypeEvent;
if (!nativeEvents.has(typeEvent)) typeEvent = originalTypeEvent;
const handlerTypeEvent = baseTypeEvent in customEvents ? baseTypeEvent : typeEvent;
return {
isDelegated,
callable,
typeEvent,
handlerTypeEvent
};
}
function addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) {
if (typeof originalTypeEvent !== "string" || !element) return;
const { isDelegated, callable, typeEvent, handlerTypeEvent } = normalizeParameters(originalTypeEvent, handler, delegationFunction);
const events = getElementEvents(element);
const handlers = events[typeEvent] || (events[typeEvent] = {});
const previousFunction = findHandler(handlers, callable, handlerTypeEvent, 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, handlerTypeEvent) : bootstrapHandler(element, callable, handlerTypeEvent);
fn.delegationSelector = isDelegated ? handler : null;
fn.callable = callable;
fn.handlerTypeEvent = handlerTypeEvent;
fn.oneOff = oneOff;
fn.uidEvent = uid;
handlers[uid] = fn;
element.addEventListener(typeEvent, fn, isDelegated);
}
function removeHandler(element, events, typeEvent, handler) {
element.removeEventListener(typeEvent, handler, Boolean(handler.delegationSelector));
delete events[typeEvent][handler.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);
}
function trigger(element, event, args) {
if (typeof event !== "string" || !element) return null;
const evt = hydrateObj(new Event(event, {
bubbles: true,
cancelable: true
}), args);
element.dispatchEvent(evt);
return evt;
}
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, handlerTypeEvent } = normalizeParameters(originalTypeEvent, handler, delegationFunction);
const inNamespace = typeEvent !== originalTypeEvent && handlerTypeEvent !== originalTypeEvent;
const events = getElementEvents(element);
const storeElementEvent = events[typeEvent] || {};
const isNamespace = originalTypeEvent.startsWith(".");
if (typeof callable !== "undefined") {
if (!Object.keys(storeElementEvent).length) return;
const fn = findHandler(storeElementEvent, callable, handlerTypeEvent, isDelegated ? handler : null);
if (fn) removeHandler(element, events, typeEvent, fn);
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 (event.handlerTypeEvent === handlerTypeEvent && (!inNamespace || originalTypeEvent.includes(handlerKey))) removeHandler(element, events, typeEvent, event);
}
},
trigger
};
function hydrateObj(obj, meta = {}) {
for (const [key, value] of Object.entries(meta)) try {
obj[key] = value;
} catch {
Object.defineProperty(obj, key, {
configurable: true,
get() {
return value;
}
});
}
return obj;
}
//#endregion
export { EventHandler as default };
//# sourceMappingURL=event-handler.js.map