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