UNPKG

dojo

Version:

Dojo core is a powerful, lightweight library that makes common tasks quicker and easier. Animate elements, manipulate the DOM, and query with easy CSS syntax, all without sacrificing performance.

445 lines (416 loc) 12.8 kB
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; });