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