utils.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. // Some general purpose JS functions for the iTop application
  2. //IE 8 compatibility, copied from: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/IndexOf
  3. if (!Array.prototype.indexOf) {
  4. if (false) // deactivated since it causes troubles: for(k in aData) => returns the indexOf function as first element on empty arrays !
  5. {
  6. Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
  7. "use strict";
  8. if (this == null) {
  9. throw new TypeError();
  10. }
  11. var t = Object(this);
  12. var len = t.length >>> 0;
  13. if (len === 0) {
  14. return -1;
  15. }
  16. var n = 0;
  17. if (arguments.length > 1) {
  18. n = Number(arguments[1]);
  19. if (n != n) { // shortcut for verifying if it's NaN
  20. n = 0;
  21. } else if (n != 0 && n != Infinity && n != -Infinity) {
  22. n = (n > 0 || -1) * Math.floor(Math.abs(n));
  23. }
  24. }
  25. if (n >= len) {
  26. return -1;
  27. }
  28. var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
  29. for (; k < len; k++) {
  30. if (k in t && t[k] === searchElement) {
  31. return k;
  32. }
  33. }
  34. return -1;
  35. }
  36. }
  37. }
  38. /**
  39. * Reload a truncated list
  40. */
  41. aTruncatedLists = {}; // To keep track of the list being loaded, each member is an ajaxRequest object
  42. function ReloadTruncatedList(divId, sSerializedFilter, sExtraParams)
  43. {
  44. $('#'+divId).block();
  45. //$('#'+divId).blockUI();
  46. if (aTruncatedLists[divId] != undefined)
  47. {
  48. try
  49. {
  50. aAjaxRequest = aTruncatedLists[divId];
  51. aAjaxRequest.abort();
  52. }
  53. catch(e)
  54. {
  55. // Do nothing special, just continue
  56. console.log('Uh,uh, exception !');
  57. }
  58. }
  59. aTruncatedLists[divId] = $.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?style=list',
  60. { operation: 'ajax', filter: sSerializedFilter, extra_params: sExtraParams },
  61. function(data)
  62. {
  63. aTruncatedLists[divId] = undefined;
  64. if (data.length > 0)
  65. {
  66. $('#'+divId).html(data);
  67. $('#'+divId+' .listResults').tableHover(); // hover tables
  68. $('#'+divId+' .listResults').each( function()
  69. {
  70. var table = $(this);
  71. var id = $(this).parent();
  72. aTruncatedLists[divId] = undefined;
  73. var checkbox = (table.find('th:first :checkbox').length > 0);
  74. if (checkbox)
  75. {
  76. // There is a checkbox in the first column, don't make it sortable
  77. table.tablesorter( { headers: { 0: {sorter: false}}, widgets: ['myZebra', 'truncatedList']} ).tablesorterPager({container: $("#pager")}); // sortable and zebra tables
  78. }
  79. else
  80. {
  81. // There is NO checkbox in the first column, all columns are considered sortable
  82. table.tablesorter( { widgets: ['myZebra', 'truncatedList']} ).tablesorterPager({container: $("#pager"), totalRows:97, filter: sSerializedFilter, extra_params: sExtraParams }); // sortable and zebra tables
  83. }
  84. });
  85. $('#'+divId).unblock();
  86. }
  87. }
  88. );
  89. }
  90. /**
  91. * Truncate a previously expanded list !
  92. */
  93. function TruncateList(divId, iLimit, sNewLabel, sLinkLabel)
  94. {
  95. $('#'+divId).block();
  96. var iCount = 0;
  97. $('#'+divId+' table.listResults tr:gt('+iLimit+')').each( function(){
  98. $(this).remove();
  99. });
  100. $('#lbl_'+divId).html(sNewLabel);
  101. $('#'+divId+' table.listResults tr:last td').addClass('truncated');
  102. $('#'+divId+' table.listResults').addClass('truncated');
  103. $('#trc_'+divId).html(sLinkLabel);
  104. $('#'+divId+' .listResults').trigger("update"); // Reset the cache
  105. $('#'+divId).unblock();
  106. }
  107. /**
  108. * Reload any block -- used for periodic auto-reload
  109. */
  110. function ReloadBlock(divId, sStyle, sSerializedFilter, sExtraParams)
  111. {
  112. // Check if the user is not editing the list properties right now
  113. var bDialogOpen = false;
  114. var oDataTable = $('#'+divId+' :itop-datatable');
  115. var bIsDataTable = false;
  116. if (oDataTable.length > 0)
  117. {
  118. bDialogOpen = oDataTable.datatable('IsDialogOpen');
  119. bIsDataTable = true;
  120. }
  121. if (!bDialogOpen)
  122. {
  123. if (bIsDataTable)
  124. {
  125. oDataTable.datatable('DoRefresh');
  126. }
  127. else
  128. {
  129. $('#'+divId).block();
  130. $.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?style='+sStyle,
  131. { operation: 'ajax', filter: sSerializedFilter, extra_params: sExtraParams },
  132. function(data){
  133. $('#'+divId).empty();
  134. $('#'+divId).append(data);
  135. $('#'+divId).removeClass('loading');
  136. }
  137. );
  138. }
  139. }
  140. }
  141. /**
  142. * Update the display and value of a file input widget when the user picks a new file
  143. */
  144. function UpdateFileName(id, sNewFileName)
  145. {
  146. var aPath = sNewFileName.split('\\');
  147. var sNewFileName = aPath[aPath.length-1];
  148. $('#'+id).val(sNewFileName);
  149. $('#'+id).trigger('validate');
  150. $('#name_'+id).text(sNewFileName);
  151. return true;
  152. }
  153. /**
  154. * Reload a search form for the specified class
  155. */
  156. function ReloadSearchForm(divId, sClassName, sBaseClass, sContext)
  157. {
  158. var oDiv = $('#ds_'+divId);
  159. oDiv.block();
  160. // deprecated in jQuery 1.8
  161. //var oFormEvents = $('#ds_'+divId+' form').data('events');
  162. var oForm = $('#ds_'+divId+' form');
  163. var oFormEvents = $._data(oForm[0], "events");
  164. // Save the submit handlers
  165. aSubmit = new Array();
  166. if ( (oFormEvents != null) && (oFormEvents.submit != undefined))
  167. {
  168. for(var index = 0; index < oFormEvents.submit.length; index++)
  169. {
  170. aSubmit [index ] = { data:oFormEvents.submit[index].data, namespace:oFormEvents.submit[index].namespace, handler: oFormEvents.submit[index].handler};
  171. }
  172. }
  173. sAction = $('#ds_'+divId+' form').attr('action');
  174. // Save the current values in the form
  175. var oMap = {};
  176. $('#ds_'+divId+" form :input[name!='']").each(function() {
  177. oMap[this.name] = this.value;
  178. });
  179. oMap.operation = 'search_form';
  180. oMap.className = sClassName;
  181. oMap.baseClass = sBaseClass;
  182. oMap.currentId = divId;
  183. oMap.action = sAction;
  184. $.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?'+sContext, oMap,
  185. function(data) {
  186. oDiv.empty();
  187. oDiv.append(data);
  188. if (aSubmit.length > 0)
  189. {
  190. var oForm = $('#ds_'+divId+' form'); // Form was reloaded, recompute it
  191. for(var index = 0; index < aSubmit.length; index++)
  192. {
  193. // Restore the previously bound submit handlers
  194. if (aSubmit[index].data != undefined)
  195. {
  196. oForm.bind('submit.'+aSubmit[index].namespace, aSubmit[index].data, aSubmit[index].handler)
  197. }
  198. else
  199. {
  200. oForm.bind('submit.'+aSubmit[index].namespace, aSubmit[index].handler)
  201. }
  202. }
  203. }
  204. oDiv.unblock();
  205. oDiv.parent().resize(); // Inform the parent that the form has just been (potentially) resized
  206. }
  207. );
  208. }
  209. /**
  210. * Stores - in a persistent way - user specific preferences
  211. * depends on a global variable oUserPreferences created/filled by the iTopWebPage
  212. * that acts as a local -write through- cache
  213. */
  214. function SetUserPreference(sPreferenceCode, sPrefValue, bPersistent)
  215. {
  216. sPreviousValue = undefined;
  217. try
  218. {
  219. sPreviousValue = oUserPreferences[sPreferenceCode];
  220. }
  221. catch(err)
  222. {
  223. sPreviousValue = undefined;
  224. }
  225. oUserPreferences[sPreferenceCode] = sPrefValue;
  226. if (bPersistent && (sPrefValue != sPreviousValue))
  227. {
  228. ajax_request = $.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php',
  229. { operation: 'set_pref', code: sPreferenceCode, value: sPrefValue} ); // Make it persistent
  230. }
  231. }
  232. /**
  233. * Get user specific preferences
  234. * depends on a global variable oUserPreferences created/filled by the iTopWebPage
  235. * that acts as a local -write through- cache
  236. */
  237. function GetUserPreference(sPreferenceCode, sDefaultValue)
  238. {
  239. var value = sDefaultValue;
  240. if ( oUserPreferences[sPreferenceCode] != undefined)
  241. {
  242. value = oUserPreferences[sPreferenceCode];
  243. }
  244. return value;
  245. }
  246. /**
  247. * Check/uncheck a whole list of checkboxes
  248. */
  249. function CheckAll(sSelector, bValue)
  250. {
  251. var value = bValue;
  252. $(sSelector).each( function() {
  253. if (this.checked != value)
  254. {
  255. this.checked = value;
  256. $(this).trigger('change');
  257. }
  258. });
  259. }
  260. /**
  261. * Toggle (enabled/disabled) the specified field of a form
  262. */
  263. function ToogleField(value, field_id)
  264. {
  265. if (value)
  266. {
  267. $('#'+field_id).removeAttr('disabled');
  268. // In case the field is rendered as a div containing several inputs (e.g. RedundancySettings)
  269. $('#'+field_id+' :input').removeAttr('disabled');
  270. }
  271. else
  272. {
  273. $('#'+field_id).attr('disabled', 'disabled');
  274. // In case the field is rendered as a div containing several inputs (e.g. RedundancySettings)
  275. $('#'+field_id+' :input').attr('disabled', 'disabled');
  276. }
  277. $('#'+field_id).trigger('update');
  278. $('#'+field_id).trigger('validate');
  279. }
  280. /**
  281. * For the fields that cannot be visually disabled, they can be blocked
  282. * @return
  283. */
  284. function BlockField(field_id, bBlocked)
  285. {
  286. if (bBlocked)
  287. {
  288. $('#'+field_id).block({ message: ' ** disabled ** '});
  289. }
  290. else
  291. {
  292. $('#'+field_id).unblock();
  293. }
  294. }
  295. /**
  296. * Updates (enables/disables) a "duration" field
  297. */
  298. function ToggleDurationField(field_id)
  299. {
  300. // Toggle all the subfields that compose the "duration" input
  301. aSubFields = new Array('d', 'h', 'm', 's');
  302. if ($('#'+field_id).attr('disabled'))
  303. {
  304. for(var i=0; i<aSubFields.length; i++)
  305. {
  306. $('#'+field_id+'_'+aSubFields[i]).attr('disabled', 'disabled');
  307. }
  308. }
  309. else
  310. {
  311. for(var i=0; i<aSubFields.length; i++)
  312. {
  313. $('#'+field_id+'_'+aSubFields[i]).removeAttr('disabled');
  314. }
  315. }
  316. }
  317. /**
  318. * PropagateCheckBox
  319. */
  320. function PropagateCheckBox(bCurrValue, aFieldsList, bCheck)
  321. {
  322. if (bCurrValue == bCheck)
  323. {
  324. for(var i=0;i<aFieldsList.length;i++)
  325. {
  326. $('#enable_'+aFieldsList[i]).attr('checked', bCheck);
  327. ToogleField(bCheck, aFieldsList[i]);
  328. }
  329. }
  330. }
  331. function FixTableSorter(table)
  332. {
  333. if ($('th.header', table).length == 0)
  334. {
  335. // Table is not sort-able, let's fix it
  336. var checkbox = (table.find('th:first :checkbox').length > 0);
  337. if (checkbox)
  338. {
  339. // There is a checkbox in the first column, don't make it sort-able
  340. table.tablesorter( { headers: { 0: {sorter: false}}, widgets: ['myZebra', 'truncatedList']} ); // sort-able and zebra tables
  341. }
  342. else
  343. {
  344. // There is NO checkbox in the first column, all columns are considered sort-able
  345. table.tablesorter( { widgets: ['myZebra', 'truncatedList']} ); // sort-able and zebra tables
  346. }
  347. }
  348. }
  349. function DashletCreationDlg(sOQL)
  350. {
  351. $.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', {operation: 'dashlet_creation_dlg', oql: sOQL}, function(data){
  352. $('body').append(data);
  353. });
  354. return false;
  355. }
  356. function ShortcutListDlg(sOQL, sDataTableId, sContext)
  357. {
  358. var sDataTableName = 'datatable_'+sDataTableId;
  359. var oTableSettings = {
  360. oColumns: $('#'+sDataTableName).datatable('option', 'oColumns'),
  361. iPageSize: $('#'+sDataTableName).datatable('option', 'iPageSize')
  362. };
  363. var sTableSettings = JSON.stringify(oTableSettings);
  364. $.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?'+sContext, {operation: 'shortcut_list_dlg', oql: sOQL, table_settings: sTableSettings}, function(data){
  365. $('body').append(data);
  366. });
  367. return false;
  368. }
  369. function DisplayHistory(sSelector, sFilter, iCount, iStart)
  370. {
  371. $(sSelector).block();
  372. var oParams = { operation: 'history_from_filter', filter: sFilter, start: iStart, count: iCount };
  373. $.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', oParams, function(data) {
  374. $(sSelector).html(data).unblock();
  375. }
  376. );
  377. }