]>
Commit | Line | Data |
---|---|---|
f0cfe83e AD |
1 | define("dojo/fx", [ |
2 | "./_base/lang", | |
3 | "./Evented", | |
4 | "./_base/kernel", | |
5 | "./_base/array", | |
6 | "./_base/connect", | |
7 | "./_base/fx", | |
8 | "./dom", | |
9 | "./dom-style", | |
10 | "./dom-geometry", | |
11 | "./ready", | |
12 | "require" // for context sensitive loading of Toggler | |
13 | ], function(lang, Evented, dojo, arrayUtil, connect, baseFx, dom, domStyle, geom, ready, require){ | |
14 | ||
15 | // module: | |
16 | // dojo/fx | |
17 | ||
18 | // For back-compat, remove in 2.0. | |
19 | if(!dojo.isAsync){ | |
20 | ready(0, function(){ | |
21 | var requires = ["./fx/Toggler"]; | |
22 | require(requires); // use indirection so modules not rolled into a build | |
23 | }); | |
24 | } | |
25 | ||
26 | var coreFx = dojo.fx = { | |
27 | // summary: | |
28 | // Effects library on top of Base animations | |
29 | }; | |
30 | ||
31 | var _baseObj = { | |
32 | _fire: function(evt, args){ | |
33 | if(this[evt]){ | |
34 | this[evt].apply(this, args||[]); | |
35 | } | |
36 | return this; | |
37 | } | |
38 | }; | |
39 | ||
40 | var _chain = function(animations){ | |
41 | this._index = -1; | |
42 | this._animations = animations||[]; | |
43 | this._current = this._onAnimateCtx = this._onEndCtx = null; | |
44 | ||
45 | this.duration = 0; | |
46 | arrayUtil.forEach(this._animations, function(a){ | |
47 | this.duration += a.duration; | |
48 | if(a.delay){ this.duration += a.delay; } | |
49 | }, this); | |
50 | }; | |
51 | _chain.prototype = new Evented(); | |
52 | lang.extend(_chain, { | |
53 | _onAnimate: function(){ | |
54 | this._fire("onAnimate", arguments); | |
55 | }, | |
56 | _onEnd: function(){ | |
57 | connect.disconnect(this._onAnimateCtx); | |
58 | connect.disconnect(this._onEndCtx); | |
59 | this._onAnimateCtx = this._onEndCtx = null; | |
60 | if(this._index + 1 == this._animations.length){ | |
61 | this._fire("onEnd"); | |
62 | }else{ | |
63 | // switch animations | |
64 | this._current = this._animations[++this._index]; | |
65 | this._onAnimateCtx = connect.connect(this._current, "onAnimate", this, "_onAnimate"); | |
66 | this._onEndCtx = connect.connect(this._current, "onEnd", this, "_onEnd"); | |
67 | this._current.play(0, true); | |
68 | } | |
69 | }, | |
70 | play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){ | |
71 | if(!this._current){ this._current = this._animations[this._index = 0]; } | |
72 | if(!gotoStart && this._current.status() == "playing"){ return this; } | |
73 | var beforeBegin = connect.connect(this._current, "beforeBegin", this, function(){ | |
74 | this._fire("beforeBegin"); | |
75 | }), | |
76 | onBegin = connect.connect(this._current, "onBegin", this, function(arg){ | |
77 | this._fire("onBegin", arguments); | |
78 | }), | |
79 | onPlay = connect.connect(this._current, "onPlay", this, function(arg){ | |
80 | this._fire("onPlay", arguments); | |
81 | connect.disconnect(beforeBegin); | |
82 | connect.disconnect(onBegin); | |
83 | connect.disconnect(onPlay); | |
84 | }); | |
85 | if(this._onAnimateCtx){ | |
86 | connect.disconnect(this._onAnimateCtx); | |
87 | } | |
88 | this._onAnimateCtx = connect.connect(this._current, "onAnimate", this, "_onAnimate"); | |
89 | if(this._onEndCtx){ | |
90 | connect.disconnect(this._onEndCtx); | |
91 | } | |
92 | this._onEndCtx = connect.connect(this._current, "onEnd", this, "_onEnd"); | |
93 | this._current.play.apply(this._current, arguments); | |
94 | return this; | |
95 | }, | |
96 | pause: function(){ | |
97 | if(this._current){ | |
98 | var e = connect.connect(this._current, "onPause", this, function(arg){ | |
99 | this._fire("onPause", arguments); | |
100 | connect.disconnect(e); | |
101 | }); | |
102 | this._current.pause(); | |
103 | } | |
104 | return this; | |
105 | }, | |
106 | gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){ | |
107 | this.pause(); | |
108 | var offset = this.duration * percent; | |
109 | this._current = null; | |
110 | arrayUtil.some(this._animations, function(a){ | |
111 | if(a.duration <= offset){ | |
112 | this._current = a; | |
113 | return true; | |
114 | } | |
115 | offset -= a.duration; | |
116 | return false; | |
117 | }); | |
118 | if(this._current){ | |
119 | this._current.gotoPercent(offset / this._current.duration, andPlay); | |
120 | } | |
121 | return this; | |
122 | }, | |
123 | stop: function(/*boolean?*/ gotoEnd){ | |
124 | if(this._current){ | |
125 | if(gotoEnd){ | |
126 | for(; this._index + 1 < this._animations.length; ++this._index){ | |
127 | this._animations[this._index].stop(true); | |
128 | } | |
129 | this._current = this._animations[this._index]; | |
130 | } | |
131 | var e = connect.connect(this._current, "onStop", this, function(arg){ | |
132 | this._fire("onStop", arguments); | |
133 | connect.disconnect(e); | |
134 | }); | |
135 | this._current.stop(); | |
136 | } | |
137 | return this; | |
138 | }, | |
139 | status: function(){ | |
140 | return this._current ? this._current.status() : "stopped"; | |
141 | }, | |
142 | destroy: function(){ | |
143 | if(this._onAnimateCtx){ connect.disconnect(this._onAnimateCtx); } | |
144 | if(this._onEndCtx){ connect.disconnect(this._onEndCtx); } | |
145 | } | |
146 | }); | |
147 | lang.extend(_chain, _baseObj); | |
148 | ||
149 | coreFx.chain = function(/*dojo/_base/fx.Animation[]*/ animations){ | |
150 | // summary: | |
151 | // Chain a list of `dojo.Animation`s to run in sequence | |
152 | // | |
153 | // description: | |
154 | // Return a `dojo.Animation` which will play all passed | |
155 | // `dojo.Animation` instances in sequence, firing its own | |
156 | // synthesized events simulating a single animation. (eg: | |
157 | // onEnd of this animation means the end of the chain, | |
158 | // not the individual animations within) | |
159 | // | |
160 | // example: | |
161 | // Once `node` is faded out, fade in `otherNode` | |
162 | // | fx.chain([ | |
163 | // | dojo.fadeIn({ node:node }), | |
164 | // | dojo.fadeOut({ node:otherNode }) | |
165 | // | ]).play(); | |
166 | // | |
167 | return new _chain(animations); // dojo/_base/fx.Animation | |
168 | }; | |
169 | ||
170 | var _combine = function(animations){ | |
171 | this._animations = animations||[]; | |
172 | this._connects = []; | |
173 | this._finished = 0; | |
174 | ||
175 | this.duration = 0; | |
176 | arrayUtil.forEach(animations, function(a){ | |
177 | var duration = a.duration; | |
178 | if(a.delay){ duration += a.delay; } | |
179 | if(this.duration < duration){ this.duration = duration; } | |
180 | this._connects.push(connect.connect(a, "onEnd", this, "_onEnd")); | |
181 | }, this); | |
182 | ||
183 | this._pseudoAnimation = new baseFx.Animation({curve: [0, 1], duration: this.duration}); | |
184 | var self = this; | |
185 | arrayUtil.forEach(["beforeBegin", "onBegin", "onPlay", "onAnimate", "onPause", "onStop", "onEnd"], | |
186 | function(evt){ | |
187 | self._connects.push(connect.connect(self._pseudoAnimation, evt, | |
188 | function(){ self._fire(evt, arguments); } | |
189 | )); | |
190 | } | |
191 | ); | |
192 | }; | |
193 | lang.extend(_combine, { | |
194 | _doAction: function(action, args){ | |
195 | arrayUtil.forEach(this._animations, function(a){ | |
196 | a[action].apply(a, args); | |
197 | }); | |
198 | return this; | |
199 | }, | |
200 | _onEnd: function(){ | |
201 | if(++this._finished > this._animations.length){ | |
202 | this._fire("onEnd"); | |
203 | } | |
204 | }, | |
205 | _call: function(action, args){ | |
206 | var t = this._pseudoAnimation; | |
207 | t[action].apply(t, args); | |
208 | }, | |
209 | play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){ | |
210 | this._finished = 0; | |
211 | this._doAction("play", arguments); | |
212 | this._call("play", arguments); | |
213 | return this; | |
214 | }, | |
215 | pause: function(){ | |
216 | this._doAction("pause", arguments); | |
217 | this._call("pause", arguments); | |
218 | return this; | |
219 | }, | |
220 | gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){ | |
221 | var ms = this.duration * percent; | |
222 | arrayUtil.forEach(this._animations, function(a){ | |
223 | a.gotoPercent(a.duration < ms ? 1 : (ms / a.duration), andPlay); | |
224 | }); | |
225 | this._call("gotoPercent", arguments); | |
226 | return this; | |
227 | }, | |
228 | stop: function(/*boolean?*/ gotoEnd){ | |
229 | this._doAction("stop", arguments); | |
230 | this._call("stop", arguments); | |
231 | return this; | |
232 | }, | |
233 | status: function(){ | |
234 | return this._pseudoAnimation.status(); | |
235 | }, | |
236 | destroy: function(){ | |
237 | arrayUtil.forEach(this._connects, connect.disconnect); | |
238 | } | |
239 | }); | |
240 | lang.extend(_combine, _baseObj); | |
241 | ||
242 | coreFx.combine = function(/*dojo/_base/fx.Animation[]*/ animations){ | |
243 | // summary: | |
244 | // Combine a list of `dojo.Animation`s to run in parallel | |
245 | // | |
246 | // description: | |
247 | // Combine an array of `dojo.Animation`s to run in parallel, | |
248 | // providing a new `dojo.Animation` instance encompasing each | |
249 | // animation, firing standard animation events. | |
250 | // | |
251 | // example: | |
252 | // Fade out `node` while fading in `otherNode` simultaneously | |
253 | // | fx.combine([ | |
254 | // | dojo.fadeIn({ node:node }), | |
255 | // | dojo.fadeOut({ node:otherNode }) | |
256 | // | ]).play(); | |
257 | // | |
258 | // example: | |
259 | // When the longest animation ends, execute a function: | |
260 | // | var anim = fx.combine([ | |
261 | // | dojo.fadeIn({ node: n, duration:700 }), | |
262 | // | dojo.fadeOut({ node: otherNode, duration: 300 }) | |
263 | // | ]); | |
264 | // | dojo.connect(anim, "onEnd", function(){ | |
265 | // | // overall animation is done. | |
266 | // | }); | |
267 | // | anim.play(); // play the animation | |
268 | // | |
269 | return new _combine(animations); // dojo/_base/fx.Animation | |
270 | }; | |
271 | ||
272 | coreFx.wipeIn = function(/*Object*/ args){ | |
273 | // summary: | |
274 | // Expand a node to it's natural height. | |
275 | // | |
276 | // description: | |
277 | // Returns an animation that will expand the | |
278 | // node defined in 'args' object from it's current height to | |
279 | // it's natural height (with no scrollbar). | |
280 | // Node must have no margin/border/padding. | |
281 | // | |
282 | // args: Object | |
283 | // A hash-map of standard `dojo.Animation` constructor properties | |
284 | // (such as easing: node: duration: and so on) | |
285 | // | |
286 | // example: | |
287 | // | fx.wipeIn({ | |
288 | // | node:"someId" | |
289 | // | }).play() | |
290 | var node = args.node = dom.byId(args.node), s = node.style, o; | |
291 | ||
292 | var anim = baseFx.animateProperty(lang.mixin({ | |
293 | properties: { | |
294 | height: { | |
295 | // wrapped in functions so we wait till the last second to query (in case value has changed) | |
296 | start: function(){ | |
297 | // start at current [computed] height, but use 1px rather than 0 | |
298 | // because 0 causes IE to display the whole panel | |
299 | o = s.overflow; | |
300 | s.overflow = "hidden"; | |
301 | if(s.visibility == "hidden" || s.display == "none"){ | |
302 | s.height = "1px"; | |
303 | s.display = ""; | |
304 | s.visibility = ""; | |
305 | return 1; | |
306 | }else{ | |
307 | var height = domStyle.get(node, "height"); | |
308 | return Math.max(height, 1); | |
309 | } | |
310 | }, | |
311 | end: function(){ | |
312 | return node.scrollHeight; | |
313 | } | |
314 | } | |
315 | } | |
316 | }, args)); | |
317 | ||
318 | var fini = function(){ | |
319 | s.height = "auto"; | |
320 | s.overflow = o; | |
321 | }; | |
322 | connect.connect(anim, "onStop", fini); | |
323 | connect.connect(anim, "onEnd", fini); | |
324 | ||
325 | return anim; // dojo/_base/fx.Animation | |
326 | }; | |
327 | ||
328 | coreFx.wipeOut = function(/*Object*/ args){ | |
329 | // summary: | |
330 | // Shrink a node to nothing and hide it. | |
331 | // | |
332 | // description: | |
333 | // Returns an animation that will shrink node defined in "args" | |
334 | // from it's current height to 1px, and then hide it. | |
335 | // | |
336 | // args: Object | |
337 | // A hash-map of standard `dojo.Animation` constructor properties | |
338 | // (such as easing: node: duration: and so on) | |
339 | // | |
340 | // example: | |
341 | // | fx.wipeOut({ node:"someId" }).play() | |
342 | ||
343 | var node = args.node = dom.byId(args.node), s = node.style, o; | |
344 | ||
345 | var anim = baseFx.animateProperty(lang.mixin({ | |
346 | properties: { | |
347 | height: { | |
348 | end: 1 // 0 causes IE to display the whole panel | |
349 | } | |
350 | } | |
351 | }, args)); | |
352 | ||
353 | connect.connect(anim, "beforeBegin", function(){ | |
354 | o = s.overflow; | |
355 | s.overflow = "hidden"; | |
356 | s.display = ""; | |
357 | }); | |
358 | var fini = function(){ | |
359 | s.overflow = o; | |
360 | s.height = "auto"; | |
361 | s.display = "none"; | |
362 | }; | |
363 | connect.connect(anim, "onStop", fini); | |
364 | connect.connect(anim, "onEnd", fini); | |
365 | ||
366 | return anim; // dojo/_base/fx.Animation | |
367 | }; | |
368 | ||
369 | coreFx.slideTo = function(/*Object*/ args){ | |
370 | // summary: | |
371 | // Slide a node to a new top/left position | |
372 | // | |
373 | // description: | |
374 | // Returns an animation that will slide "node" | |
375 | // defined in args Object from its current position to | |
376 | // the position defined by (args.left, args.top). | |
377 | // | |
378 | // args: Object | |
379 | // A hash-map of standard `dojo.Animation` constructor properties | |
380 | // (such as easing: node: duration: and so on). Special args members | |
381 | // are `top` and `left`, which indicate the new position to slide to. | |
382 | // | |
383 | // example: | |
384 | // | .slideTo({ node: node, left:"40", top:"50", units:"px" }).play() | |
385 | ||
386 | var node = args.node = dom.byId(args.node), | |
387 | top = null, left = null; | |
388 | ||
389 | var init = (function(n){ | |
390 | return function(){ | |
391 | var cs = domStyle.getComputedStyle(n); | |
392 | var pos = cs.position; | |
393 | top = (pos == 'absolute' ? n.offsetTop : parseInt(cs.top) || 0); | |
394 | left = (pos == 'absolute' ? n.offsetLeft : parseInt(cs.left) || 0); | |
395 | if(pos != 'absolute' && pos != 'relative'){ | |
396 | var ret = geom.position(n, true); | |
397 | top = ret.y; | |
398 | left = ret.x; | |
399 | n.style.position="absolute"; | |
400 | n.style.top=top+"px"; | |
401 | n.style.left=left+"px"; | |
402 | } | |
403 | }; | |
404 | })(node); | |
405 | init(); | |
406 | ||
407 | var anim = baseFx.animateProperty(lang.mixin({ | |
408 | properties: { | |
409 | top: args.top || 0, | |
410 | left: args.left || 0 | |
411 | } | |
412 | }, args)); | |
413 | connect.connect(anim, "beforeBegin", anim, init); | |
414 | ||
415 | return anim; // dojo/_base/fx.Animation | |
416 | }; | |
417 | ||
418 | return coreFx; | |
419 | }); |