{"version":3,"file":"event-listener.cjs","sources":["../src/index.ts"],"sourcesContent":["/**\n * Advanced event listener based on subscribe / publish pattern.\n *\n * @see https://www.patterns.dev/posts/classic-design-patterns/#observerpatternjavascript\n * @see https://gist.github.com/shystruk/d16c0ee7ac7d194da9644e5d740c8338#file-subpub-js\n * @see https://hackernoon.com/do-you-still-register-window-event-listeners-in-each-component-react-in-example-31a4b1f6f1c8\n */\nimport { version } from \"../package.json\";\n\nimport {\n  AnimationEvent,\n  AnimationEventHandler,\n  ChangeEvent,\n  ChangeEventHandler,\n  ClipboardEvent,\n  ClipboardEventHandler,\n  CompositionEvent,\n  CompositionEventHandler,\n  DragEvent,\n  DragEventHandler,\n  EventRegistryEntry,\n  EventsRegistry,\n  FocusEvent,\n  FocusEventHandler,\n  FormEvent,\n  FormEventHandler,\n  KeyboardEvent,\n  KeyboardEventHandler,\n  MouseEvent,\n  MouseEventHandler,\n  NativeEvent,\n  NativeEventHandler,\n  PointerEvent,\n  PointerEventHandler,\n  PossibleEventTarget,\n  TouchEvent,\n  TouchEventHandler,\n  TransitionEvent,\n  TransitionEventHandler,\n  UIEvent,\n  UIEventHandler,\n  WheelEvent,\n  WheelEventHandler,\n} from \"./types\";\n\nconst registry: EventsRegistry = {};\n\n/**\n * The global event listener. This function must be a Function.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget\n */\nconst globalListener = (e: NativeEvent) => {\n  const { type, currentTarget } = e;\n\n  registry[type].forEach((listenersMap, element) => {\n    /* istanbul ignore else @preserve */\n    if (currentTarget === element) {\n      listenersMap.forEach((options, listener) => {\n        listener.apply(element, [e]);\n\n        if (typeof options === \"object\" && options.once) {\n          removeListener(element, type, listener, options);\n        }\n      });\n    }\n  });\n};\n\n/**\n * Register a new listener with its options and attach the `globalListener`\n * to the target if this is the first listener.\n */\nconst addListener = <T = Element, L = EventListener>(\n  element: T,\n  eventType: string,\n  listener: L,\n  options?: AddEventListenerOptions,\n): void => {\n  // get element listeners first\n  /* istanbul ignore else @preserve */\n  if (!registry[eventType]) {\n    registry[eventType] = new Map();\n  }\n  const oneEventMap = registry[eventType];\n\n  /* istanbul ignore else @preserve */\n  if (!oneEventMap.has(element as PossibleEventTarget)) {\n    oneEventMap.set(element as PossibleEventTarget, new Map());\n  }\n  const oneElementMap = oneEventMap.get(\n    element as PossibleEventTarget,\n  ) as EventRegistryEntry<T, L>;\n\n  // get listeners size\n  const { size } = oneElementMap;\n\n  // register listener with its options\n  oneElementMap.set(listener, options);\n\n  // add listener last\n  /* istanbul ignore else @preserve */\n  if (!size) {\n    (element as PossibleEventTarget).addEventListener(\n      eventType,\n      globalListener as unknown as EventListener,\n      options,\n    );\n  }\n};\n\n/**\n * Remove a listener from registry and detach the `globalListener`\n * if no listeners are found in the registry.\n */\nconst removeListener = <T = Element, L = EventListener>(\n  element: T,\n  eventType: string,\n  listener: L,\n  options?: AddEventListenerOptions,\n): void => {\n  // get listener first\n  const oneEventMap = registry[eventType];\n  const oneElementMap = oneEventMap &&\n    (oneEventMap.get(element as PossibleEventTarget) as EventRegistryEntry<T>);\n  const savedOptions = oneElementMap &&\n    oneElementMap.get(listener as NativeEventHandler<typeof element>);\n\n  // also recover initial options\n  const eventOptions = savedOptions !== undefined ? savedOptions : options;\n\n  // unsubscribe second, remove from registry\n  /* istanbul ignore else @preserve */\n  if (\n    oneElementMap &&\n    oneElementMap.has(listener as NativeEventHandler<typeof element>)\n  ) {\n    oneElementMap.delete(listener as NativeEventHandler<typeof element>);\n  }\n  /* istanbul ignore else @preserve */\n  if (oneEventMap && (!oneElementMap || !oneElementMap.size)) {\n    oneEventMap.delete(element as PossibleEventTarget);\n  }\n  /* istanbul ignore else @preserve */\n  if (!oneEventMap || !oneEventMap.size) delete registry[eventType];\n\n  // remove listener last\n  /* istanbul ignore else @preserve */\n  if (!oneElementMap || !oneElementMap.size) {\n    (element as PossibleEventTarget).removeEventListener(\n      eventType,\n      globalListener as unknown as EventListener,\n      eventOptions,\n    );\n  }\n};\n\n// alias main methods\nconst on: typeof addListener = addListener;\nconst off: typeof removeListener = removeListener;\n\nexport {\n  addListener,\n  globalListener,\n  off,\n  on,\n  registry,\n  removeListener,\n  version,\n};\nexport type {\n  AnimationEvent,\n  AnimationEventHandler,\n  ChangeEvent,\n  ChangeEventHandler,\n  ClipboardEvent,\n  ClipboardEventHandler,\n  CompositionEvent,\n  CompositionEventHandler,\n  DragEvent,\n  DragEventHandler,\n  EventRegistryEntry,\n  EventsRegistry,\n  FocusEvent,\n  FocusEventHandler,\n  FormEvent,\n  FormEventHandler,\n  KeyboardEvent,\n  KeyboardEventHandler,\n  MouseEvent,\n  MouseEventHandler,\n  NativeEvent,\n  NativeEventHandler,\n  PointerEvent,\n  PointerEventHandler,\n  PossibleEventTarget,\n  TouchEvent,\n  TouchEventHandler,\n  TransitionEvent,\n  TransitionEventHandler,\n  UIEvent,\n  UIEventHandler,\n  WheelEvent,\n  WheelEventHandler,\n};\n"],"names":["registry","globalListener","e","type","currentTarget","listenersMap","element","options","listener","removeListener","addListener","eventType","oneEventMap","oneElementMap","size","savedOptions","eventOptions","on","off"],"mappings":"iGAuCMA,EAA2B,CAAA,EAG3BC,EAAkBC,GAAmB,CACnC,KAAA,CAAE,KAAAC,EAAM,cAAAC,CAAA,EAAkBF,EAEhCF,EAASG,CAAI,EAAE,QAAQ,CAACE,EAAcC,IAAY,CAE5CF,IAAkBE,GACPD,EAAA,QAAQ,CAACE,EAASC,IAAa,CAC1CA,EAAS,MAAMF,EAAS,CAACJ,CAAC,CAAC,EAEvB,OAAOK,GAAY,UAAYA,EAAQ,MAC1BE,EAAAH,EAASH,EAAMK,EAAUD,CAAO,CACjD,CACD,CACH,CACD,CACH,EAGMG,EAAc,CAClBJ,EACAK,EACAH,EACAD,IACS,CAGJP,EAASW,CAAS,IACZX,EAAAW,CAAS,EAAI,IAAI,KAEtB,MAAAC,EAAcZ,EAASW,CAAS,EAGjCC,EAAY,IAAIN,CAA8B,GACjDM,EAAY,IAAIN,EAAoC,IAAA,GAAK,EAE3D,MAAMO,EAAgBD,EAAY,IAChCN,CACF,EAGM,CAAE,KAAAQ,GAASD,EAGHA,EAAA,IAAIL,EAAUD,CAAO,EAI9BO,GACFR,EAAgC,iBAC/BK,EACAV,EACAM,CACF,CAEJ,EAGME,EAAiB,CACrBH,EACAK,EACAH,EACAD,IACS,CAEH,MAAAK,EAAcZ,EAASW,CAAS,EAChCE,EAAgBD,GACnBA,EAAY,IAAIN,CAA8B,EAC3CS,EAAeF,GACnBA,EAAc,IAAIL,CAA8C,EAG5DQ,EAAeD,IAAiB,OAAYA,EAAeR,EAK/DM,GACAA,EAAc,IAAIL,CAA8C,GAEhEK,EAAc,OAAOL,CAA8C,EAGjEI,IAAgB,CAACC,GAAiB,CAACA,EAAc,OACnDD,EAAY,OAAON,CAA8B,GAG/C,CAACM,GAAe,CAACA,EAAY,OAAM,OAAOZ,EAASW,CAAS,GAI5D,CAACE,GAAiB,CAACA,EAAc,OAClCP,EAAgC,oBAC/BK,EACAV,EACAe,CACF,CAEJ,EAGMC,EAAyBP,EACzBQ,EAA6BT"}