ttrss/lib/dojo/_base/fx.js.uncompressed.js

671 lines
19 KiB
JavaScript
Raw Normal View History

2013-03-18 06:26:24 +00:00
define("dojo/_base/fx", ["./kernel", "./config", /*===== "./declare", =====*/ "./lang", "../Evented", "./Color", "./connect", "./sniff", "../dom", "../dom-style"],
function(dojo, config, /*===== declare, =====*/ lang, Evented, Color, connect, has, dom, style){
// module:
// dojo/_base/fx
// notes:
// Animation loosely package based on Dan Pupius' work, contributed under CLA; see
// http://pupius.co.uk/js/Toolkit.Drawing.js
var _mixin = lang.mixin;
// Module export
var basefx = {
// summary:
// This module defines the base dojo/_base/fx implementation.
};
var _Line = basefx._Line = function(/*int*/ start, /*int*/ end){
// summary:
// Object used to generate values from a start value to an end value
// start: int
// Beginning value for range
// end: int
// Ending value for range
this.start = start;
this.end = end;
};
_Line.prototype.getValue = function(/*float*/ n){
// summary:
// Returns the point on the line
// n:
// a floating point number greater than 0 and less than 1
return ((this.end - this.start) * n) + this.start; // Decimal
};
var Animation = basefx.Animation = function(args){
// summary:
// A generic animation class that fires callbacks into its handlers
// object at various states.
// description:
// A generic animation class that fires callbacks into its handlers
// object at various states. Nearly all dojo animation functions
// return an instance of this method, usually without calling the
// .play() method beforehand. Therefore, you will likely need to
// call .play() on instances of `Animation` when one is
// returned.
// args: Object
// The 'magic argument', mixing all the properties into this
// animation instance.
_mixin(this, args);
if(lang.isArray(this.curve)){
this.curve = new _Line(this.curve[0], this.curve[1]);
}
};
Animation.prototype = new Evented();
lang.extend(Animation, {
// duration: Integer
// The time in milliseconds the animation will take to run
duration: 350,
/*=====
// curve: _Line|Array
// A two element array of start and end values, or a `_Line` instance to be
// used in the Animation.
curve: null,
// easing: Function?
// A Function to adjust the acceleration (or deceleration) of the progress
// across a _Line
easing: null,
=====*/
// repeat: Integer?
// The number of times to loop the animation
repeat: 0,
// rate: Integer?
// the time in milliseconds to wait before advancing to next frame
// (used as a fps timer: 1000/rate = fps)
rate: 20 /* 50 fps */,
/*=====
// delay: Integer?
// The time in milliseconds to wait before starting animation after it
// has been .play()'ed
delay: null,
// beforeBegin: Event?
// Synthetic event fired before a Animation begins playing (synchronous)
beforeBegin: null,
// onBegin: Event?
// Synthetic event fired as a Animation begins playing (useful?)
onBegin: null,
// onAnimate: Event?
// Synthetic event fired at each interval of the Animation
onAnimate: null,
// onEnd: Event?
// Synthetic event fired after the final frame of the Animation
onEnd: null,
// onPlay: Event?
// Synthetic event fired any time the Animation is play()'ed
onPlay: null,
// onPause: Event?
// Synthetic event fired when the Animation is paused
onPause: null,
// onStop: Event
// Synthetic event fires when the Animation is stopped
onStop: null,
=====*/
_percent: 0,
_startRepeatCount: 0,
_getStep: function(){
var _p = this._percent,
_e = this.easing
;
return _e ? _e(_p) : _p;
},
_fire: function(/*Event*/ evt, /*Array?*/ args){
// summary:
// Convenience function. Fire event "evt" and pass it the
// arguments specified in "args".
// description:
// Convenience function. Fire event "evt" and pass it the
// arguments specified in "args".
// Fires the callback in the scope of this Animation
// instance.
// evt:
// The event to fire.
// args:
// The arguments to pass to the event.
var a = args||[];
if(this[evt]){
if(config.debugAtAllCosts){
this[evt].apply(this, a);
}else{
try{
this[evt].apply(this, a);
}catch(e){
// squelch and log because we shouldn't allow exceptions in
// synthetic event handlers to cause the internal timer to run
// amuck, potentially pegging the CPU. I'm not a fan of this
// squelch, but hopefully logging will make it clear what's
// going on
console.error("exception in animation handler for:", evt);
console.error(e);
}
}
}
return this; // Animation
},
play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
// summary:
// Start the animation.
// delay:
// How many milliseconds to delay before starting.
// gotoStart:
// If true, starts the animation from the beginning; otherwise,
// starts it from its current position.
// returns: Animation
// The instance to allow chaining.
var _t = this;
if(_t._delayTimer){ _t._clearTimer(); }
if(gotoStart){
_t._stopTimer();
_t._active = _t._paused = false;
_t._percent = 0;
}else if(_t._active && !_t._paused){
return _t;
}
_t._fire("beforeBegin", [_t.node]);
var de = delay || _t.delay,
_p = lang.hitch(_t, "_play", gotoStart);
if(de > 0){
_t._delayTimer = setTimeout(_p, de);
return _t;
}
_p();
return _t; // Animation
},
_play: function(gotoStart){
var _t = this;
if(_t._delayTimer){ _t._clearTimer(); }
_t._startTime = new Date().valueOf();
if(_t._paused){
_t._startTime -= _t.duration * _t._percent;
}
_t._active = true;
_t._paused = false;
var value = _t.curve.getValue(_t._getStep());
if(!_t._percent){
if(!_t._startRepeatCount){
_t._startRepeatCount = _t.repeat;
}
_t._fire("onBegin", [value]);
}
_t._fire("onPlay", [value]);
_t._cycle();
return _t; // Animation
},
pause: function(){
// summary:
// Pauses a running animation.
var _t = this;
if(_t._delayTimer){ _t._clearTimer(); }
_t._stopTimer();
if(!_t._active){ return _t; /*Animation*/ }
_t._paused = true;
_t._fire("onPause", [_t.curve.getValue(_t._getStep())]);
return _t; // Animation
},
gotoPercent: function(/*Decimal*/ percent, /*Boolean?*/ andPlay){
// summary:
// Sets the progress of the animation.
// percent:
// A percentage in decimal notation (between and including 0.0 and 1.0).
// andPlay:
// If true, play the animation after setting the progress.
var _t = this;
_t._stopTimer();
_t._active = _t._paused = true;
_t._percent = percent;
if(andPlay){ _t.play(); }
return _t; // Animation
},
stop: function(/*boolean?*/ gotoEnd){
// summary:
// Stops a running animation.
// gotoEnd:
// If true, the animation will end.
var _t = this;
if(_t._delayTimer){ _t._clearTimer(); }
if(!_t._timer){ return _t; /* Animation */ }
_t._stopTimer();
if(gotoEnd){
_t._percent = 1;
}
_t._fire("onStop", [_t.curve.getValue(_t._getStep())]);
_t._active = _t._paused = false;
return _t; // Animation
},
status: function(){
// summary:
// Returns a string token representation of the status of
// the animation, one of: "paused", "playing", "stopped"
if(this._active){
return this._paused ? "paused" : "playing"; // String
}
return "stopped"; // String
},
_cycle: function(){
var _t = this;
if(_t._active){
var curr = new Date().valueOf();
// Allow durations of 0 (instant) by setting step to 1 - see #13798
var step = _t.duration === 0 ? 1 : (curr - _t._startTime) / (_t.duration);
if(step >= 1){
step = 1;
}
_t._percent = step;
// Perform easing
if(_t.easing){
step = _t.easing(step);
}
_t._fire("onAnimate", [_t.curve.getValue(step)]);
if(_t._percent < 1){
_t._startTimer();
}else{
_t._active = false;
if(_t.repeat > 0){
_t.repeat--;
_t.play(null, true);
}else if(_t.repeat == -1){
_t.play(null, true);
}else{
if(_t._startRepeatCount){
_t.repeat = _t._startRepeatCount;
_t._startRepeatCount = 0;
}
}
_t._percent = 0;
_t._fire("onEnd", [_t.node]);
!_t.repeat && _t._stopTimer();
}
}
return _t; // Animation
},
_clearTimer: function(){
// summary:
// Clear the play delay timer
clearTimeout(this._delayTimer);
delete this._delayTimer;
}
});
// the local timer, stubbed into all Animation instances
var ctr = 0,
timer = null,
runner = {
run: function(){}
};
lang.extend(Animation, {
_startTimer: function(){
if(!this._timer){
this._timer = connect.connect(runner, "run", this, "_cycle");
ctr++;
}
if(!timer){
timer = setInterval(lang.hitch(runner, "run"), this.rate);
}
},
_stopTimer: function(){
if(this._timer){
connect.disconnect(this._timer);
this._timer = null;
ctr--;
}
if(ctr <= 0){
clearInterval(timer);
timer = null;
ctr = 0;
}
}
});
var _makeFadeable =
has("ie") ? function(node){
// only set the zoom if the "tickle" value would be the same as the
// default
var ns = node.style;
// don't set the width to auto if it didn't already cascade that way.
// We don't want to f anyones designs
if(!ns.width.length && style.get(node, "width") == "auto"){
ns.width = "auto";
}
} :
function(){};
basefx._fade = function(/*Object*/ args){
// summary:
// Returns an animation that will fade the node defined by
// args.node from the start to end values passed (args.start
// args.end) (end is mandatory, start is optional)
args.node = dom.byId(args.node);
var fArgs = _mixin({ properties: {} }, args),
props = (fArgs.properties.opacity = {});
props.start = !("start" in fArgs) ?
function(){
return +style.get(fArgs.node, "opacity")||0;
} : fArgs.start;
props.end = fArgs.end;
var anim = basefx.animateProperty(fArgs);
connect.connect(anim, "beforeBegin", lang.partial(_makeFadeable, fArgs.node));
return anim; // Animation
};
/*=====
var __FadeArgs = declare(null, {
// node: DOMNode|String
// The node referenced in the animation
// duration: Integer?
// Duration of the animation in milliseconds.
// easing: Function?
// An easing function.
});
=====*/
basefx.fadeIn = function(/*__FadeArgs*/ args){
// summary:
// Returns an animation that will fade node defined in 'args' from
// its current opacity to fully opaque.
return basefx._fade(_mixin({ end: 1 }, args)); // Animation
};
basefx.fadeOut = function(/*__FadeArgs*/ args){
// summary:
// Returns an animation that will fade node defined in 'args'
// from its current opacity to fully transparent.
return basefx._fade(_mixin({ end: 0 }, args)); // Animation
};
basefx._defaultEasing = function(/*Decimal?*/ n){
// summary:
// The default easing function for Animation(s)
return 0.5 + ((Math.sin((n + 1.5) * Math.PI)) / 2); // Decimal
};
var PropLine = function(properties){
// PropLine is an internal class which is used to model the values of
// an a group of CSS properties across an animation lifecycle. In
// particular, the "getValue" function handles getting interpolated
// values between start and end for a particular CSS value.
this._properties = properties;
for(var p in properties){
var prop = properties[p];
if(prop.start instanceof Color){
// create a reusable temp color object to keep intermediate results
prop.tempColor = new Color();
}
}
};
PropLine.prototype.getValue = function(r){
var ret = {};
for(var p in this._properties){
var prop = this._properties[p],
start = prop.start;
if(start instanceof Color){
ret[p] = Color.blendColors(start, prop.end, r, prop.tempColor).toCss();
}else if(!lang.isArray(start)){
ret[p] = ((prop.end - start) * r) + start + (p != "opacity" ? prop.units || "px" : 0);
}
}
return ret;
};
/*=====
var __AnimArgs = declare(__FadeArgs, {
// properties: Object?
// A hash map of style properties to Objects describing the transition,
// such as the properties of _Line with an additional 'units' property
properties: {}
//TODOC: add event callbacks
});
=====*/
basefx.animateProperty = function(/*__AnimArgs*/ args){
// summary:
// Returns an animation that will transition the properties of
// node defined in `args` depending how they are defined in
// `args.properties`
//
// description:
// Foundation of most `dojo/_base/fx`
// animations. It takes an object of "properties" corresponding to
// style properties, and animates them in parallel over a set
// duration.
//
// example:
// A simple animation that changes the width of the specified node.
// | basefx.animateProperty({
// | node: "nodeId",
// | properties: { width: 400 },
// | }).play();
// Dojo figures out the start value for the width and converts the
// integer specified for the width to the more expressive but
// verbose form `{ width: { end: '400', units: 'px' } }` which you
// can also specify directly. Defaults to 'px' if omitted.
//
// example:
// Animate width, height, and padding over 2 seconds... the
// pedantic way:
// | basefx.animateProperty({ node: node, duration:2000,
// | properties: {
// | width: { start: '200', end: '400', units:"px" },
// | height: { start:'200', end: '400', units:"px" },
// | paddingTop: { start:'5', end:'50', units:"px" }
// | }
// | }).play();
// Note 'paddingTop' is used over 'padding-top'. Multi-name CSS properties
// are written using "mixed case", as the hyphen is illegal as an object key.
//
// example:
// Plug in a different easing function and register a callback for
// when the animation ends. Easing functions accept values between
// zero and one and return a value on that basis. In this case, an
// exponential-in curve.
// | basefx.animateProperty({
// | node: "nodeId",
// | // dojo figures out the start value
// | properties: { width: { end: 400 } },
// | easing: function(n){
// | return (n==0) ? 0 : Math.pow(2, 10 * (n - 1));
// | },
// | onEnd: function(node){
// | // called when the animation finishes. The animation
// | // target is passed to this function
// | }
// | }).play(500); // delay playing half a second
//
// example:
// Like all `Animation`s, animateProperty returns a handle to the
// Animation instance, which fires the events common to Dojo FX. Use `aspect.after`
// to access these events outside of the Animation definition:
// | var anim = basefx.animateProperty({
// | node:"someId",
// | properties:{
// | width:400, height:500
// | }
// | });
// | aspect.after(anim, "onEnd", function(){
// | console.log("animation ended");
// | }, true);
// | // play the animation now:
// | anim.play();
//
// example:
// Each property can be a function whose return value is substituted along.
// Additionally, each measurement (eg: start, end) can be a function. The node
// reference is passed directly to callbacks.
// | basefx.animateProperty({
// | node:"mine",
// | properties:{
// | height:function(node){
// | // shrink this node by 50%
// | return domGeom.position(node).h / 2
// | },
// | width:{
// | start:function(node){ return 100; },
// | end:function(node){ return 200; }
// | }
// | }
// | }).play();
//
var n = args.node = dom.byId(args.node);
if(!args.easing){ args.easing = dojo._defaultEasing; }
var anim = new Animation(args);
connect.connect(anim, "beforeBegin", anim, function(){
var pm = {};
for(var p in this.properties){
// Make shallow copy of properties into pm because we overwrite
// some values below. In particular if start/end are functions
// we don't want to overwrite them or the functions won't be
// called if the animation is reused.
if(p == "width" || p == "height"){
this.node.display = "block";
}
var prop = this.properties[p];
if(lang.isFunction(prop)){
prop = prop(n);
}
prop = pm[p] = _mixin({}, (lang.isObject(prop) ? prop: { end: prop }));
if(lang.isFunction(prop.start)){
prop.start = prop.start(n);
}
if(lang.isFunction(prop.end)){
prop.end = prop.end(n);
}
var isColor = (p.toLowerCase().indexOf("color") >= 0);
function getStyle(node, p){
// domStyle.get(node, "height") can return "auto" or "" on IE; this is more reliable:
var v = { height: node.offsetHeight, width: node.offsetWidth }[p];
if(v !== undefined){ return v; }
v = style.get(node, p);
return (p == "opacity") ? +v : (isColor ? v : parseFloat(v));
}
if(!("end" in prop)){
prop.end = getStyle(n, p);
}else if(!("start" in prop)){
prop.start = getStyle(n, p);
}
if(isColor){
prop.start = new Color(prop.start);
prop.end = new Color(prop.end);
}else{
prop.start = (p == "opacity") ? +prop.start : parseFloat(prop.start);
}
}
this.curve = new PropLine(pm);
});
connect.connect(anim, "onAnimate", lang.hitch(style, "set", anim.node));
return anim; // Animation
};
basefx.anim = function( /*DOMNode|String*/ node,
/*Object*/ properties,
/*Integer?*/ duration,
/*Function?*/ easing,
/*Function?*/ onEnd,
/*Integer?*/ delay){
// summary:
// A simpler interface to `animateProperty()`, also returns
// an instance of `Animation` but begins the animation
// immediately, unlike nearly every other Dojo animation API.
// description:
// Simpler (but somewhat less powerful) version
// of `animateProperty`. It uses defaults for many basic properties
// and allows for positional parameters to be used in place of the
// packed "property bag" which is used for other Dojo animation
// methods.
//
// The `Animation` object returned will be already playing, so
// calling play() on it again is (usually) a no-op.
// node:
// a DOM node or the id of a node to animate CSS properties on
// duration:
// The number of milliseconds over which the animation
// should run. Defaults to the global animation default duration
// (350ms).
// easing:
// An easing function over which to calculate acceleration
// and deceleration of the animation through its duration.
// A default easing algorithm is provided, but you may
// plug in any you wish. A large selection of easing algorithms
// are available in `dojo/fx/easing`.
// onEnd:
// A function to be called when the animation finishes
// running.
// delay:
// The number of milliseconds to delay beginning the
// animation by. The default is 0.
// example:
// Fade out a node
// | basefx.anim("id", { opacity: 0 });
// example:
// Fade out a node over a full second
// | basefx.anim("id", { opacity: 0 }, 1000);
return basefx.animateProperty({ // Animation
node: node,
duration: duration || Animation.prototype.duration,
properties: properties,
easing: easing,
onEnd: onEnd
}).play(delay || 0);
};
if( 1 ){
_mixin(dojo, basefx);
// Alias to drop come 2.0:
dojo._Animation = Animation;
}
return basefx;
});