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