You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
444 lines
12 KiB
444 lines
12 KiB
define([ |
|
"./_base/lang", |
|
"./Evented", |
|
"./_base/kernel", |
|
"./_base/array", |
|
"./aspect", |
|
"./_base/fx", |
|
"./dom", |
|
"./dom-style", |
|
"./dom-geometry", |
|
"./ready", |
|
"require" // for context sensitive loading of Toggler |
|
], function(lang, Evented, dojo, arrayUtil, aspect, 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){ |
|
if(a){ |
|
if(typeof a.duration != "undefined"){ |
|
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(){ |
|
this._onAnimateCtx.remove(); |
|
this._onEndCtx.remove(); |
|
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 = aspect.after(this._current, "onAnimate", lang.hitch(this, "_onAnimate"), true); |
|
this._onEndCtx = aspect.after(this._current, "onEnd", lang.hitch(this, "_onEnd"), true); |
|
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 = aspect.after(this._current, "beforeBegin", lang.hitch(this, function(){ |
|
this._fire("beforeBegin"); |
|
}), true), |
|
onBegin = aspect.after(this._current, "onBegin", lang.hitch(this, function(arg){ |
|
this._fire("onBegin", arguments); |
|
}), true), |
|
onPlay = aspect.after(this._current, "onPlay", lang.hitch(this, function(arg){ |
|
this._fire("onPlay", arguments); |
|
beforeBegin.remove(); |
|
onBegin.remove(); |
|
onPlay.remove(); |
|
})); |
|
if(this._onAnimateCtx){ |
|
this._onAnimateCtx.remove(); |
|
} |
|
this._onAnimateCtx = aspect.after(this._current, "onAnimate", lang.hitch(this, "_onAnimate"), true); |
|
if(this._onEndCtx){ |
|
this._onEndCtx.remove(); |
|
} |
|
this._onEndCtx = aspect.after(this._current, "onEnd", lang.hitch(this, "_onEnd"), true); |
|
this._current.play.apply(this._current, arguments); |
|
return this; |
|
}, |
|
pause: function(){ |
|
if(this._current){ |
|
var e = aspect.after(this._current, "onPause", lang.hitch(this, function(arg){ |
|
this._fire("onPause", arguments); |
|
e.remove(); |
|
}), true); |
|
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, index){ |
|
if(offset <= a.duration){ |
|
this._current = a; |
|
this._index = index; |
|
return true; |
|
} |
|
offset -= a.duration; |
|
return false; |
|
}, this); |
|
if(this._current){ |
|
this._current.gotoPercent(offset / this._current.duration); |
|
} |
|
if (andPlay) { this.play(); } |
|
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 = aspect.after(this._current, "onStop", lang.hitch(this, function(arg){ |
|
this._fire("onStop", arguments); |
|
e.remove(); |
|
}), true); |
|
this._current.stop(); |
|
} |
|
return this; |
|
}, |
|
status: function(){ |
|
return this._current ? this._current.status() : "stopped"; |
|
}, |
|
destroy: function(){ |
|
this.stop(); |
|
if(this._onAnimateCtx){ this._onAnimateCtx.remove(); } |
|
if(this._onEndCtx){ this._onEndCtx.remove(); } |
|
} |
|
}); |
|
lang.extend(_chain, _baseObj); |
|
|
|
coreFx.chain = function(/*dojo/_base/fx.Animation[]*/ animations){ |
|
// summary: |
|
// Chain a list of `dojo/_base/fx.Animation`s to run in sequence |
|
// |
|
// description: |
|
// Return a `dojo/_base/fx.Animation` which will play all passed |
|
// `dojo/_base/fx.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` |
|
// | require(["dojo/fx"], function(fx){ |
|
// | fx.chain([ |
|
// | fx.fadeIn({ node:node }), |
|
// | fx.fadeOut({ node:otherNode }) |
|
// | ]).play(); |
|
// | }); |
|
// |
|
return new _chain(lang.isArray(animations) ? animations : Array.prototype.slice.call(animations, 0)); // 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(aspect.after(a, "onEnd", lang.hitch(this, "_onEnd"), true)); |
|
}, 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(aspect.after(self._pseudoAnimation, evt, |
|
function(){ self._fire(evt, arguments); }, |
|
true)); |
|
} |
|
); |
|
}; |
|
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(){ |
|
this.stop(); |
|
arrayUtil.forEach(this._connects, function(handle){ |
|
handle.remove(); |
|
}); |
|
} |
|
}); |
|
lang.extend(_combine, _baseObj); |
|
|
|
coreFx.combine = function(/*dojo/_base/fx.Animation[]*/ animations){ |
|
// summary: |
|
// Combine a list of `dojo/_base/fx.Animation`s to run in parallel |
|
// |
|
// description: |
|
// Combine an array of `dojo/_base/fx.Animation`s or N |
|
// `dojo/_base/fx.Animation`s to run in parallel, providing |
|
// a new `dojo/_base/fx.Animation` instance encompassing each |
|
// animation, firing standard animation events. |
|
// |
|
// example: |
|
// Fade out `node` while fading in `otherNode` simultaneously |
|
// | require(["dojo/fx"], function(fx){ |
|
// | fx.combine([ |
|
// | fx.fadeIn({ node:node }), |
|
// | fx.fadeOut({ node:otherNode }) |
|
// | ]).play(); |
|
// | }); |
|
// |
|
// example: |
|
// When the longest animation ends, execute a function: |
|
// | require(["dojo/fx"], function(fx){ |
|
// | var anim = fx.combine([ |
|
// | fx.fadeIn({ node: n, duration:700 }), |
|
// | fx.fadeOut({ node: otherNode, duration: 300 }) |
|
// | ]); |
|
// | aspect.after(anim, "onEnd", function(){ |
|
// | // overall animation is done. |
|
// | }, true); |
|
// | anim.play(); // play the animation |
|
// | }); |
|
// |
|
return new _combine(lang.isArray(animations) ? animations : Array.prototype.slice.call(animations, 0)); // 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/_base/fx.Animation` constructor properties |
|
// (such as easing: node: duration: and so on) |
|
// |
|
// example: |
|
// | require(["dojo/fx"], function(fx){ |
|
// | 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; |
|
}; |
|
aspect.after(anim, "onStop", fini, true); |
|
aspect.after(anim, "onEnd", fini, true); |
|
|
|
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/_base/fx.Animation` constructor properties |
|
// (such as easing: node: duration: and so on) |
|
// |
|
// example: |
|
// | require(["dojo/fx"], function(fx){ |
|
// | 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)); |
|
|
|
aspect.after(anim, "beforeBegin", function(){ |
|
o = s.overflow; |
|
s.overflow = "hidden"; |
|
s.display = ""; |
|
}, true); |
|
var fini = function(){ |
|
s.overflow = o; |
|
s.height = "auto"; |
|
s.display = "none"; |
|
}; |
|
aspect.after(anim, "onStop", fini, true); |
|
aspect.after(anim, "onEnd", fini, true); |
|
|
|
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/_base/fx.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)); |
|
aspect.after(anim, "beforeBegin", init, true); |
|
|
|
return anim; // dojo/_base/fx.Animation |
|
}; |
|
|
|
return coreFx; |
|
});
|
|
|