You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

application.js 29KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015
  1. /* Redmine - project management software
  2. Copyright (C) 2006-2019 Jean-Philippe Lang */
  3. /* Fix for CVE-2015-9251, to be removed with JQuery >= 3.0 */
  4. $.ajaxPrefilter(function (s) {
  5. if (s.crossDomain) {
  6. s.contents.script = false;
  7. }
  8. });
  9. function checkAll(id, checked) {
  10. $('#'+id).find('input[type=checkbox]:enabled').prop('checked', checked);
  11. }
  12. function toggleCheckboxesBySelector(selector) {
  13. var all_checked = true;
  14. $(selector).each(function(index) {
  15. if (!$(this).is(':checked')) { all_checked = false; }
  16. });
  17. $(selector).prop('checked', !all_checked).trigger('change');
  18. }
  19. function showAndScrollTo(id, focus) {
  20. $('#'+id).show();
  21. if (focus !== null) {
  22. $('#'+focus).focus();
  23. }
  24. $('html, body').animate({scrollTop: $('#'+id).offset().top}, 100);
  25. }
  26. function toggleRowGroup(el) {
  27. var tr = $(el).parents('tr').first();
  28. var n = tr.next();
  29. tr.toggleClass('open');
  30. $(el).toggleClass('icon-expended icon-collapsed');
  31. while (n.length && !n.hasClass('group')) {
  32. n.toggle();
  33. n = n.next('tr');
  34. }
  35. }
  36. function collapseAllRowGroups(el) {
  37. var tbody = $(el).parents('tbody').first();
  38. tbody.children('tr').each(function(index) {
  39. if ($(this).hasClass('group')) {
  40. $(this).removeClass('open');
  41. $(this).find('.expander').switchClass('icon-expended', 'icon-collapsed');
  42. } else {
  43. $(this).hide();
  44. }
  45. });
  46. }
  47. function expandAllRowGroups(el) {
  48. var tbody = $(el).parents('tbody').first();
  49. tbody.children('tr').each(function(index) {
  50. if ($(this).hasClass('group')) {
  51. $(this).addClass('open');
  52. $(this).find('.expander').switchClass('icon-collapsed', 'icon-expended');
  53. } else {
  54. $(this).show();
  55. }
  56. });
  57. }
  58. function toggleAllRowGroups(el) {
  59. var tr = $(el).parents('tr').first();
  60. if (tr.hasClass('open')) {
  61. collapseAllRowGroups(el);
  62. } else {
  63. expandAllRowGroups(el);
  64. }
  65. }
  66. function toggleFieldset(el) {
  67. var fieldset = $(el).parents('fieldset').first();
  68. fieldset.toggleClass('collapsed');
  69. fieldset.children('legend').toggleClass('icon-expended icon-collapsed');
  70. fieldset.children('div').toggle();
  71. }
  72. function hideFieldset(el) {
  73. var fieldset = $(el).parents('fieldset').first();
  74. fieldset.toggleClass('collapsed');
  75. fieldset.children('div').hide();
  76. }
  77. // columns selection
  78. function moveOptions(theSelFrom, theSelTo) {
  79. $(theSelFrom).find('option:selected').detach().prop("selected", false).appendTo($(theSelTo));
  80. }
  81. function moveOptionUp(theSel) {
  82. $(theSel).find('option:selected').each(function(){
  83. $(this).prev(':not(:selected)').detach().insertAfter($(this));
  84. });
  85. }
  86. function moveOptionTop(theSel) {
  87. $(theSel).find('option:selected').detach().prependTo($(theSel));
  88. }
  89. function moveOptionDown(theSel) {
  90. $($(theSel).find('option:selected').get().reverse()).each(function(){
  91. $(this).next(':not(:selected)').detach().insertBefore($(this));
  92. });
  93. }
  94. function moveOptionBottom(theSel) {
  95. $(theSel).find('option:selected').detach().appendTo($(theSel));
  96. }
  97. function initFilters() {
  98. $('#add_filter_select').change(function() {
  99. addFilter($(this).val(), '', []);
  100. });
  101. $('#filters-table td.field input[type=checkbox]').each(function() {
  102. toggleFilter($(this).val());
  103. });
  104. $('#filters-table').on('click', 'td.field input[type=checkbox]', function() {
  105. toggleFilter($(this).val());
  106. });
  107. $('#filters-table').on('click', '.toggle-multiselect', function() {
  108. toggleMultiSelect($(this).siblings('select'))
  109. $(this).toggleClass('icon-toggle-plus icon-toggle-minus')
  110. });
  111. $('#filters-table').on('keypress', 'input[type=text]', function(e) {
  112. if (e.keyCode == 13) $(this).closest('form').submit();
  113. });
  114. }
  115. function addFilter(field, operator, values) {
  116. var fieldId = field.replace('.', '_');
  117. var tr = $('#tr_'+fieldId);
  118. var filterOptions = availableFilters[field];
  119. if (!filterOptions) return;
  120. if (filterOptions['remote'] && filterOptions['values'] == null) {
  121. $.getJSON(filtersUrl, {'name': field}).done(function(data) {
  122. filterOptions['values'] = data;
  123. addFilter(field, operator, values) ;
  124. });
  125. return;
  126. }
  127. if (tr.length > 0) {
  128. tr.show();
  129. } else {
  130. buildFilterRow(field, operator, values);
  131. }
  132. $('#cb_'+fieldId).prop('checked', true);
  133. toggleFilter(field);
  134. $('#add_filter_select').val('').find('option').each(function() {
  135. if ($(this).attr('value') == field) {
  136. $(this).attr('disabled', true);
  137. }
  138. });
  139. }
  140. function buildFilterRow(field, operator, values) {
  141. var fieldId = field.replace('.', '_');
  142. var filterTable = $("#filters-table");
  143. var filterOptions = availableFilters[field];
  144. if (!filterOptions) return;
  145. var operators = operatorByType[filterOptions['type']];
  146. var filterValues = filterOptions['values'];
  147. var i, select;
  148. var tr = $('<tr class="filter">').attr('id', 'tr_'+fieldId).html(
  149. '<td class="field"><input checked="checked" id="cb_'+fieldId+'" name="f[]" value="'+field+'" type="checkbox"><label for="cb_'+fieldId+'"> '+filterOptions['name']+'</label></td>' +
  150. '<td class="operator"><select id="operators_'+fieldId+'" name="op['+field+']"></td>' +
  151. '<td class="values"></td>'
  152. );
  153. filterTable.append(tr);
  154. select = tr.find('td.operator select');
  155. for (i = 0; i < operators.length; i++) {
  156. var option = $('<option>').val(operators[i]).text(operatorLabels[operators[i]]);
  157. if (operators[i] == operator) { option.prop('selected', true); }
  158. select.append(option);
  159. }
  160. select.change(function(){ toggleOperator(field); });
  161. switch (filterOptions['type']) {
  162. case "list":
  163. case "list_optional":
  164. case "list_status":
  165. case "list_subprojects":
  166. tr.find('td.values').append(
  167. '<span style="display:none;"><select class="value" id="values_'+fieldId+'_1" name="v['+field+'][]"></select>' +
  168. ' <span class="toggle-multiselect icon-only icon-toggle-plus">&nbsp;</span></span>'
  169. );
  170. select = tr.find('td.values select');
  171. if (values.length > 1) { select.attr('multiple', true); }
  172. for (i = 0; i < filterValues.length; i++) {
  173. var filterValue = filterValues[i];
  174. var option = $('<option>');
  175. if ($.isArray(filterValue)) {
  176. option.val(filterValue[1]).text(filterValue[0]);
  177. if ($.inArray(filterValue[1], values) > -1) {option.prop('selected', true);}
  178. if (filterValue.length == 3) {
  179. var optgroup = select.find('optgroup').filter(function(){return $(this).attr('label') == filterValue[2]});
  180. if (!optgroup.length) {optgroup = $('<optgroup>').attr('label', filterValue[2]);}
  181. option = optgroup.append(option);
  182. }
  183. } else {
  184. option.val(filterValue).text(filterValue);
  185. if ($.inArray(filterValue, values) > -1) {option.prop('selected', true);}
  186. }
  187. select.append(option);
  188. }
  189. break;
  190. case "date":
  191. case "date_past":
  192. tr.find('td.values').append(
  193. '<span style="display:none;"><input type="date" name="v['+field+'][]" id="values_'+fieldId+'_1" size="10" class="value date_value" /></span>' +
  194. ' <span style="display:none;"><input type="date" name="v['+field+'][]" id="values_'+fieldId+'_2" size="10" class="value date_value" /></span>' +
  195. ' <span style="display:none;"><input type="text" name="v['+field+'][]" id="values_'+fieldId+'" size="3" class="value" /> '+labelDayPlural+'</span>'
  196. );
  197. $('#values_'+fieldId+'_1').val(values[0]).datepickerFallback(datepickerOptions);
  198. $('#values_'+fieldId+'_2').val(values[1]).datepickerFallback(datepickerOptions);
  199. $('#values_'+fieldId).val(values[0]);
  200. break;
  201. case "string":
  202. case "text":
  203. tr.find('td.values').append(
  204. '<span style="display:none;"><input type="text" name="v['+field+'][]" id="values_'+fieldId+'" size="30" class="value" /></span>'
  205. );
  206. $('#values_'+fieldId).val(values[0]);
  207. break;
  208. case "relation":
  209. tr.find('td.values').append(
  210. '<span style="display:none;"><input type="text" name="v['+field+'][]" id="values_'+fieldId+'" size="6" class="value" /></span>' +
  211. '<span style="display:none;"><select class="value" name="v['+field+'][]" id="values_'+fieldId+'_1"></select></span>'
  212. );
  213. $('#values_'+fieldId).val(values[0]);
  214. select = tr.find('td.values select');
  215. for (i = 0; i < filterValues.length; i++) {
  216. var filterValue = filterValues[i];
  217. var option = $('<option>');
  218. option.val(filterValue[1]).text(filterValue[0]);
  219. if (values[0] == filterValue[1]) { option.prop('selected', true); }
  220. select.append(option);
  221. }
  222. break;
  223. case "integer":
  224. case "float":
  225. case "tree":
  226. tr.find('td.values').append(
  227. '<span style="display:none;"><input type="text" name="v['+field+'][]" id="values_'+fieldId+'_1" size="14" class="value" /></span>' +
  228. ' <span style="display:none;"><input type="text" name="v['+field+'][]" id="values_'+fieldId+'_2" size="14" class="value" /></span>'
  229. );
  230. $('#values_'+fieldId+'_1').val(values[0]);
  231. $('#values_'+fieldId+'_2').val(values[1]);
  232. break;
  233. }
  234. }
  235. function toggleFilter(field) {
  236. var fieldId = field.replace('.', '_');
  237. if ($('#cb_' + fieldId).is(':checked')) {
  238. $("#operators_" + fieldId).show().removeAttr('disabled');
  239. toggleOperator(field);
  240. } else {
  241. $("#operators_" + fieldId).hide().attr('disabled', true);
  242. enableValues(field, []);
  243. }
  244. }
  245. function enableValues(field, indexes) {
  246. var fieldId = field.replace('.', '_');
  247. $('#tr_'+fieldId+' td.values .value').each(function(index) {
  248. if ($.inArray(index, indexes) >= 0) {
  249. $(this).removeAttr('disabled');
  250. $(this).parents('span').first().show();
  251. } else {
  252. $(this).val('');
  253. $(this).attr('disabled', true);
  254. $(this).parents('span').first().hide();
  255. }
  256. if ($(this).hasClass('group')) {
  257. $(this).addClass('open');
  258. } else {
  259. $(this).show();
  260. }
  261. });
  262. }
  263. function toggleOperator(field) {
  264. var fieldId = field.replace('.', '_');
  265. var operator = $("#operators_" + fieldId);
  266. switch (operator.val()) {
  267. case "!*":
  268. case "*":
  269. case "nd":
  270. case "t":
  271. case "ld":
  272. case "nw":
  273. case "w":
  274. case "lw":
  275. case "l2w":
  276. case "nm":
  277. case "m":
  278. case "lm":
  279. case "y":
  280. case "o":
  281. case "c":
  282. case "*o":
  283. case "!o":
  284. enableValues(field, []);
  285. break;
  286. case "><":
  287. enableValues(field, [0,1]);
  288. break;
  289. case "<t+":
  290. case ">t+":
  291. case "><t+":
  292. case "t+":
  293. case ">t-":
  294. case "<t-":
  295. case "><t-":
  296. case "t-":
  297. enableValues(field, [2]);
  298. break;
  299. case "=p":
  300. case "=!p":
  301. case "!p":
  302. enableValues(field, [1]);
  303. break;
  304. default:
  305. enableValues(field, [0]);
  306. break;
  307. }
  308. }
  309. function toggleMultiSelect(el) {
  310. if (el.attr('multiple')) {
  311. el.removeAttr('multiple');
  312. el.attr('size', 1);
  313. } else {
  314. el.attr('multiple', true);
  315. if (el.children().length > 10)
  316. el.attr('size', 10);
  317. else
  318. el.attr('size', 4);
  319. }
  320. }
  321. function showTab(name, url) {
  322. $('#tab-content-' + name).parent().find('.tab-content').hide();
  323. $('#tab-content-' + name).show();
  324. $('#tab-' + name).closest('.tabs').find('a').removeClass('selected');
  325. $('#tab-' + name).addClass('selected');
  326. replaceInHistory(url)
  327. return false;
  328. }
  329. function showIssueHistory(journal, url) {
  330. tab_content = $('#tab-content-history');
  331. tab_content.parent().find('.tab-content').hide();
  332. tab_content.show();
  333. tab_content.parent().children('div.tabs').find('a').removeClass('selected');
  334. $('#tab-' + journal).addClass('selected');
  335. replaceInHistory(url)
  336. switch(journal) {
  337. case 'notes':
  338. tab_content.find('.journal:not(.has-notes)').hide();
  339. tab_content.find('.journal.has-notes').show();
  340. break;
  341. case 'properties':
  342. tab_content.find('.journal.has-notes').hide();
  343. tab_content.find('.journal:not(.has-notes)').show();
  344. break;
  345. default:
  346. tab_content.find('.journal').show();
  347. }
  348. return false;
  349. }
  350. function getRemoteTab(name, remote_url, url, load_always) {
  351. load_always = load_always || false;
  352. var tab_content = $('#tab-content-' + name);
  353. tab_content.parent().find('.tab-content').hide();
  354. tab_content.parent().children('div.tabs').find('a').removeClass('selected');
  355. $('#tab-' + name).addClass('selected');
  356. replaceInHistory(url);
  357. if (tab_content.children().length == 0 && load_always == false) {
  358. $.ajax({
  359. url: remote_url,
  360. type: 'get',
  361. success: function(data){
  362. tab_content.html(data)
  363. }
  364. });
  365. }
  366. tab_content.show();
  367. return false;
  368. }
  369. //replaces current URL with the "href" attribute of the current link
  370. //(only triggered if supported by browser)
  371. function replaceInHistory(url) {
  372. if ("replaceState" in window.history) {
  373. window.history.replaceState(null, document.title, url);
  374. }
  375. }
  376. function moveTabRight(el) {
  377. var lis = $(el).parents('div.tabs').first().find('ul').children();
  378. var bw = $(el).parents('div.tabs-buttons').outerWidth(true);
  379. var tabsWidth = 0;
  380. var i = 0;
  381. lis.each(function() {
  382. if ($(this).is(':visible')) {
  383. tabsWidth += $(this).outerWidth(true);
  384. }
  385. });
  386. if (tabsWidth < $(el).parents('div.tabs').first().width() - bw) { return; }
  387. $(el).siblings('.tab-left').removeClass('disabled');
  388. while (i<lis.length && !lis.eq(i).is(':visible')) { i++; }
  389. var w = lis.eq(i).width();
  390. lis.eq(i).hide();
  391. if (tabsWidth - w < $(el).parents('div.tabs').first().width() - bw) {
  392. $(el).addClass('disabled');
  393. }
  394. }
  395. function moveTabLeft(el) {
  396. var lis = $(el).parents('div.tabs').first().find('ul').children();
  397. var i = 0;
  398. while (i < lis.length && !lis.eq(i).is(':visible')) { i++; }
  399. if (i > 0) {
  400. lis.eq(i-1).show();
  401. $(el).siblings('.tab-right').removeClass('disabled');
  402. }
  403. if (i <= 1) {
  404. $(el).addClass('disabled');
  405. }
  406. }
  407. function displayTabsButtons() {
  408. var lis;
  409. var tabsWidth;
  410. var el;
  411. var numHidden;
  412. $('div.tabs').each(function() {
  413. el = $(this);
  414. lis = el.find('ul').children();
  415. tabsWidth = 0;
  416. numHidden = 0;
  417. lis.each(function(){
  418. if ($(this).is(':visible')) {
  419. tabsWidth += $(this).outerWidth(true);
  420. } else {
  421. numHidden++;
  422. }
  423. });
  424. var bw = $(el).parents('div.tabs-buttons').outerWidth(true);
  425. if ((tabsWidth < el.width() - bw) && (lis.length === 0 || lis.first().is(':visible'))) {
  426. el.find('div.tabs-buttons').hide();
  427. } else {
  428. el.find('div.tabs-buttons').show().children('button.tab-left').toggleClass('disabled', numHidden == 0);
  429. }
  430. });
  431. }
  432. function setPredecessorFieldsVisibility() {
  433. var relationType = $('#relation_relation_type');
  434. if (relationType.val() == "precedes" || relationType.val() == "follows") {
  435. $('#predecessor_fields').show();
  436. } else {
  437. $('#predecessor_fields').hide();
  438. }
  439. }
  440. function showModal(id, width, title) {
  441. var el = $('#'+id).first();
  442. if (el.length === 0 || el.is(':visible')) {return;}
  443. if (!title) title = el.find('h3.title').text();
  444. // moves existing modals behind the transparent background
  445. $(".modal").css('zIndex',99);
  446. el.dialog({
  447. width: width,
  448. modal: true,
  449. resizable: false,
  450. dialogClass: 'modal',
  451. title: title
  452. }).on('dialogclose', function(){
  453. $(".modal").css('zIndex',101);
  454. });
  455. el.find("input[type=text], input[type=submit]").first().focus();
  456. }
  457. function hideModal(el) {
  458. var modal;
  459. if (el) {
  460. modal = $(el).parents('.ui-dialog-content');
  461. } else {
  462. modal = $('#ajax-modal');
  463. }
  464. modal.dialog("close");
  465. }
  466. function collapseScmEntry(id) {
  467. $('.'+id).each(function() {
  468. if ($(this).hasClass('open')) {
  469. collapseScmEntry($(this).attr('id'));
  470. }
  471. $(this).hide();
  472. });
  473. $('#'+id).removeClass('open');
  474. }
  475. function expandScmEntry(id) {
  476. $('.'+id).each(function() {
  477. $(this).show();
  478. if ($(this).hasClass('loaded') && !$(this).hasClass('collapsed')) {
  479. expandScmEntry($(this).attr('id'));
  480. }
  481. });
  482. $('#'+id).addClass('open');
  483. }
  484. function scmEntryClick(id, url) {
  485. var el = $('#'+id);
  486. if (el.hasClass('open')) {
  487. collapseScmEntry(id);
  488. el.find('.expander').switchClass('icon-expended', 'icon-collapsed');
  489. el.addClass('collapsed');
  490. return false;
  491. } else if (el.hasClass('loaded')) {
  492. expandScmEntry(id);
  493. el.find('.expander').switchClass('icon-collapsed', 'icon-expended');
  494. el.removeClass('collapsed');
  495. return false;
  496. }
  497. if (el.hasClass('loading')) {
  498. return false;
  499. }
  500. el.addClass('loading');
  501. $.ajax({
  502. url: url,
  503. success: function(data) {
  504. el.after(data);
  505. el.addClass('open').addClass('loaded').removeClass('loading');
  506. el.find('.expander').switchClass('icon-collapsed', 'icon-expended');
  507. }
  508. });
  509. return true;
  510. }
  511. function randomKey(size) {
  512. var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
  513. var key = '';
  514. for (var i = 0; i < size; i++) {
  515. key += chars.charAt(Math.floor(Math.random() * chars.length));
  516. }
  517. return key;
  518. }
  519. function updateIssueFrom(url, el) {
  520. $('#all_attributes input, #all_attributes textarea, #all_attributes select').each(function(){
  521. $(this).data('valuebeforeupdate', $(this).val());
  522. });
  523. if (el) {
  524. $("#form_update_triggered_by").val($(el).attr('id'));
  525. }
  526. return $.ajax({
  527. url: url,
  528. type: 'post',
  529. data: $('#issue-form').serialize()
  530. });
  531. }
  532. function replaceIssueFormWith(html){
  533. var replacement = $(html);
  534. $('#all_attributes input, #all_attributes textarea, #all_attributes select').each(function(){
  535. var object_id = $(this).attr('id');
  536. if (object_id && $(this).data('valuebeforeupdate')!=$(this).val()) {
  537. replacement.find('#'+object_id).val($(this).val());
  538. }
  539. });
  540. $('#all_attributes').empty();
  541. $('#all_attributes').prepend(replacement);
  542. }
  543. function updateBulkEditFrom(url) {
  544. $.ajax({
  545. url: url,
  546. type: 'post',
  547. data: $('#bulk_edit_form').serialize()
  548. });
  549. }
  550. function observeAutocompleteField(fieldId, url, options) {
  551. $(document).ready(function() {
  552. $('#'+fieldId).autocomplete($.extend({
  553. source: url,
  554. minLength: 2,
  555. position: {collision: "flipfit"},
  556. search: function(){$('#'+fieldId).addClass('ajax-loading');},
  557. response: function(){$('#'+fieldId).removeClass('ajax-loading');}
  558. }, options));
  559. $('#'+fieldId).addClass('autocomplete');
  560. });
  561. }
  562. function observeSearchfield(fieldId, targetId, url) {
  563. $('#'+fieldId).each(function() {
  564. var $this = $(this);
  565. $this.addClass('autocomplete');
  566. $this.attr('data-value-was', $this.val());
  567. var check = function() {
  568. var val = $this.val();
  569. if ($this.attr('data-value-was') != val){
  570. $this.attr('data-value-was', val);
  571. $.ajax({
  572. url: url,
  573. type: 'get',
  574. data: {q: $this.val()},
  575. success: function(data){ if(targetId) $('#'+targetId).html(data); },
  576. beforeSend: function(){ $this.addClass('ajax-loading'); },
  577. complete: function(){ $this.removeClass('ajax-loading'); }
  578. });
  579. }
  580. };
  581. var reset = function() {
  582. if (timer) {
  583. clearInterval(timer);
  584. timer = setInterval(check, 300);
  585. }
  586. };
  587. var timer = setInterval(check, 300);
  588. $this.bind('keyup click mousemove', reset);
  589. });
  590. }
  591. $(document).ready(function(){
  592. $(".drdn .autocomplete").val('');
  593. // This variable is used to focus selected project
  594. var selected;
  595. $(".drdn-trigger").click(function(e){
  596. var drdn = $(this).closest(".drdn");
  597. if (drdn.hasClass("expanded")) {
  598. drdn.removeClass("expanded");
  599. } else {
  600. $(".drdn").removeClass("expanded");
  601. drdn.addClass("expanded");
  602. selected = $('.drdn-items a.selected'); // Store selected project
  603. selected.focus(); // Calling focus to scroll to selected project
  604. if (!isMobile()) {
  605. drdn.find(".autocomplete").focus();
  606. }
  607. e.stopPropagation();
  608. }
  609. });
  610. $(document).click(function(e){
  611. if ($(e.target).closest(".drdn").length < 1) {
  612. $(".drdn.expanded").removeClass("expanded");
  613. }
  614. });
  615. observeSearchfield('projects-quick-search', null, $('#projects-quick-search').data('automcomplete-url'));
  616. $(".drdn-content").keydown(function(event){
  617. var items = $(this).find(".drdn-items");
  618. // If a project is selected set focused to selected only once
  619. if (selected && selected.length > 0) {
  620. var focused = selected;
  621. selected = undefined;
  622. }
  623. else {
  624. var focused = items.find("a:focus");
  625. }
  626. switch (event.which) {
  627. case 40: //down
  628. if (focused.length > 0) {
  629. focused.nextAll("a").first().focus();;
  630. } else {
  631. items.find("a").first().focus();;
  632. }
  633. event.preventDefault();
  634. break;
  635. case 38: //up
  636. if (focused.length > 0) {
  637. var prev = focused.prevAll("a");
  638. if (prev.length > 0) {
  639. prev.first().focus();
  640. } else {
  641. $(this).find(".autocomplete").focus();
  642. }
  643. event.preventDefault();
  644. }
  645. break;
  646. case 35: //end
  647. if (focused.length > 0) {
  648. focused.nextAll("a").last().focus();
  649. event.preventDefault();
  650. }
  651. break;
  652. case 36: //home
  653. if (focused.length > 0) {
  654. focused.prevAll("a").last().focus();
  655. event.preventDefault();
  656. }
  657. break;
  658. }
  659. });
  660. });
  661. function beforeShowDatePicker(input, inst) {
  662. var default_date = null;
  663. switch ($(input).attr("id")) {
  664. case "issue_start_date" :
  665. if ($("#issue_due_date").size() > 0) {
  666. default_date = $("#issue_due_date").val();
  667. }
  668. break;
  669. case "issue_due_date" :
  670. if ($("#issue_start_date").size() > 0) {
  671. var start_date = $("#issue_start_date").val();
  672. if (start_date != "") {
  673. start_date = new Date(Date.parse(start_date));
  674. if (start_date > new Date()) {
  675. default_date = $("#issue_start_date").val();
  676. }
  677. }
  678. }
  679. break;
  680. }
  681. $(input).datepickerFallback("option", "defaultDate", default_date);
  682. }
  683. (function($){
  684. $.fn.positionedItems = function(sortableOptions, options){
  685. var settings = $.extend({
  686. firstPosition: 1
  687. }, options );
  688. return this.sortable($.extend({
  689. axis: 'y',
  690. handle: ".sort-handle",
  691. helper: function(event, ui){
  692. ui.children('td').each(function(){
  693. $(this).width($(this).width());
  694. });
  695. return ui;
  696. },
  697. update: function(event, ui) {
  698. var sortable = $(this);
  699. var handle = ui.item.find(".sort-handle").addClass("ajax-loading");
  700. var url = handle.data("reorder-url");
  701. var param = handle.data("reorder-param");
  702. var data = {};
  703. data[param] = {position: ui.item.index() + settings['firstPosition']};
  704. $.ajax({
  705. url: url,
  706. type: 'put',
  707. dataType: 'script',
  708. data: data,
  709. error: function(jqXHR, textStatus, errorThrown){
  710. alert(jqXHR.status);
  711. sortable.sortable("cancel");
  712. },
  713. complete: function(jqXHR, textStatus, errorThrown){
  714. handle.removeClass("ajax-loading");
  715. }
  716. });
  717. },
  718. }, sortableOptions));
  719. }
  720. }( jQuery ));
  721. var warnLeavingUnsavedMessage;
  722. function warnLeavingUnsaved(message) {
  723. warnLeavingUnsavedMessage = message;
  724. $(document).on('submit', 'form', function(){
  725. $('textarea').removeData('changed');
  726. });
  727. $(document).on('change', 'textarea', function(){
  728. $(this).data('changed', 'changed');
  729. });
  730. window.onbeforeunload = function(){
  731. var warn = false;
  732. $('textarea').blur().each(function(){
  733. if ($(this).data('changed')) {
  734. warn = true;
  735. }
  736. });
  737. if (warn) {return warnLeavingUnsavedMessage;}
  738. };
  739. }
  740. function setupAjaxIndicator() {
  741. $(document).bind('ajaxSend', function(event, xhr, settings) {
  742. if ($('.ajax-loading').length === 0 && settings.contentType != 'application/octet-stream') {
  743. $('#ajax-indicator').show();
  744. }
  745. });
  746. $(document).bind('ajaxStop', function() {
  747. $('#ajax-indicator').hide();
  748. });
  749. }
  750. function setupTabs() {
  751. if($('.tabs').length > 0) {
  752. displayTabsButtons();
  753. $(window).resize(displayTabsButtons);
  754. }
  755. }
  756. function setupFilePreviewNavigation() {
  757. // only bind arrow keys when preview navigation is present
  758. const element = $('.pagination.filepreview').first();
  759. if (element) {
  760. const handleArrowKey = function(selector, e){
  761. const href = $(element).find(selector).attr('href');
  762. if (href) {
  763. window.location = href;
  764. e.preventDefault();
  765. }
  766. };
  767. $(document).keydown(function(e) {
  768. if(e.shiftKey || e.metaKey || e.ctrlKey || e.altKey) return;
  769. switch(e.key) {
  770. case 'ArrowLeft':
  771. handleArrowKey('.previous a', e);
  772. break;
  773. case 'ArrowRight':
  774. handleArrowKey('.next a', e);
  775. break;
  776. }
  777. });
  778. }
  779. }
  780. function hideOnLoad() {
  781. $('.hol').hide();
  782. }
  783. function addFormObserversForDoubleSubmit() {
  784. $('form[method=post]').each(function() {
  785. if (!$(this).hasClass('multiple-submit')) {
  786. $(this).submit(function(form_submission) {
  787. if ($(form_submission.target).attr('data-submitted')) {
  788. form_submission.preventDefault();
  789. } else {
  790. $(form_submission.target).attr('data-submitted', true);
  791. }
  792. });
  793. }
  794. });
  795. }
  796. function defaultFocus(){
  797. if (($('#content :focus').length == 0) && (window.location.hash == '')) {
  798. $('#content input[type=text], #content textarea').first().focus();
  799. }
  800. }
  801. function blockEventPropagation(event) {
  802. event.stopPropagation();
  803. event.preventDefault();
  804. }
  805. function toggleDisabledOnChange() {
  806. var checked = $(this).is(':checked');
  807. $($(this).data('disables')).attr('disabled', checked);
  808. $($(this).data('enables')).attr('disabled', !checked);
  809. $($(this).data('shows')).toggle(checked);
  810. }
  811. function toggleDisabledInit() {
  812. $('input[data-disables], input[data-enables], input[data-shows]').each(toggleDisabledOnChange);
  813. }
  814. function toggleNewObjectDropdown() {
  815. var dropdown = $('#new-object + ul.menu-children');
  816. if(dropdown.hasClass('visible')){
  817. dropdown.removeClass('visible');
  818. }else{
  819. dropdown.addClass('visible');
  820. }
  821. }
  822. (function ( $ ) {
  823. // detect if native date input is supported
  824. var nativeDateInputSupported = true;
  825. var input = document.createElement('input');
  826. input.setAttribute('type','date');
  827. if (input.type === 'text') {
  828. nativeDateInputSupported = false;
  829. }
  830. var notADateValue = 'not-a-date';
  831. input.setAttribute('value', notADateValue);
  832. if (input.value === notADateValue) {
  833. nativeDateInputSupported = false;
  834. }
  835. $.fn.datepickerFallback = function( options ) {
  836. if (nativeDateInputSupported) {
  837. return this;
  838. } else {
  839. return this.datepicker( options );
  840. }
  841. };
  842. }( jQuery ));
  843. $(document).ready(function(){
  844. $('#content').on('change', 'input[data-disables], input[data-enables], input[data-shows]', toggleDisabledOnChange);
  845. toggleDisabledInit();
  846. $('#history .tabs').on('click', 'a', function(e){
  847. var tab = $(e.target).attr('id').replace('tab-','');
  848. document.cookie = 'history_last_tab=' + tab
  849. });
  850. });
  851. $(document).ready(function(){
  852. $('#content').on('click', 'div.jstTabs a.tab-preview', function(event){
  853. var tab = $(event.target);
  854. var url = tab.data('url');
  855. var form = tab.parents('form');
  856. var jstBlock = tab.parents('.jstBlock');
  857. var element = encodeURIComponent(jstBlock.find('.wiki-edit').val());
  858. var attachments = form.find('.attachments_fields input').serialize();
  859. $.ajax({
  860. url: url,
  861. type: 'post',
  862. data: "text=" + element + '&' + attachments,
  863. success: function(data){
  864. jstBlock.find('.wiki-preview').html(data);
  865. }
  866. });
  867. });
  868. });
  869. function keepAnchorOnSignIn(form){
  870. var hash = decodeURIComponent(self.document.location.hash);
  871. if (hash) {
  872. if (hash.indexOf("#") === -1) {
  873. hash = "#" + hash;
  874. }
  875. form.action = form.action + hash;
  876. }
  877. return true;
  878. }
  879. $(function ($) {
  880. $('#auth_source_ldap_mode').change(function () {
  881. $('.ldaps_warning').toggle($(this).val() != 'ldaps_verify_peer');
  882. }).change();
  883. });
  884. function setFilecontentContainerHeight() {
  885. var $filecontainer = $('.filecontent-container');
  886. var fileTypeSelectors = ['.image', 'video'];
  887. if($filecontainer.length > 0 && $filecontainer.find(fileTypeSelectors.join(',')).length === 1) {
  888. var containerOffsetTop = $filecontainer.offset().top;
  889. var containerMarginBottom = parseInt($filecontainer.css('marginBottom'));
  890. var paginationHeight = $filecontainer.next('.pagination').height();
  891. var diff = containerOffsetTop + containerMarginBottom + paginationHeight;
  892. $filecontainer.css('height', 'calc(100vh - ' + diff + 'px)')
  893. }
  894. }
  895. function setupAttachmentDetail() {
  896. setFilecontentContainerHeight();
  897. $(window).resize(setFilecontentContainerHeight);
  898. }
  899. $(function () {
  900. $('[title]').tooltip({
  901. show: {
  902. delay: 400
  903. },
  904. position: {
  905. my: "center bottom-5",
  906. at: "center top"
  907. }
  908. });
  909. });
  910. $(document).ready(setupAjaxIndicator);
  911. $(document).ready(hideOnLoad);
  912. $(document).ready(addFormObserversForDoubleSubmit);
  913. $(document).ready(defaultFocus);
  914. $(document).ready(setupAttachmentDetail);
  915. $(document).ready(setupTabs);
  916. $(document).ready(setupFilePreviewNavigation);