/* Copyright © 2015-2016 David Valdman */
define(function(require, exports, module) {
var OptionsManager = require('../core/_OptionsManager');
var EventHandler = require('../events/EventHandler');
var now = Date.now;
/**
* Catalogues a history of touch events. Useful for creating more complex
* touch recognition for gestures. Currently only used by TouchInput to
* track previous touches to compute velocity.
*
* TouchTracker emits these events with the following payload data:
*
* `x` - Displacement in x-direction
* `y` - Displacement in y-direction
* `identifier` - DOM event touch identifier
* `timestamp` - Timestamp
* `count` - DOM event for number of simultaneous touches
* `history` - History of touches for the gesture
* `event` - Original DOM event
*
* @class TouchTracker
* @constructor
* @private
* @uses Core._OptionsManager
* @param [options] {Object} Options
* @param [options.memory] {Number} Number of past touches to record in history
* @param [options.track] {Number} Max simultaneous touches to record
* @param [options.limit] {Number} Limit number of touches. If reached, no events are emitted
*/
function TouchTracker(options) {
this.options = OptionsManager.setOptions(this, options);
this.history = {};
this.numTouches = 0;
this._eventInput = new EventHandler();
this._eventOutput = new EventHandler();
EventHandler.setInputHandler(this, this._eventInput);
EventHandler.setOutputHandler(this, this._eventOutput);
this._eventInput.on('touchstart', handleStart.bind(this));
this._eventInput.on('touchmove', handleMove.bind(this));
this._eventInput.on('touchend', handleEnd.bind(this));
this._eventInput.on('touchcancel', handleEnd.bind(this));
}
TouchTracker.DEFAULT_OPTIONS = {
track : 1,
limit : Infinity, // number of simultaneous touches
memory : 1 // length of recorded history
};
/**
* Record touch data
*
* @method track
* @param id {Number} touch identifier
* @param data {Object} touch data
*/
TouchTracker.prototype.track = function track(id, data) {
this.numTouches++;
this.history[id] = [data];
};
/**
* Remove record of touch data
*
* @method untrack
* @param id {Number} touch identifier
*/
TouchTracker.prototype.untrack = function track(id){
this.numTouches--;
delete this.history[id];
};
function getData(touch, event, history) {
return {
x: touch.clientX,
y: touch.clientY,
touchId : touch.identifier,
timestamp: now(),
count: event.touches.length,
event: touch,
history: history
};
}
function handleStart(event) {
if (event.touches.length >= this.options.limit) return false;
if (this.numTouches >= this.options.track) return false;
for (var i = 0; i < event.changedTouches.length; i++) {
var touch = event.changedTouches[i];
var touchId = touch.identifier;
var data = getData(touch, event, null);
this._eventOutput.emit('trackstart', data);
if (!this.history[touchId])
this.track(touchId, data);
}
}
function handleMove(event) {
if (event.touches.length >= this.options.limit) return false;
if (this.numTouches > this.options.track) return false;
event.preventDefault(); // prevents scrolling on mobile
for (var i = 0; i < event.changedTouches.length; i++) {
var touch = event.changedTouches[i];
var history = this.history[touch.identifier];
if (history) {
var data = getData(touch, event, history);
this._eventOutput.emit('trackmove', data);
if (history.length >= this.options.memory)
history.shift();
history.push(data);
}
}
}
function handleEnd(event) {
if (event.touches.length >= this.options.limit) return false;
if (this.numTouches > this.options.track) return false;
for (var i = 0; i < event.changedTouches.length; i++) {
var touch = event.changedTouches[i];
var touchId = touch.identifier;
var history = this.history[touchId];
if (history) {
var data = getData(touch, event, history);
this._eventOutput.emit('trackend', data);
this.untrack(touchId);
}
}
}
module.exports = TouchTracker;
});