linkswidget.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. // JavaScript Document
  2. function LinksWidget(id, sClass, sAttCode, iInputId, sSuffix, bDuplicates, oWizHelper, sExtKeyToRemote)
  3. {
  4. this.id = id;
  5. this.iInputId = iInputId;
  6. this.sClass = sClass;
  7. this.sAttCode = sAttCode;
  8. this.sSuffix = sSuffix;
  9. this.bDuplicates = bDuplicates;
  10. this.oWizardHelper = oWizHelper;
  11. this.sExtKeyToRemote = sExtKeyToRemote;
  12. this.iMaxAddedId = 0;
  13. this.aAdded = [];
  14. this.aRemoved = [];
  15. this.aModified = {};
  16. var me = this;
  17. this.Init = function()
  18. {
  19. // make sure that the form is clean
  20. $('#linkedset_'+this.id+' .selection').each( function() { this.checked = false; });
  21. $('#'+this.id+'_btnRemove').attr('disabled','disabled');
  22. $('#linkedset_'+me.id).on('remove', function() {
  23. // prevent having the dlg div twice
  24. $('#dlg_'+me.id).remove();
  25. });
  26. $('#linkedset_'+me.id+' :input').off('change').on('change', function() {
  27. if (!($(this).hasClass('selection'))) {
  28. var oCheckbox = $(this).closest('tr').find('.selection');
  29. var iLink = oCheckbox.attr('data-link-id');
  30. var iUniqueId = oCheckbox.attr('data-unique-id');
  31. var sAttCode = $(this).closest('.attribute-edit').attr('data-attcode');
  32. var value = $(this).val();
  33. return me.OnValueChange(iLink, iUniqueId, sAttCode, value);
  34. }
  35. return true;
  36. });
  37. var oInput = $('#'+this.iInputId);
  38. oInput.bind('update_value', function() { $(this).val(me.GetUpdatedValue()); });
  39. oInput.closest('form').submit(function() { return me.OnFormSubmit(); });
  40. };
  41. this.RemoveSelected = function()
  42. {
  43. var my_id = '#'+me.id;
  44. $('#linkedset_'+me.id+' .selection:checked').each(function() {
  45. $(my_id+'_row_'+this.value).remove();
  46. var iLink = $(this).attr('data-link-id');
  47. if (iLink > 0)
  48. {
  49. me.aRemoved.push(iLink);
  50. if (me.aModified.hasOwnProperty(iLink))
  51. {
  52. delete me.aModified[iLink];
  53. }
  54. }
  55. else
  56. {
  57. var iUniqueId = $(this).attr('data-unique-id');
  58. me.aAdded[iUniqueId] = null;
  59. }
  60. });
  61. // Disable the button since all the selected items have been removed
  62. $(my_id+'_btnRemove').attr('disabled','disabled');
  63. // Re-run the zebra plugin to properly highlight the remaining lines & and take into account the removed ones
  64. $('#linkedset_'+this.id+' .listResults').trigger('update').trigger("applyWidgets");
  65. if ($('#linkedset_'+this.id+' .selection').length == 0)
  66. {
  67. // All items were removed: add a dummy hidden input to make sure that the linkset will be updated (emptied) when posted
  68. $('#'+me.id+'_empty_row').show();
  69. }
  70. };
  71. this.OnSelectChange = function()
  72. {
  73. var nbChecked = $('#linkedset_'+me.id+' .selection:checked').length;
  74. if (nbChecked > 0)
  75. {
  76. $('#'+me.id+'_btnRemove').removeAttr('disabled');
  77. }
  78. else
  79. {
  80. $('#'+me.id+'_btnRemove').attr('disabled','disabled');
  81. }
  82. };
  83. this.AddObjects = function()
  84. {
  85. var me = this;
  86. $('#'+me.id+'_indicatorAdd').html('&nbsp;<img src="../images/indicator.gif"/>');
  87. me.oWizardHelper.UpdateWizard();
  88. var theMap = { sAttCode: me.sAttCode,
  89. iInputId: me.iInputId,
  90. sSuffix: me.sSuffix,
  91. bDuplicates: me.bDuplicates,
  92. 'class' : me.sClass,
  93. operation: 'addObjects',
  94. json: me.oWizardHelper.ToJSON()
  95. };
  96. $.post( GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', theMap,
  97. function(data)
  98. {
  99. $('#dlg_'+me.id).html(data);
  100. $('#dlg_'+me.id).dialog('open');
  101. me.UpdateSizes(null, null);
  102. me.SearchObjectsToAdd();
  103. $('#'+me.id+'_indicatorAdd').html('');
  104. },
  105. 'html'
  106. );
  107. };
  108. this.SearchObjectsToAdd = function()
  109. {
  110. var theMap = { sAttCode: me.sAttCode,
  111. iInputId: me.iInputId,
  112. sSuffix: me.sSuffix,
  113. bDuplicates: me.bDuplicates
  114. };
  115. me.UpdateButtons(0);
  116. // Gather the parameters from the search form
  117. $('#SearchFormToAdd_'+me.id+' :input').each( function() {
  118. if (this.name != '')
  119. {
  120. var val = $(this).val(); // supports multiselect as well
  121. if (val !== null)
  122. {
  123. theMap[this.name] = val;
  124. }
  125. }
  126. });
  127. // Gather the already linked target objects
  128. theMap.aAlreadyLinked = [];
  129. $('#linkedset_'+me.id+' .selection:input').each(function(i) {
  130. var iRemote = $(this).attr('data-remote-id');
  131. theMap.aAlreadyLinked.push(iRemote);
  132. });
  133. theMap['sRemoteClass'] = theMap['class']; // swap 'class' (defined in the form) and 'remoteClass'
  134. theMap['class'] = me.sClass;
  135. theMap.operation = 'searchObjectsToAdd'; // Override what is defined in the form itself
  136. sSearchAreaId = '#SearchResultsToAdd_'+me.id;
  137. $(sSearchAreaId).block();
  138. // Run the query and display the results
  139. $.post( GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', theMap,
  140. function(data)
  141. {
  142. $(sSearchAreaId).html(data);
  143. $(sSearchAreaId+' .listResults').tableHover();
  144. $('#count_'+me.id).change(function(){
  145. var c = this.value;
  146. me.UpdateButtons(c);
  147. });
  148. FixSearchFormsDisposition();
  149. me.UpdateSizes(null, null);
  150. $(sSearchAreaId).unblock();
  151. },
  152. 'html'
  153. );
  154. return false; // Don't submit the form, stay in the current page !
  155. };
  156. this.UpdateButtons = function(iCount)
  157. {
  158. var okBtn = $('#btn_ok_'+me.id);
  159. if (iCount > 0)
  160. {
  161. okBtn.removeAttr('disabled');
  162. }
  163. else
  164. {
  165. okBtn.attr('disabled', 'disabled');
  166. }
  167. };
  168. this.DoAddObjects = function()
  169. {
  170. var theMap = { sAttCode: me.sAttCode,
  171. iInputId: me.iInputId,
  172. sSuffix: me.sSuffix,
  173. bDuplicates: me.bDuplicates,
  174. 'class': me.sClass
  175. };
  176. // Gather the parameters from the search form
  177. var context = $('#SearchResultsToAdd_'+me.id);
  178. var selectionMode = $(':input[name=selectionMode]', context);
  179. if (selectionMode.length > 0)
  180. {
  181. // Paginated table retrieve the mode and the exceptions
  182. var sMode = selectionMode.val();
  183. theMap['selectionMode'] = sMode;
  184. $('#fs_SearchFormToAdd_'+me.id+' :input').each(
  185. function(i)
  186. {
  187. theMap[this.name] = this.value;
  188. }
  189. );
  190. theMap['sRemoteClass'] = theMap['class']; // swap 'class' (defined in the form) and 'remoteClass'
  191. theMap['class'] = me.sClass;
  192. $(' :input[name^=storedSelection]', context).each(function() {
  193. if (theMap[this.name] == undefined)
  194. {
  195. theMap[this.name] = [];
  196. }
  197. theMap[this.name].push(this.value);
  198. $(this).remove(); // Remove the selection for the next time the dialog re-opens
  199. });
  200. // Retrieve the 'filter' definition
  201. var table = $('#ResultsToAdd_'+me.id).find('table.listResults')[0];
  202. theMap['filter'] = table.config.filter;
  203. theMap['extra_params'] = table.config.extra_params;
  204. }
  205. // else
  206. // {
  207. // Normal table, retrieve all the checked check-boxes
  208. $(':checked[name^=selectObject]', context).each(
  209. function(i)
  210. {
  211. if ( (this.name != '') && ((this.type != 'checkbox') || (this.checked)) )
  212. {
  213. arrayExpr = /\[\]$/;
  214. if (arrayExpr.test(this.name))
  215. {
  216. // Array
  217. if (theMap[this.name] == undefined)
  218. {
  219. theMap[this.name] = [];
  220. }
  221. theMap[this.name].push(this.value);
  222. }
  223. else
  224. {
  225. theMap[this.name] = this.value;
  226. }
  227. }
  228. $(this).parents('tr:first').remove(); // Remove the whole line, so that, next time the dialog gets displayed it's no longer there
  229. }
  230. );
  231. // }
  232. theMap['operation'] = 'doAddObjects';
  233. theMap['max_added_id'] = this.iMaxAddedId;
  234. if (me.oWizardHelper == null)
  235. {
  236. theMap['json'] = '';
  237. }
  238. else
  239. {
  240. // Not inside a "search form", updating a real object
  241. me.oWizardHelper.UpdateWizard();
  242. theMap['json'] = me.oWizardHelper.ToJSON();
  243. }
  244. $('#busy_'+me.iInputId).html('&nbsp;<img src="../images/indicator.gif"/>');
  245. // Run the query and display the results
  246. $.post( GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', theMap,
  247. function(data)
  248. {
  249. if (data != '')
  250. {
  251. $('#'+me.id+'_empty_row').hide();
  252. $('#linkedset_'+me.id+' .listResults tbody').prepend(data);
  253. $('#linkedset_'+me.id+' .listResults').trigger('update');
  254. $('#linkedset_'+me.id+' .listResults').tableHover();
  255. $('#linkedset_'+me.id+' .listResults').trigger('update').trigger("applyWidgets"); // table is already sortable, just refresh it
  256. $('#linkedset_'+me.id+' :input').each( function() { $(this).trigger('validate', ''); }); // Validate newly added form fields...
  257. $('#busy_'+me.iInputId).html('');
  258. }
  259. },
  260. 'html'
  261. );
  262. $('#dlg_'+me.id).dialog('close');
  263. return false;
  264. };
  265. this.OnLinkAdded = function(iAddedId, iRemote)
  266. {
  267. // Assumption: this identifier will be higher than the previous one
  268. me.iMaxAddedId = iAddedId;
  269. var sFormPrefix = me.iInputId;
  270. oAdded = {};
  271. oAdded['formPrefix'] = sFormPrefix;
  272. oAdded['attr_' + sFormPrefix + this.sExtKeyToRemote] = iRemote;
  273. me.aAdded[iAddedId] = oAdded;
  274. $('#linkedset_'+me.id+' :input').off('change').on('change', function() {
  275. if (!($(this).hasClass('selection'))) {
  276. var oCheckbox = $(this).closest('tr').find('.selection');
  277. var iLink = oCheckbox.attr('data-link-id');
  278. var iUniqueId = oCheckbox.attr('data-unique-id');
  279. var sAttCode = $(this).closest('.attribute-edit').attr('data-attcode');
  280. var value = $(this).val();
  281. return me.OnValueChange(iLink, iUniqueId, sAttCode, value);
  282. }
  283. return true;
  284. });
  285. };
  286. this.UpdateSizes = function(event, ui)
  287. {
  288. var dlg = $('#dlg_'+me.id);
  289. var searchForm = $('#SearchFormToAdd_'+me.id);
  290. var results = $('#SearchResultsToAdd_'+me.id);
  291. var padding_right = 0;
  292. if (dlg.css('padding-right'))
  293. {
  294. padding_right = parseInt(dlg.css('padding-right').replace('px', ''));
  295. }
  296. var padding_left = 0;
  297. if (dlg.css('padding-left'))
  298. {
  299. padding_left = parseInt(dlg.css('padding-left').replace('px', ''));
  300. }
  301. var padding_top = 0;
  302. if (dlg.css('padding-top'))
  303. {
  304. padding_top = parseInt(dlg.css('padding-top').replace('px', ''));
  305. }
  306. var padding_bottom = 0;
  307. if (dlg.css('padding-bottom'))
  308. {
  309. padding_bottom = parseInt(dlg.css('padding-bottom').replace('px', ''));
  310. }
  311. width = dlg.innerWidth() - padding_right - padding_left - 22; // 5 (margin-left) + 5 (padding-left) + 5 (padding-right) + 5 (margin-right) + 2 for rounding !
  312. height = dlg.innerHeight() - padding_top - padding_bottom -22;
  313. wizard = dlg.find('.wizContainer:first');
  314. wizard.width(width);
  315. wizard.height(height);
  316. form_height = searchForm.outerHeight();
  317. results.height(height - form_height - 40); // Leave some space for the buttons
  318. };
  319. this.GetUpdatedValue = function()
  320. {
  321. var sSelector = '#linkedset_'+me.id+' :input[name^=attr_'+me.id+']';
  322. var aIndexes = [];
  323. var aValues = [];
  324. $(sSelector).each(function() {
  325. var re = /\[([^\[]+)\]\[(.+)\]/;
  326. var aMatches = [];
  327. if (aMatches = this.name.match(re))
  328. {
  329. var idx = aMatches[1];
  330. var index = jQuery.inArray(idx, aIndexes);
  331. if (index == -1)
  332. {
  333. aIndexes.push(idx);
  334. index = jQuery.inArray(idx, aIndexes);
  335. aValues[index] = {};
  336. }
  337. var value = $(this).val();
  338. if (aMatches[2] == "id")
  339. {
  340. var iId = parseInt(aMatches[1], 10);
  341. if (iId < 0)
  342. {
  343. aValues[index][me.sExtKeyToRemote] = -iId;
  344. }
  345. else
  346. {
  347. aValues[index]['id'] = value;
  348. }
  349. }
  350. else
  351. {
  352. aValues[index][aMatches[2]] = value;
  353. }
  354. }
  355. });
  356. return JSON.stringify(aValues);
  357. };
  358. this.OnValueChange = function(iLink, iUniqueId, sAttCode, value)
  359. {
  360. var sFormPrefix = me.iInputId;
  361. if (iLink > 0) {
  362. // Modifying an existing link
  363. var oModified = me.aModified[iLink];
  364. if (oModified == undefined) {
  365. // Still not marked as modified
  366. oModified = {};
  367. oModified['formPrefix'] = sFormPrefix;
  368. }
  369. // Weird formatting, aligned with the output of the direct links widget (new links to be created)
  370. oModified['attr_' + sFormPrefix + sAttCode] = value;
  371. me.aModified[iLink] = oModified;
  372. }
  373. else {
  374. // Modifying a newly added link - the structure should already be up to date
  375. me.aAdded[iUniqueId]['attr_' + sFormPrefix + sAttCode] = value;
  376. }
  377. };
  378. this.OnFormSubmit = function()
  379. {
  380. var oDiv = $('#linkedset_'+me.id);
  381. var sToBeDeleted = JSON.stringify(me.aRemoved);
  382. $('<input type="hidden" name="attr_'+me.sAttCode+'_tbd">').val(sToBeDeleted).appendTo(oDiv);
  383. var sToBeModified = JSON.stringify(me.aModified);
  384. $('<input type="hidden" name="attr_'+me.sAttCode+'_tbm">').val(sToBeModified).appendTo(oDiv);
  385. var aToBeCreated = [];
  386. me.aAdded.forEach(function(oAdded){
  387. if (oAdded != null)
  388. {
  389. aToBeCreated.push(oAdded);
  390. }
  391. });
  392. var sToBeCreated = JSON.stringify(aToBeCreated);
  393. $('<input type="hidden" name="attr_'+me.sAttCode+'_tbc">').val(sToBeCreated).appendTo(oDiv);
  394. };
  395. }