NOBUG: Fixed file access permissions
[moodle.git] / lib / yuilib / 3.13.0 / event-tap / event-tap-debug.js
blob1b4b2d67afe43d5b64285282bbd1e3b429caca51
1 /*
2 YUI 3.13.0 (build 508226d)
3 Copyright 2013 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
6 */
8 YUI.add('event-tap', function (Y, NAME) {
10 /**
11 The tap module provides a gesture events, "tap", which normalizes user interactions
12 across touch and mouse or pointer based input devices.  This 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.
16 'tap' is like a touchscreen 'click', only it requires much less finger-down time since it listens to touch events,
17 but reverts to mouse events if touch is not supported.
19 @example
21     YUI().use('event-tap', function (Y) {
22         Y.one('#my-button').on('tap', function (e) {
23             Y.log('Button was tapped on');
24         });
25     });
27 @module event
28 @submodule event-tap
29 @author Andres Garza, matuzak and tilo mitra
30 @since 3.7.0
33 var doc = Y.config.doc,
34     GESTURE_MAP = Y.Event._GESTURE_MAP,
35     EVT_START = GESTURE_MAP.start,
36     EVT_TAP = 'tap',
38     HANDLES = {
39         START: 'Y_TAP_ON_START_HANDLE',
40         END: 'Y_TAP_ON_END_HANDLE',
41         CANCEL: 'Y_TAP_ON_CANCEL_HANDLE'
42     };
44 function detachHandles(subscription, handles) {
45     handles = handles || Y.Object.values(HANDLES);
47     Y.Array.each(handles, function (item) {
48         var handle = subscription[item];
49         if (handle) {
50             handle.detach();
51             subscription[item] = null;
52         }
53     });
58 /**
59 Sets up a "tap" event, that is fired on touch devices in response to a tap event (finger down, finder up).
60 This event can be used instead of listening for click events which have a 500ms delay on most touch devices.
61 This event can also be listened for using node.delegate().
63 @event tap
64 @param type {string} "tap"
65 @param fn {function} The method the event invokes. It receives the event facade of the underlying DOM event.
66 @for Event
67 @return {EventHandle} the detach handle
69 Y.Event.define(EVT_TAP, {
70     publishConfig: {
71         preventedFn: function (e) {
72             var sub = e.target.once('click', function (click) {
73                 click.preventDefault();
74             });
76             // Make sure to detach the subscription during the next event loop
77             // so this doesn't `preventDefault()` on the wrong click event.
78             setTimeout(function () {
79                 sub.detach();
80             //Setting this to `0` causes the detachment to occur before the click
81             //comes in on Android 4.0.3-4.0.4. 100ms seems to be a reliable number here
82             //that works across the board.
83             }, 100);
84         }
85     },
87     processArgs: function (args, isDelegate) {
89         //if we return for the delegate use case, then the `filter` argument
90         //returns undefined, and we have to get the filter from sub._extra[0] (ugly)
92         if (!isDelegate) {
93             var extra = args[3];
94             // remove the extra arguments from the array as specified by
95             // http://yuilibrary.com/yui/docs/event/synths.html
96             args.splice(3,1);
97             return extra;
98         }
99     },
100     /**
101     This function should set up the node that will eventually fire the event.
103     Usage:
105         node.on('tap', function (e) {
106             Y.log('the node was tapped on');
107         });
109     @method on
110     @param {Y.Node} node
111     @param {Array} subscription
112     @param {Boolean} notifier
113     @public
114     @static
115     **/
116     on: function (node, subscription, notifier) {
117         subscription[HANDLES.START] = node.on(EVT_START, this._start, this, node, subscription, notifier);
118     },
120     /**
121     Detaches all event subscriptions set up by the event-tap module
123     @method detach
124     @param {Y.Node} node
125     @param {Array} subscription
126     @param {Boolean} notifier
127     @public
128     @static
129     **/
130     detach: function (node, subscription, notifier) {
131         detachHandles(subscription);
132     },
134     /**
135     Event delegation for the 'tap' event. The delegated event will use a
136     supplied selector or filtering function to test if the event references at least one
137     node that should trigger the subscription callback.
139     Usage:
141         node.delegate('tap', function (e) {
142             Y.log('li a inside node was tapped.');
143         }, 'li a');
145     @method delegate
146     @param {Y.Node} node
147     @param {Array} subscription
148     @param {Boolean} notifier
149     @param {String | Function} filter
150     @public
151     @static
152     **/
153     delegate: function (node, subscription, notifier, filter) {
154         subscription[HANDLES.START] = node.delegate(EVT_START, function (e) {
155             this._start(e, node, subscription, notifier, true);
156         }, filter, this);
157     },
159     /**
160     Detaches the delegated event subscriptions set up by the event-tap module.
161     Only used if you use node.delegate(...) instead of node.on(...);
163     @method detachDelegate
164     @param {Y.Node} node
165     @param {Array} subscription
166     @param {Boolean} notifier
167     @public
168     @static
169     **/
170     detachDelegate: function (node, subscription, notifier) {
171         detachHandles(subscription);
172     },
174     /**
175     Called when the monitor(s) are tapped on, either through touchstart or mousedown.
177     @method _start
178     @param {DOMEventFacade} event
179     @param {Y.Node} node
180     @param {Array} subscription
181     @param {Boolean} notifier
182     @param {Boolean} delegate
183     @protected
184     @static
185     **/
186     _start: function (event, node, subscription, notifier, delegate) {
188         var context = {
189                 canceled: false,
190                 eventType: event.type
191             },
192             preventMouse = subscription.preventMouse || false;
194         //move ways to quit early to the top.
195         // no right clicks
196         if (event.button && event.button === 3) {
197             return;
198         }
200         // for now just support a 1 finger count (later enhance via config)
201         if (event.touches && event.touches.length !== 1) {
202             return;
203         }
205         context.node = delegate ? event.currentTarget : node;
207         //There is a double check in here to support event simulation tests, in which
208         //event.touches can be undefined when simulating 'touchstart' on touch devices.
209         if (event.touches) {
210           context.startXY = [ event.touches[0].pageX, event.touches[0].pageY ];
211         }
212         else {
213           context.startXY = [ event.pageX, event.pageY ];
214         }
216         //If `onTouchStart()` was called by a touch event, set up touch event subscriptions.
217         //Otherwise, set up mouse/pointer event event subscriptions.
218         if (event.touches) {
220             subscription[HANDLES.END] = node.once('touchend', this._end, this, node, subscription, notifier, delegate, context);
221             subscription[HANDLES.CANCEL] = node.once('touchcancel', this.detach, this, node, subscription, notifier, delegate, context);
223             //Since this is a touch* event, there will be corresponding mouse events
224             //that will be fired. We don't want these events to get picked up and fire
225             //another `tap` event, so we'll set this variable to `true`.
226             subscription.preventMouse = true;
227         }
229         //Only add these listeners if preventMouse is `false`
230         //ie: not when touch events have already been subscribed to
231         else if (context.eventType.indexOf('mouse') !== -1 && !preventMouse) {
232             subscription[HANDLES.END] = node.once('mouseup', this._end, this, node, subscription, notifier, delegate, context);
233             subscription[HANDLES.CANCEL] = node.once('mousecancel', this.detach, this, node, subscription, notifier, delegate, context);
234         }
236         //If a mouse event comes in after a touch event, it will go in here and
237         //reset preventMouse to `true`.
238         //If a mouse event comes in without a prior touch event, preventMouse will be
239         //false in any case, so this block doesn't do anything.
240         else if (context.eventType.indexOf('mouse') !== -1 && preventMouse) {
241             subscription.preventMouse = false;
242         }
244         else if (context.eventType.indexOf('MSPointer') !== -1) {
245             subscription[HANDLES.END] = node.once('MSPointerUp', this._end, this, node, subscription, notifier, delegate, context);
246             subscription[HANDLES.CANCEL] = node.once('MSPointerCancel', this.detach, this, node, subscription, notifier, delegate, context);
247         }
249     },
252     /**
253     Called when the monitor(s) fires a touchend event (or the mouse equivalent).
254     This method fires the 'tap' event if certain requirements are met.
256     @method _end
257     @param {DOMEventFacade} event
258     @param {Y.Node} node
259     @param {Array} subscription
260     @param {Boolean} notifier
261     @param {Boolean} delegate
262     @param {Object} context
263     @protected
264     @static
265     **/
266     _end: function (event, node, subscription, notifier, delegate, context) {
267         var startXY = context.startXY,
268             endXY,
269             clientXY,
270             sensitivity = 15;
272         if (subscription._extra && subscription._extra.sensitivity >= 0) {
273             sensitivity = subscription._extra.sensitivity;
274         }
276         //There is a double check in here to support event simulation tests, in which
277         //event.touches can be undefined when simulating 'touchstart' on touch devices.
278         if (event.changedTouches) {
279           endXY = [ event.changedTouches[0].pageX, event.changedTouches[0].pageY ];
280           clientXY = [event.changedTouches[0].clientX, event.changedTouches[0].clientY];
281         }
282         else {
283           endXY = [ event.pageX, event.pageY ];
284           clientXY = [event.clientX, event.clientY];
285         }
287         // make sure mouse didn't move
288         if (Math.abs(endXY[0] - startXY[0]) <= sensitivity && Math.abs(endXY[1] - startXY[1]) <= sensitivity) {
290             event.type = EVT_TAP;
291             event.pageX = endXY[0];
292             event.pageY = endXY[1];
293             event.clientX = clientXY[0];
294             event.clientY = clientXY[1];
295             event.currentTarget = context.node;
297             notifier.fire(event);
298         }
300         detachHandles(subscription, [HANDLES.END, HANDLES.CANCEL]);
301     }
305 }, '3.13.0', {"requires": ["node-base", "event-base", "event-touch", "event-synthetic"]});