source: Dev/trunk/src/client/dojox/widget/Rotator.js @ 532

Last change on this file since 532 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

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