|
1 /* |
|
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/ |
|
6 */ |
|
7 |
|
8 YUI.add('scrollview-base', function (Y, NAME) { |
|
9 |
|
10 /** |
|
11 * The scrollview-base module provides a basic ScrollView Widget, without scrollbar indicators |
|
12 * |
|
13 * @module scrollview |
|
14 * @submodule scrollview-base |
|
15 */ |
|
16 |
|
17 // Local vars |
|
18 var getClassName = Y.ClassNameManager.getClassName, |
|
19 DOCUMENT = Y.config.doc, |
|
20 IE = Y.UA.ie, |
|
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', |
|
24 CLASS_NAMES = { |
|
25 vertical: getClassName(SCROLLVIEW, 'vert'), |
|
26 horizontal: getClassName(SCROLLVIEW, 'horiz') |
|
27 }, |
|
28 EV_SCROLL_END = 'scrollEnd', |
|
29 FLICK = 'flick', |
|
30 DRAG = 'drag', |
|
31 MOUSEWHEEL = 'mousewheel', |
|
32 UI = 'ui', |
|
33 TOP = 'top', |
|
34 LEFT = 'left', |
|
35 PX = 'px', |
|
36 AXIS = 'axis', |
|
37 SCROLL_Y = 'scrollY', |
|
38 SCROLL_X = 'scrollX', |
|
39 BOUNCE = 'bounce', |
|
40 DISABLED = 'disabled', |
|
41 DECELERATION = 'deceleration', |
|
42 DIM_X = 'x', |
|
43 DIM_Y = 'y', |
|
44 BOUNDING_BOX = 'boundingBox', |
|
45 CONTENT_BOX = 'contentBox', |
|
46 GESTURE_MOVE = 'gesturemove', |
|
47 START = 'start', |
|
48 END = 'end', |
|
49 EMPTY = '', |
|
50 ZERO = '0s', |
|
51 SNAP_DURATION = 'snapDuration', |
|
52 SNAP_EASING = 'snapEasing', |
|
53 EASING = 'easing', |
|
54 FRAME_DURATION = 'frameDuration', |
|
55 BOUNCE_RANGE = 'bounceRange', |
|
56 _constrain = function (val, min, max) { |
|
57 return Math.min(Math.max(val, min), max); |
|
58 }; |
|
59 |
|
60 /** |
|
61 * ScrollView provides a scrollable widget, supporting flick gestures, |
|
62 * across both touch and mouse based devices. |
|
63 * |
|
64 * @class ScrollView |
|
65 * @param config {Object} Object literal with initial attribute values |
|
66 * @extends Widget |
|
67 * @constructor |
|
68 */ |
|
69 function ScrollView() { |
|
70 ScrollView.superclass.constructor.apply(this, arguments); |
|
71 } |
|
72 |
|
73 Y.ScrollView = Y.extend(ScrollView, Y.Widget, { |
|
74 |
|
75 // *** Y.ScrollView prototype |
|
76 |
|
77 /** |
|
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. |
|
80 * |
|
81 * @property _forceHWTransforms |
|
82 * @type boolean |
|
83 * @protected |
|
84 */ |
|
85 _forceHWTransforms: Y.UA.webkit ? true : false, |
|
86 |
|
87 /** |
|
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> |
|
93 * |
|
94 * <pre> |
|
95 * { |
|
96 * start: false, |
|
97 * move: true, |
|
98 * end: false |
|
99 * } |
|
100 * </pre> |
|
101 * |
|
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> |
|
105 * |
|
106 * @property _prevent |
|
107 * @type Object |
|
108 * @protected |
|
109 */ |
|
110 _prevent: { |
|
111 start: false, |
|
112 move: true, |
|
113 end: false |
|
114 }, |
|
115 |
|
116 /** |
|
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. |
|
121 * |
|
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> |
|
125 * |
|
126 * @property lastScrolledAmt |
|
127 * @type Number |
|
128 * @public |
|
129 * @default 0 |
|
130 */ |
|
131 lastScrolledAmt: 0, |
|
132 |
|
133 /** |
|
134 * Internal state, defines the minimum amount that the scrollview can be scrolled along the X axis |
|
135 * |
|
136 * @property _minScrollX |
|
137 * @type number |
|
138 * @protected |
|
139 */ |
|
140 _minScrollX: null, |
|
141 |
|
142 /** |
|
143 * Internal state, defines the maximum amount that the scrollview can be scrolled along the X axis |
|
144 * |
|
145 * @property _maxScrollX |
|
146 * @type number |
|
147 * @protected |
|
148 */ |
|
149 _maxScrollX: null, |
|
150 |
|
151 /** |
|
152 * Internal state, defines the minimum amount that the scrollview can be scrolled along the Y axis |
|
153 * |
|
154 * @property _minScrollY |
|
155 * @type number |
|
156 * @protected |
|
157 */ |
|
158 _minScrollY: null, |
|
159 |
|
160 /** |
|
161 * Internal state, defines the maximum amount that the scrollview can be scrolled along the Y axis |
|
162 * |
|
163 * @property _maxScrollY |
|
164 * @type number |
|
165 * @protected |
|
166 */ |
|
167 _maxScrollY: null, |
|
168 |
|
169 /** |
|
170 * Designated initializer |
|
171 * |
|
172 * @method initializer |
|
173 * @param {config} Configuration object for the plugin |
|
174 */ |
|
175 initializer: function () { |
|
176 var sv = this; |
|
177 |
|
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); |
|
181 |
|
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); |
|
188 }, |
|
189 |
|
190 /** |
|
191 * bindUI implementation |
|
192 * |
|
193 * Hooks up events for the widget |
|
194 * @method bindUI |
|
195 */ |
|
196 bindUI: function () { |
|
197 var sv = this; |
|
198 |
|
199 // Bind interaction listers |
|
200 sv._bindFlick(sv.get(FLICK)); |
|
201 sv._bindDrag(sv.get(DRAG)); |
|
202 sv._bindMousewheel(true); |
|
203 |
|
204 // Bind change events |
|
205 sv._bindAttrs(); |
|
206 |
|
207 // IE SELECT HACK. See if we can do this non-natively and in the gesture for a future release. |
|
208 if (IE) { |
|
209 sv._fixIESelect(sv._bb, sv._cb); |
|
210 } |
|
211 |
|
212 // Set any deprecated static properties |
|
213 if (ScrollView.SNAP_DURATION) { |
|
214 sv.set(SNAP_DURATION, ScrollView.SNAP_DURATION); |
|
215 } |
|
216 |
|
217 if (ScrollView.SNAP_EASING) { |
|
218 sv.set(SNAP_EASING, ScrollView.SNAP_EASING); |
|
219 } |
|
220 |
|
221 if (ScrollView.EASING) { |
|
222 sv.set(EASING, ScrollView.EASING); |
|
223 } |
|
224 |
|
225 if (ScrollView.FRAME_STEP) { |
|
226 sv.set(FRAME_DURATION, ScrollView.FRAME_STEP); |
|
227 } |
|
228 |
|
229 if (ScrollView.BOUNCE_RANGE) { |
|
230 sv.set(BOUNCE_RANGE, ScrollView.BOUNCE_RANGE); |
|
231 } |
|
232 |
|
233 // Recalculate dimension properties |
|
234 // TODO: This should be throttled. |
|
235 // Y.one(WINDOW).after('resize', sv._afterDimChange, sv); |
|
236 }, |
|
237 |
|
238 /** |
|
239 * Bind event listeners |
|
240 * |
|
241 * @method _bindAttrs |
|
242 * @private |
|
243 */ |
|
244 _bindAttrs: function () { |
|
245 var sv = this, |
|
246 scrollChangeHandler = sv._afterScrollChange, |
|
247 dimChangeHandler = sv._afterDimChange; |
|
248 |
|
249 // Bind any change event listeners |
|
250 sv.after({ |
|
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 |
|
260 }); |
|
261 }, |
|
262 |
|
263 /** |
|
264 * Bind (or unbind) gesture move listeners required for drag support |
|
265 * |
|
266 * @method _bindDrag |
|
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. |
|
270 * @private |
|
271 */ |
|
272 _bindDrag: function (drag) { |
|
273 var sv = this, |
|
274 bb = sv._bb; |
|
275 |
|
276 // Unbind any previous 'drag' listeners |
|
277 bb.detach(DRAG + '|*'); |
|
278 |
|
279 if (drag) { |
|
280 bb.on(DRAG + '|' + GESTURE_MOVE + START, Y.bind(sv._onGestureMoveStart, sv)); |
|
281 } |
|
282 }, |
|
283 |
|
284 /** |
|
285 * Bind (or unbind) flick listeners. |
|
286 * |
|
287 * @method _bindFlick |
|
288 * @param flick {Object|boolean} If truthy, the method binds listeners for |
|
289 * flick support. If false, the method unbinds flick listeners. |
|
290 * @private |
|
291 */ |
|
292 _bindFlick: function (flick) { |
|
293 var sv = this, |
|
294 bb = sv._bb; |
|
295 |
|
296 // Unbind any previous 'flick' listeners |
|
297 bb.detach(FLICK + '|*'); |
|
298 |
|
299 if (flick) { |
|
300 bb.on(FLICK + '|' + FLICK, Y.bind(sv._flick, sv), flick); |
|
301 |
|
302 // Rebind Drag, becuase _onGestureMoveEnd always has to fire -after- _flick |
|
303 sv._bindDrag(sv.get(DRAG)); |
|
304 } |
|
305 }, |
|
306 |
|
307 /** |
|
308 * Bind (or unbind) mousewheel listeners. |
|
309 * |
|
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. |
|
313 * @private |
|
314 */ |
|
315 _bindMousewheel: function (mousewheel) { |
|
316 var sv = this, |
|
317 bb = sv._bb; |
|
318 |
|
319 // Unbind any previous 'mousewheel' listeners |
|
320 // TODO: This doesn't actually appear to work properly. Fix. #2532743 |
|
321 bb.detach(MOUSEWHEEL + '|*'); |
|
322 |
|
323 // Only enable for vertical scrollviews |
|
324 if (mousewheel) { |
|
325 // Bound to document, because that's where mousewheel events fire off of. |
|
326 Y.one(DOCUMENT).on(MOUSEWHEEL, Y.bind(sv._mousewheel, sv)); |
|
327 } |
|
328 }, |
|
329 |
|
330 /** |
|
331 * syncUI implementation. |
|
332 * |
|
333 * Update the scroll position, based on the current value of scrollX/scrollY. |
|
334 * |
|
335 * @method syncUI |
|
336 */ |
|
337 syncUI: function () { |
|
338 var sv = this, |
|
339 scrollDims = sv._getScrollDims(), |
|
340 width = scrollDims.offsetWidth, |
|
341 height = scrollDims.offsetHeight, |
|
342 scrollWidth = scrollDims.scrollWidth, |
|
343 scrollHeight = scrollDims.scrollHeight; |
|
344 |
|
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 |
|
349 sv._cAxis = { |
|
350 x: (scrollWidth > width), |
|
351 y: (scrollHeight > height) |
|
352 }; |
|
353 |
|
354 sv._set(AXIS, sv._cAxis); |
|
355 } |
|
356 |
|
357 // get text direction on or inherited by scrollview node |
|
358 sv.rtl = (sv._cb.getComputedStyle('direction') === 'rtl'); |
|
359 |
|
360 // Cache the disabled value |
|
361 sv._cDisabled = sv.get(DISABLED); |
|
362 |
|
363 // Run this to set initial values |
|
364 sv._uiDimensionsChange(); |
|
365 |
|
366 // If we're out-of-bounds, snap back. |
|
367 if (sv._isOutOfBounds()) { |
|
368 sv._snapBack(); |
|
369 } |
|
370 }, |
|
371 |
|
372 /** |
|
373 * Utility method to obtain widget dimensions |
|
374 * |
|
375 * @method _getScrollDims |
|
376 * @return {Object} The offsetWidth, offsetHeight, scrollWidth and |
|
377 * scrollHeight as an array: [offsetWidth, offsetHeight, scrollWidth, |
|
378 * scrollHeight] |
|
379 * @private |
|
380 */ |
|
381 _getScrollDims: function () { |
|
382 var sv = this, |
|
383 cb = sv._cb, |
|
384 bb = sv._bb, |
|
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), |
|
391 origHWTransform, |
|
392 dims; |
|
393 |
|
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); |
|
398 } |
|
399 |
|
400 origHWTransform = sv._forceHWTransforms; |
|
401 sv._forceHWTransforms = false; // the z translation was causing issues with picking up accurate scrollWidths in Chrome/Mac. |
|
402 |
|
403 sv._moveTo(cb, 0, 0); |
|
404 dims = { |
|
405 'offsetWidth': bb.get('offsetWidth'), |
|
406 'offsetHeight': bb.get('offsetHeight'), |
|
407 'scrollWidth': bb.get('scrollWidth'), |
|
408 'scrollHeight': bb.get('scrollHeight') |
|
409 }; |
|
410 sv._moveTo(cb, -(origX), -(origY)); |
|
411 |
|
412 sv._forceHWTransforms = origHWTransform; |
|
413 |
|
414 return dims; |
|
415 }, |
|
416 |
|
417 /** |
|
418 * This method gets invoked whenever the height or width attributes change, |
|
419 * allowing us to determine which scrolling axes need to be enabled. |
|
420 * |
|
421 * @method _uiDimensionsChange |
|
422 * @protected |
|
423 */ |
|
424 _uiDimensionsChange: function () { |
|
425 var sv = this, |
|
426 bb = sv._bb, |
|
427 scrollDims = sv._getScrollDims(), |
|
428 width = scrollDims.offsetWidth, |
|
429 height = scrollDims.offsetHeight, |
|
430 scrollWidth = scrollDims.scrollWidth, |
|
431 scrollHeight = scrollDims.scrollHeight, |
|
432 rtl = sv.rtl, |
|
433 svAxis = sv._cAxis, |
|
434 minScrollX = (rtl ? Math.min(0, -(scrollWidth - width)) : 0), |
|
435 maxScrollX = (rtl ? 0 : Math.max(0, scrollWidth - width)), |
|
436 minScrollY = 0, |
|
437 maxScrollY = Math.max(0, scrollHeight - height); |
|
438 |
|
439 if (svAxis && svAxis.x) { |
|
440 bb.addClass(CLASS_NAMES.horizontal); |
|
441 } |
|
442 |
|
443 if (svAxis && svAxis.y) { |
|
444 bb.addClass(CLASS_NAMES.vertical); |
|
445 } |
|
446 |
|
447 sv._setBounds({ |
|
448 minScrollX: minScrollX, |
|
449 maxScrollX: maxScrollX, |
|
450 minScrollY: minScrollY, |
|
451 maxScrollY: maxScrollY |
|
452 }); |
|
453 }, |
|
454 |
|
455 /** |
|
456 * Set the bounding dimensions of the ScrollView |
|
457 * |
|
458 * @method _setBounds |
|
459 * @protected |
|
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 |
|
465 */ |
|
466 _setBounds: function (bounds) { |
|
467 var sv = this; |
|
468 |
|
469 // TODO: Do a check to log if the bounds are invalid |
|
470 |
|
471 sv._minScrollX = bounds.minScrollX; |
|
472 sv._maxScrollX = bounds.maxScrollX; |
|
473 sv._minScrollY = bounds.minScrollY; |
|
474 sv._maxScrollY = bounds.maxScrollY; |
|
475 }, |
|
476 |
|
477 /** |
|
478 * Get the bounding dimensions of the ScrollView |
|
479 * |
|
480 * @method _getBounds |
|
481 * @protected |
|
482 */ |
|
483 _getBounds: function () { |
|
484 var sv = this; |
|
485 |
|
486 return { |
|
487 minScrollX: sv._minScrollX, |
|
488 maxScrollX: sv._maxScrollX, |
|
489 minScrollY: sv._minScrollY, |
|
490 maxScrollY: sv._maxScrollY |
|
491 }; |
|
492 |
|
493 }, |
|
494 |
|
495 /** |
|
496 * Scroll the element to a given xy coordinate |
|
497 * |
|
498 * @method scrollTo |
|
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) |
|
505 */ |
|
506 scrollTo: function (x, y, duration, easing, node) { |
|
507 // Check to see if widget is disabled |
|
508 if (this._cDisabled) { |
|
509 return; |
|
510 } |
|
511 |
|
512 var sv = this, |
|
513 cb = sv._cb, |
|
514 TRANS = ScrollView._TRANSITION, |
|
515 callback = Y.bind(sv._onTransEnd, sv), // @Todo : cache this |
|
516 newX = 0, |
|
517 newY = 0, |
|
518 transition = {}, |
|
519 transform; |
|
520 |
|
521 // default the optional arguments |
|
522 duration = duration || 0; |
|
523 easing = easing || sv.get(EASING); // @TODO: Cache this |
|
524 node = node || cb; |
|
525 |
|
526 if (x !== null) { |
|
527 sv.set(SCROLL_X, x, {src:UI}); |
|
528 newX = -(x); |
|
529 } |
|
530 |
|
531 if (y !== null) { |
|
532 sv.set(SCROLL_Y, y, {src:UI}); |
|
533 newY = -(y); |
|
534 } |
|
535 |
|
536 transform = sv._transform(newX, newY); |
|
537 |
|
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); |
|
541 } |
|
542 |
|
543 // Move |
|
544 if (duration === 0) { |
|
545 if (NATIVE_TRANSITIONS) { |
|
546 node.setStyle('transform', transform); |
|
547 } |
|
548 else { |
|
549 // TODO: If both set, batch them in the same update |
|
550 // Update: Nope, setStyles() just loops through each property and applies it. |
|
551 if (x !== null) { |
|
552 node.setStyle(LEFT, newX + PX); |
|
553 } |
|
554 if (y !== null) { |
|
555 node.setStyle(TOP, newY + PX); |
|
556 } |
|
557 } |
|
558 } |
|
559 |
|
560 // Animate |
|
561 else { |
|
562 transition.easing = easing; |
|
563 transition.duration = duration / 1000; |
|
564 |
|
565 if (NATIVE_TRANSITIONS) { |
|
566 transition.transform = transform; |
|
567 } |
|
568 else { |
|
569 transition.left = newX + PX; |
|
570 transition.top = newY + PX; |
|
571 } |
|
572 |
|
573 node.transition(transition, callback); |
|
574 } |
|
575 }, |
|
576 |
|
577 /** |
|
578 * Utility method, to create the translate transform string with the |
|
579 * x, y translation amounts provided. |
|
580 * |
|
581 * @method _transform |
|
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 |
|
584 * @private |
|
585 */ |
|
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)'; |
|
589 |
|
590 if (this._forceHWTransforms) { |
|
591 prop += ' translateZ(0)'; |
|
592 } |
|
593 |
|
594 return prop; |
|
595 }, |
|
596 |
|
597 /** |
|
598 * Utility method, to move the given element to the given xy position |
|
599 * |
|
600 * @method _moveTo |
|
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 |
|
604 * @private |
|
605 */ |
|
606 _moveTo : function(node, x, y) { |
|
607 if (NATIVE_TRANSITIONS) { |
|
608 node.setStyle('transform', this._transform(x, y)); |
|
609 } else { |
|
610 node.setStyle(LEFT, x + PX); |
|
611 node.setStyle(TOP, y + PX); |
|
612 } |
|
613 }, |
|
614 |
|
615 |
|
616 /** |
|
617 * Content box transition callback |
|
618 * |
|
619 * @method _onTransEnd |
|
620 * @param {Event.Facade} e The event facade |
|
621 * @private |
|
622 */ |
|
623 _onTransEnd: function () { |
|
624 var sv = this; |
|
625 |
|
626 // If for some reason we're OOB, snapback |
|
627 if (sv._isOutOfBounds()) { |
|
628 sv._snapBack(); |
|
629 } |
|
630 else { |
|
631 /** |
|
632 * Notification event fired at the end of a scroll transition |
|
633 * |
|
634 * @event scrollEnd |
|
635 * @param e {EventFacade} The default event facade. |
|
636 */ |
|
637 sv.fire(EV_SCROLL_END); |
|
638 } |
|
639 }, |
|
640 |
|
641 /** |
|
642 * gesturemovestart event handler |
|
643 * |
|
644 * @method _onGestureMoveStart |
|
645 * @param e {Event.Facade} The gesturemovestart event facade |
|
646 * @private |
|
647 */ |
|
648 _onGestureMoveStart: function (e) { |
|
649 |
|
650 if (this._cDisabled) { |
|
651 return false; |
|
652 } |
|
653 |
|
654 var sv = this, |
|
655 bb = sv._bb, |
|
656 currentX = sv.get(SCROLL_X), |
|
657 currentY = sv.get(SCROLL_Y), |
|
658 clientX = e.clientX, |
|
659 clientY = e.clientY; |
|
660 |
|
661 if (sv._prevent.start) { |
|
662 e.preventDefault(); |
|
663 } |
|
664 |
|
665 // if a flick animation is in progress, cancel it |
|
666 if (sv._flickAnim) { |
|
667 sv._cancelFlick(); |
|
668 sv._onTransEnd(); |
|
669 } |
|
670 |
|
671 // Reset lastScrolledAmt |
|
672 sv.lastScrolledAmt = 0; |
|
673 |
|
674 // Stores data for this gesture cycle. Cleaned up later |
|
675 sv._gesture = { |
|
676 |
|
677 // Will hold the axis value |
|
678 axis: null, |
|
679 |
|
680 // The current attribute values |
|
681 startX: currentX, |
|
682 startY: currentY, |
|
683 |
|
684 // The X/Y coordinates where the event began |
|
685 startClientX: clientX, |
|
686 startClientY: clientY, |
|
687 |
|
688 // The X/Y coordinates where the event will end |
|
689 endClientX: null, |
|
690 endClientY: null, |
|
691 |
|
692 // The current delta of the event |
|
693 deltaX: null, |
|
694 deltaY: null, |
|
695 |
|
696 // Will be populated for flicks |
|
697 flick: null, |
|
698 |
|
699 // Create some listeners for the rest of the gesture cycle |
|
700 onGestureMove: bb.on(DRAG + '|' + GESTURE_MOVE, Y.bind(sv._onGestureMove, sv)), |
|
701 |
|
702 // @TODO: Don't bind gestureMoveEnd if it's a Flick? |
|
703 onGestureMoveEnd: bb.on(DRAG + '|' + GESTURE_MOVE + END, Y.bind(sv._onGestureMoveEnd, sv)) |
|
704 }; |
|
705 }, |
|
706 |
|
707 /** |
|
708 * gesturemove event handler |
|
709 * |
|
710 * @method _onGestureMove |
|
711 * @param e {Event.Facade} The gesturemove event facade |
|
712 * @private |
|
713 */ |
|
714 _onGestureMove: function (e) { |
|
715 var sv = this, |
|
716 gesture = sv._gesture, |
|
717 svAxis = sv._cAxis, |
|
718 svAxisX = svAxis.x, |
|
719 svAxisY = svAxis.y, |
|
720 startX = gesture.startX, |
|
721 startY = gesture.startY, |
|
722 startClientX = gesture.startClientX, |
|
723 startClientY = gesture.startClientY, |
|
724 clientX = e.clientX, |
|
725 clientY = e.clientY; |
|
726 |
|
727 if (sv._prevent.move) { |
|
728 e.preventDefault(); |
|
729 } |
|
730 |
|
731 gesture.deltaX = startClientX - clientX; |
|
732 gesture.deltaY = startClientY - clientY; |
|
733 |
|
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; |
|
738 } |
|
739 |
|
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); |
|
743 } |
|
744 else if (gesture.axis === DIM_Y && svAxisY) { |
|
745 sv.set(SCROLL_Y, startY + gesture.deltaY); |
|
746 } |
|
747 }, |
|
748 |
|
749 /** |
|
750 * gesturemoveend event handler |
|
751 * |
|
752 * @method _onGestureMoveEnd |
|
753 * @param e {Event.Facade} The gesturemoveend event facade |
|
754 * @private |
|
755 */ |
|
756 _onGestureMoveEnd: function (e) { |
|
757 var sv = this, |
|
758 gesture = sv._gesture, |
|
759 flick = gesture.flick, |
|
760 clientX = e.clientX, |
|
761 clientY = e.clientY, |
|
762 isOOB; |
|
763 |
|
764 if (sv._prevent.end) { |
|
765 e.preventDefault(); |
|
766 } |
|
767 |
|
768 // Store the end X/Y coordinates |
|
769 gesture.endClientX = clientX; |
|
770 gesture.endClientY = clientY; |
|
771 |
|
772 // Cleanup the event handlers |
|
773 gesture.onGestureMove.detach(); |
|
774 gesture.onGestureMoveEnd.detach(); |
|
775 |
|
776 // If this wasn't a flick, wrap up the gesture cycle |
|
777 if (!flick) { |
|
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? |
|
781 |
|
782 // If there was movement (_onGestureMove fired) |
|
783 if (gesture.deltaX !== null && gesture.deltaY !== null) { |
|
784 |
|
785 isOOB = sv._isOutOfBounds(); |
|
786 |
|
787 // If we're out-out-bounds, then snapback |
|
788 if (isOOB) { |
|
789 sv._snapBack(); |
|
790 } |
|
791 |
|
792 // Inbounds |
|
793 else { |
|
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])) { |
|
797 sv._onTransEnd(); |
|
798 } |
|
799 } |
|
800 } |
|
801 } |
|
802 }, |
|
803 |
|
804 /** |
|
805 * Execute a flick at the end of a scroll action |
|
806 * |
|
807 * @method _flick |
|
808 * @param e {Event.Facade} The Flick event facade |
|
809 * @private |
|
810 */ |
|
811 _flick: function (e) { |
|
812 if (this._cDisabled) { |
|
813 return false; |
|
814 } |
|
815 |
|
816 var sv = this, |
|
817 svAxis = sv._cAxis, |
|
818 flick = e.flick, |
|
819 flickAxis = flick.axis, |
|
820 flickVelocity = flick.velocity, |
|
821 axisAttr = flickAxis === DIM_X ? SCROLL_X : SCROLL_Y, |
|
822 startPosition = sv.get(axisAttr); |
|
823 |
|
824 // Sometimes flick is enabled, but drag is disabled |
|
825 if (sv._gesture) { |
|
826 sv._gesture.flick = flick; |
|
827 } |
|
828 |
|
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); |
|
832 } |
|
833 }, |
|
834 |
|
835 /** |
|
836 * Execute a single frame in the flick animation |
|
837 * |
|
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 |
|
842 * @protected |
|
843 */ |
|
844 _flickFrame: function (velocity, flickAxis, startPosition) { |
|
845 |
|
846 var sv = this, |
|
847 axisAttr = flickAxis === DIM_X ? SCROLL_X : SCROLL_Y, |
|
848 bounds = sv._getBounds(), |
|
849 |
|
850 // Localize cached values |
|
851 bounce = sv._cBounce, |
|
852 bounceRange = sv._cBounceRange, |
|
853 deceleration = sv._cDeceleration, |
|
854 frameDuration = sv._cFrameDuration, |
|
855 |
|
856 // Calculate |
|
857 newVelocity = velocity * deceleration, |
|
858 newPosition = startPosition - (frameDuration * newVelocity), |
|
859 |
|
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)), |
|
871 tooSlow; |
|
872 |
|
873 // If we're within the range but outside min/max, dampen the velocity |
|
874 if (withinMinRange || withinMaxRange) { |
|
875 newVelocity *= bounce; |
|
876 } |
|
877 |
|
878 // Is the velocity too slow to bother? |
|
879 tooSlow = (Math.abs(newVelocity).toFixed(4) < 0.015); |
|
880 |
|
881 // If the velocity is too slow or we're outside the range |
|
882 if (tooSlow || belowMinRange || aboveMaxRange) { |
|
883 // Cancel and delete sv._flickAnim |
|
884 if (sv._flickAnim) { |
|
885 sv._cancelFlick(); |
|
886 } |
|
887 |
|
888 // If we're inside the scroll area, just end |
|
889 if (aboveMin && belowMax) { |
|
890 sv._onTransEnd(); |
|
891 } |
|
892 |
|
893 // We're outside the scroll area, so we need to snap back |
|
894 else { |
|
895 sv._snapBack(); |
|
896 } |
|
897 } |
|
898 |
|
899 // Otherwise, animate to the next frame |
|
900 else { |
|
901 // @TODO: maybe use requestAnimationFrame instead |
|
902 sv._flickAnim = Y.later(frameDuration, sv, '_flickFrame', [newVelocity, flickAxis, newPosition]); |
|
903 sv.set(axisAttr, newPosition); |
|
904 } |
|
905 }, |
|
906 |
|
907 _cancelFlick: function () { |
|
908 var sv = this; |
|
909 |
|
910 if (sv._flickAnim) { |
|
911 // Cancel the flick (if it exists) |
|
912 sv._flickAnim.cancel(); |
|
913 |
|
914 // Also delete it, otherwise _onGestureMoveStart will think we're still flicking |
|
915 delete sv._flickAnim; |
|
916 } |
|
917 |
|
918 }, |
|
919 |
|
920 /** |
|
921 * Handle mousewheel events on the widget |
|
922 * |
|
923 * @method _mousewheel |
|
924 * @param e {Event.Facade} The mousewheel event facade |
|
925 * @private |
|
926 */ |
|
927 _mousewheel: function (e) { |
|
928 var sv = this, |
|
929 scrollY = sv.get(SCROLL_Y), |
|
930 bounds = sv._getBounds(), |
|
931 bb = sv._bb, |
|
932 scrollOffset = 10, // 10px |
|
933 isForward = (e.wheelDelta > 0), |
|
934 scrollToY = scrollY - ((isForward ? 1 : -1) * scrollOffset); |
|
935 |
|
936 scrollToY = _constrain(scrollToY, bounds.minScrollY, bounds.maxScrollY); |
|
937 |
|
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]) { |
|
943 |
|
944 // Reset lastScrolledAmt |
|
945 sv.lastScrolledAmt = 0; |
|
946 |
|
947 // Jump to the new offset |
|
948 sv.set(SCROLL_Y, scrollToY); |
|
949 |
|
950 // if we have scrollbars plugin, update & set the flash timer on the scrollbar |
|
951 // @TODO: This probably shouldn't be in this module |
|
952 if (sv.scrollbars) { |
|
953 // @TODO: The scrollbars should handle this themselves |
|
954 sv.scrollbars._update(); |
|
955 sv.scrollbars.flash(); |
|
956 // or just this |
|
957 // sv.scrollbars._hostDimensionsChange(); |
|
958 } |
|
959 |
|
960 // Fire the 'scrollEnd' event |
|
961 sv._onTransEnd(); |
|
962 |
|
963 // prevent browser default behavior on mouse scroll |
|
964 e.preventDefault(); |
|
965 } |
|
966 }, |
|
967 |
|
968 /** |
|
969 * Checks to see the current scrollX/scrollY position beyond the min/max boundary |
|
970 * |
|
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) |
|
975 * @private |
|
976 */ |
|
977 _isOutOfBounds: function (x, y) { |
|
978 var sv = this, |
|
979 svAxis = sv._cAxis, |
|
980 svAxisX = svAxis.x, |
|
981 svAxisY = svAxis.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; |
|
989 |
|
990 return (svAxisX && (currentX < minX || currentX > maxX)) || (svAxisY && (currentY < minY || currentY > maxY)); |
|
991 }, |
|
992 |
|
993 /** |
|
994 * Bounces back |
|
995 * @TODO: Should be more generalized and support both X and Y detection |
|
996 * |
|
997 * @method _snapBack |
|
998 * @private |
|
999 */ |
|
1000 _snapBack: function () { |
|
1001 var sv = this, |
|
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); |
|
1013 |
|
1014 if (newX !== currentX) { |
|
1015 sv.set(SCROLL_X, newX, {duration:duration, easing:easing}); |
|
1016 } |
|
1017 else if (newY !== currentY) { |
|
1018 sv.set(SCROLL_Y, newY, {duration:duration, easing:easing}); |
|
1019 } |
|
1020 else { |
|
1021 sv._onTransEnd(); |
|
1022 } |
|
1023 }, |
|
1024 |
|
1025 /** |
|
1026 * After listener for changes to the scrollX or scrollY attribute |
|
1027 * |
|
1028 * @method _afterScrollChange |
|
1029 * @param e {Event.Facade} The event facade |
|
1030 * @protected |
|
1031 */ |
|
1032 _afterScrollChange: function (e) { |
|
1033 if (e.src === ScrollView.UI_SRC) { |
|
1034 return false; |
|
1035 } |
|
1036 |
|
1037 var sv = this, |
|
1038 duration = e.duration, |
|
1039 easing = e.easing, |
|
1040 val = e.newVal, |
|
1041 scrollToArgs = []; |
|
1042 |
|
1043 // Set the scrolled value |
|
1044 sv.lastScrolledAmt = sv.lastScrolledAmt + (e.newVal - e.prevVal); |
|
1045 |
|
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)); |
|
1050 } |
|
1051 else { |
|
1052 scrollToArgs.push(sv.get(SCROLL_X)); |
|
1053 scrollToArgs.push(val); |
|
1054 } |
|
1055 |
|
1056 scrollToArgs.push(duration); |
|
1057 scrollToArgs.push(easing); |
|
1058 |
|
1059 sv.scrollTo.apply(sv, scrollToArgs); |
|
1060 }, |
|
1061 |
|
1062 /** |
|
1063 * After listener for changes to the flick attribute |
|
1064 * |
|
1065 * @method _afterFlickChange |
|
1066 * @param e {Event.Facade} The event facade |
|
1067 * @protected |
|
1068 */ |
|
1069 _afterFlickChange: function (e) { |
|
1070 this._bindFlick(e.newVal); |
|
1071 }, |
|
1072 |
|
1073 /** |
|
1074 * After listener for changes to the disabled attribute |
|
1075 * |
|
1076 * @method _afterDisabledChange |
|
1077 * @param e {Event.Facade} The event facade |
|
1078 * @protected |
|
1079 */ |
|
1080 _afterDisabledChange: function (e) { |
|
1081 // Cache for performance - we check during move |
|
1082 this._cDisabled = e.newVal; |
|
1083 }, |
|
1084 |
|
1085 /** |
|
1086 * After listener for the axis attribute |
|
1087 * |
|
1088 * @method _afterAxisChange |
|
1089 * @param e {Event.Facade} The event facade |
|
1090 * @protected |
|
1091 */ |
|
1092 _afterAxisChange: function (e) { |
|
1093 this._cAxis = e.newVal; |
|
1094 }, |
|
1095 |
|
1096 /** |
|
1097 * After listener for changes to the drag attribute |
|
1098 * |
|
1099 * @method _afterDragChange |
|
1100 * @param e {Event.Facade} The event facade |
|
1101 * @protected |
|
1102 */ |
|
1103 _afterDragChange: function (e) { |
|
1104 this._bindDrag(e.newVal); |
|
1105 }, |
|
1106 |
|
1107 /** |
|
1108 * After listener for the height or width attribute |
|
1109 * |
|
1110 * @method _afterDimChange |
|
1111 * @param e {Event.Facade} The event facade |
|
1112 * @protected |
|
1113 */ |
|
1114 _afterDimChange: function () { |
|
1115 this._uiDimensionsChange(); |
|
1116 }, |
|
1117 |
|
1118 /** |
|
1119 * After listener for scrollEnd, for cleanup |
|
1120 * |
|
1121 * @method _afterScrollEnd |
|
1122 * @param e {Event.Facade} The event facade |
|
1123 * @protected |
|
1124 */ |
|
1125 _afterScrollEnd: function () { |
|
1126 var sv = this; |
|
1127 |
|
1128 if (sv._flickAnim) { |
|
1129 sv._cancelFlick(); |
|
1130 } |
|
1131 |
|
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? |
|
1135 }, |
|
1136 |
|
1137 /** |
|
1138 * Setter for 'axis' attribute |
|
1139 * |
|
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 |
|
1144 * |
|
1145 * @protected |
|
1146 */ |
|
1147 _axisSetter: function (val) { |
|
1148 |
|
1149 // Turn a string into an axis object |
|
1150 if (Y.Lang.isString(val)) { |
|
1151 return { |
|
1152 x: val.match(/x/i) ? true : false, |
|
1153 y: val.match(/y/i) ? true : false |
|
1154 }; |
|
1155 } |
|
1156 }, |
|
1157 |
|
1158 /** |
|
1159 * The scrollX, scrollY setter implementation |
|
1160 * |
|
1161 * @method _setScroll |
|
1162 * @private |
|
1163 * @param {Number} val |
|
1164 * @param {String} dim |
|
1165 * |
|
1166 * @return {Number} The value |
|
1167 */ |
|
1168 _setScroll : function(val) { |
|
1169 |
|
1170 // Just ensure the widget is not disabled |
|
1171 if (this._cDisabled) { |
|
1172 val = Y.Attribute.INVALID_VALUE; |
|
1173 } |
|
1174 |
|
1175 return val; |
|
1176 }, |
|
1177 |
|
1178 /** |
|
1179 * Setter for the scrollX attribute |
|
1180 * |
|
1181 * @method _setScrollX |
|
1182 * @param val {Number} The new scrollX value |
|
1183 * @return {Number} The normalized value |
|
1184 * @protected |
|
1185 */ |
|
1186 _setScrollX: function(val) { |
|
1187 return this._setScroll(val, DIM_X); |
|
1188 }, |
|
1189 |
|
1190 /** |
|
1191 * Setter for the scrollY ATTR |
|
1192 * |
|
1193 * @method _setScrollY |
|
1194 * @param val {Number} The new scrollY value |
|
1195 * @return {Number} The normalized value |
|
1196 * @protected |
|
1197 */ |
|
1198 _setScrollY: function(val) { |
|
1199 return this._setScroll(val, DIM_Y); |
|
1200 } |
|
1201 |
|
1202 // End prototype properties |
|
1203 |
|
1204 }, { |
|
1205 |
|
1206 // Static properties |
|
1207 |
|
1208 /** |
|
1209 * The identity of the widget. |
|
1210 * |
|
1211 * @property NAME |
|
1212 * @type String |
|
1213 * @default 'scrollview' |
|
1214 * @readOnly |
|
1215 * @protected |
|
1216 * @static |
|
1217 */ |
|
1218 NAME: 'scrollview', |
|
1219 |
|
1220 /** |
|
1221 * Static property used to define the default attribute configuration of |
|
1222 * the Widget. |
|
1223 * |
|
1224 * @property ATTRS |
|
1225 * @type {Object} |
|
1226 * @protected |
|
1227 * @static |
|
1228 */ |
|
1229 ATTRS: { |
|
1230 |
|
1231 /** |
|
1232 * Specifies ability to scroll on x, y, or x and y axis/axes. |
|
1233 * |
|
1234 * @attribute axis |
|
1235 * @type String |
|
1236 */ |
|
1237 axis: { |
|
1238 setter: '_axisSetter', |
|
1239 writeOnce: 'initOnly' |
|
1240 }, |
|
1241 |
|
1242 /** |
|
1243 * The current scroll position in the x-axis |
|
1244 * |
|
1245 * @attribute scrollX |
|
1246 * @type Number |
|
1247 * @default 0 |
|
1248 */ |
|
1249 scrollX: { |
|
1250 value: 0, |
|
1251 setter: '_setScrollX' |
|
1252 }, |
|
1253 |
|
1254 /** |
|
1255 * The current scroll position in the y-axis |
|
1256 * |
|
1257 * @attribute scrollY |
|
1258 * @type Number |
|
1259 * @default 0 |
|
1260 */ |
|
1261 scrollY: { |
|
1262 value: 0, |
|
1263 setter: '_setScrollY' |
|
1264 }, |
|
1265 |
|
1266 /** |
|
1267 * Drag coefficent for inertial scrolling. The closer to 1 this |
|
1268 * value is, the less friction during scrolling. |
|
1269 * |
|
1270 * @attribute deceleration |
|
1271 * @default 0.93 |
|
1272 */ |
|
1273 deceleration: { |
|
1274 value: 0.93 |
|
1275 }, |
|
1276 |
|
1277 /** |
|
1278 * Drag coefficient for intertial scrolling at the upper |
|
1279 * and lower boundaries of the scrollview. Set to 0 to |
|
1280 * disable "rubber-banding". |
|
1281 * |
|
1282 * @attribute bounce |
|
1283 * @type Number |
|
1284 * @default 0.1 |
|
1285 */ |
|
1286 bounce: { |
|
1287 value: 0.1 |
|
1288 }, |
|
1289 |
|
1290 /** |
|
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) |
|
1293 * |
|
1294 * @attribute flick |
|
1295 * @type Object |
|
1296 * @default Object with properties minDistance = 10, minVelocity = 0.3. |
|
1297 */ |
|
1298 flick: { |
|
1299 value: { |
|
1300 minDistance: 10, |
|
1301 minVelocity: 0.3 |
|
1302 } |
|
1303 }, |
|
1304 |
|
1305 /** |
|
1306 * Enable/Disable dragging the ScrollView content (note: flick support is enabled/disabled separately) |
|
1307 * @attribute drag |
|
1308 * @type boolean |
|
1309 * @default true |
|
1310 */ |
|
1311 drag: { |
|
1312 value: true |
|
1313 }, |
|
1314 |
|
1315 /** |
|
1316 * The default duration to use when animating the bounce snap back. |
|
1317 * |
|
1318 * @attribute snapDuration |
|
1319 * @type Number |
|
1320 * @default 400 |
|
1321 */ |
|
1322 snapDuration: { |
|
1323 value: 400 |
|
1324 }, |
|
1325 |
|
1326 /** |
|
1327 * The default easing to use when animating the bounce snap back. |
|
1328 * |
|
1329 * @attribute snapEasing |
|
1330 * @type String |
|
1331 * @default 'ease-out' |
|
1332 */ |
|
1333 snapEasing: { |
|
1334 value: 'ease-out' |
|
1335 }, |
|
1336 |
|
1337 /** |
|
1338 * The default easing used when animating the flick |
|
1339 * |
|
1340 * @attribute easing |
|
1341 * @type String |
|
1342 * @default 'cubic-bezier(0, 0.1, 0, 1.0)' |
|
1343 */ |
|
1344 easing: { |
|
1345 value: 'cubic-bezier(0, 0.1, 0, 1.0)' |
|
1346 }, |
|
1347 |
|
1348 /** |
|
1349 * The interval (ms) used when animating the flick for JS-timer animations |
|
1350 * |
|
1351 * @attribute frameDuration |
|
1352 * @type Number |
|
1353 * @default 15 |
|
1354 */ |
|
1355 frameDuration: { |
|
1356 value: 15 |
|
1357 }, |
|
1358 |
|
1359 /** |
|
1360 * The default bounce distance in pixels |
|
1361 * |
|
1362 * @attribute bounceRange |
|
1363 * @type Number |
|
1364 * @default 150 |
|
1365 */ |
|
1366 bounceRange: { |
|
1367 value: 150 |
|
1368 } |
|
1369 }, |
|
1370 |
|
1371 /** |
|
1372 * List of class names used in the scrollview's DOM |
|
1373 * |
|
1374 * @property CLASS_NAMES |
|
1375 * @type Object |
|
1376 * @static |
|
1377 */ |
|
1378 CLASS_NAMES: CLASS_NAMES, |
|
1379 |
|
1380 /** |
|
1381 * Flag used to source property changes initiated from the DOM |
|
1382 * |
|
1383 * @property UI_SRC |
|
1384 * @type String |
|
1385 * @static |
|
1386 * @default 'ui' |
|
1387 */ |
|
1388 UI_SRC: UI, |
|
1389 |
|
1390 /** |
|
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"). |
|
1395 * |
|
1396 * @property _TRANSITION |
|
1397 * @private |
|
1398 */ |
|
1399 _TRANSITION: { |
|
1400 DURATION: (vendorPrefix ? vendorPrefix + 'TransitionDuration' : 'transitionDuration'), |
|
1401 PROPERTY: (vendorPrefix ? vendorPrefix + 'TransitionProperty' : 'transitionProperty') |
|
1402 }, |
|
1403 |
|
1404 /** |
|
1405 * The default bounce distance in pixels |
|
1406 * |
|
1407 * @property BOUNCE_RANGE |
|
1408 * @type Number |
|
1409 * @static |
|
1410 * @default false |
|
1411 * @deprecated (in 3.7.0) |
|
1412 */ |
|
1413 BOUNCE_RANGE: false, |
|
1414 |
|
1415 /** |
|
1416 * The interval (ms) used when animating the flick |
|
1417 * |
|
1418 * @property FRAME_STEP |
|
1419 * @type Number |
|
1420 * @static |
|
1421 * @default false |
|
1422 * @deprecated (in 3.7.0) |
|
1423 */ |
|
1424 FRAME_STEP: false, |
|
1425 |
|
1426 /** |
|
1427 * The default easing used when animating the flick |
|
1428 * |
|
1429 * @property EASING |
|
1430 * @type String |
|
1431 * @static |
|
1432 * @default false |
|
1433 * @deprecated (in 3.7.0) |
|
1434 */ |
|
1435 EASING: false, |
|
1436 |
|
1437 /** |
|
1438 * The default easing to use when animating the bounce snap back. |
|
1439 * |
|
1440 * @property SNAP_EASING |
|
1441 * @type String |
|
1442 * @static |
|
1443 * @default false |
|
1444 * @deprecated (in 3.7.0) |
|
1445 */ |
|
1446 SNAP_EASING: false, |
|
1447 |
|
1448 /** |
|
1449 * The default duration to use when animating the bounce snap back. |
|
1450 * |
|
1451 * @property SNAP_DURATION |
|
1452 * @type Number |
|
1453 * @static |
|
1454 * @default false |
|
1455 * @deprecated (in 3.7.0) |
|
1456 */ |
|
1457 SNAP_DURATION: false |
|
1458 |
|
1459 // End static properties |
|
1460 |
|
1461 }); |
|
1462 |
|
1463 }, '3.10.3', {"requires": ["widget", "event-gestures", "event-mousewheel", "transition"], "skinnable": true}); |