source: Dev/branches/rest-dojo-ui/client/dojox/widget/Rotator.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: 10.5 KB
Line 
1dojo.provide("dojox.widget.Rotator");
2dojo.require("dojo.parser");
3
4(function(d){
5
6        // build friendly strings
7        var _defaultTransition = "dojox.widget.rotator.swap", // please do NOT change
8                _defaultTransitionDuration = 500,
9                _displayStr = "display",
10                _noneStr = "none",
11                _zIndex = "zIndex";
12
13        d.declare("dojox.widget.Rotator", null, {
14                //      summary:
15                //              A widget for rotating through child nodes using transitions.
16                //
17                //      description:
18                //              A small, fast, extensible, awesome rotator that cycles, with transitions,
19                //              through panes (child nodes) displaying only one at a time and ties into
20                //              controllers used to change state.
21                //
22                //              The Rotator does not rely on dijit.  It is designed to be as lightweight
23                //              as possible.  Controllers and transitions have been externalized
24                //              so builds can be as optimized with only the components you want to use.
25                //
26                //              For best results, each rotator pane should be the same height and width as
27                //              the Rotator container node and consider setting overflow to hidden.
28                //              While the Rotator will accept any DOM node for a rotator pane, a block
29                //              element or element with display:block is recommended.
30                //
31                //              Note: When the Rotator begins, it does not transition the first pane.
32                //
33                //      subscribed topics:
34                //              [id]/rotator/control - Controls the Rotator
35                //                      Parameters:
36                //                              /*string*/ action        - The name of a method of the Rotator to run
37                //                              /*anything?*/ args       - One or more arguments to pass to the action
38                //
39                //      published topics:
40                //              [id]/rotator/update - Notifies controllers that a pane or state has changed.
41                //                      Parameters:
42                //                              /*string*/ type          - the type of notification
43                //                              /*dojox.widget.Rotator*/ rotator
44                //                                                                               - the rotator instance
45                //                              /*object?*/ params               - params
46                //
47                //      declarative dojo/method events (per pane):
48                //              onBeforeIn  - Fired before the transition in starts.
49                //              onAfterIn   - Fired after the transition in ends.
50                //              onBeforeOut - Fired before the transition out starts.
51                //              onAfterOut  - Fired after the transition out ends.
52                //
53                //      example:
54                //      |       <div dojoType="dojox.widget.Rotator">
55                //      |               <div>Pane 1!</div>
56                //      |               <div>Pane 2!</div>
57                //      |       </div>
58                //
59                //      example:
60                //      |       <script type="text/javascript">
61                //      |               dojo.require("dojox.widget.rotator.Fade");
62                //      |       </script>
63                //      |       <div dojoType="dojox.widget.Rotator" transition="dojox.widget.rotator.crossFade">
64                //      |               <div>Pane 1!</div>
65                //      |               <div>Pane 2!</div>
66                //      |       </div>
67
68                //      transition: string
69                //              The name of a function that is passed two panes nodes and a duration,
70                //              then returns a dojo.Animation object. The default value is
71                //              "dojox.widget.rotator.swap".
72                transition: _defaultTransition,
73
74                //      transitionParams: string
75                //              Parameters for the transition. The string is read in and eval'd as an
76                //              object.  If the duration is absent, the default value will be used.
77                transitionParams: "duration:" + _defaultTransitionDuration,
78
79                //      panes: array
80                //              Array of panes to be created in the Rotator. Each array element
81                //              will be passed as attributes to a dojo.create() call.
82                panes: null,
83
84                constructor: function(/*Object*/params, /*DomNode|string*/node){
85                        //      summary:
86                        //              Initializes the panes and events.
87                        d.mixin(this, params);
88
89                        var _t = this,
90                                t = _t.transition,
91                                tt = _t._transitions = {},
92                                idm = _t._idMap = {},
93                                tp = _t.transitionParams = eval("({ " + _t.transitionParams + " })"),
94                                node = _t._domNode = dojo.byId(node),
95                                cb = _t._domNodeContentBox = d.contentBox(node), // we are going to assume the rotator will not be changing size
96
97                                // default styles to apply to all the container node and rotator's panes
98                                p = {
99                                        left: 0,
100                                        top: 0
101                                },
102
103                                warn = function(bt, dt){
104                                        console.warn(_t.declaredClass, ' - Unable to find transition "', bt, '", defaulting to "', dt, '".');
105                                };
106
107                        // if we don't have an id, then generate one
108                        _t.id = node.id || (new Date()).getTime();
109
110                        // force the rotator DOM node to a relative position and attach the container node to it
111                        if(d.style(node, "position") == "static"){
112                                d.style(node, "position", "relative");
113                        }
114
115                        // create our object for caching transition objects
116                        tt[t] = d.getObject(t);
117                        if(!tt[t]){
118                                warn(t, _defaultTransition);
119                                tt[_t.transition = _defaultTransition] = d.getObject(_defaultTransition);
120                        }
121
122                        // clean up the transition params
123                        if(!tp.duration){
124                                tp.duration = _defaultTransitionDuration;
125                        }
126
127                        // if there are any panes being passed in, add them to this node
128                        d.forEach(_t.panes, function(p){
129                                d.create("div", p, node);
130                        });
131
132                        // zero out our panes array to store the real pane instance
133                        var pp = _t.panes = [];
134
135                        // find and initialize the panes
136                        d.query(">", node).forEach(function(n, i){
137                                var q = { node: n, idx: i, params: d.mixin({}, tp, eval("({ " + (d.attr(n, "transitionParams") || "") + " })")) },
138                                        r = q.trans = d.attr(n, "transition") || _t.transition;
139
140                                // cache each pane's title, duration, and waitForEvent attributes
141                                d.forEach(["id", "title", "duration", "waitForEvent"], function(a){
142                                        q[a] = d.attr(n, a);
143                                });
144
145                                if(q.id){
146                                        idm[q.id] = i;
147                                }
148
149                                // cache the transition function
150                                if(!tt[r] && !(tt[r] = d.getObject(r))){
151                                        warn(r, q.trans = _t.transition);
152                                }
153
154                                p.position = "absolute";
155                                p.display = _noneStr;
156
157                                // find the selected pane and initialize styles
158                                if(_t.idx == null || d.attr(n, "selected")){
159                                        if(_t.idx != null){
160                                                d.style(pp[_t.idx].node, _displayStr, _noneStr);
161                                        }
162                                        _t.idx = i;
163                                        p.display = "";
164                                }
165                                d.style(n, p);
166
167                                // check for any declarative script blocks
168                                d.query("> script[type^='dojo/method']", n).orphan().forEach(function(s){
169                                        var e = d.attr(s, "event");
170                                        if(e){
171                                                q[e] = d.parser._functionFromScript(s);
172                                        }
173                                });
174
175                                // add this pane to the array of panes
176                                pp.push(q);
177                        });
178
179                        _t._controlSub = d.subscribe(_t.id + "/rotator/control", _t, "control");
180                },
181
182                destroy: function(){
183                        //      summary:
184                        //              Destroys the Rotator and its DOM node.
185                        d.forEach([this._controlSub, this.wfe], d.unsubscribe);
186                        d.destroy(this._domNode);
187                },
188
189                next: function(){
190                        //      summary:
191                        //              Transitions the Rotator to the next pane.
192                        return this.go(this.idx + 1);
193                },
194
195                prev: function(){
196                        //      summary:
197                        //              Transitions the Rotator to the previous pane.
198                        return this.go(this.idx - 1);
199                },
200
201                go: function(/*int|string?*/p){
202                        //      summary:
203                        //              Transitions the Rotator to the specified pane index.
204                        var _t = this,
205                                i = _t.idx,
206                                pp = _t.panes,
207                                len = pp.length,
208                                idm = _t._idMap[p];
209
210                        // we gotta move on, so if the current pane is waiting for an event, just
211                        // ignore it and clean up
212                        _t._resetWaitForEvent();
213
214                        // determine the next index and set it to idx for the next go to
215                        p = idm != null ? idm : (p || 0);
216                        p = p < len ? (p < 0 ? len-1 : p) : 0;
217
218                        // if we're already on the requested pane or still transitioning, then return
219                        if(p == i || _t.anim){
220                                return null;
221                        }
222
223                        // get the current and next panes
224                        var current = pp[i],
225                                next = pp[p];
226
227                        // adjust the zIndexes so our animations look good... this must be done before
228                        // the animation is created so the animation could override it if necessary
229                        d.style(current.node, _zIndex, 2);
230                        d.style(next.node, _zIndex, 1);
231
232                        // info object passed to animations and onIn/Out events
233                        var info = {
234                                        current: current,
235                                        next: next,
236                                        rotator: _t
237                                },
238
239                                // get the transition
240                                anim = _t.anim = _t._transitions[next.trans](d.mixin({
241                                        rotatorBox: _t._domNodeContentBox
242                                }, info, next.params));
243
244                        if(anim){
245                                // create the deferred that we'll be returning
246                                var def = new d.Deferred(),
247                                        ev = next.waitForEvent,
248
249                                        h = d.connect(anim, "onEnd", function(){
250                                                // reset the node styles
251                                                d.style(current.node, {
252                                                        display: _noneStr,
253                                                        left: 0,
254                                                        opacity: 1,
255                                                        top: 0,
256                                                        zIndex: 0
257                                                });
258
259                                                d.disconnect(h);
260                                                _t.anim = null;
261                                                _t.idx = p;
262
263                                                // fire end events
264                                                if(current.onAfterOut){ current.onAfterOut(info); }
265                                                if(next.onAfterIn){ next.onAfterIn(info); }
266
267                                                _t.onUpdate("onAfterTransition");
268
269                                                if(!ev){
270                                                        // if there is a previous waitForEvent, then we need to make
271                                                        // sure it gets unsubscribed
272                                                        _t._resetWaitForEvent();
273
274                                                        // animation is all done, fire the deferred callback.
275                                                        def.callback();
276                                                }
277                                        });
278
279                                // if we're waiting for an event, subscribe to it so we know when to continue
280                                _t.wfe = ev ? d.subscribe(ev, function(){
281                                        _t._resetWaitForEvent();
282                                        def.callback(true);
283                                }) : null;
284
285                                _t.onUpdate("onBeforeTransition");
286
287                                // fire start events
288                                if(current.onBeforeOut){ current.onBeforeOut(info); }
289                                if(next.onBeforeIn){ next.onBeforeIn(info); }
290
291                                // play the animation
292                                anim.play();
293
294                                // return the deferred
295                                return def; /*Deferred*/
296                        }
297                },
298
299                onUpdate: function(/*string*/type, /*object?*/params){
300                        //      summary:
301                        //              Send a notification to all controllers with the state of the rotator.
302                        d.publish(this.id + "/rotator/update", [type, this, params || {}]);
303                },
304
305                _resetWaitForEvent: function(){
306                        //      summary:
307                        //              If there is a waitForEvent pending, kill it.
308                        if(this.wfe){
309                                d.unsubscribe(this.wfe);
310                                this.wfe = null;
311                        }
312                },
313
314                control: function(/*string*/action){
315                        //      summary:
316                        //              Dispatches an action, first to this engine, then to the Rotator.
317                        var args = d._toArray(arguments),
318                                _t = this;
319                        args.shift();
320
321                        _t._resetWaitForEvent();
322
323                        if(_t[action]){
324                                // action exists, so call it and fire deferred if applicable
325                                var def = _t[action].apply(_t, args);
326                                if(def){
327                                        def.addCallback(function(){
328                                                _t.onUpdate(action);
329                                        });
330                                }
331
332                                // since this action was triggered by a controller, we assume this was a
333                                // manual action, so check if we should pause
334                                _t.onManualChange(action);
335                        }else{
336                                console.warn(_t.declaredClass, ' - Unsupported action "', action, '".');
337                        }
338                },
339
340                resize: function(/*int*/width, /*int*/height){
341                        var b = this._domNodeContentBox = { w: width, h: height };
342                        d.contentBox(this._domNode, b);
343                        d.forEach(this.panes, function(p){ d.contentBox(p.node, b); });
344                },
345
346                onManualChange: function(){
347                        //      summary:
348                        //              Stub function that can be overriden or connected to.
349                }
350        });
351
352        d.setObject(_defaultTransition, function(/*Object*/args){
353                //      summary:
354                //              The default rotator transition which swaps two panes.
355                return new d._Animation({ // dojo.Animation
356                        play: function(){
357                                d.style(args.current.node, _displayStr, _noneStr);
358                                d.style(args.next.node, _displayStr, "");
359                                this._fire("onEnd");
360                        }
361                });
362        });
363
364})(dojo);
Note: See TracBrowser for help on using the repository browser.