event-handler.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. /*!
  2. * Bootstrap event-handler.js v5.0.2 (https://getbootstrap.com/)
  3. * Copyright 2011-2021 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() :
  8. typeof define === 'function' && define.amd ? define(factory) :
  9. (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.EventHandler = factory());
  10. }(this, (function () { 'use strict';
  11. const getjQuery = () => {
  12. const {
  13. jQuery
  14. } = window;
  15. if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
  16. return jQuery;
  17. }
  18. return null;
  19. };
  20. /**
  21. * --------------------------------------------------------------------------
  22. * Bootstrap (v5.0.2): dom/event-handler.js
  23. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
  24. * --------------------------------------------------------------------------
  25. */
  26. /**
  27. * ------------------------------------------------------------------------
  28. * Constants
  29. * ------------------------------------------------------------------------
  30. */
  31. const namespaceRegex = /[^.]*(?=\..*)\.|.*/;
  32. const stripNameRegex = /\..*/;
  33. const stripUidRegex = /::\d+$/;
  34. const eventRegistry = {}; // Events storage
  35. let uidEvent = 1;
  36. const customEvents = {
  37. mouseenter: 'mouseover',
  38. mouseleave: 'mouseout'
  39. };
  40. const customEventsRegex = /^(mouseenter|mouseleave)/i;
  41. 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']);
  42. /**
  43. * ------------------------------------------------------------------------
  44. * Private methods
  45. * ------------------------------------------------------------------------
  46. */
  47. function getUidEvent(element, uid) {
  48. return uid && `${uid}::${uidEvent++}` || element.uidEvent || uidEvent++;
  49. }
  50. function getEvent(element) {
  51. const uid = getUidEvent(element);
  52. element.uidEvent = uid;
  53. eventRegistry[uid] = eventRegistry[uid] || {};
  54. return eventRegistry[uid];
  55. }
  56. function bootstrapHandler(element, fn) {
  57. return function handler(event) {
  58. event.delegateTarget = element;
  59. if (handler.oneOff) {
  60. EventHandler.off(element, event.type, fn);
  61. }
  62. return fn.apply(element, [event]);
  63. };
  64. }
  65. function bootstrapDelegationHandler(element, selector, fn) {
  66. return function handler(event) {
  67. const domElements = element.querySelectorAll(selector);
  68. for (let {
  69. target
  70. } = event; target && target !== this; target = target.parentNode) {
  71. for (let i = domElements.length; i--;) {
  72. if (domElements[i] === target) {
  73. event.delegateTarget = target;
  74. if (handler.oneOff) {
  75. // eslint-disable-next-line unicorn/consistent-destructuring
  76. EventHandler.off(element, event.type, selector, fn);
  77. }
  78. return fn.apply(target, [event]);
  79. }
  80. }
  81. } // To please ESLint
  82. return null;
  83. };
  84. }
  85. function findHandler(events, handler, delegationSelector = null) {
  86. const uidEventList = Object.keys(events);
  87. for (let i = 0, len = uidEventList.length; i < len; i++) {
  88. const event = events[uidEventList[i]];
  89. if (event.originalHandler === handler && event.delegationSelector === delegationSelector) {
  90. return event;
  91. }
  92. }
  93. return null;
  94. }
  95. function normalizeParams(originalTypeEvent, handler, delegationFn) {
  96. const delegation = typeof handler === 'string';
  97. const originalHandler = delegation ? delegationFn : handler;
  98. let typeEvent = getTypeEvent(originalTypeEvent);
  99. const isNative = nativeEvents.has(typeEvent);
  100. if (!isNative) {
  101. typeEvent = originalTypeEvent;
  102. }
  103. return [delegation, originalHandler, typeEvent];
  104. }
  105. function addHandler(element, originalTypeEvent, handler, delegationFn, oneOff) {
  106. if (typeof originalTypeEvent !== 'string' || !element) {
  107. return;
  108. }
  109. if (!handler) {
  110. handler = delegationFn;
  111. delegationFn = null;
  112. } // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position
  113. // this prevents the handler from being dispatched the same way as mouseover or mouseout does
  114. if (customEventsRegex.test(originalTypeEvent)) {
  115. const wrapFn = fn => {
  116. return function (event) {
  117. if (!event.relatedTarget || event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget)) {
  118. return fn.call(this, event);
  119. }
  120. };
  121. };
  122. if (delegationFn) {
  123. delegationFn = wrapFn(delegationFn);
  124. } else {
  125. handler = wrapFn(handler);
  126. }
  127. }
  128. const [delegation, originalHandler, typeEvent] = normalizeParams(originalTypeEvent, handler, delegationFn);
  129. const events = getEvent(element);
  130. const handlers = events[typeEvent] || (events[typeEvent] = {});
  131. const previousFn = findHandler(handlers, originalHandler, delegation ? handler : null);
  132. if (previousFn) {
  133. previousFn.oneOff = previousFn.oneOff && oneOff;
  134. return;
  135. }
  136. const uid = getUidEvent(originalHandler, originalTypeEvent.replace(namespaceRegex, ''));
  137. const fn = delegation ? bootstrapDelegationHandler(element, handler, delegationFn) : bootstrapHandler(element, handler);
  138. fn.delegationSelector = delegation ? handler : null;
  139. fn.originalHandler = originalHandler;
  140. fn.oneOff = oneOff;
  141. fn.uidEvent = uid;
  142. handlers[uid] = fn;
  143. element.addEventListener(typeEvent, fn, delegation);
  144. }
  145. function removeHandler(element, events, typeEvent, handler, delegationSelector) {
  146. const fn = findHandler(events[typeEvent], handler, delegationSelector);
  147. if (!fn) {
  148. return;
  149. }
  150. element.removeEventListener(typeEvent, fn, Boolean(delegationSelector));
  151. delete events[typeEvent][fn.uidEvent];
  152. }
  153. function removeNamespacedHandlers(element, events, typeEvent, namespace) {
  154. const storeElementEvent = events[typeEvent] || {};
  155. Object.keys(storeElementEvent).forEach(handlerKey => {
  156. if (handlerKey.includes(namespace)) {
  157. const event = storeElementEvent[handlerKey];
  158. removeHandler(element, events, typeEvent, event.originalHandler, event.delegationSelector);
  159. }
  160. });
  161. }
  162. function getTypeEvent(event) {
  163. // allow to get the native events from namespaced events ('click.bs.button' --> 'click')
  164. event = event.replace(stripNameRegex, '');
  165. return customEvents[event] || event;
  166. }
  167. const EventHandler = {
  168. on(element, event, handler, delegationFn) {
  169. addHandler(element, event, handler, delegationFn, false);
  170. },
  171. one(element, event, handler, delegationFn) {
  172. addHandler(element, event, handler, delegationFn, true);
  173. },
  174. off(element, originalTypeEvent, handler, delegationFn) {
  175. if (typeof originalTypeEvent !== 'string' || !element) {
  176. return;
  177. }
  178. const [delegation, originalHandler, typeEvent] = normalizeParams(originalTypeEvent, handler, delegationFn);
  179. const inNamespace = typeEvent !== originalTypeEvent;
  180. const events = getEvent(element);
  181. const isNamespace = originalTypeEvent.startsWith('.');
  182. if (typeof originalHandler !== 'undefined') {
  183. // Simplest case: handler is passed, remove that listener ONLY.
  184. if (!events || !events[typeEvent]) {
  185. return;
  186. }
  187. removeHandler(element, events, typeEvent, originalHandler, delegation ? handler : null);
  188. return;
  189. }
  190. if (isNamespace) {
  191. Object.keys(events).forEach(elementEvent => {
  192. removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1));
  193. });
  194. }
  195. const storeElementEvent = events[typeEvent] || {};
  196. Object.keys(storeElementEvent).forEach(keyHandlers => {
  197. const handlerKey = keyHandlers.replace(stripUidRegex, '');
  198. if (!inNamespace || originalTypeEvent.includes(handlerKey)) {
  199. const event = storeElementEvent[keyHandlers];
  200. removeHandler(element, events, typeEvent, event.originalHandler, event.delegationSelector);
  201. }
  202. });
  203. },
  204. trigger(element, event, args) {
  205. if (typeof event !== 'string' || !element) {
  206. return null;
  207. }
  208. const $ = getjQuery();
  209. const typeEvent = getTypeEvent(event);
  210. const inNamespace = event !== typeEvent;
  211. const isNative = nativeEvents.has(typeEvent);
  212. let jQueryEvent;
  213. let bubbles = true;
  214. let nativeDispatch = true;
  215. let defaultPrevented = false;
  216. let evt = null;
  217. if (inNamespace && $) {
  218. jQueryEvent = $.Event(event, args);
  219. $(element).trigger(jQueryEvent);
  220. bubbles = !jQueryEvent.isPropagationStopped();
  221. nativeDispatch = !jQueryEvent.isImmediatePropagationStopped();
  222. defaultPrevented = jQueryEvent.isDefaultPrevented();
  223. }
  224. if (isNative) {
  225. evt = document.createEvent('HTMLEvents');
  226. evt.initEvent(typeEvent, bubbles, true);
  227. } else {
  228. evt = new CustomEvent(event, {
  229. bubbles,
  230. cancelable: true
  231. });
  232. } // merge custom information in our event
  233. if (typeof args !== 'undefined') {
  234. Object.keys(args).forEach(key => {
  235. Object.defineProperty(evt, key, {
  236. get() {
  237. return args[key];
  238. }
  239. });
  240. });
  241. }
  242. if (defaultPrevented) {
  243. evt.preventDefault();
  244. }
  245. if (nativeDispatch) {
  246. element.dispatchEvent(evt);
  247. }
  248. if (evt.defaultPrevented && typeof jQueryEvent !== 'undefined') {
  249. jQueryEvent.preventDefault();
  250. }
  251. return evt;
  252. }
  253. };
  254. return EventHandler;
  255. })));
  256. //# sourceMappingURL=event-handler.js.map