]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - jssource/src_files/include/javascript/yui3/build/node-flick/node-flick.js
Release 6.2.2
[Github/sugarcrm.git] / jssource / src_files / include / javascript / yui3 / build / node-flick / node-flick.js
1 /*
2 Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
5 version: 3.3.0
6 build: 3167
7 */
8 YUI.add('node-flick', function(Y) {
9
10 /**
11  * Provide a simple Flick plugin, which can be used along with the "flick" gesture event, to 
12  * animate the motion of the host node in response to a (mouse or touch) flick gesture. 
13  * 
14  * <p>The current implementation is designed to move the node, relative to the bounds of a parent node and is suitable
15  * for scroll/carousel type implementations. Future versions will remove that constraint, to allow open ended movement within
16  * the document.</p>
17  *
18  * @module node-flick
19  */
20
21     var HOST = "host",
22         PARENT_NODE = "parentNode",
23         BOUNDING_BOX = "boundingBox",
24         OFFSET_HEIGHT = "offsetHeight",
25         OFFSET_WIDTH = "offsetWidth",
26         SCROLL_HEIGHT = "scrollHeight",
27         SCROLL_WIDTH = "scrollWidth",
28         BOUNCE = "bounce",
29         MIN_DISTANCE = "minDistance",
30         MIN_VELOCITY = "minVelocity",
31         BOUNCE_DISTANCE = "bounceDistance",
32         DECELERATION = "deceleration",
33         STEP = "step",
34         DURATION = "duration",
35         EASING = "easing",
36         FLICK = "flick",
37         
38         getClassName = Y.ClassNameManager.getClassName;
39
40     /**
41      * A plugin class which can be used to animate the motion of a node, in response to a flick gesture.
42      * 
43      * @class Flick
44      * @namespace Plugin
45      * @param {Object} config The initial attribute values for the plugin
46      */
47     function Flick(config) {
48         Flick.superclass.constructor.apply(this, arguments);
49     }
50
51     Flick.ATTRS = {
52
53         /**
54          * Drag coefficent for inertial scrolling. The closer to 1 this
55          * value is, the less friction during scrolling.
56          *
57          * @attribute deceleration
58          * @default 0.98
59          */
60         deceleration : {
61             value: 0.98
62         },
63
64         /**
65          * Drag coefficient for intertial scrolling at the upper
66          * and lower boundaries of the scrollview. Set to 0 to 
67          * disable "rubber-banding".
68          *
69          * @attribute bounce
70          * @type Number
71          * @default 0.7
72          */
73         bounce : {
74             value: 0.7
75         },
76
77         /**
78          * The bounce distance in pixels
79          *
80          * @attribute bounceDistance
81          * @type Number
82          * @default 150
83          */
84         bounceDistance : {
85             value: 150
86         },
87
88         /**
89          * The minimum flick gesture velocity (px/ms) at which to trigger the flick response
90          *
91          * @attribute minVelocity
92          * @type Number
93          * @default 0
94          */
95         minVelocity : {
96             value: 0
97         },
98
99         /**
100          * The minimum flick gesture distance (px) for which to trigger the flick response
101          *
102          * @attribute minVelocity
103          * @type Number
104          * @default 10
105          */
106         minDistance : {
107             value: 10
108         },
109
110         /**
111          * The constraining box relative to which the flick animation and bounds should be calculated.
112          *
113          * @attribute boundingBox
114          * @type Node
115          * @default parentNode
116          */
117         boundingBox : {
118             valueFn : function() {
119                 return this.get(HOST).get(PARENT_NODE);
120             }
121         },
122
123         /**
124          * The constraining box relative to which the flick animation and bounds should be calculated.
125          *
126          * @attribute boundingBox
127          * @type Node
128          * @default parentNode
129          */
130         step : {
131             value:10
132         },
133
134         /**
135          * The custom duration to apply to the flick animation. By default,
136          * the animation duration is controlled by the deceleration factor.
137          *
138          * @attribute duration
139          * @type Number
140          * @default null
141          */
142         duration : {
143             value:null
144         },
145
146         /**
147          * The custom transition easing to use for the flick animation. If not
148          * provided defaults to internally to Flick.EASING, or Flick.SNAP_EASING based
149          * on whether or not we're animating the flick or bounce step. 
150          *
151          * @attribute easing
152          * @type String
153          * @default null
154          */
155         easing : {
156             value:null
157         }
158     };
159
160     /**
161      * The NAME of the Flick class. Used to prefix events generated
162      * by the plugin.
163      *
164      * @property Flick.NAME
165      * @static
166      * @type String
167      * @default "pluginFlick"
168      */
169     Flick.NAME = "pluginFlick";
170
171     /**
172      * The namespace for the plugin. This will be the property on the node, which will 
173      * reference the plugin instance, when it's plugged in.
174      *
175      * @property Flick.NS
176      * @static
177      * @type String
178      * @default "flick"
179      */
180     Flick.NS = "flick";
181
182     Y.extend(Flick, Y.Plugin.Base, {
183
184         /**
185          * The initializer lifecycle implementation.
186          *
187          * @method initializer
188          * @param {Object} config The user configuration for the plugin  
189          */
190         initializer : function(config) {
191             this._node = this.get(HOST);
192
193             this._renderClasses();
194             this.setBounds();
195
196             this._node.on(FLICK, Y.bind(this._onFlick, this), {
197                 minDistance : this.get(MIN_DISTANCE),
198                 minVelocity : this.get(MIN_VELOCITY)
199             });
200         },
201
202         /**
203          * Sets the min/max boundaries for the flick animation,
204          * based on the boundingBox dimensions.
205          * 
206          * @method setBounds
207          */
208         setBounds : function () {
209             var box = this.get(BOUNDING_BOX),
210                 node = this._node,
211
212                 boxHeight = box.get(OFFSET_HEIGHT),
213                 boxWidth = box.get(OFFSET_WIDTH),
214
215                 contentHeight = node.get(SCROLL_HEIGHT),
216                 contentWidth = node.get(SCROLL_WIDTH);
217
218             if (contentHeight > boxHeight) {
219                 this._maxY = contentHeight - boxHeight;
220                 this._minY = 0;
221                 this._scrollY = true;
222             }
223
224             if (contentWidth > boxWidth) {
225                 this._maxX = contentWidth - boxWidth;
226                 this._minX = 0;
227                 this._scrollX = true;
228             }
229
230             this._x = this._y = 0;
231
232             node.set("top", this._y + "px");
233             node.set("left", this._x + "px");
234         },
235
236         /**
237          * Adds the CSS classes, necessary to set up overflow/position properties on the
238          * node and boundingBox. 
239          *
240          * @method _renderClasses
241          * @protected
242          */
243         _renderClasses : function() {
244             this.get(BOUNDING_BOX).addClass(Flick.CLASS_NAMES.box);
245             this._node.addClass(Flick.CLASS_NAMES.content);
246         },
247
248         /**
249          * The flick event listener. Kicks off the flick animation.
250          *
251          * @method _onFlick
252          * @param e {EventFacade} The flick event facade, containing e.flick.distance, e.flick.velocity etc.
253          * @protected
254          */
255         _onFlick: function(e) {
256             this._v = e.flick.velocity;
257             this._flick = true;
258             this._flickAnim();
259         },
260
261         /**
262          * Executes a single frame in the flick animation
263          *
264          * @method _flickFrame
265          * @protected
266          */
267         _flickAnim: function() {
268
269             var y = this._y,
270                 x = this._x,
271
272                 maxY = this._maxY,
273                 minY = this._minY,
274                 maxX = this._maxX,
275                 minX = this._minX,
276                 velocity = this._v,
277
278                 step = this.get(STEP),
279                 deceleration = this.get(DECELERATION),
280                 bounce = this.get(BOUNCE);
281
282             this._v = (velocity * deceleration);
283
284             this._snapToEdge = false;
285
286             if (this._scrollX) {
287                 x = x - (velocity * step);
288             }
289     
290             if (this._scrollY) {
291                 y = y - (velocity * step);
292             }
293
294             if (Math.abs(velocity).toFixed(4) <= Flick.VELOCITY_THRESHOLD) {
295
296                 this._flick = false;
297
298                 this._killTimer(!(this._exceededYBoundary || this._exceededXBoundary));
299
300                 if (this._scrollX) {
301                     if (x < minX) {
302                         this._snapToEdge = true;
303                         this._setX(minX);
304                     } else if (x > maxX) {
305                         this._snapToEdge = true;
306                         this._setX(maxX);
307                     }
308                 }
309
310                 if (this._scrollY) {
311                     if (y < minY) {
312                         this._snapToEdge = true;
313                         this._setY(minY);
314                     } else if (y > maxY) {
315                         this._snapToEdge = true;
316                         this._setY(maxY);
317                     }
318                 }
319
320             } else {
321
322                 if (this._scrollX && (x < minX || x > maxX)) {
323                     this._exceededXBoundary = true;
324                     this._v *= bounce;
325                 }
326
327                 if (this._scrollY && (y < minY || y > maxY)) {
328                     this._exceededYBoundary = true;
329                     this._v *= bounce;
330                 }
331
332                 if (this._scrollX) {
333                     this._setX(x);
334                 }
335
336                 if (this._scrollY) {
337                     this._setY(y);
338                 }
339
340                 this._flickTimer = Y.later(step, this, this._flickAnim);
341             }
342         },
343
344         /**
345          * Internal utility method to set the X offset position
346          *
347          * @param {Number} val
348          * @private
349          */
350         _setX : function(val) {
351             this._move(val, null, this.get(DURATION), this.get(EASING));
352         },
353
354         /**
355          * Internal utility method to set the Y offset position
356          * 
357          * @param {Number} val
358          * @private
359          */
360         _setY : function(val) {
361             this._move(null, val, this.get(DURATION), this.get(EASING));
362         },
363
364         /**
365          * Internal utility method to move the node to a given XY position,
366          * using transitions, if specified.
367          *
368          * @param {Number} x The X offset position
369          * @param {Number} y The Y offset position
370          * @param {Number} duration The duration to use for the transition animation
371          * @param {String} easing The easing to use for the transition animation.
372          *
373          * @private
374          */
375         _move: function(x, y, duration, easing) {
376
377             if (x !== null) {
378                 x = this._bounce(x);
379             } else {
380                 x = this._x; 
381             }
382
383             if (y !== null) {
384                 y = this._bounce(y);
385             } else {
386                 y = this._y;
387             }
388
389             duration = duration || this._snapToEdge ? Flick.SNAP_DURATION : 0;
390             easing = easing || this._snapToEdge ? Flick.SNAP_EASING : Flick.EASING;
391
392             this._x = x;
393             this._y = y;
394
395             this._anim(x, y, duration, easing);
396         },
397
398         /**
399          * Internal utility method to perform the transition step
400          *
401          * @param {Number} x The X offset position
402          * @param {Number} y The Y offset position
403          * @param {Number} duration The duration to use for the transition animation
404          * @param {String} easing The easing to use for the transition animation.
405          *
406          * @private
407          */
408         _anim : function(x, y, duration, easing) {
409             var xn = x * -1,
410                 yn = y * -1,
411
412                 transition = {
413                     duration : duration / 1000,
414                     easing : easing
415                 };
416
417
418             if (Y.Transition.useNative) {
419                 transition.transform = 'translate('+ (xn) + 'px,' + (yn) +'px)'; 
420             } else {
421                 transition.left = xn + 'px';
422                 transition.top = yn + 'px';
423             }
424
425             this._node.transition(transition);
426         },
427
428         /**
429          * Internal utility method to constrain the offset value
430          * based on the bounce criteria. 
431          *
432          * @param {Number} x The offset value to constrain.
433          * @param {Number} max The max offset value.
434          *
435          * @private
436          */
437         _bounce : function(val, max) {
438             var bounce = this.get(BOUNCE),
439                 dist = this.get(BOUNCE_DISTANCE),
440                 min = bounce ? -dist : 0;
441
442             max = bounce ? max + dist : max;
443     
444             if(!bounce) {
445                 if(val < min) {
446                     val = min;
447                 } else if(val > max) {
448                     val = max;
449                 }            
450             }
451             return val;
452         },
453
454         /**
455          * Stop the animation timer
456          *
457          * @method _killTimer
458          * @private
459          */
460         _killTimer: function() {
461             if(this._flickTimer) {
462                 this._flickTimer.cancel();
463             }
464         }
465
466     }, {
467
468         /**
469          * The threshold used to determine when the decelerated velocity of the node
470          * is practically 0.
471          *
472          * @property Flick.VELOCITY_THRESHOLD
473          * @static
474          * @type Number
475          * @default 0.015
476          */
477         VELOCITY_THRESHOLD : 0.015,
478
479         /**
480          * The duration to use for the bounce snap-back transition
481          *
482          * @property Flick.SNAP_DURATION
483          * @static
484          * @type Number
485          * @default 400
486          */
487          SNAP_DURATION : 400,
488         
489         /**
490          * The default easing to use for the main flick movement transition
491          *
492          * @property Flick.EASING
493          * @static
494          * @type String
495          * @default 'cubic-bezier(0, 0.1, 0, 1.0)'
496          */
497         EASING : 'cubic-bezier(0, 0.1, 0, 1.0)',
498
499         /**
500          * The default easing to use for the bounce snap-back transition
501          *
502          * @property Flick.SNAP_EASING
503          * @static
504          * @type String
505          * @default 'ease-out'
506          */
507         SNAP_EASING : 'ease-out',
508
509         /**
510          * The default CSS class names used by the plugin
511          *
512          * @property Flick.CLASS_NAMES
513          * @static
514          * @type Object
515          */
516         CLASS_NAMES : {
517             box: getClassName(Flick.NS),
518             content: getClassName(Flick.NS, "content")
519         }
520     });
521
522     Y.Plugin.Flick = Flick;
523
524
525 }, '3.3.0' ,{requires:['classnamemanager', 'transition', 'event-flick', 'plugin']});