define(["lib/jquery", "lib/underscore", "lib/sylvester",
"common/util/log", "common/util/dom_utils", "common/util/id_generator",
"common/util/binding_list", "ARCore/coordinate", "common/util/check"],
function($, _, Sylvester,
Log, DOMUtils, IDGenerator,
BindingList, Coordinate, check) {
"use strict";
var $V = Sylvester.$V;
/**
* @privconstructor
* @class
* Represents a single touch. See {@tutorial touch} for a description of the
* Lens multitouch system.
*
* @property {String} id The ID of this touch. These IDs are not
* unique: they are shared between Touches
* that represent the same touch on different
* elements.
* @property {Element} el Element this touch is fired on.
* @property {Element} target Element this touch started on before
* bubbling up.
* @property {Number} origX Original X coordinate of the touch.
* @property {Number} origY Original Y coordinate of the touch.
* @property {Number} pageX Current X coordinate of the touch.
* @property {Number} pageY Current Y coordinate of the touch.
* @property {Number} elX Current X coordinate of the touch,
* relative to the parent element
* @property {Number} elY Current Y coordinate of the touch,
* relative to the parent element.
* @property {Number} dx X displacement: pageX-origX
* @property {Number} dy Y displacement: pageY-origY
* @property {Number} w Width of the touch
* @property {Number} h Height of the touch
* @name Touch
*/
var Touch = function(id, x, y, w, h, el, target) {
/** @alias Touch.prototype */
var self = {};
var pageX, pageY, elX, elY, dx, dy;
// A list of movement subscribers. Each element is an object with four
// properties: fn, the subscriber function; delta, the amount a finger
// must move before calling the subscriber; and lastX and lastY, the x
// and y positions of the finger the last time the subscriber was
// called.
var moveSubscribers = BindingList();
// a list of end subscribers
var endSubscribers = BindingList();
/**
* Updates this touch to a new position, informing movement subscribers.
* @param {Number} newX New X position
* @param {Number} newY New Y position
* @private
*/
self._update = function(newX, newY) {
pageX = newX;
pageY = newY;
dx = pageX - x;
dy = pageY - y;
var offsets = $(el).offset();
if(offsets) {
elX = pageX - (offsets.left - $(document).scrollLeft());
elY = pageY - (offsets.top - $(document).scrollTop());
}
else {
// document doens't have offsets; use 0,0
elX = pageX;
elY = pageY;
}
moveSubscribers.each(function(sub) {
if(self._vector().distanceFrom($V([sub.lastX, sub.lastY])) > sub.delta) {
// touch has moved enough
sub.fn(self);
sub.lastX = pageX;
sub.lastY = pageY;
}
});
};
/**
* Ends this touch, informing end subscribers.
* @private
*/
self._markDone = function() {
endSubscribers.callAll(self);
};
/**
* Returns the position of the touch as a Sylvester vector.
* @private
*/
self._vector = function() {
return $V([pageX, pageY]);
};
/**
* Registers a function to be called whenever this touch moves.
* @param {Function} fn The function to call. Gets this touch as
* an argument.
* @param {Number} [delta] If given, the function will only be called
* when the touch has moved this many pixels.
* @return {Binding} A Binding that allows this handler to be
* cleared
*/
self.moved = function(fn, delta) {
check(fn, Function);
check(delta, check.Match.Optional(Number));
if(!delta) {
delta = 0;
}
return moveSubscribers.add({
fn: fn,
delta: delta,
lastX: pageX,
lastY: pageY
});
};
/**
* Registers a function to be called when this touch ends.
* @param {Function} fn The function to call. Gets this touch as an
* argument.
* @return {Binding} A Binding that allows this handler to be
* cleared
*/
self.ended = function(fn) {
check(fn, Function);
return endSubscribers.add(fn);
};
/**
* Returns true if the touch is inside the element it started in.
* @return {Boolean}
*/
self.isInEl = function() {
return ((elX > 0) && (elX < $(el).width()) &&
(elY > 0) && (elY < $(el).height()));
};
/**
* Returns a {@link Coordinate} representing the position of this
* Touch, in the projector coordinate system.
* @return {Coordinate}
*/
self.coordinate = function() {
return Coordinate(Coordinate.PROJECTOR, pageX, pageY);
};
self.id = id;
self.origX = x;
self.origY = y;
self.el = el;
self.target = target;
self.w = w;
self.h = h;
Object.defineProperty(self, "pageX", { get: function(){ return pageX; } });
Object.defineProperty(self, "pageY", { get: function(){ return pageY; } });
Object.defineProperty(self, "elX", { get: function(){ return elX; } });
Object.defineProperty(self, "elY", { get: function(){ return elY; } });
Object.defineProperty(self, "dx", { get: function(){ return dx; } });
Object.defineProperty(self, "dy", { get: function(){ return dy; } });
self._update(x, y);
Object.freeze(self);
return self;
};
return Touch;
});