Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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