]> git.pond.sub.org Git - eow/blob - static/dojo-release-1.1.1/dojo/_base/fx.js
add Dojo 1.1.1
[eow] / static / dojo-release-1.1.1 / dojo / _base / fx.js
1 if(!dojo._hasResource["dojo._base.fx"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2 dojo._hasResource["dojo._base.fx"] = true;
3 dojo.provide("dojo._base.fx");
4 dojo.require("dojo._base.Color");
5 dojo.require("dojo._base.connect");
6 dojo.require("dojo._base.declare");
7 dojo.require("dojo._base.lang");
8 dojo.require("dojo._base.html");
9
10 /*
11         Animation losely package based on Dan Pupius' work, contributed under CLA: 
12                 http://pupius.co.uk/js/Toolkit.Drawing.js
13 */
14 (function(){ 
15
16         var d = dojo;
17         
18         dojo._Line = function(/*int*/ start, /*int*/ end){
19                 //      summary:
20                 //              dojo._Line is the object used to generate values from a start value
21                 //              to an end value
22                 //      start: int
23                 //              Beginning value for range
24                 //      end: int
25                 //              Ending value for range
26                 this.start = start;
27                 this.end = end;
28                 this.getValue = function(/*float*/ n){
29                         //      summary: returns the point on the line
30                         //      n: a floating point number greater than 0 and less than 1
31                         return ((this.end - this.start) * n) + this.start; // Decimal
32                 }
33         }
34         
35         d.declare("dojo._Animation", null, {
36                 //      summary
37                 //              A generic animation class that fires callbacks into its handlers
38                 //              object at various states. Nearly all dojo animation functions
39                 //              return an instance of this method, usually without calling the
40                 //              .play() method beforehand. Therefore, you will likely need to
41                 //              call .play() on instances of dojo._Animation when one is
42                 //              returned.
43                 constructor: function(/*Object*/ args){
44                         d.mixin(this, args);
45                         if(d.isArray(this.curve)){
46                                 /* curve: Array
47                                         pId: a */
48                                 this.curve = new d._Line(this.curve[0], this.curve[1]);
49                         }
50                 },
51                 
52                 // duration: Integer
53                 //      The time in milliseonds the animation will take to run
54                 duration: 350,
55         
56         /*=====
57                 // curve: dojo._Line||Array
58                 //      A two element array of start and end values, or a dojo._Line instance to be
59                 //      used in the Animation. 
60                 curve: null,
61         
62                 // easing: Function
63                 //      A Function to adjust the acceleration (or deceleration) of the progress 
64                 //      across a dojo._Line
65                 easing: null,
66         =====*/
67         
68                 // repeat: Integer
69                 //      The number of times to loop the animation
70                 repeat: 0,
71         
72                 // rate: Integer
73                 //      the time in milliseconds to wait before advancing to next frame 
74                 //      (used as a fps timer: rate/1000 = fps)
75                 rate: 10 /* 100 fps */,
76         
77         /*===== 
78                 // delay: Integer
79                 //      The time in milliseconds to wait before starting animation after it has been .play()'ed
80                 delay: null,
81         
82                 // events
83                 //
84                 // beforeBegin: Event
85                 //      Synthetic event fired before a dojo._Animation begins playing (synchronous)
86                 beforeBegin: null,
87         
88                 // onBegin: Event
89                 //      Synthetic event fired as a dojo._Animation begins playing (useful?)
90                 onBegin: null,
91         
92                 // onAnimate: Event
93                 //      Synthetic event fired at each interval of a dojo._Animation
94                 onAnimate: null,
95         
96                 // onEnd: Event
97                 //      Synthetic event fired after the final frame of a dojo._Animation
98                 onEnd: null,
99         
100                 // onPlay: Event
101                 //      Synthetic event fired any time a dojo._Animation is play()'ed
102                 onPlay: null,
103         
104                 // onPause: Event
105                 //      Synthetic event fired when a dojo._Animation is paused
106                 onPause: null,
107         
108                 // onStop: Event
109                 //      Synthetic event fires when a dojo._Animation is stopped
110                 onStop: null,
111         
112         =====*/
113         
114                 _percent: 0,
115                 _startRepeatCount: 0,
116         
117                 _fire: function(/*Event*/ evt, /*Array?*/ args){
118                         //      summary:
119                         //              Convenience function.  Fire event "evt" and pass it the
120                         //              arguments specified in "args".
121                         //      evt:
122                         //              The event to fire.
123                         //      args:
124                         //              The arguments to pass to the event.
125                         try{
126                                 if(this[evt]){
127                                         this[evt].apply(this, args||[]);
128                                 }
129                         }catch(e){
130                                 // squelch and log because we shouldn't allow exceptions in
131                                 // synthetic event handlers to cause the internal timer to run
132                                 // amuck, potentially pegging the CPU. I'm not a fan of this
133                                 // squelch, but hopefully logging will make it clear what's
134                                 // going on
135                                 console.error("exception in animation handler for:", evt);
136                                 console.error(e);
137                         }
138                         return this; // dojo._Animation
139                 },
140         
141                 play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
142                         // summary:
143                         //              Start the animation.
144                         // delay:
145                         //              How many milliseconds to delay before starting.
146                         // gotoStart:
147                         //              If true, starts the animation from the beginning; otherwise,
148                         //              starts it from its current position.
149                         var _t = this;
150                         if(gotoStart){
151                                 _t._stopTimer();
152                                 _t._active = _t._paused = false;
153                                 _t._percent = 0;
154                         }else if(_t._active && !_t._paused){
155                                 return _t; // dojo._Animation
156                         }
157         
158                         _t._fire("beforeBegin");
159         
160                         var de = delay||_t.delay;
161                         var _p = dojo.hitch(_t, "_play", gotoStart);
162                         if(de > 0){
163                                 setTimeout(_p, de);
164                                 return _t; // dojo._Animation
165                         }
166                         _p();
167                         return _t;
168                 },
169         
170                 _play: function(gotoStart){
171                         var _t = this;
172                         _t._startTime = new Date().valueOf();
173                         if(_t._paused){
174                                 _t._startTime -= _t.duration * _t._percent;
175                         }
176                         _t._endTime = _t._startTime + _t.duration;
177         
178                         _t._active = true;
179                         _t._paused = false;
180         
181                         var value = _t.curve.getValue(_t._percent);
182                         if(!_t._percent){
183                                 if(!_t._startRepeatCount){
184                                         _t._startRepeatCount = _t.repeat;
185                                 }
186                                 _t._fire("onBegin", [value]);
187                         }
188         
189                         _t._fire("onPlay", [value]);
190         
191                         _t._cycle();
192                         return _t; // dojo._Animation
193                 },
194         
195                 pause: function(){
196                         // summary: Pauses a running animation.
197                         this._stopTimer();
198                         if(!this._active){ return this; /*dojo._Animation*/ }
199                         this._paused = true;
200                         this._fire("onPause", [this.curve.getValue(this._percent)]);
201                         return this; // dojo._Animation
202                 },
203         
204                 gotoPercent: function(/*Decimal*/ percent, /*Boolean?*/ andPlay){
205                         //      summary:
206                         //              Sets the progress of the animation.
207                         //      percent:
208                         //              A percentage in decimal notation (between and including 0.0 and 1.0).
209                         //      andPlay:
210                         //              If true, play the animation after setting the progress.
211                         this._stopTimer();
212                         this._active = this._paused = true;
213                         this._percent = percent;
214                         if(andPlay){ this.play(); }
215                         return this; // dojo._Animation
216                 },
217         
218                 stop: function(/*boolean?*/ gotoEnd){
219                         // summary: Stops a running animation.
220                         // gotoEnd: If true, the animation will end.
221                         if(!this._timer){ return this; /* dojo._Animation */ }
222                         this._stopTimer();
223                         if(gotoEnd){
224                                 this._percent = 1;
225                         }
226                         this._fire("onStop", [this.curve.getValue(this._percent)]);
227                         this._active = this._paused = false;
228                         return this; // dojo._Animation
229                 },
230         
231                 status: function(){
232                         // summary: Returns a string token representation of the status of
233                         //                      the animation, one of: "paused", "playing", "stopped"
234                         if(this._active){
235                                 return this._paused ? "paused" : "playing"; // String
236                         }
237                         return "stopped"; // String
238                 },
239         
240                 _cycle: function(){
241                         var _t = this;
242                         if(_t._active){
243                                 var curr = new Date().valueOf();
244                                 var step = (curr - _t._startTime) / (_t._endTime - _t._startTime);
245         
246                                 if(step >= 1){
247                                         step = 1;
248                                 }
249                                 _t._percent = step;
250         
251                                 // Perform easing
252                                 if(_t.easing){
253                                         step = _t.easing(step);
254                                 }
255         
256                                 _t._fire("onAnimate", [_t.curve.getValue(step)]);
257         
258                                 if(_t._percent < 1){
259                                         _t._startTimer();
260                                 }else{
261                                         _t._active = false;
262         
263                                         if(_t.repeat > 0){
264                                                 _t.repeat--;
265                                                 _t.play(null, true);
266                                         }else if(_t.repeat == -1){
267                                                 _t.play(null, true);
268                                         }else{
269                                                 if(_t._startRepeatCount){
270                                                         _t.repeat = _t._startRepeatCount;
271                                                         _t._startRepeatCount = 0;
272                                                 }
273                                         }
274                                         _t._percent = 0;
275                                         _t._fire("onEnd");
276                                         _t._stopTimer();
277                                 }
278                         }
279                         return _t; // dojo._Animation
280                 }
281         });
282
283         var ctr = 0;
284         var _globalTimerList = [];
285         var runner = {
286                 run: function(){ }
287         };
288         var timer = null;
289         dojo._Animation.prototype._startTimer = function(){
290                 // this._timer = setTimeout(dojo.hitch(this, "_cycle"), this.rate);
291                 if(!this._timer){
292                         this._timer = d.connect(runner, "run", this, "_cycle");
293                         ctr++;
294                 }
295                 if(!timer){
296                         timer = setInterval(d.hitch(runner, "run"), this.rate);
297                 }
298         };
299
300         dojo._Animation.prototype._stopTimer = function(){
301                 if(this._timer){
302                         d.disconnect(this._timer);
303                         this._timer = null;
304                         ctr--;
305                 }
306                 if(ctr <= 0){
307                         clearInterval(timer);
308                         timer = null;
309                         ctr = 0;
310                 }
311         };
312
313         var _makeFadeable = (d.isIE) ? function(node){
314                 // only set the zoom if the "tickle" value would be the same as the
315                 // default
316                 var ns = node.style;
317                 if(!ns.zoom.length && d.style(node, "zoom") == "normal"){
318                         // make sure the node "hasLayout"
319                         // NOTE: this has been tested with larger and smaller user-set text
320                         // sizes and works fine
321                         ns.zoom = "1";
322                         // node.style.zoom = "normal";
323                 }
324                 // don't set the width to auto if it didn't already cascade that way.
325                 // We don't want to f anyones designs
326                 if(!ns.width.length && d.style(node, "width") == "auto"){
327                         ns.width = "auto";
328                 }
329         } : function(){};
330
331         dojo._fade = function(/*Object*/ args){
332                 //      summary: 
333                 //              Returns an animation that will fade the node defined by
334                 //              args.node from the start to end values passed (args.start
335                 //              args.end) (end is mandatory, start is optional)
336
337                 args.node = d.byId(args.node);
338                 var fArgs = d.mixin({ properties: {} }, args);
339                 var props = (fArgs.properties.opacity = {});
340                 props.start = !("start" in fArgs) ?
341                         function(){ 
342                                 return Number(d.style(fArgs.node, "opacity")); 
343                         } : fArgs.start;
344                 props.end = fArgs.end;
345
346                 var anim = d.animateProperty(fArgs);
347                 d.connect(anim, "beforeBegin", d.partial(_makeFadeable, fArgs.node));
348
349                 return anim; // dojo._Animation
350         }
351
352         /*=====
353         dojo.__FadeArgs = function(node, duration, easing){
354                 //      node: DOMNode|String
355                 //              The node referenced in the animation
356                 //      duration: Integer?
357                 //              Duration of the animation in milliseconds.
358                 //      easing: Function?
359                 //              An easing function.
360                 this.node = node;
361                 this.duration = duration;
362                 this.easing = easing;
363         }
364         =====*/
365
366         dojo.fadeIn = function(/*dojo.__FadeArgs*/ args){
367                 // summary: 
368                 //              Returns an animation that will fade node defined in 'args' from
369                 //              its current opacity to fully opaque.
370                 return d._fade(d.mixin({ end: 1 }, args)); // dojo._Animation
371         }
372
373         dojo.fadeOut = function(/*dojo.__FadeArgs*/  args){
374                 // summary: 
375                 //              Returns an animation that will fade node defined in 'args'
376                 //              from its current opacity to fully transparent.
377                 return d._fade(d.mixin({ end: 0 }, args)); // dojo._Animation
378         }
379
380         dojo._defaultEasing = function(/*Decimal?*/ n){
381                 // summary: The default easing function for dojo._Animation(s)
382                 return 0.5 + ((Math.sin((n + 1.5) * Math.PI))/2);
383         }
384
385         var PropLine = function(properties){
386                 // PropLine is an internal class which is used to model the values of
387                 // an a group of CSS properties across an animation lifecycle. In
388                 // particular, the "getValue" function handles getting interpolated
389                 // values between start and end for a particular CSS value.
390                 this._properties = properties;
391                 for(var p in properties){
392                         var prop = properties[p];
393                         if(prop.start instanceof d.Color){
394                                 // create a reusable temp color object to keep intermediate results
395                                 prop.tempColor = new d.Color();
396                         }
397                 }
398                 this.getValue = function(r){
399                         var ret = {};
400                         for(var p in this._properties){
401                                 var prop = this._properties[p];
402                                 var start = prop.start;
403                                 if(start instanceof d.Color){
404                                         ret[p] = d.blendColors(start, prop.end, r, prop.tempColor).toCss();
405                                 }else if(!d.isArray(start)){
406                                         ret[p] = ((prop.end - start) * r) + start + (p != "opacity" ? prop.units||"px" : "");
407                                 }
408                         }
409                         return ret;
410                 }
411         }
412
413         /*=====
414         dojo.declare("dojo.__AnimArgs", [dojo.__FadeArgs], {
415                 // Properties: Object?
416                 //      A hash map of style properties to Objects describing the transition,
417                 //      such as the properties of dojo._Line with an additional 'unit' property
418                 properties: {}
419                 
420                 //TODOC: add event callbacks
421         });
422         =====*/
423
424         dojo.animateProperty = function(/*dojo.__AnimArgs*/ args){
425                 //      summary: 
426                 //              Returns an animation that will transition the properties of
427                 //              node defined in 'args' depending how they are defined in
428                 //              'args.properties'
429                 //
430                 // description:
431                 //              dojo.animateProperty is the foundation of most dojo.fx
432                 //              animations. It takes an object of "properties" corresponding to
433                 //              style properties, and animates them in parallel over a set
434                 //              duration.
435                 //      
436                 //      example:
437                 //              A simple animation that changes the width of the specified node.
438                 //      |       dojo.animateProperty({ 
439                 //      |               node: "nodeId",
440                 //      |               properties: { width: 400 },
441                 //      |       }).play();
442                 //              Dojo figures out the start value for the width and converts the
443                 //              integer specified for the width to the more expressive but
444                 //              verbose form `{ width: { end: '400', units: 'px' } }` which you
445                 //              can also specify directly
446                 //      example:
447                 //              animate width, height, and padding over 2 seconds...the
448                 //              pedantic way:
449                 //      |       dojo.animateProperty({ node: node, duration:2000,
450                 //      |               properties: {
451                 //      |                       width: { start: '200', end: '400', unit:"px" },
452                 //      |                       height: { start:'200', end: '400', unit:"px" },
453                 //      |                       paddingTop: { start:'5', end:'50', unit:"px" } 
454                 //      |               }
455                 //      |       }).play();
456                 //
457                 //      example:
458                 //              plug in a different easing function and register a callback for
459                 //              when the animation ends. Easing functions accept values between
460                 //              zero and one and return a value on that basis. In this case, an
461                 //              exponential-in curve.
462                 //      |       dojo.animateProperty({ 
463                 //      |               node: "nodeId",
464                 //      |               // dojo figures out the start value
465                 //      |               properties: { width: { end: 400 } },
466                 //      |               easing: function(n){
467                 //      |                       return (n==0) ? 0 : Math.pow(2, 10 * (n - 1));
468                 //      |               },
469                 //      |               onEnd: function(){
470                 //      |                       // called when the animation finishes
471                 //      |               }
472                 //      |       }).play(500); // delay playing half a second
473
474                 args.node = d.byId(args.node);
475                 if(!args.easing){ args.easing = d._defaultEasing; }
476
477                 var anim = new d._Animation(args);
478                 d.connect(anim, "beforeBegin", anim, function(){
479                         var pm = {};
480                         for(var p in this.properties){
481                                 // Make shallow copy of properties into pm because we overwrite
482                                 // some values below. In particular if start/end are functions
483                                 // we don't want to overwrite them or the functions won't be
484                                 // called if the animation is reused.
485                                 if(p == "width" || p == "height"){
486                                         this.node.display = "block";
487                                 }
488                                 var prop = this.properties[p];
489                                 prop = pm[p] = d.mixin({}, (d.isObject(prop) ? prop: { end: prop }));
490
491                                 if(d.isFunction(prop.start)){
492                                         prop.start = prop.start();
493                                 }
494                                 if(d.isFunction(prop.end)){
495                                         prop.end = prop.end();
496                                 }
497                                 var isColor = (p.toLowerCase().indexOf("color") >= 0);
498                                 function getStyle(node, p){
499                                         // dojo.style(node, "height") can return "auto" or "" on IE; this is more reliable:
500                                         var v = ({height: node.offsetHeight, width: node.offsetWidth})[p];
501                                         if(v !== undefined){ return v; }
502                                         v = d.style(node, p);
503                                         return (p=="opacity") ? Number(v) : (isColor ? v : parseFloat(v));
504                                 }
505                                 if(!("end" in prop)){
506                                         prop.end = getStyle(this.node, p);
507                                 }else if(!("start" in prop)){
508                                         prop.start = getStyle(this.node, p);
509                                 }
510
511                                 if(isColor){
512                                         prop.start = new d.Color(prop.start);
513                                         prop.end = new d.Color(prop.end);
514                                 }else{
515                                         prop.start = (p == "opacity") ? Number(prop.start) : parseFloat(prop.start);
516                                 }
517                         }
518                         this.curve = new PropLine(pm);
519                 });
520                 d.connect(anim, "onAnimate", anim, function(propValues){
521                         // try{
522                         for(var s in propValues){
523                                 d.style(this.node, s, propValues[s]);
524                                 // this.node.style[s] = propValues[s];
525                         }
526                 });
527                 return anim; // dojo._Animation
528         }
529
530         dojo.anim = function(   /*DOMNode|String*/      node, 
531                                                         /*Object*/                      properties, 
532                                                         /*Integer?*/            duration, 
533                                                         /*Function?*/           easing, 
534                                                         /*Function?*/           onEnd,
535                                                         /*Integer?*/            delay){
536                 //      summary:
537                 //              A simpler interface to `dojo.animateProperty()`, also returns
538                 //              an instance of `dojo._Animation` but begins the animation
539                 //              immediately, unlike nearly every other Dojo animation API.
540                 //      description:
541                 //              `dojo.anim` is a simpler (but somewhat less powerful) version
542                 //              of `dojo.animateProperty`.  It uses defaults for many basic properties
543                 //              and allows for positional parameters to be used in place of the
544                 //              packed "property bag" which is used for other Dojo animation
545                 //              methods.
546                 //
547                 //              The `dojo._Animation` object returned from `dojo.anim` will be
548                 //              already playing when it is returned from this function, so
549                 //              calling play() on it again is (usually) a no-op.
550                 //      node:
551                 //              a DOM node or the id of a node to animate CSS properties on
552                 //      duration:
553                 //              The number of milliseconds over which the animation
554                 //              should run. Defaults to the global animation default duration
555                 //              (350ms).
556                 //      easing:
557                 //              An easing function over which to calculate acceleration
558                 //              and deceleration of the animation through its duration.
559                 //              A default easing algorithm is provided, but you may
560                 //              plug in any you wish. A large selection of easing algorithms
561                 //              are available in `dojox.fx.easing`.
562                 //      onEnd:
563                 //              A function to be called when the animation finishes
564                 //              running.
565                 //      delay:
566                 //              The number of milliseconds to delay beginning the
567                 //              animation by. The default is 0.
568                 //      example:
569                 //              Fade out a node
570                 //      |       dojo.anim("id", { opacity: 0 });
571                 //      example:
572                 //              Fade out a node over a full second
573                 //      |       dojo.anim("id", { opacity: 0 }, 1000);
574                 return d.animateProperty({ 
575                         node: node,
576                         duration: duration||d._Animation.prototype.duration,
577                         properties: properties,
578                         easing: easing,
579                         onEnd: onEnd 
580                 }).play(delay||0);
581         }
582 })();
583
584 }