StackContainer.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dijit.layout.StackContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dijit.layout.StackContainer"] = true;
  8. dojo.provide("dijit.layout.StackContainer");
  9. dojo.require("dijit._Templated");
  10. dojo.require("dijit.layout._LayoutWidget");
  11. dojo.requireLocalization("dijit", "common", null, "ROOT,ar,az,bg,ca,cs,da,de,el,es,fi,fr,he,hr,hu,it,ja,kk,ko,nb,nl,pl,pt,pt-pt,ro,ru,sk,sl,sv,th,tr,zh,zh-tw");
  12. dojo.require("dojo.cookie");
  13. dojo.require("dijit.layout.StackController");
  14. dojo.declare(
  15. "dijit.layout.StackContainer",
  16. dijit.layout._LayoutWidget,
  17. {
  18. // summary:
  19. // A container that has multiple children, but shows only
  20. // one child at a time
  21. //
  22. // description:
  23. // A container for widgets (ContentPanes, for example) That displays
  24. // only one Widget at a time.
  25. //
  26. // Publishes topics [widgetId]-addChild, [widgetId]-removeChild, and [widgetId]-selectChild
  27. //
  28. // Can be base class for container, Wizard, Show, etc.
  29. // doLayout: Boolean
  30. // If true, change the size of my currently displayed child to match my size
  31. doLayout: true,
  32. // persist: Boolean
  33. // Remembers the selected child across sessions
  34. persist: false,
  35. baseClass: "dijitStackContainer",
  36. /*=====
  37. // selectedChildWidget: [readonly] dijit._Widget
  38. // References the currently selected child widget, if any.
  39. // Adjust selected child with selectChild() method.
  40. selectedChildWidget: null,
  41. =====*/
  42. buildRendering: function(){
  43. this.inherited(arguments);
  44. dojo.addClass(this.domNode, "dijitLayoutContainer");
  45. dijit.setWaiRole(this.containerNode, "tabpanel");
  46. },
  47. postCreate: function(){
  48. this.inherited(arguments);
  49. this.connect(this.domNode, "onkeypress", this._onKeyPress);
  50. },
  51. startup: function(){
  52. if(this._started){ return; }
  53. var children = this.getChildren();
  54. // Setup each page panel to be initially hidden
  55. dojo.forEach(children, this._setupChild, this);
  56. // Figure out which child to initially display, defaulting to first one
  57. if(this.persist){
  58. this.selectedChildWidget = dijit.byId(dojo.cookie(this.id + "_selectedChild"));
  59. }else{
  60. dojo.some(children, function(child){
  61. if(child.selected){
  62. this.selectedChildWidget = child;
  63. }
  64. return child.selected;
  65. }, this);
  66. }
  67. var selected = this.selectedChildWidget;
  68. if(!selected && children[0]){
  69. selected = this.selectedChildWidget = children[0];
  70. selected.selected = true;
  71. }
  72. // Publish information about myself so any StackControllers can initialize.
  73. // This needs to happen before this.inherited(arguments) so that for
  74. // TabContainer, this._contentBox doesn't include the space for the tab labels.
  75. dojo.publish(this.id+"-startup", [{children: children, selected: selected}]);
  76. // Startup each child widget, and do initial layout like setting this._contentBox,
  77. // then calls this.resize() which does the initial sizing on the selected child.
  78. this.inherited(arguments);
  79. },
  80. resize: function(){
  81. // Resize is called when we are first made visible (it's called from startup()
  82. // if we are initially visible). If this is the first time we've been made
  83. // visible then show our first child.
  84. if(!this._hasBeenShown){
  85. this._hasBeenShown = true;
  86. var selected = this.selectedChildWidget;
  87. if(selected){
  88. this._showChild(selected);
  89. }
  90. }
  91. this.inherited(arguments);
  92. },
  93. _setupChild: function(/*dijit._Widget*/ child){
  94. // Overrides _LayoutWidget._setupChild()
  95. this.inherited(arguments);
  96. dojo.replaceClass(child.domNode, "dijitHidden", "dijitVisible");
  97. // remove the title attribute so it doesn't show up when i hover
  98. // over a node
  99. child.domNode.title = "";
  100. },
  101. addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
  102. // Overrides _Container.addChild() to do layout and publish events
  103. this.inherited(arguments);
  104. if(this._started){
  105. dojo.publish(this.id+"-addChild", [child, insertIndex]);
  106. // in case the tab titles have overflowed from one line to two lines
  107. // (or, if this if first child, from zero lines to one line)
  108. // TODO: w/ScrollingTabController this is no longer necessary, although
  109. // ScrollTabController.resize() does need to get called to show/hide
  110. // the navigation buttons as appropriate, but that's handled in ScrollingTabController.onAddChild()
  111. this.layout();
  112. // if this is the first child, then select it
  113. if(!this.selectedChildWidget){
  114. this.selectChild(child);
  115. }
  116. }
  117. },
  118. removeChild: function(/*dijit._Widget*/ page){
  119. // Overrides _Container.removeChild() to do layout and publish events
  120. this.inherited(arguments);
  121. if(this._started){
  122. // this will notify any tablists to remove a button; do this first because it may affect sizing
  123. dojo.publish(this.id + "-removeChild", [page]);
  124. }
  125. // If we are being destroyed than don't run the code below (to select another page), because we are deleting
  126. // every page one by one
  127. if(this._beingDestroyed){ return; }
  128. // Select new page to display, also updating TabController to show the respective tab.
  129. // Do this before layout call because it can affect the height of the TabController.
  130. if(this.selectedChildWidget === page){
  131. this.selectedChildWidget = undefined;
  132. if(this._started){
  133. var children = this.getChildren();
  134. if(children.length){
  135. this.selectChild(children[0]);
  136. }
  137. }
  138. }
  139. if(this._started){
  140. // In case the tab titles now take up one line instead of two lines
  141. // (note though that ScrollingTabController never overflows to multiple lines),
  142. // or the height has changed slightly because of addition/removal of tab which close icon
  143. this.layout();
  144. }
  145. },
  146. selectChild: function(/*dijit._Widget|String*/ page, /*Boolean*/ animate){
  147. // summary:
  148. // Show the given widget (which must be one of my children)
  149. // page:
  150. // Reference to child widget or id of child widget
  151. page = dijit.byId(page);
  152. if(this.selectedChildWidget != page){
  153. // Deselect old page and select new one
  154. var d = this._transition(page, this.selectedChildWidget, animate);
  155. this._set("selectedChildWidget", page);
  156. dojo.publish(this.id+"-selectChild", [page]);
  157. if(this.persist){
  158. dojo.cookie(this.id + "_selectedChild", this.selectedChildWidget.id);
  159. }
  160. }
  161. return d; // If child has an href, promise that fires when the child's href finishes loading
  162. },
  163. _transition: function(/*dijit._Widget*/ newWidget, /*dijit._Widget*/ oldWidget, /*Boolean*/ animate){
  164. // summary:
  165. // Hide the old widget and display the new widget.
  166. // Subclasses should override this.
  167. // tags:
  168. // protected extension
  169. if(oldWidget){
  170. this._hideChild(oldWidget);
  171. }
  172. var d = this._showChild(newWidget);
  173. // Size the new widget, in case this is the first time it's being shown,
  174. // or I have been resized since the last time it was shown.
  175. // Note that page must be visible for resizing to work.
  176. if(newWidget.resize){
  177. if(this.doLayout){
  178. newWidget.resize(this._containerContentBox || this._contentBox);
  179. }else{
  180. // the child should pick it's own size but we still need to call resize()
  181. // (with no arguments) to let the widget lay itself out
  182. newWidget.resize();
  183. }
  184. }
  185. return d; // If child has an href, promise that fires when the child's href finishes loading
  186. },
  187. _adjacent: function(/*Boolean*/ forward){
  188. // summary:
  189. // Gets the next/previous child widget in this container from the current selection.
  190. var children = this.getChildren();
  191. var index = dojo.indexOf(children, this.selectedChildWidget);
  192. index += forward ? 1 : children.length - 1;
  193. return children[ index % children.length ]; // dijit._Widget
  194. },
  195. forward: function(){
  196. // summary:
  197. // Advance to next page.
  198. return this.selectChild(this._adjacent(true), true);
  199. },
  200. back: function(){
  201. // summary:
  202. // Go back to previous page.
  203. return this.selectChild(this._adjacent(false), true);
  204. },
  205. _onKeyPress: function(e){
  206. dojo.publish(this.id+"-containerKeyPress", [{ e: e, page: this}]);
  207. },
  208. layout: function(){
  209. // Implement _LayoutWidget.layout() virtual method.
  210. if(this.doLayout && this.selectedChildWidget && this.selectedChildWidget.resize){
  211. this.selectedChildWidget.resize(this._containerContentBox || this._contentBox);
  212. }
  213. },
  214. _showChild: function(/*dijit._Widget*/ page){
  215. // summary:
  216. // Show the specified child by changing it's CSS, and call _onShow()/onShow() so
  217. // it can do any updates it needs regarding loading href's etc.
  218. // returns:
  219. // Promise that fires when page has finished showing, or true if there's no href
  220. var children = this.getChildren();
  221. page.isFirstChild = (page == children[0]);
  222. page.isLastChild = (page == children[children.length-1]);
  223. page._set("selected", true);
  224. dojo.replaceClass(page.domNode, "dijitVisible", "dijitHidden");
  225. return page._onShow() || true;
  226. },
  227. _hideChild: function(/*dijit._Widget*/ page){
  228. // summary:
  229. // Hide the specified child by changing it's CSS, and call _onHide() so
  230. // it's notified.
  231. page._set("selected", false);
  232. dojo.replaceClass(page.domNode, "dijitHidden", "dijitVisible");
  233. page.onHide();
  234. },
  235. closeChild: function(/*dijit._Widget*/ page){
  236. // summary:
  237. // Callback when user clicks the [X] to remove a page.
  238. // If onClose() returns true then remove and destroy the child.
  239. // tags:
  240. // private
  241. var remove = page.onClose(this, page);
  242. if(remove){
  243. this.removeChild(page);
  244. // makes sure we can clean up executeScripts in ContentPane onUnLoad
  245. page.destroyRecursive();
  246. }
  247. },
  248. destroyDescendants: function(/*Boolean*/ preserveDom){
  249. dojo.forEach(this.getChildren(), function(child){
  250. this.removeChild(child);
  251. child.destroyRecursive(preserveDom);
  252. }, this);
  253. }
  254. });
  255. // For back-compat, remove for 2.0
  256. // These arguments can be specified for the children of a StackContainer.
  257. // Since any widget can be specified as a StackContainer child, mix them
  258. // into the base widget class. (This is a hack, but it's effective.)
  259. dojo.extend(dijit._Widget, {
  260. // selected: Boolean
  261. // Parameter for children of `dijit.layout.StackContainer` or subclasses.
  262. // Specifies that this widget should be the initially displayed pane.
  263. // Note: to change the selected child use `dijit.layout.StackContainer.selectChild`
  264. selected: false,
  265. // closable: Boolean
  266. // Parameter for children of `dijit.layout.StackContainer` or subclasses.
  267. // True if user can close (destroy) this child, such as (for example) clicking the X on the tab.
  268. closable: false,
  269. // iconClass: String
  270. // Parameter for children of `dijit.layout.StackContainer` or subclasses.
  271. // CSS Class specifying icon to use in label associated with this pane.
  272. iconClass: "",
  273. // showTitle: Boolean
  274. // Parameter for children of `dijit.layout.StackContainer` or subclasses.
  275. // When true, display title of this widget as tab label etc., rather than just using
  276. // icon specified in iconClass
  277. showTitle: true
  278. });
  279. }