420 lines
12 KiB
JavaScript
420 lines
12 KiB
JavaScript
|
define("dojo/fx", [
|
||
|
"./_base/lang",
|
||
|
"./Evented",
|
||
|
"./_base/kernel",
|
||
|
"./_base/array",
|
||
|
"./_base/connect",
|
||
|
"./_base/fx",
|
||
|
"./dom",
|
||
|
"./dom-style",
|
||
|
"./dom-geometry",
|
||
|
"./ready",
|
||
|
"require" // for context sensitive loading of Toggler
|
||
|
], function(lang, Evented, dojo, arrayUtil, connect, baseFx, dom, domStyle, geom, ready, require){
|
||
|
|
||
|
// module:
|
||
|
// dojo/fx
|
||
|
|
||
|
// For back-compat, remove in 2.0.
|
||
|
if(!dojo.isAsync){
|
||
|
ready(0, function(){
|
||
|
var requires = ["./fx/Toggler"];
|
||
|
require(requires); // use indirection so modules not rolled into a build
|
||
|
});
|
||
|
}
|
||
|
|
||
|
var coreFx = dojo.fx = {
|
||
|
// summary:
|
||
|
// Effects library on top of Base animations
|
||
|
};
|
||
|
|
||
|
var _baseObj = {
|
||
|
_fire: function(evt, args){
|
||
|
if(this[evt]){
|
||
|
this[evt].apply(this, args||[]);
|
||
|
}
|
||
|
return this;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var _chain = function(animations){
|
||
|
this._index = -1;
|
||
|
this._animations = animations||[];
|
||
|
this._current = this._onAnimateCtx = this._onEndCtx = null;
|
||
|
|
||
|
this.duration = 0;
|
||
|
arrayUtil.forEach(this._animations, function(a){
|
||
|
this.duration += a.duration;
|
||
|
if(a.delay){ this.duration += a.delay; }
|
||
|
}, this);
|
||
|
};
|
||
|
_chain.prototype = new Evented();
|
||
|
lang.extend(_chain, {
|
||
|
_onAnimate: function(){
|
||
|
this._fire("onAnimate", arguments);
|
||
|
},
|
||
|
_onEnd: function(){
|
||
|
connect.disconnect(this._onAnimateCtx);
|
||
|
connect.disconnect(this._onEndCtx);
|
||
|
this._onAnimateCtx = this._onEndCtx = null;
|
||
|
if(this._index + 1 == this._animations.length){
|
||
|
this._fire("onEnd");
|
||
|
}else{
|
||
|
// switch animations
|
||
|
this._current = this._animations[++this._index];
|
||
|
this._onAnimateCtx = connect.connect(this._current, "onAnimate", this, "_onAnimate");
|
||
|
this._onEndCtx = connect.connect(this._current, "onEnd", this, "_onEnd");
|
||
|
this._current.play(0, true);
|
||
|
}
|
||
|
},
|
||
|
play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
|
||
|
if(!this._current){ this._current = this._animations[this._index = 0]; }
|
||
|
if(!gotoStart && this._current.status() == "playing"){ return this; }
|
||
|
var beforeBegin = connect.connect(this._current, "beforeBegin", this, function(){
|
||
|
this._fire("beforeBegin");
|
||
|
}),
|
||
|
onBegin = connect.connect(this._current, "onBegin", this, function(arg){
|
||
|
this._fire("onBegin", arguments);
|
||
|
}),
|
||
|
onPlay = connect.connect(this._current, "onPlay", this, function(arg){
|
||
|
this._fire("onPlay", arguments);
|
||
|
connect.disconnect(beforeBegin);
|
||
|
connect.disconnect(onBegin);
|
||
|
connect.disconnect(onPlay);
|
||
|
});
|
||
|
if(this._onAnimateCtx){
|
||
|
connect.disconnect(this._onAnimateCtx);
|
||
|
}
|
||
|
this._onAnimateCtx = connect.connect(this._current, "onAnimate", this, "_onAnimate");
|
||
|
if(this._onEndCtx){
|
||
|
connect.disconnect(this._onEndCtx);
|
||
|
}
|
||
|
this._onEndCtx = connect.connect(this._current, "onEnd", this, "_onEnd");
|
||
|
this._current.play.apply(this._current, arguments);
|
||
|
return this;
|
||
|
},
|
||
|
pause: function(){
|
||
|
if(this._current){
|
||
|
var e = connect.connect(this._current, "onPause", this, function(arg){
|
||
|
this._fire("onPause", arguments);
|
||
|
connect.disconnect(e);
|
||
|
});
|
||
|
this._current.pause();
|
||
|
}
|
||
|
return this;
|
||
|
},
|
||
|
gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){
|
||
|
this.pause();
|
||
|
var offset = this.duration * percent;
|
||
|
this._current = null;
|
||
|
arrayUtil.some(this._animations, function(a){
|
||
|
if(a.duration <= offset){
|
||
|
this._current = a;
|
||
|
return true;
|
||
|
}
|
||
|
offset -= a.duration;
|
||
|
return false;
|
||
|
});
|
||
|
if(this._current){
|
||
|
this._current.gotoPercent(offset / this._current.duration, andPlay);
|
||
|
}
|
||
|
return this;
|
||
|
},
|
||
|
stop: function(/*boolean?*/ gotoEnd){
|
||
|
if(this._current){
|
||
|
if(gotoEnd){
|
||
|
for(; this._index + 1 < this._animations.length; ++this._index){
|
||
|
this._animations[this._index].stop(true);
|
||
|
}
|
||
|
this._current = this._animations[this._index];
|
||
|
}
|
||
|
var e = connect.connect(this._current, "onStop", this, function(arg){
|
||
|
this._fire("onStop", arguments);
|
||
|
connect.disconnect(e);
|
||
|
});
|
||
|
this._current.stop();
|
||
|
}
|
||
|
return this;
|
||
|
},
|
||
|
status: function(){
|
||
|
return this._current ? this._current.status() : "stopped";
|
||
|
},
|
||
|
destroy: function(){
|
||
|
if(this._onAnimateCtx){ connect.disconnect(this._onAnimateCtx); }
|
||
|
if(this._onEndCtx){ connect.disconnect(this._onEndCtx); }
|
||
|
}
|
||
|
});
|
||
|
lang.extend(_chain, _baseObj);
|
||
|
|
||
|
coreFx.chain = function(/*dojo/_base/fx.Animation[]*/ animations){
|
||
|
// summary:
|
||
|
// Chain a list of `dojo.Animation`s to run in sequence
|
||
|
//
|
||
|
// description:
|
||
|
// Return a `dojo.Animation` which will play all passed
|
||
|
// `dojo.Animation` instances in sequence, firing its own
|
||
|
// synthesized events simulating a single animation. (eg:
|
||
|
// onEnd of this animation means the end of the chain,
|
||
|
// not the individual animations within)
|
||
|
//
|
||
|
// example:
|
||
|
// Once `node` is faded out, fade in `otherNode`
|
||
|
// | fx.chain([
|
||
|
// | dojo.fadeIn({ node:node }),
|
||
|
// | dojo.fadeOut({ node:otherNode })
|
||
|
// | ]).play();
|
||
|
//
|
||
|
return new _chain(animations); // dojo/_base/fx.Animation
|
||
|
};
|
||
|
|
||
|
var _combine = function(animations){
|
||
|
this._animations = animations||[];
|
||
|
this._connects = [];
|
||
|
this._finished = 0;
|
||
|
|
||
|
this.duration = 0;
|
||
|
arrayUtil.forEach(animations, function(a){
|
||
|
var duration = a.duration;
|
||
|
if(a.delay){ duration += a.delay; }
|
||
|
if(this.duration < duration){ this.duration = duration; }
|
||
|
this._connects.push(connect.connect(a, "onEnd", this, "_onEnd"));
|
||
|
}, this);
|
||
|
|
||
|
this._pseudoAnimation = new baseFx.Animation({curve: [0, 1], duration: this.duration});
|
||
|
var self = this;
|
||
|
arrayUtil.forEach(["beforeBegin", "onBegin", "onPlay", "onAnimate", "onPause", "onStop", "onEnd"],
|
||
|
function(evt){
|
||
|
self._connects.push(connect.connect(self._pseudoAnimation, evt,
|
||
|
function(){ self._fire(evt, arguments); }
|
||
|
));
|
||
|
}
|
||
|
);
|
||
|
};
|
||
|
lang.extend(_combine, {
|
||
|
_doAction: function(action, args){
|
||
|
arrayUtil.forEach(this._animations, function(a){
|
||
|
a[action].apply(a, args);
|
||
|
});
|
||
|
return this;
|
||
|
},
|
||
|
_onEnd: function(){
|
||
|
if(++this._finished > this._animations.length){
|
||
|
this._fire("onEnd");
|
||
|
}
|
||
|
},
|
||
|
_call: function(action, args){
|
||
|
var t = this._pseudoAnimation;
|
||
|
t[action].apply(t, args);
|
||
|
},
|
||
|
play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
|
||
|
this._finished = 0;
|
||
|
this._doAction("play", arguments);
|
||
|
this._call("play", arguments);
|
||
|
return this;
|
||
|
},
|
||
|
pause: function(){
|
||
|
this._doAction("pause", arguments);
|
||
|
this._call("pause", arguments);
|
||
|
return this;
|
||
|
},
|
||
|
gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){
|
||
|
var ms = this.duration * percent;
|
||
|
arrayUtil.forEach(this._animations, function(a){
|
||
|
a.gotoPercent(a.duration < ms ? 1 : (ms / a.duration), andPlay);
|
||
|
});
|
||
|
this._call("gotoPercent", arguments);
|
||
|
return this;
|
||
|
},
|
||
|
stop: function(/*boolean?*/ gotoEnd){
|
||
|
this._doAction("stop", arguments);
|
||
|
this._call("stop", arguments);
|
||
|
return this;
|
||
|
},
|
||
|
status: function(){
|
||
|
return this._pseudoAnimation.status();
|
||
|
},
|
||
|
destroy: function(){
|
||
|
arrayUtil.forEach(this._connects, connect.disconnect);
|
||
|
}
|
||
|
});
|
||
|
lang.extend(_combine, _baseObj);
|
||
|
|
||
|
coreFx.combine = function(/*dojo/_base/fx.Animation[]*/ animations){
|
||
|
// summary:
|
||
|
// Combine a list of `dojo.Animation`s to run in parallel
|
||
|
//
|
||
|
// description:
|
||
|
// Combine an array of `dojo.Animation`s to run in parallel,
|
||
|
// providing a new `dojo.Animation` instance encompasing each
|
||
|
// animation, firing standard animation events.
|
||
|
//
|
||
|
// example:
|
||
|
// Fade out `node` while fading in `otherNode` simultaneously
|
||
|
// | fx.combine([
|
||
|
// | dojo.fadeIn({ node:node }),
|
||
|
// | dojo.fadeOut({ node:otherNode })
|
||
|
// | ]).play();
|
||
|
//
|
||
|
// example:
|
||
|
// When the longest animation ends, execute a function:
|
||
|
// | var anim = fx.combine([
|
||
|
// | dojo.fadeIn({ node: n, duration:700 }),
|
||
|
// | dojo.fadeOut({ node: otherNode, duration: 300 })
|
||
|
// | ]);
|
||
|
// | dojo.connect(anim, "onEnd", function(){
|
||
|
// | // overall animation is done.
|
||
|
// | });
|
||
|
// | anim.play(); // play the animation
|
||
|
//
|
||
|
return new _combine(animations); // dojo/_base/fx.Animation
|
||
|
};
|
||
|
|
||
|
coreFx.wipeIn = function(/*Object*/ args){
|
||
|
// summary:
|
||
|
// Expand a node to it's natural height.
|
||
|
//
|
||
|
// description:
|
||
|
// Returns an animation that will expand the
|
||
|
// node defined in 'args' object from it's current height to
|
||
|
// it's natural height (with no scrollbar).
|
||
|
// Node must have no margin/border/padding.
|
||
|
//
|
||
|
// args: Object
|
||
|
// A hash-map of standard `dojo.Animation` constructor properties
|
||
|
// (such as easing: node: duration: and so on)
|
||
|
//
|
||
|
// example:
|
||
|
// | fx.wipeIn({
|
||
|
// | node:"someId"
|
||
|
// | }).play()
|
||
|
var node = args.node = dom.byId(args.node), s = node.style, o;
|
||
|
|
||
|
var anim = baseFx.animateProperty(lang.mixin({
|
||
|
properties: {
|
||
|
height: {
|
||
|
// wrapped in functions so we wait till the last second to query (in case value has changed)
|
||
|
start: function(){
|
||
|
// start at current [computed] height, but use 1px rather than 0
|
||
|
// because 0 causes IE to display the whole panel
|
||
|
o = s.overflow;
|
||
|
s.overflow = "hidden";
|
||
|
if(s.visibility == "hidden" || s.display == "none"){
|
||
|
s.height = "1px";
|
||
|
s.display = "";
|
||
|
s.visibility = "";
|
||
|
return 1;
|
||
|
}else{
|
||
|
var height = domStyle.get(node, "height");
|
||
|
return Math.max(height, 1);
|
||
|
}
|
||
|
},
|
||
|
end: function(){
|
||
|
return node.scrollHeight;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}, args));
|
||
|
|
||
|
var fini = function(){
|
||
|
s.height = "auto";
|
||
|
s.overflow = o;
|
||
|
};
|
||
|
connect.connect(anim, "onStop", fini);
|
||
|
connect.connect(anim, "onEnd", fini);
|
||
|
|
||
|
return anim; // dojo/_base/fx.Animation
|
||
|
};
|
||
|
|
||
|
coreFx.wipeOut = function(/*Object*/ args){
|
||
|
// summary:
|
||
|
// Shrink a node to nothing and hide it.
|
||
|
//
|
||
|
// description:
|
||
|
// Returns an animation that will shrink node defined in "args"
|
||
|
// from it's current height to 1px, and then hide it.
|
||
|
//
|
||
|
// args: Object
|
||
|
// A hash-map of standard `dojo.Animation` constructor properties
|
||
|
// (such as easing: node: duration: and so on)
|
||
|
//
|
||
|
// example:
|
||
|
// | fx.wipeOut({ node:"someId" }).play()
|
||
|
|
||
|
var node = args.node = dom.byId(args.node), s = node.style, o;
|
||
|
|
||
|
var anim = baseFx.animateProperty(lang.mixin({
|
||
|
properties: {
|
||
|
height: {
|
||
|
end: 1 // 0 causes IE to display the whole panel
|
||
|
}
|
||
|
}
|
||
|
}, args));
|
||
|
|
||
|
connect.connect(anim, "beforeBegin", function(){
|
||
|
o = s.overflow;
|
||
|
s.overflow = "hidden";
|
||
|
s.display = "";
|
||
|
});
|
||
|
var fini = function(){
|
||
|
s.overflow = o;
|
||
|
s.height = "auto";
|
||
|
s.display = "none";
|
||
|
};
|
||
|
connect.connect(anim, "onStop", fini);
|
||
|
connect.connect(anim, "onEnd", fini);
|
||
|
|
||
|
return anim; // dojo/_base/fx.Animation
|
||
|
};
|
||
|
|
||
|
coreFx.slideTo = function(/*Object*/ args){
|
||
|
// summary:
|
||
|
// Slide a node to a new top/left position
|
||
|
//
|
||
|
// description:
|
||
|
// Returns an animation that will slide "node"
|
||
|
// defined in args Object from its current position to
|
||
|
// the position defined by (args.left, args.top).
|
||
|
//
|
||
|
// args: Object
|
||
|
// A hash-map of standard `dojo.Animation` constructor properties
|
||
|
// (such as easing: node: duration: and so on). Special args members
|
||
|
// are `top` and `left`, which indicate the new position to slide to.
|
||
|
//
|
||
|
// example:
|
||
|
// | .slideTo({ node: node, left:"40", top:"50", units:"px" }).play()
|
||
|
|
||
|
var node = args.node = dom.byId(args.node),
|
||
|
top = null, left = null;
|
||
|
|
||
|
var init = (function(n){
|
||
|
return function(){
|
||
|
var cs = domStyle.getComputedStyle(n);
|
||
|
var pos = cs.position;
|
||
|
top = (pos == 'absolute' ? n.offsetTop : parseInt(cs.top) || 0);
|
||
|
left = (pos == 'absolute' ? n.offsetLeft : parseInt(cs.left) || 0);
|
||
|
if(pos != 'absolute' && pos != 'relative'){
|
||
|
var ret = geom.position(n, true);
|
||
|
top = ret.y;
|
||
|
left = ret.x;
|
||
|
n.style.position="absolute";
|
||
|
n.style.top=top+"px";
|
||
|
n.style.left=left+"px";
|
||
|
}
|
||
|
};
|
||
|
})(node);
|
||
|
init();
|
||
|
|
||
|
var anim = baseFx.animateProperty(lang.mixin({
|
||
|
properties: {
|
||
|
top: args.top || 0,
|
||
|
left: args.left || 0
|
||
|
}
|
||
|
}, args));
|
||
|
connect.connect(anim, "beforeBegin", anim, init);
|
||
|
|
||
|
return anim; // dojo/_base/fx.Animation
|
||
|
};
|
||
|
|
||
|
return coreFx;
|
||
|
});
|