111 lines
3.8 KiB
JavaScript
111 lines
3.8 KiB
JavaScript
/**
|
|
* Add an event listener bound to the context of the superClass.
|
|
*
|
|
* @param {HTMLElement} node Element to attach the event listener to.
|
|
* @param {String} eventName Name of the event.
|
|
* @param {String} cbName Name of the handler.
|
|
*/
|
|
export const EventHelpers = function(superClass) {
|
|
return class extends superClass {
|
|
listen(node, eventName, cbName, options) {
|
|
const boundListener = this.__registerListener(cbName, node, eventName);
|
|
|
|
if (eventName === 'track') {
|
|
this._track(node, boundListener);
|
|
return;
|
|
}
|
|
|
|
node.addEventListener(eventName, boundListener, options);
|
|
}
|
|
|
|
/**
|
|
* Remove an event listener bound to the context of the superClass.
|
|
*
|
|
* @param {HTMLElement} node Element to attach the event listener to.
|
|
* @param {String} eventName Name of the event.
|
|
* @param {String} cbName Name of the handler.
|
|
*/
|
|
unlisten(node, eventName, cbName, options) {
|
|
this.__boundEventListeners = this.__boundEventListeners || new WeakMap();
|
|
const listeners = this.__boundEventListeners.get(node);
|
|
const eventKey = `${eventName}_${cbName}`;
|
|
|
|
if (listeners && typeof listeners[eventKey]) {
|
|
node.removeEventListener(eventName, listeners[eventKey], options);
|
|
listeners[eventKey] = null;
|
|
}
|
|
}
|
|
|
|
once(node, eventName, cbName, options) {
|
|
const wrappedCbName = `__onceCb__${cbName}`;
|
|
|
|
this[wrappedCbName] = (...args) => {
|
|
this[cbName](...args);
|
|
this.unlisten(node, eventName, wrappedCbName, options);
|
|
};
|
|
|
|
this.listen(node, eventName, wrappedCbName, options);
|
|
}
|
|
|
|
__registerListener(cbName, node, eventName) {
|
|
this.__boundEventListeners = this.__boundEventListeners || new WeakMap();
|
|
const boundListener = this[cbName].bind(this);
|
|
const eventKey = `${eventName}_${cbName}`;
|
|
let listeners = this.__boundEventListeners.get(node);
|
|
|
|
// If there is already a handler for the event assigned we stop here.
|
|
if (listeners && typeof listeners[eventKey] === 'function') return;
|
|
|
|
if (!listeners) {
|
|
listeners = {};
|
|
}
|
|
|
|
listeners[eventKey] = boundListener;
|
|
this.__boundEventListeners.set(node, listeners);
|
|
|
|
return boundListener;
|
|
}
|
|
|
|
_track(node, boundListener) {
|
|
this._boundTrackingListener = boundListener;
|
|
this.listen(node, 'mousedown', '_trackMouseDown');
|
|
}
|
|
|
|
_trackMouseDown(e) {
|
|
this._trackInfo = {
|
|
originX: e.clientX,
|
|
originY: e.clientY,
|
|
started: false
|
|
};
|
|
|
|
this.listen(window, 'mousemove', '_trackMouseMove', true);
|
|
this.once(window, 'mouseup', '_trackMouseUp');
|
|
}
|
|
|
|
_trackMouseMove(e) {
|
|
if (!this._trackInfo.started && trackHasMovedEnough(this._trackInfo, e.clientX, e.clientY)) {
|
|
this._trackInfo.started = true;
|
|
this._boundTrackingListener(new CustomEvent('track', { detail: { state: 'start', dx: e.clientX - this._trackInfo.originX, dy: e.clientY - this._trackInfo.originY }, bubbles: true, composed: true }));
|
|
}
|
|
|
|
if (this._trackInfo.started) {
|
|
this._boundTrackingListener(new CustomEvent('track', { detail: { state: 'track', dx: e.clientX - this._trackInfo.originX, dy: e.clientY - this._trackInfo.originY }, bubbles: true, composed: true }));
|
|
}
|
|
}
|
|
|
|
_trackMouseUp(e) {
|
|
this._boundTrackingListener(new CustomEvent('track', { detail: { state: 'end', dx: e.clientX - this._trackInfo.originX, dy: e.clientY - this._trackInfo.originY }, bubbles: true, composed: true }));
|
|
this.unlisten(window, 'mousemove', '_trackMouseMove', true);
|
|
this._trackInfo = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
const TRACK_DISTANCE = 5;
|
|
|
|
function trackHasMovedEnough(info, x, y) {
|
|
let dx = Math.abs(info.originX - x);
|
|
let dy = Math.abs(info.originY - y);
|
|
return (dx >= TRACK_DISTANCE || dy >= TRACK_DISTANCE);
|
|
}
|