]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - jssource/src_files/include/javascript/yui3/build/anim/anim-base.js
Release 6.2.0beta4
[Github/sugarcrm.git] / jssource / src_files / include / javascript / yui3 / build / anim / anim-base.js
1 /*
2 Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
5 version: 3.0.0
6 build: 1549
7 */
8 YUI.add('anim-base', function(Y) {
9
10 /**
11 * The Animation Utility provides an API for creating advanced transitions.
12 * @module anim
13 */
14
15 /**
16 * Provides the base Anim class, for animating numeric properties.
17 *
18 * @module anim
19 * @submodule anim-base
20 */
21
22     /**
23      * A class for constructing animation instances.
24      * @class Anim
25      * @for Anim
26      * @constructor
27      * @extends Base
28      */
29
30     var RUNNING = 'running',
31         START_TIME = 'startTime',
32         ELAPSED_TIME = 'elapsedTime',
33         /**
34         * @for Anim
35         * @event start
36         * @description fires when an animation begins.
37         * @param {Event} ev The start event.
38         * @type Event.Custom
39         */
40         START = 'start',
41
42         /**
43         * @event tween
44         * @description fires every frame of the animation.
45         * @param {Event} ev The tween event.
46         * @type Event.Custom
47         */
48         TWEEN = 'tween',
49
50         /**
51         * @event end
52         * @description fires after the animation completes.
53         * @param {Event} ev The end event.
54         * @type Event.Custom
55         */
56         END = 'end',
57         NODE = 'node',
58         PAUSED = 'paused',
59         REVERSE = 'reverse', // TODO: cleanup
60         ITERATION_COUNT = 'iterationCount',
61
62         NUM = Number;
63
64     var _running = {},
65         _instances = {},
66         _timer;
67
68     Y.Anim = function() {
69         Y.Anim.superclass.constructor.apply(this, arguments);
70         _instances[Y.stamp(this)] = this;
71     };
72
73     Y.Anim.NAME = 'anim';
74
75     /**
76      * Regex of properties that should use the default unit.
77      *
78      * @property RE_DEFAULT_UNIT
79      * @static
80      */
81     Y.Anim.RE_DEFAULT_UNIT = /^width|height|top|right|bottom|left|margin.*|padding.*|border.*$/i;
82
83     /**
84      * The default unit to use with properties that pass the RE_DEFAULT_UNIT test.
85      *
86      * @property DEFAULT_UNIT
87      * @static
88      */
89     Y.Anim.DEFAULT_UNIT = 'px';
90
91     Y.Anim.DEFAULT_EASING = function (t, b, c, d) {
92         return c * t / d + b; // linear easing
93     };
94
95     /**
96      * Bucket for custom getters and setters
97      *
98      * @property behaviors
99      * @static
100      */
101     Y.Anim.behaviors = {
102         left: {
103             get: function(anim, attr) {
104                 return anim._getOffset(attr);
105             }
106         }
107     };
108
109     Y.Anim.behaviors.top = Y.Anim.behaviors.left;
110
111     /**
112      * The default setter to use when setting object properties.
113      *
114      * @property DEFAULT_SETTER
115      * @static
116      */
117     Y.Anim.DEFAULT_SETTER = function(anim, att, from, to, elapsed, duration, fn, unit) {
118         unit = unit || '';
119         anim._node.setStyle(att, fn(elapsed, NUM(from), NUM(to) - NUM(from), duration) + unit);
120     };
121
122     /**
123      * The default getter to use when getting object properties.
124      *
125      * @property DEFAULT_GETTER
126      * @static
127      */
128     Y.Anim.DEFAULT_GETTER = function(anim, prop) {
129         return anim._node.getComputedStyle(prop);
130     };
131
132     Y.Anim.ATTRS = {
133         /**
134          * The object to be animated.
135          * @attribute node
136          * @type Node
137          */
138         node: {
139             setter: function(node) {
140                 node = Y.get(node);
141                 this._node = node;
142                 if (!node) {
143                 }
144                 return node;
145             }
146         },
147
148         /**
149          * The length of the animation.  Defaults to "1" (second).
150          * @attribute duration
151          * @type NUM
152          */
153         duration: {
154             value: 1
155         },
156
157         /**
158          * The method that will provide values to the attribute(s) during the animation. 
159          * Defaults to "Easing.easeNone".
160          * @attribute easing
161          * @type Function
162          */
163         easing: {
164             value: Y.Anim.DEFAULT_EASING,
165
166             setter: function(val) {
167                 if (typeof val === 'string' && Y.Easing) {
168                     return Y.Easing[val];
169                 }
170             }
171         },
172
173         /**
174          * The starting values for the animated properties. 
175          * Fields may be strings, numbers, or functions.
176          * If a function is used, the return value becomes the from value.
177          * If no from value is specified, the DEFAULT_GETTER will be used. 
178          * @attribute from
179          * @type Object
180          */
181         from: {},
182
183         /**
184          * The ending values for the animated properties. 
185          * Fields may be strings, numbers, or functions.
186          * @attribute to
187          * @type Object
188          */
189         to: {},
190
191         /**
192          * Date stamp for the first frame of the animation.
193          * @attribute startTime
194          * @type Int
195          * @default 0 
196          * @readOnly
197          */
198         startTime: {
199             value: 0,
200             readOnly: true
201         },
202
203         /**
204          * Current time the animation has been running.
205          * @attribute elapsedTime
206          * @type Int
207          * @default 0 
208          * @readOnly
209          */
210         elapsedTime: {
211             value: 0,
212             readOnly: true
213         },
214
215         /**
216          * Whether or not the animation is currently running.
217          * @attribute running 
218          * @type Boolean
219          * @default false 
220          * @readOnly
221          */
222         running: {
223             getter: function() {
224                 return !!_running[Y.stamp(this)];
225             },
226             value: false,
227             readOnly: true
228         },
229
230         /**
231          * The number of times the animation should run 
232          * @attribute iterations
233          * @type Int
234          * @default 1 
235          */
236         iterations: {
237             value: 1
238         },
239
240         /**
241          * The number of iterations that have occurred.
242          * Resets when an animation ends (reaches iteration count or stop() called). 
243          * @attribute iterationCount
244          * @type Int
245          * @default 0
246          * @readOnly
247          */
248         iterationCount: {
249             value: 0,
250             readOnly: true
251         },
252
253         /**
254          * How iterations of the animation should behave. 
255          * Possible values are "normal" and "alternate".
256          * Normal will repeat the animation, alternate will reverse on every other pass.
257          *
258          * @attribute direction
259          * @type String
260          * @default "normal"
261          */
262         direction: {
263             value: 'normal' // | alternate (fwd on odd, rev on even per spec)
264         },
265
266         /**
267          * Whether or not the animation is currently paused.
268          * @attribute paused 
269          * @type Boolean
270          * @default false 
271          * @readOnly
272          */
273         paused: {
274             readOnly: true,
275             value: false
276         },
277
278         /**
279          * If true, animation begins from last frame
280          * @attribute reverse
281          * @type Boolean
282          * @default false 
283          */
284         reverse: {
285             value: false
286         }
287
288
289     };
290
291     /**
292      * Runs all animation instances.
293      * @method run
294      * @static
295      */    
296     Y.Anim.run = function() {
297         for (var i in _instances) {
298             if (_instances[i].run) {
299                 _instances[i].run();
300             }
301         }
302     };
303
304     /**
305      * Pauses all animation instances.
306      * @method pause
307      * @static
308      */    
309     Y.Anim.pause = function() {
310         for (var i in _running) { // stop timer if nothing running
311             if (_running[i].pause) {
312                 _running[i].pause();
313             }
314         }
315         Y.Anim._stopTimer();
316     };
317
318     /**
319      * Stops all animation instances.
320      * @method stop
321      * @static
322      */    
323     Y.Anim.stop = function() {
324         for (var i in _running) { // stop timer if nothing running
325             if (_running[i].stop) {
326                 _running[i].stop();
327             }
328         }
329         Y.Anim._stopTimer();
330     };
331     
332     Y.Anim._startTimer = function() {
333         if (!_timer) {
334             _timer = setInterval(Y.Anim._runFrame, 1);
335         }
336     };
337
338     Y.Anim._stopTimer = function() {
339         clearInterval(_timer);
340         _timer = 0;
341     };
342
343     /**
344      * Called per Interval to handle each animation frame.
345      * @method _runFrame
346      * @private
347      * @static
348      */    
349     Y.Anim._runFrame = function() {
350         var done = true;
351         for (var anim in _running) {
352             if (_running[anim]._runFrame) {
353                 done = false;
354                 _running[anim]._runFrame();
355             }
356         }
357
358         if (done) {
359             Y.Anim._stopTimer();
360         }
361     };
362
363     Y.Anim.RE_UNITS = /^(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)*$/;
364
365     var proto = {
366         /**
367          * Starts or resumes an animation.
368          * percent start time marker.
369          * @method run
370          * @chainable
371          */    
372         run: function() {
373             if (!this.get(RUNNING)) {
374                 this._start();
375             } else if (this.get(PAUSED)) {
376                 this._resume();
377             }
378             return this;
379         },
380
381         /**
382          * Pauses the animation and
383          * freezes it in its current state and time.
384          * Calling run() will continue where it left off.
385          * @method pause
386          * @chainable
387          */    
388         pause: function() {
389             if (this.get(RUNNING)) {
390                 this._pause();
391             }
392             return this;
393         },
394
395         /**
396          * Stops the animation and resets its time.
397          * @method stop
398          * @chainable
399          */    
400         stop: function(finish) {
401             if (this.get(RUNNING) || this.get(PAUSED)) {
402                 this._end(finish);
403             }
404             return this;
405         },
406
407         _added: false,
408
409         _start: function() {
410             this._set(START_TIME, new Date() - this.get(ELAPSED_TIME));
411             this._actualFrames = 0;
412             if (!this.get(PAUSED)) {
413                 this._initAnimAttr();
414             }
415             _running[Y.stamp(this)] = this;
416             Y.Anim._startTimer();
417
418             this.fire(START);
419         },
420
421         _pause: function() {
422             this._set(START_TIME, null);
423             this._set(PAUSED, true);
424             delete _running[Y.stamp(this)];
425
426             /**
427             * @event pause
428             * @description fires when an animation is paused.
429             * @param {Event} ev The pause event.
430             * @type Event.Custom
431             */
432             this.fire('pause');
433         },
434
435         _resume: function() {
436             this._set(PAUSED, false);
437             _running[Y.stamp(this)] = this;
438
439             /**
440             * @event resume
441             * @description fires when an animation is resumed (run from pause).
442             * @param {Event} ev The pause event.
443             * @type Event.Custom
444             */
445             this.fire('resume');
446         },
447
448         _end: function(finish) {
449             this._set(START_TIME, null);
450             this._set(ELAPSED_TIME, 0);
451             this._set(PAUSED, false);
452
453             delete _running[Y.stamp(this)];
454             this.fire(END, {elapsed: this.get(ELAPSED_TIME)});
455         },
456
457         _runFrame: function() {
458             var attr = this._runtimeAttr,
459                 customAttr = Y.Anim.behaviors,
460                 easing = attr.easing,
461                 d = attr.duration,
462                 t = new Date() - this.get(START_TIME),
463                 reversed = this.get(REVERSE),
464                 done = (t >= d),
465                 lastFrame = d,
466                 attribute,
467                 setter;
468                 
469             if (reversed) {
470                 t = d - t;
471                 done = (t <= 0);
472                 lastFrame = 0;
473             }
474
475             for (var i in attr) {
476                 if (attr[i].to) {
477                     attribute = attr[i];
478                     setter = (i in customAttr && 'set' in customAttr[i]) ?
479                             customAttr[i].set : Y.Anim.DEFAULT_SETTER;
480
481                     if (!done) {
482                         setter(this, i, attribute.from, attribute.to, t, d, easing, attribute.unit); 
483                     } else { // ensure final frame value is set
484                        // TODO: handle keyframes 
485                         setter(this, i, attribute.from, attribute.to, lastFrame, d, easing, attribute.unit); 
486                     }
487                 }
488             }
489
490             this._actualFrames += 1;
491             this._set(ELAPSED_TIME, t);
492
493             this.fire(TWEEN);
494             if (done) {
495                 this._lastFrame();
496             }
497         },
498
499         _lastFrame: function() {
500             var iter = this.get('iterations'),
501                 iterCount = this.get(ITERATION_COUNT);
502
503             iterCount += 1;
504             if (iter === 'infinite' || iterCount < iter) {
505                 if (this.get('direction') === 'alternate') {
506                     this.set(REVERSE, !this.get(REVERSE)); // flip it
507                 }
508                 /**
509                 * @event iteration
510                 * @description fires when an animation begins an iteration.
511                 * @param {Event} ev The iteration event.
512                 * @type Event.Custom
513                 */
514                 this.fire('iteration');
515             } else {
516                 iterCount = 0;
517                 this._end();
518             }
519
520             this._set(START_TIME, new Date());
521             this._set(ITERATION_COUNT, iterCount);
522         },
523
524         _initAnimAttr: function() {
525             var from = this.get('from') || {},
526                 to = this.get('to') || {},
527                 dur = this.get('duration') * 1000,
528                 node = this.get(NODE),
529                 easing = this.get('easing') || {},
530                 attr = {},
531                 customAttr = Y.Anim.behaviors,
532                 unit, begin, end;
533
534             Y.each(to, function(val, name) {
535                 if (typeof val === 'function') {
536                     val = val.call(this, node);
537                 }
538
539                 begin = from[name];
540                 if (begin === undefined) {
541                     begin = (name in customAttr && 'get' in customAttr[name])  ?
542                             customAttr[name].get(this, name) : Y.Anim.DEFAULT_GETTER(this, name);
543                 } else if (typeof begin === 'function') {
544                     begin = begin.call(this, node);
545                 }
546
547                 var mFrom = Y.Anim.RE_UNITS.exec(begin);
548                 var mTo = Y.Anim.RE_UNITS.exec(val);
549
550                 begin = mFrom ? mFrom[1] : begin;
551                 end = mTo ? mTo[1] : val;
552                 unit = mTo ? mTo[2] : mFrom ?  mFrom[2] : ''; // one might be zero TODO: mixed units
553
554                 if (!unit && Y.Anim.RE_DEFAULT_UNIT.test(name)) {
555                     unit = Y.Anim.DEFAULT_UNIT;
556                 }
557
558                 if (!begin || !end) {
559                     Y.error('invalid "from" or "to" for "' + name + '"', 'Anim');
560                     return;
561                 }
562
563                 attr[name] = {
564                     from: begin,
565                     to: end,
566                     unit: unit
567                 };
568
569                 attr.duration = dur;
570                 attr.easing = easing;
571
572             }, this);
573
574             this._runtimeAttr = attr;
575         },
576
577
578         // TODO: move to computedStyle? (browsers dont agree on default computed offsets)
579         _getOffset: function(attr) {
580             var node = this._node,
581                 val = node.getComputedStyle(attr),
582                 get = (attr === 'left') ? 'getX': 'getY',
583                 set = (attr === 'left') ? 'setX': 'setY';
584
585             if (val === 'auto') {
586                 var position = node.getStyle('position');
587                 if (position === 'absolute' || position === 'fixed') {
588                     val = node[get]();
589                     node[set](val);
590                 } else {
591                     val = 0;
592                 }
593             }
594
595             return val;
596         }
597     };
598
599     Y.extend(Y.Anim, Y.Base, proto);
600
601
602 }, '3.0.0' ,{requires:['base-base', 'node-style']});