text: define([
  	"./_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;
  });
  