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; });