~launchpad-pqm/lazr-js/toolchain

« back to all changes in this revision

Viewing changes to src-js/lazrjs/yui/event-gestures/event-move.js

  • Committer: Sidnei da Silva
  • Date: 2009-11-16 00:51:29 UTC
  • mto: This revision was merged to the branch mainline in revision 154.
  • Revision ID: sidnei.da.silva@canonical.com-20091116005129-8ibwjlboa38glaw5
- Improved generation of skin modules and revamped combo service to make it more twisty.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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.2.0
6
 
build: 2676
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] = touch[TARGET] || touchFacade[TARGET];
82
 
        touchFacade[CURRENT_TARGET] = touch[CURRENT_TARGET] || touchFacade[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.2.0' ,{requires:['node-base','event-touch','event-synthetic']});