瀏覽代碼

Replace the event system with the util package.

Naotoshi Fujita 2 年之前
父節點
當前提交
8c02b3aea9
共有 42 個文件被更改,包括 1277 次插入1078 次删除
  1. 0 0
      dist/js/splide-renderer.min.js
  2. 0 0
      dist/js/splide-renderer.min.js.map
  3. 251 221
      dist/js/splide.cjs.js
  4. 252 220
      dist/js/splide.esm.js
  5. 250 218
      dist/js/splide.js
  6. 0 0
      dist/js/splide.min.js
  7. 二進制
      dist/js/splide.min.js.gz
  8. 0 0
      dist/js/splide.min.js.map
  9. 106 64
      dist/types/index.d.ts
  10. 0 14
      jest.config.js
  11. 18 0
      jest.config.ts
  12. 183 37
      package-lock.json
  13. 4 0
      package.json
  14. 2 0
      scripts/build-module.js
  15. 2 0
      scripts/build-script.js
  16. 8 3
      src/js/components/Arrows/Arrows.ts
  17. 10 3
      src/js/components/Autoplay/Autoplay.ts
  18. 8 3
      src/js/components/Clones/Clones.ts
  19. 9 3
      src/js/components/Controller/Controller.ts
  20. 9 3
      src/js/components/Cover/Cover.ts
  21. 13 7
      src/js/components/Drag/Drag.ts
  22. 9 3
      src/js/components/Elements/Elements.ts
  23. 9 10
      src/js/components/Keyboard/Keyboard.ts
  24. 10 3
      src/js/components/Layout/Layout.ts
  25. 9 3
      src/js/components/LazyLoad/LazyLoad.ts
  26. 10 3
      src/js/components/Live/Live.ts
  27. 1 1
      src/js/components/Media/Media.ts
  28. 9 3
      src/js/components/Move/Move.ts
  29. 8 4
      src/js/components/Pagination/Pagination.ts
  30. 10 3
      src/js/components/Scroll/Scroll.ts
  31. 1 2
      src/js/components/Slides/Slide.ts
  32. 9 3
      src/js/components/Slides/Slides.ts
  33. 19 9
      src/js/components/Sync/Sync.ts
  34. 9 5
      src/js/components/Wheel/Wheel.ts
  35. 0 79
      src/js/constructors/EventInterface/EventInterface.ts
  36. 0 127
      src/js/constructors/EventInterface/test/general.test.ts
  37. 2 2
      src/js/constructors/index.ts
  38. 15 13
      src/js/core/Splide/Splide.ts
  39. 1 2
      src/js/renderer/SplideRenderer/SplideRenderer.ts
  40. 9 3
      src/js/transitions/Fade/Fade.ts
  41. 9 3
      src/js/transitions/Slide/Slide.ts
  42. 3 1
      src/js/types/general.ts

文件差異過大導致無法顯示
+ 0 - 0
dist/js/splide-renderer.min.js


文件差異過大導致無法顯示
+ 0 - 0
dist/js/splide-renderer.min.js.map


+ 251 - 221
dist/js/splide.cjs.js

@@ -27,6 +27,120 @@ const STATES = {
   DESTROYED
 };
 
+function x(n) {
+  n.length = 0;
+}
+const D = Array.prototype;
+function m(n, t, e) {
+  return D.slice.call(n, t, e);
+}
+function a(n) {
+  return n.bind(null, ...m(arguments, 1));
+}
+function p(n, t) {
+  return typeof t === n;
+}
+function g(n) {
+  return !q(n) && p("object", n);
+}
+const $ = Array.isArray; a(p, "function"); const h = a(p, "string"); a(p, "boolean"); a(p, "undefined");
+function q(n) {
+  return n === null;
+}
+function L(n) {
+  return $(n) ? n : [n];
+}
+function d(n, t) {
+  L(n).forEach(t);
+}
+const b = Object.assign, A = Object.keys;
+function O(n, t, e) {
+  return n && (e ? A(n).reverse() : A(n)).forEach((i) => {
+    i !== "__proto__" && t(n[i], i);
+  }), n;
+}
+function rn(n) {
+  let t = "";
+  return O(n, (e, i) => {
+    t += e || e === 0 ? ` ${i}${e !== true ? `="${e}"` : ""}` : "";
+  }), t.trim();
+}
+function _() {
+  return m(arguments).reduce((n, t) => `${n} ${$(t) ? _(...t) : t || ""}`.trim(), "");
+}
+function on(n, t) {
+  return t = $(t) || !g(t) ? { class: _(t) } : t, `<${`${n} ${t ? rn(t) : ""}`.trim()}>`;
+}
+a(on, "div");
+function C(n, t) {
+  d(n, (e) => {
+    h(e) && e.split(" ").forEach(t);
+  });
+}
+function S(n, t) {
+  const e = [];
+  for (let i = n.length - 1; i >= 0; i--)
+    t(n[i]) && e.push(...n.splice(i, 1));
+  return e;
+}
+function H(n) {
+  const t = n || [], e = n ? {} : void 0;
+  function i(o, f, u, c) {
+    C(f, (s) => {
+      o.addEventListener(s, u, c), t.push([o.removeEventListener.bind(o, s, u, c), e]);
+    });
+  }
+  function r() {
+    e ? S(t, (o) => o[1] === e).forEach((o) => {
+      o[0]();
+    }) : (t.forEach((o) => {
+      o[0]();
+    }), x(t));
+  }
+  return {
+    bind: i,
+    create: a(H, t),
+    destroy: r
+  };
+}
+function v(n) {
+  const t = n || [], e = n ? {} : void 0;
+  function i(u, c) {
+    C(u, (s) => {
+      t.push([s, c, e]);
+    });
+  }
+  function r(u, c) {
+    C(u, (s) => {
+      S(t, (l) => l[0] === s && (!c || l[1] === c) && l[2] === e);
+    });
+  }
+  function o(u, ...c) {
+    t.slice().forEach((s) => {
+      s[0] === u && s[1](...c);
+    });
+  }
+  function f() {
+    e ? S(t, (u) => u[2] === e) : x(t);
+  }
+  return {
+    on: i,
+    off: r,
+    emit: o,
+    create: a(v, t),
+    destroy: f
+  };
+}
+function fn(n = H(), t = v()) {
+  function e() {
+    return fn(n.create(), t.create());
+  }
+  function i() {
+    n.destroy(), t.destroy();
+  }
+  return b({}, n, t, { create: e, destroy: i });
+}
+
 function empty(array) {
   array.length = 0;
 }
@@ -316,63 +430,6 @@ function uniqueId(prefix) {
   return `${prefix}${pad(ids[prefix] = (ids[prefix] || 0) + 1)}`;
 }
 
-function EventBinder() {
-  let listeners = [];
-  function bind(targets, events, callback, options) {
-    forEachEvent(targets, events, (target, event, namespace) => {
-      const isEventTarget = "addEventListener" in target;
-      const remover = isEventTarget ? target.removeEventListener.bind(target, event, callback, options) : target["removeListener"].bind(target, callback);
-      isEventTarget ? target.addEventListener(event, callback, options) : target["addListener"](callback);
-      listeners.push([target, event, namespace, callback, remover]);
-    });
-  }
-  function unbind(targets, events, callback) {
-    forEachEvent(targets, events, (target, event, namespace) => {
-      listeners = listeners.filter((listener) => {
-        if (listener[0] === target && listener[1] === event && listener[2] === namespace && (!callback || listener[3] === callback)) {
-          listener[4]();
-          return false;
-        }
-        return true;
-      });
-    });
-  }
-  function dispatch(target, type, detail) {
-    let e;
-    const bubbles = true;
-    if (typeof CustomEvent === "function") {
-      e = new CustomEvent(type, { bubbles, detail });
-    } else {
-      e = document.createEvent("CustomEvent");
-      e.initCustomEvent(type, bubbles, false, detail);
-    }
-    target.dispatchEvent(e);
-    return e;
-  }
-  function forEachEvent(targets, events, iteratee) {
-    forEach(targets, (target) => {
-      target && forEach(events, (events2) => {
-        events2.split(" ").forEach((eventNS) => {
-          const fragment = eventNS.split(".");
-          iteratee(target, fragment[0], fragment[1]);
-        });
-      });
-    });
-  }
-  function destroy() {
-    listeners.forEach((data) => {
-      data[4]();
-    });
-    empty(listeners);
-  }
-  return {
-    bind,
-    unbind,
-    dispatch,
-    destroy
-  };
-}
-
 const EVENT_MOUNTED = "mounted";
 const EVENT_READY = "ready";
 const EVENT_MOVE = "move";
@@ -406,110 +463,11 @@ const EVENT_SLIDE_KEYDOWN = "sk";
 const EVENT_SHIFTED = "sh";
 const EVENT_END_INDEX_CHANGED = "ei";
 
-function EventInterface(Splide2) {
-  const bus = Splide2 ? Splide2.event.bus : document.createDocumentFragment();
-  const binder = EventBinder();
-  function on(events, callback) {
-    binder.bind(bus, toArray(events).join(" "), (e) => {
-      callback.apply(callback, isArray(e.detail) ? e.detail : []);
-    });
-  }
-  function emit(event) {
-    binder.dispatch(bus, event, slice(arguments, 1));
-  }
-  if (Splide2) {
-    Splide2.event.on(EVENT_DESTROY, binder.destroy);
-  }
-  return assign(binder, {
-    bus,
-    on,
-    off: apply(binder.unbind, bus),
-    emit
-  });
-}
-
-function RequestInterval(interval, onInterval, onUpdate, limit) {
-  const { now } = Date;
-  let startTime;
-  let rate = 0;
-  let id;
-  let paused = true;
-  let count = 0;
-  function update() {
-    if (!paused) {
-      rate = interval ? min((now() - startTime) / interval, 1) : 1;
-      onUpdate && onUpdate(rate);
-      if (rate >= 1) {
-        onInterval();
-        startTime = now();
-        if (limit && ++count >= limit) {
-          return pause();
-        }
-      }
-      id = raf(update);
-    }
-  }
-  function start(resume) {
-    resume || cancel();
-    startTime = now() - (resume ? rate * interval : 0);
-    paused = false;
-    id = raf(update);
-  }
-  function pause() {
-    paused = true;
-  }
-  function rewind() {
-    startTime = now();
-    rate = 0;
-    if (onUpdate) {
-      onUpdate(rate);
-    }
-  }
-  function cancel() {
-    id && cancelAnimationFrame(id);
-    rate = 0;
-    id = 0;
-    paused = true;
-  }
-  function set(time) {
-    interval = time;
-  }
-  function isPaused() {
-    return paused;
-  }
-  return {
-    start,
-    rewind,
-    pause,
-    cancel,
-    set,
-    isPaused
-  };
-}
-
-function State(initialState) {
-  let state = initialState;
-  function set(value) {
-    state = value;
-  }
-  function is(states) {
-    return includes(toArray(states), state);
-  }
-  return { set, is };
-}
-
-function Throttle(func, duration) {
-  const interval = RequestInterval(duration || 0, func, null, 1);
-  return () => {
-    interval.isPaused() && interval.start();
-  };
-}
-
 function Media(Splide2, Components2, options) {
   const { state } = Splide2;
   const breakpoints = options.breakpoints || {};
   const reducedMotion = options.reducedMotion || {};
-  const binder = EventBinder();
+  const binder = H();
   const queries = [];
   function setup() {
     const isMin = options.mediaQuery === "min";
@@ -702,8 +660,8 @@ const POINTER_DOWN_EVENTS = "touchstart mousedown";
 const POINTER_MOVE_EVENTS = "touchmove mousemove";
 const POINTER_UP_EVENTS = "touchend touchcancel mouseup click";
 
-function Elements(Splide2, Components2, options) {
-  const { on, bind } = EventInterface(Splide2);
+function Elements(Splide2, Components2, options, event) {
+  const { on, bind } = event;
   const { root } = Splide2;
   const { i18n } = options;
   const elements = {};
@@ -801,7 +759,7 @@ const LOOP = "loop";
 const FADE = "fade";
 
 function Slide$1(Splide2, index, slideIndex, slide) {
-  const event = EventInterface(Splide2);
+  const event = Splide2.event.create();
   const { on, emit, bind } = event;
   const { Components, root, options } = Splide2;
   const { isNavigation, updateOnMove, i18n, pagination, slideFocus } = options;
@@ -927,8 +885,8 @@ function Slide$1(Splide2, index, slideIndex, slide) {
   return self;
 }
 
-function Slides(Splide2, Components2, options) {
-  const { on, emit, bind } = EventInterface(Splide2);
+function Slides(Splide2, Components2, options, event) {
+  const { on, emit, bind } = event;
   const { slides, list } = Components2.Elements;
   const Slides2 = [];
   function mount() {
@@ -1040,8 +998,85 @@ function Slides(Splide2, Components2, options) {
   };
 }
 
-function Layout(Splide2, Components2, options) {
-  const { on, bind, emit } = EventInterface(Splide2);
+function RequestInterval(interval, onInterval, onUpdate, limit) {
+  const { now } = Date;
+  let startTime;
+  let rate = 0;
+  let id;
+  let paused = true;
+  let count = 0;
+  function update() {
+    if (!paused) {
+      rate = interval ? min((now() - startTime) / interval, 1) : 1;
+      onUpdate && onUpdate(rate);
+      if (rate >= 1) {
+        onInterval();
+        startTime = now();
+        if (limit && ++count >= limit) {
+          return pause();
+        }
+      }
+      id = raf(update);
+    }
+  }
+  function start(resume) {
+    resume || cancel();
+    startTime = now() - (resume ? rate * interval : 0);
+    paused = false;
+    id = raf(update);
+  }
+  function pause() {
+    paused = true;
+  }
+  function rewind() {
+    startTime = now();
+    rate = 0;
+    if (onUpdate) {
+      onUpdate(rate);
+    }
+  }
+  function cancel() {
+    id && cancelAnimationFrame(id);
+    rate = 0;
+    id = 0;
+    paused = true;
+  }
+  function set(time) {
+    interval = time;
+  }
+  function isPaused() {
+    return paused;
+  }
+  return {
+    start,
+    rewind,
+    pause,
+    cancel,
+    set,
+    isPaused
+  };
+}
+
+function State(initialState) {
+  let state = initialState;
+  function set(value) {
+    state = value;
+  }
+  function is(states) {
+    return includes(toArray(states), state);
+  }
+  return { set, is };
+}
+
+function Throttle(func, duration) {
+  const interval = RequestInterval(duration || 0, func, null, 1);
+  return () => {
+    interval.isPaused() && interval.start();
+  };
+}
+
+function Layout(Splide2, Components2, options, event) {
+  const { on, bind, emit } = event;
   const { Slides } = Components2;
   const { resolve } = Components2.Direction;
   const { root, track, list } = Components2.Elements;
@@ -1146,8 +1181,7 @@ function Layout(Splide2, Components2, options) {
 }
 
 const MULTIPLIER = 2;
-function Clones(Splide2, Components2, options) {
-  const event = EventInterface(Splide2);
+function Clones(Splide2, Components2, options, event) {
   const { on } = event;
   const { Elements, Slides } = Components2;
   const { resolve } = Components2.Direction;
@@ -1217,8 +1251,8 @@ function Clones(Splide2, Components2, options) {
   };
 }
 
-function Move(Splide2, Components2, options) {
-  const { on, emit } = EventInterface(Splide2);
+function Move(Splide2, Components2, options, event) {
+  const { on, emit } = event;
   const { set } = Splide2.state;
   const { slideSize, getPadding, totalSize, listSize, sliderSize } = Components2.Layout;
   const { resolve, orient } = Components2.Direction;
@@ -1342,8 +1376,8 @@ function Move(Splide2, Components2, options) {
   };
 }
 
-function Controller(Splide2, Components2, options) {
-  const { on, emit } = EventInterface(Splide2);
+function Controller(Splide2, Components2, options, event) {
+  const { on, emit } = event;
   const { Move } = Components2;
   const { getPosition, getLimit, toPosition } = Move;
   const { isEnough, getLength } = Components2.Slides;
@@ -1521,8 +1555,7 @@ const XML_NAME_SPACE = "http://www.w3.org/2000/svg";
 const PATH = "m15.5 0.932-4.3 4.38 14.5 14.6-14.5 14.5 4.3 4.4 14.6-14.6 4.4-4.3-4.4-4.4-14.6-14.6z";
 const SIZE = 40;
 
-function Arrows(Splide2, Components2, options) {
-  const event = EventInterface(Splide2);
+function Arrows(Splide2, Components2, options, event) {
   const { on, bind, emit } = event;
   const { classes, i18n } = options;
   const { Elements, Controller } = Components2;
@@ -1612,8 +1645,8 @@ function Arrows(Splide2, Components2, options) {
 
 const INTERVAL_DATA_ATTRIBUTE = `${DATA_ATTRIBUTE}-interval`;
 
-function Autoplay(Splide2, Components2, options) {
-  const { on, bind, emit } = EventInterface(Splide2);
+function Autoplay(Splide2, Components2, options, event) {
+  const { on, bind, emit } = event;
   const interval = RequestInterval(options.interval, Splide2.go.bind(Splide2, ">"), onAnimationFrame);
   const { isPaused } = interval;
   const { Elements, Elements: { root, toggle } } = Components2;
@@ -1695,8 +1728,8 @@ function Autoplay(Splide2, Components2, options) {
   };
 }
 
-function Cover(Splide2, Components2, options) {
-  const { on } = EventInterface(Splide2);
+function Cover(Splide2, Components2, options, event) {
+  const { on } = event;
   function mount() {
     if (options.cover) {
       on(EVENT_LAZYLOAD_LOADED, apply(toggle, true));
@@ -1727,8 +1760,8 @@ const FRICTION_FACTOR = 0.6;
 const BASE_VELOCITY = 1.5;
 const MIN_DURATION = 800;
 
-function Scroll(Splide2, Components2, options) {
-  const { on, emit } = EventInterface(Splide2);
+function Scroll(Splide2, Components2, options, event) {
+  const { on, emit } = event;
   const { state: { set } } = Splide2;
   const { Move } = Components2;
   const { getPosition, getLimit, exceededLimit, translate } = Move;
@@ -1799,8 +1832,9 @@ function Scroll(Splide2, Components2, options) {
 
 const SCROLL_LISTENER_OPTIONS = { passive: false, capture: true };
 
-function Drag(Splide2, Components2, options) {
-  const { on, emit, bind, unbind } = EventInterface(Splide2);
+function Drag(Splide2, Components2, options, event) {
+  const { on, emit, bind } = event;
+  const binder = event.create();
   const { state } = Splide2;
   const { Move, Scroll, Controller, Elements: { track }, Media: { reduce } } = Components2;
   const { resolve, orient } = Components2.Direction;
@@ -1836,8 +1870,8 @@ function Drag(Splide2, Components2, options) {
           target = isTouch ? track : window;
           dragging = state.is([MOVING, SCROLLING]);
           prevBaseEvent = null;
-          bind(target, POINTER_MOVE_EVENTS, onPointerMove, SCROLL_LISTENER_OPTIONS);
-          bind(target, POINTER_UP_EVENTS, onPointerUp, SCROLL_LISTENER_OPTIONS);
+          binder.bind(target, POINTER_MOVE_EVENTS, onPointerMove, SCROLL_LISTENER_OPTIONS);
+          binder.bind(target, POINTER_UP_EVENTS, onPointerUp, SCROLL_LISTENER_OPTIONS);
           Move.cancel();
           Scroll.cancel();
           save(e);
@@ -1878,8 +1912,7 @@ function Drag(Splide2, Components2, options) {
       move(e);
       prevent(e);
     }
-    unbind(target, POINTER_MOVE_EVENTS, onPointerMove);
-    unbind(target, POINTER_UP_EVENTS, onPointerUp);
+    binder.destroy();
     dragging = false;
   }
   function onClick(e) {
@@ -1981,8 +2014,8 @@ function normalizeKey(key) {
 }
 
 const KEYBOARD_EVENT = "keydown";
-function Keyboard(Splide2, Components2, options) {
-  const { on, bind, unbind } = EventInterface(Splide2);
+function Keyboard(Splide2, Components2, options, event) {
+  const { on, bind, destroy } = event;
   const { root } = Splide2;
   const { resolve } = Components2.Direction;
   let target;
@@ -2000,9 +2033,6 @@ function Keyboard(Splide2, Components2, options) {
       bind(target, KEYBOARD_EVENT, onKeydown);
     }
   }
-  function destroy() {
-    unbind(target, KEYBOARD_EVENT);
-  }
   function disable(value) {
     disabled = value;
   }
@@ -2034,8 +2064,8 @@ const SRC_DATA_ATTRIBUTE = `${DATA_ATTRIBUTE}-lazy`;
 const SRCSET_DATA_ATTRIBUTE = `${SRC_DATA_ATTRIBUTE}-srcset`;
 const IMAGE_SELECTOR = `[${SRC_DATA_ATTRIBUTE}], [${SRCSET_DATA_ATTRIBUTE}]`;
 
-function LazyLoad(Splide2, Components2, options) {
-  const { on, off, bind, emit } = EventInterface(Splide2);
+function LazyLoad(Splide2, Components2, options, event) {
+  const { on, off, bind, emit } = event;
   const isSequential = options.lazyLoad === "sequential";
   const events = [EVENT_MOVED, EVENT_SCROLLED];
   let entries = [];
@@ -2108,8 +2138,7 @@ function LazyLoad(Splide2, Components2, options) {
   };
 }
 
-function Pagination(Splide2, Components2, options) {
-  const event = EventInterface(Splide2);
+function Pagination(Splide2, Components2, options, event) {
   const { on, emit, bind } = event;
   const { Slides, Elements, Controller } = Components2;
   const { hasFocus, getIndex, go } = Controller;
@@ -2222,7 +2251,7 @@ function Pagination(Splide2, Components2, options) {
 }
 
 const TRIGGER_KEYS = [" ", "Enter"];
-function Sync(Splide2, Components2, options) {
+function Sync(Splide2, Components2, options, event) {
   const { isNavigation, slideFocus } = options;
   const events = [];
   function mount() {
@@ -2237,8 +2266,8 @@ function Sync(Splide2, Components2, options) {
     }
   }
   function destroy() {
-    events.forEach((event) => {
-      event.destroy();
+    events.forEach((event2) => {
+      event2.destroy();
     });
     empty(events);
   }
@@ -2247,23 +2276,27 @@ function Sync(Splide2, Components2, options) {
     mount();
   }
   function sync(splide, target) {
-    const event = EventInterface(splide);
-    event.on(EVENT_MOVE, (index, prev, dest) => {
+    const event2 = splide.event.create();
+    event2.on(EVENT_MOVE, (index, prev, dest) => {
       target.go(target.is(LOOP) ? dest : index);
     });
-    events.push(event);
+    events.push(event2);
   }
   function navigate() {
-    const event = EventInterface(Splide2);
-    const { on } = event;
+    const ev = event.create();
+    const { on } = ev;
     on(EVENT_CLICK, onClick);
     on(EVENT_SLIDE_KEYDOWN, onKeydown);
     on([EVENT_MOUNTED, EVENT_UPDATED], update);
-    events.push(event);
-    event.emit(EVENT_NAVIGATION_MOUNTED, Splide2.splides);
+    events.push(ev);
+    ev.emit(EVENT_NAVIGATION_MOUNTED, Splide2.splides);
   }
   function update() {
-    setAttribute(Components2.Elements.list, ARIA_ORIENTATION, options.direction === TTB ? "vertical" : "");
+    setAttribute(
+      Components2.Elements.list,
+      ARIA_ORIENTATION,
+      options.direction === TTB ? "vertical" : ""
+    );
   }
   function onClick(Slide) {
     Splide2.go(Slide.index);
@@ -2286,12 +2319,11 @@ function Sync(Splide2, Components2, options) {
   };
 }
 
-function Wheel(Splide2, Components2, options) {
-  const { bind } = EventInterface(Splide2);
+function Wheel(Splide2, Components2, options, event) {
   let lastTime = 0;
   function mount() {
     if (options.wheel) {
-      bind(Components2.Elements.track, "wheel", onWheel, SCROLL_LISTENER_OPTIONS);
+      event.bind(Components2.Elements.track, "wheel", onWheel, SCROLL_LISTENER_OPTIONS);
     }
   }
   function onWheel(e) {
@@ -2317,8 +2349,8 @@ function Wheel(Splide2, Components2, options) {
 }
 
 const SR_REMOVAL_DELAY = 90;
-function Live(Splide2, Components2, options) {
-  const { on } = EventInterface(Splide2);
+function Live(Splide2, Components2, options, event) {
+  const { on } = event;
   const { track } = Components2.Elements;
   const enabled = options.live && !options.isNavigation;
   const sr = create("span", CLASS_SR);
@@ -2425,10 +2457,10 @@ const DEFAULTS = {
   }
 };
 
-function Fade(Splide2, Components2, options) {
+function Fade(Splide2, Components2, options, event) {
   const { Slides } = Components2;
   function mount() {
-    EventInterface(Splide2).on([EVENT_MOUNTED, EVENT_REFRESH], init);
+    event.on([EVENT_MOUNTED, EVENT_REFRESH], init);
   }
   function init() {
     Slides.forEach((Slide) => {
@@ -2446,13 +2478,13 @@ function Fade(Splide2, Components2, options) {
   };
 }
 
-function Slide(Splide2, Components2, options) {
+function Slide(Splide2, Components2, options, event) {
   const { Move, Controller, Scroll } = Components2;
   const { list } = Components2.Elements;
   const transition = apply(style, list, "transition");
   let endCallback;
   function mount() {
-    EventInterface(Splide2).bind(list, "transitionend", (e) => {
+    event.bind(list, "transitionend", (e) => {
       if (e.target === list && endCallback) {
         cancel();
         endCallback();
@@ -2502,7 +2534,7 @@ class Splide {
   static defaults = {};
   static STATES = STATES;
   root;
-  event = EventInterface();
+  event = fn();
   Components = {};
   state = State(CREATED);
   splides = [];
@@ -2534,7 +2566,7 @@ class Splide {
     this._E = Extensions || this._E;
     const Constructors = assign({}, ComponentConstructors, this._E, { Transition: this._T });
     forOwn(Constructors, (Component, key) => {
-      const component = Component(this, Components2, this._o);
+      const component = Component(this, Components2, this._o, this.event.create());
       Components2[key] = component;
       component.setup && component.setup();
     });
@@ -2564,8 +2596,8 @@ class Splide {
     this.event.on(events, callback);
     return this;
   }
-  off(events) {
-    this.event.off(events);
+  off(events, callback) {
+    this.event.off(events, callback);
     return this;
   }
   emit(event) {
@@ -2590,7 +2622,7 @@ class Splide {
   destroy(completely = true) {
     const { event, state } = this;
     if (state.is(CREATED)) {
-      EventInterface(this).on(EVENT_READY, this.destroy.bind(this, completely));
+      this.on(EVENT_READY, this.destroy.bind(this, completely));
     } else {
       forOwn(this._C, (component) => {
         component.destroy && component.destroy(completely);
@@ -2669,7 +2701,7 @@ class Style {
 
 class SplideRenderer {
   static clean(splide) {
-    const { on } = EventInterface(splide);
+    const { on } = splide.event;
     const { root } = splide;
     const clones = queryAll(root, `.${CLASS_CLONE}`);
     on(EVENT_MOUNTED, () => {
@@ -3051,8 +3083,6 @@ exports.EVENT_SHIFTED = EVENT_SHIFTED;
 exports.EVENT_SLIDE_KEYDOWN = EVENT_SLIDE_KEYDOWN;
 exports.EVENT_UPDATED = EVENT_UPDATED;
 exports.EVENT_VISIBLE = EVENT_VISIBLE;
-exports.EventBinder = EventBinder;
-exports.EventInterface = EventInterface;
 exports.FADE = FADE;
 exports.LOOP = LOOP;
 exports.LTR = LTR;

+ 252 - 220
dist/js/splide.esm.js

@@ -23,6 +23,120 @@ const STATES = {
   DESTROYED
 };
 
+function x(n) {
+  n.length = 0;
+}
+const D = Array.prototype;
+function m(n, t, e) {
+  return D.slice.call(n, t, e);
+}
+function a(n) {
+  return n.bind(null, ...m(arguments, 1));
+}
+function p(n, t) {
+  return typeof t === n;
+}
+function g(n) {
+  return !q(n) && p("object", n);
+}
+const $ = Array.isArray; a(p, "function"); const h = a(p, "string"); a(p, "boolean"); a(p, "undefined");
+function q(n) {
+  return n === null;
+}
+function L(n) {
+  return $(n) ? n : [n];
+}
+function d(n, t) {
+  L(n).forEach(t);
+}
+const b = Object.assign, A = Object.keys;
+function O(n, t, e) {
+  return n && (e ? A(n).reverse() : A(n)).forEach((i) => {
+    i !== "__proto__" && t(n[i], i);
+  }), n;
+}
+function rn(n) {
+  let t = "";
+  return O(n, (e, i) => {
+    t += e || e === 0 ? ` ${i}${e !== true ? `="${e}"` : ""}` : "";
+  }), t.trim();
+}
+function _() {
+  return m(arguments).reduce((n, t) => `${n} ${$(t) ? _(...t) : t || ""}`.trim(), "");
+}
+function on(n, t) {
+  return t = $(t) || !g(t) ? { class: _(t) } : t, `<${`${n} ${t ? rn(t) : ""}`.trim()}>`;
+}
+a(on, "div");
+function C(n, t) {
+  d(n, (e) => {
+    h(e) && e.split(" ").forEach(t);
+  });
+}
+function S(n, t) {
+  const e = [];
+  for (let i = n.length - 1; i >= 0; i--)
+    t(n[i]) && e.push(...n.splice(i, 1));
+  return e;
+}
+function H(n) {
+  const t = n || [], e = n ? {} : void 0;
+  function i(o, f, u, c) {
+    C(f, (s) => {
+      o.addEventListener(s, u, c), t.push([o.removeEventListener.bind(o, s, u, c), e]);
+    });
+  }
+  function r() {
+    e ? S(t, (o) => o[1] === e).forEach((o) => {
+      o[0]();
+    }) : (t.forEach((o) => {
+      o[0]();
+    }), x(t));
+  }
+  return {
+    bind: i,
+    create: a(H, t),
+    destroy: r
+  };
+}
+function v(n) {
+  const t = n || [], e = n ? {} : void 0;
+  function i(u, c) {
+    C(u, (s) => {
+      t.push([s, c, e]);
+    });
+  }
+  function r(u, c) {
+    C(u, (s) => {
+      S(t, (l) => l[0] === s && (!c || l[1] === c) && l[2] === e);
+    });
+  }
+  function o(u, ...c) {
+    t.slice().forEach((s) => {
+      s[0] === u && s[1](...c);
+    });
+  }
+  function f() {
+    e ? S(t, (u) => u[2] === e) : x(t);
+  }
+  return {
+    on: i,
+    off: r,
+    emit: o,
+    create: a(v, t),
+    destroy: f
+  };
+}
+function fn(n = H(), t = v()) {
+  function e() {
+    return fn(n.create(), t.create());
+  }
+  function i() {
+    n.destroy(), t.destroy();
+  }
+  return b({}, n, t, { create: e, destroy: i });
+}
+
 function empty(array) {
   array.length = 0;
 }
@@ -312,63 +426,6 @@ function uniqueId(prefix) {
   return `${prefix}${pad(ids[prefix] = (ids[prefix] || 0) + 1)}`;
 }
 
-function EventBinder() {
-  let listeners = [];
-  function bind(targets, events, callback, options) {
-    forEachEvent(targets, events, (target, event, namespace) => {
-      const isEventTarget = "addEventListener" in target;
-      const remover = isEventTarget ? target.removeEventListener.bind(target, event, callback, options) : target["removeListener"].bind(target, callback);
-      isEventTarget ? target.addEventListener(event, callback, options) : target["addListener"](callback);
-      listeners.push([target, event, namespace, callback, remover]);
-    });
-  }
-  function unbind(targets, events, callback) {
-    forEachEvent(targets, events, (target, event, namespace) => {
-      listeners = listeners.filter((listener) => {
-        if (listener[0] === target && listener[1] === event && listener[2] === namespace && (!callback || listener[3] === callback)) {
-          listener[4]();
-          return false;
-        }
-        return true;
-      });
-    });
-  }
-  function dispatch(target, type, detail) {
-    let e;
-    const bubbles = true;
-    if (typeof CustomEvent === "function") {
-      e = new CustomEvent(type, { bubbles, detail });
-    } else {
-      e = document.createEvent("CustomEvent");
-      e.initCustomEvent(type, bubbles, false, detail);
-    }
-    target.dispatchEvent(e);
-    return e;
-  }
-  function forEachEvent(targets, events, iteratee) {
-    forEach(targets, (target) => {
-      target && forEach(events, (events2) => {
-        events2.split(" ").forEach((eventNS) => {
-          const fragment = eventNS.split(".");
-          iteratee(target, fragment[0], fragment[1]);
-        });
-      });
-    });
-  }
-  function destroy() {
-    listeners.forEach((data) => {
-      data[4]();
-    });
-    empty(listeners);
-  }
-  return {
-    bind,
-    unbind,
-    dispatch,
-    destroy
-  };
-}
-
 const EVENT_MOUNTED = "mounted";
 const EVENT_READY = "ready";
 const EVENT_MOVE = "move";
@@ -402,110 +459,11 @@ const EVENT_SLIDE_KEYDOWN = "sk";
 const EVENT_SHIFTED = "sh";
 const EVENT_END_INDEX_CHANGED = "ei";
 
-function EventInterface(Splide2) {
-  const bus = Splide2 ? Splide2.event.bus : document.createDocumentFragment();
-  const binder = EventBinder();
-  function on(events, callback) {
-    binder.bind(bus, toArray(events).join(" "), (e) => {
-      callback.apply(callback, isArray(e.detail) ? e.detail : []);
-    });
-  }
-  function emit(event) {
-    binder.dispatch(bus, event, slice(arguments, 1));
-  }
-  if (Splide2) {
-    Splide2.event.on(EVENT_DESTROY, binder.destroy);
-  }
-  return assign(binder, {
-    bus,
-    on,
-    off: apply(binder.unbind, bus),
-    emit
-  });
-}
-
-function RequestInterval(interval, onInterval, onUpdate, limit) {
-  const { now } = Date;
-  let startTime;
-  let rate = 0;
-  let id;
-  let paused = true;
-  let count = 0;
-  function update() {
-    if (!paused) {
-      rate = interval ? min((now() - startTime) / interval, 1) : 1;
-      onUpdate && onUpdate(rate);
-      if (rate >= 1) {
-        onInterval();
-        startTime = now();
-        if (limit && ++count >= limit) {
-          return pause();
-        }
-      }
-      id = raf(update);
-    }
-  }
-  function start(resume) {
-    resume || cancel();
-    startTime = now() - (resume ? rate * interval : 0);
-    paused = false;
-    id = raf(update);
-  }
-  function pause() {
-    paused = true;
-  }
-  function rewind() {
-    startTime = now();
-    rate = 0;
-    if (onUpdate) {
-      onUpdate(rate);
-    }
-  }
-  function cancel() {
-    id && cancelAnimationFrame(id);
-    rate = 0;
-    id = 0;
-    paused = true;
-  }
-  function set(time) {
-    interval = time;
-  }
-  function isPaused() {
-    return paused;
-  }
-  return {
-    start,
-    rewind,
-    pause,
-    cancel,
-    set,
-    isPaused
-  };
-}
-
-function State(initialState) {
-  let state = initialState;
-  function set(value) {
-    state = value;
-  }
-  function is(states) {
-    return includes(toArray(states), state);
-  }
-  return { set, is };
-}
-
-function Throttle(func, duration) {
-  const interval = RequestInterval(duration || 0, func, null, 1);
-  return () => {
-    interval.isPaused() && interval.start();
-  };
-}
-
 function Media(Splide2, Components2, options) {
   const { state } = Splide2;
   const breakpoints = options.breakpoints || {};
   const reducedMotion = options.reducedMotion || {};
-  const binder = EventBinder();
+  const binder = H();
   const queries = [];
   function setup() {
     const isMin = options.mediaQuery === "min";
@@ -698,8 +656,8 @@ const POINTER_DOWN_EVENTS = "touchstart mousedown";
 const POINTER_MOVE_EVENTS = "touchmove mousemove";
 const POINTER_UP_EVENTS = "touchend touchcancel mouseup click";
 
-function Elements(Splide2, Components2, options) {
-  const { on, bind } = EventInterface(Splide2);
+function Elements(Splide2, Components2, options, event) {
+  const { on, bind } = event;
   const { root } = Splide2;
   const { i18n } = options;
   const elements = {};
@@ -797,7 +755,7 @@ const LOOP = "loop";
 const FADE = "fade";
 
 function Slide$1(Splide2, index, slideIndex, slide) {
-  const event = EventInterface(Splide2);
+  const event = Splide2.event.create();
   const { on, emit, bind } = event;
   const { Components, root, options } = Splide2;
   const { isNavigation, updateOnMove, i18n, pagination, slideFocus } = options;
@@ -923,8 +881,8 @@ function Slide$1(Splide2, index, slideIndex, slide) {
   return self;
 }
 
-function Slides(Splide2, Components2, options) {
-  const { on, emit, bind } = EventInterface(Splide2);
+function Slides(Splide2, Components2, options, event) {
+  const { on, emit, bind } = event;
   const { slides, list } = Components2.Elements;
   const Slides2 = [];
   function mount() {
@@ -1036,8 +994,85 @@ function Slides(Splide2, Components2, options) {
   };
 }
 
-function Layout(Splide2, Components2, options) {
-  const { on, bind, emit } = EventInterface(Splide2);
+function RequestInterval(interval, onInterval, onUpdate, limit) {
+  const { now } = Date;
+  let startTime;
+  let rate = 0;
+  let id;
+  let paused = true;
+  let count = 0;
+  function update() {
+    if (!paused) {
+      rate = interval ? min((now() - startTime) / interval, 1) : 1;
+      onUpdate && onUpdate(rate);
+      if (rate >= 1) {
+        onInterval();
+        startTime = now();
+        if (limit && ++count >= limit) {
+          return pause();
+        }
+      }
+      id = raf(update);
+    }
+  }
+  function start(resume) {
+    resume || cancel();
+    startTime = now() - (resume ? rate * interval : 0);
+    paused = false;
+    id = raf(update);
+  }
+  function pause() {
+    paused = true;
+  }
+  function rewind() {
+    startTime = now();
+    rate = 0;
+    if (onUpdate) {
+      onUpdate(rate);
+    }
+  }
+  function cancel() {
+    id && cancelAnimationFrame(id);
+    rate = 0;
+    id = 0;
+    paused = true;
+  }
+  function set(time) {
+    interval = time;
+  }
+  function isPaused() {
+    return paused;
+  }
+  return {
+    start,
+    rewind,
+    pause,
+    cancel,
+    set,
+    isPaused
+  };
+}
+
+function State(initialState) {
+  let state = initialState;
+  function set(value) {
+    state = value;
+  }
+  function is(states) {
+    return includes(toArray(states), state);
+  }
+  return { set, is };
+}
+
+function Throttle(func, duration) {
+  const interval = RequestInterval(duration || 0, func, null, 1);
+  return () => {
+    interval.isPaused() && interval.start();
+  };
+}
+
+function Layout(Splide2, Components2, options, event) {
+  const { on, bind, emit } = event;
   const { Slides } = Components2;
   const { resolve } = Components2.Direction;
   const { root, track, list } = Components2.Elements;
@@ -1142,8 +1177,7 @@ function Layout(Splide2, Components2, options) {
 }
 
 const MULTIPLIER = 2;
-function Clones(Splide2, Components2, options) {
-  const event = EventInterface(Splide2);
+function Clones(Splide2, Components2, options, event) {
   const { on } = event;
   const { Elements, Slides } = Components2;
   const { resolve } = Components2.Direction;
@@ -1213,8 +1247,8 @@ function Clones(Splide2, Components2, options) {
   };
 }
 
-function Move(Splide2, Components2, options) {
-  const { on, emit } = EventInterface(Splide2);
+function Move(Splide2, Components2, options, event) {
+  const { on, emit } = event;
   const { set } = Splide2.state;
   const { slideSize, getPadding, totalSize, listSize, sliderSize } = Components2.Layout;
   const { resolve, orient } = Components2.Direction;
@@ -1338,8 +1372,8 @@ function Move(Splide2, Components2, options) {
   };
 }
 
-function Controller(Splide2, Components2, options) {
-  const { on, emit } = EventInterface(Splide2);
+function Controller(Splide2, Components2, options, event) {
+  const { on, emit } = event;
   const { Move } = Components2;
   const { getPosition, getLimit, toPosition } = Move;
   const { isEnough, getLength } = Components2.Slides;
@@ -1517,8 +1551,7 @@ const XML_NAME_SPACE = "http://www.w3.org/2000/svg";
 const PATH = "m15.5 0.932-4.3 4.38 14.5 14.6-14.5 14.5 4.3 4.4 14.6-14.6 4.4-4.3-4.4-4.4-14.6-14.6z";
 const SIZE = 40;
 
-function Arrows(Splide2, Components2, options) {
-  const event = EventInterface(Splide2);
+function Arrows(Splide2, Components2, options, event) {
   const { on, bind, emit } = event;
   const { classes, i18n } = options;
   const { Elements, Controller } = Components2;
@@ -1608,8 +1641,8 @@ function Arrows(Splide2, Components2, options) {
 
 const INTERVAL_DATA_ATTRIBUTE = `${DATA_ATTRIBUTE}-interval`;
 
-function Autoplay(Splide2, Components2, options) {
-  const { on, bind, emit } = EventInterface(Splide2);
+function Autoplay(Splide2, Components2, options, event) {
+  const { on, bind, emit } = event;
   const interval = RequestInterval(options.interval, Splide2.go.bind(Splide2, ">"), onAnimationFrame);
   const { isPaused } = interval;
   const { Elements, Elements: { root, toggle } } = Components2;
@@ -1691,8 +1724,8 @@ function Autoplay(Splide2, Components2, options) {
   };
 }
 
-function Cover(Splide2, Components2, options) {
-  const { on } = EventInterface(Splide2);
+function Cover(Splide2, Components2, options, event) {
+  const { on } = event;
   function mount() {
     if (options.cover) {
       on(EVENT_LAZYLOAD_LOADED, apply(toggle, true));
@@ -1723,8 +1756,8 @@ const FRICTION_FACTOR = 0.6;
 const BASE_VELOCITY = 1.5;
 const MIN_DURATION = 800;
 
-function Scroll(Splide2, Components2, options) {
-  const { on, emit } = EventInterface(Splide2);
+function Scroll(Splide2, Components2, options, event) {
+  const { on, emit } = event;
   const { state: { set } } = Splide2;
   const { Move } = Components2;
   const { getPosition, getLimit, exceededLimit, translate } = Move;
@@ -1795,8 +1828,9 @@ function Scroll(Splide2, Components2, options) {
 
 const SCROLL_LISTENER_OPTIONS = { passive: false, capture: true };
 
-function Drag(Splide2, Components2, options) {
-  const { on, emit, bind, unbind } = EventInterface(Splide2);
+function Drag(Splide2, Components2, options, event) {
+  const { on, emit, bind } = event;
+  const binder = event.create();
   const { state } = Splide2;
   const { Move, Scroll, Controller, Elements: { track }, Media: { reduce } } = Components2;
   const { resolve, orient } = Components2.Direction;
@@ -1832,8 +1866,8 @@ function Drag(Splide2, Components2, options) {
           target = isTouch ? track : window;
           dragging = state.is([MOVING, SCROLLING]);
           prevBaseEvent = null;
-          bind(target, POINTER_MOVE_EVENTS, onPointerMove, SCROLL_LISTENER_OPTIONS);
-          bind(target, POINTER_UP_EVENTS, onPointerUp, SCROLL_LISTENER_OPTIONS);
+          binder.bind(target, POINTER_MOVE_EVENTS, onPointerMove, SCROLL_LISTENER_OPTIONS);
+          binder.bind(target, POINTER_UP_EVENTS, onPointerUp, SCROLL_LISTENER_OPTIONS);
           Move.cancel();
           Scroll.cancel();
           save(e);
@@ -1874,8 +1908,7 @@ function Drag(Splide2, Components2, options) {
       move(e);
       prevent(e);
     }
-    unbind(target, POINTER_MOVE_EVENTS, onPointerMove);
-    unbind(target, POINTER_UP_EVENTS, onPointerUp);
+    binder.destroy();
     dragging = false;
   }
   function onClick(e) {
@@ -1977,8 +2010,8 @@ function normalizeKey(key) {
 }
 
 const KEYBOARD_EVENT = "keydown";
-function Keyboard(Splide2, Components2, options) {
-  const { on, bind, unbind } = EventInterface(Splide2);
+function Keyboard(Splide2, Components2, options, event) {
+  const { on, bind, destroy } = event;
   const { root } = Splide2;
   const { resolve } = Components2.Direction;
   let target;
@@ -1996,9 +2029,6 @@ function Keyboard(Splide2, Components2, options) {
       bind(target, KEYBOARD_EVENT, onKeydown);
     }
   }
-  function destroy() {
-    unbind(target, KEYBOARD_EVENT);
-  }
   function disable(value) {
     disabled = value;
   }
@@ -2030,8 +2060,8 @@ const SRC_DATA_ATTRIBUTE = `${DATA_ATTRIBUTE}-lazy`;
 const SRCSET_DATA_ATTRIBUTE = `${SRC_DATA_ATTRIBUTE}-srcset`;
 const IMAGE_SELECTOR = `[${SRC_DATA_ATTRIBUTE}], [${SRCSET_DATA_ATTRIBUTE}]`;
 
-function LazyLoad(Splide2, Components2, options) {
-  const { on, off, bind, emit } = EventInterface(Splide2);
+function LazyLoad(Splide2, Components2, options, event) {
+  const { on, off, bind, emit } = event;
   const isSequential = options.lazyLoad === "sequential";
   const events = [EVENT_MOVED, EVENT_SCROLLED];
   let entries = [];
@@ -2104,8 +2134,7 @@ function LazyLoad(Splide2, Components2, options) {
   };
 }
 
-function Pagination(Splide2, Components2, options) {
-  const event = EventInterface(Splide2);
+function Pagination(Splide2, Components2, options, event) {
   const { on, emit, bind } = event;
   const { Slides, Elements, Controller } = Components2;
   const { hasFocus, getIndex, go } = Controller;
@@ -2218,7 +2247,7 @@ function Pagination(Splide2, Components2, options) {
 }
 
 const TRIGGER_KEYS = [" ", "Enter"];
-function Sync(Splide2, Components2, options) {
+function Sync(Splide2, Components2, options, event) {
   const { isNavigation, slideFocus } = options;
   const events = [];
   function mount() {
@@ -2233,8 +2262,8 @@ function Sync(Splide2, Components2, options) {
     }
   }
   function destroy() {
-    events.forEach((event) => {
-      event.destroy();
+    events.forEach((event2) => {
+      event2.destroy();
     });
     empty(events);
   }
@@ -2243,23 +2272,27 @@ function Sync(Splide2, Components2, options) {
     mount();
   }
   function sync(splide, target) {
-    const event = EventInterface(splide);
-    event.on(EVENT_MOVE, (index, prev, dest) => {
+    const event2 = splide.event.create();
+    event2.on(EVENT_MOVE, (index, prev, dest) => {
       target.go(target.is(LOOP) ? dest : index);
     });
-    events.push(event);
+    events.push(event2);
   }
   function navigate() {
-    const event = EventInterface(Splide2);
-    const { on } = event;
+    const ev = event.create();
+    const { on } = ev;
     on(EVENT_CLICK, onClick);
     on(EVENT_SLIDE_KEYDOWN, onKeydown);
     on([EVENT_MOUNTED, EVENT_UPDATED], update);
-    events.push(event);
-    event.emit(EVENT_NAVIGATION_MOUNTED, Splide2.splides);
+    events.push(ev);
+    ev.emit(EVENT_NAVIGATION_MOUNTED, Splide2.splides);
   }
   function update() {
-    setAttribute(Components2.Elements.list, ARIA_ORIENTATION, options.direction === TTB ? "vertical" : "");
+    setAttribute(
+      Components2.Elements.list,
+      ARIA_ORIENTATION,
+      options.direction === TTB ? "vertical" : ""
+    );
   }
   function onClick(Slide) {
     Splide2.go(Slide.index);
@@ -2282,12 +2315,11 @@ function Sync(Splide2, Components2, options) {
   };
 }
 
-function Wheel(Splide2, Components2, options) {
-  const { bind } = EventInterface(Splide2);
+function Wheel(Splide2, Components2, options, event) {
   let lastTime = 0;
   function mount() {
     if (options.wheel) {
-      bind(Components2.Elements.track, "wheel", onWheel, SCROLL_LISTENER_OPTIONS);
+      event.bind(Components2.Elements.track, "wheel", onWheel, SCROLL_LISTENER_OPTIONS);
     }
   }
   function onWheel(e) {
@@ -2313,8 +2345,8 @@ function Wheel(Splide2, Components2, options) {
 }
 
 const SR_REMOVAL_DELAY = 90;
-function Live(Splide2, Components2, options) {
-  const { on } = EventInterface(Splide2);
+function Live(Splide2, Components2, options, event) {
+  const { on } = event;
   const { track } = Components2.Elements;
   const enabled = options.live && !options.isNavigation;
   const sr = create("span", CLASS_SR);
@@ -2421,10 +2453,10 @@ const DEFAULTS = {
   }
 };
 
-function Fade(Splide2, Components2, options) {
+function Fade(Splide2, Components2, options, event) {
   const { Slides } = Components2;
   function mount() {
-    EventInterface(Splide2).on([EVENT_MOUNTED, EVENT_REFRESH], init);
+    event.on([EVENT_MOUNTED, EVENT_REFRESH], init);
   }
   function init() {
     Slides.forEach((Slide) => {
@@ -2442,13 +2474,13 @@ function Fade(Splide2, Components2, options) {
   };
 }
 
-function Slide(Splide2, Components2, options) {
+function Slide(Splide2, Components2, options, event) {
   const { Move, Controller, Scroll } = Components2;
   const { list } = Components2.Elements;
   const transition = apply(style, list, "transition");
   let endCallback;
   function mount() {
-    EventInterface(Splide2).bind(list, "transitionend", (e) => {
+    event.bind(list, "transitionend", (e) => {
       if (e.target === list && endCallback) {
         cancel();
         endCallback();
@@ -2498,7 +2530,7 @@ class Splide {
   static defaults = {};
   static STATES = STATES;
   root;
-  event = EventInterface();
+  event = fn();
   Components = {};
   state = State(CREATED);
   splides = [];
@@ -2530,7 +2562,7 @@ class Splide {
     this._E = Extensions || this._E;
     const Constructors = assign({}, ComponentConstructors, this._E, { Transition: this._T });
     forOwn(Constructors, (Component, key) => {
-      const component = Component(this, Components2, this._o);
+      const component = Component(this, Components2, this._o, this.event.create());
       Components2[key] = component;
       component.setup && component.setup();
     });
@@ -2560,8 +2592,8 @@ class Splide {
     this.event.on(events, callback);
     return this;
   }
-  off(events) {
-    this.event.off(events);
+  off(events, callback) {
+    this.event.off(events, callback);
     return this;
   }
   emit(event) {
@@ -2586,7 +2618,7 @@ class Splide {
   destroy(completely = true) {
     const { event, state } = this;
     if (state.is(CREATED)) {
-      EventInterface(this).on(EVENT_READY, this.destroy.bind(this, completely));
+      this.on(EVENT_READY, this.destroy.bind(this, completely));
     } else {
       forOwn(this._C, (component) => {
         component.destroy && component.destroy(completely);
@@ -2665,7 +2697,7 @@ class Style {
 
 class SplideRenderer {
   static clean(splide) {
-    const { on } = EventInterface(splide);
+    const { on } = splide.event;
     const { root } = splide;
     const clones = queryAll(root, `.${CLASS_CLONE}`);
     on(EVENT_MOUNTED, () => {
@@ -2986,4 +3018,4 @@ class SplideRenderer {
   }
 }
 
-export { CLASSES, CLASS_ACTIVE, CLASS_ARROW, CLASS_ARROWS, CLASS_ARROW_NEXT, CLASS_ARROW_PREV, CLASS_CLONE, CLASS_CONTAINER, CLASS_FOCUS_IN, CLASS_INITIALIZED, CLASS_LIST, CLASS_LOADING, CLASS_NEXT, CLASS_OVERFLOW, CLASS_PAGINATION, CLASS_PAGINATION_PAGE, CLASS_PREV, CLASS_PROGRESS, CLASS_PROGRESS_BAR, CLASS_ROOT, CLASS_SLIDE, CLASS_SPINNER, CLASS_SR, CLASS_TOGGLE, CLASS_TOGGLE_PAUSE, CLASS_TOGGLE_PLAY, CLASS_TRACK, CLASS_VISIBLE, DEFAULTS, EVENT_ACTIVE, EVENT_ARROWS_MOUNTED, EVENT_ARROWS_UPDATED, EVENT_AUTOPLAY_PAUSE, EVENT_AUTOPLAY_PLAY, EVENT_AUTOPLAY_PLAYING, EVENT_CLICK, EVENT_DESTROY, EVENT_DRAG, EVENT_DRAGGED, EVENT_DRAGGING, EVENT_END_INDEX_CHANGED, EVENT_HIDDEN, EVENT_INACTIVE, EVENT_LAZYLOAD_LOADED, EVENT_MOUNTED, EVENT_MOVE, EVENT_MOVED, EVENT_NAVIGATION_MOUNTED, EVENT_OVERFLOW, EVENT_PAGINATION_MOUNTED, EVENT_PAGINATION_UPDATED, EVENT_READY, EVENT_REFRESH, EVENT_RESIZE, EVENT_RESIZED, EVENT_SCROLL, EVENT_SCROLLED, EVENT_SHIFTED, EVENT_SLIDE_KEYDOWN, EVENT_UPDATED, EVENT_VISIBLE, EventBinder, EventInterface, FADE, LOOP, LTR, RTL, RequestInterval, SLIDE, STATUS_CLASSES, Splide, SplideRenderer, State, TTB, Throttle, Splide as default };
+export { CLASSES, CLASS_ACTIVE, CLASS_ARROW, CLASS_ARROWS, CLASS_ARROW_NEXT, CLASS_ARROW_PREV, CLASS_CLONE, CLASS_CONTAINER, CLASS_FOCUS_IN, CLASS_INITIALIZED, CLASS_LIST, CLASS_LOADING, CLASS_NEXT, CLASS_OVERFLOW, CLASS_PAGINATION, CLASS_PAGINATION_PAGE, CLASS_PREV, CLASS_PROGRESS, CLASS_PROGRESS_BAR, CLASS_ROOT, CLASS_SLIDE, CLASS_SPINNER, CLASS_SR, CLASS_TOGGLE, CLASS_TOGGLE_PAUSE, CLASS_TOGGLE_PLAY, CLASS_TRACK, CLASS_VISIBLE, DEFAULTS, EVENT_ACTIVE, EVENT_ARROWS_MOUNTED, EVENT_ARROWS_UPDATED, EVENT_AUTOPLAY_PAUSE, EVENT_AUTOPLAY_PLAY, EVENT_AUTOPLAY_PLAYING, EVENT_CLICK, EVENT_DESTROY, EVENT_DRAG, EVENT_DRAGGED, EVENT_DRAGGING, EVENT_END_INDEX_CHANGED, EVENT_HIDDEN, EVENT_INACTIVE, EVENT_LAZYLOAD_LOADED, EVENT_MOUNTED, EVENT_MOVE, EVENT_MOVED, EVENT_NAVIGATION_MOUNTED, EVENT_OVERFLOW, EVENT_PAGINATION_MOUNTED, EVENT_PAGINATION_UPDATED, EVENT_READY, EVENT_REFRESH, EVENT_RESIZE, EVENT_RESIZED, EVENT_SCROLL, EVENT_SCROLLED, EVENT_SHIFTED, EVENT_SLIDE_KEYDOWN, EVENT_UPDATED, EVENT_VISIBLE, FADE, LOOP, LTR, RTL, RequestInterval, SLIDE, STATUS_CLASSES, Splide, SplideRenderer, State, TTB, Throttle, Splide as default };

+ 250 - 218
dist/js/splide.js

@@ -29,6 +29,120 @@
     DESTROYED
   };
 
+  function x(n) {
+    n.length = 0;
+  }
+  const D = Array.prototype;
+  function m(n, t, e) {
+    return D.slice.call(n, t, e);
+  }
+  function a(n) {
+    return n.bind(null, ...m(arguments, 1));
+  }
+  function p(n, t) {
+    return typeof t === n;
+  }
+  function g(n) {
+    return !q(n) && p("object", n);
+  }
+  const $ = Array.isArray; a(p, "function"); const h = a(p, "string"); a(p, "boolean"); a(p, "undefined");
+  function q(n) {
+    return n === null;
+  }
+  function L(n) {
+    return $(n) ? n : [n];
+  }
+  function d(n, t) {
+    L(n).forEach(t);
+  }
+  const b = Object.assign, A = Object.keys;
+  function O(n, t, e) {
+    return n && (e ? A(n).reverse() : A(n)).forEach((i) => {
+      i !== "__proto__" && t(n[i], i);
+    }), n;
+  }
+  function rn(n) {
+    let t = "";
+    return O(n, (e, i) => {
+      t += e || e === 0 ? ` ${i}${e !== true ? `="${e}"` : ""}` : "";
+    }), t.trim();
+  }
+  function _() {
+    return m(arguments).reduce((n, t) => `${n} ${$(t) ? _(...t) : t || ""}`.trim(), "");
+  }
+  function on(n, t) {
+    return t = $(t) || !g(t) ? { class: _(t) } : t, `<${`${n} ${t ? rn(t) : ""}`.trim()}>`;
+  }
+  a(on, "div");
+  function C(n, t) {
+    d(n, (e) => {
+      h(e) && e.split(" ").forEach(t);
+    });
+  }
+  function S(n, t) {
+    const e = [];
+    for (let i = n.length - 1; i >= 0; i--)
+      t(n[i]) && e.push(...n.splice(i, 1));
+    return e;
+  }
+  function H(n) {
+    const t = n || [], e = n ? {} : void 0;
+    function i(o, f, u, c) {
+      C(f, (s) => {
+        o.addEventListener(s, u, c), t.push([o.removeEventListener.bind(o, s, u, c), e]);
+      });
+    }
+    function r() {
+      e ? S(t, (o) => o[1] === e).forEach((o) => {
+        o[0]();
+      }) : (t.forEach((o) => {
+        o[0]();
+      }), x(t));
+    }
+    return {
+      bind: i,
+      create: a(H, t),
+      destroy: r
+    };
+  }
+  function v(n) {
+    const t = n || [], e = n ? {} : void 0;
+    function i(u, c) {
+      C(u, (s) => {
+        t.push([s, c, e]);
+      });
+    }
+    function r(u, c) {
+      C(u, (s) => {
+        S(t, (l) => l[0] === s && (!c || l[1] === c) && l[2] === e);
+      });
+    }
+    function o(u, ...c) {
+      t.slice().forEach((s) => {
+        s[0] === u && s[1](...c);
+      });
+    }
+    function f() {
+      e ? S(t, (u) => u[2] === e) : x(t);
+    }
+    return {
+      on: i,
+      off: r,
+      emit: o,
+      create: a(v, t),
+      destroy: f
+    };
+  }
+  function fn(n = H(), t = v()) {
+    function e() {
+      return fn(n.create(), t.create());
+    }
+    function i() {
+      n.destroy(), t.destroy();
+    }
+    return b({}, n, t, { create: e, destroy: i });
+  }
+
   function empty(array) {
     array.length = 0;
   }
@@ -314,63 +428,6 @@
     return `${prefix}${pad(ids[prefix] = (ids[prefix] || 0) + 1)}`;
   }
 
-  function EventBinder() {
-    let listeners = [];
-    function bind(targets, events, callback, options) {
-      forEachEvent(targets, events, (target, event, namespace) => {
-        const isEventTarget = "addEventListener" in target;
-        const remover = isEventTarget ? target.removeEventListener.bind(target, event, callback, options) : target["removeListener"].bind(target, callback);
-        isEventTarget ? target.addEventListener(event, callback, options) : target["addListener"](callback);
-        listeners.push([target, event, namespace, callback, remover]);
-      });
-    }
-    function unbind(targets, events, callback) {
-      forEachEvent(targets, events, (target, event, namespace) => {
-        listeners = listeners.filter((listener) => {
-          if (listener[0] === target && listener[1] === event && listener[2] === namespace && (!callback || listener[3] === callback)) {
-            listener[4]();
-            return false;
-          }
-          return true;
-        });
-      });
-    }
-    function dispatch(target, type, detail) {
-      let e;
-      const bubbles = true;
-      if (typeof CustomEvent === "function") {
-        e = new CustomEvent(type, { bubbles, detail });
-      } else {
-        e = document.createEvent("CustomEvent");
-        e.initCustomEvent(type, bubbles, false, detail);
-      }
-      target.dispatchEvent(e);
-      return e;
-    }
-    function forEachEvent(targets, events, iteratee) {
-      forEach(targets, (target) => {
-        target && forEach(events, (events2) => {
-          events2.split(" ").forEach((eventNS) => {
-            const fragment = eventNS.split(".");
-            iteratee(target, fragment[0], fragment[1]);
-          });
-        });
-      });
-    }
-    function destroy() {
-      listeners.forEach((data) => {
-        data[4]();
-      });
-      empty(listeners);
-    }
-    return {
-      bind,
-      unbind,
-      dispatch,
-      destroy
-    };
-  }
-
   const EVENT_MOUNTED = "mounted";
   const EVENT_READY = "ready";
   const EVENT_MOVE = "move";
@@ -404,110 +461,11 @@
   const EVENT_SHIFTED = "sh";
   const EVENT_END_INDEX_CHANGED = "ei";
 
-  function EventInterface(Splide2) {
-    const bus = Splide2 ? Splide2.event.bus : document.createDocumentFragment();
-    const binder = EventBinder();
-    function on(events, callback) {
-      binder.bind(bus, toArray(events).join(" "), (e) => {
-        callback.apply(callback, isArray(e.detail) ? e.detail : []);
-      });
-    }
-    function emit(event) {
-      binder.dispatch(bus, event, slice(arguments, 1));
-    }
-    if (Splide2) {
-      Splide2.event.on(EVENT_DESTROY, binder.destroy);
-    }
-    return assign(binder, {
-      bus,
-      on,
-      off: apply(binder.unbind, bus),
-      emit
-    });
-  }
-
-  function RequestInterval(interval, onInterval, onUpdate, limit) {
-    const { now } = Date;
-    let startTime;
-    let rate = 0;
-    let id;
-    let paused = true;
-    let count = 0;
-    function update() {
-      if (!paused) {
-        rate = interval ? min((now() - startTime) / interval, 1) : 1;
-        onUpdate && onUpdate(rate);
-        if (rate >= 1) {
-          onInterval();
-          startTime = now();
-          if (limit && ++count >= limit) {
-            return pause();
-          }
-        }
-        id = raf(update);
-      }
-    }
-    function start(resume) {
-      resume || cancel();
-      startTime = now() - (resume ? rate * interval : 0);
-      paused = false;
-      id = raf(update);
-    }
-    function pause() {
-      paused = true;
-    }
-    function rewind() {
-      startTime = now();
-      rate = 0;
-      if (onUpdate) {
-        onUpdate(rate);
-      }
-    }
-    function cancel() {
-      id && cancelAnimationFrame(id);
-      rate = 0;
-      id = 0;
-      paused = true;
-    }
-    function set(time) {
-      interval = time;
-    }
-    function isPaused() {
-      return paused;
-    }
-    return {
-      start,
-      rewind,
-      pause,
-      cancel,
-      set,
-      isPaused
-    };
-  }
-
-  function State(initialState) {
-    let state = initialState;
-    function set(value) {
-      state = value;
-    }
-    function is(states) {
-      return includes(toArray(states), state);
-    }
-    return { set, is };
-  }
-
-  function Throttle(func, duration) {
-    const interval = RequestInterval(duration || 0, func, null, 1);
-    return () => {
-      interval.isPaused() && interval.start();
-    };
-  }
-
   function Media(Splide2, Components2, options) {
     const { state } = Splide2;
     const breakpoints = options.breakpoints || {};
     const reducedMotion = options.reducedMotion || {};
-    const binder = EventBinder();
+    const binder = H();
     const queries = [];
     function setup() {
       const isMin = options.mediaQuery === "min";
@@ -697,8 +655,8 @@
   const POINTER_MOVE_EVENTS = "touchmove mousemove";
   const POINTER_UP_EVENTS = "touchend touchcancel mouseup click";
 
-  function Elements(Splide2, Components2, options) {
-    const { on, bind } = EventInterface(Splide2);
+  function Elements(Splide2, Components2, options, event) {
+    const { on, bind } = event;
     const { root } = Splide2;
     const { i18n } = options;
     const elements = {};
@@ -796,7 +754,7 @@
   const FADE = "fade";
 
   function Slide$1(Splide2, index, slideIndex, slide) {
-    const event = EventInterface(Splide2);
+    const event = Splide2.event.create();
     const { on, emit, bind } = event;
     const { Components, root, options } = Splide2;
     const { isNavigation, updateOnMove, i18n, pagination, slideFocus } = options;
@@ -922,8 +880,8 @@
     return self;
   }
 
-  function Slides(Splide2, Components2, options) {
-    const { on, emit, bind } = EventInterface(Splide2);
+  function Slides(Splide2, Components2, options, event) {
+    const { on, emit, bind } = event;
     const { slides, list } = Components2.Elements;
     const Slides2 = [];
     function mount() {
@@ -1035,8 +993,85 @@
     };
   }
 
-  function Layout(Splide2, Components2, options) {
-    const { on, bind, emit } = EventInterface(Splide2);
+  function RequestInterval(interval, onInterval, onUpdate, limit) {
+    const { now } = Date;
+    let startTime;
+    let rate = 0;
+    let id;
+    let paused = true;
+    let count = 0;
+    function update() {
+      if (!paused) {
+        rate = interval ? min((now() - startTime) / interval, 1) : 1;
+        onUpdate && onUpdate(rate);
+        if (rate >= 1) {
+          onInterval();
+          startTime = now();
+          if (limit && ++count >= limit) {
+            return pause();
+          }
+        }
+        id = raf(update);
+      }
+    }
+    function start(resume) {
+      resume || cancel();
+      startTime = now() - (resume ? rate * interval : 0);
+      paused = false;
+      id = raf(update);
+    }
+    function pause() {
+      paused = true;
+    }
+    function rewind() {
+      startTime = now();
+      rate = 0;
+      if (onUpdate) {
+        onUpdate(rate);
+      }
+    }
+    function cancel() {
+      id && cancelAnimationFrame(id);
+      rate = 0;
+      id = 0;
+      paused = true;
+    }
+    function set(time) {
+      interval = time;
+    }
+    function isPaused() {
+      return paused;
+    }
+    return {
+      start,
+      rewind,
+      pause,
+      cancel,
+      set,
+      isPaused
+    };
+  }
+
+  function State(initialState) {
+    let state = initialState;
+    function set(value) {
+      state = value;
+    }
+    function is(states) {
+      return includes(toArray(states), state);
+    }
+    return { set, is };
+  }
+
+  function Throttle(func, duration) {
+    const interval = RequestInterval(duration || 0, func, null, 1);
+    return () => {
+      interval.isPaused() && interval.start();
+    };
+  }
+
+  function Layout(Splide2, Components2, options, event) {
+    const { on, bind, emit } = event;
     const { Slides } = Components2;
     const { resolve } = Components2.Direction;
     const { root, track, list } = Components2.Elements;
@@ -1141,8 +1176,7 @@
   }
 
   const MULTIPLIER = 2;
-  function Clones(Splide2, Components2, options) {
-    const event = EventInterface(Splide2);
+  function Clones(Splide2, Components2, options, event) {
     const { on } = event;
     const { Elements, Slides } = Components2;
     const { resolve } = Components2.Direction;
@@ -1212,8 +1246,8 @@
     };
   }
 
-  function Move(Splide2, Components2, options) {
-    const { on, emit } = EventInterface(Splide2);
+  function Move(Splide2, Components2, options, event) {
+    const { on, emit } = event;
     const { set } = Splide2.state;
     const { slideSize, getPadding, totalSize, listSize, sliderSize } = Components2.Layout;
     const { resolve, orient } = Components2.Direction;
@@ -1337,8 +1371,8 @@
     };
   }
 
-  function Controller(Splide2, Components2, options) {
-    const { on, emit } = EventInterface(Splide2);
+  function Controller(Splide2, Components2, options, event) {
+    const { on, emit } = event;
     const { Move } = Components2;
     const { getPosition, getLimit, toPosition } = Move;
     const { isEnough, getLength } = Components2.Slides;
@@ -1516,8 +1550,7 @@
   const PATH = "m15.5 0.932-4.3 4.38 14.5 14.6-14.5 14.5 4.3 4.4 14.6-14.6 4.4-4.3-4.4-4.4-14.6-14.6z";
   const SIZE = 40;
 
-  function Arrows(Splide2, Components2, options) {
-    const event = EventInterface(Splide2);
+  function Arrows(Splide2, Components2, options, event) {
     const { on, bind, emit } = event;
     const { classes, i18n } = options;
     const { Elements, Controller } = Components2;
@@ -1607,8 +1640,8 @@
 
   const INTERVAL_DATA_ATTRIBUTE = `${DATA_ATTRIBUTE}-interval`;
 
-  function Autoplay(Splide2, Components2, options) {
-    const { on, bind, emit } = EventInterface(Splide2);
+  function Autoplay(Splide2, Components2, options, event) {
+    const { on, bind, emit } = event;
     const interval = RequestInterval(options.interval, Splide2.go.bind(Splide2, ">"), onAnimationFrame);
     const { isPaused } = interval;
     const { Elements, Elements: { root, toggle } } = Components2;
@@ -1690,8 +1723,8 @@
     };
   }
 
-  function Cover(Splide2, Components2, options) {
-    const { on } = EventInterface(Splide2);
+  function Cover(Splide2, Components2, options, event) {
+    const { on } = event;
     function mount() {
       if (options.cover) {
         on(EVENT_LAZYLOAD_LOADED, apply(toggle, true));
@@ -1722,8 +1755,8 @@
   const BASE_VELOCITY = 1.5;
   const MIN_DURATION = 800;
 
-  function Scroll(Splide2, Components2, options) {
-    const { on, emit } = EventInterface(Splide2);
+  function Scroll(Splide2, Components2, options, event) {
+    const { on, emit } = event;
     const { state: { set } } = Splide2;
     const { Move } = Components2;
     const { getPosition, getLimit, exceededLimit, translate } = Move;
@@ -1794,8 +1827,9 @@
 
   const SCROLL_LISTENER_OPTIONS = { passive: false, capture: true };
 
-  function Drag(Splide2, Components2, options) {
-    const { on, emit, bind, unbind } = EventInterface(Splide2);
+  function Drag(Splide2, Components2, options, event) {
+    const { on, emit, bind } = event;
+    const binder = event.create();
     const { state } = Splide2;
     const { Move, Scroll, Controller, Elements: { track }, Media: { reduce } } = Components2;
     const { resolve, orient } = Components2.Direction;
@@ -1831,8 +1865,8 @@
             target = isTouch ? track : window;
             dragging = state.is([MOVING, SCROLLING]);
             prevBaseEvent = null;
-            bind(target, POINTER_MOVE_EVENTS, onPointerMove, SCROLL_LISTENER_OPTIONS);
-            bind(target, POINTER_UP_EVENTS, onPointerUp, SCROLL_LISTENER_OPTIONS);
+            binder.bind(target, POINTER_MOVE_EVENTS, onPointerMove, SCROLL_LISTENER_OPTIONS);
+            binder.bind(target, POINTER_UP_EVENTS, onPointerUp, SCROLL_LISTENER_OPTIONS);
             Move.cancel();
             Scroll.cancel();
             save(e);
@@ -1873,8 +1907,7 @@
         move(e);
         prevent(e);
       }
-      unbind(target, POINTER_MOVE_EVENTS, onPointerMove);
-      unbind(target, POINTER_UP_EVENTS, onPointerUp);
+      binder.destroy();
       dragging = false;
     }
     function onClick(e) {
@@ -1976,8 +2009,8 @@
   }
 
   const KEYBOARD_EVENT = "keydown";
-  function Keyboard(Splide2, Components2, options) {
-    const { on, bind, unbind } = EventInterface(Splide2);
+  function Keyboard(Splide2, Components2, options, event) {
+    const { on, bind, destroy } = event;
     const { root } = Splide2;
     const { resolve } = Components2.Direction;
     let target;
@@ -1995,9 +2028,6 @@
         bind(target, KEYBOARD_EVENT, onKeydown);
       }
     }
-    function destroy() {
-      unbind(target, KEYBOARD_EVENT);
-    }
     function disable(value) {
       disabled = value;
     }
@@ -2029,8 +2059,8 @@
   const SRCSET_DATA_ATTRIBUTE = `${SRC_DATA_ATTRIBUTE}-srcset`;
   const IMAGE_SELECTOR = `[${SRC_DATA_ATTRIBUTE}], [${SRCSET_DATA_ATTRIBUTE}]`;
 
-  function LazyLoad(Splide2, Components2, options) {
-    const { on, off, bind, emit } = EventInterface(Splide2);
+  function LazyLoad(Splide2, Components2, options, event) {
+    const { on, off, bind, emit } = event;
     const isSequential = options.lazyLoad === "sequential";
     const events = [EVENT_MOVED, EVENT_SCROLLED];
     let entries = [];
@@ -2103,8 +2133,7 @@
     };
   }
 
-  function Pagination(Splide2, Components2, options) {
-    const event = EventInterface(Splide2);
+  function Pagination(Splide2, Components2, options, event) {
     const { on, emit, bind } = event;
     const { Slides, Elements, Controller } = Components2;
     const { hasFocus, getIndex, go } = Controller;
@@ -2217,7 +2246,7 @@
   }
 
   const TRIGGER_KEYS = [" ", "Enter"];
-  function Sync(Splide2, Components2, options) {
+  function Sync(Splide2, Components2, options, event) {
     const { isNavigation, slideFocus } = options;
     const events = [];
     function mount() {
@@ -2232,8 +2261,8 @@
       }
     }
     function destroy() {
-      events.forEach((event) => {
-        event.destroy();
+      events.forEach((event2) => {
+        event2.destroy();
       });
       empty(events);
     }
@@ -2242,23 +2271,27 @@
       mount();
     }
     function sync(splide, target) {
-      const event = EventInterface(splide);
-      event.on(EVENT_MOVE, (index, prev, dest) => {
+      const event2 = splide.event.create();
+      event2.on(EVENT_MOVE, (index, prev, dest) => {
         target.go(target.is(LOOP) ? dest : index);
       });
-      events.push(event);
+      events.push(event2);
     }
     function navigate() {
-      const event = EventInterface(Splide2);
-      const { on } = event;
+      const ev = event.create();
+      const { on } = ev;
       on(EVENT_CLICK, onClick);
       on(EVENT_SLIDE_KEYDOWN, onKeydown);
       on([EVENT_MOUNTED, EVENT_UPDATED], update);
-      events.push(event);
-      event.emit(EVENT_NAVIGATION_MOUNTED, Splide2.splides);
+      events.push(ev);
+      ev.emit(EVENT_NAVIGATION_MOUNTED, Splide2.splides);
     }
     function update() {
-      setAttribute(Components2.Elements.list, ARIA_ORIENTATION, options.direction === TTB ? "vertical" : "");
+      setAttribute(
+        Components2.Elements.list,
+        ARIA_ORIENTATION,
+        options.direction === TTB ? "vertical" : ""
+      );
     }
     function onClick(Slide) {
       Splide2.go(Slide.index);
@@ -2281,12 +2314,11 @@
     };
   }
 
-  function Wheel(Splide2, Components2, options) {
-    const { bind } = EventInterface(Splide2);
+  function Wheel(Splide2, Components2, options, event) {
     let lastTime = 0;
     function mount() {
       if (options.wheel) {
-        bind(Components2.Elements.track, "wheel", onWheel, SCROLL_LISTENER_OPTIONS);
+        event.bind(Components2.Elements.track, "wheel", onWheel, SCROLL_LISTENER_OPTIONS);
       }
     }
     function onWheel(e) {
@@ -2312,8 +2344,8 @@
   }
 
   const SR_REMOVAL_DELAY = 90;
-  function Live(Splide2, Components2, options) {
-    const { on } = EventInterface(Splide2);
+  function Live(Splide2, Components2, options, event) {
+    const { on } = event;
     const { track } = Components2.Elements;
     const enabled = options.live && !options.isNavigation;
     const sr = create("span", CLASS_SR);
@@ -2420,10 +2452,10 @@
     }
   };
 
-  function Fade(Splide2, Components2, options) {
+  function Fade(Splide2, Components2, options, event) {
     const { Slides } = Components2;
     function mount() {
-      EventInterface(Splide2).on([EVENT_MOUNTED, EVENT_REFRESH], init);
+      event.on([EVENT_MOUNTED, EVENT_REFRESH], init);
     }
     function init() {
       Slides.forEach((Slide) => {
@@ -2441,13 +2473,13 @@
     };
   }
 
-  function Slide(Splide2, Components2, options) {
+  function Slide(Splide2, Components2, options, event) {
     const { Move, Controller, Scroll } = Components2;
     const { list } = Components2.Elements;
     const transition = apply(style, list, "transition");
     let endCallback;
     function mount() {
-      EventInterface(Splide2).bind(list, "transitionend", (e) => {
+      event.bind(list, "transitionend", (e) => {
         if (e.target === list && endCallback) {
           cancel();
           endCallback();
@@ -2497,7 +2529,7 @@
     static defaults = {};
     static STATES = STATES;
     root;
-    event = EventInterface();
+    event = fn();
     Components = {};
     state = State(CREATED);
     splides = [];
@@ -2529,7 +2561,7 @@
       this._E = Extensions || this._E;
       const Constructors = assign({}, ComponentConstructors, this._E, { Transition: this._T });
       forOwn(Constructors, (Component, key) => {
-        const component = Component(this, Components2, this._o);
+        const component = Component(this, Components2, this._o, this.event.create());
         Components2[key] = component;
         component.setup && component.setup();
       });
@@ -2559,8 +2591,8 @@
       this.event.on(events, callback);
       return this;
     }
-    off(events) {
-      this.event.off(events);
+    off(events, callback) {
+      this.event.off(events, callback);
       return this;
     }
     emit(event) {
@@ -2585,7 +2617,7 @@
     destroy(completely = true) {
       const { event, state } = this;
       if (state.is(CREATED)) {
-        EventInterface(this).on(EVENT_READY, this.destroy.bind(this, completely));
+        this.on(EVENT_READY, this.destroy.bind(this, completely));
       } else {
         forOwn(this._C, (component) => {
           component.destroy && component.destroy(completely);

文件差異過大導致無法顯示
+ 0 - 0
dist/js/splide.min.js


二進制
dist/js/splide.min.js.gz


文件差異過大導致無法顯示
+ 0 - 0
dist/js/splide.min.js.map


+ 106 - 64
dist/types/index.d.ts

@@ -1,3 +1,97 @@
+/**
+ * The type that matches any function.
+ */
+declare type AnyFunction$1 = (...args: any[]) => any;
+
+/**
+ * The type for an array with remover functions.
+ *
+ * @since 0.0.1
+ */
+declare type Removers = [() => void, object?][];
+/**
+ * The interface for the EventBinder instance.
+ *
+ * @since 0.0.1
+ */
+interface EventBinder {
+    bind(target: EventTarget, events: string | string[], callback: AnyFunction$1, options?: AddEventListenerOptions): void;
+    create(): EventBinder;
+    destroy(): void;
+}
+/**
+ * The constructor function to provide methods to subscribe native events.
+ *
+ * @since 0.0.1
+ * @constructor
+ *
+ * @return An EventBinder instance.
+ */
+declare function EventBinder(removersRef?: Removers): EventBinder;
+
+/**
+ * The type for an array with listener data as `[ event, callback, key ]`.
+ *
+ * @since 0.0.1
+ */
+declare type Listeners = [string, AnyFunction$1, object?][];
+/**
+ * The interface for the EventBus instance.
+ *
+ * @since 0.0.1
+ */
+interface EventBus<M extends Record<string, AnyFunction$1> = Record<string, AnyFunction$1>> {
+    on<K extends keyof M & string>(event: K, callback: M[K]): void;
+    on(events: string | string[], callback: AnyFunction$1): void;
+    off<K extends keyof M & string>(event: K, callback?: M[K]): void;
+    off(events: string | string[], callback?: AnyFunction$1): void;
+    emit<K extends keyof M & string>(event: K, ...args: Parameters<M[K]>): void;
+    emit(event: string, ...args: any[]): void;
+    create(): EventBus<M>;
+    destroy(): void;
+}
+/**
+ * Provides the simple event system.
+ * Note that `M` - type for an event map - must have index signature,
+ * but that makes all callback function `AnyFunction`.
+ * To avoid this:
+ * - Use a type alias instead of interface.
+ * - Or do like `EventBus<EventMap & Record<string, AnyFunction>, keyof EventMap>`.
+ *
+ * @see https://github.com/microsoft/TypeScript/issues/15300
+ *
+ * @since 0.0.1
+ * @constructor
+ *
+ * @param listenersRef - An array with listener data. Internal use only.
+ *
+ * @return An EventBus instance.
+ */
+declare function EventBus<M extends Record<string, AnyFunction$1>, K extends keyof M & string>(listenersRef?: Listeners): EventBus<M>;
+
+/**
+ * The interface for the EventInterface object.
+ *
+ * @since 0.0.1
+ */
+interface EventInterface<M extends Record<string, AnyFunction$1> = Record<string, AnyFunction$1>> extends Omit<EventBinder, 'create'>, Omit<EventBus<M>, 'create'> {
+    create(): EventInterface<M>;
+    destroy(): void;
+}
+/**
+ * The constructor function that provides interface for both internal and native events.
+ *
+ * @since 0.0.1
+ * @constructor
+ * @internal
+ *
+ * @param binder - An `EventBinder` instance. Internal use only.
+ * @param bus    - An `EventBus` instance. Internal use only.
+ *
+ * @return A collection of interface functions.
+ */
+declare function EventInterface<M extends Record<string, AnyFunction$1> = Record<string, AnyFunction$1>>(binder?: EventBinder, bus?: EventBus<Record<string, AnyFunction$1>>): EventInterface<M>;
+
 /**
  * The interface for the Media component.
  *
@@ -650,7 +744,7 @@ declare type AnyFunction = (...args: any[]) => any;
  *
  * @since 3.0.0
  */
-declare type ComponentConstructor = (Splide: Splide, Components: Components, options: Options) => BaseComponent;
+declare type ComponentConstructor = (Splide: Splide, Components: Components, options: Options, event: EventInterface) => BaseComponent;
 /**
  * The interface for any component.
  *
@@ -824,59 +918,6 @@ declare type SlidesPredicate = (Slide: SlideComponent, index: number, Slides: Sl
  */
 declare type SlideMatcher = number | number[] | string | SlidesPredicate;
 
-/**
- * The type for an EventTarget or an array with EventTarget objects.
- *
- * @since 4.0.0
- */
-declare type EventTargets = EventTarget | EventTarget[];
-/**
- * The interface for the EventBinder object.
- *
- * @since 3.0.0
- */
-interface EventBinderObject {
-    bind(target: EventTargets, events: string | string[], callback: AnyFunction, options?: AddEventListenerOptions): void;
-    unbind(target: EventTarget | EventTarget[], events: string | string[], callback?: AnyFunction): void;
-    dispatch<T>(target: EventTarget, event: string, detail?: T): void;
-    destroy(): void;
-}
-/**
- * The constructor function to provide methods to subscribe native events.
- *
- * @since 4.0.0
- * @constructor
- *
- * @return An EventBinder object.
- */
-declare function EventBinder(): EventBinderObject;
-
-/**
- * The interface for the EventInterface object.
- *
- * @since 3.0.0
- */
-interface EventInterfaceObject extends EventBinderObject {
-    on<K extends keyof EventMap>(event: K, callback: EventMap[K]): void;
-    on(events: string | string[], callback: AnyFunction): void;
-    off<K extends keyof EventMap>(events: K | K[] | string | string[]): void;
-    emit<K extends keyof EventMap>(event: K, ...args: Parameters<EventMap[K]>): void;
-    emit(event: string, ...args: any[]): void;
-    /** @internal */
-    bus: DocumentFragment;
-}
-/**
- * The constructor function that provides interface for internal and native events.
- *
- * @since 3.0.0
- * @constructor
- *
- * @param Splide - A Splide instance.
- *
- * @return A collection of interface functions.
- */
-declare function EventInterface(Splide?: Splide): EventInterfaceObject;
-
 /**
  * The interface for the returning value of the RequestInterval.
  *
@@ -965,7 +1006,7 @@ declare class Splide {
     /**
      * The EventBusObject object.
      */
-    readonly event: EventInterfaceObject;
+    readonly event: EventInterface<Record<string, AnyFunction$1>>;
     /**
      * The collection of all component objects.
      */
@@ -1016,8 +1057,8 @@ declare class Splide {
      *
      * @example
      * ```ts
-     * var primary   = new Splide();
-     * var secondary = new Splide();
+     * const primary   = new Splide();
+     * const secondary = new Splide();
      *
      * primary.sync( secondary );
      * primary.mount();
@@ -1046,7 +1087,7 @@ declare class Splide {
      *
      * @example
      * ```ts
-     * var splide = new Splide();
+     * const splide = new Splide();
      *
      * // Goes to the slide 1:
      * splide.go( 1 );
@@ -1071,7 +1112,7 @@ declare class Splide {
      *
      * @example
      * ```ts
-     * var splide = new Splide();
+     * const splide = new Splide();
      *
      * // Listens to a single event:
      * splide.on( 'move', function() {} );
@@ -1096,7 +1137,7 @@ declare class Splide {
      *
      * @example
      * ```ts
-     * var splide = new Splide();
+     * const splide = new Splide();
      *
      * // Removes all handlers assigned to "move":
      * splide.off( 'move' );
@@ -1105,11 +1146,12 @@ declare class Splide {
      * splide.off( 'move.myNamespace' );
      * ```
      *
-     * @param events - An event name or names separated by spaces. Use a dot(.) to append a namespace.
+     * @param events   - An event name or names separated by spaces. Use a dot(.) to append a namespace.
+     * @param callback - A callback function to remove.
      *
      * @return `this`
      */
-    off<K extends keyof EventMap>(events: K | K[] | string | string[]): this;
+    off<K extends keyof EventMap>(events: K | K[] | string | string[], callback: AnyFunction): this;
     /**
      * Emits an event and triggers registered handlers.
      *
@@ -1125,7 +1167,7 @@ declare class Splide {
      *
      * @example
      * ```ts
-     * var splide = new Splide();
+     * const splide = new Splide();
      * splide.mount();
      *
      * // Adds the slide by the HTML:
@@ -1688,4 +1730,4 @@ declare const LOOP = "loop";
  */
 declare const FADE = "fade";
 
-export { AnyFunction, ArrowsComponent, AutoplayComponent, BaseComponent, CLASSES, CLASS_ACTIVE, CLASS_ARROW, CLASS_ARROWS, CLASS_ARROW_NEXT, CLASS_ARROW_PREV, CLASS_CLONE, CLASS_CONTAINER, CLASS_FOCUS_IN, CLASS_INITIALIZED, CLASS_LIST, CLASS_LOADING, CLASS_NEXT, CLASS_OVERFLOW, CLASS_PAGINATION, CLASS_PAGINATION_PAGE, CLASS_PREV, CLASS_PROGRESS, CLASS_PROGRESS_BAR, CLASS_ROOT, CLASS_SLIDE, CLASS_SPINNER, CLASS_SR, CLASS_TOGGLE, CLASS_TOGGLE_PAUSE, CLASS_TOGGLE_PLAY, CLASS_TRACK, CLASS_VISIBLE, Cast, ClonesComponent, ComponentConstructor, Components, ControllerComponent, CoverComponent, DEFAULTS, DirectionComponent, DragComponent, EVENT_ACTIVE, EVENT_ARROWS_MOUNTED, EVENT_ARROWS_UPDATED, EVENT_AUTOPLAY_PAUSE, EVENT_AUTOPLAY_PLAY, EVENT_AUTOPLAY_PLAYING, EVENT_CLICK, EVENT_DESTROY, EVENT_DRAG, EVENT_DRAGGED, EVENT_DRAGGING, EVENT_END_INDEX_CHANGED, EVENT_HIDDEN, EVENT_INACTIVE, EVENT_LAZYLOAD_LOADED, EVENT_MOUNTED, EVENT_MOVE, EVENT_MOVED, EVENT_NAVIGATION_MOUNTED, EVENT_OVERFLOW, EVENT_PAGINATION_MOUNTED, EVENT_PAGINATION_UPDATED, EVENT_READY, EVENT_REFRESH, EVENT_RESIZE, EVENT_RESIZED, EVENT_SCROLL, EVENT_SCROLLED, EVENT_SHIFTED, EVENT_SLIDE_KEYDOWN, EVENT_UPDATED, EVENT_VISIBLE, ElementsComponent, EventBinder, EventBinderObject, EventInterface, EventInterfaceObject, EventMap, FADE, Head, KeyboardComponent, LOOP, LTR, LayoutComponent, LazyLoadComponent, LiveComponent, MediaComponent, MoveComponent, Options, PaginationComponent, PaginationData, PaginationItem, Push, RTL, RequestInterval, RequestIntervalInterface, Resolve, ResponsiveOptions, SLIDE, STATUS_CLASSES, ScrollComponent, Shift, ShiftN, SlideComponent, SlidesComponent, Splide, SplideRenderer, State, StateObject, SyncComponent, SyncTarget, TTB, Throttle, ThrottleInstance, TransitionComponent, WheelComponent, Splide as default };
+export { AnyFunction, ArrowsComponent, AutoplayComponent, BaseComponent, CLASSES, CLASS_ACTIVE, CLASS_ARROW, CLASS_ARROWS, CLASS_ARROW_NEXT, CLASS_ARROW_PREV, CLASS_CLONE, CLASS_CONTAINER, CLASS_FOCUS_IN, CLASS_INITIALIZED, CLASS_LIST, CLASS_LOADING, CLASS_NEXT, CLASS_OVERFLOW, CLASS_PAGINATION, CLASS_PAGINATION_PAGE, CLASS_PREV, CLASS_PROGRESS, CLASS_PROGRESS_BAR, CLASS_ROOT, CLASS_SLIDE, CLASS_SPINNER, CLASS_SR, CLASS_TOGGLE, CLASS_TOGGLE_PAUSE, CLASS_TOGGLE_PLAY, CLASS_TRACK, CLASS_VISIBLE, Cast, ClonesComponent, ComponentConstructor, Components, ControllerComponent, CoverComponent, DEFAULTS, DirectionComponent, DragComponent, EVENT_ACTIVE, EVENT_ARROWS_MOUNTED, EVENT_ARROWS_UPDATED, EVENT_AUTOPLAY_PAUSE, EVENT_AUTOPLAY_PLAY, EVENT_AUTOPLAY_PLAYING, EVENT_CLICK, EVENT_DESTROY, EVENT_DRAG, EVENT_DRAGGED, EVENT_DRAGGING, EVENT_END_INDEX_CHANGED, EVENT_HIDDEN, EVENT_INACTIVE, EVENT_LAZYLOAD_LOADED, EVENT_MOUNTED, EVENT_MOVE, EVENT_MOVED, EVENT_NAVIGATION_MOUNTED, EVENT_OVERFLOW, EVENT_PAGINATION_MOUNTED, EVENT_PAGINATION_UPDATED, EVENT_READY, EVENT_REFRESH, EVENT_RESIZE, EVENT_RESIZED, EVENT_SCROLL, EVENT_SCROLLED, EVENT_SHIFTED, EVENT_SLIDE_KEYDOWN, EVENT_UPDATED, EVENT_VISIBLE, ElementsComponent, EventMap, FADE, Head, KeyboardComponent, LOOP, LTR, LayoutComponent, LazyLoadComponent, LiveComponent, MediaComponent, MoveComponent, Options, PaginationComponent, PaginationData, PaginationItem, Push, RTL, RequestInterval, RequestIntervalInterface, Resolve, ResponsiveOptions, SLIDE, STATUS_CLASSES, ScrollComponent, Shift, ShiftN, SlideComponent, SlidesComponent, Splide, SplideRenderer, State, StateObject, SyncComponent, SyncTarget, TTB, Throttle, ThrottleInstance, TransitionComponent, WheelComponent, Splide as default };

+ 0 - 14
jest.config.js

@@ -1,14 +0,0 @@
-module.exports = {
-  rootDir: './src',
-  transform: {
-    '^.+\\.(ts|tsx)$': 'ts-jest',
-  },
-  testEnvironment: 'jsdom',
-  setupFiles: [
-    './js/test/jest/setup.ts',
-  ],
-  transformIgnorePatterns: [
-    '<rootDir>/node_modules/@babel',
-    '<rootDir>/node_modules/@jest',
-  ],
-};

+ 18 - 0
jest.config.ts

@@ -0,0 +1,18 @@
+import type { JestConfigWithTsJest } from 'ts-jest/dist/types';
+
+
+const config: JestConfigWithTsJest = {
+  transform: {
+    '^.+\\.(ts|tsx)$': 'ts-jest',
+  },
+  testEnvironment: 'jsdom',
+  setupFiles: [
+    './js/test/jest/setup.ts',
+  ],
+  moduleNameMapper: {
+    '^@test/(.*)$': '<rootDir>/test/$1',
+  },
+  rootDir: './src',
+};
+
+export default config;

+ 183 - 37
package-lock.json

@@ -8,7 +8,11 @@
       "name": "@splidejs/splide",
       "version": "4.1.3",
       "license": "MIT",
+      "dependencies": {
+        "@splidejs/utils": "../utils"
+      },
       "devDependencies": {
+        "@rollup/plugin-node-resolve": "^15.0.0",
         "@types/jest": "^29.0.0",
         "@types/node": "^18.7.14",
         "@typescript-eslint/eslint-plugin": "^5.36.1",
@@ -32,6 +36,24 @@
         "uglify-js": "^3.17.0"
       }
     },
+    "../utils": {
+      "name": "@splidejs/utils",
+      "version": "1.0.3",
+      "devDependencies": {
+        "@types/jest": "^29.0.3",
+        "@types/node": "^18.7.20",
+        "@typescript-eslint/eslint-plugin": "^5.38.0",
+        "@typescript-eslint/parser": "^5.38.0",
+        "eslint": "^8.24.0",
+        "jest": "next",
+        "jest-environment-jsdom": "^29.0.3",
+        "ts-jest": "^29.0.2",
+        "ts-node": "^10.9.1",
+        "typedoc": "^0.23.15",
+        "typescript": "^4.6.4",
+        "vite": "^3.1.0"
+      }
+    },
     "node_modules/@ampproject/remapping": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
@@ -1445,6 +1467,44 @@
         "node": ">= 8"
       }
     },
+    "node_modules/@rollup/plugin-node-resolve": {
+      "version": "15.0.0",
+      "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.0.0.tgz",
+      "integrity": "sha512-iwJbzfTzlzDDQcGmkS7EkCKwe2kSkdBrjX87Fy/KrNjr6UNnLpod0t6X66e502LRe5JJCA4FFqrEscWPnZAkig==",
+      "dev": true,
+      "dependencies": {
+        "@rollup/pluginutils": "^4.2.1",
+        "@types/resolve": "1.20.2",
+        "deepmerge": "^4.2.2",
+        "is-builtin-module": "^3.2.0",
+        "is-module": "^1.0.0",
+        "resolve": "^1.22.1"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      },
+      "peerDependencies": {
+        "rollup": "^2.78.0||^3.0.0"
+      },
+      "peerDependenciesMeta": {
+        "rollup": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@rollup/pluginutils": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz",
+      "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==",
+      "dev": true,
+      "dependencies": {
+        "estree-walker": "^2.0.1",
+        "picomatch": "^2.2.2"
+      },
+      "engines": {
+        "node": ">= 8.0.0"
+      }
+    },
     "node_modules/@sinclair/typebox": {
       "version": "0.24.34",
       "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.34.tgz",
@@ -1469,6 +1529,10 @@
         "@sinonjs/commons": "^1.7.0"
       }
     },
+    "node_modules/@splidejs/utils": {
+      "resolved": "../utils",
+      "link": true
+    },
     "node_modules/@tootallnate/once": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
@@ -1641,6 +1705,12 @@
       "integrity": "sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==",
       "dev": true
     },
+    "node_modules/@types/resolve": {
+      "version": "1.20.2",
+      "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
+      "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
+      "dev": true
+    },
     "node_modules/@types/stack-utils": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
@@ -2327,6 +2397,18 @@
       "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
       "dev": true
     },
+    "node_modules/builtin-modules": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz",
+      "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/callsites": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -3667,6 +3749,12 @@
         "node": ">=4.0"
       }
     },
+    "node_modules/estree-walker": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+      "dev": true
+    },
     "node_modules/esutils": {
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
@@ -4289,6 +4377,21 @@
         "node": ">=8"
       }
     },
+    "node_modules/is-builtin-module": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.0.tgz",
+      "integrity": "sha512-phDA4oSGt7vl1n5tJvTWooWWAsXLY+2xCnxNqvKhGEzujg+A43wPlPOyDg3C8XQHN+6k/JTQWJ/j0dQh/qr+Hw==",
+      "dev": true,
+      "dependencies": {
+        "builtin-modules": "^3.3.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/is-core-module": {
       "version": "2.10.0",
       "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz",
@@ -4340,6 +4443,12 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/is-module": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
+      "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==",
+      "dev": true
+    },
     "node_modules/is-number": {
       "version": "7.0.0",
       "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@@ -7293,25 +7402,6 @@
         "rollup": "^1.20.0 || ^2.0.0"
       }
     },
-    "node_modules/rollup-plugin-esbuild/node_modules/@rollup/pluginutils": {
-      "version": "4.2.1",
-      "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz",
-      "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==",
-      "dev": true,
-      "dependencies": {
-        "estree-walker": "^2.0.1",
-        "picomatch": "^2.2.2"
-      },
-      "engines": {
-        "node": ">= 8.0.0"
-      }
-    },
-    "node_modules/rollup-plugin-esbuild/node_modules/estree-walker": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
-      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
-      "dev": true
-    },
     "node_modules/run-parallel": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
@@ -9618,6 +9708,30 @@
         "fastq": "^1.6.0"
       }
     },
+    "@rollup/plugin-node-resolve": {
+      "version": "15.0.0",
+      "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.0.0.tgz",
+      "integrity": "sha512-iwJbzfTzlzDDQcGmkS7EkCKwe2kSkdBrjX87Fy/KrNjr6UNnLpod0t6X66e502LRe5JJCA4FFqrEscWPnZAkig==",
+      "dev": true,
+      "requires": {
+        "@rollup/pluginutils": "^4.2.1",
+        "@types/resolve": "1.20.2",
+        "deepmerge": "^4.2.2",
+        "is-builtin-module": "^3.2.0",
+        "is-module": "^1.0.0",
+        "resolve": "^1.22.1"
+      }
+    },
+    "@rollup/pluginutils": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz",
+      "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==",
+      "dev": true,
+      "requires": {
+        "estree-walker": "^2.0.1",
+        "picomatch": "^2.2.2"
+      }
+    },
     "@sinclair/typebox": {
       "version": "0.24.34",
       "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.34.tgz",
@@ -9642,6 +9756,23 @@
         "@sinonjs/commons": "^1.7.0"
       }
     },
+    "@splidejs/utils": {
+      "version": "file:../utils",
+      "requires": {
+        "@types/jest": "^29.0.3",
+        "@types/node": "^18.7.20",
+        "@typescript-eslint/eslint-plugin": "^5.38.0",
+        "@typescript-eslint/parser": "^5.38.0",
+        "eslint": "^8.24.0",
+        "jest": "next",
+        "jest-environment-jsdom": "^29.0.3",
+        "ts-jest": "^29.0.2",
+        "ts-node": "^10.9.1",
+        "typedoc": "^0.23.15",
+        "typescript": "^4.6.4",
+        "vite": "^3.1.0"
+      }
+    },
     "@tootallnate/once": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
@@ -9811,6 +9942,12 @@
       "integrity": "sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==",
       "dev": true
     },
+    "@types/resolve": {
+      "version": "1.20.2",
+      "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
+      "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
+      "dev": true
+    },
     "@types/stack-utils": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
@@ -10289,6 +10426,12 @@
       "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
       "dev": true
     },
+    "builtin-modules": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz",
+      "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==",
+      "dev": true
+    },
     "callsites": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -11179,6 +11322,12 @@
       "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
       "dev": true
     },
+    "estree-walker": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+      "dev": true
+    },
     "esutils": {
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
@@ -11654,6 +11803,15 @@
         "binary-extensions": "^2.0.0"
       }
     },
+    "is-builtin-module": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.0.tgz",
+      "integrity": "sha512-phDA4oSGt7vl1n5tJvTWooWWAsXLY+2xCnxNqvKhGEzujg+A43wPlPOyDg3C8XQHN+6k/JTQWJ/j0dQh/qr+Hw==",
+      "dev": true,
+      "requires": {
+        "builtin-modules": "^3.3.0"
+      }
+    },
     "is-core-module": {
       "version": "2.10.0",
       "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz",
@@ -11690,6 +11848,12 @@
         "is-extglob": "^2.1.1"
       }
     },
+    "is-module": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
+      "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==",
+      "dev": true
+    },
     "is-number": {
       "version": "7.0.0",
       "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@@ -13851,24 +14015,6 @@
         "es-module-lexer": "^0.9.3",
         "joycon": "^3.0.1",
         "jsonc-parser": "^3.0.0"
-      },
-      "dependencies": {
-        "@rollup/pluginutils": {
-          "version": "4.2.1",
-          "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz",
-          "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==",
-          "dev": true,
-          "requires": {
-            "estree-walker": "^2.0.1",
-            "picomatch": "^2.2.2"
-          }
-        },
-        "estree-walker": {
-          "version": "2.0.2",
-          "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
-          "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
-          "dev": true
-        }
       }
     },
     "run-parallel": {

+ 4 - 0
package.json

@@ -30,6 +30,7 @@
     "url": "https://github.com/Splidejs/splide/issues"
   },
   "devDependencies": {
+    "@rollup/plugin-node-resolve": "^15.0.0",
     "@types/jest": "^29.0.0",
     "@types/node": "^18.7.14",
     "@typescript-eslint/eslint-plugin": "^5.36.1",
@@ -90,5 +91,8 @@
     "./dist/": "./dist/",
     "./src/css/template/": "./src/css/template/",
     "./package.json": "./package.json"
+  },
+  "dependencies": {
+    "@splidejs/utils": "../utils"
   }
 }

+ 2 - 0
scripts/build-module.js

@@ -1,5 +1,6 @@
 import { rollup } from 'rollup';
 import esbuild from 'rollup-plugin-esbuild';
+import resolve from '@rollup/plugin-node-resolve';
 import { BANNER } from './constants/banner.js';
 
 
@@ -9,6 +10,7 @@ function buildModule( type ) {
   return rollup( {
     input: './src/js/index.ts',
     plugins: [
+      resolve(),
       esbuild(),
     ],
   } ).then( bundle => {

+ 2 - 0
scripts/build-script.js

@@ -1,5 +1,6 @@
 import { rollup } from 'rollup';
 import esbuild from 'rollup-plugin-esbuild';
+import resolve from '@rollup/plugin-node-resolve';
 import { minify } from './plugins/minify.js';
 import { BANNER } from './constants/banner.js';
 import fs from 'fs/promises';
@@ -14,6 +15,7 @@ async function buildScript( compress, type = 'default' ) {
   const bundle = await rollup( {
     input: `./src/js/build/${ type }.ts`,
     plugins: [
+      resolve(),
       esbuild( { minify: false } ),
       compress ? minify() : false,
     ],

+ 8 - 3
src/js/components/Arrows/Arrows.ts

@@ -10,7 +10,6 @@ import {
   EVENT_SCROLLED,
   EVENT_UPDATED,
 } from '../../constants/events';
-import { EventInterface } from '../../constructors';
 import { Splide } from '../../core/Splide/Splide';
 import { BaseComponent, Components, Options } from '../../types';
 import {
@@ -28,6 +27,7 @@ import {
   setAttribute,
 } from '../../utils';
 import { PATH, SIZE, XML_NAME_SPACE } from './path';
+import { EventInterface } from '@splidejs/utils';
 
 
 /**
@@ -50,11 +50,16 @@ export interface ArrowsComponent extends BaseComponent {
  * @param Splide     - A Splide instance.
  * @param Components - A collection of components.
  * @param options    - Options.
+ * @param event      - An EventInterface instance.
  *
  * @return An Arrows component object.
  */
-export function Arrows( Splide: Splide, Components: Components, options: Options ): ArrowsComponent {
-  const event = EventInterface( Splide );
+export function Arrows(
+  Splide: Splide,
+  Components: Components,
+  options: Options,
+  event: EventInterface
+): ArrowsComponent {
   const { on, bind, emit } = event;
   const { classes, i18n } = options;
   const { Elements, Controller } = Components;

+ 10 - 3
src/js/components/Autoplay/Autoplay.ts

@@ -8,11 +8,12 @@ import {
   EVENT_REFRESH,
   EVENT_SCROLL,
 } from '../../constants/events';
-import { EventInterface, RequestInterval } from '../../constructors';
 import { Splide } from '../../core/Splide/Splide';
 import { BaseComponent, Components, Options } from '../../types';
 import { getAttribute, setAttribute, style, toggleClass } from '../../utils';
 import { INTERVAL_DATA_ATTRIBUTE } from './constants';
+import { RequestInterval } from '../../constructors';
+import { EventInterface } from '@splidejs/utils';
 
 
 /**
@@ -34,11 +35,17 @@ export interface AutoplayComponent extends BaseComponent {
  * @param Splide     - A Splide instance.
  * @param Components - A collection of components.
  * @param options    - Options.
+ * @param event      - An EventInterface instance.
  *
  * @return An Autoplay component object.
  */
-export function Autoplay( Splide: Splide, Components: Components, options: Options ): AutoplayComponent {
-  const { on, bind, emit } = EventInterface( Splide );
+export function Autoplay(
+  Splide: Splide,
+  Components: Components,
+  options: Options,
+  event: EventInterface
+): AutoplayComponent {
+  const { on, bind, emit } = event;
   const interval = RequestInterval( options.interval, Splide.go.bind( Splide, '>' ), onAnimationFrame );
   const { isPaused } = interval;
   const { Elements, Elements: { root, toggle } } = Components;

+ 8 - 3
src/js/components/Clones/Clones.ts

@@ -1,9 +1,9 @@
 import { EVENT_REFRESH, EVENT_RESIZE, EVENT_UPDATED } from '../../constants/events';
 import { LOOP } from '../../constants/types';
-import { EventInterface } from '../../constructors';
 import { Splide } from '../../core/Splide/Splide';
 import { BaseComponent, Components, Options } from '../../types';
 import { addClass, append, before, ceil, empty, isUndefined, pad, push, rect, remove } from '../../utils';
+import { EventInterface } from '@splidejs/utils';
 
 
 /**
@@ -29,11 +29,16 @@ export const MULTIPLIER = 2;
  * @param Splide     - A Splide instance.
  * @param Components - A collection of components.
  * @param options    - Options.
+ * @param event      - An EventInterface instance.
  *
  * @return A Clones component object.
  */
-export function Clones( Splide: Splide, Components: Components, options: Options ): ClonesComponent {
-  const event = EventInterface( Splide );
+export function Clones(
+  Splide: Splide,
+  Components: Components,
+  options: Options,
+  event: EventInterface
+): ClonesComponent {
   const { on } = event;
   const { Elements, Slides } = Components;
   const { resolve } = Components.Direction;

+ 9 - 3
src/js/components/Controller/Controller.ts

@@ -1,10 +1,10 @@
 import { EVENT_END_INDEX_CHANGED, EVENT_REFRESH, EVENT_RESIZED, EVENT_UPDATED } from '../../constants/events';
 import { MOVING, SCROLLING } from '../../constants/states';
 import { LOOP, SLIDE } from '../../constants/types';
-import { EventInterface } from '../../constructors';
 import { Splide } from '../../core/Splide/Splide';
 import { AnyFunction, BaseComponent, Components, Options } from '../../types';
 import { apply, approximatelyEqual, between, clamp, floor, isString, isUndefined, min } from '../../utils';
+import { EventInterface } from '@splidejs/utils';
 
 
 /**
@@ -38,11 +38,17 @@ export interface ControllerComponent extends BaseComponent {
  * @param Splide     - A Splide instance.
  * @param Components - A collection of components.
  * @param options    - Options.
+ * @param event      - An EventInterface instance.
  *
  * @return A Controller component object.
  */
-export function Controller( Splide: Splide, Components: Components, options: Options ): ControllerComponent {
-  const { on, emit } = EventInterface( Splide );
+export function Controller(
+  Splide: Splide,
+  Components: Components,
+  options: Options,
+  event: EventInterface
+): ControllerComponent {
+  const { on, emit } = event;
   const { Move } = Components;
   const { getPosition, getLimit, toPosition } = Move;
   const { isEnough, getLength } = Components.Slides;

+ 9 - 3
src/js/components/Cover/Cover.ts

@@ -1,9 +1,9 @@
 import { EVENT_LAZYLOAD_LOADED, EVENT_MOUNTED, EVENT_REFRESH, EVENT_UPDATED } from '../../constants/events';
-import { EventInterface } from '../../constructors';
 import { Splide } from '../../core/Splide/Splide';
 import { BaseComponent, Components, Options } from '../../types';
 import { apply, child, display } from '../../utils';
 import { SlideComponent } from '../Slides/Slide';
+import { EventInterface } from '@splidejs/utils';
 
 
 /**
@@ -22,11 +22,17 @@ export interface CoverComponent extends BaseComponent {
  * @param Splide     - A Splide instance.
  * @param Components - A collection of components.
  * @param options    - Options.
+ * @param event      - An EventInterface instance.
  *
  * @return A Cover component object.
  */
-export function Cover( Splide: Splide, Components: Components, options: Options ): CoverComponent {
-  const { on } = EventInterface( Splide );
+export function Cover(
+  Splide: Splide,
+  Components: Components,
+  options: Options,
+  event: EventInterface
+): CoverComponent {
+  const { on } = event;
 
   /**
    * Called when the component is mounted.

+ 13 - 7
src/js/components/Drag/Drag.ts

@@ -3,11 +3,11 @@ import { EVENT_DRAG, EVENT_DRAGGED, EVENT_DRAGGING, EVENT_MOUNTED, EVENT_UPDATED
 import { SCROLL_LISTENER_OPTIONS } from '../../constants/listener-options';
 import { DRAGGING, IDLE, MOVING, SCROLLING } from '../../constants/states';
 import { FADE, LOOP, SLIDE } from '../../constants/types';
-import { EventInterface } from '../../constructors';
 import { Splide } from '../../core/Splide/Splide';
 import { BaseComponent, Components, Options } from '../../types';
 import { abs, isObject, matches, min, noop, prevent, sign, timeOf } from '../../utils';
 import { FRICTION, LOG_INTERVAL, POINTER_DOWN_EVENTS, POINTER_MOVE_EVENTS, POINTER_UP_EVENTS } from './constants';
+import { EventBinder, EventInterface } from '@splidejs/utils';
 
 
 /**
@@ -28,11 +28,18 @@ export interface DragComponent extends BaseComponent {
  * @param Splide     - A Splide instance.
  * @param Components - A collection of components.
  * @param options    - Options.
+ * @param event      - An EventInterface instance.
  *
  * @return A Drag component object.
  */
-export function Drag( Splide: Splide, Components: Components, options: Options ): DragComponent {
-  const { on, emit, bind, unbind } = EventInterface( Splide );
+export function Drag(
+  Splide: Splide,
+  Components: Components,
+  options: Options,
+  event: EventInterface
+): DragComponent {
+  const { on, emit, bind } = event;
+  const binder = event.create();
   const { state } = Splide;
   const { Move, Scroll, Controller, Elements: { track }, Media: { reduce } } = Components;
   const { resolve, orient } = Components.Direction;
@@ -125,8 +132,8 @@ export function Drag( Splide: Splide, Components: Components, options: Options )
           dragging      = state.is( [ MOVING, SCROLLING ] );
           prevBaseEvent = null;
 
-          bind( target, POINTER_MOVE_EVENTS, onPointerMove, SCROLL_LISTENER_OPTIONS );
-          bind( target, POINTER_UP_EVENTS, onPointerUp, SCROLL_LISTENER_OPTIONS );
+          binder.bind( target, POINTER_MOVE_EVENTS, onPointerMove, SCROLL_LISTENER_OPTIONS );
+          binder.bind( target, POINTER_UP_EVENTS, onPointerUp, SCROLL_LISTENER_OPTIONS );
           Move.cancel();
           Scroll.cancel();
           save( e );
@@ -188,8 +195,7 @@ export function Drag( Splide: Splide, Components: Components, options: Options )
       prevent( e );
     }
 
-    unbind( target, POINTER_MOVE_EVENTS, onPointerMove );
-    unbind( target, POINTER_UP_EVENTS, onPointerUp );
+    binder.destroy();
     dragging = false;
   }
 

+ 9 - 3
src/js/components/Elements/Elements.ts

@@ -16,7 +16,6 @@ import {
 } from '../../constants/classes';
 import { EVENT_REFRESH, EVENT_UPDATED } from '../../constants/events';
 import { PROJECT_CODE } from '../../constants/project';
-import { EventInterface } from '../../constructors';
 import { Splide } from '../../core/Splide/Splide';
 import { BaseComponent, Components, Options } from '../../types';
 import {
@@ -38,6 +37,7 @@ import {
 } from '../../utils';
 import { closest } from '../../utils/dom/closest/closest';
 import { POINTER_DOWN_EVENTS } from '../Drag/constants';
+import { EventInterface } from '@splidejs/utils';
 
 
 /**
@@ -74,11 +74,17 @@ export interface ElementsComponent extends BaseComponent, ElementCollection {
  * @param Splide     - A Splide instance.
  * @param Components - A collection of components.
  * @param options    - Options.
+ * @param event      - An EventInterface instance.
  *
  * @return An Elements component object.
  */
-export function Elements( Splide: Splide, Components: Components, options: Options ): ElementsComponent {
-  const { on, bind } = EventInterface( Splide );
+export function Elements(
+  Splide: Splide,
+  Components: Components,
+  options: Options,
+  event: EventInterface
+): ElementsComponent {
+  const { on, bind } = event;
   const { root } = Splide;
   const { i18n } = options;
   const elements: ElementCollection = {} as ElementCollection;

+ 9 - 10
src/js/components/Keyboard/Keyboard.ts

@@ -1,10 +1,10 @@
 import { ARROW_LEFT, ARROW_RIGHT } from '../../constants/arrows';
 import { EVENT_MOVE, EVENT_UPDATED } from '../../constants/events';
-import { EventInterface } from '../../constructors';
 import { Splide } from '../../core/Splide/Splide';
 import { BaseComponent, Components, Options } from '../../types';
 import { nextTick } from '../../utils';
 import { normalizeKey } from '../../utils/dom/normalizeKey/normalizeKey';
+import { EventInterface } from '@splidejs/utils';
 
 
 /**
@@ -31,11 +31,17 @@ const KEYBOARD_EVENT = 'keydown';
  * @param Splide     - A Splide instance.
  * @param Components - A collection of components.
  * @param options    - Options.
+ * @param event      - An EventInterface instance.
  *
  * @return A Keyboard component object.
  */
-export function Keyboard( Splide: Splide, Components: Components, options: Options ): KeyboardComponent {
-  const { on, bind, unbind } = EventInterface( Splide );
+export function Keyboard(
+  Splide: Splide,
+  Components: Components,
+  options: Options,
+  event: EventInterface
+): KeyboardComponent {
+  const { on, bind, destroy } = event;
   const { root } = Splide;
   const { resolve } = Components.Direction;
 
@@ -71,13 +77,6 @@ export function Keyboard( Splide: Splide, Components: Components, options: Optio
     }
   }
 
-  /**
-   * Destroys the component.
-   */
-  function destroy(): void {
-    unbind( target, KEYBOARD_EVENT );
-  }
-
   /**
    * Disables the keyboard input.
    *

+ 10 - 3
src/js/components/Layout/Layout.ts

@@ -1,11 +1,12 @@
 import { TTB } from '../../constants/directions';
 import { EVENT_OVERFLOW, EVENT_REFRESH, EVENT_RESIZE, EVENT_RESIZED, EVENT_UPDATED } from '../../constants/events';
-import { EventInterface, Throttle } from '../../constructors';
+import { Throttle } from '../../constructors';
 import { Splide } from '../../core/Splide/Splide';
 import { BaseComponent, Components, Options } from '../../types';
 import { abs, apply, assert, isObject, rect, style, toggleClass, unit } from '../../utils';
 import { FADE } from '../../constants/types';
 import { CLASS_OVERFLOW } from '../../constants/classes';
+import { EventInterface } from '@splidejs/utils';
 
 
 /**
@@ -33,11 +34,17 @@ export interface LayoutComponent extends BaseComponent {
  * @param Splide     - A Splide instance.
  * @param Components - A collection of components.
  * @param options    - Options.
+ * @param event      - An EventInterface instance.
  *
  * @return An Layout component object.
  */
-export function Layout( Splide: Splide, Components: Components, options: Options ): LayoutComponent {
-  const { on, bind, emit } = EventInterface( Splide );
+export function Layout(
+  Splide: Splide,
+  Components: Components,
+  options: Options,
+  event: EventInterface
+): LayoutComponent {
+  const { on, bind, emit } = event;
   const { Slides } = Components;
   const { resolve } = Components.Direction;
   const { root, track, list } = Components.Elements;

+ 9 - 3
src/js/components/LazyLoad/LazyLoad.ts

@@ -6,7 +6,6 @@ import {
   EVENT_RESIZE,
   EVENT_SCROLLED,
 } from '../../constants/events';
-import { EventInterface } from '../../constructors';
 import { Splide } from '../../core/Splide/Splide';
 import { BaseComponent, Components, Options } from '../../types';
 import {
@@ -25,6 +24,7 @@ import {
 } from '../../utils';
 import { SlideComponent } from '../Slides/Slide';
 import { IMAGE_SELECTOR, SRC_DATA_ATTRIBUTE, SRCSET_DATA_ATTRIBUTE } from './constants';
+import { EventInterface } from '@splidejs/utils';
 
 
 /**
@@ -53,11 +53,17 @@ type LazyLoadEntry = [ HTMLImageElement, SlideComponent, HTMLSpanElement ];
  * @param Splide     - A Splide instance.
  * @param Components - A collection of components.
  * @param options    - Options.
+ * @param event      - An EventInterface instance.
  *
  * @return An LazyLoad component object.
  */
-export function LazyLoad( Splide: Splide, Components: Components, options: Options ): LazyLoadComponent {
-  const { on, off, bind, emit } = EventInterface( Splide );
+export function LazyLoad(
+  Splide: Splide,
+  Components: Components,
+  options: Options,
+  event: EventInterface
+): LazyLoadComponent {
+  const { on, off, bind, emit } = event;
   const isSequential = options.lazyLoad === 'sequential';
   const events       = [ EVENT_MOVED, EVENT_SCROLLED ];
 

+ 10 - 3
src/js/components/Live/Live.ts

@@ -1,10 +1,11 @@
 import { ARIA_ATOMIC, ARIA_BUSY, ARIA_LIVE } from '../../constants/attributes';
 import { CLASS_SR } from '../../constants/classes';
 import { EVENT_AUTOPLAY_PAUSE, EVENT_AUTOPLAY_PLAY, EVENT_MOVED, EVENT_SCROLLED } from '../../constants/events';
-import { EventInterface, RequestInterval } from '../../constructors';
+import { RequestInterval } from '../../constructors';
 import { Splide } from '../../core/Splide/Splide';
 import { BaseComponent, Components, Options } from '../../types';
 import { append, apply, create, remove, removeAttribute, setAttribute } from '../../utils';
+import { EventInterface } from '@splidejs/utils';
 
 
 /**
@@ -31,11 +32,17 @@ const SR_REMOVAL_DELAY = 90;
  * @param Splide     - A Splide instance.
  * @param Components - A collection of components.
  * @param options    - Options.
+ * @param event      - An EventInterface instance.
  *
  * @return A Live component object.
  */
-export function Live( Splide: Splide, Components: Components, options: Options ): LiveComponent {
-  const { on } = EventInterface( Splide );
+export function Live(
+  Splide: Splide,
+  Components: Components,
+  options: Options,
+  event: EventInterface
+): LiveComponent {
+  const { on } = event;
   const { track } = Components.Elements;
 
   /**

+ 1 - 1
src/js/components/Media/Media.ts

@@ -1,6 +1,6 @@
 import { MEDIA_PREFERS_REDUCED_MOTION } from '../../constants/media';
 import { CREATED, DESTROYED } from '../../constants/states';
-import { EventBinder } from '../../constructors';
+import { EventBinder } from '@splidejs/utils';
 import { Splide } from '../../core/Splide/Splide';
 import { BaseComponent, Components, Options } from '../../types';
 import { merge, omit, ownKeys } from '../../utils';

+ 9 - 3
src/js/components/Move/Move.ts

@@ -9,10 +9,10 @@ import {
 } from '../../constants/events';
 import { IDLE, MOVING } from '../../constants/states';
 import { FADE, LOOP, SLIDE } from '../../constants/types';
-import { EventInterface } from '../../constructors';
 import { Splide } from '../../core/Splide/Splide';
 import { AnyFunction, BaseComponent, Components, Options, TransitionComponent } from '../../types';
 import { abs, ceil, clamp, isUndefined, rect, style } from '../../utils';
+import { EventInterface } from '@splidejs/utils';
 
 
 /**
@@ -44,11 +44,17 @@ export interface MoveComponent extends BaseComponent {
  * @param Splide     - A Splide instance.
  * @param Components - A collection of components.
  * @param options    - Options.
+ * @param event      - An EventInterface instance.
  *
  * @return A Move component object.
  */
-export function Move( Splide: Splide, Components: Components, options: Options ): MoveComponent {
-  const { on, emit } = EventInterface( Splide );
+export function Move(
+  Splide: Splide,
+  Components: Components,
+  options: Options,
+  event: EventInterface
+): MoveComponent {
+  const { on, emit } = event;
   const { set } = Splide.state;
   const { slideSize, getPadding, totalSize, listSize, sliderSize } = Components.Layout;
   const { resolve, orient } = Components.Direction;

+ 8 - 4
src/js/components/Pagination/Pagination.ts

@@ -19,7 +19,6 @@ import {
   EVENT_SCROLLED,
   EVENT_UPDATED,
 } from '../../constants/events';
-import { EventInterface } from '../../constructors';
 import { Splide } from '../../core/Splide/Splide';
 import { BaseComponent, Components, Options } from '../../types';
 import {
@@ -39,6 +38,7 @@ import {
   slice,
 } from '../../utils';
 import { normalizeKey } from '../../utils/dom/normalizeKey/normalizeKey';
+import { EventInterface } from '@splidejs/utils';
 
 
 /**
@@ -76,17 +76,21 @@ export interface PaginationItem {
 /**
  * The component for the pagination UI (a slide picker).
  *
- * @link https://www.w3.org/TR/2021/NOTE-wai-aria-practices-1.2-20211129/#grouped-carousel-elements
  * @since 3.0.0
  *
  * @param Splide     - A Splide instance.
  * @param Components - A collection of components.
  * @param options    - Options.
+ * @param event      - An EventInterface instance.
  *
  * @return A Pagination component object.
  */
-export function Pagination( Splide: Splide, Components: Components, options: Options ): PaginationComponent {
-  const event = EventInterface( Splide );
+export function Pagination(
+  Splide: Splide,
+  Components: Components,
+  options: Options,
+  event: EventInterface
+): PaginationComponent {
   const { on, emit, bind } = event;
   const { Slides, Elements, Controller } = Components;
   const { hasFocus, getIndex, go } = Controller;

+ 10 - 3
src/js/components/Scroll/Scroll.ts

@@ -1,11 +1,12 @@
 import { EVENT_MOVE, EVENT_REFRESH, EVENT_SCROLL, EVENT_SCROLLED, EVENT_UPDATED } from '../../constants/events';
 import { IDLE, SCROLLING } from '../../constants/states';
 import { SLIDE } from '../../constants/types';
-import { EventInterface, RequestInterval, RequestIntervalInterface } from '../../constructors';
+import { RequestInterval, RequestIntervalInterface } from '../../constructors';
 import { Splide } from '../../core/Splide/Splide';
 import { AnyFunction, BaseComponent, Components, Options } from '../../types';
 import { abs, apply, approximatelyEqual, floor, max, sign } from '../../utils';
 import { BASE_VELOCITY, BOUNCE_DIFF_THRESHOLD, BOUNCE_DURATION, FRICTION_FACTOR, MIN_DURATION } from './constants';
+import { EventInterface } from '@splidejs/utils';
 
 
 /**
@@ -26,11 +27,17 @@ export interface ScrollComponent extends BaseComponent {
  * @param Splide     - A Splide instance.
  * @param Components - A collection of components.
  * @param options    - Options.
+ * @param event      - An EventInterface instance.
  *
  * @return A Scroll component object.
  */
-export function Scroll( Splide: Splide, Components: Components, options: Options ): ScrollComponent {
-  const { on, emit } = EventInterface( Splide );
+export function Scroll(
+  Splide: Splide,
+  Components: Components,
+  options: Options,
+  event: EventInterface
+): ScrollComponent {
+  const { on, emit } = event;
   const { state: { set } } = Splide;
   const { Move } = Components;
   const { getPosition, getLimit, exceededLimit, translate } = Move;

+ 1 - 2
src/js/components/Slides/Slide.ts

@@ -31,7 +31,6 @@ import {
 } from '../../constants/events';
 import { MOVING, SCROLLING } from '../../constants/states';
 import { FADE, LOOP } from '../../constants/types';
-import { EventInterface } from '../../constructors';
 import { Splide } from '../../core/Splide/Splide';
 import { BaseComponent } from '../../types';
 import {
@@ -85,7 +84,7 @@ export interface  SlideComponent extends BaseComponent {
  * @return A Slide subcomponent.
  */
 export function Slide( Splide: Splide, index: number, slideIndex: number, slide: HTMLElement ): SlideComponent {
-  const event = EventInterface( Splide );
+  const event = Splide.event.create();
   const { on, emit, bind } = event;
   const { Components, root, options } = Splide;
   const { isNavigation, updateOnMove, i18n, pagination, slideFocus } = options;

+ 9 - 3
src/js/components/Slides/Slides.ts

@@ -1,5 +1,4 @@
 import { EVENT_REFRESH, EVENT_RESIZE } from '../../constants/events';
-import { EventInterface } from '../../constructors';
 import { Splide } from '../../core/Splide/Splide';
 import { AnyFunction, BaseComponent, Components, Options } from '../../types';
 import {
@@ -21,6 +20,7 @@ import {
   toArray,
 } from '../../utils';
 import { Slide, SlideComponent } from './Slide';
+import { EventInterface } from '@splidejs/utils';
 
 
 /**
@@ -72,11 +72,17 @@ export type SlideMatcher = number | number[] | string | SlidesPredicate;
  * @param Splide     - A Splide instance.
  * @param Components - A collection of components.
  * @param options    - Options.
+ * @param event      - An EventInterface instance.
  *
  * @return An Slides component object.
  */
-export function Slides( Splide: Splide, Components: Components, options: Options ): SlidesComponent {
-  const { on, emit, bind } = EventInterface( Splide );
+export function Slides(
+  Splide: Splide,
+  Components: Components,
+  options: Options,
+  event: EventInterface
+): SlidesComponent {
+  const { on, emit, bind } = event;
   const { slides, list } = Components.Elements;
 
   /**

+ 19 - 9
src/js/components/Sync/Sync.ts

@@ -9,12 +9,12 @@ import {
   EVENT_UPDATED,
 } from '../../constants/events';
 import { LOOP } from '../../constants/types';
-import { EventInterface, EventInterfaceObject } from '../../constructors';
 import { Splide } from '../../core/Splide/Splide';
 import { BaseComponent, Components, Options } from '../../types';
 import { apply, empty, includes, isUndefined, prevent, setAttribute } from '../../utils';
 import { normalizeKey } from '../../utils/dom/normalizeKey/normalizeKey';
 import { SlideComponent } from '../Slides/Slide';
+import { EventInterface } from '@splidejs/utils';
 
 
 /**
@@ -41,16 +41,22 @@ const TRIGGER_KEYS = [ ' ', 'Enter' ];
  * @param Splide     - A Splide instance.
  * @param Components - A collection of components.
  * @param options    - Options.
+ * @param event      - An EventInterface object.
  *
  * @return A Sync component object.
  */
-export function Sync( Splide: Splide, Components: Components, options: Options ): SyncComponent {
+export function Sync(
+  Splide: Splide,
+  Components: Components,
+  options: Options,
+  event: EventInterface
+): SyncComponent {
   const { isNavigation, slideFocus } = options;
 
   /**
    * Stores event objects.
    */
-  const events: EventInterfaceObject[] = [];
+  const events: EventInterface[] = []; // todo
 
   /**
    * Called when the component is mounted.
@@ -93,7 +99,7 @@ export function Sync( Splide: Splide, Components: Components, options: Options )
    * @param target - A target splide instance.
    */
   function sync( splide: Splide, target: Splide ): void {
-    const event = EventInterface( splide );
+    const event = splide.event.create();
 
     event.on( EVENT_MOVE, ( index, prev, dest ) => {
       target.go( target.is( LOOP ) ? dest : index );
@@ -107,22 +113,26 @@ export function Sync( Splide: Splide, Components: Components, options: Options )
    * Note that the direction of `menu` is implicitly `vertical` as default.
    */
   function navigate(): void {
-    const event = EventInterface( Splide );
-    const { on } = event;
+    const ev = event.create();
+    const { on } = ev;
 
     on( EVENT_CLICK, onClick );
     on( EVENT_SLIDE_KEYDOWN, onKeydown );
     on( [ EVENT_MOUNTED, EVENT_UPDATED ], update );
 
-    events.push( event );
-    event.emit( EVENT_NAVIGATION_MOUNTED, Splide.splides );
+    events.push( ev );
+    ev.emit( EVENT_NAVIGATION_MOUNTED, Splide.splides );
   }
 
   /**
    * Update attributes.
    */
   function update(): void {
-    setAttribute( Components.Elements.list, ARIA_ORIENTATION, options.direction === TTB ? 'vertical' : '' );
+    setAttribute(
+      Components.Elements.list,
+      ARIA_ORIENTATION,
+      options.direction === TTB ? 'vertical' : ''
+    );
   }
 
   /**

+ 9 - 5
src/js/components/Wheel/Wheel.ts

@@ -1,9 +1,9 @@
 import { SCROLL_LISTENER_OPTIONS } from '../../constants/listener-options';
 import { MOVING } from '../../constants/states';
-import { EventInterface } from '../../constructors';
 import { Splide } from '../../core/Splide/Splide';
 import { BaseComponent, Components, Options } from '../../types';
 import { abs, prevent, timeOf } from '../../utils';
+import { EventInterface } from '@splidejs/utils';
 
 
 /**
@@ -22,12 +22,16 @@ export interface WheelComponent extends BaseComponent {
  * @param Splide     - A Splide instance.
  * @param Components - A collection of components.
  * @param options    - Options.
+ * @param event      - An EventInterface instance.
  *
  * @return A Wheel component object.
  */
-export function Wheel( Splide: Splide, Components: Components, options: Options ): WheelComponent {
-  const { bind } = EventInterface( Splide );
-
+export function Wheel(
+  Splide: Splide,
+  Components: Components,
+  options: Options,
+  event: EventInterface
+): WheelComponent {
   /**
    * Holds the last time when the wheel moves the slider.
    */
@@ -38,7 +42,7 @@ export function Wheel( Splide: Splide, Components: Components, options: Options
    */
   function mount(): void {
     if ( options.wheel ) {
-      bind( Components.Elements.track, 'wheel', onWheel, SCROLL_LISTENER_OPTIONS );
+      event.bind( Components.Elements.track, 'wheel', onWheel, SCROLL_LISTENER_OPTIONS );
     }
   }
 

+ 0 - 79
src/js/constructors/EventInterface/EventInterface.ts

@@ -1,79 +0,0 @@
-import { EVENT_DESTROY } from '../../constants/events';
-import { Splide } from '../../core/Splide/Splide';
-import { AnyFunction, EventMap } from '../../types';
-import { apply, assign, isArray, slice, toArray } from '../../utils';
-import { EventBinder, EventBinderObject } from '../EventBinder/EventBinder';
-
-
-/**
- * The interface for the EventInterface object.
- *
- * @since 3.0.0
- */
-export interface EventInterfaceObject extends EventBinderObject {
-  on<K extends keyof EventMap>( event: K, callback: EventMap[ K ] ): void;
-  on( events: string | string[], callback: AnyFunction ): void;
-  off<K extends keyof EventMap>( events: K | K[] | string | string[] ): void;
-  emit<K extends keyof EventMap>( event: K, ...args: Parameters<EventMap[ K ]> ): void
-  emit( event: string, ...args: any[] ): void;
-
-  /** @internal */
-  bus: DocumentFragment;
-}
-
-/**
- * The constructor function that provides interface for internal and native events.
- *
- * @since 3.0.0
- * @constructor
- *
- * @param Splide - A Splide instance.
- *
- * @return A collection of interface functions.
- */
-export function EventInterface( Splide?: Splide ): EventInterfaceObject {
-  /**
-   * The document fragment for internal events.
-   * Provide the Splide instance to share the bus.
-   */
-  const bus = Splide ? Splide.event.bus : document.createDocumentFragment();
-
-  /**
-   * An event binder object.
-   */
-  const binder = EventBinder();
-
-  /**
-   * Listens to an internal event or events.
-   *
-   * @param events   - An event name or names separated by spaces. Use a dot(.) to add a namespace.
-   * @param callback - A callback function to register.
-   */
-  function on( events: string | string[], callback: AnyFunction ): void {
-    binder.bind( bus, toArray( events ).join( ' ' ), e => {
-      callback.apply( callback, isArray( e.detail ) ? e.detail : [] );
-    } );
-  }
-
-  /**
-   * Triggers callback functions.
-   * This accepts additional arguments and passes them to callbacks.
-   *
-   * @param event - An event name.
-   */
-  function emit( event: string ): void {
-    // eslint-disable-next-line prefer-rest-params, prefer-spread
-    binder.dispatch( bus, event, slice( arguments, 1 ) );
-  }
-
-  if ( Splide ) {
-    Splide.event.on( EVENT_DESTROY, binder.destroy );
-  }
-
-  return assign( binder, {
-    bus,
-    on,
-    off: apply( binder.unbind, bus ),
-    emit,
-  } );
-}

+ 0 - 127
src/js/constructors/EventInterface/test/general.test.ts

@@ -1,127 +0,0 @@
-import { fire, init } from '../../../test';
-import { EventInterface } from '../EventInterface';
-
-
-describe( 'EventInterface', () => {
-  const splide = init( { speed: 0 } );
-
-  test( 'can provide `on` to listen to internal events and lock listeners.', () => {
-    const { on } = EventInterface( splide );
-    const callback1 = jest.fn();
-    const callback2 = jest.fn();
-
-    on( 'mounted', callback1 );
-    on( 'moved', callback2 );
-
-    splide.emit( 'mounted' );
-    splide.emit( 'moved' );
-
-    expect( callback1 ).toHaveBeenCalledTimes( 1 );
-    expect( callback2 ).toHaveBeenCalledTimes( 1 );
-
-    // Handlers should not be removed by `off()`.
-    splide.off( 'mounted' );
-    splide.off( 'moved' );
-
-    splide.emit( 'mounted' );
-    splide.emit( 'moved' );
-
-    expect( callback1 ).toHaveBeenCalledTimes( 2 );
-    expect( callback2 ).toHaveBeenCalledTimes( 2 );
-  } );
-
-  test( 'can receive arguments passed by `emit`.', () => {
-    const { on } = EventInterface( splide );
-    const callback1 = jest.fn();
-    const callback2 = jest.fn();
-
-    const array  = [ 1, 2 ];
-    const object = { a: 1, b: 2 };
-
-    on( 'myEvent1', callback1 );
-    on( 'myEvent2', callback2 );
-
-    splide.emit( 'myEvent1', 1, 2, 3 );
-    splide.emit( 'myEvent2', array, object, Infinity );
-
-    expect( callback1 ).toHaveBeenCalledWith( 1, 2, 3 );
-    expect( callback2 ).toHaveBeenCalledWith( array, object, Infinity );
-  } );
-
-  test( 'can provide `off` to remove locked listeners.', () => {
-    const { on, off } = EventInterface( splide );
-    const callback1 = jest.fn();
-    const callback2 = jest.fn();
-
-    on( 'mounted', callback1 );
-    on( 'moved', callback2 );
-
-    splide.emit( 'mounted' );
-    splide.emit( 'moved' );
-
-    expect( callback1 ).toHaveBeenCalledTimes( 1 );
-    expect( callback2 ).toHaveBeenCalledTimes( 1 );
-
-    // `off()` can remove handlers registered by `on()`.
-    off( 'mounted' );
-    off( 'moved' );
-
-    splide.emit( 'mounted' );
-    splide.emit( 'moved' );
-
-    expect( callback1 ).toHaveBeenCalledTimes( 1 );
-    expect( callback2 ).toHaveBeenCalledTimes( 1 );
-  } );
-
-  test( 'can provide `bind` to listen to native events.', () => {
-    const { bind } = EventInterface( splide );
-    const div = document.createElement( 'div' );
-    const callback1 = jest.fn();
-    const callback2 = jest.fn();
-
-    bind( window, 'resize', callback1 );
-    bind( div, 'click', callback2 );
-
-    fire( window, 'resize' );
-    fire( div, 'click' );
-
-    expect( callback1 ).toHaveBeenCalledTimes( 1 );
-    expect( callback2 ).toHaveBeenCalledTimes( 1 );
-  } );
-
-  test( 'can provide `unbind` to remove listeners.', () => {
-    const { bind, unbind } = EventInterface( splide );
-    const div = document.createElement( 'div' );
-    const callback1 = jest.fn();
-    const callback2 = jest.fn();
-
-    bind( window, 'resize', callback1 );
-    bind( div, 'click', callback2 );
-
-    unbind( window, 'resize' );
-    unbind( div, 'click' );
-
-    fire( window, 'resize' );
-    fire( div, 'click' );
-
-    expect( callback1 ).not.toHaveBeenCalled();
-    expect( callback2 ).not.toHaveBeenCalled();
-  } );
-
-  test( 'can remove all listeners when the splide is destroyed.', () => {
-    const { on, bind } = EventInterface( splide );
-    const callback1 = jest.fn();
-    const callback2 = jest.fn();
-
-    bind( window, 'resize', callback1 );
-    on( 'moved', callback2 );
-
-    splide.destroy();
-
-    fire( window, 'resize' );
-    splide.emit( 'moved' );
-
-    expect( callback1 ).not.toHaveBeenCalled();
-    expect( callback2 ).not.toHaveBeenCalled();
-  } );
-} );

+ 2 - 2
src/js/constructors/index.ts

@@ -1,5 +1,5 @@
-export * from './EventBinder/EventBinder';
-export * from './EventInterface/EventInterface';
+// export * from './EventBinder/EventBinder';
+// export * from './EventInterface/EventInterface';
 export * from './RequestInterval/RequestInterval';
 export * from './State/State';
 export * from './Throttle/Throttle';

+ 15 - 13
src/js/core/Splide/Splide.ts

@@ -6,11 +6,12 @@ import { EVENT_DESTROY, EVENT_MOUNTED, EVENT_READY, EVENT_REFRESH } from '../../
 import { DATA_ATTRIBUTE } from '../../constants/project';
 import { CREATED, DESTROYED, IDLE, STATES } from '../../constants/states';
 import { FADE } from '../../constants/types';
-import { EventInterface, EventInterfaceObject, State, StateObject } from '../../constructors';
+import { State, StateObject } from '../../constructors';
 import { Fade, Slide } from '../../transitions';
 import { AnyFunction, ComponentConstructor, Components, EventMap, Options, SyncTarget } from '../../types';
 import { addClass, assert, assign, empty, forOwn, getAttribute, isString, merge, query, slice } from '../../utils';
 import { ARIA_LABEL, ARIA_LABELLEDBY } from '../../constants/attributes';
+import { EventInterface } from '@splidejs/utils';
 
 
 /**
@@ -37,7 +38,7 @@ export class Splide {
   /**
    * The EventBusObject object.
    */
-  readonly event: EventInterfaceObject = EventInterface();
+  readonly event = EventInterface();
 
   /**
    * The collection of all component objects.
@@ -121,7 +122,7 @@ export class Splide {
     const Constructors = assign( {}, ComponentConstructors, this._E, { Transition: this._T } );
 
     forOwn( Constructors, ( Component, key ) => {
-      const component = Component( this, Components, this._o );
+      const component = Component( this, Components, this._o, this.event.create() );
       Components[ key ] = component;
       component.setup && component.setup();
     } );
@@ -146,8 +147,8 @@ export class Splide {
    *
    * @example
    * ```ts
-   * var primary   = new Splide();
-   * var secondary = new Splide();
+   * const primary   = new Splide();
+   * const secondary = new Splide();
    *
    * primary.sync( secondary );
    * primary.mount();
@@ -187,7 +188,7 @@ export class Splide {
    *
    * @example
    * ```ts
-   * var splide = new Splide();
+   * const splide = new Splide();
    *
    * // Goes to the slide 1:
    * splide.go( 1 );
@@ -216,7 +217,7 @@ export class Splide {
    *
    * @example
    * ```ts
-   * var splide = new Splide();
+   * const splide = new Splide();
    *
    * // Listens to a single event:
    * splide.on( 'move', function() {} );
@@ -246,7 +247,7 @@ export class Splide {
    *
    * @example
    * ```ts
-   * var splide = new Splide();
+   * const splide = new Splide();
    *
    * // Removes all handlers assigned to "move":
    * splide.off( 'move' );
@@ -255,12 +256,13 @@ export class Splide {
    * splide.off( 'move.myNamespace' );
    * ```
    *
-   * @param events - An event name or names separated by spaces. Use a dot(.) to append a namespace.
+   * @param events   - An event name or names separated by spaces. Use a dot(.) to append a namespace.
+   * @param callback - A callback function to remove.
    *
    * @return `this`
    */
-  off<K extends keyof EventMap>( events: K | K[] | string | string[] ): this {
-    this.event.off( events );
+  off<K extends keyof EventMap>( events: K | K[] | string | string[], callback: AnyFunction ): this {
+    this.event.off( events, callback );
     return this;
   }
 
@@ -285,7 +287,7 @@ export class Splide {
    *
    * @example
    * ```ts
-   * var splide = new Splide();
+   * const splide = new Splide();
    * splide.mount();
    *
    * // Adds the slide by the HTML:
@@ -349,7 +351,7 @@ export class Splide {
 
     if ( state.is( CREATED ) ) {
       // Postpones destruction requested before the slider becomes ready.
-      EventInterface( this ).on( EVENT_READY, this.destroy.bind( this, completely ) );
+      this.on( EVENT_READY, this.destroy.bind( this, completely ) );
     } else {
       forOwn( this._C, component => {
         component.destroy && component.destroy( completely );

+ 1 - 2
src/js/renderer/SplideRenderer/SplideRenderer.ts

@@ -5,7 +5,6 @@ import { DEFAULTS } from '../../constants/defaults';
 import { TTB } from '../../constants/directions';
 import { EVENT_MOUNTED } from '../../constants/events';
 import { LOOP, SLIDE } from '../../constants/types';
-import { EventInterface } from '../../constructors';
 import { Splide } from '../../core/Splide/Splide';
 import { Options } from '../../types';
 import {
@@ -42,7 +41,7 @@ export class SplideRenderer {
    * @param splide - A Splide instance.
    */
   static clean( splide: Splide ): void {
-    const { on } = EventInterface( splide );
+    const { on } = splide.event;
     const { root } = splide;
     const clones = queryAll( root, `.${ CLASS_CLONE }` );
 

+ 9 - 3
src/js/transitions/Fade/Fade.ts

@@ -1,8 +1,8 @@
 import { EVENT_MOUNTED, EVENT_REFRESH } from '../../constants/events';
-import { EventInterface } from '../../constructors';
 import { Splide } from '../../core/Splide/Splide';
 import { Components, Options, TransitionComponent } from '../../types';
 import { nextTick, noop } from '../../utils';
+import { EventInterface } from '@splidejs/utils';
 
 
 /**
@@ -13,17 +13,23 @@ import { nextTick, noop } from '../../utils';
  * @param Splide     - A Splide instance.
  * @param Components - A collection of components.
  * @param options    - Options.
+ * @param event      - An EventInterface instance.
  *
  * @return A Transition component object.
  */
-export function Fade( Splide: Splide, Components: Components, options: Options ): TransitionComponent {
+export function Fade(
+  Splide: Splide,
+  Components: Components,
+  options: Options,
+  event: EventInterface
+): TransitionComponent {
   const { Slides } = Components;
 
   /**
    * Called when the component is mounted.
    */
   function mount(): void {
-    EventInterface( Splide ).on( [ EVENT_MOUNTED, EVENT_REFRESH ], init );
+    event.on( [ EVENT_MOUNTED, EVENT_REFRESH ], init );
   }
 
   /**

+ 9 - 3
src/js/transitions/Slide/Slide.ts

@@ -1,8 +1,8 @@
 import { SLIDE } from '../../constants/types';
-import { EventInterface } from '../../constructors';
 import { Splide } from '../../core/Splide/Splide';
 import { Components, Options, TransitionComponent } from '../../types';
 import { abs, apply, style } from '../../utils';
+import { EventInterface } from '@splidejs/utils';
 
 
 /**
@@ -13,10 +13,16 @@ import { abs, apply, style } from '../../utils';
  * @param Splide     - A Splide instance.
  * @param Components - A collection of components.
  * @param options    - Options.
+ * @param event      - An EventInterface instance.
  *
  * @return A Transition component object.
  */
-export function Slide( Splide: Splide, Components: Components, options: Options ): TransitionComponent {
+export function Slide(
+  Splide: Splide,
+  Components: Components,
+  options: Options,
+  event: EventInterface
+): TransitionComponent {
   const { Move, Controller, Scroll } = Components;
   const { list } = Components.Elements;
   const transition = apply( style, list, 'transition' );
@@ -30,7 +36,7 @@ export function Slide( Splide: Splide, Components: Components, options: Options
    * Called when the component is mounted.
    */
   function mount(): void {
-    EventInterface( Splide ).bind( list, 'transitionend', e => {
+    event.bind( list, 'transitionend', e => {
       if ( e.target === list && endCallback ) {
         cancel();
         endCallback();

+ 3 - 1
src/js/types/general.ts

@@ -1,6 +1,7 @@
 import { Splide } from '../core/Splide/Splide';
 import { Components } from './components';
 import { Options } from './options';
+import { EventInterface } from '@splidejs/utils';
 
 
 /**
@@ -15,7 +16,8 @@ export type AnyFunction = ( ...args: any[] ) => any;
  *
  * @since 3.0.0
  */
-export type ComponentConstructor = ( Splide: Splide, Components: Components, options: Options ) => BaseComponent;
+export type ComponentConstructor = ( Splide: Splide, Components: Components, options: Options, event: EventInterface )
+  => BaseComponent;
 
 /**
  * The interface for any component.

部分文件因文件數量過多而無法顯示