src/cm/media/js/lib/yui/yui3-3.15.0/build/gesture-simulate/gesture-simulate-debug.js
changeset 602 e16a97fb364a
equal deleted inserted replaced
601:d334a616c023 602:e16a97fb364a
       
     1 YUI.add('gesture-simulate', function (Y, NAME) {
       
     2 
       
     3 /**
       
     4  * Simulate high-level user gestures by generating a set of native DOM events.
       
     5  *
       
     6  * @module gesture-simulate
       
     7  * @requires event-simulate, async-queue, node-screen
       
     8  */
       
     9 
       
    10 var NAME = "gesture-simulate",
       
    11 
       
    12     // 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)
       
    13     SUPPORTS_TOUCH = ((Y.config.win && ("ontouchstart" in Y.config.win)) && !(Y.UA.phantomjs) && !(Y.UA.chrome && Y.UA.chrome < 6)),
       
    14 
       
    15     gestureNames = {
       
    16         tap: 1,
       
    17         doubletap: 1,
       
    18         press: 1,
       
    19         move: 1,
       
    20         flick: 1,
       
    21         pinch: 1,
       
    22         rotate: 1
       
    23     },
       
    24 
       
    25     touchEvents = {
       
    26         touchstart: 1,
       
    27         touchmove: 1,
       
    28         touchend: 1,
       
    29         touchcancel: 1
       
    30     },
       
    31 
       
    32     document = Y.config.doc,
       
    33     emptyTouchList,
       
    34 
       
    35     EVENT_INTERVAL = 20,        // 20ms
       
    36     START_PAGEX,                // will be adjusted to the node element center
       
    37     START_PAGEY,                // will be adjusted to the node element center
       
    38 
       
    39     // defaults that user can override.
       
    40     DEFAULTS = {
       
    41         // tap gestures
       
    42         HOLD_TAP: 10,           // 10ms
       
    43         DELAY_TAP: 10,          // 10ms
       
    44 
       
    45         // press gesture
       
    46         HOLD_PRESS: 3000,       // 3sec
       
    47         MIN_HOLD_PRESS: 1000,   // 1sec
       
    48         MAX_HOLD_PRESS: 60000,  // 1min
       
    49 
       
    50         // move gesture
       
    51         DISTANCE_MOVE: 200,     // 200 pixels
       
    52         DURATION_MOVE: 1000,    // 1sec
       
    53         MAX_DURATION_MOVE: 5000,// 5sec
       
    54 
       
    55         // flick gesture
       
    56         MIN_VELOCITY_FLICK: 1.3,
       
    57         DISTANCE_FLICK: 200,     // 200 pixels
       
    58         DURATION_FLICK: 1000,    // 1sec
       
    59         MAX_DURATION_FLICK: 5000,// 5sec
       
    60 
       
    61         // pinch/rotation
       
    62         DURATION_PINCH: 1000     // 1sec
       
    63     },
       
    64 
       
    65     TOUCH_START = 'touchstart',
       
    66     TOUCH_MOVE = 'touchmove',
       
    67     TOUCH_END = 'touchend',
       
    68 
       
    69     GESTURE_START = 'gesturestart',
       
    70     GESTURE_CHANGE = 'gesturechange',
       
    71     GESTURE_END = 'gestureend',
       
    72 
       
    73     MOUSE_UP    = 'mouseup',
       
    74     MOUSE_MOVE  = 'mousemove',
       
    75     MOUSE_DOWN  = 'mousedown',
       
    76     MOUSE_CLICK = 'click',
       
    77     MOUSE_DBLCLICK = 'dblclick',
       
    78 
       
    79     X_AXIS = 'x',
       
    80     Y_AXIS = 'y';
       
    81 
       
    82 
       
    83 function Simulations(node) {
       
    84     if(!node) {
       
    85         Y.error(NAME+': invalid target node');
       
    86     }
       
    87     this.node = node;
       
    88     this.target = Y.Node.getDOMNode(node);
       
    89 
       
    90     var startXY = this.node.getXY(),
       
    91         dims = this._getDims();
       
    92 
       
    93     START_PAGEX = startXY[0] + (dims[0])/2;
       
    94     START_PAGEY = startXY[1] + (dims[1])/2;
       
    95 }
       
    96 
       
    97 Simulations.prototype = {
       
    98 
       
    99     /**
       
   100      * Helper method to convert a degree to a radian.
       
   101      *
       
   102      * @method _toRadian
       
   103      * @private
       
   104      * @param {Number} deg A degree to be converted to a radian.
       
   105      * @return {Number} The degree in radian.
       
   106      */
       
   107     _toRadian: function(deg) {
       
   108         return deg * (Math.PI/180);
       
   109     },
       
   110 
       
   111     /**
       
   112      * Helper method to get height/width while accounting for
       
   113      * rotation/scale transforms where possible by using the
       
   114      * bounding client rectangle height/width instead of the
       
   115      * offsetWidth/Height which region uses.
       
   116      * @method _getDims
       
   117      * @private
       
   118      * @return {Array} Array with [height, width]
       
   119      */
       
   120     _getDims : function() {
       
   121         var region,
       
   122             width,
       
   123             height;
       
   124 
       
   125         // Ideally, this should be in DOM somewhere.
       
   126         if (this.target.getBoundingClientRect) {
       
   127             region = this.target.getBoundingClientRect();
       
   128 
       
   129             if ("height" in region) {
       
   130                 height = region.height;
       
   131             } else {
       
   132                 // IE7,8 has getBCR, but no height.
       
   133                 height = Math.abs(region.bottom - region.top);
       
   134             }
       
   135 
       
   136             if ("width" in region) {
       
   137                 width = region.width;
       
   138             } else {
       
   139                 // IE7,8 has getBCR, but no width.
       
   140                 width = Math.abs(region.right - region.left);
       
   141             }
       
   142         } else {
       
   143             region = this.node.get("region");
       
   144             width = region.width;
       
   145             height = region.height;
       
   146         }
       
   147 
       
   148         return [width, height];
       
   149     },
       
   150 
       
   151     /**
       
   152      * Helper method to convert a point relative to the node element into
       
   153      * the point in the page coordination.
       
   154      *
       
   155      * @method _calculateDefaultPoint
       
   156      * @private
       
   157      * @param {Array} point A point relative to the node element.
       
   158      * @return {Array} The same point in the page coordination.
       
   159      */
       
   160     _calculateDefaultPoint: function(point) {
       
   161 
       
   162         var height;
       
   163 
       
   164         if(!Y.Lang.isArray(point) || point.length === 0) {
       
   165             point = [START_PAGEX, START_PAGEY];
       
   166         } else {
       
   167             if(point.length == 1) {
       
   168                 height = this._getDims[1];
       
   169                 point[1] = height/2;
       
   170             }
       
   171             // convert to page(viewport) coordination
       
   172             point[0] = this.node.getX() + point[0];
       
   173             point[1] = this.node.getY() + point[1];
       
   174         }
       
   175 
       
   176         return point;
       
   177     },
       
   178 
       
   179     /**
       
   180      * The "rotate" and "pinch" methods are essencially same with the exact same
       
   181      * arguments. Only difference is the required parameters. The rotate method
       
   182      * requires "rotation" parameter while the pinch method requires "startRadius"
       
   183      * and "endRadius" parameters.
       
   184      *
       
   185      * @method rotate
       
   186      * @param {Function} cb The callback to execute when the gesture simulation
       
   187      *      is completed.
       
   188      * @param {Array} center A center point where the pinch gesture of two fingers
       
   189      *      should happen. It is relative to the top left corner of the target
       
   190      *      node element.
       
   191      * @param {Number} startRadius A radius of start circle where 2 fingers are
       
   192      *      on when the gesture starts. This is optional. The default is a fourth of
       
   193      *      either target node width or height whichever is smaller.
       
   194      * @param {Number} endRadius A radius of end circle where 2 fingers will be on when
       
   195      *      the pinch or spread gestures are completed. This is optional.
       
   196      *      The default is a fourth of either target node width or height whichever is less.
       
   197      * @param {Number} duration A duration of the gesture in millisecond.
       
   198      * @param {Number} start A start angle(0 degree at 12 o'clock) where the
       
   199      *      gesture should start. Default is 0.
       
   200      * @param {Number} rotation A rotation in degree. It is required.
       
   201      */
       
   202     rotate: function(cb, center, startRadius, endRadius, duration, start, rotation) {
       
   203         var radius,
       
   204             r1 = startRadius,   // optional
       
   205             r2 = endRadius;     // optional
       
   206 
       
   207         if(!Y.Lang.isNumber(r1) || !Y.Lang.isNumber(r2) || r1<0 || r2<0) {
       
   208             radius = (this.target.offsetWidth < this.target.offsetHeight)?
       
   209                 this.target.offsetWidth/4 : this.target.offsetHeight/4;
       
   210             r1 = radius;
       
   211             r2 = radius;
       
   212         }
       
   213 
       
   214         // required
       
   215         if(!Y.Lang.isNumber(rotation)) {
       
   216             Y.error(NAME+'Invalid rotation detected.');
       
   217         }
       
   218 
       
   219         this.pinch(cb, center, r1, r2, duration, start, rotation);
       
   220     },
       
   221 
       
   222     /**
       
   223      * The "rotate" and "pinch" methods are essencially same with the exact same
       
   224      * arguments. Only difference is the required parameters. The rotate method
       
   225      * requires "rotation" parameter while the pinch method requires "startRadius"
       
   226      * and "endRadius" parameters.
       
   227      *
       
   228      * The "pinch" gesture can simulate various 2 finger gestures such as pinch,
       
   229      * spread and/or rotation. The "startRadius" and "endRadius" are required.
       
   230      * If endRadius is larger than startRadius, it becomes a spread gesture
       
   231      * otherwise a pinch gesture.
       
   232      *
       
   233      * @method pinch
       
   234      * @param {Function} cb The callback to execute when the gesture simulation
       
   235      *      is completed.
       
   236      * @param {Array} center A center point where the pinch gesture of two fingers
       
   237      *      should happen. It is relative to the top left corner of the target
       
   238      *      node element.
       
   239      * @param {Number} startRadius A radius of start circle where 2 fingers are
       
   240      *      on when the gesture starts. This paramenter is required.
       
   241      * @param {Number} endRadius A radius of end circle where 2 fingers will be on when
       
   242      *      the pinch or spread gestures are completed. This parameter is required.
       
   243      * @param {Number} duration A duration of the gesture in millisecond.
       
   244      * @param {Number} start A start angle(0 degree at 12 o'clock) where the
       
   245      *      gesture should start. Default is 0.
       
   246      * @param {Number} rotation If rotation is desired during the pinch or
       
   247      *      spread gestures, this parameter can be used. Default is 0 degree.
       
   248      */
       
   249     pinch: function(cb, center, startRadius, endRadius, duration, start, rotation) {
       
   250         var eventQueue,
       
   251             i,
       
   252             interval = EVENT_INTERVAL,
       
   253             touches,
       
   254             id = 0,
       
   255             r1 = startRadius,   // required
       
   256             r2 = endRadius,     // required
       
   257             radiusPerStep,
       
   258             centerX, centerY,
       
   259             startScale, endScale, scalePerStep,
       
   260             startRot, endRot, rotPerStep,
       
   261             path1 = {start: [], end: []}, // paths for 1st and 2nd fingers.
       
   262             path2 = {start: [], end: []},
       
   263             steps,
       
   264             touchMove;
       
   265 
       
   266         center = this._calculateDefaultPoint(center);
       
   267 
       
   268         if(!Y.Lang.isNumber(r1) || !Y.Lang.isNumber(r2) || r1<0 || r2<0) {
       
   269             Y.error(NAME+'Invalid startRadius and endRadius detected.');
       
   270         }
       
   271 
       
   272         if(!Y.Lang.isNumber(duration) || duration <= 0) {
       
   273             duration = DEFAULTS.DURATION_PINCH;
       
   274         }
       
   275 
       
   276         if(!Y.Lang.isNumber(start)) {
       
   277             start = 0.0;
       
   278         } else {
       
   279             start = start%360;
       
   280             while(start < 0) {
       
   281                 start += 360;
       
   282             }
       
   283         }
       
   284 
       
   285         if(!Y.Lang.isNumber(rotation)) {
       
   286             rotation = 0.0;
       
   287         }
       
   288 
       
   289         Y.AsyncQueue.defaults.timeout = interval;
       
   290         eventQueue = new Y.AsyncQueue();
       
   291 
       
   292         // range determination
       
   293         centerX = center[0];
       
   294         centerY = center[1];
       
   295 
       
   296         startRot = start;
       
   297         endRot = start + rotation;
       
   298 
       
   299         // 1st finger path
       
   300         path1.start = [
       
   301             centerX + r1*Math.sin(this._toRadian(startRot)),
       
   302             centerY - r1*Math.cos(this._toRadian(startRot))
       
   303         ];
       
   304         path1.end   = [
       
   305             centerX + r2*Math.sin(this._toRadian(endRot)),
       
   306             centerY - r2*Math.cos(this._toRadian(endRot))
       
   307         ];
       
   308 
       
   309         // 2nd finger path
       
   310         path2.start = [
       
   311             centerX - r1*Math.sin(this._toRadian(startRot)),
       
   312             centerY + r1*Math.cos(this._toRadian(startRot))
       
   313         ];
       
   314         path2.end   = [
       
   315             centerX - r2*Math.sin(this._toRadian(endRot)),
       
   316             centerY + r2*Math.cos(this._toRadian(endRot))
       
   317         ];
       
   318 
       
   319         startScale = 1.0;
       
   320         endScale = endRadius/startRadius;
       
   321 
       
   322         // touch/gesture start
       
   323         eventQueue.add({
       
   324             fn: function() {
       
   325                 var coord1, coord2, coord, touches;
       
   326 
       
   327                 // coordinate for each touch object.
       
   328                 coord1 = {
       
   329                     pageX: path1.start[0],
       
   330                     pageY: path1.start[1],
       
   331                     clientX: path1.start[0],
       
   332                     clientY: path1.start[1]
       
   333                 };
       
   334                 coord2 = {
       
   335                     pageX: path2.start[0],
       
   336                     pageY: path2.start[1],
       
   337                     clientX: path2.start[0],
       
   338                     clientY: path2.start[1]
       
   339                 };
       
   340                 touches = this._createTouchList([Y.merge({
       
   341                     identifier: (id++)
       
   342                 }, coord1), Y.merge({
       
   343                     identifier: (id++)
       
   344                 }, coord2)]);
       
   345 
       
   346                 // coordinate for top level event
       
   347                 coord = {
       
   348                     pageX: (path1.start[0] + path2.start[0])/2,
       
   349                     pageY: (path1.start[0] + path2.start[1])/2,
       
   350                     clientX: (path1.start[0] + path2.start[0])/2,
       
   351                     clientY: (path1.start[0] + path2.start[1])/2
       
   352                 };
       
   353 
       
   354                 this._simulateEvent(this.target, TOUCH_START, Y.merge({
       
   355                     touches: touches,
       
   356                     targetTouches: touches,
       
   357                     changedTouches: touches,
       
   358                     scale: startScale,
       
   359                     rotation: startRot
       
   360                 }, coord));
       
   361 
       
   362                 if(Y.UA.ios >= 2.0) {
       
   363                     /* gesture starts when the 2nd finger touch starts.
       
   364                     * The implementation will fire 1 touch start event for both fingers,
       
   365                     * simulating 2 fingers touched on the screen at the same time.
       
   366                     */
       
   367                     this._simulateEvent(this.target, GESTURE_START, Y.merge({
       
   368                         scale: startScale,
       
   369                         rotation: startRot
       
   370                     }, coord));
       
   371                 }
       
   372             },
       
   373             timeout: 0,
       
   374             context: this
       
   375         });
       
   376 
       
   377         // gesture change
       
   378         steps = Math.floor(duration/interval);
       
   379         radiusPerStep = (r2 - r1)/steps;
       
   380         scalePerStep = (endScale - startScale)/steps;
       
   381         rotPerStep = (endRot - startRot)/steps;
       
   382 
       
   383         touchMove = function(step) {
       
   384             var radius = r1 + (radiusPerStep)*step,
       
   385                 px1 = centerX + radius*Math.sin(this._toRadian(startRot + rotPerStep*step)),
       
   386                 py1 = centerY - radius*Math.cos(this._toRadian(startRot + rotPerStep*step)),
       
   387                 px2 = centerX - radius*Math.sin(this._toRadian(startRot + rotPerStep*step)),
       
   388                 py2 = centerY + radius*Math.cos(this._toRadian(startRot + rotPerStep*step)),
       
   389                 px = (px1+px2)/2,
       
   390                 py = (py1+py2)/2,
       
   391                 coord1, coord2, coord, touches;
       
   392 
       
   393             // coordinate for each touch object.
       
   394             coord1 = {
       
   395                 pageX: px1,
       
   396                 pageY: py1,
       
   397                 clientX: px1,
       
   398                 clientY: py1
       
   399             };
       
   400             coord2 = {
       
   401                 pageX: px2,
       
   402                 pageY: py2,
       
   403                 clientX: px2,
       
   404                 clientY: py2
       
   405             };
       
   406             touches = this._createTouchList([Y.merge({
       
   407                 identifier: (id++)
       
   408             }, coord1), Y.merge({
       
   409                 identifier: (id++)
       
   410             }, coord2)]);
       
   411 
       
   412             // coordinate for top level event
       
   413             coord = {
       
   414                 pageX: px,
       
   415                 pageY: py,
       
   416                 clientX: px,
       
   417                 clientY: py
       
   418             };
       
   419 
       
   420             this._simulateEvent(this.target, TOUCH_MOVE, Y.merge({
       
   421                 touches: touches,
       
   422                 targetTouches: touches,
       
   423                 changedTouches: touches,
       
   424                 scale: startScale + scalePerStep*step,
       
   425                 rotation: startRot + rotPerStep*step
       
   426             }, coord));
       
   427 
       
   428             if(Y.UA.ios >= 2.0) {
       
   429                 this._simulateEvent(this.target, GESTURE_CHANGE, Y.merge({
       
   430                     scale: startScale + scalePerStep*step,
       
   431                     rotation: startRot + rotPerStep*step
       
   432                 }, coord));
       
   433             }
       
   434         };
       
   435 
       
   436         for (i=0; i < steps; i++) {
       
   437             eventQueue.add({
       
   438                 fn: touchMove,
       
   439                 args: [i],
       
   440                 context: this
       
   441             });
       
   442         }
       
   443 
       
   444         // gesture end
       
   445         eventQueue.add({
       
   446             fn: function() {
       
   447                 var emptyTouchList = this._getEmptyTouchList(),
       
   448                     coord1, coord2, coord, touches;
       
   449 
       
   450                 // coordinate for each touch object.
       
   451                 coord1 = {
       
   452                     pageX: path1.end[0],
       
   453                     pageY: path1.end[1],
       
   454                     clientX: path1.end[0],
       
   455                     clientY: path1.end[1]
       
   456                 };
       
   457                 coord2 = {
       
   458                     pageX: path2.end[0],
       
   459                     pageY: path2.end[1],
       
   460                     clientX: path2.end[0],
       
   461                     clientY: path2.end[1]
       
   462                 };
       
   463                 touches = this._createTouchList([Y.merge({
       
   464                     identifier: (id++)
       
   465                 }, coord1), Y.merge({
       
   466                     identifier: (id++)
       
   467                 }, coord2)]);
       
   468 
       
   469                 // coordinate for top level event
       
   470                 coord = {
       
   471                     pageX: (path1.end[0] + path2.end[0])/2,
       
   472                     pageY: (path1.end[0] + path2.end[1])/2,
       
   473                     clientX: (path1.end[0] + path2.end[0])/2,
       
   474                     clientY: (path1.end[0] + path2.end[1])/2
       
   475                 };
       
   476 
       
   477                 if(Y.UA.ios >= 2.0) {
       
   478                     this._simulateEvent(this.target, GESTURE_END, Y.merge({
       
   479                         scale: endScale,
       
   480                         rotation: endRot
       
   481                     }, coord));
       
   482                 }
       
   483 
       
   484                 this._simulateEvent(this.target, TOUCH_END, Y.merge({
       
   485                     touches: emptyTouchList,
       
   486                     targetTouches: emptyTouchList,
       
   487                     changedTouches: touches,
       
   488                     scale: endScale,
       
   489                     rotation: endRot
       
   490                 }, coord));
       
   491             },
       
   492             context: this
       
   493         });
       
   494 
       
   495         if(cb && Y.Lang.isFunction(cb)) {
       
   496             eventQueue.add({
       
   497                 fn: cb,
       
   498 
       
   499                 // by default, the callback runs the node context where
       
   500                 // simulateGesture method is called.
       
   501                 context: this.node
       
   502 
       
   503                 //TODO: Use args to pass error object as 1st param if there is an error.
       
   504                 //args:
       
   505             });
       
   506         }
       
   507 
       
   508         eventQueue.run();
       
   509     },
       
   510 
       
   511     /**
       
   512      * The "tap" gesture can be used for various single touch point gestures
       
   513      * such as single tap, N number of taps, long press. The default is a single
       
   514      * tap.
       
   515      *
       
   516      * @method tap
       
   517      * @param {Function} cb The callback to execute when the gesture simulation
       
   518      *      is completed.
       
   519      * @param {Array} point A point(relative to the top left corner of the
       
   520      *      target node element) where the tap gesture should start. The default
       
   521      *      is the center of the taget node.
       
   522      * @param {Number} times The number of taps. Default is 1.
       
   523      * @param {Number} hold The hold time in milliseconds between "touchstart" and
       
   524      *      "touchend" event generation. Default is 10ms.
       
   525      * @param {Number} delay The time gap in millisecond between taps if this
       
   526      *      gesture has more than 1 tap. Default is 10ms.
       
   527      */
       
   528     tap: function(cb, point, times, hold, delay) {
       
   529         var eventQueue = new Y.AsyncQueue(),
       
   530             emptyTouchList = this._getEmptyTouchList(),
       
   531             touches,
       
   532             coord,
       
   533             i,
       
   534             touchStart,
       
   535             touchEnd;
       
   536 
       
   537         point = this._calculateDefaultPoint(point);
       
   538 
       
   539         if(!Y.Lang.isNumber(times) || times < 1) {
       
   540             times = 1;
       
   541         }
       
   542 
       
   543         if(!Y.Lang.isNumber(hold)) {
       
   544             hold = DEFAULTS.HOLD_TAP;
       
   545         }
       
   546 
       
   547         if(!Y.Lang.isNumber(delay)) {
       
   548             delay = DEFAULTS.DELAY_TAP;
       
   549         }
       
   550 
       
   551         coord = {
       
   552             pageX: point[0],
       
   553             pageY: point[1],
       
   554             clientX: point[0],
       
   555             clientY: point[1]
       
   556         };
       
   557 
       
   558         touches = this._createTouchList([Y.merge({identifier: 0}, coord)]);
       
   559 
       
   560         touchStart = function() {
       
   561             this._simulateEvent(this.target, TOUCH_START, Y.merge({
       
   562                 touches: touches,
       
   563                 targetTouches: touches,
       
   564                 changedTouches: touches
       
   565             }, coord));
       
   566         };
       
   567 
       
   568         touchEnd = function() {
       
   569             this._simulateEvent(this.target, TOUCH_END, Y.merge({
       
   570                 touches: emptyTouchList,
       
   571                 targetTouches: emptyTouchList,
       
   572                 changedTouches: touches
       
   573             }, coord));
       
   574         };
       
   575 
       
   576         for (i=0; i < times; i++) {
       
   577             eventQueue.add({
       
   578                 fn: touchStart,
       
   579                 context: this,
       
   580                 timeout: (i === 0)? 0 : delay
       
   581             });
       
   582 
       
   583             eventQueue.add({
       
   584                 fn: touchEnd,
       
   585                 context: this,
       
   586                 timeout: hold
       
   587             });
       
   588         }
       
   589 
       
   590         if(times > 1 && !SUPPORTS_TOUCH) {
       
   591             eventQueue.add({
       
   592                 fn: function() {
       
   593                     this._simulateEvent(this.target, MOUSE_DBLCLICK, coord);
       
   594                 },
       
   595                 context: this
       
   596             });
       
   597         }
       
   598 
       
   599         if(cb && Y.Lang.isFunction(cb)) {
       
   600             eventQueue.add({
       
   601                 fn: cb,
       
   602 
       
   603                 // by default, the callback runs the node context where
       
   604                 // simulateGesture method is called.
       
   605                 context: this.node
       
   606 
       
   607                 //TODO: Use args to pass error object as 1st param if there is an error.
       
   608                 //args:
       
   609             });
       
   610         }
       
   611 
       
   612         eventQueue.run();
       
   613     },
       
   614 
       
   615     /**
       
   616      * The "flick" gesture is a specialized "move" that has some velocity
       
   617      * and the movement always runs either x or y axis. The velocity is calculated
       
   618      * with "distance" and "duration" arguments. If the calculated velocity is
       
   619      * below than the minimum velocity, the given duration will be ignored and
       
   620      * new duration will be created to make a valid flick gesture.
       
   621      *
       
   622      * @method flick
       
   623      * @param {Function} cb The callback to execute when the gesture simulation
       
   624      *      is completed.
       
   625      * @param {Array} point A point(relative to the top left corner of the
       
   626      *      target node element) where the flick gesture should start. The default
       
   627      *      is the center of the taget node.
       
   628      * @param {String} axis Either "x" or "y".
       
   629      * @param {Number} distance A distance in pixels to flick.
       
   630      * @param {Number} duration A duration of the gesture in millisecond.
       
   631      *
       
   632      */
       
   633     flick: function(cb, point, axis, distance, duration) {
       
   634         var path;
       
   635 
       
   636         point = this._calculateDefaultPoint(point);
       
   637 
       
   638         if(!Y.Lang.isString(axis)) {
       
   639             axis = X_AXIS;
       
   640         } else {
       
   641             axis = axis.toLowerCase();
       
   642             if(axis !== X_AXIS && axis !== Y_AXIS) {
       
   643                 Y.error(NAME+'(flick): Only x or y axis allowed');
       
   644             }
       
   645         }
       
   646 
       
   647         if(!Y.Lang.isNumber(distance)) {
       
   648             distance = DEFAULTS.DISTANCE_FLICK;
       
   649         }
       
   650 
       
   651         if(!Y.Lang.isNumber(duration)){
       
   652             duration = DEFAULTS.DURATION_FLICK; // ms
       
   653         } else {
       
   654             if(duration > DEFAULTS.MAX_DURATION_FLICK) {
       
   655                 duration = DEFAULTS.MAX_DURATION_FLICK;
       
   656             }
       
   657         }
       
   658 
       
   659         /*
       
   660          * Check if too slow for a flick.
       
   661          * Adjust duration if the calculated velocity is less than
       
   662          * the minimum velcocity to be claimed as a flick.
       
   663          */
       
   664         if(Math.abs(distance)/duration < DEFAULTS.MIN_VELOCITY_FLICK) {
       
   665             duration = Math.abs(distance)/DEFAULTS.MIN_VELOCITY_FLICK;
       
   666         }
       
   667 
       
   668         path = {
       
   669             start: Y.clone(point),
       
   670             end: [
       
   671                 (axis === X_AXIS) ? point[0]+distance : point[0],
       
   672                 (axis === Y_AXIS) ? point[1]+distance : point[1]
       
   673             ]
       
   674         };
       
   675 
       
   676         this._move(cb, path, duration);
       
   677     },
       
   678 
       
   679     /**
       
   680      * The "move" gesture simulate the movement of any direction between
       
   681      * the straight line of start and end point for the given duration.
       
   682      * The path argument is an object with "point", "xdist" and "ydist" properties.
       
   683      * The "point" property is an array with x and y coordinations(relative to the
       
   684      * top left corner of the target node element) while "xdist" and "ydist"
       
   685      * properties are used for the distance along the x and y axis. A negative
       
   686      * distance number can be used to drag either left or up direction.
       
   687      *
       
   688      * If no arguments are given, it will simulate the default move, which
       
   689      * is moving 200 pixels from the center of the element to the positive X-axis
       
   690      * direction for 1 sec.
       
   691      *
       
   692      * @method move
       
   693      * @param {Function} cb The callback to execute when the gesture simulation
       
   694      *      is completed.
       
   695      * @param {Object} path An object with "point", "xdist" and "ydist".
       
   696      * @param {Number} duration A duration of the gesture in millisecond.
       
   697      */
       
   698     move: function(cb, path, duration) {
       
   699         var convertedPath;
       
   700 
       
   701         if(!Y.Lang.isObject(path)) {
       
   702             path = {
       
   703                 point: this._calculateDefaultPoint([]),
       
   704                 xdist: DEFAULTS.DISTANCE_MOVE,
       
   705                 ydist: 0
       
   706             };
       
   707         } else {
       
   708             // convert to the page coordination
       
   709             if(!Y.Lang.isArray(path.point)) {
       
   710                 path.point = this._calculateDefaultPoint([]);
       
   711             } else {
       
   712                 path.point = this._calculateDefaultPoint(path.point);
       
   713             }
       
   714 
       
   715             if(!Y.Lang.isNumber(path.xdist)) {
       
   716                 path.xdist = DEFAULTS.DISTANCE_MOVE;
       
   717             }
       
   718 
       
   719             if(!Y.Lang.isNumber(path.ydist)) {
       
   720                 path.ydist = 0;
       
   721             }
       
   722         }
       
   723 
       
   724         if(!Y.Lang.isNumber(duration)){
       
   725             duration = DEFAULTS.DURATION_MOVE; // ms
       
   726         } else {
       
   727             if(duration > DEFAULTS.MAX_DURATION_MOVE) {
       
   728                 duration = DEFAULTS.MAX_DURATION_MOVE;
       
   729             }
       
   730         }
       
   731 
       
   732         convertedPath = {
       
   733             start: Y.clone(path.point),
       
   734             end: [path.point[0]+path.xdist, path.point[1]+path.ydist]
       
   735         };
       
   736 
       
   737         this._move(cb, convertedPath, duration);
       
   738     },
       
   739 
       
   740     /**
       
   741      * A base method on top of "move" and "flick" methods. The method takes
       
   742      * the path with start/end properties and duration to generate a set of
       
   743      * touch events for the movement gesture.
       
   744      *
       
   745      * @method _move
       
   746      * @private
       
   747      * @param {Function} cb The callback to execute when the gesture simulation
       
   748      *      is completed.
       
   749      * @param {Object} path An object with "start" and "end" properties. Each
       
   750      *      property should be an array with x and y coordination (e.g. start: [100, 50])
       
   751      * @param {Number} duration A duration of the gesture in millisecond.
       
   752      */
       
   753     _move: function(cb, path, duration) {
       
   754         var eventQueue,
       
   755             i,
       
   756             interval = EVENT_INTERVAL,
       
   757             steps, stepX, stepY,
       
   758             id = 0,
       
   759             touchMove;
       
   760 
       
   761         if(!Y.Lang.isNumber(duration)){
       
   762             duration = DEFAULTS.DURATION_MOVE; // ms
       
   763         } else {
       
   764             if(duration > DEFAULTS.MAX_DURATION_MOVE) {
       
   765                 duration = DEFAULTS.MAX_DURATION_MOVE;
       
   766             }
       
   767         }
       
   768 
       
   769         if(!Y.Lang.isObject(path)) {
       
   770             path = {
       
   771                 start: [
       
   772                     START_PAGEX,
       
   773                     START_PAGEY
       
   774                 ],
       
   775                 end: [
       
   776                     START_PAGEX + DEFAULTS.DISTANCE_MOVE,
       
   777                     START_PAGEY
       
   778                 ]
       
   779             };
       
   780         } else {
       
   781             if(!Y.Lang.isArray(path.start)) {
       
   782                 path.start = [
       
   783                     START_PAGEX,
       
   784                     START_PAGEY
       
   785                 ];
       
   786             }
       
   787             if(!Y.Lang.isArray(path.end)) {
       
   788                 path.end = [
       
   789                     START_PAGEX + DEFAULTS.DISTANCE_MOVE,
       
   790                     START_PAGEY
       
   791                 ];
       
   792             }
       
   793         }
       
   794 
       
   795         Y.AsyncQueue.defaults.timeout = interval;
       
   796         eventQueue = new Y.AsyncQueue();
       
   797 
       
   798         // start
       
   799         eventQueue.add({
       
   800             fn: function() {
       
   801                 var coord = {
       
   802                         pageX: path.start[0],
       
   803                         pageY: path.start[1],
       
   804                         clientX: path.start[0],
       
   805                         clientY: path.start[1]
       
   806                     },
       
   807                     touches = this._createTouchList([
       
   808                         Y.merge({identifier: (id++)}, coord)
       
   809                     ]);
       
   810 
       
   811                 this._simulateEvent(this.target, TOUCH_START, Y.merge({
       
   812                     touches: touches,
       
   813                     targetTouches: touches,
       
   814                     changedTouches: touches
       
   815                 }, coord));
       
   816             },
       
   817             timeout: 0,
       
   818             context: this
       
   819         });
       
   820 
       
   821         // move
       
   822         steps = Math.floor(duration/interval);
       
   823         stepX = (path.end[0] - path.start[0])/steps;
       
   824         stepY = (path.end[1] - path.start[1])/steps;
       
   825 
       
   826         touchMove = function(step) {
       
   827             var px = path.start[0]+(stepX * step),
       
   828                 py = path.start[1]+(stepY * step),
       
   829                 coord = {
       
   830                     pageX: px,
       
   831                     pageY: py,
       
   832                     clientX: px,
       
   833                     clientY: py
       
   834                 },
       
   835                 touches = this._createTouchList([
       
   836                     Y.merge({identifier: (id++)}, coord)
       
   837                 ]);
       
   838 
       
   839             this._simulateEvent(this.target, TOUCH_MOVE, Y.merge({
       
   840                 touches: touches,
       
   841                 targetTouches: touches,
       
   842                 changedTouches: touches
       
   843             }, coord));
       
   844         };
       
   845 
       
   846         for (i=0; i < steps; i++) {
       
   847             eventQueue.add({
       
   848                 fn: touchMove,
       
   849                 args: [i],
       
   850                 context: this
       
   851             });
       
   852         }
       
   853 
       
   854         // last move
       
   855         eventQueue.add({
       
   856             fn: function() {
       
   857                 var coord = {
       
   858                         pageX: path.end[0],
       
   859                         pageY: path.end[1],
       
   860                         clientX: path.end[0],
       
   861                         clientY: path.end[1]
       
   862                     },
       
   863                     touches = this._createTouchList([
       
   864                         Y.merge({identifier: id}, coord)
       
   865                     ]);
       
   866 
       
   867                 this._simulateEvent(this.target, TOUCH_MOVE, Y.merge({
       
   868                     touches: touches,
       
   869                     targetTouches: touches,
       
   870                     changedTouches: touches
       
   871                 }, coord));
       
   872             },
       
   873             timeout: 0,
       
   874             context: this
       
   875         });
       
   876 
       
   877         // end
       
   878         eventQueue.add({
       
   879             fn: function() {
       
   880                 var coord = {
       
   881                     pageX: path.end[0],
       
   882                     pageY: path.end[1],
       
   883                     clientX: path.end[0],
       
   884                     clientY: path.end[1]
       
   885                 },
       
   886                 emptyTouchList = this._getEmptyTouchList(),
       
   887                 touches = this._createTouchList([
       
   888                     Y.merge({identifier: id}, coord)
       
   889                 ]);
       
   890 
       
   891                 this._simulateEvent(this.target, TOUCH_END, Y.merge({
       
   892                     touches: emptyTouchList,
       
   893                     targetTouches: emptyTouchList,
       
   894                     changedTouches: touches
       
   895                 }, coord));
       
   896             },
       
   897             context: this
       
   898         });
       
   899 
       
   900         if(cb && Y.Lang.isFunction(cb)) {
       
   901             eventQueue.add({
       
   902                 fn: cb,
       
   903 
       
   904                 // by default, the callback runs the node context where
       
   905                 // simulateGesture method is called.
       
   906                 context: this.node
       
   907 
       
   908                 //TODO: Use args to pass error object as 1st param if there is an error.
       
   909                 //args:
       
   910             });
       
   911         }
       
   912 
       
   913         eventQueue.run();
       
   914     },
       
   915 
       
   916     /**
       
   917      * Helper method to return a singleton instance of empty touch list.
       
   918      *
       
   919      * @method _getEmptyTouchList
       
   920      * @private
       
   921      * @return {TouchList | Array} An empty touch list object.
       
   922      */
       
   923     _getEmptyTouchList: function() {
       
   924         if(!emptyTouchList) {
       
   925             emptyTouchList = this._createTouchList([]);
       
   926         }
       
   927 
       
   928         return emptyTouchList;
       
   929     },
       
   930 
       
   931     /**
       
   932      * Helper method to convert an array with touch points to TouchList object as
       
   933      * defined in http://www.w3.org/TR/touch-events/
       
   934      *
       
   935      * @method _createTouchList
       
   936      * @private
       
   937      * @param {Array} touchPoints
       
   938      * @return {TouchList | Array} If underlaying platform support creating touch list
       
   939      *      a TouchList object will be returned otherwise a fake Array object
       
   940      *      will be returned.
       
   941      */
       
   942     _createTouchList: function(touchPoints) {
       
   943         /*
       
   944         * Android 4.0.3 emulator:
       
   945         * Native touch api supported starting in version 4.0 (Ice Cream Sandwich).
       
   946         * However the support seems limited. In Android 4.0.3 emulator, I got
       
   947         * "TouchList is not defined".
       
   948         */
       
   949         var touches = [],
       
   950             touchList,
       
   951             self = this;
       
   952 
       
   953         if(!!touchPoints && Y.Lang.isArray(touchPoints)) {
       
   954             if(Y.UA.android && Y.UA.android >= 4.0 || Y.UA.ios && Y.UA.ios >= 2.0) {
       
   955                 Y.each(touchPoints, function(point) {
       
   956                     if(!point.identifier) {point.identifier = 0;}
       
   957                     if(!point.pageX) {point.pageX = 0;}
       
   958                     if(!point.pageY) {point.pageY = 0;}
       
   959                     if(!point.screenX) {point.screenX = 0;}
       
   960                     if(!point.screenY) {point.screenY = 0;}
       
   961 
       
   962                     touches.push(document.createTouch(Y.config.win,
       
   963                         self.target,
       
   964                         point.identifier,
       
   965                         point.pageX, point.pageY,
       
   966                         point.screenX, point.screenY));
       
   967                 });
       
   968 
       
   969                 touchList = document.createTouchList.apply(document, touches);
       
   970             } else if(Y.UA.ios && Y.UA.ios < 2.0) {
       
   971                 Y.error(NAME+': No touch event simulation framework present.');
       
   972             } else {
       
   973                 // this will inclide android(Y.UA.android && Y.UA.android < 4.0)
       
   974                 // and desktops among all others.
       
   975 
       
   976                 /*
       
   977                  * Touch APIs are broken in androids older than 4.0. We will use
       
   978                  * simulated touch apis for these versions.
       
   979                  */
       
   980                 touchList = [];
       
   981                 Y.each(touchPoints, function(point) {
       
   982                     if(!point.identifier) {point.identifier = 0;}
       
   983                     if(!point.clientX)  {point.clientX = 0;}
       
   984                     if(!point.clientY)  {point.clientY = 0;}
       
   985                     if(!point.pageX)    {point.pageX = 0;}
       
   986                     if(!point.pageY)    {point.pageY = 0;}
       
   987                     if(!point.screenX)  {point.screenX = 0;}
       
   988                     if(!point.screenY)  {point.screenY = 0;}
       
   989 
       
   990                     touchList.push({
       
   991                         target: self.target,
       
   992                         identifier: point.identifier,
       
   993                         clientX: point.clientX,
       
   994                         clientY: point.clientY,
       
   995                         pageX: point.pageX,
       
   996                         pageY: point.pageY,
       
   997                         screenX: point.screenX,
       
   998                         screenY: point.screenY
       
   999                     });
       
  1000                 });
       
  1001 
       
  1002                 touchList.item = function(i) {
       
  1003                     return touchList[i];
       
  1004                 };
       
  1005             }
       
  1006         } else {
       
  1007             Y.error(NAME+': Invalid touchPoints passed');
       
  1008         }
       
  1009 
       
  1010         return touchList;
       
  1011     },
       
  1012 
       
  1013     /**
       
  1014      * @method _simulateEvent
       
  1015      * @private
       
  1016      * @param {HTMLElement} target The DOM element that's the target of the event.
       
  1017      * @param {String} type The type of event or name of the supported gesture to simulate
       
  1018      *      (i.e., "click", "doubletap", "flick").
       
  1019      * @param {Object} options (Optional) Extra options to copy onto the event object.
       
  1020      *      For gestures, options are used to refine the gesture behavior.
       
  1021      */
       
  1022     _simulateEvent: function(target, type, options) {
       
  1023         var touches;
       
  1024 
       
  1025         if (touchEvents[type]) {
       
  1026             if(SUPPORTS_TOUCH) {
       
  1027                 Y.Event.simulate(target, type, options);
       
  1028             } else {
       
  1029                 // simulate using mouse events if touch is not applicable on this platform.
       
  1030                 // but only single touch event can be simulated.
       
  1031                 if(this._isSingleTouch(options.touches, options.targetTouches, options.changedTouches)) {
       
  1032                     type = {
       
  1033                         touchstart: MOUSE_DOWN,
       
  1034                         touchmove: MOUSE_MOVE,
       
  1035                         touchend: MOUSE_UP
       
  1036                     }[type];
       
  1037 
       
  1038                     options.button = 0;
       
  1039                     options.relatedTarget = null; // since we are not using mouseover event.
       
  1040 
       
  1041                     // touchend has none in options.touches.
       
  1042                     touches = (type === MOUSE_UP)? options.changedTouches : options.touches;
       
  1043 
       
  1044                     options = Y.mix(options, {
       
  1045                         screenX: touches.item(0).screenX,
       
  1046                         screenY: touches.item(0).screenY,
       
  1047                         clientX: touches.item(0).clientX,
       
  1048                         clientY: touches.item(0).clientY
       
  1049                     }, true);
       
  1050 
       
  1051                     Y.Event.simulate(target, type, options);
       
  1052 
       
  1053                     if(type == MOUSE_UP) {
       
  1054                         Y.Event.simulate(target, MOUSE_CLICK, options);
       
  1055                     }
       
  1056                 } else {
       
  1057                     Y.error("_simulateEvent(): Event '" + type + "' has multi touch objects that can't be simulated in your platform.");
       
  1058                 }
       
  1059             }
       
  1060         } else {
       
  1061             // pass thru for all non touch events
       
  1062             Y.Event.simulate(target, type, options);
       
  1063         }
       
  1064     },
       
  1065 
       
  1066     /**
       
  1067      * Helper method to check the single touch.
       
  1068      * @method _isSingleTouch
       
  1069      * @private
       
  1070      * @param {TouchList} touches
       
  1071      * @param {TouchList} targetTouches
       
  1072      * @param {TouchList} changedTouches
       
  1073      */
       
  1074     _isSingleTouch: function(touches, targetTouches, changedTouches) {
       
  1075         return (touches && (touches.length <= 1)) &&
       
  1076             (targetTouches && (targetTouches.length <= 1)) &&
       
  1077             (changedTouches && (changedTouches.length <= 1));
       
  1078     }
       
  1079 };
       
  1080 
       
  1081 /*
       
  1082  * A gesture simulation class.
       
  1083  */
       
  1084 Y.GestureSimulation = Simulations;
       
  1085 
       
  1086 /*
       
  1087  * Various simulation default behavior properties. If user override
       
  1088  * Y.GestureSimulation.defaults, overriden values will be used and this
       
  1089  * should be done before the gesture simulation.
       
  1090  */
       
  1091 Y.GestureSimulation.defaults = DEFAULTS;
       
  1092 
       
  1093 /*
       
  1094  * The high level gesture names that YUI knows how to simulate.
       
  1095  */
       
  1096 Y.GestureSimulation.GESTURES = gestureNames;
       
  1097 
       
  1098 /**
       
  1099  * Simulates the higher user level gesture of the given name on a target.
       
  1100  * This method generates a set of low level touch events(Apple specific gesture
       
  1101  * events as well for the iOS platforms) asynchronously. Note that gesture
       
  1102  * simulation is relying on `Y.Event.simulate()` method to generate
       
  1103  * the touch events under the hood. The `Y.Event.simulate()` method
       
  1104  * itself is a synchronous method.
       
  1105  *
       
  1106  * Users are suggested to use `Node.simulateGesture()` method which
       
  1107  * basically calls this method internally. Supported gestures are `tap`,
       
  1108  * `doubletap`, `press`, `move`, `flick`, `pinch` and `rotate`.
       
  1109  *
       
  1110  * The `pinch` gesture is used to simulate the pinching and spreading of two
       
  1111  * fingers. During a pinch simulation, rotation is also possible. Essentially
       
  1112  * `pinch` and `rotate` simulations share the same base implementation to allow
       
  1113  * both pinching and rotation at the same time. The only difference is `pinch`
       
  1114  * requires `start` and `end` option properties while `rotate` requires `rotation`
       
  1115  * option property.
       
  1116  *
       
  1117  * The `pinch` and `rotate` gestures can be described as placing 2 fingers along a
       
  1118  * circle. Pinching and spreading can be described by start and end circles while
       
  1119  * rotation occurs on a single circle. If the radius of the start circle is greater
       
  1120  * than the end circle, the gesture becomes a pinch, otherwise it is a spread spread.
       
  1121  *
       
  1122  * @example
       
  1123  *
       
  1124  *     var node = Y.one("#target");
       
  1125  *
       
  1126  *     // double tap example
       
  1127  *     node.simulateGesture("doubletap", function() {
       
  1128  *         // my callback function
       
  1129  *     });
       
  1130  *
       
  1131  *     // flick example from the center of the node, move 50 pixels down for 50ms)
       
  1132  *     node.simulateGesture("flick", {
       
  1133  *         axis: y,
       
  1134  *         distance: -100
       
  1135  *         duration: 50
       
  1136  *     }, function() {
       
  1137  *         // my callback function
       
  1138  *     });
       
  1139  *
       
  1140  *     // simulate rotating a node 75 degrees counter-clockwise
       
  1141  *     node.simulateGesture("rotate", {
       
  1142  *         rotation: -75
       
  1143  *     });
       
  1144  *
       
  1145  *     // simulate a pinch and a rotation at the same time.
       
  1146  *     // fingers start on a circle of radius 100 px, placed at top/bottom
       
  1147  *     // fingers end on a circle of radius 50px, placed at right/left
       
  1148  *     node.simulateGesture("pinch", {
       
  1149  *         r1: 100,
       
  1150  *         r2: 50,
       
  1151  *         start: 0
       
  1152  *         rotation: 90
       
  1153  *     });
       
  1154  *
       
  1155  * @method simulateGesture
       
  1156  * @param {HTMLElement|Node} node The YUI node or HTML element that's the target
       
  1157  *      of the event.
       
  1158  * @param {String} name The name of the supported gesture to simulate. The
       
  1159  *      supported gesture name is one of "tap", "doubletap", "press", "move",
       
  1160  *      "flick", "pinch" and "rotate".
       
  1161  * @param {Object} [options] Extra options used to define the gesture behavior:
       
  1162  *
       
  1163  *      Valid options properties for the `tap` gesture:
       
  1164  *
       
  1165  *      @param {Array} [options.point] (Optional) Indicates the [x,y] coordinates
       
  1166  *        where the tap should be simulated. Default is the center of the node
       
  1167  *        element.
       
  1168  *      @param {Number} [options.hold=10] (Optional) The hold time in milliseconds.
       
  1169  *        This is the time between `touchstart` and `touchend` event generation.
       
  1170  *      @param {Number} [options.times=1] (Optional) Indicates the number of taps.
       
  1171  *      @param {Number} [options.delay=10] (Optional) The number of milliseconds
       
  1172  *        before the next tap simulation happens. This is valid only when `times`
       
  1173  *        is more than 1.
       
  1174  *
       
  1175  *      Valid options properties for the `doubletap` gesture:
       
  1176  *
       
  1177  *      @param {Array} [options.point] (Optional) Indicates the [x,y] coordinates
       
  1178  *        where the doubletap should be simulated. Default is the center of the
       
  1179  *        node element.
       
  1180  *
       
  1181  *      Valid options properties for the `press` gesture:
       
  1182  *
       
  1183  *      @param {Array} [options.point] (Optional) Indicates the [x,y] coordinates
       
  1184  *        where the press should be simulated. Default is the center of the node
       
  1185  *        element.
       
  1186  *      @param {Number} [options.hold=3000] (Optional) The hold time in milliseconds.
       
  1187  *        This is the time between `touchstart` and `touchend` event generation.
       
  1188  *        Default is 3000ms (3 seconds).
       
  1189  *
       
  1190  *      Valid options properties for the `move` gesture:
       
  1191  *
       
  1192  *      @param {Object} [options.path] (Optional) Indicates the path of the finger
       
  1193  *        movement. It's an object with three optional properties: `point`,
       
  1194  *        `xdist` and  `ydist`.
       
  1195  *        @param {Array} [options.path.point] A starting point of the gesture.
       
  1196  *          Default is the center of the node element.
       
  1197  *        @param {Number} [options.path.xdist=200] A distance to move in pixels
       
  1198  *          along the X axis. A negative distance value indicates moving left.
       
  1199  *        @param {Number} [options.path.ydist=0] A distance to move in pixels
       
  1200  *          along the Y axis. A negative distance value indicates moving up.
       
  1201  *      @param {Number} [options.duration=1000] (Optional) The duration of the
       
  1202  *        gesture in milliseconds.
       
  1203  *
       
  1204  *      Valid options properties for the `flick` gesture:
       
  1205  *
       
  1206  *      @param {Array} [options.point] (Optional) Indicates the [x, y] coordinates
       
  1207  *        where the flick should be simulated. Default is the center of the
       
  1208  *        node element.
       
  1209  *      @param {String} [options.axis='x'] (Optional) Valid values are either
       
  1210  *        "x" or "y". Indicates axis to move along. The flick can move to one of
       
  1211  *        4 directions(left, right, up and down).
       
  1212  *      @param {Number} [options.distance=200] (Optional) Distance to move in pixels
       
  1213  *      @param {Number} [options.duration=1000] (Optional) The duration of the
       
  1214  *        gesture in milliseconds. User given value could be automatically
       
  1215  *        adjusted by the framework if it is below the minimum velocity to be
       
  1216  *        a flick gesture.
       
  1217  *
       
  1218  *      Valid options properties for the `pinch` gesture:
       
  1219  *
       
  1220  *      @param {Array} [options.center] (Optional) The center of the circle where
       
  1221  *        two fingers are placed. Default is the center of the node element.
       
  1222  *      @param {Number} [options.r1] (Required) Pixel radius of the start circle
       
  1223  *        where 2 fingers will be on when the gesture starts. The circles are
       
  1224  *        centered at the center of the element.
       
  1225  *      @param {Number} [options.r2] (Required) Pixel radius of the end circle
       
  1226  *        when this gesture ends.
       
  1227  *      @param {Number} [options.duration=1000] (Optional) The duration of the
       
  1228  *        gesture in milliseconds.
       
  1229  *      @param {Number} [options.start=0] (Optional) Starting degree of the first
       
  1230  *        finger. The value is relative to the path of the north. Default is 0
       
  1231  *        (i.e., 12:00 on a clock).
       
  1232  *      @param {Number} [options.rotation=0] (Optional) Degrees to rotate from
       
  1233  *        the starting degree. A negative value means rotation to the
       
  1234  *        counter-clockwise direction.
       
  1235  *
       
  1236  *      Valid options properties for the `rotate` gesture:
       
  1237  *
       
  1238  *      @param {Array} [options.center] (Optional) The center of the circle where
       
  1239  *        two fingers are placed. Default is the center of the node element.
       
  1240  *      @param {Number} [options.r1] (Optional) Pixel radius of the start circle
       
  1241  *        where 2 fingers will be on when the gesture starts. The circles are
       
  1242  *        centered at the center of the element. Default is a fourth of the node
       
  1243  *        element width or height, whichever is smaller.
       
  1244  *      @param {Number} [options.r2] (Optional) Pixel radius of the end circle
       
  1245  *        when this gesture ends. Default is a fourth of the node element width or
       
  1246  *        height, whichever is smaller.
       
  1247  *      @param {Number} [options.duration=1000] (Optional) The duration of the
       
  1248  *        gesture in milliseconds.
       
  1249  *      @param {Number} [options.start=0] (Optional) Starting degree of the first
       
  1250  *        finger. The value is relative to the path of the north. Default is 0
       
  1251  *        (i.e., 12:00 on a clock).
       
  1252  *      @param {Number} [options.rotation] (Required) Degrees to rotate from
       
  1253  *        the starting degree. A negative value means rotation to the
       
  1254  *        counter-clockwise direction.
       
  1255  *
       
  1256  * @param {Function} [cb] The callback to execute when the asynchronouse gesture
       
  1257  *      simulation is completed.
       
  1258  *      @param {Error} cb.err An error object if the simulation is failed.
       
  1259  * @for Event
       
  1260  * @static
       
  1261  */
       
  1262 Y.Event.simulateGesture = function(node, name, options, cb) {
       
  1263 
       
  1264     node = Y.one(node);
       
  1265 
       
  1266     var sim = new Y.GestureSimulation(node);
       
  1267     name = name.toLowerCase();
       
  1268 
       
  1269     if(!cb && Y.Lang.isFunction(options)) {
       
  1270         cb = options;
       
  1271         options = {};
       
  1272     }
       
  1273 
       
  1274     options = options || {};
       
  1275 
       
  1276     if (gestureNames[name]) {
       
  1277         switch(name) {
       
  1278             // single-touch: point gestures
       
  1279             case 'tap':
       
  1280                 sim.tap(cb, options.point, options.times, options.hold, options.delay);
       
  1281                 break;
       
  1282             case 'doubletap':
       
  1283                 sim.tap(cb, options.point, 2);
       
  1284                 break;
       
  1285             case 'press':
       
  1286                 if(!Y.Lang.isNumber(options.hold)) {
       
  1287                     options.hold = DEFAULTS.HOLD_PRESS;
       
  1288                 } else if(options.hold < DEFAULTS.MIN_HOLD_PRESS) {
       
  1289                     options.hold = DEFAULTS.MIN_HOLD_PRESS;
       
  1290                 } else if(options.hold > DEFAULTS.MAX_HOLD_PRESS) {
       
  1291                     options.hold = DEFAULTS.MAX_HOLD_PRESS;
       
  1292                 }
       
  1293                 sim.tap(cb, options.point, 1, options.hold);
       
  1294                 break;
       
  1295 
       
  1296             // single-touch: move gestures
       
  1297             case 'move':
       
  1298                 sim.move(cb, options.path, options.duration);
       
  1299                 break;
       
  1300             case 'flick':
       
  1301                 sim.flick(cb, options.point, options.axis, options.distance,
       
  1302                     options.duration);
       
  1303                 break;
       
  1304 
       
  1305             // multi-touch: pinch/rotation gestures
       
  1306             case 'pinch':
       
  1307                 sim.pinch(cb, options.center, options.r1, options.r2,
       
  1308                     options.duration, options.start, options.rotation);
       
  1309                 break;
       
  1310             case 'rotate':
       
  1311                 sim.rotate(cb, options.center, options.r1, options.r2,
       
  1312                     options.duration, options.start, options.rotation);
       
  1313                 break;
       
  1314         }
       
  1315     } else {
       
  1316         Y.error(NAME+': Not a supported gesture simulation: '+name);
       
  1317     }
       
  1318 };
       
  1319 
       
  1320 
       
  1321 }, '@VERSION@', {"requires": ["async-queue", "event-simulate", "node-screen"]});