~launchpad-pqm/lazr-js/toolchain

« back to all changes in this revision

Viewing changes to src-js/lazrjs/yui/scrollview/scrollview-base-debug.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('scrollview-base', function(Y) {
9
 
 
10
 
/**
11
 
 * The scrollview-base module provides a basic ScrollView Widget, without scrollbar indicators
12
 
 *
13
 
 * @module scrollview-base
14
 
 */
15
 
 
16
 
var getClassName = Y.ClassNameManager.getClassName,
17
 
    SCROLLVIEW = 'scrollview',
18
 
    CLASS_NAMES = {
19
 
        vertical: getClassName(SCROLLVIEW, 'vert'),
20
 
        horizontal: getClassName(SCROLLVIEW, 'horiz')
21
 
    },
22
 
    EV_SCROLL_END = 'scrollEnd',
23
 
    EV_SCROLL_FLICK = 'flick',
24
 
 
25
 
    FLICK = EV_SCROLL_FLICK,
26
 
 
27
 
    UI = 'ui',
28
 
    
29
 
    LEFT = "left",
30
 
    TOP = "top",
31
 
    
32
 
    PX = "px",
33
 
 
34
 
    SCROLL_Y = "scrollY",
35
 
    SCROLL_X = "scrollX",
36
 
    BOUNCE = "bounce",
37
 
    
38
 
    DIM_X = "x",
39
 
    DIM_Y = "y",
40
 
 
41
 
    BOUNDING_BOX = "boundingBox",
42
 
    CONTENT_BOX = "contentBox",
43
 
    
44
 
    EMPTY = "",
45
 
    ZERO = "0s",
46
 
    
47
 
    OWNER_DOC = "ownerDocument",
48
 
    MOUSE_UP = "mouseup",
49
 
 
50
 
    IE = Y.UA.ie,
51
 
 
52
 
    NATIVE_TRANSITIONS = Y.Transition.useNative;
53
 
 
54
 
Y.Node.DOM_EVENTS.DOMSubtreeModified = true;
55
 
 
56
 
/**
57
 
 * ScrollView provides a scrollable widget, supporting flick gestures, across both touch and mouse based devices. 
58
 
 *
59
 
 * @class ScrollView
60
 
 * @namespace 
61
 
 * @param config {Object} Object literal with initial attribute values
62
 
 * @extends Widget
63
 
 * @constructor
64
 
 */
65
 
function ScrollView() {
66
 
    ScrollView.superclass.constructor.apply(this, arguments);
67
 
}
68
 
 
69
 
Y.ScrollView = Y.extend(ScrollView, Y.Widget, {
70
 
    
71
 
    // Y.ScrollView prototype
72
 
    
73
 
    /**
74
 
     * Designated initializer
75
 
     *
76
 
     * @method initializer
77
 
     */
78
 
    initializer: function() {
79
 
        this._createEvents();
80
 
 
81
 
        // Cache - they're write once, and not going to change
82
 
        this._cb = this.get(CONTENT_BOX);
83
 
        this._bb = this.get(BOUNDING_BOX);
84
 
    },
85
 
 
86
 
    /** 
87
 
     * Publishes events which occur during the scroll lifecycle
88
 
     *
89
 
     * @method _createEvents
90
 
     * @private
91
 
     */    
92
 
    _createEvents: function() {
93
 
        /**
94
 
         * Notification event fired at the end of a scroll transition
95
 
         * 
96
 
         * @event scrollEnd
97
 
         * @param e {EventFacade} The default event facade.
98
 
         */
99
 
        this.publish(EV_SCROLL_END);
100
 
 
101
 
        /**
102
 
         * Notification event fired at the end of a flick gesture (the flick animation may still be in progress)
103
 
         * 
104
 
         * @event flick
105
 
         * @param e {EventFacade} The default event facade.
106
 
         */
107
 
        this.publish(EV_SCROLL_FLICK);
108
 
    },
109
 
 
110
 
    /** 
111
 
     * Override the contentBox sizing method, since the contentBox height
112
 
     * should not be that of the boundingBox.
113
 
     *
114
 
     * @method _uiSizeCB
115
 
     * @protected
116
 
     */
117
 
    _uiSizeCB: function() {},
118
 
 
119
 
    /**
120
 
     * Content box transition callback
121
 
     *
122
 
     * @method _transitionEnded
123
 
     * @param {Event.Facade} e The event facade
124
 
     * @private
125
 
     */
126
 
    _transitionEnded: function(e) {
127
 
        this.fire(EV_SCROLL_END);
128
 
    },
129
 
 
130
 
    /**
131
 
     * bindUI implementation
132
 
     *
133
 
     * Hooks up events for the widget
134
 
     * @method bindUI
135
 
     */
136
 
    bindUI: function() {
137
 
 
138
 
        var cb = this._cb,
139
 
            bb = this._bb,
140
 
            flick = this.get(FLICK); 
141
 
 
142
 
        bb.on('gesturemovestart', Y.bind(this._onGestureMoveStart, this));
143
 
 
144
 
        // IE SELECT HACK. See if we can do this non-natively and in the gesture for a future release.
145
 
        if (IE) {
146
 
            this._nativeBody = Y.Node.getDOMNode(Y.one("body", cb.get("ownerDocument")));
147
 
            this._cbDoc = cb.get(OWNER_DOC);
148
 
 
149
 
            cb.on("mousedown", function() {
150
 
                this._selectstart = this._nativeBody.onselectstart;
151
 
                this._nativeBody.onselectstart = this._iePreventSelect;
152
 
                this._cbDoc.once(MOUSE_UP, this._ieRestoreSelect, this);
153
 
            }, this);
154
 
        }
155
 
 
156
 
        // TODO: Fires way to often when using non-native transitions, due to property change
157
 
        if (NATIVE_TRANSITIONS) {
158
 
            cb.on('DOMSubtreeModified', Y.bind(this._uiDimensionsChange, this));
159
 
        }
160
 
 
161
 
        if (flick) {
162
 
            cb.on("flick", Y.bind(this._flick, this), flick);
163
 
        }
164
 
 
165
 
        this.after({
166
 
            'scrollYChange' : this._afterScrollYChange,
167
 
            'scrollXChange' : this._afterScrollXChange,
168
 
            'heightChange'  : this._afterHeightChange,
169
 
            'widthChange'   : this._afterWidthChange,
170
 
            'renderedChange': function() { Y.later(0, this, '_uiDimensionsChange'); } 
171
 
        });
172
 
    },
173
 
    
174
 
    /**
175
 
     * syncUI implementation
176
 
     *
177
 
     * Update the scroll position, based on the current value of scrollY
178
 
     * @method bindUI
179
 
     */
180
 
    syncUI: function() {
181
 
        this.scrollTo(this.get(SCROLL_X), this.get(SCROLL_Y));
182
 
    },
183
 
 
184
 
    /**
185
 
     * Scroll the element to a given y coordinate
186
 
     *
187
 
     * @method scrollTo
188
 
     * @param x {Number} The x-position to scroll to
189
 
     * @param y {Number} The y-position to scroll to
190
 
     * @param duration {Number} Duration, in ms, of the scroll animation (default is 0)
191
 
     * @param easing {String} An easing equation if duration is set
192
 
     */
193
 
    scrollTo: function(x, y, duration, easing) {
194
 
 
195
 
        var cb = this._cb,
196
 
            xSet = (x !== null),
197
 
            ySet = (y !== null),
198
 
            xMove = (xSet) ? x * -1 : 0,
199
 
            yMove = (ySet) ? y * -1 : 0,
200
 
            transition,
201
 
            callback = this._transEndCallback;
202
 
 
203
 
        duration = duration || 0;
204
 
        easing = easing || ScrollView.EASING;
205
 
 
206
 
        if (xSet) {
207
 
            this.set(SCROLL_X, x, { src: UI });
208
 
        }
209
 
 
210
 
        if (ySet) {
211
 
            this.set(SCROLL_Y, y, { src: UI });
212
 
        }
213
 
 
214
 
        if (NATIVE_TRANSITIONS) {
215
 
            // ANDROID WORKAROUND - try and stop existing transition, before kicking off new one.
216
 
            cb.setStyle(ScrollView._TRANSITION_DURATION, ZERO);
217
 
            cb.setStyle(ScrollView._TRANSITION_PROPERTY, EMPTY);
218
 
 
219
 
            // Causes bounce back from 0,0 instead of current translation for bottom/right edge animation
220
 
            // cb.setStyle("WebkitTransform", cb.getComputedStyle("WebkitTransform"));
221
 
        }
222
 
 
223
 
        if (duration !== 0) {
224
 
 
225
 
            transition = {
226
 
                easing : easing,
227
 
                duration : duration/1000
228
 
            };
229
 
 
230
 
            if (NATIVE_TRANSITIONS) {
231
 
                transition.transform = 'translate3D('+ xMove +'px,'+ yMove +'px, 0px)';
232
 
            } else {
233
 
                if (xSet) { transition.left = xMove + PX; }
234
 
                if (ySet) { transition.top = yMove + PX; }
235
 
            }
236
 
 
237
 
            Y.log("Transition: duration, easing:" + [transition.duration, transition.easing], "scrollview");
238
 
 
239
 
            if (!callback) {
240
 
                callback = this._transEndCallback = Y.bind(this._transitionEnded, this);
241
 
            }
242
 
 
243
 
            cb.transition(transition, callback);
244
 
 
245
 
        } else {
246
 
            if (NATIVE_TRANSITIONS) {
247
 
                cb.setStyle('transform', 'translate3D('+ xMove +'px,'+ yMove +'px, 0px)');
248
 
            } else {
249
 
                if (xSet) { cb.setStyle(LEFT, xMove + PX); }
250
 
                if (ySet) { cb.setStyle(TOP, yMove + PX); }
251
 
            }
252
 
        }
253
 
    },
254
 
 
255
 
    /**
256
 
     * Native onselectstart handle to prevent selection in IE
257
 
     *
258
 
     * @method _iePreventSelect
259
 
     * @private
260
 
     */
261
 
    _iePreventSelect : function() {
262
 
        return false;
263
 
    },
264
 
 
265
 
    /**
266
 
     * Restores native onselectstart handle, backed up to prevent selection in IE
267
 
     *
268
 
     * @method _ieRestoreSelect
269
 
     * @private
270
 
     */
271
 
    _ieRestoreSelect : function() {
272
 
        this._nativeBody.onselectstart = this._selectstart;
273
 
    },
274
 
 
275
 
    _preventStart : false,
276
 
 
277
 
    _preventMove : true,
278
 
    
279
 
    _preventEnd : true,
280
 
 
281
 
    /**
282
 
     * gesturemovestart event handler
283
 
     *
284
 
     * @method _onGestureMoveStart
285
 
     * @param e {Event.Facade} The gesturemovestart event facade
286
 
     * @private
287
 
     */
288
 
    _onGestureMoveStart: function(e) {
289
 
 
290
 
        var bb = this._bb;
291
 
 
292
 
        if (this._preventStart) {
293
 
            e.preventDefault();
294
 
        }
295
 
 
296
 
        this._killTimer();
297
 
 
298
 
        this._moveEvt = bb.on('gesturemove', Y.bind(this._onGestureMove, this));
299
 
        this._moveEndEvt = bb.on('gesturemoveend', Y.bind(this._onGestureMoveEnd, this));
300
 
 
301
 
        this._moveStartY = e.clientY + this.get(SCROLL_Y);
302
 
        this._moveStartX = e.clientX + this.get(SCROLL_X);
303
 
 
304
 
        this._moveStartTime = (new Date()).getTime();
305
 
        this._moveStartClientY = this._moveEndClientY = e.clientY;
306
 
        this._moveStartClientX = this._moveEndClientX = e.clientX;
307
 
 
308
 
        /**
309
 
         * Internal state, defines whether or not the scrollview is currently being dragged
310
 
         * 
311
 
         * @property _isDragging
312
 
         * @type boolean
313
 
         * @protected
314
 
         */
315
 
        this._isDragging = false;
316
 
        
317
 
        /**
318
 
         * Internal state, defines whether or not the scrollview is currently animating a flick
319
 
         * 
320
 
         * @property _flicking
321
 
         * @type boolean
322
 
         * @protected
323
 
         */
324
 
        this._flicking = false;
325
 
        
326
 
        /**
327
 
         * Internal state, defines whether or not the scrollview needs to snap to a boundary edge
328
 
         * 
329
 
         * @property _snapToEdge
330
 
         * @type boolean
331
 
         * @protected
332
 
         */
333
 
        this._snapToEdge = false;
334
 
    },    
335
 
    
336
 
    /**
337
 
     * gesturemove event handler
338
 
     *
339
 
     * @method _onGestureMove
340
 
     * @param e {Event.Facade} The gesturemove event facade
341
 
     * @private
342
 
     */
343
 
    _onGestureMove: function(e) {
344
 
 
345
 
        if (this._preventMove) {
346
 
            e.preventDefault();
347
 
        }
348
 
 
349
 
        this._isDragging = true;
350
 
        this._moveEndClientY = e.clientY;
351
 
        this._moveEndClientX = e.clientX;
352
 
        this._lastMoved = (new Date()).getTime();
353
 
 
354
 
        if(this._scrollsVertical) {
355
 
            this.set(SCROLL_Y, -(e.clientY - this._moveStartY));
356
 
        }
357
 
 
358
 
        if(this._scrollsHorizontal) {
359
 
            this.set(SCROLL_X, -(e.clientX - this._moveStartX));
360
 
        }
361
 
    },
362
 
 
363
 
    /**
364
 
     * gestureend event handler
365
 
     *
366
 
     * @method _onGestureMoveEnd
367
 
     * @param e {Event.Facade} The gesturemoveend event facade
368
 
     * @private
369
 
     */
370
 
    _onGestureMoveEnd: function(e) {
371
 
 
372
 
        if (this._preventEnd) {
373
 
            e.preventDefault();
374
 
        }
375
 
 
376
 
        var minY = this._minScrollY,
377
 
            maxY = this._maxScrollY,
378
 
            minX = this._minScrollX,
379
 
            maxX = this._maxScrollX,
380
 
            startPoint = this._scrollsVertical ? this._moveStartClientY : this._moveStartClientX,
381
 
            endPoint = this._scrollsVertical ? this._moveEndClientY : this._moveEndClientX,
382
 
            distance = startPoint - endPoint;
383
 
 
384
 
        this._moveEvt.detach();
385
 
        this._moveEndEvt.detach();
386
 
        
387
 
        /**
388
 
         * Internal state, defines whether or not the scrollview has been scrolled half it's width/height
389
 
         * 
390
 
         * @property _scrolledHalfway
391
 
         * @type boolean
392
 
         * @protected
393
 
         */
394
 
        this._scrolledHalfway = false;
395
 
        this._snapToEdge = false;
396
 
        this._isDragging = false;
397
 
 
398
 
        /**
399
 
         * Contains the distance (postive or negative) in pixels by which the scrollview was last scrolled. This is useful when
400
 
         * setting up click listeners on the scrollview content, which on mouse based devices are always fired, even after a
401
 
         * drag/flick. 
402
 
         * 
403
 
         * <p>Touch based devices don't currently fire a click event, if the finger has been moved (beyond a threshold) so this check isn't required,
404
 
         * if working in a purely touch based environment</p>
405
 
         * 
406
 
         * @property lastScrolledAmt
407
 
         * @type Number
408
 
         * @public
409
 
         */
410
 
        this.lastScrolledAmt = distance;
411
 
 
412
 
        if(this._scrollsHorizontal && Math.abs(distance) > (this.get('width')/2)) {
413
 
            this._scrolledHalfway = true;
414
 
            
415
 
            /**
416
 
             * Internal state, defines whether or not the scrollview has been scrolled in the forward (distance > 0), or backward (distance < 0) direction
417
 
             * 
418
 
             * @property _scrolledForward
419
 
             * @type boolean
420
 
             * @protected
421
 
             */
422
 
            this._scrolledForward = distance > 0;
423
 
        }
424
 
 
425
 
        if(this._scrollsVertical && Math.abs(distance) > (this.get('height')/2)) {
426
 
            this._scrolledHalfway = true;
427
 
            this._scrolledForward = distance > 0;
428
 
        }
429
 
 
430
 
        // Check for minY
431
 
        if(this._scrollsVertical && this.get(SCROLL_Y) < minY) {
432
 
            this._snapToEdge = true;
433
 
            this.set(SCROLL_Y, minY);
434
 
        }
435
 
        
436
 
        // Check for minX
437
 
        if(this._scrollsHorizontal && this.get(SCROLL_X) < minX) {
438
 
            this._snapToEdge = true;
439
 
            this.set(SCROLL_X, minX);
440
 
        }
441
 
        
442
 
        // Check for maxY
443
 
        if(this.get(SCROLL_Y) > maxY) {
444
 
            this._snapToEdge = true;
445
 
            this.set(SCROLL_Y, maxY);
446
 
        }
447
 
        
448
 
        // Check for maxX
449
 
        if(this.get(SCROLL_X) > maxX) {
450
 
            this._snapToEdge = true;
451
 
            this.set(SCROLL_X, maxX);
452
 
        }
453
 
 
454
 
        Y.log("half:" + this._scrolledHalfway + ", fwd:"  + this._scrolledForward, "scrollview");
455
 
 
456
 
        if(this._snapToEdge) {
457
 
            return;
458
 
        }
459
 
 
460
 
        this.fire(EV_SCROLL_END, {
461
 
            onGestureMoveEnd: true
462
 
        });
463
 
 
464
 
        return;
465
 
    },
466
 
 
467
 
    /**
468
 
     * After listener for changes to the scrollY attribute
469
 
     *
470
 
     * @method _afterScrollYChange
471
 
     * @param e {Event.Facade} The event facade
472
 
     * @protected
473
 
     */
474
 
    _afterScrollYChange : function(e) {
475
 
        if(e.src !== UI) {
476
 
            this._uiScrollY(e.newVal, e.duration, e.easing);
477
 
        }
478
 
    },
479
 
 
480
 
    /**
481
 
     * Update the UI when the scrollY attribute changes
482
 
     *
483
 
     * @method _uiScrollY
484
 
     * @param val {Number} The scrollY value
485
 
     * @param duration {Number} The length (in ms) of the scroll animation
486
 
     * @param easing {String} An easing equation, if duration is defined
487
 
     * @protected
488
 
     */
489
 
    _uiScrollY : function(val, duration, easing) {
490
 
        duration = duration || this._snapToEdge ? 400 : 0;
491
 
        easing = easing || this._snapToEdge ? ScrollView.SNAP_EASING : null;
492
 
 
493
 
        this.scrollTo(null, val, duration, easing);
494
 
    },
495
 
 
496
 
    /**
497
 
     * After listener for changes to the scrollX attribute
498
 
     *
499
 
     * @method _afterScrollXChange
500
 
     * @param e {Event.Facade} The event facade
501
 
     * @protected
502
 
     */
503
 
    _afterScrollXChange : function(e) {
504
 
        if(e.src !== UI) {
505
 
            this._uiScrollX(e.newVal, e.duration, e.easing);
506
 
        }
507
 
    },
508
 
 
509
 
    /**
510
 
     * Update the UI when the scrollX attribute changes
511
 
     *
512
 
     * @method _uiScrollX
513
 
     * @param val {Number} The scrollX value
514
 
     * @param duration {Number} The length (in ms) of the scroll animation
515
 
     * @param easing {String} An easing equation, if duration is defined
516
 
     * @protected
517
 
     */
518
 
    _uiScrollX : function(val, duration, easing) {
519
 
        duration = duration || this._snapToEdge ? 400 : 0;
520
 
        easing = easing || this._snapToEdge ? ScrollView.SNAP_EASING : null;
521
 
 
522
 
        this.scrollTo(val, null, duration, easing);
523
 
    },
524
 
    
525
 
    /**
526
 
     * After listener for the height attribute
527
 
     *
528
 
     * @method _afterHeightChange
529
 
     * @param e {Event.Facade} The event facade
530
 
     * @protected
531
 
     */
532
 
    _afterHeightChange: function() {
533
 
        this._uiDimensionsChange();
534
 
    },
535
 
    
536
 
    /**
537
 
     * After listener for the width attribute
538
 
     *
539
 
     * @method _afterWidthChange
540
 
     * @param e {Event.Facade} The event facade
541
 
     * @protected
542
 
     */
543
 
    _afterWidthChange: function() {
544
 
        this._uiDimensionsChange();
545
 
    },
546
 
    
547
 
    /**
548
 
     * This method gets invoked whenever the height or width attributes change,
549
 
     * allowing us to determine which scrolling axes need to be enabled.
550
 
     *
551
 
     * @method _uiDimensionsChange
552
 
     * @protected
553
 
     */
554
 
    _uiDimensionsChange: function() {
555
 
        var bb = this._bb,
556
 
 
557
 
            height = this.get('height'),
558
 
            width = this.get('width'),
559
 
 
560
 
            // Use bb instead of cb. cb doesn't gives us the right results
561
 
            // in FF (due to overflow:hidden)
562
 
            scrollHeight = bb.get('scrollHeight'),
563
 
            scrollWidth = bb.get('scrollWidth');
564
 
 
565
 
        if(height && scrollHeight > height) {            
566
 
            this._scrollsVertical = true;
567
 
            this._maxScrollY = scrollHeight - height;
568
 
            this._minScrollY = 0;
569
 
            this._scrollHeight = scrollHeight;
570
 
            bb.addClass(ScrollView.CLASS_NAMES.vertical);
571
 
        }
572
 
 
573
 
        if(width && scrollWidth > width) {
574
 
            this._scrollsHorizontal = true;
575
 
            this._maxScrollX = scrollWidth - width;
576
 
            this._minScrollX = 0;
577
 
            this._scrollWidth = scrollWidth;
578
 
            bb.addClass(ScrollView.CLASS_NAMES.horizontal);
579
 
        }
580
 
        
581
 
        /**
582
 
         * Internal state, defines whether or not the scrollview can scroll vertically 
583
 
         * 
584
 
         * @property _scrollsVertical
585
 
         * @type boolean
586
 
         * @protected
587
 
         */
588
 
        
589
 
        /**
590
 
         * Internal state, defines the maximum amount that the scrollview can be scrolled along the Y axis 
591
 
         * 
592
 
         * @property _maxScrollY
593
 
         * @type number
594
 
         * @protected
595
 
         */
596
 
 
597
 
        /**
598
 
         * Internal state, defines the minimum amount that the scrollview can be scrolled along the Y axis 
599
 
         * 
600
 
         * @property _minScrollY
601
 
         * @type number
602
 
         * @protected
603
 
         */
604
 
 
605
 
        /**
606
 
         * Internal state, cached scrollHeight, for performance 
607
 
         * 
608
 
         * @property _scrollHeight
609
 
         * @type number
610
 
         * @protected
611
 
         */
612
 
 
613
 
        /**
614
 
         * Internal state, defines whether or not the scrollview can scroll horizontally 
615
 
         * 
616
 
         * @property _scrollsHorizontal
617
 
         * @type boolean
618
 
         * @protected
619
 
         */
620
 
        
621
 
        /**
622
 
         * Internal state, defines the maximum amount that the scrollview can be scrolled along the X axis 
623
 
         * 
624
 
         * @property _maxScrollX
625
 
         * @type number
626
 
         * @protected
627
 
         */
628
 
 
629
 
        /**
630
 
         * Internal state, defines the minimum amount that the scrollview can be scrolled along the X axis 
631
 
         * 
632
 
         * @property _minScrollX
633
 
         * @type number
634
 
         * @protected
635
 
         */
636
 
 
637
 
        /**
638
 
         * Internal state, cached scrollWidth, for performance 
639
 
         * 
640
 
         * @property _scrollWidth
641
 
         * @type number
642
 
         * @protected
643
 
         */
644
 
    },
645
 
 
646
 
    /**
647
 
     * Execute a flick at the end of a scroll action
648
 
     *
649
 
     * @method _flick
650
 
     * @param distance {Number} The distance (in px) the user scrolled before the flick
651
 
     * @param time {Number} The number of ms the scroll event lasted before the flick
652
 
     * @protected
653
 
     */
654
 
    _flick: function(e) {
655
 
        var flick = e.flick;
656
 
 
657
 
        /**
658
 
         * Internal state, currently calculated velocity from the flick 
659
 
         * 
660
 
         * @property _currentVelocity
661
 
         * @type number
662
 
         * @protected
663
 
         */
664
 
        this._currentVelocity = flick.velocity;
665
 
        this._flicking = true;
666
 
 
667
 
        this._decelCached = this.get('deceleration');
668
 
        this._bounceCached = this.get('bounce');
669
 
 
670
 
        this._pastYEdge = false;
671
 
        this._pastXEdge = false;
672
 
 
673
 
        this._flickFrame();
674
 
 
675
 
        this.fire(EV_SCROLL_FLICK);
676
 
    },
677
 
 
678
 
    /**
679
 
     * Execute a single frame in the flick animation
680
 
     *
681
 
     * @method _flickFrame
682
 
     * @protected
683
 
     */
684
 
    _flickFrame: function() {
685
 
        var newY,
686
 
            maxY,
687
 
            minY,
688
 
            newX,
689
 
            maxX,
690
 
            minX,
691
 
            scrollsVertical  = this._scrollsVertical,
692
 
            scrollsHorizontal = this._scrollsHorizontal,
693
 
            deceleration = this._decelCached,
694
 
            bounce = this._bounceCached,
695
 
            step = ScrollView.FRAME_STEP;
696
 
 
697
 
        if(scrollsVertical) {
698
 
            maxY = this._maxScrollY;
699
 
            minY = this._minScrollY;
700
 
            newY = this.get(SCROLL_Y) - (this._currentVelocity * step);
701
 
        }
702
 
 
703
 
        if(scrollsHorizontal) {
704
 
            maxX = this._maxScrollX;
705
 
            minX = this._minScrollX;
706
 
            newX = this.get(SCROLL_X) - (this._currentVelocity * step);
707
 
        }
708
 
        
709
 
        this._currentVelocity = (this._currentVelocity * deceleration);
710
 
 
711
 
        if(Math.abs(this._currentVelocity).toFixed(4) <= 0.015) {
712
 
            this._flicking = false;
713
 
            this._killTimer(!(this._pastYEdge || this._pastXEdge));
714
 
 
715
 
            if(scrollsVertical) {
716
 
                if(newY < minY) {
717
 
                    this._snapToEdge = true;
718
 
                    this.set(SCROLL_Y, minY);
719
 
                } else if(newY > maxY) {
720
 
                    this._snapToEdge = true;
721
 
                    this.set(SCROLL_Y, maxY);
722
 
                }
723
 
            }
724
 
            
725
 
            if(scrollsHorizontal) {
726
 
                if(newX < minX) {
727
 
                    this._snapToEdge = true;
728
 
                    this.set(SCROLL_X, minX);
729
 
                } else if(newX > maxX) {
730
 
                    this._snapToEdge = true;
731
 
                    this.set(SCROLL_X, maxX);
732
 
                }
733
 
            }
734
 
 
735
 
            return;
736
 
        }
737
 
 
738
 
        if (scrollsVertical) {
739
 
            if (newY < minY || newY > maxY) {
740
 
                this._pastYEdge = true;
741
 
                this._currentVelocity *= bounce;
742
 
            }
743
 
 
744
 
            this.set(SCROLL_Y, newY);
745
 
        }
746
 
 
747
 
        if (scrollsHorizontal) {
748
 
            if (newX < minX || newX > maxX) {
749
 
                this._pastXEdge = true;
750
 
                this._currentVelocity *= bounce;
751
 
            }
752
 
 
753
 
            this.set(SCROLL_X, newX);
754
 
        }
755
 
 
756
 
        if (!this._flickTimer) {
757
 
            this._flickTimer = Y.later(step, this, '_flickFrame', null, true);
758
 
        }
759
 
    },
760
 
 
761
 
    /**
762
 
     * Stop the animation timer
763
 
     *
764
 
     * @method _killTimer
765
 
     * @param fireEvent {Boolean} If true, fire the scrollEnd event
766
 
     * @protected
767
 
     */
768
 
    _killTimer: function(fireEvent) {
769
 
        if(this._flickTimer) {
770
 
            this._flickTimer.cancel();
771
 
            this._flickTimer = null;
772
 
        }
773
 
 
774
 
        if(fireEvent) {
775
 
            this.fire(EV_SCROLL_END);
776
 
        }
777
 
    },
778
 
 
779
 
    /**
780
 
     * The scrollX, scrollY setter implementation
781
 
     * 
782
 
     * @method _setScroll
783
 
     * @private
784
 
     * @param {Number} val
785
 
     * @param {String} dim
786
 
     * 
787
 
     * @return {Number} The constrained value, if it exceeds min/max range
788
 
     */
789
 
    _setScroll : function(val, dim) {
790
 
        var bouncing = this._cachedBounce || this.get(BOUNCE),
791
 
            range = ScrollView.BOUNCE_RANGE,
792
 
 
793
 
            maxScroll = (dim == DIM_X) ? this._maxScrollX : this._maxScrollY,
794
 
 
795
 
            min = bouncing ? -range : 0,
796
 
            max = bouncing ? maxScroll + range : maxScroll;
797
 
 
798
 
        if(!bouncing || !this._isDragging) {
799
 
            if(val < min) {
800
 
                val = min;
801
 
            } else if(val > max) {
802
 
                val = max;
803
 
            }            
804
 
        }
805
 
 
806
 
        return val;
807
 
    },
808
 
 
809
 
    /**
810
 
     * Setter for the scrollX attribute
811
 
     *
812
 
     * @method _setScrollX
813
 
     * @param val {Number} The new scrollX value
814
 
     * @return {Number} The normalized value
815
 
     * @protected
816
 
     */    
817
 
    _setScrollX: function(val) {
818
 
        return this._setScroll(val, DIM_X);
819
 
    },
820
 
 
821
 
    /**
822
 
     * Setter for the scrollY ATTR
823
 
     *
824
 
     * @method _setScrollY
825
 
     * @param val {Number} The new scrollY value
826
 
     * @return {Number} The normalized value 
827
 
     * @protected
828
 
     */
829
 
    _setScrollY: function(val) {
830
 
        return this._setScroll(val, DIM_Y);
831
 
    }
832
 
    
833
 
}, {
834
 
   
835
 
   // Y.ScrollView static properties
836
 
 
837
 
   /**
838
 
    * The identity of the widget.
839
 
    *
840
 
    * @property ScrollView.NAME
841
 
    * @type String
842
 
    * @default 'scrollview'
843
 
    * @readOnly
844
 
    * @protected
845
 
    * @static
846
 
    */
847
 
   NAME: 'scrollview',
848
 
 
849
 
   /**
850
 
    * Static property used to define the default attribute configuration of
851
 
    * the Widget.
852
 
    *
853
 
    * @property ScrollView.ATTRS
854
 
    * @type {Object}
855
 
    * @protected
856
 
    * @static
857
 
    */
858
 
    ATTRS: {
859
 
 
860
 
        /**
861
 
         * The scroll position in the y-axis
862
 
         *
863
 
         * @attribute scrollY
864
 
         * @type Number
865
 
         * @default 0
866
 
         */
867
 
        scrollY: {
868
 
            value: 0,
869
 
            setter: '_setScrollY'
870
 
        },
871
 
 
872
 
        /**
873
 
         * The scroll position in the x-axis
874
 
         *
875
 
         * @attribute scrollX
876
 
         * @type Number
877
 
         * @default 0
878
 
         */
879
 
        scrollX: {
880
 
            value: 0,
881
 
            setter: '_setScrollX'
882
 
        },
883
 
 
884
 
        /**
885
 
         * Drag coefficent for inertial scrolling. The closer to 1 this
886
 
         * value is, the less friction during scrolling.
887
 
         *
888
 
         * @attribute deceleration
889
 
         * @default 0.93
890
 
         */
891
 
        deceleration: {
892
 
            value: 0.93
893
 
        },
894
 
 
895
 
        /**
896
 
         * Drag coefficient for intertial scrolling at the upper
897
 
         * and lower boundaries of the scrollview. Set to 0 to 
898
 
         * disable "rubber-banding".
899
 
         *
900
 
         * @attribute bounce
901
 
         * @type Number
902
 
         * @default 0.1
903
 
         */
904
 
        bounce: {
905
 
            value: 0.1
906
 
        },
907
 
 
908
 
        /**
909
 
         * The minimum distance and/or velocity which define a flick
910
 
         *
911
 
         * @attribute flick
912
 
         * @type Object
913
 
         * @default Object with properties minDistance = 10, minVelocity = 0.3.
914
 
         */
915
 
        flick: {
916
 
            value: {
917
 
                minDistance: 10,
918
 
                minVelocity: 0.3
919
 
            }
920
 
        }
921
 
    },
922
 
 
923
 
    /**
924
 
     * List of class names used in the scrollview's DOM
925
 
     *
926
 
     * @property ScrollView.CLASS_NAMES
927
 
     * @type Object
928
 
     * @static
929
 
     */
930
 
    CLASS_NAMES: CLASS_NAMES,
931
 
 
932
 
    /**
933
 
     * Flag used to source property changes initiated from the DOM
934
 
     *
935
 
     * @property ScrollView.UI_SRC
936
 
     * @type String
937
 
     * @static
938
 
     * @default "ui"
939
 
     */
940
 
    UI_SRC: UI,
941
 
 
942
 
    /**
943
 
     * The default bounce distance in pixels
944
 
     *
945
 
     * @property ScrollView.BOUNCE_RANGE
946
 
     * @type Number
947
 
     * @static
948
 
     * @default 150
949
 
     */
950
 
    BOUNCE_RANGE : 150,
951
 
 
952
 
    /**
953
 
     * The interval used when animating the flick
954
 
     *
955
 
     * @property ScrollView.FRAME_STEP
956
 
     * @type Number
957
 
     * @static
958
 
     * @default 30
959
 
     */
960
 
    FRAME_STEP : 30,
961
 
 
962
 
    /**
963
 
     * The default easing used when animating the flick
964
 
     *
965
 
     * @property ScrollView.EASING
966
 
     * @type String
967
 
     * @static
968
 
     * @default 'cubic-bezier(0, 0.1, 0, 1.0)'
969
 
     */
970
 
    EASING : 'cubic-bezier(0, 0.1, 0, 1.0)',
971
 
 
972
 
    /**
973
 
     * The default easing to use when animatiing the bounce snap back.
974
 
     *
975
 
     * @property ScrollView.SNAP_EASING
976
 
     * @type String
977
 
     * @static
978
 
     * @default 'ease-out'
979
 
     */
980
 
    SNAP_EASING : 'ease-out',
981
 
 
982
 
    /**
983
 
     * Style property name to use to set transition duration. Currently Webkit specific (WebkitTransitionDuration)
984
 
     * 
985
 
     * @property ScrollView._TRANSITION_DURATION
986
 
     * @private
987
 
     */
988
 
    _TRANSITION_DURATION : "WebkitTransitionDuration",
989
 
 
990
 
    /**
991
 
     * Style property name to use to set transition property. Currently, Webkit specific (WebkitTransitionProperty)
992
 
     *
993
 
     * @property ScrollView._TRANSITION_PROPERTY
994
 
     * @private
995
 
     */
996
 
    _TRANSITION_PROPERTY : "WebkitTransitionProperty"
997
 
});
998
 
 
999
 
 
1000
 
}, '3.2.0' ,{skinnable:true, requires:['widget', 'event-gestures', 'transition']});