_SelectStackMixin.js 6.9 KB

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