aboutsummaryrefslogtreecommitdiffstats
path: root/src/js/utils/events.js
blob: 287129f1d88a5c97682bdd189db4fab5cf7e6a62 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// ==========================================================================
// Event utils
// ==========================================================================

import is from './is';

// Check for passive event listener support
// https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md
// https://www.youtube.com/watch?v=NPM6172J22g
const supportsPassiveListeners = (() => {
  // Test via a getter in the options object to see if the passive property is accessed
  let supported = false;
  try {
    const options = Object.defineProperty({}, 'passive', {
      get() {
        supported = true;
        return null;
      },
    });
    window.addEventListener('test', null, options);
    window.removeEventListener('test', null, options);
  } catch (e) {
    // Do nothing
  }

  return supported;
})();

// Toggle event listener
export function toggleListener(element, event, callback, toggle = false, passive = true, capture = false) {
  // Bail if no element, event, or callback
  if (!element || !('addEventListener' in element) || is.empty(event) || !is.function(callback)) {
    return;
  }

  // Allow multiple events
  const events = event.split(' ');
  // Build options
  // Default to just the capture boolean for browsers with no passive listener support
  let options = capture;

  // If passive events listeners are supported
  if (supportsPassiveListeners) {
    options = {
      // Whether the listener can be passive (i.e. default never prevented)
      passive,
      // Whether the listener is a capturing listener or not
      capture,
    };
  }

  // If a single node is passed, bind the event listener
  events.forEach((type) => {
    if (this && this.eventListeners && toggle) {
      // Cache event listener
      this.eventListeners.push({ element, type, callback, options });
    }

    element[toggle ? 'addEventListener' : 'removeEventListener'](type, callback, options);
  });
}

// Bind event handler
export function on(element, events = '', callback, passive = true, capture = false) {
  toggleListener.call(this, element, events, callback, true, passive, capture);
}

// Unbind event handler
export function off(element, events = '', callback, passive = true, capture = false) {
  toggleListener.call(this, element, events, callback, false, passive, capture);
}

// Bind once-only event handler
export function once(element, events = '', callback, passive = true, capture = false) {
  const onceCallback = (...args) => {
    off(element, events, onceCallback, passive, capture);
    callback.apply(this, args);
  };

  toggleListener.call(this, element, events, onceCallback, true, passive, capture);
}

// Trigger event
export function triggerEvent(element, type = '', bubbles = false, detail = {}) {
  // Bail if no element
  if (!is.element(element) || is.empty(type)) {
    return;
  }

  // Create and dispatch the event
  const event = new CustomEvent(type, {
    bubbles,
    detail: { ...detail, plyr: this },
  });

  // Dispatch the event
  element.dispatchEvent(event);
}

// Unbind all cached event listeners
export function unbindListeners() {
  if (this && this.eventListeners) {
    this.eventListeners.forEach((item) => {
      const { element, type, callback, options } = item;
      element.removeEventListener(type, callback, options);
    });

    this.eventListeners = [];
  }
}

// Run method when / if player is ready
export function ready() {
  return new Promise((resolve) =>
    this.ready ? setTimeout(resolve, 0) : on.call(this, this.elements.container, 'ready', resolve),
  ).then(() => {});
}