[483] | 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 | }); |
---|