You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

237 lines
9.2 KiB

5 months ago
  1. /*!
  2. * Bootstrap event-handler.js v5.3.3 (https://getbootstrap.com/)
  3. * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
  4. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
  5. */
  6. (function (global, factory) {
  7. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('../util/index.js')) :
  8. typeof define === 'function' && define.amd ? define(['../util/index'], factory) :
  9. (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.EventHandler = factory(global.Index));
  10. })(this, (function (index_js) { 'use strict';
  11. /**
  12. * --------------------------------------------------------------------------
  13. * Bootstrap dom/event-handler.js
  14. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
  15. * --------------------------------------------------------------------------
  16. */
  17. /**
  18. * Constants
  19. */
  20. const namespaceRegex = /[^.]*(?=\..*)\.|.*/;
  21. const stripNameRegex = /\..*/;
  22. const stripUidRegex = /::\d+$/;
  23. const eventRegistry = {}; // Events storage
  24. let uidEvent = 1;
  25. const customEvents = {
  26. mouseenter: 'mouseover',
  27. mouseleave: 'mouseout'
  28. };
  29. 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']);
  30. /**
  31. * Private methods
  32. */
  33. function makeEventUid(element, uid) {
  34. return uid && `${uid}::${uidEvent++}` || element.uidEvent || uidEvent++;
  35. }
  36. function getElementEvents(element) {
  37. const uid = makeEventUid(element);
  38. element.uidEvent = uid;
  39. eventRegistry[uid] = eventRegistry[uid] || {};
  40. return eventRegistry[uid];
  41. }
  42. function bootstrapHandler(element, fn) {
  43. return function handler(event) {
  44. hydrateObj(event, {
  45. delegateTarget: element
  46. });
  47. if (handler.oneOff) {
  48. EventHandler.off(element, event.type, fn);
  49. }
  50. return fn.apply(element, [event]);
  51. };
  52. }
  53. function bootstrapDelegationHandler(element, selector, fn) {
  54. return function handler(event) {
  55. const domElements = element.querySelectorAll(selector);
  56. for (let {
  57. target
  58. } = event; target && target !== this; target = target.parentNode) {
  59. for (const domElement of domElements) {
  60. if (domElement !== target) {
  61. continue;
  62. }
  63. hydrateObj(event, {
  64. delegateTarget: target
  65. });
  66. if (handler.oneOff) {
  67. EventHandler.off(element, event.type, selector, fn);
  68. }
  69. return fn.apply(target, [event]);
  70. }
  71. }
  72. };
  73. }
  74. function findHandler(events, callable, delegationSelector = null) {
  75. return Object.values(events).find(event => event.callable === callable && event.delegationSelector === delegationSelector);
  76. }
  77. function normalizeParameters(originalTypeEvent, handler, delegationFunction) {
  78. const isDelegated = typeof handler === 'string';
  79. // TODO: tooltip passes `false` instead of selector, so we need to check
  80. const callable = isDelegated ? delegationFunction : handler || delegationFunction;
  81. let typeEvent = getTypeEvent(originalTypeEvent);
  82. if (!nativeEvents.has(typeEvent)) {
  83. typeEvent = originalTypeEvent;
  84. }
  85. return [isDelegated, callable, typeEvent];
  86. }
  87. function addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) {
  88. if (typeof originalTypeEvent !== 'string' || !element) {
  89. return;
  90. }
  91. let [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction);
  92. // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position
  93. // this prevents the handler from being dispatched the same way as mouseover or mouseout does
  94. if (originalTypeEvent in customEvents) {
  95. const wrapFunction = fn => {
  96. return function (event) {
  97. if (!event.relatedTarget || event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget)) {
  98. return fn.call(this, event);
  99. }
  100. };
  101. };
  102. callable = wrapFunction(callable);
  103. }
  104. const events = getElementEvents(element);
  105. const handlers = events[typeEvent] || (events[typeEvent] = {});
  106. const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null);
  107. if (previousFunction) {
  108. previousFunction.oneOff = previousFunction.oneOff && oneOff;
  109. return;
  110. }
  111. const uid = makeEventUid(callable, originalTypeEvent.replace(namespaceRegex, ''));
  112. const fn = isDelegated ? bootstrapDelegationHandler(element, handler, callable) : bootstrapHandler(element, callable);
  113. fn.delegationSelector = isDelegated ? handler : null;
  114. fn.callable = callable;
  115. fn.oneOff = oneOff;
  116. fn.uidEvent = uid;
  117. handlers[uid] = fn;
  118. element.addEventListener(typeEvent, fn, isDelegated);
  119. }
  120. function removeHandler(element, events, typeEvent, handler, delegationSelector) {
  121. const fn = findHandler(events[typeEvent], handler, delegationSelector);
  122. if (!fn) {
  123. return;
  124. }
  125. element.removeEventListener(typeEvent, fn, Boolean(delegationSelector));
  126. delete events[typeEvent][fn.uidEvent];
  127. }
  128. function removeNamespacedHandlers(element, events, typeEvent, namespace) {
  129. const storeElementEvent = events[typeEvent] || {};
  130. for (const [handlerKey, event] of Object.entries(storeElementEvent)) {
  131. if (handlerKey.includes(namespace)) {
  132. removeHandler(element, events, typeEvent, event.callable, event.delegationSelector);
  133. }
  134. }
  135. }
  136. function getTypeEvent(event) {
  137. // allow to get the native events from namespaced events ('click.bs.button' --> 'click')
  138. event = event.replace(stripNameRegex, '');
  139. return customEvents[event] || event;
  140. }
  141. const EventHandler = {
  142. on(element, event, handler, delegationFunction) {
  143. addHandler(element, event, handler, delegationFunction, false);
  144. },
  145. one(element, event, handler, delegationFunction) {
  146. addHandler(element, event, handler, delegationFunction, true);
  147. },
  148. off(element, originalTypeEvent, handler, delegationFunction) {
  149. if (typeof originalTypeEvent !== 'string' || !element) {
  150. return;
  151. }
  152. const [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction);
  153. const inNamespace = typeEvent !== originalTypeEvent;
  154. const events = getElementEvents(element);
  155. const storeElementEvent = events[typeEvent] || {};
  156. const isNamespace = originalTypeEvent.startsWith('.');
  157. if (typeof callable !== 'undefined') {
  158. // Simplest case: handler is passed, remove that listener ONLY.
  159. if (!Object.keys(storeElementEvent).length) {
  160. return;
  161. }
  162. removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null);
  163. return;
  164. }
  165. if (isNamespace) {
  166. for (const elementEvent of Object.keys(events)) {
  167. removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1));
  168. }
  169. }
  170. for (const [keyHandlers, event] of Object.entries(storeElementEvent)) {
  171. const handlerKey = keyHandlers.replace(stripUidRegex, '');
  172. if (!inNamespace || originalTypeEvent.includes(handlerKey)) {
  173. removeHandler(element, events, typeEvent, event.callable, event.delegationSelector);
  174. }
  175. }
  176. },
  177. trigger(element, event, args) {
  178. if (typeof event !== 'string' || !element) {
  179. return null;
  180. }
  181. const $ = index_js.getjQuery();
  182. const typeEvent = getTypeEvent(event);
  183. const inNamespace = event !== typeEvent;
  184. let jQueryEvent = null;
  185. let bubbles = true;
  186. let nativeDispatch = true;
  187. let defaultPrevented = false;
  188. if (inNamespace && $) {
  189. jQueryEvent = $.Event(event, args);
  190. $(element).trigger(jQueryEvent);
  191. bubbles = !jQueryEvent.isPropagationStopped();
  192. nativeDispatch = !jQueryEvent.isImmediatePropagationStopped();
  193. defaultPrevented = jQueryEvent.isDefaultPrevented();
  194. }
  195. const evt = hydrateObj(new Event(event, {
  196. bubbles,
  197. cancelable: true
  198. }), args);
  199. if (defaultPrevented) {
  200. evt.preventDefault();
  201. }
  202. if (nativeDispatch) {
  203. element.dispatchEvent(evt);
  204. }
  205. if (evt.defaultPrevented && jQueryEvent) {
  206. jQueryEvent.preventDefault();
  207. }
  208. return evt;
  209. }
  210. };
  211. function hydrateObj(obj, meta = {}) {
  212. for (const [key, value] of Object.entries(meta)) {
  213. try {
  214. obj[key] = value;
  215. } catch (_unused) {
  216. Object.defineProperty(obj, key, {
  217. configurable: true,
  218. get() {
  219. return value;
  220. }
  221. });
  222. }
  223. }
  224. return obj;
  225. }
  226. return EventHandler;
  227. }));
  228. //# sourceMappingURL=event-handler.js.map