]> git.wh0rd.org - 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 });