1 | define(["dojo/_base/declare","dojo/_base/html","dojo/_base/connect","dojo/_base/lang","dojo/_base/array", |
---|
2 | "dojo/_base/fx","dojo/fx","dijit/_base/manager","dijit/layout/StackContainer","dijit/layout/StackController","dijit/_Widget", |
---|
3 | "dijit/_Templated","dijit/_Contained" |
---|
4 | ],function(declare,html,connect,lang,array,baseFx,coreFx,manager, |
---|
5 | StackContainer,StackController,Widget,Templated,Contained){ |
---|
6 | |
---|
7 | var RotatorContainer = declare("dojox.layout.RotatorContainer",[StackContainer, Templated], { |
---|
8 | // summary: |
---|
9 | // Extends a StackContainer to automatically transition between children |
---|
10 | // and display navigation in the form of tabs or a pager. |
---|
11 | // |
---|
12 | // description: |
---|
13 | // The RotatorContainer cycles through the children with a transition. |
---|
14 | // |
---|
15 | // ####published topics: |
---|
16 | // |
---|
17 | // [widgetId]-update - Notifies pager(s) that a child has changed. |
---|
18 | // Parameters: |
---|
19 | // |
---|
20 | // - /*boolean*/ playing - true if playing, false if paused |
---|
21 | // - /*int*/ current - current selected child |
---|
22 | // - /*int*/ total - total number of children |
---|
23 | // |
---|
24 | // example: |
---|
25 | // | <div dojoType="dojox.layout.RotatorContainer" id="myRotator" showTabs="true" autoStart="true" transitionDelay="5000"> |
---|
26 | // | <div id="pane1" dojoType="dijit.layout.ContentPane" title="1"> |
---|
27 | // | Pane 1! |
---|
28 | // | </div> |
---|
29 | // | <div id="pane2" dojoType="dijit.layout.ContentPane" title="2"> |
---|
30 | // | Pane 2! |
---|
31 | // | </div> |
---|
32 | // | <div id="pane3" dojoType="dijit.layout.ContentPane" title="3" transitionDelay="10000"> |
---|
33 | // | Pane 3 with overrided transitionDelay! |
---|
34 | // | </div> |
---|
35 | // | </div> |
---|
36 | |
---|
37 | templateString: '<div class="dojoxRotatorContainer"><div dojoAttachPoint="tabNode"></div><div class="dojoxRotatorPager" dojoAttachPoint="pagerNode"></div><div class="dojoxRotatorContent" dojoAttachPoint="containerNode"></div></div>', |
---|
38 | |
---|
39 | // showTabs: Boolean |
---|
40 | // Sets the display of the tabs. The tabs are actually a StackController. |
---|
41 | // The child's title is used for the tab's label. |
---|
42 | showTabs: true, |
---|
43 | |
---|
44 | // transitionDelay: int |
---|
45 | // The delay in milliseconds before transitioning to the next child. |
---|
46 | transitionDelay: 5000, |
---|
47 | |
---|
48 | // transition: String |
---|
49 | // The type of transition to perform when switching children. |
---|
50 | // A null transition will transition instantly. |
---|
51 | transition: "fade", |
---|
52 | |
---|
53 | // transitionDuration: int |
---|
54 | // The duration of the transition in milliseconds. |
---|
55 | transitionDuration: 1000, |
---|
56 | |
---|
57 | // autoStart: Boolean |
---|
58 | // Starts the timer to transition children upon creation. |
---|
59 | autoStart: true, |
---|
60 | |
---|
61 | // suspendOnHover: Boolean |
---|
62 | // Pause the rotator when the mouse hovers over it. |
---|
63 | suspendOnHover: false, |
---|
64 | |
---|
65 | // pauseOnManualChange: Boolean |
---|
66 | // Pause the rotator when the tab is changed or the pager's next/previous |
---|
67 | // buttons are clicked. |
---|
68 | pauseOnManualChange: null, |
---|
69 | |
---|
70 | // reverse: Boolean |
---|
71 | // Causes the rotator to rotate in reverse order. |
---|
72 | reverse: false, |
---|
73 | |
---|
74 | // pagerId: String |
---|
75 | // ID the pager widget. |
---|
76 | pagerId: "", |
---|
77 | |
---|
78 | // cycles: int |
---|
79 | // Number of cycles before pausing. |
---|
80 | cycles: -1, |
---|
81 | |
---|
82 | // pagerClass: String |
---|
83 | // The declared Class of the Pager used for this Widget |
---|
84 | pagerClass: "dojox.layout.RotatorPager", |
---|
85 | |
---|
86 | postCreate: function(){ |
---|
87 | // summary: |
---|
88 | // Initializes the DOM nodes, tabs, and transition stuff. |
---|
89 | this.inherited(arguments); |
---|
90 | |
---|
91 | // force this DOM node to a relative position and make sure the children are absolute positioned |
---|
92 | html.style(this.domNode, "position", "relative"); |
---|
93 | |
---|
94 | // validate the cycles counter |
---|
95 | if(this.cycles-0 == this.cycles && this.cycles != -1){ |
---|
96 | // we need to add 1 because we decrement cycles before the animation starts |
---|
97 | this.cycles++; |
---|
98 | }else{ |
---|
99 | this.cycles = -1; |
---|
100 | } |
---|
101 | |
---|
102 | // if they didn't specify the pauseOnManualChange, then we want it to be the opposite of |
---|
103 | // the suspendOnHover since it doesn't make sense to do both, unless you really want to |
---|
104 | if(this.pauseOnManualChange === null){ |
---|
105 | this.pauseOnManualChange = !this.suspendOnHover; |
---|
106 | } |
---|
107 | |
---|
108 | // create the stack controller if we are using tabs |
---|
109 | var id = this.id || "rotator"+(new Date()).getTime(), |
---|
110 | sc = new StackController({ containerId:id }, this.tabNode); |
---|
111 | this.tabNode = sc.domNode; |
---|
112 | this._stackController = sc; |
---|
113 | html.style(this.tabNode, "display", this.showTabs ? "" : "none"); |
---|
114 | |
---|
115 | // if the controller's tabs are clicked, check if we should pause and reset the cycle counter |
---|
116 | this.connect(sc, "onButtonClick","_manualChange"); |
---|
117 | |
---|
118 | // set up our topic listeners |
---|
119 | this._subscriptions = [ |
---|
120 | connect.subscribe(this.id+"-cycle", this, "_cycle"), |
---|
121 | connect.subscribe(this.id+"-state", this, "_state") |
---|
122 | ]; |
---|
123 | |
---|
124 | // make sure the transition duration isn't less than the transition delay |
---|
125 | var d = Math.round(this.transitionDelay * 0.75); |
---|
126 | if(d < this.transitionDuration){ |
---|
127 | this.transitionDuration = d; |
---|
128 | } |
---|
129 | |
---|
130 | // wire up the mouse hover events |
---|
131 | if(this.suspendOnHover){ |
---|
132 | this.connect(this.domNode, "onmouseover", "_onMouseOver"); |
---|
133 | this.connect(this.domNode, "onmouseout", "_onMouseOut"); |
---|
134 | } |
---|
135 | }, |
---|
136 | |
---|
137 | startup: function(){ |
---|
138 | // summary: |
---|
139 | // Initializes the pagers. |
---|
140 | if(this._started){ return; } |
---|
141 | |
---|
142 | // check if the pager is defined within the rotator container |
---|
143 | var c = this.getChildren(); |
---|
144 | for(var i=0, len=c.length; i<len; i++){ |
---|
145 | if(c[i].declaredClass == this.pagerClass){ |
---|
146 | this.pagerNode.appendChild(c[i].domNode); |
---|
147 | break; |
---|
148 | } |
---|
149 | } |
---|
150 | |
---|
151 | // process the child widgets |
---|
152 | this.inherited(arguments); |
---|
153 | |
---|
154 | // check if we should start automatically |
---|
155 | if(this.autoStart){ |
---|
156 | // start playing |
---|
157 | setTimeout(lang.hitch(this, "_play"), 10); |
---|
158 | }else{ |
---|
159 | // update the pagers with the initial state |
---|
160 | this._updatePager(); |
---|
161 | } |
---|
162 | }, |
---|
163 | |
---|
164 | destroy: function(){ |
---|
165 | // summary: |
---|
166 | // Unsubscribe to all of our topics |
---|
167 | array.forEach(this._subscriptions, connect.unsubscribe); |
---|
168 | this.inherited(arguments); |
---|
169 | }, |
---|
170 | |
---|
171 | _setShowTabsAttr: function(/*anything*/value){ |
---|
172 | this.showTabs = value; |
---|
173 | html.style(this.tabNode, "display", value ? "" : "none"); |
---|
174 | }, |
---|
175 | |
---|
176 | _updatePager: function(){ |
---|
177 | // summary: |
---|
178 | // Notify the pager's current and total numbers. |
---|
179 | var c = this.getChildren(); |
---|
180 | connect.publish(this.id+"-update", [this._playing, array.indexOf(c, this.selectedChildWidget)+1, c.length]); |
---|
181 | }, |
---|
182 | |
---|
183 | _onMouseOver: function(){ |
---|
184 | // summary: |
---|
185 | // Triggered when the mouse is moved over the rotator container. |
---|
186 | |
---|
187 | // temporarily suspend the cycling, but don't officially pause it |
---|
188 | this._resetTimer(); |
---|
189 | this._over = true; |
---|
190 | }, |
---|
191 | |
---|
192 | _onMouseOut: function(){ |
---|
193 | // summary: |
---|
194 | // Triggered when the mouse is moved off the rotator container. |
---|
195 | this._over = false; |
---|
196 | |
---|
197 | // if we were playing, resume playback in 200ms |
---|
198 | // we need to wait because we may be moused over again right away |
---|
199 | if(this._playing){ |
---|
200 | clearTimeout(this._timer); |
---|
201 | this._timer = setTimeout(lang.hitch(this, "_play", true), 200); |
---|
202 | } |
---|
203 | }, |
---|
204 | |
---|
205 | _resetTimer: function(){ |
---|
206 | // summary: |
---|
207 | // Resets the timer used to start the next transition. |
---|
208 | clearTimeout(this._timer); |
---|
209 | this._timer = null; |
---|
210 | }, |
---|
211 | |
---|
212 | _cycle: function(/*boolean or int*/next){ |
---|
213 | // summary: |
---|
214 | // Cycles to the next/previous child. |
---|
215 | |
---|
216 | // if next is an int, then _cycle() was called via a timer |
---|
217 | // if next is a boolean, then _cycle() was called via the next/prev buttons, stop playing and reset cycles |
---|
218 | if(next instanceof Boolean || typeof next == "boolean"){ |
---|
219 | this._manualChange(); |
---|
220 | } |
---|
221 | |
---|
222 | var c = this.getChildren(), |
---|
223 | len = c.length, |
---|
224 | i = array.indexOf(c, this.selectedChildWidget) + (next === false || (next !== true && this.reverse) ? -1 : 1); |
---|
225 | this.selectChild(c[(i < len ? (i < 0 ? len-1 : i) : 0)]); |
---|
226 | this._updatePager(); |
---|
227 | }, |
---|
228 | |
---|
229 | _manualChange: function(){ |
---|
230 | // summary: |
---|
231 | // This function is only called when a manual change occurs in which |
---|
232 | // case we may need to stop playing and we need to reset the cycle counter |
---|
233 | if(this.pauseOnManualChange){ |
---|
234 | this._playing = false; |
---|
235 | } |
---|
236 | this.cycles = -1; |
---|
237 | }, |
---|
238 | |
---|
239 | _play: function(skip){ |
---|
240 | // summary: |
---|
241 | // Schedules the next transition. |
---|
242 | this._playing = true; |
---|
243 | this._resetTimer(); |
---|
244 | if(skip !== true && this.cycles>0){ |
---|
245 | this.cycles--; |
---|
246 | } |
---|
247 | if(this.cycles==0){ |
---|
248 | this._pause(); |
---|
249 | }else if((!this.suspendOnHover || !this._over) && this.transitionDelay){ |
---|
250 | // check if current pane has a delay |
---|
251 | this._timer = setTimeout(lang.hitch(this, "_cycle"), this.selectedChildWidget.domNode.getAttribute("transitionDelay") || this.transitionDelay); |
---|
252 | } |
---|
253 | this._updatePager(); |
---|
254 | }, |
---|
255 | |
---|
256 | _pause: function(){ |
---|
257 | // summary: |
---|
258 | // Clears the transition timer and pauses the rotator. |
---|
259 | this._playing = false; |
---|
260 | this._resetTimer(); |
---|
261 | }, |
---|
262 | |
---|
263 | _state: function(playing){ |
---|
264 | // summary: |
---|
265 | // Fired when the play/pause pager button is toggled. |
---|
266 | if(playing){ |
---|
267 | // since we were manually changed, disable the cycle counter |
---|
268 | this.cycles = -1; |
---|
269 | this._play(); |
---|
270 | }else{ |
---|
271 | this._pause(); |
---|
272 | } |
---|
273 | }, |
---|
274 | |
---|
275 | _transition: function(/*dijit._Widget*/ next, /*dijit._Widget*/ prev){ |
---|
276 | // summary: |
---|
277 | // Dispatches the appropriate transition. |
---|
278 | this._resetTimer(); |
---|
279 | |
---|
280 | // check if we have anything to transition |
---|
281 | if(prev && this.transitionDuration){ |
---|
282 | switch(this.transition){ |
---|
283 | case "fade": this._fade(next, prev); return; |
---|
284 | } |
---|
285 | } |
---|
286 | |
---|
287 | this._transitionEnd(); |
---|
288 | this.inherited(arguments); |
---|
289 | }, |
---|
290 | |
---|
291 | _transitionEnd: function(){ |
---|
292 | if(this._playing){ |
---|
293 | this._play(); |
---|
294 | }else{ |
---|
295 | this._updatePager(); |
---|
296 | } |
---|
297 | }, |
---|
298 | |
---|
299 | _fade: function(/*dijit._Widget*/ next, /*dijit._Widget*/ prev){ |
---|
300 | // summary: |
---|
301 | // Crossfades two children. |
---|
302 | this._styleNode(prev.domNode, 1, 1); |
---|
303 | this._styleNode(next.domNode, 0, 2); |
---|
304 | |
---|
305 | // show the next child and make sure it's sized properly |
---|
306 | this._showChild(next); |
---|
307 | if(this.doLayout && next.resize){ |
---|
308 | next.resize(this._containerContentBox || this._contentBox); |
---|
309 | } |
---|
310 | |
---|
311 | // create the crossfade animation |
---|
312 | var args = { duration:this.transitionDuration }, |
---|
313 | anim = coreFx.combine([ |
---|
314 | baseFx["fadeOut"](lang.mixin({node:prev.domNode}, args)), |
---|
315 | baseFx["fadeIn"](lang.mixin({node:next.domNode}, args)) |
---|
316 | ]); |
---|
317 | |
---|
318 | this.connect(anim, "onEnd", lang.hitch(this,function(){ |
---|
319 | this._hideChild(prev); |
---|
320 | this._transitionEnd(); |
---|
321 | })); |
---|
322 | |
---|
323 | anim.play(); |
---|
324 | }, |
---|
325 | |
---|
326 | _styleNode: function(/*DomNode*/node, /*number*/opacity, /*int*/zIndex){ |
---|
327 | // summary: |
---|
328 | // Helper function to style the children. |
---|
329 | html.style(node, "opacity", opacity); |
---|
330 | html.style(node, "zIndex", zIndex); |
---|
331 | html.style(node, "position", "absolute"); |
---|
332 | } |
---|
333 | }); |
---|
334 | |
---|
335 | declare("dojox.layout.RotatorPager", [Widget, Templated, Contained], { |
---|
336 | // summary: |
---|
337 | // Defines controls used to manipulate a RotatorContainer |
---|
338 | // |
---|
339 | // description: |
---|
340 | // A pager can be defined one of two ways: |
---|
341 | // |
---|
342 | // - Externally of the RotatorContainer's template and tell the |
---|
343 | // RotatorPager the rotatorId of the RotatorContainer |
---|
344 | // - As a direct descendant of the RotatorContainer (i.e. inside the |
---|
345 | // RotatorContainer's template) |
---|
346 | // |
---|
347 | // The pager can contain the following components: |
---|
348 | // |
---|
349 | // - Previous button |
---|
350 | // - Must be a dijit.form.Button |
---|
351 | // - dojoAttachPoint must be named "previous" |
---|
352 | // - Next button |
---|
353 | // - Must be a dijit.form.Button |
---|
354 | // - dojoAttachPoint must be named "next" |
---|
355 | // - Play/Pause toggle button |
---|
356 | // - Must be a dijit.form.ToggleButton |
---|
357 | // - dojoAttachPoint must be named "playPause" |
---|
358 | // - Use iconClass to specify toggled state |
---|
359 | // - Current child # |
---|
360 | // - dojoAttachPoint must be named "current" |
---|
361 | // - Total # of children |
---|
362 | // - dojoAttachPoint must be named "total" |
---|
363 | // |
---|
364 | // You can choose to exclude specific controls as well as add elements |
---|
365 | // for styling. |
---|
366 | // |
---|
367 | // Should you need a pager, but don't want to use Dijit buttons, you can |
---|
368 | // write your own pager widget and just wire it into the topics. The |
---|
369 | // topic names are prefixed with the widget ID of the RotatorContainer. |
---|
370 | // Notifications are received from and sent to the RotatorContainer as |
---|
371 | // well as other RotatorPagers. |
---|
372 | // |
---|
373 | // ####published topics: |
---|
374 | // |
---|
375 | // [widgetId]-cycle - Notify that the next or previous button was pressed. |
---|
376 | // Parameters: |
---|
377 | // |
---|
378 | // - /*boolean*/ next - true if next, false if previous |
---|
379 | // |
---|
380 | // [widgetId]-state - Notify that the play/pause button was toggled. |
---|
381 | // Parameters: |
---|
382 | // |
---|
383 | // - /*boolean*/ playing - true if playing, false if paused |
---|
384 | // |
---|
385 | // example: |
---|
386 | // A pager with the current/total children and previous/next buttons. |
---|
387 | // | <div dojoType="dojox.layout.RotatorPager" rotatorId="myRotator"> |
---|
388 | // | <button dojoType="dijit.form.Button" dojoAttachPoint="previous">Prev</button> |
---|
389 | // | <span dojoAttachPoint="current"></span> / <span dojoAttachPoint="total"></span> |
---|
390 | // | <button dojoType="dijit.form.Button" dojoAttachPoint="next">Next</button> |
---|
391 | // | </div> |
---|
392 | // |
---|
393 | // example: |
---|
394 | // A pager with only a play/pause toggle button. |
---|
395 | // | <div dojoType="dojox.layout.RotatorPager" rotatorId="myRotator"> |
---|
396 | // | <button dojoType="dijit.form.ToggleButton" dojoAttachPoint="playPause"></button> |
---|
397 | // | </div> |
---|
398 | // |
---|
399 | // example: |
---|
400 | // A pager styled with iconClass. |
---|
401 | // | <div dojoType="dojox.layout.RotatorPager" class="rotatorIcons" rotatorId="myRotator"> |
---|
402 | // | <button dojoType="dijit.form.Button" iconClass="previous" dojoAttachPoint="previous">Prev</button> |
---|
403 | // | <button dojoType="dijit.form.ToggleButton" iconClass="playPause" dojoAttachPoint="playPause"></button> |
---|
404 | // | <button dojoType="dijit.form.Button" iconClass="next" dojoAttachPoint="next">Next</button> |
---|
405 | // | <span dojoAttachPoint="current"></span> / <span dojoAttachPoint="total"></span> |
---|
406 | // | </div> |
---|
407 | |
---|
408 | widgetsInTemplate: true, |
---|
409 | |
---|
410 | // rotatorId: int |
---|
411 | // The ID of the rotator this pager is tied to. |
---|
412 | // Only required if defined outside of the RotatorContainer's container. |
---|
413 | rotatorId: "", |
---|
414 | |
---|
415 | postMixInProperties: function(){ |
---|
416 | this.templateString = "<div>" + this.srcNodeRef.innerHTML + "</div>"; |
---|
417 | }, |
---|
418 | |
---|
419 | postCreate: function(){ |
---|
420 | var p = manager.byId(this.rotatorId) || this.getParent(); |
---|
421 | if(p && p.declaredClass == "dojox.layout.RotatorContainer"){ |
---|
422 | if(this.previous){ |
---|
423 | connect.connect(this.previous, "onClick", function(){ |
---|
424 | connect.publish(p.id+"-cycle", [false]); |
---|
425 | }); |
---|
426 | } |
---|
427 | if(this.next){ |
---|
428 | connect.connect(this.next, "onClick", function(){ |
---|
429 | connect.publish(p.id+"-cycle", [true]); |
---|
430 | }); |
---|
431 | } |
---|
432 | if(this.playPause){ |
---|
433 | connect.connect(this.playPause, "onClick", function(){ |
---|
434 | this.set('label', this.checked ? "Pause" : "Play"); |
---|
435 | connect.publish(p.id+"-state", [this.checked]); |
---|
436 | }); |
---|
437 | } |
---|
438 | this._subscriptions = [ |
---|
439 | connect.subscribe(p.id+"-state", this, "_state"), |
---|
440 | connect.subscribe(p.id+"-update", this, "_update") |
---|
441 | ]; |
---|
442 | } |
---|
443 | }, |
---|
444 | |
---|
445 | destroy: function(){ |
---|
446 | // Unsubscribe to all of our topics |
---|
447 | array.forEach(this._subscriptions, connect.unsubscribe); |
---|
448 | this.inherited(arguments); |
---|
449 | }, |
---|
450 | |
---|
451 | _state: function(/*boolean*/playing){ |
---|
452 | // summary: |
---|
453 | // Updates the display of the play/pause button |
---|
454 | if(this.playPause && this.playPause.checked != playing){ |
---|
455 | this.playPause.set("label", playing ? "Pause" : "Play"); |
---|
456 | this.playPause.set("checked", playing); |
---|
457 | } |
---|
458 | }, |
---|
459 | |
---|
460 | _update: function(/*boolean*/playing, /*int*/current, /*int*/total){ |
---|
461 | // summary: |
---|
462 | // Updates the pager's play/pause button, current child, and total number of children. |
---|
463 | this._state(playing); |
---|
464 | if(this.current && current){ |
---|
465 | this.current.innerHTML = current; |
---|
466 | } |
---|
467 | if(this.total && total){ |
---|
468 | this.total.innerHTML = total; |
---|
469 | } |
---|
470 | } |
---|
471 | }); |
---|
472 | return RotatorContainer; |
---|
473 | }); |
---|