_SelectStackMixin.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  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["dojox.form._SelectStackMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.form._SelectStackMixin"] = true;
  8. dojo.provide("dojox.form._SelectStackMixin");
  9. dojo.declare("dojox.form._SelectStackMixin", null, {
  10. // summary:
  11. // Mix this class in to a dijit.form._FormSelectWidget in order to
  12. // provide support for "selectable" multiforms. The widget is pointed
  13. // to a dijit.layout.StackContainer and will handle displaying and
  14. // submitting the values of only the appropriate pane.
  15. //
  16. // The options for this widget will be automatically set - based on
  17. // the panes that are in the stack container. The "title" attribute of
  18. // the pane will be used for the display of the option. The "id" attribute
  19. // of the pane will be used as the value of the option. In order to
  20. // avoid running into unique ID constraint issues, a stackPrefix mechanism
  21. // is provided.
  22. // stackId: string
  23. // The id of the stack that this widget is supposed to control
  24. stackId: "",
  25. // stackPrefix: string
  26. // A prefix to remove from our stack pane ids when setting our options.
  27. // This exists so that we won't run into unique ID constraints. For
  28. // example, if stackPrefix is set to "foo_", and there are three panes
  29. // in our stack with ids of "foo_a", "foo_b", and "foo_c", then the values
  30. // of the options created for the stack controller widget will be "a",
  31. // "b", and "c". This allows you to have multiple select stack widgets
  32. // with the same values - without having to have the panes require the
  33. // same ids.
  34. stackPrefix: "",
  35. _paneIdFromOption: function(/*String*/ oVal){
  36. // summary: Gets the pane ID given an option value
  37. return (this.stackPrefix || "") + oVal; // String
  38. },
  39. _optionValFromPane: function(/*String*/ id){
  40. // summary: Gets the option value given a pane ID
  41. var sp = this.stackPrefix;
  42. if(sp && id.indexOf(sp) === 0){
  43. return id.substring(sp.length); // String
  44. }
  45. return id; // String
  46. },
  47. _togglePane: function(/*dijit._Widget*/ pane, /*Boolean*/ shown){
  48. // summary: called when a pane is either shown or hidden (so that
  49. // we can toggle the widgets on it)
  50. if(pane._shown != undefined && pane._shown == shown){ return; }
  51. var widgets = dojo.filter(pane.getDescendants(), "return item.name;");
  52. if(!shown){
  53. // We are hiding - save the current state and then disable them
  54. savedStates = {};
  55. dojo.forEach(widgets, function(w){
  56. savedStates[w.id] = w.disabled;
  57. w.set("disabled", true);
  58. });
  59. pane._savedStates = savedStates;
  60. }else{
  61. // We are showing - restore our saved states
  62. var savedStates = pane._savedStates||{};
  63. dojo.forEach(widgets, function(w){
  64. var state = savedStates[w.id];
  65. if(state == undefined){
  66. state = false;
  67. }
  68. w.set("disabled", state);
  69. });
  70. delete pane._savedStates;
  71. }
  72. pane._shown = shown;
  73. },
  74. _connectTitle: function(/*dijit._Widget*/ pane, /*String*/ value){
  75. var fx = dojo.hitch(this, function(title){
  76. this.updateOption({value: value, label: title});
  77. });
  78. if(pane._setTitleAttr){
  79. this.connect(pane, "_setTitleAttr", fx);
  80. }else{
  81. this.connect(pane, "attr", function(attr, val){
  82. if(attr == "title" && arguments.length > 1){
  83. fx(val);
  84. }
  85. });
  86. }
  87. },
  88. onAddChild: function(/*dijit._Widget*/ pane, /*Integer?*/ insertIndex){
  89. // summary: Called when the stack container adds a new pane
  90. if(!this._panes[pane.id]){
  91. this._panes[pane.id] = pane;
  92. var v = this._optionValFromPane(pane.id);
  93. this.addOption({value: v, label: pane.title});
  94. this._connectTitle(pane, v);
  95. }
  96. if(!pane.onShow || !pane.onHide || pane._shown == undefined){
  97. pane.onShow = dojo.hitch(this, "_togglePane", pane, true);
  98. pane.onHide = dojo.hitch(this, "_togglePane", pane, false);
  99. pane.onHide();
  100. }
  101. },
  102. _setValueAttr: function(v){
  103. if("_savedValue" in this){
  104. return;
  105. }
  106. this.inherited(arguments);
  107. },
  108. attr: function(/*String|Object*/name, /*Object?*/value){
  109. if(name == "value" && arguments.length == 2 && "_savedValue" in this){
  110. this._savedValue = value;
  111. }
  112. return this.inherited(arguments);
  113. },
  114. onRemoveChild: function(/*dijit._Widget*/ pane){
  115. // summary: Called when the stack container removes a pane
  116. if(this._panes[pane.id]){
  117. delete this._panes[pane.id];
  118. this.removeOption(this._optionValFromPane(pane.id));
  119. }
  120. },
  121. onSelectChild: function(/*dijit._Widget*/ pane){
  122. // summary: Called when the stack container selects a new pane
  123. this._setValueAttr(this._optionValFromPane(pane.id));
  124. },
  125. onStartup: function(/*Object*/ info){
  126. // summary: Called when the stack container is started up
  127. var selPane = info.selected;
  128. this.addOption(dojo.filter(dojo.map(info.children, function(c){
  129. var v = this._optionValFromPane(c.id);
  130. this._connectTitle(c, v);
  131. var toAdd = null;
  132. if(!this._panes[c.id]){
  133. this._panes[c.id] = c;
  134. toAdd = {value: v, label: c.title};
  135. }
  136. if(!c.onShow || !c.onHide || c._shown == undefined){
  137. c.onShow = dojo.hitch(this, "_togglePane", c, true);
  138. c.onHide = dojo.hitch(this, "_togglePane", c, false);
  139. c.onHide();
  140. }
  141. if("_savedValue" in this && v === this._savedValue){
  142. selPane = c;
  143. }
  144. return toAdd;
  145. }, this), function(i){ return i;}));
  146. var _this = this;
  147. var fx = function(){
  148. // This stuff needs to be run after we show our child, if
  149. // the stack is going to show a different child than is
  150. // selected - see trac #9396
  151. delete _this._savedValue;
  152. _this.onSelectChild(selPane);
  153. if(!selPane._shown){
  154. _this._togglePane(selPane, true);
  155. }
  156. };
  157. if(selPane !== info.selected){
  158. var stack = dijit.byId(this.stackId);
  159. var c = this.connect(stack, "_showChild", function(sel){
  160. this.disconnect(c);
  161. fx();
  162. });
  163. }else{
  164. fx();
  165. }
  166. },
  167. postMixInProperties: function(){
  168. this._savedValue = this.value;
  169. this.inherited(arguments);
  170. this.connect(this, "onChange", "_handleSelfOnChange");
  171. },
  172. postCreate: function(){
  173. this.inherited(arguments);
  174. this._panes = {};
  175. this._subscriptions = [
  176. dojo.subscribe(this.stackId + "-startup", this, "onStartup"),
  177. dojo.subscribe(this.stackId + "-addChild", this, "onAddChild"),
  178. dojo.subscribe(this.stackId + "-removeChild", this, "onRemoveChild"),
  179. dojo.subscribe(this.stackId + "-selectChild", this, "onSelectChild")
  180. ];
  181. var stack = dijit.byId(this.stackId);
  182. if(stack && stack._started){
  183. // If we have a stack, and it's already started, call our onStartup now
  184. this.onStartup({children: stack.getChildren(), selected: stack.selectedChildWidget});
  185. }
  186. },
  187. destroy: function(){
  188. dojo.forEach(this._subscriptions, dojo.unsubscribe);
  189. delete this._panes; // Fixes memory leak in IE
  190. this.inherited("destroy", arguments);
  191. },
  192. _handleSelfOnChange: function(/*String*/ val){
  193. // summary: Called when form select widget's value has changed
  194. var pane = this._panes[this._paneIdFromOption(val)];
  195. if (pane){
  196. var s = dijit.byId(this.stackId);
  197. if(pane == s.selectedChildWidget){
  198. s._transition(pane);
  199. }else{
  200. s.selectChild(pane);
  201. }
  202. }
  203. }
  204. });
  205. }