property_field.js 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  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. parent_selector: null,
  12. field_id: '',
  13. get_field_value: null,
  14. equals: null,
  15. submit_to: 'index.php',
  16. submit_parameters: {operation: 'async_action'},
  17. do_apply: null,
  18. do_cancel: null,
  19. auto_apply: false
  20. },
  21. // the constructor
  22. _create: function()
  23. {
  24. var me = this;
  25. this.element
  26. .addClass( "itop-property-field" )
  27. .bind('apply_changes.itop-property-field', function(){me._do_apply();} );
  28. this.bModified = false;
  29. if (this.options.field_id != '')
  30. {
  31. $('#'+this.options.field_id).bind('change.itop-property-field', function() { me._on_change(); });
  32. this.value = this._get_field_value();
  33. }
  34. this.element.find(".prop_apply").bind('click.itop-property-field', function() { me._do_apply(); });
  35. this.element.find(".prop_cancel").bind('click.itop-property-field', function() { me._do_cancel(); });
  36. this._refresh();
  37. },
  38. // called when created, and later when changing options
  39. _refresh: function()
  40. {
  41. if (this.bModified)
  42. {
  43. this.element.addClass("itop-property-field-modified");
  44. this.element.find(".prop_icon span.ui-icon-circle-check").css({visibility: ''});
  45. this.element.find(".prop_icon span.ui-icon-circle-close").css({visibility: ''});
  46. }
  47. else
  48. {
  49. this.element.removeClass("itop-property-field-modified");
  50. this.element.find(".prop_icon span.ui-icon-circle-check").css({visibility: 'hidden'});
  51. this.element.find(".prop_icon span.ui-icon-circle-close").css({visibility: 'hidden'});
  52. }
  53. },
  54. // events bound via _bind are removed automatically
  55. // revert other modifications here
  56. _destroy: function()
  57. {
  58. this.element.removeClass( "itop-property-field" );
  59. this.element.removeClass("itop-property-field-modified");
  60. },
  61. // _setOptions is called with a hash of all options that are changing
  62. // always refresh when changing options
  63. _setOptions: function()
  64. {
  65. // in 1.9 would use _superApply
  66. this._superApply(arguments);
  67. this._refresh();
  68. },
  69. // _setOption is called for each individual option that is changing
  70. _setOption: function( key, value )
  71. {
  72. // in 1.9 would use _super
  73. this._superApply(arguments);
  74. },
  75. _on_change: function()
  76. {
  77. var new_value = this._get_field_value();
  78. if (this._equals(new_value, this.value))
  79. {
  80. this.bModified = false;
  81. }
  82. else
  83. {
  84. this.bModified = true;
  85. if (this.options.auto_apply)
  86. {
  87. this._do_apply();
  88. }
  89. }
  90. this._refresh();
  91. },
  92. _equals: function( value1, value2 )
  93. {
  94. if (this.options.equals === null)
  95. {
  96. return value1 == value2;
  97. }
  98. else
  99. {
  100. return this.options.equals(value1, value2);
  101. }
  102. },
  103. _get_field_value: function()
  104. {
  105. if (this.options.get_field_value === null)
  106. {
  107. var oField = $('#'+this.options.field_id);
  108. if (oField.attr('type') == 'checkbox')
  109. {
  110. return (oField.attr('checked') == 'checked');
  111. }
  112. else
  113. {
  114. return oField.val();
  115. }
  116. }
  117. else
  118. {
  119. return this.options.get_field_value();
  120. }
  121. },
  122. _get_committed_value: function()
  123. {
  124. return { name: $('#'+this.options.field_id).attr('name'), value: this.value };
  125. },
  126. _do_apply: function()
  127. {
  128. if (this.options.parent_selector)
  129. {
  130. $(this.options.parent_selector).trigger('mark_as_modified');
  131. }
  132. if (this.options.do_apply)
  133. {
  134. // specific behavior...
  135. if (this.options.do_apply())
  136. {
  137. this.bModified = false;
  138. this.previous_value = this.value;
  139. this.value = this._get_field_value();
  140. this._refresh();
  141. }
  142. }
  143. else
  144. {
  145. // Validate the field
  146. sFormId = this.element.closest('form').attr('id');
  147. var oField = $('#'+this.options.field_id);
  148. oField.trigger('validate');
  149. if ( $.inArray(this.options.field_id, oFormValidation[sFormId]) == -1)
  150. {
  151. this.bModified = false;
  152. this.previous_value = this.value;
  153. this.value = this._get_field_value();
  154. this._do_submit();
  155. this._refresh();
  156. }
  157. }
  158. },
  159. _do_cancel: function()
  160. {
  161. if (this.options.do_cancel)
  162. {
  163. // specific behavior...
  164. this.options.do_cancel();
  165. }
  166. else
  167. {
  168. this.bModified = false;
  169. var oField = $('#'+this.options.field_id);
  170. if (oField.attr('type') == 'checkbox')
  171. {
  172. if (this.value)
  173. {
  174. oField.attr('checked', true);
  175. }
  176. else
  177. {
  178. oField.removeAttr('checked');
  179. }
  180. }
  181. else
  182. {
  183. oField.val(this.value);
  184. }
  185. this._refresh();
  186. oField.trigger('reverted', {type: 'designer', previous_value: this.value });
  187. oField.trigger('validate');
  188. }
  189. },
  190. _do_submit: function()
  191. {
  192. var oData = {};
  193. this.element.closest('form').find(':input[type=hidden]').each(function()
  194. {
  195. // Hidden form fields
  196. oData[$(this).attr('name')] = $(this).val();
  197. });
  198. this.element.closest('form').find('.itop-property-field').each(function()
  199. {
  200. var oWidget = $(this).data('itopProperty_field');
  201. if (oWidget && oWidget._is_visible())
  202. {
  203. var oVal = oWidget._get_committed_value();
  204. oData[oVal.name] = oVal.value;
  205. }
  206. });
  207. oPostedData = this.options.submit_parameters;
  208. oPostedData.params = oData;
  209. oPostedData.params.updated = [ $('#'+this.options.field_id).attr('name') ]; // only one field updated in this case
  210. oPostedData.params.previous_values = {};
  211. oPostedData.params.previous_values[oPostedData.params.updated] = this.previous_value; // pass also the previous value(s)
  212. $.post(this.options.submit_to, oPostedData, function(data)
  213. {
  214. $('#prop_submit_result').html(data);
  215. });
  216. },
  217. _is_visible: function()
  218. {
  219. return this.element.parent().is(':visible');
  220. }
  221. });
  222. });
  223. var oFormValidation = {};
  224. function ValidateWithPattern(sFieldId, bMandatory, sPattern, sFormId, aForbiddenValues, sExplainForbiddenValues)
  225. {
  226. var currentVal = $('#'+sFieldId).val();
  227. var bValid = true;
  228. var sMessage = null;
  229. if (bMandatory && (currentVal == ''))
  230. {
  231. bValid = false;
  232. }
  233. if ((sPattern != '') && (currentVal != ''))
  234. {
  235. re = new RegExp(sPattern);
  236. bValid = re.test(currentVal);
  237. }
  238. if (aForbiddenValues)
  239. {
  240. for(var i in aForbiddenValues)
  241. {
  242. if (aForbiddenValues[i] == currentVal)
  243. {
  244. bValid = false;
  245. sMessage = sExplainForbiddenValues;
  246. break;
  247. }
  248. }
  249. }
  250. if (oFormValidation[sFormId] == undefined) oFormValidation[sFormId] = [];
  251. if (!bValid)
  252. {
  253. $('#v_'+sFieldId).addClass('ui-state-error');
  254. oFormValidation[sFormId].push(sFieldId);
  255. if (sMessage)
  256. {
  257. $('#'+sFieldId).attr('title', sMessage).tooltip();
  258. if ($('#'+sFieldId).is(":focus"))
  259. {
  260. $('#'+sFieldId).tooltip('open');
  261. }
  262. }
  263. }
  264. else
  265. {
  266. $('#v_'+sFieldId).removeClass('ui-state-error');
  267. if ($('#'+sFieldId).data('uiTooltip'))
  268. {
  269. $('#'+sFieldId).tooltip('close');
  270. }
  271. $('#'+sFieldId).removeAttr('title');
  272. // Remove the element from the array
  273. iFieldIdPos = jQuery.inArray(sFieldId, oFormValidation[sFormId]);
  274. if (iFieldIdPos > -1)
  275. {
  276. oFormValidation[sFormId].splice(iFieldIdPos, 1);
  277. }
  278. }
  279. }
  280. function ValidateForm(sFormId, bValidateAll)
  281. {
  282. oFormValidation[sFormId] = [];
  283. if (bValidateAll)
  284. {
  285. $('#'+sFormId+' :input').trigger('validate');
  286. }
  287. else
  288. {
  289. // Only the visible fields
  290. $('#'+sFormId+' :input:visible').each(function() {
  291. $(this).trigger('validate');
  292. });
  293. }
  294. return oFormValidation[sFormId];
  295. }
  296. function ReadFormParams(sFormId)
  297. {
  298. var oMap = { };
  299. $('#'+sFormId+' :input').each( function() {
  300. if ($(this).parent().is(':visible'))
  301. {
  302. var sName = $(this).attr('name');
  303. if (sName && sName != '')
  304. {
  305. if (this.type == 'checkbox')
  306. {
  307. oMap[sName] = ($(this).attr('checked') == 'checked');
  308. }
  309. else
  310. {
  311. oMap[sName] = $(this).val();
  312. }
  313. }
  314. }
  315. });
  316. return oMap;
  317. }
  318. function SubmitForm(sFormId, onSubmitResult)
  319. {
  320. var aErrors = ValidateForm(sFormId, false);
  321. if (aErrors.length == 0)
  322. {
  323. var oMap = ReadFormParams(sFormId);
  324. oMap.module_name = sCurrentModule;
  325. $('#'+sFormId+' :input').each( function() {
  326. if ($(this).parent().is(':visible'))
  327. {
  328. var sName = $(this).attr('name');
  329. if (sName && sName != '')
  330. {
  331. if (this.type == 'checkbox')
  332. {
  333. oMap[sName] = ($(this).attr('checked') == 'checked');
  334. }
  335. else
  336. {
  337. oMap[sName] = $(this).val();
  338. }
  339. }
  340. }
  341. });
  342. $.post(GetAbsoluteUrlAppRoot()+'designer/module.php', oMap, function(data)
  343. {
  344. onSubmitResult(data);
  345. });
  346. }
  347. else
  348. {
  349. // TODO: better error reporting !!!
  350. alert('Please fill all the fields before continuing...');
  351. }
  352. }