]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - jssource/src_files/include/javascript/yui3/build/event-gestures/event-flick.js
Release 6.5.0
[Github/sugarcrm.git] / jssource / src_files / include / javascript / yui3 / build / event-gestures / event-flick.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']});