property_field.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. //iTop Designer widget for editing properties line by line
  2. $(function()
  3. {
  4. // the widget definition, where "itop" is the namespace,
  5. // "property_field" the widget name
  6. $.widget( "itop.property_field",
  7. {
  8. // default options
  9. options:
  10. {
  11. field_id: '',
  12. submit_to: 'index.php',
  13. submit_parameters: {operation: 'async_action'},
  14. do_apply: null,
  15. do_cancel: null
  16. },
  17. // the constructor
  18. _create: function()
  19. {
  20. this.element.addClass( "itop-property-field" );
  21. this.bModified = false;
  22. var me = this;
  23. if (this.options.field_id != '')
  24. {
  25. $('#'+this.options.field_id).bind('change.itop-property-field', function() { me._on_change(); });
  26. this.value = this._get_field_value();
  27. }
  28. this.element.find(".prop_apply").bind('click.itop-property-field', function() { me._do_apply(); });
  29. this.element.find(".prop_cancel").bind('click.itop-property-field', function() { me._do_cancel(); });
  30. this._refresh();
  31. },
  32. // called when created, and later when changing options
  33. _refresh: function()
  34. {
  35. if (this.bModified)
  36. {
  37. this.element.addClass("itop-property-field-modified");
  38. this.element.find(".prop_icon span.ui-icon-circle-check").css({visibility: ''});
  39. this.element.find(".prop_icon span.ui-icon-circle-close").css({visibility: ''});
  40. }
  41. else
  42. {
  43. this.element.removeClass("itop-property-field-modified");
  44. this.element.find(".prop_icon span.ui-icon-circle-check").css({visibility: 'hidden'});
  45. this.element.find(".prop_icon span.ui-icon-circle-close").css({visibility: 'hidden'});
  46. }
  47. },
  48. // events bound via _bind are removed automatically
  49. // revert other modifications here
  50. _destroy: function()
  51. {
  52. this.element.removeClass( "itop-property-field" );
  53. this.element.removeClass("itop-property-field-modified");
  54. },
  55. // _setOptions is called with a hash of all options that are changing
  56. // always refresh when changing options
  57. _setOptions: function()
  58. {
  59. // in 1.9 would use _superApply
  60. $.Widget.prototype._setOptions.apply( this, arguments );
  61. this._refresh();
  62. },
  63. // _setOption is called for each individual option that is changing
  64. _setOption: function( key, value )
  65. {
  66. // in 1.9 would use _super
  67. $.Widget.prototype._setOption.call( this, key, value );
  68. },
  69. _on_change: function()
  70. {
  71. var new_value = this._get_field_value();
  72. if (new_value != this.value)
  73. {
  74. this.bModified = true;
  75. }
  76. else
  77. {
  78. this.bModified = false;
  79. }
  80. this._refresh();
  81. },
  82. _get_field_value: function()
  83. {
  84. var oField = $('#'+this.options.field_id);
  85. if (oField.attr('type') == 'checkbox')
  86. {
  87. return (oField.attr('checked') == 'checked');
  88. }
  89. else
  90. {
  91. return oField.val();
  92. }
  93. },
  94. _get_committed_value: function()
  95. {
  96. return { name: $('#'+this.options.field_id).attr('name'), value: this.value };
  97. },
  98. _do_apply: function()
  99. {
  100. if (this.options.do_apply)
  101. {
  102. // specific behavior...
  103. if (this.options.do_apply())
  104. {
  105. this.bModified = false;
  106. this.previous_value = this.value;
  107. this.value = this._get_field_value();
  108. this._refresh();
  109. }
  110. }
  111. else
  112. {
  113. // Validate the field
  114. sFormId = this.element.closest('form').attr('id');
  115. var oField = $('#'+this.options.field_id);
  116. oField.trigger('validate');
  117. if ( $.inArray(this.options.field_id, oFormValidation[sFormId]) == -1)
  118. {
  119. this.bModified = false;
  120. this.previous_value = this.value;
  121. this.value = this._get_field_value();
  122. this._do_submit();
  123. this._refresh();
  124. }
  125. }
  126. },
  127. _do_cancel: function()
  128. {
  129. if (this.options.do_cancel)
  130. {
  131. // specific behavior...
  132. this.options.do_cancel();
  133. }
  134. else
  135. {
  136. this.bModified = false;
  137. var oField = $('#'+this.options.field_id);
  138. if (oField.attr('type') == 'checkbox')
  139. {
  140. if (this.value)
  141. {
  142. oField.attr('checked', true);
  143. }
  144. else
  145. {
  146. oField.removeAttr('checked');
  147. }
  148. }
  149. else
  150. {
  151. oField.val(this.value);
  152. }
  153. this._refresh();
  154. oField.trigger('reverted', {type: 'designer', previous_value: this.value });
  155. oField.trigger('validate');
  156. }
  157. },
  158. _do_submit: function()
  159. {
  160. var oData = {};
  161. this.element.closest('form').find(':input[type=hidden]').each(function()
  162. {
  163. // Hidden form fields
  164. oData[$(this).attr('name')] = $(this).val();
  165. });
  166. this.element.closest('form').find('.itop-property-field').each(function()
  167. {
  168. var oWidget = $(this).data('property_field');
  169. if (oWidget && oWidget._is_visible())
  170. {
  171. var oVal = oWidget._get_committed_value();
  172. oData[oVal.name] = oVal.value;
  173. }
  174. });
  175. oPostedData = this.options.submit_parameters;
  176. oPostedData.params = oData;
  177. oPostedData.params.updated = [ $('#'+this.options.field_id).attr('name') ]; // only one field updated in this case
  178. oPostedData.params.previous_values = {};
  179. oPostedData.params.previous_values[oPostedData.params.updated] = this.previous_value; // pass also the previous value(s)
  180. $.post(this.options.submit_to, oPostedData, function(data)
  181. {
  182. $('#prop_submit_result').html(data);
  183. });
  184. },
  185. _is_visible: function()
  186. {
  187. return this.element.parent().is(':visible');
  188. }
  189. });
  190. });
  191. var oFormValidation = {};
  192. function ValidateWithPattern(sFieldId, bMandatory, sPattern, sFormId)
  193. {
  194. var currentVal = $('#'+sFieldId).val();
  195. var bValid = true;
  196. if (bMandatory && (currentVal == ''))
  197. {
  198. bValid = false;
  199. }
  200. if ((sPattern != '') && (currentVal != ''))
  201. {
  202. re = new RegExp(sPattern);
  203. bValid = re.test(currentVal);
  204. }
  205. if (!bValid)
  206. {
  207. $('#v_'+sFieldId).addClass('ui-state-error');
  208. if (oFormValidation[sFormId] == undefined) oFormValidation[sFormId] = [];
  209. oFormValidation[sFormId].push(sFieldId);
  210. }
  211. else
  212. {
  213. $('#v_'+sFieldId).removeClass('ui-state-error');
  214. }
  215. }
  216. function ValidateForm(sFormId, bValidateAll)
  217. {
  218. oFormValidation[sFormId] = [];
  219. if (bValidateAll)
  220. {
  221. $('#'+sFormId+' :input').trigger('validate');
  222. }
  223. else
  224. {
  225. // Only the visible fields
  226. $('#'+sFormId+' :input:visible').each(function() {
  227. $(this).trigger('validate');
  228. });
  229. }
  230. return oFormValidation[sFormId];
  231. }
  232. function ReadFormParams(sFormId)
  233. {
  234. var oMap = { };
  235. $('#'+sFormId+' :input').each( function() {
  236. if ($(this).parent().is(':visible'))
  237. {
  238. var sName = $(this).attr('name');
  239. if (sName && sName != '')
  240. {
  241. if (this.type == 'checkbox')
  242. {
  243. oMap[sName] = ($(this).attr('checked') == 'checked');
  244. }
  245. else
  246. {
  247. oMap[sName] = $(this).val();
  248. }
  249. }
  250. }
  251. });
  252. return oMap;
  253. }
  254. function SubmitForm(sFormId, onSubmitResult)
  255. {
  256. var aErrors = ValidateForm(sFormId, false);
  257. if (aErrors.length == 0)
  258. {
  259. var oMap = ReadFormParams(sFormId);
  260. oMap.module_name = sCurrentModule;
  261. $('#'+sFormId+' :input').each( function() {
  262. if ($(this).parent().is(':visible'))
  263. {
  264. var sName = $(this).attr('name');
  265. if (sName && sName != '')
  266. {
  267. if (this.type == 'checkbox')
  268. {
  269. oMap[sName] = ($(this).attr('checked') == 'checked');
  270. }
  271. else
  272. {
  273. oMap[sName] = $(this).val();
  274. }
  275. }
  276. }
  277. });
  278. $.post(GetAbsoluteUrlAppRoot()+'designer/module.php', oMap, function(data)
  279. {
  280. onSubmitResult(data);
  281. });
  282. }
  283. else
  284. {
  285. // TODO: better error reporting !!!
  286. alert('Please fill all the fields before continuing...');
  287. }
  288. }