232 lines
7.3 KiB
JavaScript
232 lines
7.3 KiB
JavaScript
'use strict';
|
|
|
|
(function (factory) {
|
|
if (typeof define === 'function' && define.amd) {
|
|
// AMD. Register as an anonymous module.
|
|
define([], factory);
|
|
} else if (typeof exports === 'object') {
|
|
// Node. Does not work with strict CommonJS, but
|
|
// only CommonJS-like environments that support module.exports,
|
|
// like Node.
|
|
module.exports = factory();
|
|
} else {
|
|
// Browser globals (root is window)
|
|
window.propagating = factory();
|
|
}
|
|
}(function () {
|
|
var _firstTarget = null; // singleton, will contain the target element where the touch event started
|
|
|
|
/**
|
|
* Extend an Hammer.js instance with event propagation.
|
|
*
|
|
* Features:
|
|
* - Events emitted by hammer will propagate in order from child to parent
|
|
* elements.
|
|
* - Events are extended with a function `event.stopPropagation()` to stop
|
|
* propagation to parent elements.
|
|
* - An option `preventDefault` to stop all default browser behavior.
|
|
*
|
|
* Usage:
|
|
* var hammer = propagatingHammer(new Hammer(element));
|
|
* var hammer = propagatingHammer(new Hammer(element), {preventDefault: true});
|
|
*
|
|
* @param {Hammer.Manager} hammer An hammer instance.
|
|
* @param {Object} [options] Available options:
|
|
* - `preventDefault: true | false | 'mouse' | 'touch' | 'pen'`.
|
|
* Enforce preventing the default browser behavior.
|
|
* Cannot be set to `false`.
|
|
* @return {Hammer.Manager} Returns the same hammer instance with extended
|
|
* functionality
|
|
*/
|
|
return function propagating(hammer, options) {
|
|
var _options = options || {
|
|
preventDefault: false
|
|
};
|
|
|
|
if (hammer.Manager) {
|
|
// This looks like the Hammer constructor.
|
|
// Overload the constructors with our own.
|
|
var Hammer = hammer;
|
|
|
|
var PropagatingHammer = function(element, options) {
|
|
var o = Object.create(_options);
|
|
if (options) Hammer.assign(o, options);
|
|
return propagating(new Hammer(element, o), o);
|
|
};
|
|
Hammer.assign(PropagatingHammer, Hammer);
|
|
|
|
PropagatingHammer.Manager = function (element, options) {
|
|
var o = Object.create(_options);
|
|
if (options) Hammer.assign(o, options);
|
|
return propagating(new Hammer.Manager(element, o), o);
|
|
};
|
|
|
|
return PropagatingHammer;
|
|
}
|
|
|
|
// create a wrapper object which will override the functions
|
|
// `on`, `off`, `destroy`, and `emit` of the hammer instance
|
|
var wrapper = Object.create(hammer);
|
|
|
|
// attach to DOM element
|
|
var element = hammer.element;
|
|
|
|
if(!element.hammer) element.hammer = [];
|
|
element.hammer.push(wrapper);
|
|
|
|
// register an event to catch the start of a gesture and store the
|
|
// target in a singleton
|
|
hammer.on('hammer.input', function (event) {
|
|
if (_options.preventDefault === true || (_options.preventDefault === event.pointerType)) {
|
|
event.preventDefault();
|
|
}
|
|
if (event.isFirst) {
|
|
_firstTarget = event.target;
|
|
}
|
|
});
|
|
|
|
/** @type {Object.<String, Array.<function>>} */
|
|
wrapper._handlers = {};
|
|
|
|
/**
|
|
* Register a handler for one or multiple events
|
|
* @param {String} events A space separated string with events
|
|
* @param {function} handler A callback function, called as handler(event)
|
|
* @returns {Hammer.Manager} Returns the hammer instance
|
|
*/
|
|
wrapper.on = function (events, handler) {
|
|
// register the handler
|
|
split(events).forEach(function (event) {
|
|
var _handlers = wrapper._handlers[event];
|
|
if (!_handlers) {
|
|
wrapper._handlers[event] = _handlers = [];
|
|
|
|
// register the static, propagated handler
|
|
hammer.on(event, propagatedHandler);
|
|
}
|
|
_handlers.push(handler);
|
|
});
|
|
|
|
return wrapper;
|
|
};
|
|
|
|
/**
|
|
* Unregister a handler for one or multiple events
|
|
* @param {String} events A space separated string with events
|
|
* @param {function} [handler] Optional. The registered handler. If not
|
|
* provided, all handlers for given events
|
|
* are removed.
|
|
* @returns {Hammer.Manager} Returns the hammer instance
|
|
*/
|
|
wrapper.off = function (events, handler) {
|
|
// unregister the handler
|
|
split(events).forEach(function (event) {
|
|
var _handlers = wrapper._handlers[event];
|
|
if (_handlers) {
|
|
_handlers = handler ? _handlers.filter(function (h) {
|
|
return h !== handler;
|
|
}) : [];
|
|
|
|
if (_handlers.length > 0) {
|
|
wrapper._handlers[event] = _handlers;
|
|
}
|
|
else {
|
|
// remove static, propagated handler
|
|
hammer.off(event, propagatedHandler);
|
|
delete wrapper._handlers[event];
|
|
}
|
|
}
|
|
});
|
|
|
|
return wrapper;
|
|
};
|
|
|
|
/**
|
|
* Emit to the event listeners
|
|
* @param {string} eventType
|
|
* @param {Event} event
|
|
*/
|
|
wrapper.emit = function(eventType, event) {
|
|
_firstTarget = event.target;
|
|
hammer.emit(eventType, event);
|
|
};
|
|
|
|
wrapper.destroy = function () {
|
|
// Detach from DOM element
|
|
var hammers = hammer.element.hammer;
|
|
var idx = hammers.indexOf(wrapper);
|
|
if(idx !== -1) hammers.splice(idx,1);
|
|
if(!hammers.length) delete hammer.element.hammer;
|
|
|
|
// clear all handlers
|
|
wrapper._handlers = {};
|
|
|
|
// call original hammer destroy
|
|
hammer.destroy();
|
|
};
|
|
|
|
// split a string with space separated words
|
|
function split(events) {
|
|
return events.match(/[^ ]+/g);
|
|
}
|
|
|
|
/**
|
|
* A static event handler, applying event propagation.
|
|
* @param {Object} event
|
|
*/
|
|
function propagatedHandler(event) {
|
|
// let only a single hammer instance handle this event
|
|
if (event.type !== 'hammer.input') {
|
|
// it is possible that the same srcEvent is used with multiple hammer events,
|
|
// we keep track on which events are handled in an object _handled
|
|
if (!event.srcEvent._handled) {
|
|
event.srcEvent._handled = {};
|
|
}
|
|
|
|
if (event.srcEvent._handled[event.type]) {
|
|
return;
|
|
}
|
|
else {
|
|
event.srcEvent._handled[event.type] = true;
|
|
}
|
|
}
|
|
|
|
// attach a stopPropagation function to the event
|
|
var stopped = false;
|
|
event.stopPropagation = function () {
|
|
stopped = true;
|
|
};
|
|
|
|
//wrap the srcEvent's stopPropagation to also stop hammer propagation:
|
|
var srcStop = event.srcEvent.stopPropagation.bind(event.srcEvent);
|
|
if(typeof srcStop == "function") {
|
|
event.srcEvent.stopPropagation = function(){
|
|
srcStop();
|
|
event.stopPropagation();
|
|
}
|
|
}
|
|
|
|
// attach firstTarget property to the event
|
|
event.firstTarget = _firstTarget;
|
|
|
|
// propagate over all elements (until stopped)
|
|
var elem = _firstTarget;
|
|
while (elem && !stopped) {
|
|
var elemHammer = elem.hammer;
|
|
if(elemHammer){
|
|
var _handlers;
|
|
for(var k = 0; k < elemHammer.length; k++){
|
|
_handlers = elemHammer[k]._handlers[event.type];
|
|
if(_handlers) for (var i = 0; i < _handlers.length && !stopped; i++) {
|
|
_handlers[i](event);
|
|
}
|
|
}
|
|
}
|
|
elem = elem.parentNode;
|
|
}
|
|
}
|
|
|
|
return wrapper;
|
|
};
|
|
}));
|