MDL-32843 import YUI 3.5.1
[moodle.git] / lib / yui / 3.5.1 / build / event-move / event-move-debug.js
bloba9ac1b2d85ba2967f217fc86e3d0bc469e4dd056
1 /*
2 YUI 3.5.1 (build 22)
3 Copyright 2012 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
6 */
7 YUI.add('event-move', function(Y) {
9 /**
10  * Adds lower level support for "gesturemovestart", "gesturemove" and "gesturemoveend" events, which can be used to create drag/drop
11  * interactions which work across touch and mouse input devices. They correspond to "touchstart", "touchmove" and "touchend" on a touch input
12  * device, and "mousedown", "mousemove", "mouseup" on a mouse based input device.
13  *
14  * @module event-gestures
15  * @submodule event-move
16  */
18 var EVENT = ((Y.config.win && ("ontouchstart" in Y.config.win)) && !(Y.UA.chrome && Y.UA.chrome < 6)) ? {
19         start: "touchstart",
20         move: "touchmove",
21         end: "touchend"
22     } : {
23         start: "mousedown",
24         move: "mousemove",
25         end: "mouseup"
26     },
28     START = "start",
29     MOVE = "move",
30     END = "end",
32     GESTURE_MOVE = "gesture" + MOVE,
33     GESTURE_MOVE_END = GESTURE_MOVE + END,
34     GESTURE_MOVE_START = GESTURE_MOVE + START,
36     _MOVE_START_HANDLE = "_msh",
37     _MOVE_HANDLE = "_mh",
38     _MOVE_END_HANDLE = "_meh",
40     _DEL_MOVE_START_HANDLE = "_dmsh",
41     _DEL_MOVE_HANDLE = "_dmh",
42     _DEL_MOVE_END_HANDLE = "_dmeh",
44     _MOVE_START = "_ms",
45     _MOVE = "_m",
47     MIN_TIME = "minTime",
48     MIN_DISTANCE = "minDistance",
49     PREVENT_DEFAULT = "preventDefault",
50     BUTTON = "button",
51     OWNER_DOCUMENT = "ownerDocument",
53     CURRENT_TARGET = "currentTarget",
54     TARGET = "target",
56     NODE_TYPE = "nodeType",
58     _defArgsProcessor = function(se, args, delegate) {
59         var iConfig = (delegate) ? 4 : 3, 
60             config = (args.length > iConfig) ? Y.merge(args.splice(iConfig,1)[0]) : {};
62         if (!(PREVENT_DEFAULT in config)) {
63             config[PREVENT_DEFAULT] = se.PREVENT_DEFAULT;
64         }
66         return config;
67     },
69     _getRoot = function(node, subscriber) {
70         return subscriber._extra.root || (node.get(NODE_TYPE) === 9) ? node : node.get(OWNER_DOCUMENT);
71     },
73     _normTouchFacade = function(touchFacade, touch, params) {
74         touchFacade.pageX = touch.pageX;
75         touchFacade.pageY = touch.pageY;
76         touchFacade.screenX = touch.screenX;
77         touchFacade.screenY = touch.screenY;
78         touchFacade.clientX = touch.clientX;
79         touchFacade.clientY = touch.clientY;
80         touchFacade[TARGET] = touchFacade[TARGET] || touch[TARGET];
81         touchFacade[CURRENT_TARGET] = touchFacade[CURRENT_TARGET] || touch[CURRENT_TARGET];
83         touchFacade[BUTTON] = (params && params[BUTTON]) || 1; // default to left (left as per vendors, not W3C which is 0)
84     },
86     _prevent = function(e, preventDefault) {
87         if (preventDefault) {
88             // preventDefault is a boolean or a function
89             if (!preventDefault.call || preventDefault(e)) {
90                 e.preventDefault();
91             }
92         }
93     },
95     define = Y.Event.define;
97 /**
98  * Sets up a "gesturemovestart" event, that is fired on touch devices in response to a single finger "touchstart",
99  * and on mouse based devices in response to a "mousedown". The subscriber can specify the minimum time
100  * and distance thresholds which should be crossed before the "gesturemovestart" is fired and for the mouse,
101  * which button should initiate a "gesturemovestart". This event can also be listened for using node.delegate().
102  * 
103  * <p>It is recommended that you use Y.bind to set up context and additional arguments for your event handler,
104  * however if you want to pass the context and arguments as additional signature arguments to on/delegate, 
105  * 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  * @event gesturemovestart
108  * @for YUI
109  * @param type {string} "gesturemovestart"
110  * @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.
111  * @param cfg {Object} Optional. An object which specifies:
113  * <dl>
114  * <dt>minDistance (defaults to 0)</dt>
115  * <dd>The minimum distance threshold which should be crossed before the gesturemovestart is fired</dd>
116  * <dt>minTime (defaults to 0)</dt>
117  * <dd>The minimum time threshold for which the finger/mouse should be help down before the gesturemovestart is fired</dd>
118  * <dt>button (no default)</dt>
119  * <dd>In the case of a mouse input device, if the event should only be fired for a specific mouse button.</dd>
120  * <dt>preventDefault (defaults to false)</dt>
121  * <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 
122  * 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>
123  * </dl>
125  * @return {EventHandle} the detach handle
126  */
128 define(GESTURE_MOVE_START, {
130     on: function (node, subscriber, ce) {
132         subscriber[_MOVE_START_HANDLE] = node.on(EVENT[START], 
133             this._onStart,
134             this,
135             node,
136             subscriber,
137             ce);
138     },
140     delegate : function(node, subscriber, ce, filter) {
142         var se = this;
144         subscriber[_DEL_MOVE_START_HANDLE] = node.delegate(EVENT[START],
145             function(e) {
146                 se._onStart(e, node, subscriber, ce, true);
147             },
148             filter);
149     },
151     detachDelegate : function(node, subscriber, ce, filter) {
152         var handle = subscriber[_DEL_MOVE_START_HANDLE];
154         if (handle) {
155             handle.detach();
156             subscriber[_DEL_MOVE_START_HANDLE] = null;
157         }
158     },
160     detach: function (node, subscriber, ce) {
161         var startHandle = subscriber[_MOVE_START_HANDLE];
163         if (startHandle) {
164             startHandle.detach();
165             subscriber[_MOVE_START_HANDLE] = null;
166         }
167     },
169     processArgs : function(args, delegate) {
170         var params = _defArgsProcessor(this, args, delegate);
172         if (!(MIN_TIME in params)) {
173             params[MIN_TIME] = this.MIN_TIME;
174         }
176         if (!(MIN_DISTANCE in params)) {
177             params[MIN_DISTANCE] = this.MIN_DISTANCE;
178         }
180         return params;
181     },
183     _onStart : function(e, node, subscriber, ce, delegate) {
185         if (delegate) {
186             node = e[CURRENT_TARGET];
187         }
189         var params = subscriber._extra,
190             fireStart = true,
191             minTime = params[MIN_TIME],
192             minDistance = params[MIN_DISTANCE],
193             button = params.button,
194             preventDefault = params[PREVENT_DEFAULT],
195             root = _getRoot(node, subscriber),
196             startXY;
198         if (e.touches) {
199             if (e.touches.length === 1) {
200                 _normTouchFacade(e, e.touches[0], params);
201             } else {
202                 fireStart = false;
203             }
204         } else {
205             fireStart = (button === undefined) || (button === e.button);
206         }
208         Y.log("gesturemovestart: params = button:" + button + ", minTime = " + minTime + ", minDistance = " + minDistance, "event-gestures");
210         if (fireStart) {
212             _prevent(e, preventDefault);
214             if (minTime === 0 || minDistance === 0) {
215                 Y.log("gesturemovestart: No minTime or minDistance. Firing immediately", "event-gestures");
216                 this._start(e, node, ce, params);
218             } else {
220                 startXY = [e.pageX, e.pageY];
222                 if (minTime > 0) {
224                     Y.log("gesturemovestart: minTime specified. Setup timer.", "event-gestures");
225                     Y.log("gesturemovestart: initialTime for minTime = " + new Date().getTime(), "event-gestures");
227                     params._ht = Y.later(minTime, this, this._start, [e, node, ce, params]);
229                     params._hme = root.on(EVENT[END], Y.bind(function() {
230                         this._cancel(params);
231                     }, this));
232                 }
234                 if (minDistance > 0) {
236                     Y.log("gesturemovestart: minDistance specified. Setup native mouse/touchmove listener to measure distance.", "event-gestures");
237                     Y.log("gesturemovestart: initialXY for minDistance = " + startXY, "event-gestures");
239                     params._hm = root.on(EVENT[MOVE], Y.bind(function(em) {
240                         if (Math.abs(em.pageX - startXY[0]) > minDistance || Math.abs(em.pageY - startXY[1]) > minDistance) {
241                             Y.log("gesturemovestart: minDistance hit.", "event-gestures");
242                             this._start(e, node, ce, params);
243                         }
244                     }, this));
245                 }                        
246             }
247         }
248     },
250     _cancel : function(params) {
251         if (params._ht) {
252             params._ht.cancel();
253             params._ht = null;
254         }
255         if (params._hme) {
256             params._hme.detach();
257             params._hme = null;
258         }
259         if (params._hm) {
260             params._hm.detach();
261             params._hm = null;
262         }
263     },
265     _start : function(e, node, ce, params) {
267         if (params) {
268             this._cancel(params);
269         }
271         e.type = GESTURE_MOVE_START;
273         Y.log("gesturemovestart: Firing start: " + new Date().getTime(), "event-gestures");
275         node.setData(_MOVE_START, e);
276         ce.fire(e);
277     },
279     MIN_TIME : 0,
280     MIN_DISTANCE : 0,
281     PREVENT_DEFAULT : false
285  * Sets up a "gesturemove" event, that is fired on touch devices in response to a single finger "touchmove",
286  * and on mouse based devices in response to a "mousemove".
287  * 
288  * <p>By default this event is only fired when the same node
289  * has received a "gesturemovestart" event. The subscriber can set standAlone to true, in the configuration properties,
290  * if they want to listen for this event without an initial "gesturemovestart".</p>
291  * 
292  * <p>By default this event sets up it's internal "touchmove" and "mousemove" DOM listeners on the document element. The subscriber
293  * can set the root configuration property, to specify which node to attach DOM listeners to, if different from the document.</p> 
295  * <p>This event can also be listened for using node.delegate().</p>
297  * <p>It is recommended that you use Y.bind to set up context and additional arguments for your event handler,
298  * however if you want to pass the context and arguments as additional signature arguments to on/delegate, 
299  * 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>
301  * @event gesturemove
302  * @for YUI
303  * @param type {string} "gesturemove"
304  * @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.
305  * @param cfg {Object} Optional. An object which specifies:
306  * <dl>
307  * <dt>standAlone (defaults to false)</dt>
308  * <dd>true, if the subscriber should be notified even if a "gesturemovestart" has not occured on the same node.</dd>
309  * <dt>root (defaults to document)</dt>
310  * <dd>The node to which the internal DOM listeners should be attached.</dd>
311  * <dt>preventDefault (defaults to false)</dt>
312  * <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>
313  * </dl>
315  * @return {EventHandle} the detach handle
316  */
317 define(GESTURE_MOVE, {
319     on : function (node, subscriber, ce) {
321         var root = _getRoot(node, subscriber),
323             moveHandle = root.on(EVENT[MOVE], 
324                 this._onMove,
325                 this,
326                 node,
327                 subscriber,
328                 ce);
330         subscriber[_MOVE_HANDLE] = moveHandle;
331     },
333     delegate : function(node, subscriber, ce, filter) {
335         var se = this;
337         subscriber[_DEL_MOVE_HANDLE] = node.delegate(EVENT[MOVE],
338             function(e) {
339                 se._onMove(e, node, subscriber, ce, true);
340             },
341             filter);
342     },
344     detach : function (node, subscriber, ce) {
345         var moveHandle = subscriber[_MOVE_HANDLE];
347         if (moveHandle) {
348             moveHandle.detach();
349             subscriber[_MOVE_HANDLE] = null;
350         }
351     },
352     
353     detachDelegate : function(node, subscriber, ce, filter) {
354         var handle = subscriber[_DEL_MOVE_HANDLE];
356         if (handle) {
357             handle.detach();
358             subscriber[_DEL_MOVE_HANDLE] = null;
359         }
361     },
363     processArgs : function(args, delegate) {
364         return _defArgsProcessor(this, args, delegate);
365     },
367     _onMove : function(e, node, subscriber, ce, delegate) {
369         if (delegate) {
370             node = e[CURRENT_TARGET];
371         }
373         var fireMove = subscriber._extra.standAlone || node.getData(_MOVE_START),
374             preventDefault = subscriber._extra.preventDefault;
376         Y.log("onMove initial fireMove check:" + fireMove,"event-gestures");
378         if (fireMove) {
380             if (e.touches) {
381                 if (e.touches.length === 1) {
382                     _normTouchFacade(e, e.touches[0]);                    
383                 } else {
384                     fireMove = false;
385                 }
386             }
388             if (fireMove) {
390                 _prevent(e, preventDefault);
392                 Y.log("onMove second fireMove check:" + fireMove,"event-gestures");
394                 e.type = GESTURE_MOVE;
395                 ce.fire(e);
396             }
397         }
398     },
399     
400     PREVENT_DEFAULT : false
404  * Sets up a "gesturemoveend" event, that is fired on touch devices in response to a single finger "touchend",
405  * and on mouse based devices in response to a "mouseup".
406  * 
407  * <p>By default this event is only fired when the same node
408  * has received a "gesturemove" or "gesturemovestart" event. The subscriber can set standAlone to true, in the configuration properties,
409  * if they want to listen for this event without a preceding "gesturemovestart" or "gesturemove".</p>
411  * <p>By default this event sets up it's internal "touchend" and "mouseup" DOM listeners on the document element. The subscriber
412  * can set the root configuration property, to specify which node to attach DOM listeners to, if different from the document.</p> 
414  * <p>This event can also be listened for using node.delegate().</p>
416  * <p>It is recommended that you use Y.bind to set up context and additional arguments for your event handler,
417  * however if you want to pass the context and arguments as additional signature arguments to on/delegate, 
418  * 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>
421  * @event gesturemoveend
422  * @for YUI
423  * @param type {string} "gesturemoveend"
424  * @param fn {function} The method the event invokes. It receives the event facade of the underlying DOM event (mouseup or touchend.changedTouches[0]).
425  * @param cfg {Object} Optional. An object which specifies:
426  * <dl>
427  * <dt>standAlone (defaults to false)</dt>
428  * <dd>true, if the subscriber should be notified even if a "gesturemovestart" or "gesturemove" has not occured on the same node.</dd>
429  * <dt>root (defaults to document)</dt>
430  * <dd>The node to which the internal DOM listeners should be attached.</dd>
431  * <dt>preventDefault (defaults to false)</dt>
432  * <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>
433  * </dl>
435  * @return {EventHandle} the detach handle
436  */
437 define(GESTURE_MOVE_END, {
439     on : function (node, subscriber, ce) {
441         var root = _getRoot(node, subscriber),
443             endHandle = root.on(EVENT[END], 
444                 this._onEnd, 
445                 this,
446                 node,
447                 subscriber, 
448                 ce);
450         subscriber[_MOVE_END_HANDLE] = endHandle;
451     },
453     delegate : function(node, subscriber, ce, filter) {
455         var se = this;
457         subscriber[_DEL_MOVE_END_HANDLE] = node.delegate(EVENT[END],
458             function(e) {
459                 se._onEnd(e, node, subscriber, ce, true);
460             },
461             filter);
462     },
464     detachDelegate : function(node, subscriber, ce, filter) {
465         var handle = subscriber[_DEL_MOVE_END_HANDLE];
467         if (handle) {
468             handle.detach();
469             subscriber[_DEL_MOVE_END_HANDLE] = null;
470         }
472     },
474     detach : function (node, subscriber, ce) {
475         var endHandle = subscriber[_MOVE_END_HANDLE];
476     
477         if (endHandle) {
478             endHandle.detach();
479             subscriber[_MOVE_END_HANDLE] = null;
480         }
481     },
483     processArgs : function(args, delegate) {
484         return _defArgsProcessor(this, args, delegate);
485     },
487     _onEnd : function(e, node, subscriber, ce, delegate) {
489         if (delegate) {
490             node = e[CURRENT_TARGET];
491         }
493         var fireMoveEnd = subscriber._extra.standAlone || node.getData(_MOVE) || node.getData(_MOVE_START),
494             preventDefault = subscriber._extra.preventDefault;
496         if (fireMoveEnd) {
498             if (e.changedTouches) {
499                 if (e.changedTouches.length === 1) {
500                     _normTouchFacade(e, e.changedTouches[0]);                    
501                 } else {
502                     fireMoveEnd = false;
503                 }
504             }
506             if (fireMoveEnd) {
508                 _prevent(e, preventDefault);
510                 e.type = GESTURE_MOVE_END;
511                 ce.fire(e);
513                 node.clearData(_MOVE_START);
514                 node.clearData(_MOVE);
515             }
516         }
517     },
519     PREVENT_DEFAULT : false
523 }, '3.5.1' ,{requires:['node-base','event-touch','event-synthetic']});