]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - jssource/src_files/include/javascript/yui3/build/event-gestures/event-move.js
Release 6.5.0
[Github/sugarcrm.git] / jssource / src_files / include / javascript / yui3 / build / event-gestures / event-move.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-move', function(Y) {
9
10 /**
11  * Adds lower level support for "gesturemovestart", "gesturemove" and "gesturemoveend" events, which can be used to create drag/drop
12  * interactions which work across touch and mouse input devices. They correspond to "touchstart", "touchmove" and "touchend" on a touch input
13  * device, and "mousedown", "mousemove", "mouseup" on a mouse based input device.
14  *
15  * @module event-gestures
16  * @submodule event-move
17  */
18
19 var EVENT = ("ontouchstart" in Y.config.win && !Y.UA.chrome) ? {
20         start: "touchstart",
21         move: "touchmove",
22         end: "touchend"
23     } : {
24         start: "mousedown",
25         move: "mousemove",
26         end: "mouseup"
27     },
28
29     START = "start",
30     MOVE = "move",
31     END = "end",
32
33     GESTURE_MOVE = "gesture" + MOVE,
34     GESTURE_MOVE_END = GESTURE_MOVE + END,
35     GESTURE_MOVE_START = GESTURE_MOVE + START,
36
37     _MOVE_START_HANDLE = "_msh",
38     _MOVE_HANDLE = "_mh",
39     _MOVE_END_HANDLE = "_meh",
40
41     _DEL_MOVE_START_HANDLE = "_dmsh",
42     _DEL_MOVE_HANDLE = "_dmh",
43     _DEL_MOVE_END_HANDLE = "_dmeh",
44
45     _MOVE_START = "_ms",
46     _MOVE = "_m",
47
48     MIN_TIME = "minTime",
49     MIN_DISTANCE = "minDistance",
50     PREVENT_DEFAULT = "preventDefault",
51     BUTTON = "button",
52     OWNER_DOCUMENT = "ownerDocument",
53
54     CURRENT_TARGET = "currentTarget",
55     TARGET = "target",
56
57     NODE_TYPE = "nodeType",
58
59     _defArgsProcessor = function(se, args, delegate) {
60         var iConfig = (delegate) ? 4 : 3, 
61             config = (args.length > iConfig) ? Y.merge(args.splice(iConfig,1)[0]) : {};
62
63         if (!(PREVENT_DEFAULT in config)) {
64             config[PREVENT_DEFAULT] = se.PREVENT_DEFAULT;
65         }
66
67         return config;
68     },
69
70     _getRoot = function(node, subscriber) {
71         return subscriber._extra.root || (node.get(NODE_TYPE) === 9) ? node : node.get(OWNER_DOCUMENT);
72     },
73
74     _normTouchFacade = function(touchFacade, touch, params) {
75         touchFacade.pageX = touch.pageX;
76         touchFacade.pageY = touch.pageY;
77         touchFacade.screenX = touch.screenX;
78         touchFacade.screenY = touch.screenY;
79         touchFacade.clientX = touch.clientX;
80         touchFacade.clientY = touch.clientY;
81         touchFacade[TARGET] = touchFacade[TARGET] || touch[TARGET];
82         touchFacade[CURRENT_TARGET] = touchFacade[CURRENT_TARGET] || touch[CURRENT_TARGET];
83
84         touchFacade[BUTTON] = (params && params[BUTTON]) || 1; // default to left (left as per vendors, not W3C which is 0)
85     },
86
87     _prevent = function(e, preventDefault) {
88         if (preventDefault) {
89             // preventDefault is a boolean or a function
90             if (!preventDefault.call || preventDefault(e)) {
91                 e.preventDefault();
92             }
93         }
94     },
95
96     define = Y.Event.define;
97
98 /**
99  * Sets up a "gesturemovestart" event, that is fired on touch devices in response to a single finger "touchstart",
100  * and on mouse based devices in response to a "mousedown". The subscriber can specify the minimum time
101  * and distance thresholds which should be crossed before the "gesturemovestart" is fired and for the mouse,
102  * which button should initiate a "gesturemovestart". This event can also be listened for using node.delegate().
103  * 
104  * <p>It is recommended that you use Y.bind to set up context and additional arguments for your event handler,
105  * however if you want to pass the context and arguments as additional signature arguments to on/delegate, 
106  * 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>
107  *
108  * @event gesturemovestart
109  * @for YUI
110  * @param type {string} "gesturemovestart"
111  * @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.
112  * @param cfg {Object} Optional. An object which specifies:
113  *
114  * <dl>
115  * <dt>minDistance (defaults to 0)</dt>
116  * <dd>The minimum distance threshold which should be crossed before the gesturemovestart is fired</dd>
117  * <dt>minTime (defaults to 0)</dt>
118  * <dd>The minimum time threshold for which the finger/mouse should be help down before the gesturemovestart is fired</dd>
119  * <dt>button (no default)</dt>
120  * <dd>In the case of a mouse input device, if the event should only be fired for a specific mouse button.</dd>
121  * <dt>preventDefault (defaults to false)</dt>
122  * <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 
123  * 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>
124  * </dl>
125  *
126  * @return {EventHandle} the detach handle
127  */
128
129 define(GESTURE_MOVE_START, {
130
131     on: function (node, subscriber, ce) {
132
133         subscriber[_MOVE_START_HANDLE] = node.on(EVENT[START], 
134             this._onStart,
135             this,
136             node,
137             subscriber,
138             ce);
139     },
140
141     delegate : function(node, subscriber, ce, filter) {
142
143         var se = this;
144
145         subscriber[_DEL_MOVE_START_HANDLE] = node.delegate(EVENT[START],
146             function(e) {
147                 se._onStart(e, node, subscriber, ce, true);
148             },
149             filter);
150     },
151
152     detachDelegate : function(node, subscriber, ce, filter) {
153         var handle = subscriber[_DEL_MOVE_START_HANDLE];
154
155         if (handle) {
156             handle.detach();
157             subscriber[_DEL_MOVE_START_HANDLE] = null;
158         }
159     },
160
161     detach: function (node, subscriber, ce) {
162         var startHandle = subscriber[_MOVE_START_HANDLE];
163
164         if (startHandle) {
165             startHandle.detach();
166             subscriber[_MOVE_START_HANDLE] = null;
167         }
168     },
169
170     processArgs : function(args, delegate) {
171         var params = _defArgsProcessor(this, args, delegate);
172
173         if (!(MIN_TIME in params)) {
174             params[MIN_TIME] = this.MIN_TIME;
175         }
176
177         if (!(MIN_DISTANCE in params)) {
178             params[MIN_DISTANCE] = this.MIN_DISTANCE;
179         }
180
181         return params;
182     },
183
184     _onStart : function(e, node, subscriber, ce, delegate) {
185
186         if (delegate) {
187             node = e[CURRENT_TARGET];
188         }
189
190         var params = subscriber._extra,
191             fireStart = true,
192             minTime = params[MIN_TIME],
193             minDistance = params[MIN_DISTANCE],
194             button = params.button,
195             preventDefault = params[PREVENT_DEFAULT],
196             root = _getRoot(node, subscriber),
197             startXY;
198
199         if (e.touches) {
200             if (e.touches.length === 1) {
201                 _normTouchFacade(e, e.touches[0], params);
202             } else {
203                 fireStart = false;
204             }
205         } else {
206             fireStart = (button === undefined) || (button === e.button);
207         }
208
209
210         if (fireStart) {
211
212             _prevent(e, preventDefault);
213
214             if (minTime === 0 || minDistance === 0) {
215                 this._start(e, node, ce, params);
216
217             } else {
218
219                 startXY = [e.pageX, e.pageY];
220
221                 if (minTime > 0) {
222
223
224                     params._ht = Y.later(minTime, this, this._start, [e, node, ce, params]);
225
226                     params._hme = root.on(EVENT[END], Y.bind(function() {
227                         this._cancel(params);
228                     }, this));
229                 }
230
231                 if (minDistance > 0) {
232
233
234                     params._hm = root.on(EVENT[MOVE], Y.bind(function(em) {
235                         if (Math.abs(em.pageX - startXY[0]) > minDistance || Math.abs(em.pageY - startXY[1]) > minDistance) {
236                             this._start(e, node, ce, params);
237                         }
238                     }, this));
239                 }                        
240             }
241         }
242     },
243
244     _cancel : function(params) {
245         if (params._ht) {
246             params._ht.cancel();
247             params._ht = null;
248         }
249         if (params._hme) {
250             params._hme.detach();
251             params._hme = null;
252         }
253         if (params._hm) {
254             params._hm.detach();
255             params._hm = null;
256         }
257     },
258
259     _start : function(e, node, ce, params) {
260
261         if (params) {
262             this._cancel(params);
263         }
264
265         e.type = GESTURE_MOVE_START;
266
267
268         node.setData(_MOVE_START, e);
269         ce.fire(e);
270     },
271
272     MIN_TIME : 0,
273     MIN_DISTANCE : 0,
274     PREVENT_DEFAULT : false
275 });
276
277 /**
278  * Sets up a "gesturemove" event, that is fired on touch devices in response to a single finger "touchmove",
279  * and on mouse based devices in response to a "mousemove".
280  * 
281  * <p>By default this event is only fired when the same node
282  * has received a "gesturemovestart" event. The subscriber can set standAlone to true, in the configuration properties,
283  * if they want to listen for this event without an initial "gesturemovestart".</p>
284  * 
285  * <p>By default this event sets up it's internal "touchmove" and "mousemove" DOM listeners on the document element. The subscriber
286  * can set the root configuration property, to specify which node to attach DOM listeners to, if different from the document.</p> 
287  *
288  * <p>This event can also be listened for using node.delegate().</p>
289  *
290  * <p>It is recommended that you use Y.bind to set up context and additional arguments for your event handler,
291  * however if you want to pass the context and arguments as additional signature arguments to on/delegate, 
292  * 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>
293  *
294  * @event gesturemove
295  * @for YUI
296  * @param type {string} "gesturemove"
297  * @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.
298  * @param cfg {Object} Optional. An object which specifies:
299  * <dl>
300  * <dt>standAlone (defaults to false)</dt>
301  * <dd>true, if the subscriber should be notified even if a "gesturemovestart" has not occured on the same node.</dd>
302  * <dt>root (defaults to document)</dt>
303  * <dd>The node to which the internal DOM listeners should be attached.</dd>
304  * <dt>preventDefault (defaults to false)</dt>
305  * <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>
306  * </dl>
307  *
308  * @return {EventHandle} the detach handle
309  */
310 define(GESTURE_MOVE, {
311
312     on : function (node, subscriber, ce) {
313
314         var root = _getRoot(node, subscriber),
315
316             moveHandle = root.on(EVENT[MOVE], 
317                 this._onMove,
318                 this,
319                 node,
320                 subscriber,
321                 ce);
322
323         subscriber[_MOVE_HANDLE] = moveHandle;
324     },
325
326     delegate : function(node, subscriber, ce, filter) {
327
328         var se = this;
329
330         subscriber[_DEL_MOVE_HANDLE] = node.delegate(EVENT[MOVE],
331             function(e) {
332                 se._onMove(e, node, subscriber, ce, true);
333             },
334             filter);
335     },
336
337     detach : function (node, subscriber, ce) {
338         var moveHandle = subscriber[_MOVE_HANDLE];
339
340         if (moveHandle) {
341             moveHandle.detach();
342             subscriber[_MOVE_HANDLE] = null;
343         }
344     },
345     
346     detachDelegate : function(node, subscriber, ce, filter) {
347         var handle = subscriber[_DEL_MOVE_HANDLE];
348
349         if (handle) {
350             handle.detach();
351             subscriber[_DEL_MOVE_HANDLE] = null;
352         }
353
354     },
355
356     processArgs : function(args, delegate) {
357         return _defArgsProcessor(this, args, delegate);
358     },
359
360     _onMove : function(e, node, subscriber, ce, delegate) {
361
362         if (delegate) {
363             node = e[CURRENT_TARGET];
364         }
365
366         var fireMove = subscriber._extra.standAlone || node.getData(_MOVE_START),
367             preventDefault = subscriber._extra.preventDefault;
368
369
370         if (fireMove) {
371
372             if (e.touches) {
373                 if (e.touches.length === 1) {
374                     _normTouchFacade(e, e.touches[0]);                    
375                 } else {
376                     fireMove = false;
377                 }
378             }
379
380             if (fireMove) {
381
382                 _prevent(e, preventDefault);
383
384
385                 e.type = GESTURE_MOVE;
386                 ce.fire(e);
387             }
388         }
389     },
390     
391     PREVENT_DEFAULT : false
392 });
393
394 /**
395  * Sets up a "gesturemoveend" event, that is fired on touch devices in response to a single finger "touchend",
396  * and on mouse based devices in response to a "mouseup".
397  * 
398  * <p>By default this event is only fired when the same node
399  * has received a "gesturemove" or "gesturemovestart" event. The subscriber can set standAlone to true, in the configuration properties,
400  * if they want to listen for this event without a preceding "gesturemovestart" or "gesturemove".</p>
401  *
402  * <p>By default this event sets up it's internal "touchend" and "mouseup" DOM listeners on the document element. The subscriber
403  * can set the root configuration property, to specify which node to attach DOM listeners to, if different from the document.</p> 
404  *
405  * <p>This event can also be listened for using node.delegate().</p>
406  *
407  * <p>It is recommended that you use Y.bind to set up context and additional arguments for your event handler,
408  * however if you want to pass the context and arguments as additional signature arguments to on/delegate, 
409  * 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>
410  *
411  *
412  * @event gesturemoveend
413  * @for YUI
414  * @param type {string} "gesturemoveend"
415  * @param fn {function} The method the event invokes. It receives the event facade of the underlying DOM event (mouseup or touchend.changedTouches[0]).
416  * @param cfg {Object} Optional. An object which specifies:
417  * <dl>
418  * <dt>standAlone (defaults to false)</dt>
419  * <dd>true, if the subscriber should be notified even if a "gesturemovestart" or "gesturemove" has not occured on the same node.</dd>
420  * <dt>root (defaults to document)</dt>
421  * <dd>The node to which the internal DOM listeners should be attached.</dd>
422  * <dt>preventDefault (defaults to false)</dt>
423  * <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>
424  * </dl>
425  *
426  * @return {EventHandle} the detach handle
427  */
428 define(GESTURE_MOVE_END, {
429
430     on : function (node, subscriber, ce) {
431
432         var root = _getRoot(node, subscriber),
433
434             endHandle = root.on(EVENT[END], 
435                 this._onEnd, 
436                 this,
437                 node,
438                 subscriber, 
439                 ce);
440
441         subscriber[_MOVE_END_HANDLE] = endHandle;
442     },
443
444     delegate : function(node, subscriber, ce, filter) {
445
446         var se = this;
447
448         subscriber[_DEL_MOVE_END_HANDLE] = node.delegate(EVENT[END],
449             function(e) {
450                 se._onEnd(e, node, subscriber, ce, true);
451             },
452             filter);
453     },
454
455     detachDelegate : function(node, subscriber, ce, filter) {
456         var handle = subscriber[_DEL_MOVE_END_HANDLE];
457
458         if (handle) {
459             handle.detach();
460             subscriber[_DEL_MOVE_END_HANDLE] = null;
461         }
462
463     },
464
465     detach : function (node, subscriber, ce) {
466         var endHandle = subscriber[_MOVE_END_HANDLE];
467     
468         if (endHandle) {
469             endHandle.detach();
470             subscriber[_MOVE_END_HANDLE] = null;
471         }
472     },
473
474     processArgs : function(args, delegate) {
475         return _defArgsProcessor(this, args, delegate);
476     },
477
478     _onEnd : function(e, node, subscriber, ce, delegate) {
479
480         if (delegate) {
481             node = e[CURRENT_TARGET];
482         }
483
484         var fireMoveEnd = subscriber._extra.standAlone || node.getData(_MOVE) || node.getData(_MOVE_START),
485             preventDefault = subscriber._extra.preventDefault;
486
487         if (fireMoveEnd) {
488
489             if (e.changedTouches) {
490                 if (e.changedTouches.length === 1) {
491                     _normTouchFacade(e, e.changedTouches[0]);                    
492                 } else {
493                     fireMoveEnd = false;
494                 }
495             }
496
497             if (fireMoveEnd) {
498
499                 _prevent(e, preventDefault);
500
501                 e.type = GESTURE_MOVE_END;
502                 ce.fire(e);
503
504                 node.clearData(_MOVE_START);
505                 node.clearData(_MOVE);
506             }
507         }
508     },
509
510     PREVENT_DEFAULT : false
511 });
512
513
514 }, '3.3.0' ,{requires:['node-base','event-touch','event-synthetic']});