_NodeMixin.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  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.manager._NodeMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.form.manager._NodeMixin"] = true;
  8. dojo.provide("dojox.form.manager._NodeMixin");
  9. dojo.require("dojox.form.manager._Mixin");
  10. (function(){
  11. var fm = dojox.form.manager,
  12. aa = fm.actionAdapter,
  13. keys = fm._keys,
  14. ce = fm.changeEvent = function(node){
  15. // summary:
  16. // Function that returns a valid "onchange" event for a given form node.
  17. // node: Node:
  18. // Form node.
  19. var eventName = "onclick";
  20. switch(node.tagName.toLowerCase()){
  21. case "textarea":
  22. eventName = "onkeyup";
  23. break;
  24. case "select":
  25. eventName = "onchange";
  26. break;
  27. case "input":
  28. switch(node.type.toLowerCase()){
  29. case "text":
  30. case "password":
  31. eventName = "onkeyup";
  32. break;
  33. }
  34. break;
  35. // button, input/button, input/checkbox, input/radio,
  36. // input/file, input/image, input/submit, input/reset
  37. // use "onclick" (the default)
  38. }
  39. return eventName; // String
  40. },
  41. registerNode = function(node, groupNode){
  42. var name = dojo.attr(node, "name");
  43. groupNode = groupNode || this.domNode;
  44. if(name && !(name in this.formWidgets)){
  45. // verify that it is not part of any widget
  46. for(var n = node; n && n !== groupNode; n = n.parentNode){
  47. if(dojo.attr(n, "widgetId") && dijit.byNode(n) instanceof dijit.form._FormWidget){
  48. // this is a child of some widget --- bail out
  49. return null;
  50. }
  51. }
  52. // register the node
  53. if(node.tagName.toLowerCase() == "input" && node.type.toLowerCase() == "radio"){
  54. var a = this.formNodes[name];
  55. a = a && a.node;
  56. if(a && dojo.isArray(a)){
  57. a.push(node);
  58. }else{
  59. this.formNodes[name] = {node: [node], connections: []};
  60. }
  61. }else{
  62. this.formNodes[name] = {node: node, connections: []};
  63. }
  64. }else{
  65. name = null;
  66. }
  67. return name;
  68. },
  69. getObserversFromNode = function(name){
  70. var observers = {};
  71. aa(function(_, n){
  72. var o = dojo.attr(n, "observer");
  73. if(o && typeof o == "string"){
  74. dojo.forEach(o.split(","), function(o){
  75. o = dojo.trim(o);
  76. if(o && dojo.isFunction(this[o])){
  77. observers[o] = 1;
  78. }
  79. }, this);
  80. }
  81. }).call(this, null, this.formNodes[name].node);
  82. return keys(observers);
  83. },
  84. connectNode = function(name, observers){
  85. var t = this.formNodes[name], c = t.connections;
  86. if(c.length){
  87. dojo.forEach(c, dojo.disconnect);
  88. c = t.connections = [];
  89. }
  90. aa(function(_, n){
  91. // the next line is a crude workaround for dijit.form.Button that fires onClick instead of onChange
  92. var eventName = ce(n);
  93. dojo.forEach(observers, function(o){
  94. c.push(dojo.connect(n, eventName, this, function(evt){
  95. if(this.watching){
  96. this[o](this.formNodeValue(name), name, n, evt);
  97. }
  98. }));
  99. }, this);
  100. }).call(this, null, t.node);
  101. };
  102. dojo.declare("dojox.form.manager._NodeMixin", null, {
  103. // summary:
  104. // Mixin to orchestrate dynamic forms (works with DOM nodes).
  105. // description:
  106. // This mixin provideas a foundation for an enhanced form
  107. // functionality: unified access to individual form elements,
  108. // unified "onchange" event processing, and general event
  109. // processing. It complements dojox.form.manager._Mixin
  110. // extending the functionality to DOM nodes.
  111. destroy: function(){
  112. // summary:
  113. // Called when the widget is being destroyed
  114. for(var name in this.formNodes){
  115. dojo.forEach(this.formNodes[name].connections, dojo.disconnect);
  116. }
  117. this.formNodes = {};
  118. this.inherited(arguments);
  119. },
  120. // register/unregister widgets and nodes
  121. registerNode: function(node){
  122. // summary:
  123. // Register a node with the form manager
  124. // node: String|Node:
  125. // A node, or its id
  126. // returns: Object:
  127. // Returns self
  128. if(typeof node == "string"){
  129. node = dojo.byId(node);
  130. }
  131. var name = registerNode.call(this, node);
  132. if(name){
  133. connectNode.call(this, name, getObserversFromNode.call(this, name));
  134. }
  135. return this;
  136. },
  137. unregisterNode: function(name){
  138. // summary:
  139. // Removes the node by name from internal tables unregistering
  140. // connected observers
  141. // name: String:
  142. // Name of the to unregister
  143. // returns: Object:
  144. // Returns self
  145. if(name in this.formNodes){
  146. dojo.forEach(this.formNodes[name].connections, this.disconnect, this);
  147. delete this.formNodes[name];
  148. }
  149. return this;
  150. },
  151. registerNodeDescendants: function(node){
  152. // summary:
  153. // Register node's descendants (form nodes) with the form manager
  154. // node: String|Node:
  155. // A widget, or its widgetId, or its DOM node
  156. // returns: Object:
  157. // Returns self
  158. if(typeof node == "string"){
  159. node = dojo.byId(node);
  160. }
  161. dojo.query("input, select, textarea, button", node).
  162. map(function(n){
  163. return registerNode.call(this, n, node);
  164. }, this).
  165. forEach(function(name){
  166. if(name){
  167. connectNode.call(this, name, getObserversFromNode.call(this, name));
  168. }
  169. }, this);
  170. return this;
  171. },
  172. unregisterNodeDescendants: function(node){
  173. // summary:
  174. // Unregister node's descendants (form nodes) with the form manager
  175. // node: String|Node:
  176. // A widget, or its widgetId, or its DOM node
  177. // returns: Object:
  178. // Returns self
  179. if(typeof node == "string"){
  180. node = dojo.byId(node);
  181. }
  182. dojo.query("input, select, textarea, button", node).
  183. map(function(n){ return dojo.attr(node, "name") || null; }).
  184. forEach(function(name){
  185. if(name){
  186. this.unregisterNode(name);
  187. }
  188. }, this);
  189. return this;
  190. },
  191. // value accessors
  192. formNodeValue: function(elem, value){
  193. // summary:
  194. // Set or get a form element by name.
  195. // elem: String|Node|Array:
  196. // Form element's name, DOM node, or array or radio nodes.
  197. // value: Object?:
  198. // Optional. The value to set.
  199. // returns: Object:
  200. // For a getter it returns the value, for a setter it returns
  201. // self. If the elem is not valid, null will be returned.
  202. var isSetter = arguments.length == 2 && value !== undefined, result;
  203. if(typeof elem == "string"){
  204. elem = this.formNodes[elem];
  205. if(elem){
  206. elem = elem.node;
  207. }
  208. }
  209. if(!elem){
  210. return null; // Object
  211. }
  212. if(dojo.isArray(elem)){
  213. // input/radio array
  214. if(isSetter){
  215. dojo.forEach(elem, function(node){
  216. node.checked = "";
  217. });
  218. dojo.forEach(elem, function(node){
  219. node.checked = node.value === value ? "checked" : "";
  220. });
  221. return this; // self
  222. }
  223. // getter
  224. dojo.some(elem, function(node){
  225. if(node.checked){
  226. result = node;
  227. return true;
  228. }
  229. return false;
  230. });
  231. return result ? result.value : ""; // String
  232. }
  233. // all other elements
  234. switch(elem.tagName.toLowerCase()){
  235. case "select":
  236. if(elem.multiple){
  237. // multiple is allowed
  238. if(isSetter){
  239. if(dojo.isArray(value)){
  240. var dict = {};
  241. dojo.forEach(value, function(v){
  242. dict[v] = 1;
  243. });
  244. dojo.query("> option", elem).forEach(function(opt){
  245. opt.selected = opt.value in dict;
  246. });
  247. return this; // self
  248. }
  249. // singular property
  250. dojo.query("> option", elem).forEach(function(opt){
  251. opt.selected = opt.value === value;
  252. });
  253. return this; // self
  254. }
  255. // getter
  256. var result = dojo.query("> option", elem).filter(function(opt){
  257. return opt.selected;
  258. }).map(function(opt){
  259. return opt.value;
  260. });
  261. return result.length == 1 ? result[0] : result; // Object
  262. }
  263. // singular
  264. if(isSetter){
  265. dojo.query("> option", elem).forEach(function(opt){
  266. opt.selected = opt.value === value;
  267. });
  268. return this; // self
  269. }
  270. // getter
  271. return elem.value || ""; // String
  272. case "button":
  273. if(isSetter){
  274. elem.innerHTML = "" + value;
  275. return this;
  276. }
  277. // getter
  278. return elem.innerHTML;
  279. case "input":
  280. if(elem.type.toLowerCase() == "checkbox"){
  281. // input/checkbox element
  282. if(isSetter){
  283. elem.checked = value ? "checked" : "";
  284. return this;
  285. }
  286. // getter
  287. return Boolean(elem.checked);
  288. }
  289. }
  290. // the rest of inputs
  291. if(isSetter){
  292. elem.value = "" + value;
  293. return this;
  294. }
  295. // getter
  296. return elem.value;
  297. },
  298. // inspectors
  299. inspectFormNodes: function(inspector, state, defaultValue){
  300. // summary:
  301. // Run an inspector function on controlled form elements returning a result object.
  302. // inspector: Function:
  303. // A function to be called on a form element. Takes three arguments: a name, a node or
  304. // an array of nodes, and a supplied value. Runs in the context of the form manager.
  305. // Returns a value that will be collected and returned as a state.
  306. // state: Object?:
  307. // Optional. If a name-value dictionary --- only listed names will be processed.
  308. // If an array, all names in the array will be processed with defaultValue.
  309. // If omitted or null, all form elements will be processed with defaultValue.
  310. // defaultValue: Object?:
  311. // Optional. The default state (true, if omitted).
  312. var name, result = {};
  313. if(state){
  314. if(dojo.isArray(state)){
  315. dojo.forEach(state, function(name){
  316. if(name in this.formNodes){
  317. result[name] = inspector.call(this, name, this.formNodes[name].node, defaultValue);
  318. }
  319. }, this);
  320. }else{
  321. for(name in state){
  322. if(name in this.formNodes){
  323. result[name] = inspector.call(this, name, this.formNodes[name].node, state[name]);
  324. }
  325. }
  326. }
  327. }else{
  328. for(name in this.formNodes){
  329. result[name] = inspector.call(this, name, this.formNodes[name].node, defaultValue);
  330. }
  331. }
  332. return result; // Object
  333. }
  334. });
  335. })();
  336. }