actions.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. /*global gettext, interpolate, ngettext*/
  2. 'use strict';
  3. {
  4. const $ = django.jQuery;
  5. let lastChecked;
  6. $.fn.actions = function(opts) {
  7. const options = $.extend({}, $.fn.actions.defaults, opts);
  8. const actionCheckboxes = $(this);
  9. let list_editable_changed = false;
  10. const showQuestion = function() {
  11. $(options.acrossClears).hide();
  12. $(options.acrossQuestions).show();
  13. $(options.allContainer).hide();
  14. },
  15. showClear = function() {
  16. $(options.acrossClears).show();
  17. $(options.acrossQuestions).hide();
  18. $(options.actionContainer).toggleClass(options.selectedClass);
  19. $(options.allContainer).show();
  20. $(options.counterContainer).hide();
  21. },
  22. reset = function() {
  23. $(options.acrossClears).hide();
  24. $(options.acrossQuestions).hide();
  25. $(options.allContainer).hide();
  26. $(options.counterContainer).show();
  27. },
  28. clearAcross = function() {
  29. reset();
  30. $(options.acrossInput).val(0);
  31. $(options.actionContainer).removeClass(options.selectedClass);
  32. },
  33. checker = function(checked) {
  34. if (checked) {
  35. showQuestion();
  36. } else {
  37. reset();
  38. }
  39. $(actionCheckboxes).prop("checked", checked)
  40. .parent().parent().toggleClass(options.selectedClass, checked);
  41. },
  42. updateCounter = function() {
  43. const sel = $(actionCheckboxes).filter(":checked").length;
  44. // data-actions-icnt is defined in the generated HTML
  45. // and contains the total amount of objects in the queryset
  46. const actions_icnt = $('.action-counter').data('actionsIcnt');
  47. $(options.counterContainer).html(interpolate(
  48. ngettext('%(sel)s of %(cnt)s selected', '%(sel)s of %(cnt)s selected', sel), {
  49. sel: sel,
  50. cnt: actions_icnt
  51. }, true));
  52. $(options.allToggle).prop("checked", function() {
  53. let value;
  54. if (sel === actionCheckboxes.length) {
  55. value = true;
  56. showQuestion();
  57. } else {
  58. value = false;
  59. clearAcross();
  60. }
  61. return value;
  62. });
  63. };
  64. // Show counter by default
  65. $(options.counterContainer).show();
  66. // Check state of checkboxes and reinit state if needed
  67. $(this).filter(":checked").each(function(i) {
  68. $(this).parent().parent().toggleClass(options.selectedClass);
  69. updateCounter();
  70. if ($(options.acrossInput).val() === 1) {
  71. showClear();
  72. }
  73. });
  74. $(options.allToggle).show().on('click', function() {
  75. checker($(this).prop("checked"));
  76. updateCounter();
  77. });
  78. $("a", options.acrossQuestions).on('click', function(event) {
  79. event.preventDefault();
  80. $(options.acrossInput).val(1);
  81. showClear();
  82. });
  83. $("a", options.acrossClears).on('click', function(event) {
  84. event.preventDefault();
  85. $(options.allToggle).prop("checked", false);
  86. clearAcross();
  87. checker(0);
  88. updateCounter();
  89. });
  90. lastChecked = null;
  91. $(actionCheckboxes).on('click', function(event) {
  92. if (!event) { event = window.event; }
  93. const target = event.target ? event.target : event.srcElement;
  94. if (lastChecked && $.data(lastChecked) !== $.data(target) && event.shiftKey === true) {
  95. let inrange = false;
  96. $(lastChecked).prop("checked", target.checked)
  97. .parent().parent().toggleClass(options.selectedClass, target.checked);
  98. $(actionCheckboxes).each(function() {
  99. if ($.data(this) === $.data(lastChecked) || $.data(this) === $.data(target)) {
  100. inrange = (inrange) ? false : true;
  101. }
  102. if (inrange) {
  103. $(this).prop("checked", target.checked)
  104. .parent().parent().toggleClass(options.selectedClass, target.checked);
  105. }
  106. });
  107. }
  108. $(target).parent().parent().toggleClass(options.selectedClass, target.checked);
  109. lastChecked = target;
  110. updateCounter();
  111. });
  112. $('form#changelist-form table#result_list tr').on('change', 'td:gt(0) :input', function() {
  113. list_editable_changed = true;
  114. });
  115. $('form#changelist-form button[name="index"]').on('click', function(event) {
  116. if (list_editable_changed) {
  117. return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."));
  118. }
  119. });
  120. $('form#changelist-form input[name="_save"]').on('click', function(event) {
  121. let action_changed = false;
  122. $('select option:selected', options.actionContainer).each(function() {
  123. if ($(this).val()) {
  124. action_changed = true;
  125. }
  126. });
  127. if (action_changed) {
  128. if (list_editable_changed) {
  129. return confirm(gettext("You have selected an action, but you haven’t saved your changes to individual fields yet. Please click OK to save. You’ll need to re-run the action."));
  130. } else {
  131. return confirm(gettext("You have selected an action, and you haven’t made any changes on individual fields. You’re probably looking for the Go button rather than the Save button."));
  132. }
  133. }
  134. });
  135. };
  136. /* Setup plugin defaults */
  137. $.fn.actions.defaults = {
  138. actionContainer: "div.actions",
  139. counterContainer: "span.action-counter",
  140. allContainer: "div.actions span.all",
  141. acrossInput: "div.actions input.select-across",
  142. acrossQuestions: "div.actions span.question",
  143. acrossClears: "div.actions span.clear",
  144. allToggle: "#action-toggle",
  145. selectedClass: "selected"
  146. };
  147. $(document).ready(function() {
  148. const $actionsEls = $('tr input.action-select');
  149. if ($actionsEls.length > 0) {
  150. $actionsEls.actions();
  151. }
  152. });
  153. }