Merge branch 'MDL-32509' of git://github.com/danpoltawski/moodle
[moodle.git] / lib / yui / 3.5.0 / build / anim-base / anim-base-debug.js
blob691307221f6c9ddffe569726e366999a62cedc13
1 /*
2 YUI 3.5.0 (build 5089)
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) {
9 /**
10 * The Animation Utility provides an API for creating advanced transitions.
11 * @module anim
14 /**
15 * Provides the base Anim class, for animating numeric properties.
17 * @module anim
18 * @submodule anim-base
21     /**
22      * A class for constructing animation instances.
23      * @class Anim
24      * @for Anim
25      * @constructor
26      * @extends Base
27      */
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',
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',
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',
61         NUM = Number;
63     var _running = {},
64         _timer;
66     Y.Anim = function() {
67         Y.Anim.superclass.constructor.apply(this, arguments);
68         Y.Anim._instances[Y.stamp(this)] = this;
69     };
71     Y.Anim.NAME = 'anim';
73     Y.Anim._instances = {};
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;
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';
91     Y.Anim.DEFAULT_EASING = function (t, b, c, d) {
92         return c * t / d + b; // linear easing
93     };
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;
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     };
118     Y.Anim.behaviors.top = Y.Anim.behaviors.left;
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     };
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         }
164         return val;
165     };
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                 }
181                 this._node = node;
182                 if (!node) {
183                     Y.log(node + ' is not a valid node', 'warn', 'Anim');
184                 }
185                 return node;
186             }
187         },
189         /**
190          * The length of the animation.  Defaults to "1" (second).
191          * @attribute duration
192          * @type NUM
193          */
194         duration: {
195             value: 1
196         },
198         /**
199          * The method that will provide values to the attribute(s) during the animation. 
200          * Defaults to "Easing.easeNone".
201          * @attribute easing
202          * @type Function
203          */
204         easing: {
205             value: Y.Anim.DEFAULT_EASING,
207             setter: function(val) {
208                 if (typeof val === 'string' && Y.Easing) {
209                     return Y.Easing[val];
210                 }
211             }
212         },
214         /**
215          * The starting values for the animated properties.
216          *
217          * Fields may be strings, numbers, or functions.
218          * If a function is used, the return value becomes the from value.
219          * If no from value is specified, the DEFAULT_GETTER will be used.
220          * Supports any unit, provided it matches the "to" (or default)
221          * unit (e.g. `{width: '10em', color: 'rgb(0, 0 0)', borderColor: '#ccc'}`).
222          *
223          * If using the default ('px' for length-based units), the unit may be omitted
224          * (e.g. `{width: 100}, borderColor: 'ccc'}`, which defaults to pixels
225          * and hex, respectively).
226          *
227          * @attribute from
228          * @type Object
229          */
230         from: {},
232         /**
233          * The ending values for the animated properties.
234          *
235          * Fields may be strings, numbers, or functions.
236          * Supports any unit, provided it matches the "from" (or default)
237          * unit (e.g. `{width: '50%', color: 'red', borderColor: '#ccc'}`).
238          *
239          * If using the default ('px' for length-based units), the unit may be omitted
240          * (e.g. `{width: 100, borderColor: 'ccc'}`, which defaults to pixels
241          * and hex, respectively).
242          *
243          * @attribute to
244          * @type Object
245          */
246         to: {},
248         /**
249          * Date stamp for the first frame of the animation.
250          * @attribute startTime
251          * @type Int
252          * @default 0 
253          * @readOnly
254          */
255         startTime: {
256             value: 0,
257             readOnly: true
258         },
260         /**
261          * Current time the animation has been running.
262          * @attribute elapsedTime
263          * @type Int
264          * @default 0 
265          * @readOnly
266          */
267         elapsedTime: {
268             value: 0,
269             readOnly: true
270         },
272         /**
273          * Whether or not the animation is currently running.
274          * @attribute running 
275          * @type Boolean
276          * @default false 
277          * @readOnly
278          */
279         running: {
280             getter: function() {
281                 return !!_running[Y.stamp(this)];
282             },
283             value: false,
284             readOnly: true
285         },
287         /**
288          * The number of times the animation should run 
289          * @attribute iterations
290          * @type Int
291          * @default 1 
292          */
293         iterations: {
294             value: 1
295         },
297         /**
298          * The number of iterations that have occurred.
299          * Resets when an animation ends (reaches iteration count or stop() called). 
300          * @attribute iterationCount
301          * @type Int
302          * @default 0
303          * @readOnly
304          */
305         iterationCount: {
306             value: 0,
307             readOnly: true
308         },
310         /**
311          * How iterations of the animation should behave. 
312          * Possible values are "normal" and "alternate".
313          * Normal will repeat the animation, alternate will reverse on every other pass.
314          *
315          * @attribute direction
316          * @type String
317          * @default "normal"
318          */
319         direction: {
320             value: 'normal' // | alternate (fwd on odd, rev on even per spec)
321         },
323         /**
324          * Whether or not the animation is currently paused.
325          * @attribute paused 
326          * @type Boolean
327          * @default false 
328          * @readOnly
329          */
330         paused: {
331             readOnly: true,
332             value: false
333         },
335         /**
336          * If true, animation begins from last frame
337          * @attribute reverse
338          * @type Boolean
339          * @default false 
340          */
341         reverse: {
342             value: false
343         }
346     };
348     /**
349      * Runs all animation instances.
350      * @method run
351      * @static
352      */    
353     Y.Anim.run = function() {
354         var instances = Y.Anim._instances;
355         for (var i in instances) {
356             if (instances[i].run) {
357                 instances[i].run();
358             }
359         }
360     };
362     /**
363      * Pauses all animation instances.
364      * @method pause
365      * @static
366      */    
367     Y.Anim.pause = function() {
368         for (var i in _running) { // stop timer if nothing running
369             if (_running[i].pause) {
370                 _running[i].pause();
371             }
372         }
374         Y.Anim._stopTimer();
375     };
377     /**
378      * Stops all animation instances.
379      * @method stop
380      * @static
381      */    
382     Y.Anim.stop = function() {
383         for (var i in _running) { // stop timer if nothing running
384             if (_running[i].stop) {
385                 _running[i].stop();
386             }
387         }
388         Y.Anim._stopTimer();
389     };
390     
391     Y.Anim._startTimer = function() {
392         if (!_timer) {
393             _timer = setInterval(Y.Anim._runFrame, Y.Anim._intervalTime);
394         }
395     };
397     Y.Anim._stopTimer = function() {
398         clearInterval(_timer);
399         _timer = 0;
400     };
402     /**
403      * Called per Interval to handle each animation frame.
404      * @method _runFrame
405      * @private
406      * @static
407      */    
408     Y.Anim._runFrame = function() {
409         var done = true;
410         for (var anim in _running) {
411             if (_running[anim]._runFrame) {
412                 done = false;
413                 _running[anim]._runFrame();
414             }
415         }
417         if (done) {
418             Y.Anim._stopTimer();
419         }
420     };
422     Y.Anim.RE_UNITS = /^(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)*$/;
424     var proto = {
425         /**
426          * Starts or resumes an animation.
427          * @method run
428          * @chainable
429          */    
430         run: function() {
431             if (this.get(PAUSED)) {
432                 this._resume();
433             } else if (!this.get(RUNNING)) {
434                 this._start();
435             }
436             return this;
437         },
439         /**
440          * Pauses the animation and
441          * freezes it in its current state and time.
442          * Calling run() will continue where it left off.
443          * @method pause
444          * @chainable
445          */    
446         pause: function() {
447             if (this.get(RUNNING)) {
448                 this._pause();
449             }
450             return this;
451         },
453         /**
454          * Stops the animation and resets its time.
455          * @method stop
456          * @param {Boolean} finish If true, the animation will move to the last frame
457          * @chainable
458          */    
459         stop: function(finish) {
460             if (this.get(RUNNING) || this.get(PAUSED)) {
461                 this._end(finish);
462             }
463             return this;
464         },
466         _added: false,
468         _start: function() {
469             this._set(START_TIME, new Date() - this.get(ELAPSED_TIME));
470             this._actualFrames = 0;
471             if (!this.get(PAUSED)) {
472                 this._initAnimAttr();
473             }
474             _running[Y.stamp(this)] = this;
475             Y.Anim._startTimer();
477             this.fire(START);
478         },
480         _pause: function() {
481             this._set(START_TIME, null);
482             this._set(PAUSED, true);
483             delete _running[Y.stamp(this)];
485             /**
486             * @event pause
487             * @description fires when an animation is paused.
488             * @param {Event} ev The pause event.
489             * @type Event.Custom
490             */
491             this.fire('pause');
492         },
494         _resume: function() {
495             this._set(PAUSED, false);
496             _running[Y.stamp(this)] = this;
497             this._set(START_TIME, new Date() - this.get(ELAPSED_TIME));
498             Y.Anim._startTimer();
500             /**
501             * @event resume
502             * @description fires when an animation is resumed (run from pause).
503             * @param {Event} ev The pause event.
504             * @type Event.Custom
505             */
506             this.fire('resume');
507         },
509         _end: function(finish) {
510             var duration = this.get('duration') * 1000;
511             if (finish) { // jump to last frame
512                 this._runAttrs(duration, duration, this.get(REVERSE));
513             }
515             this._set(START_TIME, null);
516             this._set(ELAPSED_TIME, 0);
517             this._set(PAUSED, false);
519             delete _running[Y.stamp(this)];
520             this.fire(END, {elapsed: this.get(ELAPSED_TIME)});
521         },
523         _runFrame: function() {
524             var d = this._runtimeAttr.duration,
525                 t = new Date() - this.get(START_TIME),
526                 reverse = this.get(REVERSE),
527                 done = (t >= d),
528                 attribute,
529                 setter;
530                 
531             this._runAttrs(t, d, reverse);
532             this._actualFrames += 1;
533             this._set(ELAPSED_TIME, t);
535             this.fire(TWEEN);
536             if (done) {
537                 this._lastFrame();
538             }
539         },
541         _runAttrs: function(t, d, reverse) {
542             var attr = this._runtimeAttr,
543                 customAttr = Y.Anim.behaviors,
544                 easing = attr.easing,
545                 lastFrame = d,
546                 done = false,
547                 attribute,
548                 setter,
549                 i;
551             if (t >= d) {
552                 done = true;
553             }
555             if (reverse) {
556                 t = d - t;
557                 lastFrame = 0;
558             }
560             for (i in attr) {
561                 if (attr[i].to) {
562                     attribute = attr[i];
563                     setter = (i in customAttr && 'set' in customAttr[i]) ?
564                             customAttr[i].set : Y.Anim.DEFAULT_SETTER;
566                     if (!done) {
567                         setter(this, i, attribute.from, attribute.to, t, d, easing, attribute.unit); 
568                     } else {
569                         setter(this, i, attribute.from, attribute.to, lastFrame, d, easing, attribute.unit); 
570                     }
571                 }
572             }
575         },
577         _lastFrame: function() {
578             var iter = this.get('iterations'),
579                 iterCount = this.get(ITERATION_COUNT);
581             iterCount += 1;
582             if (iter === 'infinite' || iterCount < iter) {
583                 if (this.get('direction') === 'alternate') {
584                     this.set(REVERSE, !this.get(REVERSE)); // flip it
585                 }
586                 /**
587                 * @event iteration
588                 * @description fires when an animation begins an iteration.
589                 * @param {Event} ev The iteration event.
590                 * @type Event.Custom
591                 */
592                 this.fire('iteration');
593             } else {
594                 iterCount = 0;
595                 this._end();
596             }
598             this._set(START_TIME, new Date());
599             this._set(ITERATION_COUNT, iterCount);
600         },
602         _initAnimAttr: function() {
603             var from = this.get('from') || {},
604                 to = this.get('to') || {},
605                 attr = {
606                     duration: this.get('duration') * 1000,
607                     easing: this.get('easing')
608                 },
609                 customAttr = Y.Anim.behaviors,
610                 node = this.get(NODE), // implicit attr init
611                 unit, begin, end;
613             Y.each(to, function(val, name) {
614                 if (typeof val === 'function') {
615                     val = val.call(this, node);
616                 }
618                 begin = from[name];
619                 if (begin === undefined) {
620                     begin = (name in customAttr && 'get' in customAttr[name])  ?
621                             customAttr[name].get(this, name) : Y.Anim.DEFAULT_GETTER(this, name);
622                 } else if (typeof begin === 'function') {
623                     begin = begin.call(this, node);
624                 }
626                 var mFrom = Y.Anim.RE_UNITS.exec(begin);
627                 var mTo = Y.Anim.RE_UNITS.exec(val);
629                 begin = mFrom ? mFrom[1] : begin;
630                 end = mTo ? mTo[1] : val;
631                 unit = mTo ? mTo[2] : mFrom ?  mFrom[2] : ''; // one might be zero TODO: mixed units
633                 if (!unit && Y.Anim.RE_DEFAULT_UNIT.test(name)) {
634                     unit = Y.Anim.DEFAULT_UNIT;
635                 }
637                 if (!begin || !end) {
638                     Y.error('invalid "from" or "to" for "' + name + '"', 'Anim');
639                     return;
640                 }
642                 attr[name] = {
643                     from: begin,
644                     to: end,
645                     unit: unit
646                 };
648             }, this);
650             this._runtimeAttr = attr;
651         },
654         // TODO: move to computedStyle? (browsers dont agree on default computed offsets)
655         _getOffset: function(attr) {
656             var node = this._node,
657                 val = node.getComputedStyle(attr),
658                 get = (attr === 'left') ? 'getX': 'getY',
659                 set = (attr === 'left') ? 'setX': 'setY';
661             if (val === 'auto') {
662                 var position = node.getStyle('position');
663                 if (position === 'absolute' || position === 'fixed') {
664                     val = node[get]();
665                     node[set](val);
666                 } else {
667                     val = 0;
668                 }
669             }
671             return val;
672         },
674         destructor: function() {
675             delete Y.Anim._instances[Y.stamp(this)];
676         }
677     };
679     Y.extend(Y.Anim, Y.Base, proto);
682 }, '3.5.0' ,{requires:['base-base', 'node-style']});