~bac/juju-gui/trunkcopy

« back to all changes in this revision

Viewing changes to lib/yui/build/anim-base/anim-base.js

  • Committer: kapil.foss at gmail
  • Date: 2012-07-13 18:45:59 UTC
  • Revision ID: kapil.foss@gmail.com-20120713184559-2xl7be17egsrz0c9
reshape

Show diffs side-by-side

added added

removed removed

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