]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - jssource/src_files/include/javascript/yui3/build/event-gestures/event-gestures.js
Release 6.5.0
[Github/sugarcrm.git] / jssource / src_files / include / javascript / yui3 / build / event-gestures / event-gestures.js
1 /*
2 Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
5 version: 3.3.0
6 build: 3167
7 */
8 YUI.add('event-flick', function(Y) {
9
10 /**
11  * The gestures module provides gesture events such as "flick", which normalize user interactions
12  * across touch and mouse or pointer based input devices. This layer can be used by application developers
13  * to build input device agnostic components which behave the same in response to either touch or mouse based  
14  * interaction.
15  *
16  * <p>Documentation for events added by this module can be found in the event document for the <a href="YUI.html#events">YUI</a> global.</p>
17  *
18  * @module event-gestures
19  */
20
21 /**
22  * Adds support for a "flick" event, which is fired at the end of a touch or mouse based flick gesture, and provides 
23  * velocity of the flick, along with distance and time information.
24  *
25  * @module event-gestures
26  * @submodule event-flick
27  */
28
29 var EVENT = ("ontouchstart" in Y.config.win && !Y.UA.chrome) ? {
30         start: "touchstart",
31         end: "touchend"
32     } : {
33         start: "mousedown",
34         end: "mouseup"
35     },
36
37     START = "start",
38     END = "end",
39
40     OWNER_DOCUMENT = "ownerDocument",
41     MIN_VELOCITY = "minVelocity",
42     MIN_DISTANCE = "minDistance",
43     PREVENT_DEFAULT = "preventDefault",
44
45     _FLICK_START = "_fs",
46     _FLICK_START_HANDLE = "_fsh",
47     _FLICK_END_HANDLE = "_feh",
48
49     NODE_TYPE = "nodeType";
50
51 /**
52  * Sets up a "flick" event, that is fired whenever the user initiates a flick gesture on the node
53  * where the listener is attached. The subscriber can specify a minimum distance or velocity for
54  * which the event is to be fired. The subscriber can also specify if there is a particular axis which
55  * they are interested in - "x" or "y". If no axis is specified, the axis along which there was most distance
56  * covered is used.
57  *
58  * <p>It is recommended that you use Y.bind to set up context and additional arguments for your event handler,
59  * however if you want to pass the context and arguments as additional signature arguments to "on", 
60  * you need to provide a null value for the configuration object, e.g: <code>node.on("flick", fn, null, context, arg1, arg2, arg3)</code></p>
61  *
62  * @event flick
63  * @for YUI
64  * @param type {string} "flick"
65  * @param fn {function} The method the event invokes. It receives an event facade with an e.flick object containing the flick related properties: e.flick.time, e.flick.distance, e.flick.velocity and e.flick.axis, e.flick.start.
66  * @param cfg {Object} Optional. An object which specifies any of the following:
67  * <dl>
68  * <dt>minDistance (in pixels, defaults to 10)</dt>
69  * <dd>The minimum distance between start and end points, which would qualify the gesture as a flick.</dd>
70  * <dt>minVelocity (in pixels/ms, defaults to 0)</dt>
71  * <dd>The minimum velocity which would qualify the gesture as a flick.</dd>
72  * <dt>preventDefault (defaults to false)</dt>
73  * <dd>Can be set to true/false to prevent default behavior as soon as the touchstart/touchend or mousedown/mouseup is received so that things like scrolling or text selection can be 
74  * prevented. This property can also be set to a function, which returns true or false, based on the event facade passed to it.</dd>
75  * <dt>axis (no default)</dt>
76  * <dd>Can be set to "x" or "y" if you want to constrain the flick velocity and distance to a single axis. If not
77  * defined, the axis along which the maximum distance was covered is used.</dd>
78  * </dl>
79  * @return {EventHandle} the detach handle
80  */
81
82 Y.Event.define('flick', {
83
84     on: function (node, subscriber, ce) {
85
86         var startHandle = node.on(EVENT[START],
87             this._onStart,
88             this,
89             node,
90             subscriber, 
91             ce);
92  
93         subscriber[_FLICK_START_HANDLE] = startHandle;
94     },
95
96     detach: function (node, subscriber, ce) {
97
98         var startHandle = subscriber[_FLICK_START_HANDLE],
99             endHandle = subscriber[_FLICK_END_HANDLE];
100
101         if (startHandle) {
102             startHandle.detach();
103             subscriber[_FLICK_START_HANDLE] = null;
104         }
105
106         if (endHandle) {
107             endHandle.detach();
108             subscriber[_FLICK_END_HANDLE] = null;
109         }
110     },
111
112     processArgs: function(args) {
113         var params = (args.length > 3) ? Y.merge(args.splice(3, 1)[0]) : {};
114
115         if (!(MIN_VELOCITY in params)) {
116             params[MIN_VELOCITY] = this.MIN_VELOCITY;
117         }
118
119         if (!(MIN_DISTANCE in params)) {
120             params[MIN_DISTANCE] = this.MIN_DISTANCE;
121         }
122
123         if (!(PREVENT_DEFAULT in params)) {
124             params[PREVENT_DEFAULT] = this.PREVENT_DEFAULT;
125         }
126
127         return params;
128     },
129
130     _onStart: function(e, node, subscriber, ce) {
131
132         var start = true, // always true for mouse
133             endHandle,
134             doc,
135             preventDefault = subscriber._extra.preventDefault,
136             origE = e; 
137
138         if (e.touches) {
139             start = (e.touches.length === 1);
140             e = e.touches[0];
141         }
142
143         if (start) {
144
145             if (preventDefault) {
146                 // preventDefault is a boolean or function
147                 if (!preventDefault.call || preventDefault(e)) {
148                     origE.preventDefault();
149                 }
150             }
151
152             e.flick = {
153                 time : new Date().getTime()
154             };
155
156             subscriber[_FLICK_START] = e;
157
158             endHandle = subscriber[_FLICK_END_HANDLE];
159
160             if (!endHandle) {
161                 doc = (node.get(NODE_TYPE) === 9) ? node : node.get(OWNER_DOCUMENT);
162
163                 endHandle = doc.on(EVENT[END], Y.bind(this._onEnd, this), null, node, subscriber, ce);
164                 subscriber[_FLICK_END_HANDLE] = endHandle;
165             }
166         }
167     },
168
169     _onEnd: function(e, node, subscriber, ce) {
170
171         var endTime = new Date().getTime(),
172             start = subscriber[_FLICK_START],
173             valid = !!start,
174             endEvent = e,
175             startTime,
176             time,
177             preventDefault,
178             params,
179             xyDistance, 
180             distance,
181             velocity,
182             axis;
183
184         if (valid) {
185
186             if (e.changedTouches) {
187                 if (e.changedTouches.length === 1 && e.touches.length === 0) {
188                     endEvent = e.changedTouches[0];
189                 } else {
190                     valid = false;
191                 }
192             }
193
194             if (valid) {
195
196                 params = subscriber._extra;
197                 preventDefault = params[PREVENT_DEFAULT];
198
199                 if (preventDefault) {
200                     // preventDefault is a boolean or function
201                     if (!preventDefault.call || preventDefault(e)) {
202                         endEvent.preventDefault();
203                     }
204                 }
205
206                 startTime = start.flick.time;
207                 endTime = new Date().getTime();
208                 time = endTime - startTime;
209
210
211                 xyDistance = [
212                     endEvent.pageX - start.pageX,
213                     endEvent.pageY - start.pageY
214                 ];
215
216                 if (params.axis) {
217                     axis = params.axis;
218                 } else {
219                     axis = (Math.abs(xyDistance[0]) >= Math.abs(xyDistance[1])) ? 'x' : 'y';
220                 }
221
222                 distance = xyDistance[(axis === 'x') ? 0 : 1];
223                 velocity = (time !== 0) ? distance/time : 0;
224
225                 if (isFinite(velocity) && (Math.abs(distance) >= params[MIN_DISTANCE]) && (Math.abs(velocity)  >= params[MIN_VELOCITY])) {
226
227                     e.type = "flick";
228                     e.flick = {
229                         time:time,
230                         distance: distance,
231                         velocity:velocity,
232                         axis: axis,
233                         start : start
234                     };
235
236                     ce.fire(e);
237
238                 }
239
240                 subscriber[_FLICK_START] = null;
241             }
242         }
243     },
244
245     MIN_VELOCITY : 0,
246     MIN_DISTANCE : 0,
247     PREVENT_DEFAULT : false
248 });
249
250
251 }, '3.3.0' ,{requires:['node-base','event-touch','event-synthetic']});
252 YUI.add('event-move', function(Y) {
253
254 /**
255  * Adds lower level support for "gesturemovestart", "gesturemove" and "gesturemoveend" events, which can be used to create drag/drop
256  * interactions which work across touch and mouse input devices. They correspond to "touchstart", "touchmove" and "touchend" on a touch input
257  * device, and "mousedown", "mousemove", "mouseup" on a mouse based input device.
258  *
259  * @module event-gestures
260  * @submodule event-move
261  */
262
263 var EVENT = ("ontouchstart" in Y.config.win && !Y.UA.chrome) ? {
264         start: "touchstart",
265         move: "touchmove",
266         end: "touchend"
267     } : {
268         start: "mousedown",
269         move: "mousemove",
270         end: "mouseup"
271     },
272
273     START = "start",
274     MOVE = "move",
275     END = "end",
276
277     GESTURE_MOVE = "gesture" + MOVE,
278     GESTURE_MOVE_END = GESTURE_MOVE + END,
279     GESTURE_MOVE_START = GESTURE_MOVE + START,
280
281     _MOVE_START_HANDLE = "_msh",
282     _MOVE_HANDLE = "_mh",
283     _MOVE_END_HANDLE = "_meh",
284
285     _DEL_MOVE_START_HANDLE = "_dmsh",
286     _DEL_MOVE_HANDLE = "_dmh",
287     _DEL_MOVE_END_HANDLE = "_dmeh",
288
289     _MOVE_START = "_ms",
290     _MOVE = "_m",
291
292     MIN_TIME = "minTime",
293     MIN_DISTANCE = "minDistance",
294     PREVENT_DEFAULT = "preventDefault",
295     BUTTON = "button",
296     OWNER_DOCUMENT = "ownerDocument",
297
298     CURRENT_TARGET = "currentTarget",
299     TARGET = "target",
300
301     NODE_TYPE = "nodeType",
302
303     _defArgsProcessor = function(se, args, delegate) {
304         var iConfig = (delegate) ? 4 : 3, 
305             config = (args.length > iConfig) ? Y.merge(args.splice(iConfig,1)[0]) : {};
306
307         if (!(PREVENT_DEFAULT in config)) {
308             config[PREVENT_DEFAULT] = se.PREVENT_DEFAULT;
309         }
310
311         return config;
312     },
313
314     _getRoot = function(node, subscriber) {
315         return subscriber._extra.root || (node.get(NODE_TYPE) === 9) ? node : node.get(OWNER_DOCUMENT);
316     },
317
318     _normTouchFacade = function(touchFacade, touch, params) {
319         touchFacade.pageX = touch.pageX;
320         touchFacade.pageY = touch.pageY;
321         touchFacade.screenX = touch.screenX;
322         touchFacade.screenY = touch.screenY;
323         touchFacade.clientX = touch.clientX;
324         touchFacade.clientY = touch.clientY;
325         touchFacade[TARGET] = touchFacade[TARGET] || touch[TARGET];
326         touchFacade[CURRENT_TARGET] = touchFacade[CURRENT_TARGET] || touch[CURRENT_TARGET];
327
328         touchFacade[BUTTON] = (params && params[BUTTON]) || 1; // default to left (left as per vendors, not W3C which is 0)
329     },
330
331     _prevent = function(e, preventDefault) {
332         if (preventDefault) {
333             // preventDefault is a boolean or a function
334             if (!preventDefault.call || preventDefault(e)) {
335                 e.preventDefault();
336             }
337         }
338     },
339
340     define = Y.Event.define;
341
342 /**
343  * Sets up a "gesturemovestart" event, that is fired on touch devices in response to a single finger "touchstart",
344  * and on mouse based devices in response to a "mousedown". The subscriber can specify the minimum time
345  * and distance thresholds which should be crossed before the "gesturemovestart" is fired and for the mouse,
346  * which button should initiate a "gesturemovestart". This event can also be listened for using node.delegate().
347  * 
348  * <p>It is recommended that you use Y.bind to set up context and additional arguments for your event handler,
349  * however if you want to pass the context and arguments as additional signature arguments to on/delegate, 
350  * you need to provide a null value for the configuration object, e.g: <code>node.on("gesturemovestart", fn, null, context, arg1, arg2, arg3)</code></p>
351  *
352  * @event gesturemovestart
353  * @for YUI
354  * @param type {string} "gesturemovestart"
355  * @param fn {function} The method the event invokes. It receives the event facade of the underlying DOM event (mousedown or touchstart.touches[0]) which contains position co-ordinates.
356  * @param cfg {Object} Optional. An object which specifies:
357  *
358  * <dl>
359  * <dt>minDistance (defaults to 0)</dt>
360  * <dd>The minimum distance threshold which should be crossed before the gesturemovestart is fired</dd>
361  * <dt>minTime (defaults to 0)</dt>
362  * <dd>The minimum time threshold for which the finger/mouse should be help down before the gesturemovestart is fired</dd>
363  * <dt>button (no default)</dt>
364  * <dd>In the case of a mouse input device, if the event should only be fired for a specific mouse button.</dd>
365  * <dt>preventDefault (defaults to false)</dt>
366  * <dd>Can be set to true/false to prevent default behavior as soon as the touchstart or mousedown is received (that is before minTime or minDistance thresholds are crossed, and so before the gesturemovestart listener is notified) so that things like text selection and context popups (on touch devices) can be 
367  * prevented. This property can also be set to a function, which returns true or false, based on the event facade passed to it (for example, DragDrop can determine if the target is a valid handle or not before preventing default).</dd>
368  * </dl>
369  *
370  * @return {EventHandle} the detach handle
371  */
372
373 define(GESTURE_MOVE_START, {
374
375     on: function (node, subscriber, ce) {
376
377         subscriber[_MOVE_START_HANDLE] = node.on(EVENT[START], 
378             this._onStart,
379             this,
380             node,
381             subscriber,
382             ce);
383     },
384
385     delegate : function(node, subscriber, ce, filter) {
386
387         var se = this;
388
389         subscriber[_DEL_MOVE_START_HANDLE] = node.delegate(EVENT[START],
390             function(e) {
391                 se._onStart(e, node, subscriber, ce, true);
392             },
393             filter);
394     },
395
396     detachDelegate : function(node, subscriber, ce, filter) {
397         var handle = subscriber[_DEL_MOVE_START_HANDLE];
398
399         if (handle) {
400             handle.detach();
401             subscriber[_DEL_MOVE_START_HANDLE] = null;
402         }
403     },
404
405     detach: function (node, subscriber, ce) {
406         var startHandle = subscriber[_MOVE_START_HANDLE];
407
408         if (startHandle) {
409             startHandle.detach();
410             subscriber[_MOVE_START_HANDLE] = null;
411         }
412     },
413
414     processArgs : function(args, delegate) {
415         var params = _defArgsProcessor(this, args, delegate);
416
417         if (!(MIN_TIME in params)) {
418             params[MIN_TIME] = this.MIN_TIME;
419         }
420
421         if (!(MIN_DISTANCE in params)) {
422             params[MIN_DISTANCE] = this.MIN_DISTANCE;
423         }
424
425         return params;
426     },
427
428     _onStart : function(e, node, subscriber, ce, delegate) {
429
430         if (delegate) {
431             node = e[CURRENT_TARGET];
432         }
433
434         var params = subscriber._extra,
435             fireStart = true,
436             minTime = params[MIN_TIME],
437             minDistance = params[MIN_DISTANCE],
438             button = params.button,
439             preventDefault = params[PREVENT_DEFAULT],
440             root = _getRoot(node, subscriber),
441             startXY;
442
443         if (e.touches) {
444             if (e.touches.length === 1) {
445                 _normTouchFacade(e, e.touches[0], params);
446             } else {
447                 fireStart = false;
448             }
449         } else {
450             fireStart = (button === undefined) || (button === e.button);
451         }
452
453
454         if (fireStart) {
455
456             _prevent(e, preventDefault);
457
458             if (minTime === 0 || minDistance === 0) {
459                 this._start(e, node, ce, params);
460
461             } else {
462
463                 startXY = [e.pageX, e.pageY];
464
465                 if (minTime > 0) {
466
467
468                     params._ht = Y.later(minTime, this, this._start, [e, node, ce, params]);
469
470                     params._hme = root.on(EVENT[END], Y.bind(function() {
471                         this._cancel(params);
472                     }, this));
473                 }
474
475                 if (minDistance > 0) {
476
477
478                     params._hm = root.on(EVENT[MOVE], Y.bind(function(em) {
479                         if (Math.abs(em.pageX - startXY[0]) > minDistance || Math.abs(em.pageY - startXY[1]) > minDistance) {
480                             this._start(e, node, ce, params);
481                         }
482                     }, this));
483                 }                        
484             }
485         }
486     },
487
488     _cancel : function(params) {
489         if (params._ht) {
490             params._ht.cancel();
491             params._ht = null;
492         }
493         if (params._hme) {
494             params._hme.detach();
495             params._hme = null;
496         }
497         if (params._hm) {
498             params._hm.detach();
499             params._hm = null;
500         }
501     },
502
503     _start : function(e, node, ce, params) {
504
505         if (params) {
506             this._cancel(params);
507         }
508
509         e.type = GESTURE_MOVE_START;
510
511
512         node.setData(_MOVE_START, e);
513         ce.fire(e);
514     },
515
516     MIN_TIME : 0,
517     MIN_DISTANCE : 0,
518     PREVENT_DEFAULT : false
519 });
520
521 /**
522  * Sets up a "gesturemove" event, that is fired on touch devices in response to a single finger "touchmove",
523  * and on mouse based devices in response to a "mousemove".
524  * 
525  * <p>By default this event is only fired when the same node
526  * has received a "gesturemovestart" event. The subscriber can set standAlone to true, in the configuration properties,
527  * if they want to listen for this event without an initial "gesturemovestart".</p>
528  * 
529  * <p>By default this event sets up it's internal "touchmove" and "mousemove" DOM listeners on the document element. The subscriber
530  * can set the root configuration property, to specify which node to attach DOM listeners to, if different from the document.</p> 
531  *
532  * <p>This event can also be listened for using node.delegate().</p>
533  *
534  * <p>It is recommended that you use Y.bind to set up context and additional arguments for your event handler,
535  * however if you want to pass the context and arguments as additional signature arguments to on/delegate, 
536  * you need to provide a null value for the configuration object, e.g: <code>node.on("gesturemove", fn, null, context, arg1, arg2, arg3)</code></p>
537  *
538  * @event gesturemove
539  * @for YUI
540  * @param type {string} "gesturemove"
541  * @param fn {function} The method the event invokes. It receives the event facade of the underlying DOM event (mousemove or touchmove.touches[0]) which contains position co-ordinates.
542  * @param cfg {Object} Optional. An object which specifies:
543  * <dl>
544  * <dt>standAlone (defaults to false)</dt>
545  * <dd>true, if the subscriber should be notified even if a "gesturemovestart" has not occured on the same node.</dd>
546  * <dt>root (defaults to document)</dt>
547  * <dd>The node to which the internal DOM listeners should be attached.</dd>
548  * <dt>preventDefault (defaults to false)</dt>
549  * <dd>Can be set to true/false to prevent default behavior as soon as the touchmove or mousemove is received. As with gesturemovestart, can also be set to function which returns true/false based on the event facade passed to it.</dd>
550  * </dl>
551  *
552  * @return {EventHandle} the detach handle
553  */
554 define(GESTURE_MOVE, {
555
556     on : function (node, subscriber, ce) {
557
558         var root = _getRoot(node, subscriber),
559
560             moveHandle = root.on(EVENT[MOVE], 
561                 this._onMove,
562                 this,
563                 node,
564                 subscriber,
565                 ce);
566
567         subscriber[_MOVE_HANDLE] = moveHandle;
568     },
569
570     delegate : function(node, subscriber, ce, filter) {
571
572         var se = this;
573
574         subscriber[_DEL_MOVE_HANDLE] = node.delegate(EVENT[MOVE],
575             function(e) {
576                 se._onMove(e, node, subscriber, ce, true);
577             },
578             filter);
579     },
580
581     detach : function (node, subscriber, ce) {
582         var moveHandle = subscriber[_MOVE_HANDLE];
583
584         if (moveHandle) {
585             moveHandle.detach();
586             subscriber[_MOVE_HANDLE] = null;
587         }
588     },
589     
590     detachDelegate : function(node, subscriber, ce, filter) {
591         var handle = subscriber[_DEL_MOVE_HANDLE];
592
593         if (handle) {
594             handle.detach();
595             subscriber[_DEL_MOVE_HANDLE] = null;
596         }
597
598     },
599
600     processArgs : function(args, delegate) {
601         return _defArgsProcessor(this, args, delegate);
602     },
603
604     _onMove : function(e, node, subscriber, ce, delegate) {
605
606         if (delegate) {
607             node = e[CURRENT_TARGET];
608         }
609
610         var fireMove = subscriber._extra.standAlone || node.getData(_MOVE_START),
611             preventDefault = subscriber._extra.preventDefault;
612
613
614         if (fireMove) {
615
616             if (e.touches) {
617                 if (e.touches.length === 1) {
618                     _normTouchFacade(e, e.touches[0]);                    
619                 } else {
620                     fireMove = false;
621                 }
622             }
623
624             if (fireMove) {
625
626                 _prevent(e, preventDefault);
627
628
629                 e.type = GESTURE_MOVE;
630                 ce.fire(e);
631             }
632         }
633     },
634     
635     PREVENT_DEFAULT : false
636 });
637
638 /**
639  * Sets up a "gesturemoveend" event, that is fired on touch devices in response to a single finger "touchend",
640  * and on mouse based devices in response to a "mouseup".
641  * 
642  * <p>By default this event is only fired when the same node
643  * has received a "gesturemove" or "gesturemovestart" event. The subscriber can set standAlone to true, in the configuration properties,
644  * if they want to listen for this event without a preceding "gesturemovestart" or "gesturemove".</p>
645  *
646  * <p>By default this event sets up it's internal "touchend" and "mouseup" DOM listeners on the document element. The subscriber
647  * can set the root configuration property, to specify which node to attach DOM listeners to, if different from the document.</p> 
648  *
649  * <p>This event can also be listened for using node.delegate().</p>
650  *
651  * <p>It is recommended that you use Y.bind to set up context and additional arguments for your event handler,
652  * however if you want to pass the context and arguments as additional signature arguments to on/delegate, 
653  * you need to provide a null value for the configuration object, e.g: <code>node.on("gesturemoveend", fn, null, context, arg1, arg2, arg3)</code></p>
654  *
655  *
656  * @event gesturemoveend
657  * @for YUI
658  * @param type {string} "gesturemoveend"
659  * @param fn {function} The method the event invokes. It receives the event facade of the underlying DOM event (mouseup or touchend.changedTouches[0]).
660  * @param cfg {Object} Optional. An object which specifies:
661  * <dl>
662  * <dt>standAlone (defaults to false)</dt>
663  * <dd>true, if the subscriber should be notified even if a "gesturemovestart" or "gesturemove" has not occured on the same node.</dd>
664  * <dt>root (defaults to document)</dt>
665  * <dd>The node to which the internal DOM listeners should be attached.</dd>
666  * <dt>preventDefault (defaults to false)</dt>
667  * <dd>Can be set to true/false to prevent default behavior as soon as the touchend or mouseup is received. As with gesturemovestart, can also be set to function which returns true/false based on the event facade passed to it.</dd>
668  * </dl>
669  *
670  * @return {EventHandle} the detach handle
671  */
672 define(GESTURE_MOVE_END, {
673
674     on : function (node, subscriber, ce) {
675
676         var root = _getRoot(node, subscriber),
677
678             endHandle = root.on(EVENT[END], 
679                 this._onEnd, 
680                 this,
681                 node,
682                 subscriber, 
683                 ce);
684
685         subscriber[_MOVE_END_HANDLE] = endHandle;
686     },
687
688     delegate : function(node, subscriber, ce, filter) {
689
690         var se = this;
691
692         subscriber[_DEL_MOVE_END_HANDLE] = node.delegate(EVENT[END],
693             function(e) {
694                 se._onEnd(e, node, subscriber, ce, true);
695             },
696             filter);
697     },
698
699     detachDelegate : function(node, subscriber, ce, filter) {
700         var handle = subscriber[_DEL_MOVE_END_HANDLE];
701
702         if (handle) {
703             handle.detach();
704             subscriber[_DEL_MOVE_END_HANDLE] = null;
705         }
706
707     },
708
709     detach : function (node, subscriber, ce) {
710         var endHandle = subscriber[_MOVE_END_HANDLE];
711     
712         if (endHandle) {
713             endHandle.detach();
714             subscriber[_MOVE_END_HANDLE] = null;
715         }
716     },
717
718     processArgs : function(args, delegate) {
719         return _defArgsProcessor(this, args, delegate);
720     },
721
722     _onEnd : function(e, node, subscriber, ce, delegate) {
723
724         if (delegate) {
725             node = e[CURRENT_TARGET];
726         }
727
728         var fireMoveEnd = subscriber._extra.standAlone || node.getData(_MOVE) || node.getData(_MOVE_START),
729             preventDefault = subscriber._extra.preventDefault;
730
731         if (fireMoveEnd) {
732
733             if (e.changedTouches) {
734                 if (e.changedTouches.length === 1) {
735                     _normTouchFacade(e, e.changedTouches[0]);                    
736                 } else {
737                     fireMoveEnd = false;
738                 }
739             }
740
741             if (fireMoveEnd) {
742
743                 _prevent(e, preventDefault);
744
745                 e.type = GESTURE_MOVE_END;
746                 ce.fire(e);
747
748                 node.clearData(_MOVE_START);
749                 node.clearData(_MOVE);
750             }
751         }
752     },
753
754     PREVENT_DEFAULT : false
755 });
756
757
758 }, '3.3.0' ,{requires:['node-base','event-touch','event-synthetic']});
759
760
761 YUI.add('event-gestures', function(Y){}, '3.3.0' ,{use:['event-flick', 'event-move']});
762