Flick.js revision 35a16952b0308d5bf01f468c2e748bd2895a4bdf
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots/**
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots * Adds support for a "flick" event, which is fired at the end of a touch or mouse based flick gesture, and provides
9a2430d08e4d1b8b870cd3ba6c17ffc7881d16a6Todd Kloots * velocity of the flick, along with distance and time information.
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots *
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots * @module event-gestures
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots * @submodule event-flick
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots */
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots// TODO: Better way to sniff 'n' switch touch support?
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Klootsvar EVENT = ("ontouchstart" in Y.config.win) ? {
9a2430d08e4d1b8b870cd3ba6c17ffc7881d16a6Todd Kloots start: "touchstart",
14b1e9d6653e5b6ec6b2fbd4e30faaf3a74b6cf7Todd Kloots end: "touchend"
9a2430d08e4d1b8b870cd3ba6c17ffc7881d16a6Todd Kloots } : {
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots start: "mousedown",
b659a7f728e509c746e5c15dc6b85034a07eb47bTodd Kloots end: "mouseup"
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots },
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots START = "start",
b659a7f728e509c746e5c15dc6b85034a07eb47bTodd Kloots END = "end",
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots
b659a7f728e509c746e5c15dc6b85034a07eb47bTodd Kloots OWNER_DOCUMENT = "ownerDocument",
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots MIN_VELOCITY = "minVelocity",
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots MIN_DISTANCE = "minDistance",
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots
b659a7f728e509c746e5c15dc6b85034a07eb47bTodd Kloots _FLICK_START = "_fs",
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots _FLICK_START_HANDLE = "_fsh",
b659a7f728e509c746e5c15dc6b85034a07eb47bTodd Kloots _FLICK_END_HANDLE = "_feh",
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots NODE_TYPE = "nodeType";
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots/**
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots * Sets up a "flick" event, that is fired whenever the user initiates a flick gesture on the node
b659a7f728e509c746e5c15dc6b85034a07eb47bTodd Kloots * where the listener is attached. The subscriber can specify a minimum distance or velocity for
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots * which the event is to be fired.
b659a7f728e509c746e5c15dc6b85034a07eb47bTodd Kloots *
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots * @event flick
b659a7f728e509c746e5c15dc6b85034a07eb47bTodd Kloots * @param type {string} "flick"
b659a7f728e509c746e5c15dc6b85034a07eb47bTodd Kloots * @param fn {function} The method the event invokes.
b659a7f728e509c746e5c15dc6b85034a07eb47bTodd Kloots * @param cfg {Object} Optional. An object which specifies the minimum distance and/or velocity
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots * of the flick gesture for which the event is to be fired.
9a2430d08e4d1b8b870cd3ba6c17ffc7881d16a6Todd Kloots *
c73ea722dccc6aae68dc27f7709399507f5ee126Todd Kloots * @return {EventHandle} the detach handle
14b1e9d6653e5b6ec6b2fbd4e30faaf3a74b6cf7Todd Kloots */
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots
dcc27ab1cda56d7ad286e36c57f451db77894301Todd KlootsY.Event.define('flick', {
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots
9a2430d08e4d1b8b870cd3ba6c17ffc7881d16a6Todd Kloots // The initialization implementation. Called for the first subscription per node.
b659a7f728e509c746e5c15dc6b85034a07eb47bTodd Kloots init: function (node, subscriber, ce) {
14b1e9d6653e5b6ec6b2fbd4e30faaf3a74b6cf7Todd Kloots
9a2430d08e4d1b8b870cd3ba6c17ffc7881d16a6Todd Kloots var startHandle = node.on(EVENT[START],
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots this._onStart,
b659a7f728e509c746e5c15dc6b85034a07eb47bTodd Kloots this,
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots node,
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots subscriber,
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots ce);
b659a7f728e509c746e5c15dc6b85034a07eb47bTodd Kloots
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots node.setData(_FLICK_START_HANDLE, startHandle);
b659a7f728e509c746e5c15dc6b85034a07eb47bTodd Kloots },
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots // The destroy implementation. Called for the last detach per node.
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots destroy: function (node, subscriber, ce) {
b659a7f728e509c746e5c15dc6b85034a07eb47bTodd Kloots
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots var startHandle = node.getData(_FLICK_START_HANDLE),
b659a7f728e509c746e5c15dc6b85034a07eb47bTodd Kloots endHandle = node.getData(_FLICK_END_HANDLE);
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots if (startHandle) {
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots startHandle.detach();
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots node.clearData(_FLICK_START_HANDLE);
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots }
b659a7f728e509c746e5c15dc6b85034a07eb47bTodd Kloots
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots if (endHandle) {
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots endHandle.detach();
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots node.clearData(_FLICK_END_HANDLE);
b659a7f728e509c746e5c15dc6b85034a07eb47bTodd Kloots }
b659a7f728e509c746e5c15dc6b85034a07eb47bTodd Kloots },
b659a7f728e509c746e5c15dc6b85034a07eb47bTodd Kloots
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots // How to process the additional spec args
9a2430d08e4d1b8b870cd3ba6c17ffc7881d16a6Todd Kloots processArgs: function(args) {
c73ea722dccc6aae68dc27f7709399507f5ee126Todd Kloots var params = (args[3]) ? args.splice(3, 1)[0] : {};
9a2430d08e4d1b8b870cd3ba6c17ffc7881d16a6Todd Kloots
8a4137ab24ac4a19400c698a65cf8ca511b081c5Todd Kloots if (!(MIN_VELOCITY in params)) {
9a2430d08e4d1b8b870cd3ba6c17ffc7881d16a6Todd Kloots params.minVelocity = this.MIN_VELOCITY;
9a2430d08e4d1b8b870cd3ba6c17ffc7881d16a6Todd Kloots }
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots
9a2430d08e4d1b8b870cd3ba6c17ffc7881d16a6Todd Kloots if (!(MIN_DISTANCE in params)) {
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots params.minDistance = this.MIN_DISTANCE;
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots }
9a2430d08e4d1b8b870cd3ba6c17ffc7881d16a6Todd Kloots
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots Y.log("flick, processArgs : minDistance =" + params.minDistance + ", minVelocity =" + params.minVelocity);
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots return params;
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots },
9a2430d08e4d1b8b870cd3ba6c17ffc7881d16a6Todd Kloots
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots // Internal DOM listener to identify the start of the gesture
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots _onStart: function(e, node, subscriber, ce) {
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots var start = true, // always true for mouse
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots endHandle,
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots doc;
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots if (e.touches) {
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots start = (e.touches.length === 1);
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots e = e.touches[0];
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots }
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots if (start) {
de413b3e25c9cba63ab4b3003f5a4524d49f9f05Todd Kloots
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots e.preventDefault();
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots node.setData(_FLICK_START, {
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots time : new Date().getTime(),
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots pageX: e.pageX,
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots pageY: e.pageY,
9a2430d08e4d1b8b870cd3ba6c17ffc7881d16a6Todd Kloots clientX: e.clientX,
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots clientY: e.clientY,
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots _e : e
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots });
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots endHandle = node.getData(_FLICK_END_HANDLE);
269127229912bbc887ad01187105dcbd16133144Todd Kloots
269127229912bbc887ad01187105dcbd16133144Todd Kloots if (!endHandle) {
269127229912bbc887ad01187105dcbd16133144Todd Kloots doc = (node.get(NODE_TYPE) === 9) ? node : node.get(OWNER_DOCUMENT);
186030b6cf32f403c5bbd9c51366d2170473b0d2Todd Kloots
186030b6cf32f403c5bbd9c51366d2170473b0d2Todd Kloots endHandle = doc.on(EVENT[END], Y.bind(this._onEnd, this), null, node, subscriber, ce);
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots node.setData(_FLICK_END_HANDLE,endHandle);
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots }
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots }
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots },
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots // Internal DOM listener to identify the end of the gesture. Fires the
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots // synthetic flick event.
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots _onEnd: function(e, node, subscriber, ce) {
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots var endTime = new Date().getTime(),
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots valid = node.getData(_FLICK_START),
9a2430d08e4d1b8b870cd3ba6c17ffc7881d16a6Todd Kloots start = valid,
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots endEvent = e,
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots startTime,
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots time,
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots params,
de413b3e25c9cba63ab4b3003f5a4524d49f9f05Todd Kloots xyDistance,
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots distance,
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots absDistance,
de413b3e25c9cba63ab4b3003f5a4524d49f9f05Todd Kloots velocity,
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots axis;
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots
9a2430d08e4d1b8b870cd3ba6c17ffc7881d16a6Todd Kloots if (valid) {
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots if (e.changedTouches) {
dcc27ab1cda56d7ad286e36c57f451db77894301Todd Kloots if (e.changedTouches.length === 1 && e.touches.length === 0) {
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots endEvent = e.changedTouches[0];
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots } else {
9a2430d08e4d1b8b870cd3ba6c17ffc7881d16a6Todd Kloots valid = false;
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots }
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots }
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots if (valid) {
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots
269127229912bbc887ad01187105dcbd16133144Todd Kloots startTime = start.time;
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots endTime = new Date().getTime();
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots time = endTime - startTime;
269127229912bbc887ad01187105dcbd16133144Todd Kloots
9a2430d08e4d1b8b870cd3ba6c17ffc7881d16a6Todd Kloots params = subscriber._extra;
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots xyDistance = [
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots endEvent.pageX - start.pageX,
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots endEvent.pageY - start.pageY
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots ];
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots axis = params.axis || (Math.abs(xyDistance[0]) >= Math.abs(xyDistance[1])) ? 'x' : 'y';
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots distance = xyDistance[(axis === 'x') ? 0 : 1];
269127229912bbc887ad01187105dcbd16133144Todd Kloots absDistance = Math.abs(distance);
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots velocity = absDistance/time;
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots
269127229912bbc887ad01187105dcbd16133144Todd Kloots if (isFinite(velocity) && velocity >= params.minVelocity && absDistance >= params.minDistance) {
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots ce.fire({
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots distance: distance,
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots time: time,
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots velocity: velocity,
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots axis: axis,
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots button: e.button,
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots start: start,
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots end: {
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots time: endTime,
ac91eab7de6e2070ada47e7e1e78f1e3051f49abTodd Kloots clientX: endEvent.clientX,
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots clientY: endEvent.clientY,
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots pageX: endEvent.pageX,
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots pageY: endEvent.pageY,
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots _e : e
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots }
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots });
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots }
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots node.clearData(_FLICK_START);
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots }
9a2430d08e4d1b8b870cd3ba6c17ffc7881d16a6Todd Kloots }
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots },
9a2430d08e4d1b8b870cd3ba6c17ffc7881d16a6Todd Kloots
5f40f927dba3cf399373572f6ed6fe59a376376eTodd Kloots MIN_VELOCITY : 0,
9a2430d08e4d1b8b870cd3ba6c17ffc7881d16a6Todd Kloots MIN_DISTANCE : 10
9a2430d08e4d1b8b870cd3ba6c17ffc7881d16a6Todd Kloots});