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