source: Dev/branches/rest-dojo-ui/client/dojo/fx.js @ 274

Last change on this file since 274 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

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