You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1328 lines
47 KiB
1328 lines
47 KiB
/*
|
|
YUI 3.17.2 (build 9c3c78e)
|
|
Copyright 2014 Yahoo! Inc. All rights reserved.
|
|
Licensed under the BSD License.
|
|
http://yuilibrary.com/license/
|
|
*/
|
|
|
|
YUI.add('gesture-simulate', function (Y, NAME) {
|
|
|
|
/**
|
|
* Simulate high-level user gestures by generating a set of native DOM events.
|
|
*
|
|
* @module gesture-simulate
|
|
* @requires event-simulate, async-queue, node-screen
|
|
*/
|
|
|
|
var NAME = "gesture-simulate",
|
|
|
|
// phantomjs check may be temporary, until we determine if it really support touch all the way through, like it claims to (http://code.google.com/p/phantomjs/issues/detail?id=375)
|
|
SUPPORTS_TOUCH = ((Y.config.win && ("ontouchstart" in Y.config.win)) && !(Y.UA.phantomjs) && !(Y.UA.chrome && Y.UA.chrome < 6)),
|
|
|
|
gestureNames = {
|
|
tap: 1,
|
|
doubletap: 1,
|
|
press: 1,
|
|
move: 1,
|
|
flick: 1,
|
|
pinch: 1,
|
|
rotate: 1
|
|
},
|
|
|
|
touchEvents = {
|
|
touchstart: 1,
|
|
touchmove: 1,
|
|
touchend: 1,
|
|
touchcancel: 1
|
|
},
|
|
|
|
document = Y.config.doc,
|
|
emptyTouchList,
|
|
|
|
EVENT_INTERVAL = 20, // 20ms
|
|
START_PAGEX, // will be adjusted to the node element center
|
|
START_PAGEY, // will be adjusted to the node element center
|
|
|
|
// defaults that user can override.
|
|
DEFAULTS = {
|
|
// tap gestures
|
|
HOLD_TAP: 10, // 10ms
|
|
DELAY_TAP: 10, // 10ms
|
|
|
|
// press gesture
|
|
HOLD_PRESS: 3000, // 3sec
|
|
MIN_HOLD_PRESS: 1000, // 1sec
|
|
MAX_HOLD_PRESS: 60000, // 1min
|
|
|
|
// move gesture
|
|
DISTANCE_MOVE: 200, // 200 pixels
|
|
DURATION_MOVE: 1000, // 1sec
|
|
MAX_DURATION_MOVE: 5000,// 5sec
|
|
|
|
// flick gesture
|
|
MIN_VELOCITY_FLICK: 1.3,
|
|
DISTANCE_FLICK: 200, // 200 pixels
|
|
DURATION_FLICK: 1000, // 1sec
|
|
MAX_DURATION_FLICK: 5000,// 5sec
|
|
|
|
// pinch/rotation
|
|
DURATION_PINCH: 1000 // 1sec
|
|
},
|
|
|
|
TOUCH_START = 'touchstart',
|
|
TOUCH_MOVE = 'touchmove',
|
|
TOUCH_END = 'touchend',
|
|
|
|
GESTURE_START = 'gesturestart',
|
|
GESTURE_CHANGE = 'gesturechange',
|
|
GESTURE_END = 'gestureend',
|
|
|
|
MOUSE_UP = 'mouseup',
|
|
MOUSE_MOVE = 'mousemove',
|
|
MOUSE_DOWN = 'mousedown',
|
|
MOUSE_CLICK = 'click',
|
|
MOUSE_DBLCLICK = 'dblclick',
|
|
|
|
X_AXIS = 'x',
|
|
Y_AXIS = 'y';
|
|
|
|
|
|
function Simulations(node) {
|
|
if(!node) {
|
|
Y.error(NAME+': invalid target node');
|
|
}
|
|
this.node = node;
|
|
this.target = Y.Node.getDOMNode(node);
|
|
|
|
var startXY = this.node.getXY(),
|
|
dims = this._getDims();
|
|
|
|
START_PAGEX = startXY[0] + (dims[0])/2;
|
|
START_PAGEY = startXY[1] + (dims[1])/2;
|
|
}
|
|
|
|
Simulations.prototype = {
|
|
|
|
/**
|
|
* Helper method to convert a degree to a radian.
|
|
*
|
|
* @method _toRadian
|
|
* @private
|
|
* @param {Number} deg A degree to be converted to a radian.
|
|
* @return {Number} The degree in radian.
|
|
*/
|
|
_toRadian: function(deg) {
|
|
return deg * (Math.PI/180);
|
|
},
|
|
|
|
/**
|
|
* Helper method to get height/width while accounting for
|
|
* rotation/scale transforms where possible by using the
|
|
* bounding client rectangle height/width instead of the
|
|
* offsetWidth/Height which region uses.
|
|
* @method _getDims
|
|
* @private
|
|
* @return {Array} Array with [height, width]
|
|
*/
|
|
_getDims : function() {
|
|
var region,
|
|
width,
|
|
height;
|
|
|
|
// Ideally, this should be in DOM somewhere.
|
|
if (this.target.getBoundingClientRect) {
|
|
region = this.target.getBoundingClientRect();
|
|
|
|
if ("height" in region) {
|
|
height = region.height;
|
|
} else {
|
|
// IE7,8 has getBCR, but no height.
|
|
height = Math.abs(region.bottom - region.top);
|
|
}
|
|
|
|
if ("width" in region) {
|
|
width = region.width;
|
|
} else {
|
|
// IE7,8 has getBCR, but no width.
|
|
width = Math.abs(region.right - region.left);
|
|
}
|
|
} else {
|
|
region = this.node.get("region");
|
|
width = region.width;
|
|
height = region.height;
|
|
}
|
|
|
|
return [width, height];
|
|
},
|
|
|
|
/**
|
|
* Helper method to convert a point relative to the node element into
|
|
* the point in the page coordination.
|
|
*
|
|
* @method _calculateDefaultPoint
|
|
* @private
|
|
* @param {Array} point A point relative to the node element.
|
|
* @return {Array} The same point in the page coordination.
|
|
*/
|
|
_calculateDefaultPoint: function(point) {
|
|
|
|
var height;
|
|
|
|
if(!Y.Lang.isArray(point) || point.length === 0) {
|
|
point = [START_PAGEX, START_PAGEY];
|
|
} else {
|
|
if(point.length == 1) {
|
|
height = this._getDims[1];
|
|
point[1] = height/2;
|
|
}
|
|
// convert to page(viewport) coordination
|
|
point[0] = this.node.getX() + point[0];
|
|
point[1] = this.node.getY() + point[1];
|
|
}
|
|
|
|
return point;
|
|
},
|
|
|
|
/**
|
|
* The "rotate" and "pinch" methods are essencially same with the exact same
|
|
* arguments. Only difference is the required parameters. The rotate method
|
|
* requires "rotation" parameter while the pinch method requires "startRadius"
|
|
* and "endRadius" parameters.
|
|
*
|
|
* @method rotate
|
|
* @param {Function} cb The callback to execute when the gesture simulation
|
|
* is completed.
|
|
* @param {Array} center A center point where the pinch gesture of two fingers
|
|
* should happen. It is relative to the top left corner of the target
|
|
* node element.
|
|
* @param {Number} startRadius A radius of start circle where 2 fingers are
|
|
* on when the gesture starts. This is optional. The default is a fourth of
|
|
* either target node width or height whichever is smaller.
|
|
* @param {Number} endRadius A radius of end circle where 2 fingers will be on when
|
|
* the pinch or spread gestures are completed. This is optional.
|
|
* The default is a fourth of either target node width or height whichever is less.
|
|
* @param {Number} duration A duration of the gesture in millisecond.
|
|
* @param {Number} start A start angle(0 degree at 12 o'clock) where the
|
|
* gesture should start. Default is 0.
|
|
* @param {Number} rotation A rotation in degree. It is required.
|
|
*/
|
|
rotate: function(cb, center, startRadius, endRadius, duration, start, rotation) {
|
|
var radius,
|
|
r1 = startRadius, // optional
|
|
r2 = endRadius; // optional
|
|
|
|
if(!Y.Lang.isNumber(r1) || !Y.Lang.isNumber(r2) || r1<0 || r2<0) {
|
|
radius = (this.target.offsetWidth < this.target.offsetHeight)?
|
|
this.target.offsetWidth/4 : this.target.offsetHeight/4;
|
|
r1 = radius;
|
|
r2 = radius;
|
|
}
|
|
|
|
// required
|
|
if(!Y.Lang.isNumber(rotation)) {
|
|
Y.error(NAME+'Invalid rotation detected.');
|
|
}
|
|
|
|
this.pinch(cb, center, r1, r2, duration, start, rotation);
|
|
},
|
|
|
|
/**
|
|
* The "rotate" and "pinch" methods are essencially same with the exact same
|
|
* arguments. Only difference is the required parameters. The rotate method
|
|
* requires "rotation" parameter while the pinch method requires "startRadius"
|
|
* and "endRadius" parameters.
|
|
*
|
|
* The "pinch" gesture can simulate various 2 finger gestures such as pinch,
|
|
* spread and/or rotation. The "startRadius" and "endRadius" are required.
|
|
* If endRadius is larger than startRadius, it becomes a spread gesture
|
|
* otherwise a pinch gesture.
|
|
*
|
|
* @method pinch
|
|
* @param {Function} cb The callback to execute when the gesture simulation
|
|
* is completed.
|
|
* @param {Array} center A center point where the pinch gesture of two fingers
|
|
* should happen. It is relative to the top left corner of the target
|
|
* node element.
|
|
* @param {Number} startRadius A radius of start circle where 2 fingers are
|
|
* on when the gesture starts. This paramenter is required.
|
|
* @param {Number} endRadius A radius of end circle where 2 fingers will be on when
|
|
* the pinch or spread gestures are completed. This parameter is required.
|
|
* @param {Number} duration A duration of the gesture in millisecond.
|
|
* @param {Number} start A start angle(0 degree at 12 o'clock) where the
|
|
* gesture should start. Default is 0.
|
|
* @param {Number} rotation If rotation is desired during the pinch or
|
|
* spread gestures, this parameter can be used. Default is 0 degree.
|
|
*/
|
|
pinch: function(cb, center, startRadius, endRadius, duration, start, rotation) {
|
|
var eventQueue,
|
|
i,
|
|
interval = EVENT_INTERVAL,
|
|
touches,
|
|
id = 0,
|
|
r1 = startRadius, // required
|
|
r2 = endRadius, // required
|
|
radiusPerStep,
|
|
centerX, centerY,
|
|
startScale, endScale, scalePerStep,
|
|
startRot, endRot, rotPerStep,
|
|
path1 = {start: [], end: []}, // paths for 1st and 2nd fingers.
|
|
path2 = {start: [], end: []},
|
|
steps,
|
|
touchMove;
|
|
|
|
center = this._calculateDefaultPoint(center);
|
|
|
|
if(!Y.Lang.isNumber(r1) || !Y.Lang.isNumber(r2) || r1<0 || r2<0) {
|
|
Y.error(NAME+'Invalid startRadius and endRadius detected.');
|
|
}
|
|
|
|
if(!Y.Lang.isNumber(duration) || duration <= 0) {
|
|
duration = DEFAULTS.DURATION_PINCH;
|
|
}
|
|
|
|
if(!Y.Lang.isNumber(start)) {
|
|
start = 0.0;
|
|
} else {
|
|
start = start%360;
|
|
while(start < 0) {
|
|
start += 360;
|
|
}
|
|
}
|
|
|
|
if(!Y.Lang.isNumber(rotation)) {
|
|
rotation = 0.0;
|
|
}
|
|
|
|
Y.AsyncQueue.defaults.timeout = interval;
|
|
eventQueue = new Y.AsyncQueue();
|
|
|
|
// range determination
|
|
centerX = center[0];
|
|
centerY = center[1];
|
|
|
|
startRot = start;
|
|
endRot = start + rotation;
|
|
|
|
// 1st finger path
|
|
path1.start = [
|
|
centerX + r1*Math.sin(this._toRadian(startRot)),
|
|
centerY - r1*Math.cos(this._toRadian(startRot))
|
|
];
|
|
path1.end = [
|
|
centerX + r2*Math.sin(this._toRadian(endRot)),
|
|
centerY - r2*Math.cos(this._toRadian(endRot))
|
|
];
|
|
|
|
// 2nd finger path
|
|
path2.start = [
|
|
centerX - r1*Math.sin(this._toRadian(startRot)),
|
|
centerY + r1*Math.cos(this._toRadian(startRot))
|
|
];
|
|
path2.end = [
|
|
centerX - r2*Math.sin(this._toRadian(endRot)),
|
|
centerY + r2*Math.cos(this._toRadian(endRot))
|
|
];
|
|
|
|
startScale = 1.0;
|
|
endScale = endRadius/startRadius;
|
|
|
|
// touch/gesture start
|
|
eventQueue.add({
|
|
fn: function() {
|
|
var coord1, coord2, coord, touches;
|
|
|
|
// coordinate for each touch object.
|
|
coord1 = {
|
|
pageX: path1.start[0],
|
|
pageY: path1.start[1],
|
|
clientX: path1.start[0],
|
|
clientY: path1.start[1]
|
|
};
|
|
coord2 = {
|
|
pageX: path2.start[0],
|
|
pageY: path2.start[1],
|
|
clientX: path2.start[0],
|
|
clientY: path2.start[1]
|
|
};
|
|
touches = this._createTouchList([Y.merge({
|
|
identifier: (id++)
|
|
}, coord1), Y.merge({
|
|
identifier: (id++)
|
|
}, coord2)]);
|
|
|
|
// coordinate for top level event
|
|
coord = {
|
|
pageX: (path1.start[0] + path2.start[0])/2,
|
|
pageY: (path1.start[0] + path2.start[1])/2,
|
|
clientX: (path1.start[0] + path2.start[0])/2,
|
|
clientY: (path1.start[0] + path2.start[1])/2
|
|
};
|
|
|
|
this._simulateEvent(this.target, TOUCH_START, Y.merge({
|
|
touches: touches,
|
|
targetTouches: touches,
|
|
changedTouches: touches,
|
|
scale: startScale,
|
|
rotation: startRot
|
|
}, coord));
|
|
|
|
if(Y.UA.ios >= 2.0) {
|
|
/* gesture starts when the 2nd finger touch starts.
|
|
* The implementation will fire 1 touch start event for both fingers,
|
|
* simulating 2 fingers touched on the screen at the same time.
|
|
*/
|
|
this._simulateEvent(this.target, GESTURE_START, Y.merge({
|
|
scale: startScale,
|
|
rotation: startRot
|
|
}, coord));
|
|
}
|
|
},
|
|
timeout: 0,
|
|
context: this
|
|
});
|
|
|
|
// gesture change
|
|
steps = Math.floor(duration/interval);
|
|
radiusPerStep = (r2 - r1)/steps;
|
|
scalePerStep = (endScale - startScale)/steps;
|
|
rotPerStep = (endRot - startRot)/steps;
|
|
|
|
touchMove = function(step) {
|
|
var radius = r1 + (radiusPerStep)*step,
|
|
px1 = centerX + radius*Math.sin(this._toRadian(startRot + rotPerStep*step)),
|
|
py1 = centerY - radius*Math.cos(this._toRadian(startRot + rotPerStep*step)),
|
|
px2 = centerX - radius*Math.sin(this._toRadian(startRot + rotPerStep*step)),
|
|
py2 = centerY + radius*Math.cos(this._toRadian(startRot + rotPerStep*step)),
|
|
px = (px1+px2)/2,
|
|
py = (py1+py2)/2,
|
|
coord1, coord2, coord, touches;
|
|
|
|
// coordinate for each touch object.
|
|
coord1 = {
|
|
pageX: px1,
|
|
pageY: py1,
|
|
clientX: px1,
|
|
clientY: py1
|
|
};
|
|
coord2 = {
|
|
pageX: px2,
|
|
pageY: py2,
|
|
clientX: px2,
|
|
clientY: py2
|
|
};
|
|
touches = this._createTouchList([Y.merge({
|
|
identifier: (id++)
|
|
}, coord1), Y.merge({
|
|
identifier: (id++)
|
|
}, coord2)]);
|
|
|
|
// coordinate for top level event
|
|
coord = {
|
|
pageX: px,
|
|
pageY: py,
|
|
clientX: px,
|
|
clientY: py
|
|
};
|
|
|
|
this._simulateEvent(this.target, TOUCH_MOVE, Y.merge({
|
|
touches: touches,
|
|
targetTouches: touches,
|
|
changedTouches: touches,
|
|
scale: startScale + scalePerStep*step,
|
|
rotation: startRot + rotPerStep*step
|
|
}, coord));
|
|
|
|
if(Y.UA.ios >= 2.0) {
|
|
this._simulateEvent(this.target, GESTURE_CHANGE, Y.merge({
|
|
scale: startScale + scalePerStep*step,
|
|
rotation: startRot + rotPerStep*step
|
|
}, coord));
|
|
}
|
|
};
|
|
|
|
for (i=0; i < steps; i++) {
|
|
eventQueue.add({
|
|
fn: touchMove,
|
|
args: [i],
|
|
context: this
|
|
});
|
|
}
|
|
|
|
// gesture end
|
|
eventQueue.add({
|
|
fn: function() {
|
|
var emptyTouchList = this._getEmptyTouchList(),
|
|
coord1, coord2, coord, touches;
|
|
|
|
// coordinate for each touch object.
|
|
coord1 = {
|
|
pageX: path1.end[0],
|
|
pageY: path1.end[1],
|
|
clientX: path1.end[0],
|
|
clientY: path1.end[1]
|
|
};
|
|
coord2 = {
|
|
pageX: path2.end[0],
|
|
pageY: path2.end[1],
|
|
clientX: path2.end[0],
|
|
clientY: path2.end[1]
|
|
};
|
|
touches = this._createTouchList([Y.merge({
|
|
identifier: (id++)
|
|
}, coord1), Y.merge({
|
|
identifier: (id++)
|
|
}, coord2)]);
|
|
|
|
// coordinate for top level event
|
|
coord = {
|
|
pageX: (path1.end[0] + path2.end[0])/2,
|
|
pageY: (path1.end[0] + path2.end[1])/2,
|
|
clientX: (path1.end[0] + path2.end[0])/2,
|
|
clientY: (path1.end[0] + path2.end[1])/2
|
|
};
|
|
|
|
if(Y.UA.ios >= 2.0) {
|
|
this._simulateEvent(this.target, GESTURE_END, Y.merge({
|
|
scale: endScale,
|
|
rotation: endRot
|
|
}, coord));
|
|
}
|
|
|
|
this._simulateEvent(this.target, TOUCH_END, Y.merge({
|
|
touches: emptyTouchList,
|
|
targetTouches: emptyTouchList,
|
|
changedTouches: touches,
|
|
scale: endScale,
|
|
rotation: endRot
|
|
}, coord));
|
|
},
|
|
context: this
|
|
});
|
|
|
|
if(cb && Y.Lang.isFunction(cb)) {
|
|
eventQueue.add({
|
|
fn: cb,
|
|
|
|
// by default, the callback runs the node context where
|
|
// simulateGesture method is called.
|
|
context: this.node
|
|
|
|
//TODO: Use args to pass error object as 1st param if there is an error.
|
|
//args:
|
|
});
|
|
}
|
|
|
|
eventQueue.run();
|
|
},
|
|
|
|
/**
|
|
* The "tap" gesture can be used for various single touch point gestures
|
|
* such as single tap, N number of taps, long press. The default is a single
|
|
* tap.
|
|
*
|
|
* @method tap
|
|
* @param {Function} cb The callback to execute when the gesture simulation
|
|
* is completed.
|
|
* @param {Array} point A point(relative to the top left corner of the
|
|
* target node element) where the tap gesture should start. The default
|
|
* is the center of the taget node.
|
|
* @param {Number} times The number of taps. Default is 1.
|
|
* @param {Number} hold The hold time in milliseconds between "touchstart" and
|
|
* "touchend" event generation. Default is 10ms.
|
|
* @param {Number} delay The time gap in millisecond between taps if this
|
|
* gesture has more than 1 tap. Default is 10ms.
|
|
*/
|
|
tap: function(cb, point, times, hold, delay) {
|
|
var eventQueue = new Y.AsyncQueue(),
|
|
emptyTouchList = this._getEmptyTouchList(),
|
|
touches,
|
|
coord,
|
|
i,
|
|
touchStart,
|
|
touchEnd;
|
|
|
|
point = this._calculateDefaultPoint(point);
|
|
|
|
if(!Y.Lang.isNumber(times) || times < 1) {
|
|
times = 1;
|
|
}
|
|
|
|
if(!Y.Lang.isNumber(hold)) {
|
|
hold = DEFAULTS.HOLD_TAP;
|
|
}
|
|
|
|
if(!Y.Lang.isNumber(delay)) {
|
|
delay = DEFAULTS.DELAY_TAP;
|
|
}
|
|
|
|
coord = {
|
|
pageX: point[0],
|
|
pageY: point[1],
|
|
clientX: point[0],
|
|
clientY: point[1]
|
|
};
|
|
|
|
touches = this._createTouchList([Y.merge({identifier: 0}, coord)]);
|
|
|
|
touchStart = function() {
|
|
this._simulateEvent(this.target, TOUCH_START, Y.merge({
|
|
touches: touches,
|
|
targetTouches: touches,
|
|
changedTouches: touches
|
|
}, coord));
|
|
};
|
|
|
|
touchEnd = function() {
|
|
this._simulateEvent(this.target, TOUCH_END, Y.merge({
|
|
touches: emptyTouchList,
|
|
targetTouches: emptyTouchList,
|
|
changedTouches: touches
|
|
}, coord));
|
|
};
|
|
|
|
for (i=0; i < times; i++) {
|
|
eventQueue.add({
|
|
fn: touchStart,
|
|
context: this,
|
|
timeout: (i === 0)? 0 : delay
|
|
});
|
|
|
|
eventQueue.add({
|
|
fn: touchEnd,
|
|
context: this,
|
|
timeout: hold
|
|
});
|
|
}
|
|
|
|
if(times > 1 && !SUPPORTS_TOUCH) {
|
|
eventQueue.add({
|
|
fn: function() {
|
|
this._simulateEvent(this.target, MOUSE_DBLCLICK, coord);
|
|
},
|
|
context: this
|
|
});
|
|
}
|
|
|
|
if(cb && Y.Lang.isFunction(cb)) {
|
|
eventQueue.add({
|
|
fn: cb,
|
|
|
|
// by default, the callback runs the node context where
|
|
// simulateGesture method is called.
|
|
context: this.node
|
|
|
|
//TODO: Use args to pass error object as 1st param if there is an error.
|
|
//args:
|
|
});
|
|
}
|
|
|
|
eventQueue.run();
|
|
},
|
|
|
|
/**
|
|
* The "flick" gesture is a specialized "move" that has some velocity
|
|
* and the movement always runs either x or y axis. The velocity is calculated
|
|
* with "distance" and "duration" arguments. If the calculated velocity is
|
|
* below than the minimum velocity, the given duration will be ignored and
|
|
* new duration will be created to make a valid flick gesture.
|
|
*
|
|
* @method flick
|
|
* @param {Function} cb The callback to execute when the gesture simulation
|
|
* is completed.
|
|
* @param {Array} point A point(relative to the top left corner of the
|
|
* target node element) where the flick gesture should start. The default
|
|
* is the center of the taget node.
|
|
* @param {String} axis Either "x" or "y".
|
|
* @param {Number} distance A distance in pixels to flick.
|
|
* @param {Number} duration A duration of the gesture in millisecond.
|
|
*
|
|
*/
|
|
flick: function(cb, point, axis, distance, duration) {
|
|
var path;
|
|
|
|
point = this._calculateDefaultPoint(point);
|
|
|
|
if(!Y.Lang.isString(axis)) {
|
|
axis = X_AXIS;
|
|
} else {
|
|
axis = axis.toLowerCase();
|
|
if(axis !== X_AXIS && axis !== Y_AXIS) {
|
|
Y.error(NAME+'(flick): Only x or y axis allowed');
|
|
}
|
|
}
|
|
|
|
if(!Y.Lang.isNumber(distance)) {
|
|
distance = DEFAULTS.DISTANCE_FLICK;
|
|
}
|
|
|
|
if(!Y.Lang.isNumber(duration)){
|
|
duration = DEFAULTS.DURATION_FLICK; // ms
|
|
} else {
|
|
if(duration > DEFAULTS.MAX_DURATION_FLICK) {
|
|
duration = DEFAULTS.MAX_DURATION_FLICK;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check if too slow for a flick.
|
|
* Adjust duration if the calculated velocity is less than
|
|
* the minimum velcocity to be claimed as a flick.
|
|
*/
|
|
if(Math.abs(distance)/duration < DEFAULTS.MIN_VELOCITY_FLICK) {
|
|
duration = Math.abs(distance)/DEFAULTS.MIN_VELOCITY_FLICK;
|
|
}
|
|
|
|
path = {
|
|
start: Y.clone(point),
|
|
end: [
|
|
(axis === X_AXIS) ? point[0]+distance : point[0],
|
|
(axis === Y_AXIS) ? point[1]+distance : point[1]
|
|
]
|
|
};
|
|
|
|
this._move(cb, path, duration);
|
|
},
|
|
|
|
/**
|
|
* The "move" gesture simulate the movement of any direction between
|
|
* the straight line of start and end point for the given duration.
|
|
* The path argument is an object with "point", "xdist" and "ydist" properties.
|
|
* The "point" property is an array with x and y coordinations(relative to the
|
|
* top left corner of the target node element) while "xdist" and "ydist"
|
|
* properties are used for the distance along the x and y axis. A negative
|
|
* distance number can be used to drag either left or up direction.
|
|
*
|
|
* If no arguments are given, it will simulate the default move, which
|
|
* is moving 200 pixels from the center of the element to the positive X-axis
|
|
* direction for 1 sec.
|
|
*
|
|
* @method move
|
|
* @param {Function} cb The callback to execute when the gesture simulation
|
|
* is completed.
|
|
* @param {Object} path An object with "point", "xdist" and "ydist".
|
|
* @param {Number} duration A duration of the gesture in millisecond.
|
|
*/
|
|
move: function(cb, path, duration) {
|
|
var convertedPath;
|
|
|
|
if(!Y.Lang.isObject(path)) {
|
|
path = {
|
|
point: this._calculateDefaultPoint([]),
|
|
xdist: DEFAULTS.DISTANCE_MOVE,
|
|
ydist: 0
|
|
};
|
|
} else {
|
|
// convert to the page coordination
|
|
if(!Y.Lang.isArray(path.point)) {
|
|
path.point = this._calculateDefaultPoint([]);
|
|
} else {
|
|
path.point = this._calculateDefaultPoint(path.point);
|
|
}
|
|
|
|
if(!Y.Lang.isNumber(path.xdist)) {
|
|
path.xdist = DEFAULTS.DISTANCE_MOVE;
|
|
}
|
|
|
|
if(!Y.Lang.isNumber(path.ydist)) {
|
|
path.ydist = 0;
|
|
}
|
|
}
|
|
|
|
if(!Y.Lang.isNumber(duration)){
|
|
duration = DEFAULTS.DURATION_MOVE; // ms
|
|
} else {
|
|
if(duration > DEFAULTS.MAX_DURATION_MOVE) {
|
|
duration = DEFAULTS.MAX_DURATION_MOVE;
|
|
}
|
|
}
|
|
|
|
convertedPath = {
|
|
start: Y.clone(path.point),
|
|
end: [path.point[0]+path.xdist, path.point[1]+path.ydist]
|
|
};
|
|
|
|
this._move(cb, convertedPath, duration);
|
|
},
|
|
|
|
/**
|
|
* A base method on top of "move" and "flick" methods. The method takes
|
|
* the path with start/end properties and duration to generate a set of
|
|
* touch events for the movement gesture.
|
|
*
|
|
* @method _move
|
|
* @private
|
|
* @param {Function} cb The callback to execute when the gesture simulation
|
|
* is completed.
|
|
* @param {Object} path An object with "start" and "end" properties. Each
|
|
* property should be an array with x and y coordination (e.g. start: [100, 50])
|
|
* @param {Number} duration A duration of the gesture in millisecond.
|
|
*/
|
|
_move: function(cb, path, duration) {
|
|
var eventQueue,
|
|
i,
|
|
interval = EVENT_INTERVAL,
|
|
steps, stepX, stepY,
|
|
id = 0,
|
|
touchMove;
|
|
|
|
if(!Y.Lang.isNumber(duration)){
|
|
duration = DEFAULTS.DURATION_MOVE; // ms
|
|
} else {
|
|
if(duration > DEFAULTS.MAX_DURATION_MOVE) {
|
|
duration = DEFAULTS.MAX_DURATION_MOVE;
|
|
}
|
|
}
|
|
|
|
if(!Y.Lang.isObject(path)) {
|
|
path = {
|
|
start: [
|
|
START_PAGEX,
|
|
START_PAGEY
|
|
],
|
|
end: [
|
|
START_PAGEX + DEFAULTS.DISTANCE_MOVE,
|
|
START_PAGEY
|
|
]
|
|
};
|
|
} else {
|
|
if(!Y.Lang.isArray(path.start)) {
|
|
path.start = [
|
|
START_PAGEX,
|
|
START_PAGEY
|
|
];
|
|
}
|
|
if(!Y.Lang.isArray(path.end)) {
|
|
path.end = [
|
|
START_PAGEX + DEFAULTS.DISTANCE_MOVE,
|
|
START_PAGEY
|
|
];
|
|
}
|
|
}
|
|
|
|
Y.AsyncQueue.defaults.timeout = interval;
|
|
eventQueue = new Y.AsyncQueue();
|
|
|
|
// start
|
|
eventQueue.add({
|
|
fn: function() {
|
|
var coord = {
|
|
pageX: path.start[0],
|
|
pageY: path.start[1],
|
|
clientX: path.start[0],
|
|
clientY: path.start[1]
|
|
},
|
|
touches = this._createTouchList([
|
|
Y.merge({identifier: (id++)}, coord)
|
|
]);
|
|
|
|
this._simulateEvent(this.target, TOUCH_START, Y.merge({
|
|
touches: touches,
|
|
targetTouches: touches,
|
|
changedTouches: touches
|
|
}, coord));
|
|
},
|
|
timeout: 0,
|
|
context: this
|
|
});
|
|
|
|
// move
|
|
steps = Math.floor(duration/interval);
|
|
stepX = (path.end[0] - path.start[0])/steps;
|
|
stepY = (path.end[1] - path.start[1])/steps;
|
|
|
|
touchMove = function(step) {
|
|
var px = path.start[0]+(stepX * step),
|
|
py = path.start[1]+(stepY * step),
|
|
coord = {
|
|
pageX: px,
|
|
pageY: py,
|
|
clientX: px,
|
|
clientY: py
|
|
},
|
|
touches = this._createTouchList([
|
|
Y.merge({identifier: (id++)}, coord)
|
|
]);
|
|
|
|
this._simulateEvent(this.target, TOUCH_MOVE, Y.merge({
|
|
touches: touches,
|
|
targetTouches: touches,
|
|
changedTouches: touches
|
|
}, coord));
|
|
};
|
|
|
|
for (i=0; i < steps; i++) {
|
|
eventQueue.add({
|
|
fn: touchMove,
|
|
args: [i],
|
|
context: this
|
|
});
|
|
}
|
|
|
|
// last move
|
|
eventQueue.add({
|
|
fn: function() {
|
|
var coord = {
|
|
pageX: path.end[0],
|
|
pageY: path.end[1],
|
|
clientX: path.end[0],
|
|
clientY: path.end[1]
|
|
},
|
|
touches = this._createTouchList([
|
|
Y.merge({identifier: id}, coord)
|
|
]);
|
|
|
|
this._simulateEvent(this.target, TOUCH_MOVE, Y.merge({
|
|
touches: touches,
|
|
targetTouches: touches,
|
|
changedTouches: touches
|
|
}, coord));
|
|
},
|
|
timeout: 0,
|
|
context: this
|
|
});
|
|
|
|
// end
|
|
eventQueue.add({
|
|
fn: function() {
|
|
var coord = {
|
|
pageX: path.end[0],
|
|
pageY: path.end[1],
|
|
clientX: path.end[0],
|
|
clientY: path.end[1]
|
|
},
|
|
emptyTouchList = this._getEmptyTouchList(),
|
|
touches = this._createTouchList([
|
|
Y.merge({identifier: id}, coord)
|
|
]);
|
|
|
|
this._simulateEvent(this.target, TOUCH_END, Y.merge({
|
|
touches: emptyTouchList,
|
|
targetTouches: emptyTouchList,
|
|
changedTouches: touches
|
|
}, coord));
|
|
},
|
|
context: this
|
|
});
|
|
|
|
if(cb && Y.Lang.isFunction(cb)) {
|
|
eventQueue.add({
|
|
fn: cb,
|
|
|
|
// by default, the callback runs the node context where
|
|
// simulateGesture method is called.
|
|
context: this.node
|
|
|
|
//TODO: Use args to pass error object as 1st param if there is an error.
|
|
//args:
|
|
});
|
|
}
|
|
|
|
eventQueue.run();
|
|
},
|
|
|
|
/**
|
|
* Helper method to return a singleton instance of empty touch list.
|
|
*
|
|
* @method _getEmptyTouchList
|
|
* @private
|
|
* @return {TouchList | Array} An empty touch list object.
|
|
*/
|
|
_getEmptyTouchList: function() {
|
|
if(!emptyTouchList) {
|
|
emptyTouchList = this._createTouchList([]);
|
|
}
|
|
|
|
return emptyTouchList;
|
|
},
|
|
|
|
/**
|
|
* Helper method to convert an array with touch points to TouchList object as
|
|
* defined in http://www.w3.org/TR/touch-events/
|
|
*
|
|
* @method _createTouchList
|
|
* @private
|
|
* @param {Array} touchPoints
|
|
* @return {TouchList | Array} If underlaying platform support creating touch list
|
|
* a TouchList object will be returned otherwise a fake Array object
|
|
* will be returned.
|
|
*/
|
|
_createTouchList: function(touchPoints) {
|
|
/*
|
|
* Android 4.0.3 emulator:
|
|
* Native touch api supported starting in version 4.0 (Ice Cream Sandwich).
|
|
* However the support seems limited. In Android 4.0.3 emulator, I got
|
|
* "TouchList is not defined".
|
|
*/
|
|
var touches = [],
|
|
touchList,
|
|
self = this;
|
|
|
|
if(!!touchPoints && Y.Lang.isArray(touchPoints)) {
|
|
if(Y.UA.android && Y.UA.android >= 4.0 || Y.UA.ios && Y.UA.ios >= 2.0) {
|
|
Y.each(touchPoints, function(point) {
|
|
if(!point.identifier) {point.identifier = 0;}
|
|
if(!point.pageX) {point.pageX = 0;}
|
|
if(!point.pageY) {point.pageY = 0;}
|
|
if(!point.screenX) {point.screenX = 0;}
|
|
if(!point.screenY) {point.screenY = 0;}
|
|
|
|
touches.push(document.createTouch(Y.config.win,
|
|
self.target,
|
|
point.identifier,
|
|
point.pageX, point.pageY,
|
|
point.screenX, point.screenY));
|
|
});
|
|
|
|
touchList = document.createTouchList.apply(document, touches);
|
|
} else if(Y.UA.ios && Y.UA.ios < 2.0) {
|
|
Y.error(NAME+': No touch event simulation framework present.');
|
|
} else {
|
|
// this will inclide android(Y.UA.android && Y.UA.android < 4.0)
|
|
// and desktops among all others.
|
|
|
|
/*
|
|
* Touch APIs are broken in androids older than 4.0. We will use
|
|
* simulated touch apis for these versions.
|
|
*/
|
|
touchList = [];
|
|
Y.each(touchPoints, function(point) {
|
|
if(!point.identifier) {point.identifier = 0;}
|
|
if(!point.clientX) {point.clientX = 0;}
|
|
if(!point.clientY) {point.clientY = 0;}
|
|
if(!point.pageX) {point.pageX = 0;}
|
|
if(!point.pageY) {point.pageY = 0;}
|
|
if(!point.screenX) {point.screenX = 0;}
|
|
if(!point.screenY) {point.screenY = 0;}
|
|
|
|
touchList.push({
|
|
target: self.target,
|
|
identifier: point.identifier,
|
|
clientX: point.clientX,
|
|
clientY: point.clientY,
|
|
pageX: point.pageX,
|
|
pageY: point.pageY,
|
|
screenX: point.screenX,
|
|
screenY: point.screenY
|
|
});
|
|
});
|
|
|
|
touchList.item = function(i) {
|
|
return touchList[i];
|
|
};
|
|
}
|
|
} else {
|
|
Y.error(NAME+': Invalid touchPoints passed');
|
|
}
|
|
|
|
return touchList;
|
|
},
|
|
|
|
/**
|
|
* @method _simulateEvent
|
|
* @private
|
|
* @param {HTMLElement} target The DOM element that's the target of the event.
|
|
* @param {String} type The type of event or name of the supported gesture to simulate
|
|
* (i.e., "click", "doubletap", "flick").
|
|
* @param {Object} options (Optional) Extra options to copy onto the event object.
|
|
* For gestures, options are used to refine the gesture behavior.
|
|
*/
|
|
_simulateEvent: function(target, type, options) {
|
|
var touches;
|
|
|
|
if (touchEvents[type]) {
|
|
if(SUPPORTS_TOUCH) {
|
|
Y.Event.simulate(target, type, options);
|
|
} else {
|
|
// simulate using mouse events if touch is not applicable on this platform.
|
|
// but only single touch event can be simulated.
|
|
if(this._isSingleTouch(options.touches, options.targetTouches, options.changedTouches)) {
|
|
type = {
|
|
touchstart: MOUSE_DOWN,
|
|
touchmove: MOUSE_MOVE,
|
|
touchend: MOUSE_UP
|
|
}[type];
|
|
|
|
options.button = 0;
|
|
options.relatedTarget = null; // since we are not using mouseover event.
|
|
|
|
// touchend has none in options.touches.
|
|
touches = (type === MOUSE_UP)? options.changedTouches : options.touches;
|
|
|
|
options = Y.mix(options, {
|
|
screenX: touches.item(0).screenX,
|
|
screenY: touches.item(0).screenY,
|
|
clientX: touches.item(0).clientX,
|
|
clientY: touches.item(0).clientY
|
|
}, true);
|
|
|
|
Y.Event.simulate(target, type, options);
|
|
|
|
if(type == MOUSE_UP) {
|
|
Y.Event.simulate(target, MOUSE_CLICK, options);
|
|
}
|
|
} else {
|
|
Y.error("_simulateEvent(): Event '" + type + "' has multi touch objects that can't be simulated in your platform.");
|
|
}
|
|
}
|
|
} else {
|
|
// pass thru for all non touch events
|
|
Y.Event.simulate(target, type, options);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Helper method to check the single touch.
|
|
* @method _isSingleTouch
|
|
* @private
|
|
* @param {TouchList} touches
|
|
* @param {TouchList} targetTouches
|
|
* @param {TouchList} changedTouches
|
|
*/
|
|
_isSingleTouch: function(touches, targetTouches, changedTouches) {
|
|
return (touches && (touches.length <= 1)) &&
|
|
(targetTouches && (targetTouches.length <= 1)) &&
|
|
(changedTouches && (changedTouches.length <= 1));
|
|
}
|
|
};
|
|
|
|
/*
|
|
* A gesture simulation class.
|
|
*/
|
|
Y.GestureSimulation = Simulations;
|
|
|
|
/*
|
|
* Various simulation default behavior properties. If user override
|
|
* Y.GestureSimulation.defaults, overriden values will be used and this
|
|
* should be done before the gesture simulation.
|
|
*/
|
|
Y.GestureSimulation.defaults = DEFAULTS;
|
|
|
|
/*
|
|
* The high level gesture names that YUI knows how to simulate.
|
|
*/
|
|
Y.GestureSimulation.GESTURES = gestureNames;
|
|
|
|
/**
|
|
* Simulates the higher user level gesture of the given name on a target.
|
|
* This method generates a set of low level touch events(Apple specific gesture
|
|
* events as well for the iOS platforms) asynchronously. Note that gesture
|
|
* simulation is relying on `Y.Event.simulate()` method to generate
|
|
* the touch events under the hood. The `Y.Event.simulate()` method
|
|
* itself is a synchronous method.
|
|
*
|
|
* Users are suggested to use `Node.simulateGesture()` method which
|
|
* basically calls this method internally. Supported gestures are `tap`,
|
|
* `doubletap`, `press`, `move`, `flick`, `pinch` and `rotate`.
|
|
*
|
|
* The `pinch` gesture is used to simulate the pinching and spreading of two
|
|
* fingers. During a pinch simulation, rotation is also possible. Essentially
|
|
* `pinch` and `rotate` simulations share the same base implementation to allow
|
|
* both pinching and rotation at the same time. The only difference is `pinch`
|
|
* requires `start` and `end` option properties while `rotate` requires `rotation`
|
|
* option property.
|
|
*
|
|
* The `pinch` and `rotate` gestures can be described as placing 2 fingers along a
|
|
* circle. Pinching and spreading can be described by start and end circles while
|
|
* rotation occurs on a single circle. If the radius of the start circle is greater
|
|
* than the end circle, the gesture becomes a pinch, otherwise it is a spread spread.
|
|
*
|
|
* @example
|
|
*
|
|
* var node = Y.one("#target");
|
|
*
|
|
* // double tap example
|
|
* node.simulateGesture("doubletap", function() {
|
|
* // my callback function
|
|
* });
|
|
*
|
|
* // flick example from the center of the node, move 50 pixels down for 50ms)
|
|
* node.simulateGesture("flick", {
|
|
* axis: y,
|
|
* distance: -100
|
|
* duration: 50
|
|
* }, function() {
|
|
* // my callback function
|
|
* });
|
|
*
|
|
* // simulate rotating a node 75 degrees counter-clockwise
|
|
* node.simulateGesture("rotate", {
|
|
* rotation: -75
|
|
* });
|
|
*
|
|
* // simulate a pinch and a rotation at the same time.
|
|
* // fingers start on a circle of radius 100 px, placed at top/bottom
|
|
* // fingers end on a circle of radius 50px, placed at right/left
|
|
* node.simulateGesture("pinch", {
|
|
* r1: 100,
|
|
* r2: 50,
|
|
* start: 0
|
|
* rotation: 90
|
|
* });
|
|
*
|
|
* @method simulateGesture
|
|
* @param {HTMLElement|Node} node The YUI node or HTML element that's the target
|
|
* of the event.
|
|
* @param {String} name The name of the supported gesture to simulate. The
|
|
* supported gesture name is one of "tap", "doubletap", "press", "move",
|
|
* "flick", "pinch" and "rotate".
|
|
* @param {Object} [options] Extra options used to define the gesture behavior:
|
|
*
|
|
* Valid options properties for the `tap` gesture:
|
|
*
|
|
* @param {Array} [options.point] (Optional) Indicates the [x,y] coordinates
|
|
* where the tap should be simulated. Default is the center of the node
|
|
* element.
|
|
* @param {Number} [options.hold=10] (Optional) The hold time in milliseconds.
|
|
* This is the time between `touchstart` and `touchend` event generation.
|
|
* @param {Number} [options.times=1] (Optional) Indicates the number of taps.
|
|
* @param {Number} [options.delay=10] (Optional) The number of milliseconds
|
|
* before the next tap simulation happens. This is valid only when `times`
|
|
* is more than 1.
|
|
*
|
|
* Valid options properties for the `doubletap` gesture:
|
|
*
|
|
* @param {Array} [options.point] (Optional) Indicates the [x,y] coordinates
|
|
* where the doubletap should be simulated. Default is the center of the
|
|
* node element.
|
|
*
|
|
* Valid options properties for the `press` gesture:
|
|
*
|
|
* @param {Array} [options.point] (Optional) Indicates the [x,y] coordinates
|
|
* where the press should be simulated. Default is the center of the node
|
|
* element.
|
|
* @param {Number} [options.hold=3000] (Optional) The hold time in milliseconds.
|
|
* This is the time between `touchstart` and `touchend` event generation.
|
|
* Default is 3000ms (3 seconds).
|
|
*
|
|
* Valid options properties for the `move` gesture:
|
|
*
|
|
* @param {Object} [options.path] (Optional) Indicates the path of the finger
|
|
* movement. It's an object with three optional properties: `point`,
|
|
* `xdist` and `ydist`.
|
|
* @param {Array} [options.path.point] A starting point of the gesture.
|
|
* Default is the center of the node element.
|
|
* @param {Number} [options.path.xdist=200] A distance to move in pixels
|
|
* along the X axis. A negative distance value indicates moving left.
|
|
* @param {Number} [options.path.ydist=0] A distance to move in pixels
|
|
* along the Y axis. A negative distance value indicates moving up.
|
|
* @param {Number} [options.duration=1000] (Optional) The duration of the
|
|
* gesture in milliseconds.
|
|
*
|
|
* Valid options properties for the `flick` gesture:
|
|
*
|
|
* @param {Array} [options.point] (Optional) Indicates the [x, y] coordinates
|
|
* where the flick should be simulated. Default is the center of the
|
|
* node element.
|
|
* @param {String} [options.axis='x'] (Optional) Valid values are either
|
|
* "x" or "y". Indicates axis to move along. The flick can move to one of
|
|
* 4 directions(left, right, up and down).
|
|
* @param {Number} [options.distance=200] (Optional) Distance to move in pixels
|
|
* @param {Number} [options.duration=1000] (Optional) The duration of the
|
|
* gesture in milliseconds. User given value could be automatically
|
|
* adjusted by the framework if it is below the minimum velocity to be
|
|
* a flick gesture.
|
|
*
|
|
* Valid options properties for the `pinch` gesture:
|
|
*
|
|
* @param {Array} [options.center] (Optional) The center of the circle where
|
|
* two fingers are placed. Default is the center of the node element.
|
|
* @param {Number} [options.r1] (Required) Pixel radius of the start circle
|
|
* where 2 fingers will be on when the gesture starts. The circles are
|
|
* centered at the center of the element.
|
|
* @param {Number} [options.r2] (Required) Pixel radius of the end circle
|
|
* when this gesture ends.
|
|
* @param {Number} [options.duration=1000] (Optional) The duration of the
|
|
* gesture in milliseconds.
|
|
* @param {Number} [options.start=0] (Optional) Starting degree of the first
|
|
* finger. The value is relative to the path of the north. Default is 0
|
|
* (i.e., 12:00 on a clock).
|
|
* @param {Number} [options.rotation=0] (Optional) Degrees to rotate from
|
|
* the starting degree. A negative value means rotation to the
|
|
* counter-clockwise direction.
|
|
*
|
|
* Valid options properties for the `rotate` gesture:
|
|
*
|
|
* @param {Array} [options.center] (Optional) The center of the circle where
|
|
* two fingers are placed. Default is the center of the node element.
|
|
* @param {Number} [options.r1] (Optional) Pixel radius of the start circle
|
|
* where 2 fingers will be on when the gesture starts. The circles are
|
|
* centered at the center of the element. Default is a fourth of the node
|
|
* element width or height, whichever is smaller.
|
|
* @param {Number} [options.r2] (Optional) Pixel radius of the end circle
|
|
* when this gesture ends. Default is a fourth of the node element width or
|
|
* height, whichever is smaller.
|
|
* @param {Number} [options.duration=1000] (Optional) The duration of the
|
|
* gesture in milliseconds.
|
|
* @param {Number} [options.start=0] (Optional) Starting degree of the first
|
|
* finger. The value is relative to the path of the north. Default is 0
|
|
* (i.e., 12:00 on a clock).
|
|
* @param {Number} [options.rotation] (Required) Degrees to rotate from
|
|
* the starting degree. A negative value means rotation to the
|
|
* counter-clockwise direction.
|
|
*
|
|
* @param {Function} [cb] The callback to execute when the asynchronouse gesture
|
|
* simulation is completed.
|
|
* @param {Error} cb.err An error object if the simulation is failed.
|
|
* @for Event
|
|
* @static
|
|
*/
|
|
Y.Event.simulateGesture = function(node, name, options, cb) {
|
|
|
|
node = Y.one(node);
|
|
|
|
var sim = new Y.GestureSimulation(node);
|
|
name = name.toLowerCase();
|
|
|
|
if(!cb && Y.Lang.isFunction(options)) {
|
|
cb = options;
|
|
options = {};
|
|
}
|
|
|
|
options = options || {};
|
|
|
|
if (gestureNames[name]) {
|
|
switch(name) {
|
|
// single-touch: point gestures
|
|
case 'tap':
|
|
sim.tap(cb, options.point, options.times, options.hold, options.delay);
|
|
break;
|
|
case 'doubletap':
|
|
sim.tap(cb, options.point, 2);
|
|
break;
|
|
case 'press':
|
|
if(!Y.Lang.isNumber(options.hold)) {
|
|
options.hold = DEFAULTS.HOLD_PRESS;
|
|
} else if(options.hold < DEFAULTS.MIN_HOLD_PRESS) {
|
|
options.hold = DEFAULTS.MIN_HOLD_PRESS;
|
|
} else if(options.hold > DEFAULTS.MAX_HOLD_PRESS) {
|
|
options.hold = DEFAULTS.MAX_HOLD_PRESS;
|
|
}
|
|
sim.tap(cb, options.point, 1, options.hold);
|
|
break;
|
|
|
|
// single-touch: move gestures
|
|
case 'move':
|
|
sim.move(cb, options.path, options.duration);
|
|
break;
|
|
case 'flick':
|
|
sim.flick(cb, options.point, options.axis, options.distance,
|
|
options.duration);
|
|
break;
|
|
|
|
// multi-touch: pinch/rotation gestures
|
|
case 'pinch':
|
|
sim.pinch(cb, options.center, options.r1, options.r2,
|
|
options.duration, options.start, options.rotation);
|
|
break;
|
|
case 'rotate':
|
|
sim.rotate(cb, options.center, options.r1, options.r2,
|
|
options.duration, options.start, options.rotation);
|
|
break;
|
|
}
|
|
} else {
|
|
Y.error(NAME+': Not a supported gesture simulation: '+name);
|
|
}
|
|
};
|
|
|
|
|
|
}, '3.17.2', {"requires": ["async-queue", "event-simulate", "node-screen"]});
|
|
|