Fixes #2715 - Adding Microsoft Edge UA string support to Browser.
[mootools.git] / Source / Fx / Fx.js
blob0db234ecf081ee1b227fb0a4b238c293a916330c
1 /*
2 ---
4 name: Fx
6 description: Contains the basic animation logic to be extended by all other Fx Classes.
8 license: MIT-style license.
10 requires: [Chain, Events, Options]
12 provides: Fx
14 ...
17 (function(){
19 var Fx = this.Fx = new Class({
21         Implements: [Chain, Events, Options],
23         options: {
24                 /*
25                 onStart: nil,
26                 onCancel: nil,
27                 onComplete: nil,
28                 */
29                 fps: 60,
30                 unit: false,
31                 duration: 500,
32                 frames: null,
33                 frameSkip: true,
34                 link: 'ignore'
35         },
37         initialize: function(options){
38                 this.subject = this.subject || this;
39                 this.setOptions(options);
40         },
42         getTransition: function(){
43                 return function(p){
44                         return -(Math.cos(Math.PI * p) - 1) / 2;
45                 };
46         },
48         step: function(now){
49                 if (this.options.frameSkip){
50                         var diff = (this.time != null) ? (now - this.time) : 0, frames = diff / this.frameInterval;
51                         this.time = now;
52                         this.frame += frames;
53                 } else {
54                         this.frame++;
55                 }
57                 if (this.frame < this.frames){
58                         var delta = this.transition(this.frame / this.frames);
59                         this.set(this.compute(this.from, this.to, delta));
60                 } else {
61                         this.frame = this.frames;
62                         this.set(this.compute(this.from, this.to, 1));
63                         this.stop();
64                 }
65         },
67         set: function(now){
68                 return now;
69         },
71         compute: function(from, to, delta){
72                 return Fx.compute(from, to, delta);
73         },
75         check: function(){
76                 if (!this.isRunning()) return true;
77                 switch (this.options.link){
78                         case 'cancel': this.cancel(); return true;
79                         case 'chain': this.chain(this.caller.pass(arguments, this)); return false;
80                 }
81                 return false;
82         },
84         start: function(from, to){
85                 if (!this.check(from, to)) return this;
86                 this.from = from;
87                 this.to = to;
88                 this.frame = (this.options.frameSkip) ? 0 : -1;
89                 this.time = null;
90                 this.transition = this.getTransition();
91                 var frames = this.options.frames, fps = this.options.fps, duration = this.options.duration;
92                 this.duration = Fx.Durations[duration] || duration.toInt();
93                 this.frameInterval = 1000 / fps;
94                 this.frames = frames || Math.round(this.duration / this.frameInterval);
95                 this.fireEvent('start', this.subject);
96                 pushInstance.call(this, fps);
97                 return this;
98         },
100         stop: function(){
101                 if (this.isRunning()){
102                         this.time = null;
103                         pullInstance.call(this, this.options.fps);
104                         if (this.frames == this.frame){
105                                 this.fireEvent('complete', this.subject);
106                                 if (!this.callChain()) this.fireEvent('chainComplete', this.subject);
107                         } else {
108                                 this.fireEvent('stop', this.subject);
109                         }
110                 }
111                 return this;
112         },
114         cancel: function(){
115                 if (this.isRunning()){
116                         this.time = null;
117                         pullInstance.call(this, this.options.fps);
118                         this.frame = this.frames;
119                         this.fireEvent('cancel', this.subject).clearChain();
120                 }
121                 return this;
122         },
124         pause: function(){
125                 if (this.isRunning()){
126                         this.time = null;
127                         pullInstance.call(this, this.options.fps);
128                 }
129                 return this;
130         },
132         resume: function(){
133                 if (this.isPaused()) pushInstance.call(this, this.options.fps);
134                 return this;
135         },
137         isRunning: function(){
138                 var list = instances[this.options.fps];
139                 return list && list.contains(this);
140         },
142         isPaused: function(){
143                 return (this.frame < this.frames) && !this.isRunning();
144         }
148 Fx.compute = function(from, to, delta){
149         return (to - from) * delta + from;
152 Fx.Durations = {'short': 250, 'normal': 500, 'long': 1000};
154 // global timers
156 var instances = {}, timers = {};
158 var loop = function(){
159         var now = Date.now();
160         for (var i = this.length; i--;){
161                 var instance = this[i];
162                 if (instance) instance.step(now);
163         }
166 var pushInstance = function(fps){
167         var list = instances[fps] || (instances[fps] = []);
168         list.push(this);
169         if (!timers[fps]) timers[fps] = loop.periodical(Math.round(1000 / fps), list);
172 var pullInstance = function(fps){
173         var list = instances[fps];
174         if (list){
175                 list.erase(this);
176                 if (!list.length && timers[fps]){
177                         delete instances[fps];
178                         timers[fps] = clearInterval(timers[fps]);
179                 }
180         }
183 })();