field_set.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. //iTop Field set
  2. //Used by itop.form_handler and itop.subform_field to list their fields
  3. ;
  4. $(function()
  5. {
  6. // the widget definition, where 'itop' is the namespace,
  7. // 'field_set' the widget name
  8. $.widget( 'itop.field_set',
  9. {
  10. // default options
  11. options:
  12. {
  13. field_identifier_attr: 'data-field-id', // convention: fields are rendered into a div and are identified by this attribute
  14. fields_list: null,
  15. fields_impacts: {},
  16. touched_fields: [],
  17. is_valid: true,
  18. form_path: '',
  19. script_element: null,
  20. style_element: null
  21. },
  22. buildData:
  23. {
  24. script_code: '',
  25. style_code: ''
  26. },
  27. // the constructor
  28. _create: function()
  29. {
  30. var me = this;
  31. this.element
  32. .addClass('field_set');
  33. this.element
  34. .bind('field_change', function(oEvent, oData){
  35. console.log('field_set: field_change');
  36. me._onFieldChange(oEvent, oData);
  37. })
  38. .bind('update_form', function(oEvent, oData){
  39. console.log('field_set: update_form');
  40. me._onUpdateForm(oEvent, oData);
  41. })
  42. .bind('get_current_values', function(oEvent, oData){
  43. console.log('field_set: get_current_values');
  44. return me._onGetCurrentValues(oEvent, oData);
  45. })
  46. .bind('validate', function(oEvent, oData){
  47. if (oData === undefined)
  48. {
  49. oData = {};
  50. }
  51. console.log('field_set: validate');
  52. return me._onValidate(oEvent, oData);
  53. });
  54. // Creating DOM elements if not using user's specifics
  55. if(this.options.script_element === null)
  56. {
  57. this.options.script_element = $('<script type="text/javascript"></script>');
  58. this.element.after(this.options.script_element);
  59. }
  60. if(this.options.style_element === null)
  61. {
  62. this.options.style_element = $('<style></style>');
  63. this.element.before(this.options.style_element);
  64. }
  65. // Building the form
  66. if(this.options.fields_list !== null)
  67. {
  68. this.buildForm();
  69. }
  70. },
  71. // called when created, and later when changing options
  72. _refresh: function()
  73. {
  74. },
  75. // events bound via _bind are removed automatically
  76. // revert other modifications here
  77. _destroy: function()
  78. {
  79. this.element
  80. .removeClass('field_set');
  81. },
  82. // _setOptions is called with a hash of all options that are changing
  83. // always refresh when changing options
  84. _setOptions: function()
  85. {
  86. this._superApply(arguments);
  87. },
  88. // _setOption is called for each individual option that is changing
  89. _setOption: function( key, value )
  90. {
  91. this._super( key, value );
  92. },
  93. getField: function (sFieldId)
  94. {
  95. return this.element.find('[' + this.options.field_identifier_attr + '="' + sFieldId + '"][data-form-path="' + this.options.form_path + '"]');
  96. },
  97. _onGetCurrentValues: function(oEvent, oData)
  98. {
  99. oEvent.stopPropagation();
  100. var oResult = {};
  101. for(var i in this.options.fields_list)
  102. {
  103. var oField = this.options.fields_list[i];
  104. if(this.getField(oField.id).hasClass('form_field'))
  105. {
  106. oResult[oField.id] = this.getField(oField.id).triggerHandler('get_current_value');
  107. }
  108. else
  109. {
  110. console.log('Field set : Cannot retrieve current value from field [' + this.options.field_identifier_attr + '="' + oField.id + '"][data-form-path="' + this.options.form_path + '"] as it seems to have no itop.form_field widget attached.');
  111. }
  112. }
  113. return oResult;
  114. },
  115. _getRequestedFields: function(sSourceFieldName)
  116. {
  117. var aFieldsName = [];
  118. if(this.options.fields_impacts[sSourceFieldName] !== undefined)
  119. {
  120. for(var i in this.options.fields_impacts[sSourceFieldName])
  121. {
  122. aFieldsName.push(this.options.fields_impacts[sSourceFieldName][i]);
  123. }
  124. }
  125. return aFieldsName;
  126. },
  127. _onFieldChange: function(oEvent, oData)
  128. {
  129. oEvent.stopPropagation();
  130. // Set field as touched so we know that we have to do checks on it later
  131. if(this.options.touched_fields.indexOf(oData.name) < 0)
  132. {
  133. this.options.touched_fields.push(oData.name);
  134. }
  135. // Validate the field
  136. var oResult = this.getField(oData.name).triggerHandler('validate', {touched_fields_only: true});
  137. if (!oResult.is_valid)
  138. {
  139. this.options.is_valid = false;
  140. }
  141. var oRequestedFields = this._getRequestedFields(oData.name);
  142. if(oRequestedFields.length > 0)
  143. {
  144. this.element.trigger('update_fields', {form_path: this.options.form_path, requested_fields: oRequestedFields});
  145. }
  146. },
  147. _onUpdateForm: function(oEvent, oData)
  148. {
  149. oEvent.stopPropagation();
  150. this.buildData.script_code = '';
  151. this.buildData.style_code = '';
  152. for (var i in oData.updated_fields)
  153. {
  154. var oUpdatedField = oData.updated_fields[i];
  155. this.options.fields_list[oUpdatedField.id] = oUpdatedField;
  156. this._prepareField(oUpdatedField.id);
  157. }
  158. // Adding code to the dom
  159. this.options.script_element.append('\n\n// Appended by update on ' + Date() + '\n' + this.buildData.script_code);
  160. this.options.style_element.append('\n\n// Appended by update on ' + Date() + '\n' + this.buildData.style_code);
  161. // Evaluating script code as adding it to dom did not executed it (only script from update !)
  162. eval(this.buildData.script_code);
  163. },
  164. _onValidate: function(oEvent, oData)
  165. {
  166. oEvent.stopPropagation();
  167. this.options.is_valid = true;
  168. var aFieldsToValidate = [];
  169. if ((oData.touched_fields_only !== undefined) && (oData.touched_fields_only === true))
  170. {
  171. aFieldsToValidate = this.options.touched_fields;
  172. }
  173. else
  174. {
  175. // TODO : Requires IE9+ Object.keys(this.options.fields_list);
  176. for (var sFieldId in this.options.fields_list)
  177. {
  178. aFieldsToValidate.push(sFieldId);
  179. }
  180. }
  181. for(var i in aFieldsToValidate)
  182. {
  183. var oRes = this.getField(aFieldsToValidate[i]).triggerHandler('validate', oData);
  184. if (!oRes.is_valid)
  185. {
  186. this.options.is_valid = false;
  187. }
  188. }
  189. return this.options.is_valid;
  190. },
  191. // Debug helper
  192. showOptions: function()
  193. {
  194. return this.options;
  195. },
  196. _loadCssFile: function(url)
  197. {
  198. if (!$('link[href="' + url + '"]').length)
  199. $('<link href="' + url + '" rel="stylesheet">').appendTo('head');
  200. },
  201. _loadJsFile: function(url)
  202. {
  203. if (!$('script[src="' + url + '"]').length)
  204. $.getScript(url);
  205. },
  206. // Place a field for which no container exists
  207. _addField: function(sFieldId)
  208. {
  209. $('<div ' + this.options.field_identifier_attr + '="' + sFieldId + '" data-form-path="' + this.options.form_path + '"></div>').appendTo(this.element);
  210. },
  211. _prepareField: function(sFieldId)
  212. {
  213. var oField = this.options.fields_list[sFieldId];
  214. if(this.getField(oField.id).length === 1)
  215. {
  216. // We replace the node instead of just replacing the inner html so the previous widget is automatically destroyed.
  217. this.getField(oField.id).replaceWith(
  218. $('<div ' + this.options.field_identifier_attr + '="' + oField.id + '" data-form-path="' + this.options.form_path + '"></div>')
  219. );
  220. }
  221. else
  222. {
  223. this._addField(oField.id);
  224. }
  225. var oFieldContainer = this.getField(oField.id);
  226. // HTML
  227. if( (oField.html !== undefined) && (oField.html !== '') )
  228. {
  229. oFieldContainer.html(oField.html);
  230. }
  231. // JS files
  232. if( (oField.js_files !== undefined) && (oField.js_files.length > 0) )
  233. {
  234. for(var j in oField.js_files)
  235. {
  236. this._loadJsFile(oField.js_files[i]);
  237. }
  238. }
  239. // CSS files
  240. if( (oField.css_files !== undefined) && (oField.css_files.length > 0) )
  241. {
  242. for(var j in oField.css_files)
  243. {
  244. this._loadCssFile(oField.css_files[i]);
  245. }
  246. }
  247. // JS inline
  248. if( (oField.js_inline !== undefined) && (oField.js_inline !== '') )
  249. {
  250. this.buildData.script_code += '; '+ oField.js_inline;
  251. }
  252. // CSS inline
  253. if( (oField.css_inline !== undefined) && (oField.css_inline !== '') )
  254. {
  255. this.buildData.style_code += ' '+ oField.css_inline;
  256. }
  257. },
  258. buildForm: function()
  259. {
  260. this.buildData.script_code = '';
  261. this.buildData.style_code = '';
  262. for(var i in this.options.fields_list)
  263. {
  264. var oField = this.options.fields_list[i];
  265. if(oField.id === undefined)
  266. {
  267. console.log('Field set : An field must have at least an id property.');
  268. return false;
  269. }
  270. this._prepareField(oField.id);
  271. }
  272. this.options.script_element.text('$(document).ready(function(){ ' + this.buildData.script_code + ' });');
  273. this.options.style_element.text(this.buildData.style_code);
  274. eval(this.options.script_element.text());
  275. }
  276. });
  277. });