2
YUI 3.10.3 (build 2fb5187)
3
Copyright 2013 Yahoo! Inc. All rights reserved.
4
Licensed under the BSD License.
5
http://yuilibrary.com/license/
8
YUI.add('scrollview-base', function (Y, NAME) {
11
* The scrollview-base module provides a basic ScrollView Widget, without scrollbar indicators
14
* @submodule scrollview-base
18
var getClassName = Y.ClassNameManager.getClassName,
19
DOCUMENT = Y.config.doc,
21
NATIVE_TRANSITIONS = Y.Transition.useNative,
22
vendorPrefix = Y.Transition._VENDOR_PREFIX, // Todo: This is a private property, and alternative approaches should be investigated
23
SCROLLVIEW = 'scrollview',
25
vertical: getClassName(SCROLLVIEW, 'vert'),
26
horizontal: getClassName(SCROLLVIEW, 'horiz')
28
EV_SCROLL_END = 'scrollEnd',
31
MOUSEWHEEL = 'mousewheel',
40
DISABLED = 'disabled',
41
DECELERATION = 'deceleration',
44
BOUNDING_BOX = 'boundingBox',
45
CONTENT_BOX = 'contentBox',
46
GESTURE_MOVE = 'gesturemove',
51
SNAP_DURATION = 'snapDuration',
52
SNAP_EASING = 'snapEasing',
54
FRAME_DURATION = 'frameDuration',
55
BOUNCE_RANGE = 'bounceRange',
56
_constrain = function (val, min, max) {
57
return Math.min(Math.max(val, min), max);
61
* ScrollView provides a scrollable widget, supporting flick gestures,
62
* across both touch and mouse based devices.
65
* @param config {Object} Object literal with initial attribute values
69
function ScrollView() {
70
ScrollView.superclass.constructor.apply(this, arguments);
73
Y.ScrollView = Y.extend(ScrollView, Y.Widget, {
75
// *** Y.ScrollView prototype
78
* Flag driving whether or not we should try and force H/W acceleration when transforming. Currently enabled by default for Webkit.
79
* Used by the _transform method.
81
* @property _forceHWTransforms
85
_forceHWTransforms: Y.UA.webkit ? true : false,
88
* <p>Used to control whether or not ScrollView's internal
89
* gesturemovestart, gesturemove and gesturemoveend
90
* event listeners should preventDefault. The value is an
91
* object, with "start", "move" and "end" properties used to
92
* specify which events should preventDefault and which shouldn't:</p>
102
* <p>The default values are set up in order to prevent panning,
103
* on touch devices, while allowing click listeners on elements inside
104
* the ScrollView to be notified as expected.</p>
117
* Contains the distance (postive or negative) in pixels by which
118
* the scrollview was last scrolled. This is useful when setting up
119
* click listeners on the scrollview content, which on mouse based
120
* devices are always fired, even after a drag/flick.
122
* <p>Touch based devices don't currently fire a click event,
123
* if the finger has been moved (beyond a threshold) so this
124
* check isn't required, if working in a purely touch based environment</p>
126
* @property lastScrolledAmt
134
* Internal state, defines the minimum amount that the scrollview can be scrolled along the X axis
136
* @property _minScrollX
143
* Internal state, defines the maximum amount that the scrollview can be scrolled along the X axis
145
* @property _maxScrollX
152
* Internal state, defines the minimum amount that the scrollview can be scrolled along the Y axis
154
* @property _minScrollY
161
* Internal state, defines the maximum amount that the scrollview can be scrolled along the Y axis
163
* @property _maxScrollY
170
* Designated initializer
172
* @method initializer
173
* @param {config} Configuration object for the plugin
175
initializer: function () {
178
// Cache these values, since they aren't going to change.
179
sv._bb = sv.get(BOUNDING_BOX);
180
sv._cb = sv.get(CONTENT_BOX);
182
// Cache some attributes
183
sv._cAxis = sv.get(AXIS);
184
sv._cBounce = sv.get(BOUNCE);
185
sv._cBounceRange = sv.get(BOUNCE_RANGE);
186
sv._cDeceleration = sv.get(DECELERATION);
187
sv._cFrameDuration = sv.get(FRAME_DURATION);
191
* bindUI implementation
193
* Hooks up events for the widget
196
bindUI: function () {
199
// Bind interaction listers
200
sv._bindFlick(sv.get(FLICK));
201
sv._bindDrag(sv.get(DRAG));
202
sv._bindMousewheel(true);
204
// Bind change events
207
// IE SELECT HACK. See if we can do this non-natively and in the gesture for a future release.
209
sv._fixIESelect(sv._bb, sv._cb);
212
// Set any deprecated static properties
213
if (ScrollView.SNAP_DURATION) {
214
sv.set(SNAP_DURATION, ScrollView.SNAP_DURATION);
217
if (ScrollView.SNAP_EASING) {
218
sv.set(SNAP_EASING, ScrollView.SNAP_EASING);
221
if (ScrollView.EASING) {
222
sv.set(EASING, ScrollView.EASING);
225
if (ScrollView.FRAME_STEP) {
226
sv.set(FRAME_DURATION, ScrollView.FRAME_STEP);
229
if (ScrollView.BOUNCE_RANGE) {
230
sv.set(BOUNCE_RANGE, ScrollView.BOUNCE_RANGE);
233
// Recalculate dimension properties
234
// TODO: This should be throttled.
235
// Y.one(WINDOW).after('resize', sv._afterDimChange, sv);
239
* Bind event listeners
244
_bindAttrs: function () {
246
scrollChangeHandler = sv._afterScrollChange,
247
dimChangeHandler = sv._afterDimChange;
249
// Bind any change event listeners
251
'scrollEnd': sv._afterScrollEnd,
252
'disabledChange': sv._afterDisabledChange,
253
'flickChange': sv._afterFlickChange,
254
'dragChange': sv._afterDragChange,
255
'axisChange': sv._afterAxisChange,
256
'scrollYChange': scrollChangeHandler,
257
'scrollXChange': scrollChangeHandler,
258
'heightChange': dimChangeHandler,
259
'widthChange': dimChangeHandler
264
* Bind (or unbind) gesture move listeners required for drag support
267
* @param drag {boolean} If true, the method binds listener to enable
268
* drag (gesturemovestart). If false, the method unbinds gesturemove
269
* listeners for drag support.
272
_bindDrag: function (drag) {
276
// Unbind any previous 'drag' listeners
277
bb.detach(DRAG + '|*');
280
bb.on(DRAG + '|' + GESTURE_MOVE + START, Y.bind(sv._onGestureMoveStart, sv));
285
* Bind (or unbind) flick listeners.
288
* @param flick {Object|boolean} If truthy, the method binds listeners for
289
* flick support. If false, the method unbinds flick listeners.
292
_bindFlick: function (flick) {
296
// Unbind any previous 'flick' listeners
297
bb.detach(FLICK + '|*');
300
bb.on(FLICK + '|' + FLICK, Y.bind(sv._flick, sv), flick);
302
// Rebind Drag, becuase _onGestureMoveEnd always has to fire -after- _flick
303
sv._bindDrag(sv.get(DRAG));
308
* Bind (or unbind) mousewheel listeners.
310
* @method _bindMousewheel
311
* @param mousewheel {Object|boolean} If truthy, the method binds listeners for
312
* mousewheel support. If false, the method unbinds mousewheel listeners.
315
_bindMousewheel: function (mousewheel) {
319
// Unbind any previous 'mousewheel' listeners
320
// TODO: This doesn't actually appear to work properly. Fix. #2532743
321
bb.detach(MOUSEWHEEL + '|*');
323
// Only enable for vertical scrollviews
325
// Bound to document, because that's where mousewheel events fire off of.
326
Y.one(DOCUMENT).on(MOUSEWHEEL, Y.bind(sv._mousewheel, sv));
331
* syncUI implementation.
333
* Update the scroll position, based on the current value of scrollX/scrollY.
337
syncUI: function () {
339
scrollDims = sv._getScrollDims(),
340
width = scrollDims.offsetWidth,
341
height = scrollDims.offsetHeight,
342
scrollWidth = scrollDims.scrollWidth,
343
scrollHeight = scrollDims.scrollHeight;
345
// If the axis is undefined, auto-calculate it
346
if (sv._cAxis === undefined) {
347
// This should only ever be run once (for now).
348
// In the future SV might post-load axis changes
350
x: (scrollWidth > width),
351
y: (scrollHeight > height)
354
sv._set(AXIS, sv._cAxis);
357
// get text direction on or inherited by scrollview node
358
sv.rtl = (sv._cb.getComputedStyle('direction') === 'rtl');
360
// Cache the disabled value
361
sv._cDisabled = sv.get(DISABLED);
363
// Run this to set initial values
364
sv._uiDimensionsChange();
366
// If we're out-of-bounds, snap back.
367
if (sv._isOutOfBounds()) {
373
* Utility method to obtain widget dimensions
375
* @method _getScrollDims
376
* @return {Object} The offsetWidth, offsetHeight, scrollWidth and
377
* scrollHeight as an array: [offsetWidth, offsetHeight, scrollWidth,
381
_getScrollDims: function () {
385
TRANS = ScrollView._TRANSITION,
386
// Ideally using CSSMatrix - don't think we have it normalized yet though.
387
// origX = (new WebKitCSSMatrix(cb.getComputedStyle("transform"))).e,
388
// origY = (new WebKitCSSMatrix(cb.getComputedStyle("transform"))).f,
389
origX = sv.get(SCROLL_X),
390
origY = sv.get(SCROLL_Y),
394
// TODO: Is this OK? Just in case it's called 'during' a transition.
395
if (NATIVE_TRANSITIONS) {
396
cb.setStyle(TRANS.DURATION, ZERO);
397
cb.setStyle(TRANS.PROPERTY, EMPTY);
400
origHWTransform = sv._forceHWTransforms;
401
sv._forceHWTransforms = false; // the z translation was causing issues with picking up accurate scrollWidths in Chrome/Mac.
403
sv._moveTo(cb, 0, 0);
405
'offsetWidth': bb.get('offsetWidth'),
406
'offsetHeight': bb.get('offsetHeight'),
407
'scrollWidth': bb.get('scrollWidth'),
408
'scrollHeight': bb.get('scrollHeight')
410
sv._moveTo(cb, -(origX), -(origY));
412
sv._forceHWTransforms = origHWTransform;
418
* This method gets invoked whenever the height or width attributes change,
419
* allowing us to determine which scrolling axes need to be enabled.
421
* @method _uiDimensionsChange
424
_uiDimensionsChange: function () {
427
scrollDims = sv._getScrollDims(),
428
width = scrollDims.offsetWidth,
429
height = scrollDims.offsetHeight,
430
scrollWidth = scrollDims.scrollWidth,
431
scrollHeight = scrollDims.scrollHeight,
434
minScrollX = (rtl ? Math.min(0, -(scrollWidth - width)) : 0),
435
maxScrollX = (rtl ? 0 : Math.max(0, scrollWidth - width)),
437
maxScrollY = Math.max(0, scrollHeight - height);
439
if (svAxis && svAxis.x) {
440
bb.addClass(CLASS_NAMES.horizontal);
443
if (svAxis && svAxis.y) {
444
bb.addClass(CLASS_NAMES.vertical);
448
minScrollX: minScrollX,
449
maxScrollX: maxScrollX,
450
minScrollY: minScrollY,
451
maxScrollY: maxScrollY
456
* Set the bounding dimensions of the ScrollView
460
* @param bounds {Object} [duration] ms of the scroll animation. (default is 0)
461
* @param {Number} [bounds.minScrollX] The minimum scroll X value
462
* @param {Number} [bounds.maxScrollX] The maximum scroll X value
463
* @param {Number} [bounds.minScrollY] The minimum scroll Y value
464
* @param {Number} [bounds.maxScrollY] The maximum scroll Y value
466
_setBounds: function (bounds) {
469
// TODO: Do a check to log if the bounds are invalid
471
sv._minScrollX = bounds.minScrollX;
472
sv._maxScrollX = bounds.maxScrollX;
473
sv._minScrollY = bounds.minScrollY;
474
sv._maxScrollY = bounds.maxScrollY;
478
* Get the bounding dimensions of the ScrollView
483
_getBounds: function () {
487
minScrollX: sv._minScrollX,
488
maxScrollX: sv._maxScrollX,
489
minScrollY: sv._minScrollY,
490
maxScrollY: sv._maxScrollY
496
* Scroll the element to a given xy coordinate
499
* @param x {Number} The x-position to scroll to. (null for no movement)
500
* @param y {Number} The y-position to scroll to. (null for no movement)
501
* @param {Number} [duration] ms of the scroll animation. (default is 0)
502
* @param {String} [easing] An easing equation if duration is set. (default is `easing` attribute)
503
* @param {String} [node] The node to transform. Setting this can be useful in
504
* dual-axis paginated instances. (default is the instance's contentBox)
506
scrollTo: function (x, y, duration, easing, node) {
507
// Check to see if widget is disabled
508
if (this._cDisabled) {
514
TRANS = ScrollView._TRANSITION,
515
callback = Y.bind(sv._onTransEnd, sv), // @Todo : cache this
521
// default the optional arguments
522
duration = duration || 0;
523
easing = easing || sv.get(EASING); // @TODO: Cache this
527
sv.set(SCROLL_X, x, {src:UI});
532
sv.set(SCROLL_Y, y, {src:UI});
536
transform = sv._transform(newX, newY);
538
if (NATIVE_TRANSITIONS) {
539
// ANDROID WORKAROUND - try and stop existing transition, before kicking off new one.
540
node.setStyle(TRANS.DURATION, ZERO).setStyle(TRANS.PROPERTY, EMPTY);
544
if (duration === 0) {
545
if (NATIVE_TRANSITIONS) {
546
node.setStyle('transform', transform);
549
// TODO: If both set, batch them in the same update
550
// Update: Nope, setStyles() just loops through each property and applies it.
552
node.setStyle(LEFT, newX + PX);
555
node.setStyle(TOP, newY + PX);
562
transition.easing = easing;
563
transition.duration = duration / 1000;
565
if (NATIVE_TRANSITIONS) {
566
transition.transform = transform;
569
transition.left = newX + PX;
570
transition.top = newY + PX;
573
node.transition(transition, callback);
578
* Utility method, to create the translate transform string with the
579
* x, y translation amounts provided.
582
* @param {Number} x Number of pixels to translate along the x axis
583
* @param {Number} y Number of pixels to translate along the y axis
586
_transform: function (x, y) {
587
// TODO: Would we be better off using a Matrix for this?
588
var prop = 'translate(' + x + 'px, ' + y + 'px)';
590
if (this._forceHWTransforms) {
591
prop += ' translateZ(0)';
598
* Utility method, to move the given element to the given xy position
601
* @param node {Node} The node to move
602
* @param x {Number} The x-position to move to
603
* @param y {Number} The y-position to move to
606
_moveTo : function(node, x, y) {
607
if (NATIVE_TRANSITIONS) {
608
node.setStyle('transform', this._transform(x, y));
610
node.setStyle(LEFT, x + PX);
611
node.setStyle(TOP, y + PX);
617
* Content box transition callback
619
* @method _onTransEnd
620
* @param {Event.Facade} e The event facade
623
_onTransEnd: function () {
626
// If for some reason we're OOB, snapback
627
if (sv._isOutOfBounds()) {
632
* Notification event fired at the end of a scroll transition
635
* @param e {EventFacade} The default event facade.
637
sv.fire(EV_SCROLL_END);
642
* gesturemovestart event handler
644
* @method _onGestureMoveStart
645
* @param e {Event.Facade} The gesturemovestart event facade
648
_onGestureMoveStart: function (e) {
650
if (this._cDisabled) {
656
currentX = sv.get(SCROLL_X),
657
currentY = sv.get(SCROLL_Y),
661
if (sv._prevent.start) {
665
// if a flick animation is in progress, cancel it
671
// Reset lastScrolledAmt
672
sv.lastScrolledAmt = 0;
674
// Stores data for this gesture cycle. Cleaned up later
677
// Will hold the axis value
680
// The current attribute values
684
// The X/Y coordinates where the event began
685
startClientX: clientX,
686
startClientY: clientY,
688
// The X/Y coordinates where the event will end
692
// The current delta of the event
696
// Will be populated for flicks
699
// Create some listeners for the rest of the gesture cycle
700
onGestureMove: bb.on(DRAG + '|' + GESTURE_MOVE, Y.bind(sv._onGestureMove, sv)),
702
// @TODO: Don't bind gestureMoveEnd if it's a Flick?
703
onGestureMoveEnd: bb.on(DRAG + '|' + GESTURE_MOVE + END, Y.bind(sv._onGestureMoveEnd, sv))
708
* gesturemove event handler
710
* @method _onGestureMove
711
* @param e {Event.Facade} The gesturemove event facade
714
_onGestureMove: function (e) {
716
gesture = sv._gesture,
720
startX = gesture.startX,
721
startY = gesture.startY,
722
startClientX = gesture.startClientX,
723
startClientY = gesture.startClientY,
727
if (sv._prevent.move) {
731
gesture.deltaX = startClientX - clientX;
732
gesture.deltaY = startClientY - clientY;
734
// Determine if this is a vertical or horizontal movement
735
// @TODO: This is crude, but it works. Investigate more intelligent ways to detect intent
736
if (gesture.axis === null) {
737
gesture.axis = (Math.abs(gesture.deltaX) > Math.abs(gesture.deltaY)) ? DIM_X : DIM_Y;
740
// Move X or Y. @TODO: Move both if dualaxis.
741
if (gesture.axis === DIM_X && svAxisX) {
742
sv.set(SCROLL_X, startX + gesture.deltaX);
744
else if (gesture.axis === DIM_Y && svAxisY) {
745
sv.set(SCROLL_Y, startY + gesture.deltaY);
750
* gesturemoveend event handler
752
* @method _onGestureMoveEnd
753
* @param e {Event.Facade} The gesturemoveend event facade
756
_onGestureMoveEnd: function (e) {
758
gesture = sv._gesture,
759
flick = gesture.flick,
764
if (sv._prevent.end) {
768
// Store the end X/Y coordinates
769
gesture.endClientX = clientX;
770
gesture.endClientY = clientY;
772
// Cleanup the event handlers
773
gesture.onGestureMove.detach();
774
gesture.onGestureMoveEnd.detach();
776
// If this wasn't a flick, wrap up the gesture cycle
778
// @TODO: Be more intelligent about this. Look at the Flick attribute to see
779
// if it is safe to assume _flick did or didn't fire.
780
// Then, the order _flick and _onGestureMoveEnd fire doesn't matter?
782
// If there was movement (_onGestureMove fired)
783
if (gesture.deltaX !== null && gesture.deltaY !== null) {
785
isOOB = sv._isOutOfBounds();
787
// If we're out-out-bounds, then snapback
794
// Fire scrollEnd unless this is a paginated instance and the gesture axis is the same as paginator's
795
// Not totally confident this is ideal to access a plugin's properties from a host, @TODO revisit
796
if (!sv.pages || (sv.pages && !sv.pages.get(AXIS)[gesture.axis])) {
805
* Execute a flick at the end of a scroll action
808
* @param e {Event.Facade} The Flick event facade
811
_flick: function (e) {
812
if (this._cDisabled) {
819
flickAxis = flick.axis,
820
flickVelocity = flick.velocity,
821
axisAttr = flickAxis === DIM_X ? SCROLL_X : SCROLL_Y,
822
startPosition = sv.get(axisAttr);
824
// Sometimes flick is enabled, but drag is disabled
826
sv._gesture.flick = flick;
829
// Prevent unneccesary firing of _flickFrame if we can't scroll on the flick axis
830
if (svAxis[flickAxis]) {
831
sv._flickFrame(flickVelocity, flickAxis, startPosition);
836
* Execute a single frame in the flick animation
838
* @method _flickFrame
839
* @param velocity {Number} The velocity of this animated frame
840
* @param flickAxis {String} The axis on which to animate
841
* @param startPosition {Number} The starting X/Y point to flick from
844
_flickFrame: function (velocity, flickAxis, startPosition) {
847
axisAttr = flickAxis === DIM_X ? SCROLL_X : SCROLL_Y,
848
bounds = sv._getBounds(),
850
// Localize cached values
851
bounce = sv._cBounce,
852
bounceRange = sv._cBounceRange,
853
deceleration = sv._cDeceleration,
854
frameDuration = sv._cFrameDuration,
857
newVelocity = velocity * deceleration,
858
newPosition = startPosition - (frameDuration * newVelocity),
860
// Some convinience conditions
861
min = flickAxis === DIM_X ? bounds.minScrollX : bounds.minScrollY,
862
max = flickAxis === DIM_X ? bounds.maxScrollX : bounds.maxScrollY,
863
belowMin = (newPosition < min),
864
belowMax = (newPosition < max),
865
aboveMin = (newPosition > min),
866
aboveMax = (newPosition > max),
867
belowMinRange = (newPosition < (min - bounceRange)),
868
withinMinRange = (belowMin && (newPosition > (min - bounceRange))),
869
withinMaxRange = (aboveMax && (newPosition < (max + bounceRange))),
870
aboveMaxRange = (newPosition > (max + bounceRange)),
873
// If we're within the range but outside min/max, dampen the velocity
874
if (withinMinRange || withinMaxRange) {
875
newVelocity *= bounce;
878
// Is the velocity too slow to bother?
879
tooSlow = (Math.abs(newVelocity).toFixed(4) < 0.015);
881
// If the velocity is too slow or we're outside the range
882
if (tooSlow || belowMinRange || aboveMaxRange) {
883
// Cancel and delete sv._flickAnim
888
// If we're inside the scroll area, just end
889
if (aboveMin && belowMax) {
893
// We're outside the scroll area, so we need to snap back
899
// Otherwise, animate to the next frame
901
// @TODO: maybe use requestAnimationFrame instead
902
sv._flickAnim = Y.later(frameDuration, sv, '_flickFrame', [newVelocity, flickAxis, newPosition]);
903
sv.set(axisAttr, newPosition);
907
_cancelFlick: function () {
911
// Cancel the flick (if it exists)
912
sv._flickAnim.cancel();
914
// Also delete it, otherwise _onGestureMoveStart will think we're still flicking
915
delete sv._flickAnim;
921
* Handle mousewheel events on the widget
923
* @method _mousewheel
924
* @param e {Event.Facade} The mousewheel event facade
927
_mousewheel: function (e) {
929
scrollY = sv.get(SCROLL_Y),
930
bounds = sv._getBounds(),
932
scrollOffset = 10, // 10px
933
isForward = (e.wheelDelta > 0),
934
scrollToY = scrollY - ((isForward ? 1 : -1) * scrollOffset);
936
scrollToY = _constrain(scrollToY, bounds.minScrollY, bounds.maxScrollY);
938
// Because Mousewheel events fire off 'document', every ScrollView widget will react
939
// to any mousewheel anywhere on the page. This check will ensure that the mouse is currently
940
// over this specific ScrollView. Also, only allow mousewheel scrolling on Y-axis,
941
// becuase otherwise the 'prevent' will block page scrolling.
942
if (bb.contains(e.target) && sv._cAxis[DIM_Y]) {
944
// Reset lastScrolledAmt
945
sv.lastScrolledAmt = 0;
947
// Jump to the new offset
948
sv.set(SCROLL_Y, scrollToY);
950
// if we have scrollbars plugin, update & set the flash timer on the scrollbar
951
// @TODO: This probably shouldn't be in this module
953
// @TODO: The scrollbars should handle this themselves
954
sv.scrollbars._update();
955
sv.scrollbars.flash();
957
// sv.scrollbars._hostDimensionsChange();
960
// Fire the 'scrollEnd' event
963
// prevent browser default behavior on mouse scroll
969
* Checks to see the current scrollX/scrollY position beyond the min/max boundary
971
* @method _isOutOfBounds
972
* @param x {Number} [optional] The X position to check
973
* @param y {Number} [optional] The Y position to check
974
* @return {boolen} Whether the current X/Y position is out of bounds (true) or not (false)
977
_isOutOfBounds: function (x, y) {
982
currentX = x || sv.get(SCROLL_X),
983
currentY = y || sv.get(SCROLL_Y),
984
bounds = sv._getBounds(),
985
minX = bounds.minScrollX,
986
minY = bounds.minScrollY,
987
maxX = bounds.maxScrollX,
988
maxY = bounds.maxScrollY;
990
return (svAxisX && (currentX < minX || currentX > maxX)) || (svAxisY && (currentY < minY || currentY > maxY));
995
* @TODO: Should be more generalized and support both X and Y detection
1000
_snapBack: function () {
1002
currentX = sv.get(SCROLL_X),
1003
currentY = sv.get(SCROLL_Y),
1004
bounds = sv._getBounds(),
1005
minX = bounds.minScrollX,
1006
minY = bounds.minScrollY,
1007
maxX = bounds.maxScrollX,
1008
maxY = bounds.maxScrollY,
1009
newY = _constrain(currentY, minY, maxY),
1010
newX = _constrain(currentX, minX, maxX),
1011
duration = sv.get(SNAP_DURATION),
1012
easing = sv.get(SNAP_EASING);
1014
if (newX !== currentX) {
1015
sv.set(SCROLL_X, newX, {duration:duration, easing:easing});
1017
else if (newY !== currentY) {
1018
sv.set(SCROLL_Y, newY, {duration:duration, easing:easing});
1026
* After listener for changes to the scrollX or scrollY attribute
1028
* @method _afterScrollChange
1029
* @param e {Event.Facade} The event facade
1032
_afterScrollChange: function (e) {
1033
if (e.src === ScrollView.UI_SRC) {
1038
duration = e.duration,
1043
// Set the scrolled value
1044
sv.lastScrolledAmt = sv.lastScrolledAmt + (e.newVal - e.prevVal);
1046
// Generate the array of args to pass to scrollTo()
1047
if (e.attrName === SCROLL_X) {
1048
scrollToArgs.push(val);
1049
scrollToArgs.push(sv.get(SCROLL_Y));
1052
scrollToArgs.push(sv.get(SCROLL_X));
1053
scrollToArgs.push(val);
1056
scrollToArgs.push(duration);
1057
scrollToArgs.push(easing);
1059
sv.scrollTo.apply(sv, scrollToArgs);
1063
* After listener for changes to the flick attribute
1065
* @method _afterFlickChange
1066
* @param e {Event.Facade} The event facade
1069
_afterFlickChange: function (e) {
1070
this._bindFlick(e.newVal);
1074
* After listener for changes to the disabled attribute
1076
* @method _afterDisabledChange
1077
* @param e {Event.Facade} The event facade
1080
_afterDisabledChange: function (e) {
1081
// Cache for performance - we check during move
1082
this._cDisabled = e.newVal;
1086
* After listener for the axis attribute
1088
* @method _afterAxisChange
1089
* @param e {Event.Facade} The event facade
1092
_afterAxisChange: function (e) {
1093
this._cAxis = e.newVal;
1097
* After listener for changes to the drag attribute
1099
* @method _afterDragChange
1100
* @param e {Event.Facade} The event facade
1103
_afterDragChange: function (e) {
1104
this._bindDrag(e.newVal);
1108
* After listener for the height or width attribute
1110
* @method _afterDimChange
1111
* @param e {Event.Facade} The event facade
1114
_afterDimChange: function () {
1115
this._uiDimensionsChange();
1119
* After listener for scrollEnd, for cleanup
1121
* @method _afterScrollEnd
1122
* @param e {Event.Facade} The event facade
1125
_afterScrollEnd: function () {
1128
if (sv._flickAnim) {
1132
// Ideally this should be removed, but doing so causing some JS errors with fast swiping
1133
// because _gesture is being deleted after the previous one has been overwritten
1134
// delete sv._gesture; // TODO: Move to sv.prevGesture?
1138
* Setter for 'axis' attribute
1140
* @method _axisSetter
1141
* @param val {Mixed} A string ('x', 'y', 'xy') to specify which axis/axes to allow scrolling on
1142
* @param name {String} The attribute name
1143
* @return {Object} An object to specify scrollability on the x & y axes
1147
_axisSetter: function (val) {
1149
// Turn a string into an axis object
1150
if (Y.Lang.isString(val)) {
1152
x: val.match(/x/i) ? true : false,
1153
y: val.match(/y/i) ? true : false
1159
* The scrollX, scrollY setter implementation
1161
* @method _setScroll
1163
* @param {Number} val
1164
* @param {String} dim
1166
* @return {Number} The value
1168
_setScroll : function(val) {
1170
// Just ensure the widget is not disabled
1171
if (this._cDisabled) {
1172
val = Y.Attribute.INVALID_VALUE;
1179
* Setter for the scrollX attribute
1181
* @method _setScrollX
1182
* @param val {Number} The new scrollX value
1183
* @return {Number} The normalized value
1186
_setScrollX: function(val) {
1187
return this._setScroll(val, DIM_X);
1191
* Setter for the scrollY ATTR
1193
* @method _setScrollY
1194
* @param val {Number} The new scrollY value
1195
* @return {Number} The normalized value
1198
_setScrollY: function(val) {
1199
return this._setScroll(val, DIM_Y);
1202
// End prototype properties
1206
// Static properties
1209
* The identity of the widget.
1213
* @default 'scrollview'
1221
* Static property used to define the default attribute configuration of
1232
* Specifies ability to scroll on x, y, or x and y axis/axes.
1238
setter: '_axisSetter',
1239
writeOnce: 'initOnly'
1243
* The current scroll position in the x-axis
1245
* @attribute scrollX
1251
setter: '_setScrollX'
1255
* The current scroll position in the y-axis
1257
* @attribute scrollY
1263
setter: '_setScrollY'
1267
* Drag coefficent for inertial scrolling. The closer to 1 this
1268
* value is, the less friction during scrolling.
1270
* @attribute deceleration
1278
* Drag coefficient for intertial scrolling at the upper
1279
* and lower boundaries of the scrollview. Set to 0 to
1280
* disable "rubber-banding".
1291
* The minimum distance and/or velocity which define a flick. Can be set to false,
1292
* to disable flick support (note: drag support is enabled/disabled separately)
1296
* @default Object with properties minDistance = 10, minVelocity = 0.3.
1306
* Enable/Disable dragging the ScrollView content (note: flick support is enabled/disabled separately)
1316
* The default duration to use when animating the bounce snap back.
1318
* @attribute snapDuration
1327
* The default easing to use when animating the bounce snap back.
1329
* @attribute snapEasing
1331
* @default 'ease-out'
1338
* The default easing used when animating the flick
1342
* @default 'cubic-bezier(0, 0.1, 0, 1.0)'
1345
value: 'cubic-bezier(0, 0.1, 0, 1.0)'
1349
* The interval (ms) used when animating the flick for JS-timer animations
1351
* @attribute frameDuration
1360
* The default bounce distance in pixels
1362
* @attribute bounceRange
1372
* List of class names used in the scrollview's DOM
1374
* @property CLASS_NAMES
1378
CLASS_NAMES: CLASS_NAMES,
1381
* Flag used to source property changes initiated from the DOM
1391
* Object map of style property names used to set transition properties.
1392
* Defaults to the vendor prefix established by the Transition module.
1393
* The configured property names are `_TRANSITION.DURATION` (e.g. "WebkitTransitionDuration") and
1394
* `_TRANSITION.PROPERTY (e.g. "WebkitTransitionProperty").
1396
* @property _TRANSITION
1400
DURATION: (vendorPrefix ? vendorPrefix + 'TransitionDuration' : 'transitionDuration'),
1401
PROPERTY: (vendorPrefix ? vendorPrefix + 'TransitionProperty' : 'transitionProperty')
1405
* The default bounce distance in pixels
1407
* @property BOUNCE_RANGE
1411
* @deprecated (in 3.7.0)
1413
BOUNCE_RANGE: false,
1416
* The interval (ms) used when animating the flick
1418
* @property FRAME_STEP
1422
* @deprecated (in 3.7.0)
1427
* The default easing used when animating the flick
1433
* @deprecated (in 3.7.0)
1438
* The default easing to use when animating the bounce snap back.
1440
* @property SNAP_EASING
1444
* @deprecated (in 3.7.0)
1449
* The default duration to use when animating the bounce snap back.
1451
* @property SNAP_DURATION
1455
* @deprecated (in 3.7.0)
1457
SNAP_DURATION: false
1459
// End static properties
1463
}, '3.10.3', {"requires": ["widget", "event-gestures", "event-mousewheel", "transition"], "skinnable": true});