Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
YUI.add('event-move', function (Y, NAME) {
2
 
3
/**
4
 * Adds lower level support for "gesturemovestart", "gesturemove" and "gesturemoveend" events, which can be used to create drag/drop
5
 * interactions which work across touch and mouse input devices. They correspond to "touchstart", "touchmove" and "touchend" on a touch input
6
 * device, and "mousedown", "mousemove", "mouseup" on a mouse based input device.
7
 *
8
 * <p>Documentation for the gesturemove triplet of events can be found on the <a href="../classes/YUI.html#event_gesturemove">YUI</a> global,
9
 * along with the other supported events.</p>
10
 
11
 @example
12
 
13
     YUI().use('event-move', function (Y) {
14
         Y.one('#myNode').on('gesturemovestart', function (e) {
15
         });
16
         Y.one('#myNode').on('gesturemove', function (e) {
17
         });
18
         Y.one('#myNode').on('gesturemoveend', function (e) {
19
         });
20
     });
21
 
22
 * @module event-gestures
23
 * @submodule event-move
24
 */
25
 
26
 
27
 var GESTURE_MAP = Y.Event._GESTURE_MAP,
28
     EVENT = {
29
         start: GESTURE_MAP.start,
30
         end: GESTURE_MAP.end,
31
         move: GESTURE_MAP.move
32
     },
33
    START = "start",
34
    MOVE = "move",
35
    END = "end",
36
 
37
    GESTURE_MOVE = "gesture" + MOVE,
38
    GESTURE_MOVE_END = GESTURE_MOVE + END,
39
    GESTURE_MOVE_START = GESTURE_MOVE + START,
40
 
41
    _MOVE_START_HANDLE = "_msh",
42
    _MOVE_HANDLE = "_mh",
43
    _MOVE_END_HANDLE = "_meh",
44
 
45
    _DEL_MOVE_START_HANDLE = "_dmsh",
46
    _DEL_MOVE_HANDLE = "_dmh",
47
    _DEL_MOVE_END_HANDLE = "_dmeh",
48
 
49
    _MOVE_START = "_ms",
50
    _MOVE = "_m",
51
 
52
    MIN_TIME = "minTime",
53
    MIN_DISTANCE = "minDistance",
54
    PREVENT_DEFAULT = "preventDefault",
55
    BUTTON = "button",
56
    OWNER_DOCUMENT = "ownerDocument",
57
 
58
    CURRENT_TARGET = "currentTarget",
59
    TARGET = "target",
60
 
61
    NODE_TYPE = "nodeType",
62
    _getTouchAction = function(win) {
63
        var touchAction;
64
        if(win) {
65
            if("PointerEvent" in win) {
66
                touchAction = "touchAction";
67
            } else if("msPointerEnabled" in win.navigator) {
68
                touchAction = "msTouchAction";
69
            }
70
        }
71
        return touchAction;
72
    },
73
    TOUCH_ACTION = _getTouchAction(Y.config.win),
74
    SUPPORTS_POINTER = (TOUCH_ACTION === "msTouchAction" || TOUCH_ACTION === "touchAction"),
75
    MS_TOUCH_ACTION_COUNT = 'msTouchActionCount',
76
    MS_INIT_TOUCH_ACTION = 'msInitTouchAction',
77
 
78
    _defArgsProcessor = function(se, args, delegate) {
79
        var iConfig = (delegate) ? 4 : 3,
80
            config = (args.length > iConfig) ? Y.merge(args.splice(iConfig,1)[0]) : {};
81
 
82
        if (!(PREVENT_DEFAULT in config)) {
83
            config[PREVENT_DEFAULT] = se.PREVENT_DEFAULT;
84
        }
85
 
86
        return config;
87
    },
88
 
89
    _getRoot = function(node, subscriber) {
90
        return subscriber._extra.root || (node.get(NODE_TYPE) === 9) ? node : node.get(OWNER_DOCUMENT);
91
    },
92
 
93
    //Checks to see if the node is the document, and if it is, returns the documentElement.
94
    _checkDocumentElem = function(node) {
95
        var elem = node.getDOMNode();
96
        if (node.compareTo(Y.config.doc) && elem.documentElement) {
97
            return elem.documentElement;
98
        }
99
        else {
100
            return false;
101
        }
102
    },
103
 
104
    _normTouchFacade = function(touchFacade, touch, params) {
105
        touchFacade.pageX = touch.pageX;
106
        touchFacade.pageY = touch.pageY;
107
        touchFacade.screenX = touch.screenX;
108
        touchFacade.screenY = touch.screenY;
109
        touchFacade.clientX = touch.clientX;
110
        touchFacade.clientY = touch.clientY;
111
        touchFacade[TARGET] = touchFacade[TARGET] || touch[TARGET];
112
        touchFacade[CURRENT_TARGET] = touchFacade[CURRENT_TARGET] || touch[CURRENT_TARGET];
113
 
114
        touchFacade[BUTTON] = (params && params[BUTTON]) || 1; // default to left (left as per vendors, not W3C which is 0)
115
    },
116
 
117
    /*
118
    In IE10 touch mode, gestures will not work properly unless the -ms-touch-action CSS property is set to something other than 'auto'. Read http://msdn.microsoft.com/en-us/library/windows/apps/hh767313.aspx for more info. To get around this, we set -ms-touch-action: none which is the same as e.preventDefault() on touch environments. This tells the browser to fire DOM events for all touch events, and not perform any default behavior.
119
 
120
    The user can over-ride this by setting a more lenient -ms-touch-action property on a node (such as pan-x, pan-y, etc.) via CSS when subscribing to the 'gesturemovestart' event.
121
    */
122
    _setTouchActions = function (node) {
123
        var elem = node.getDOMNode(),
124
            num = node.getData(MS_TOUCH_ACTION_COUNT);
125
 
126
        //Checks to see if msTouchAction is supported.
127
        if (SUPPORTS_POINTER) {
128
            if (!num) {
129
                num = 0;
130
                node.setData(MS_INIT_TOUCH_ACTION, elem.style[TOUCH_ACTION]);
131
            }
132
            elem.style[TOUCH_ACTION] = Y.Event._DEFAULT_TOUCH_ACTION;
133
            num++;
134
            node.setData(MS_TOUCH_ACTION_COUNT, num);
135
        }
136
    },
137
 
138
    /*
139
    Resets the element's -ms-touch-action property back to the original value, This is called on detach() and detachDelegate().
140
    */
141
    _unsetTouchActions = function (node) {
142
        var elem = node.getDOMNode(),
143
            num = node.getData(MS_TOUCH_ACTION_COUNT),
144
            initTouchAction = node.getData(MS_INIT_TOUCH_ACTION);
145
 
146
        if (SUPPORTS_POINTER) {
147
            num--;
148
            node.setData(MS_TOUCH_ACTION_COUNT, num);
149
            if (num === 0 && elem.style[TOUCH_ACTION] !== initTouchAction) {
150
                elem.style[TOUCH_ACTION] = initTouchAction;
151
            }
152
        }
153
    },
154
 
155
    _prevent = function(e, preventDefault) {
156
        if (preventDefault) {
157
            // preventDefault is a boolean or a function
158
            if (!preventDefault.call || preventDefault(e)) {
159
                e.preventDefault();
160
            }
161
        }
162
    },
163
 
164
    define = Y.Event.define;
165
    Y.Event._DEFAULT_TOUCH_ACTION = 'none';
166
 
167
/**
168
 * Sets up a "gesturemovestart" event, that is fired on touch devices in response to a single finger "touchstart",
169
 * and on mouse based devices in response to a "mousedown". The subscriber can specify the minimum time
170
 * and distance thresholds which should be crossed before the "gesturemovestart" is fired and for the mouse,
171
 * which button should initiate a "gesturemovestart". This event can also be listened for using node.delegate().
172
 *
173
 * <p>It is recommended that you use Y.bind to set up context and additional arguments for your event handler,
174
 * however if you want to pass the context and arguments as additional signature arguments to on/delegate,
175
 * 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>
176
 *
177
 * @event gesturemovestart
178
 * @for YUI
179
 * @param type {string} "gesturemovestart"
180
 * @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.
181
 * @param cfg {Object} Optional. An object which specifies:
182
 *
183
 * <dl>
184
 * <dt>minDistance (defaults to 0)</dt>
185
 * <dd>The minimum distance threshold which should be crossed before the gesturemovestart is fired</dd>
186
 * <dt>minTime (defaults to 0)</dt>
187
 * <dd>The minimum time threshold for which the finger/mouse should be help down before the gesturemovestart is fired</dd>
188
 * <dt>button (no default)</dt>
189
 * <dd>In the case of a mouse input device, if the event should only be fired for a specific mouse button.</dd>
190
 * <dt>preventDefault (defaults to false)</dt>
191
 * <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
192
 * 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>
193
 * </dl>
194
 *
195
 * @return {EventHandle} the detach handle
196
 */
197
 
198
define(GESTURE_MOVE_START, {
199
 
200
    on: function (node, subscriber, ce) {
201
 
202
        //Set -ms-touch-action on IE10 and set preventDefault to true
203
        if (!_checkDocumentElem(node)) {
204
            _setTouchActions(node);
205
        }
206
 
207
        subscriber[_MOVE_START_HANDLE] = node.on(EVENT[START],
208
            this._onStart,
209
            this,
210
            node,
211
            subscriber,
212
            ce);
213
    },
214
 
215
    delegate : function(node, subscriber, ce, filter) {
216
 
217
        var se = this;
218
 
219
        subscriber[_DEL_MOVE_START_HANDLE] = node.delegate(EVENT[START],
220
            function(e) {
221
                se._onStart(e, node, subscriber, ce, true);
222
            },
223
            filter);
224
    },
225
 
226
    detachDelegate : function(node, subscriber, ce, filter) {
227
        var handle = subscriber[_DEL_MOVE_START_HANDLE];
228
 
229
        if (handle) {
230
            handle.detach();
231
            subscriber[_DEL_MOVE_START_HANDLE] = null;
232
        }
233
 
234
        if (!_checkDocumentElem(node)) {
235
            _unsetTouchActions(node);
236
        }
237
    },
238
 
239
    detach: function (node, subscriber, ce) {
240
        var startHandle = subscriber[_MOVE_START_HANDLE];
241
 
242
        if (startHandle) {
243
            startHandle.detach();
244
            subscriber[_MOVE_START_HANDLE] = null;
245
        }
246
 
247
        if (!_checkDocumentElem(node)) {
248
            _unsetTouchActions(node);
249
        }
250
    },
251
 
252
    processArgs : function(args, delegate) {
253
        var params = _defArgsProcessor(this, args, delegate);
254
 
255
        if (!(MIN_TIME in params)) {
256
            params[MIN_TIME] = this.MIN_TIME;
257
        }
258
 
259
        if (!(MIN_DISTANCE in params)) {
260
            params[MIN_DISTANCE] = this.MIN_DISTANCE;
261
        }
262
 
263
        return params;
264
    },
265
 
266
    _onStart : function(e, node, subscriber, ce, delegate) {
267
 
268
        if (delegate) {
269
            node = e[CURRENT_TARGET];
270
        }
271
 
272
        var params = subscriber._extra,
273
            fireStart = true,
274
            minTime = params[MIN_TIME],
275
            minDistance = params[MIN_DISTANCE],
276
            button = params.button,
277
            preventDefault = params[PREVENT_DEFAULT],
278
            root = _getRoot(node, subscriber),
279
            startXY;
280
 
281
        if (e.touches) {
282
            if (e.touches.length === 1) {
283
                _normTouchFacade(e, e.touches[0], params);
284
            } else {
285
                fireStart = false;
286
            }
287
        } else {
288
            fireStart = (button === undefined) || (button === e.button);
289
        }
290
 
291
 
292
        if (fireStart) {
293
 
294
            _prevent(e, preventDefault);
295
 
296
            if (minTime === 0 || minDistance === 0) {
297
                this._start(e, node, ce, params);
298
 
299
            } else {
300
 
301
                startXY = [e.pageX, e.pageY];
302
 
303
                if (minTime > 0) {
304
 
305
 
306
                    params._ht = Y.later(minTime, this, this._start, [e, node, ce, params]);
307
 
308
                    params._hme = root.on(EVENT[END], Y.bind(function() {
309
                        this._cancel(params);
310
                    }, this));
311
                }
312
 
313
                if (minDistance > 0) {
314
 
315
 
316
                    params._hm = root.on(EVENT[MOVE], Y.bind(function(em) {
317
                        if (Math.abs(em.pageX - startXY[0]) > minDistance || Math.abs(em.pageY - startXY[1]) > minDistance) {
318
                            this._start(e, node, ce, params);
319
                        }
320
                    }, this));
321
                }
322
            }
323
        }
324
    },
325
 
326
    _cancel : function(params) {
327
        if (params._ht) {
328
            params._ht.cancel();
329
            params._ht = null;
330
        }
331
        if (params._hme) {
332
            params._hme.detach();
333
            params._hme = null;
334
        }
335
        if (params._hm) {
336
            params._hm.detach();
337
            params._hm = null;
338
        }
339
    },
340
 
341
    _start : function(e, node, ce, params) {
342
 
343
        if (params) {
344
            this._cancel(params);
345
        }
346
 
347
        e.type = GESTURE_MOVE_START;
348
 
349
 
350
        node.setData(_MOVE_START, e);
351
        ce.fire(e);
352
    },
353
 
354
    MIN_TIME : 0,
355
    MIN_DISTANCE : 0,
356
    PREVENT_DEFAULT : false
357
});
358
 
359
/**
360
 * Sets up a "gesturemove" event, that is fired on touch devices in response to a single finger "touchmove",
361
 * and on mouse based devices in response to a "mousemove".
362
 *
363
 * <p>By default this event is only fired when the same node
364
 * has received a "gesturemovestart" event. The subscriber can set standAlone to true, in the configuration properties,
365
 * if they want to listen for this event without an initial "gesturemovestart".</p>
366
 *
367
 * <p>By default this event sets up it's internal "touchmove" and "mousemove" DOM listeners on the document element. The subscriber
368
 * can set the root configuration property, to specify which node to attach DOM listeners to, if different from the document.</p>
369
 *
370
 * <p>This event can also be listened for using node.delegate().</p>
371
 *
372
 * <p>It is recommended that you use Y.bind to set up context and additional arguments for your event handler,
373
 * however if you want to pass the context and arguments as additional signature arguments to on/delegate,
374
 * 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>
375
 *
376
 * @event gesturemove
377
 * @for YUI
378
 * @param type {string} "gesturemove"
379
 * @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.
380
 * @param cfg {Object} Optional. An object which specifies:
381
 * <dl>
382
 * <dt>standAlone (defaults to false)</dt>
383
 * <dd>true, if the subscriber should be notified even if a "gesturemovestart" has not occured on the same node.</dd>
384
 * <dt>root (defaults to document)</dt>
385
 * <dd>The node to which the internal DOM listeners should be attached.</dd>
386
 * <dt>preventDefault (defaults to false)</dt>
387
 * <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>
388
 * </dl>
389
 *
390
 * @return {EventHandle} the detach handle
391
 */
392
define(GESTURE_MOVE, {
393
 
394
    on : function (node, subscriber, ce) {
395
        var moveHandle,
396
            root;
397
 
398
        // if the passed node is NOT the document itself, modify the ms-pointer
399
        // behavior to prevent scrolling, highlighting, etc.
400
        if (!_checkDocumentElem(node)) {
401
            _setTouchActions(node);
402
        }
403
 
404
        root = _getRoot(node, subscriber, EVENT[MOVE]);
405
 
406
        moveHandle = root.on(EVENT[MOVE],
407
            this._onMove,
408
            this,
409
            node,
410
            subscriber,
411
            ce);
412
 
413
        subscriber[_MOVE_HANDLE] = moveHandle;
414
 
415
    },
416
 
417
    delegate : function(node, subscriber, ce, filter) {
418
 
419
        var se = this;
420
 
421
        subscriber[_DEL_MOVE_HANDLE] = node.delegate(EVENT[MOVE],
422
            function(e) {
423
                se._onMove(e, node, subscriber, ce, true);
424
            },
425
            filter);
426
    },
427
 
428
    detach : function (node, subscriber, ce) {
429
        var moveHandle = subscriber[_MOVE_HANDLE];
430
 
431
        if (moveHandle) {
432
            moveHandle.detach();
433
            subscriber[_MOVE_HANDLE] = null;
434
        }
435
 
436
        if (!_checkDocumentElem(node)) {
437
            _unsetTouchActions(node);
438
        }
439
    },
440
 
441
    detachDelegate : function(node, subscriber, ce, filter) {
442
        var handle = subscriber[_DEL_MOVE_HANDLE];
443
 
444
        if (handle) {
445
            handle.detach();
446
            subscriber[_DEL_MOVE_HANDLE] = null;
447
        }
448
 
449
        if (!_checkDocumentElem(node)) {
450
            _unsetTouchActions(node);
451
        }
452
 
453
    },
454
 
455
    processArgs : function(args, delegate) {
456
        return _defArgsProcessor(this, args, delegate);
457
    },
458
 
459
    _onMove : function(e, node, subscriber, ce, delegate) {
460
 
461
        if (delegate) {
462
            node = e[CURRENT_TARGET];
463
        }
464
 
465
        var fireMove = subscriber._extra.standAlone || node.getData(_MOVE_START),
466
            preventDefault = subscriber._extra.preventDefault;
467
 
468
 
469
        if (fireMove) {
470
 
471
            if (e.touches) {
472
                if (e.touches.length === 1) {
473
                    _normTouchFacade(e, e.touches[0]);
474
                } else {
475
                    fireMove = false;
476
                }
477
            }
478
 
479
            if (fireMove) {
480
 
481
                _prevent(e, preventDefault);
482
 
483
 
484
                e.type = GESTURE_MOVE;
485
                ce.fire(e);
486
            }
487
        }
488
    },
489
 
490
    PREVENT_DEFAULT : false
491
});
492
 
493
/**
494
 * Sets up a "gesturemoveend" event, that is fired on touch devices in response to a single finger "touchend",
495
 * and on mouse based devices in response to a "mouseup".
496
 *
497
 * <p>By default this event is only fired when the same node
498
 * has received a "gesturemove" or "gesturemovestart" event. The subscriber can set standAlone to true, in the configuration properties,
499
 * if they want to listen for this event without a preceding "gesturemovestart" or "gesturemove".</p>
500
 *
501
 * <p>By default this event sets up it's internal "touchend" and "mouseup" DOM listeners on the document element. The subscriber
502
 * can set the root configuration property, to specify which node to attach DOM listeners to, if different from the document.</p>
503
 *
504
 * <p>This event can also be listened for using node.delegate().</p>
505
 *
506
 * <p>It is recommended that you use Y.bind to set up context and additional arguments for your event handler,
507
 * however if you want to pass the context and arguments as additional signature arguments to on/delegate,
508
 * 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>
509
 *
510
 *
511
 * @event gesturemoveend
512
 * @for YUI
513
 * @param type {string} "gesturemoveend"
514
 * @param fn {function} The method the event invokes. It receives the event facade of the underlying DOM event (mouseup or touchend.changedTouches[0]).
515
 * @param cfg {Object} Optional. An object which specifies:
516
 * <dl>
517
 * <dt>standAlone (defaults to false)</dt>
518
 * <dd>true, if the subscriber should be notified even if a "gesturemovestart" or "gesturemove" has not occured on the same node.</dd>
519
 * <dt>root (defaults to document)</dt>
520
 * <dd>The node to which the internal DOM listeners should be attached.</dd>
521
 * <dt>preventDefault (defaults to false)</dt>
522
 * <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>
523
 * </dl>
524
 *
525
 * @return {EventHandle} the detach handle
526
 */
527
define(GESTURE_MOVE_END, {
528
 
529
    on : function (node, subscriber, ce) {
530
        var endHandle,
531
            root;
532
 
533
        if (!_checkDocumentElem(node)) {
534
            _setTouchActions(node);
535
        }
536
 
537
        root = _getRoot(node, subscriber);
538
        endHandle = root.on(EVENT[END],
539
            this._onEnd,
540
            this,
541
            node,
542
            subscriber,
543
            ce);
544
 
545
        subscriber[_MOVE_END_HANDLE] = endHandle;
546
    },
547
 
548
    delegate : function(node, subscriber, ce, filter) {
549
 
550
        var se = this;
551
 
552
        subscriber[_DEL_MOVE_END_HANDLE] = node.delegate(EVENT[END],
553
            function(e) {
554
                se._onEnd(e, node, subscriber, ce, true);
555
            },
556
            filter);
557
    },
558
 
559
    detachDelegate : function(node, subscriber, ce, filter) {
560
        var handle = subscriber[_DEL_MOVE_END_HANDLE];
561
 
562
        if (handle) {
563
            handle.detach();
564
            subscriber[_DEL_MOVE_END_HANDLE] = null;
565
        }
566
 
567
        if (!_checkDocumentElem(node)) {
568
            _unsetTouchActions(node);
569
        }
570
 
571
    },
572
 
573
    detach : function (node, subscriber, ce) {
574
        var endHandle = subscriber[_MOVE_END_HANDLE];
575
 
576
        if (endHandle) {
577
            endHandle.detach();
578
            subscriber[_MOVE_END_HANDLE] = null;
579
        }
580
 
581
        if (!_checkDocumentElem(node)) {
582
            _unsetTouchActions(node);
583
        }
584
    },
585
 
586
    processArgs : function(args, delegate) {
587
        return _defArgsProcessor(this, args, delegate);
588
    },
589
 
590
    _onEnd : function(e, node, subscriber, ce, delegate) {
591
 
592
        if (delegate) {
593
            node = e[CURRENT_TARGET];
594
        }
595
 
596
        var fireMoveEnd = subscriber._extra.standAlone || node.getData(_MOVE) || node.getData(_MOVE_START),
597
            preventDefault = subscriber._extra.preventDefault;
598
 
599
        if (fireMoveEnd) {
600
 
601
            if (e.changedTouches) {
602
                if (e.changedTouches.length === 1) {
603
                    _normTouchFacade(e, e.changedTouches[0]);
604
                } else {
605
                    fireMoveEnd = false;
606
                }
607
            }
608
 
609
            if (fireMoveEnd) {
610
 
611
                _prevent(e, preventDefault);
612
 
613
                e.type = GESTURE_MOVE_END;
614
                ce.fire(e);
615
 
616
                node.clearData(_MOVE_START);
617
                node.clearData(_MOVE);
618
            }
619
        }
620
    },
621
 
622
    PREVENT_DEFAULT : false
623
});
624
 
625
 
626
}, '3.18.1', {"requires": ["node-base", "event-touch", "event-synthetic"]});