summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/issuable.js
blob: 3bfce32768a6923255a1809d3163447fbdce91fe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/* eslint-disable no-param-reassign, func-names, no-var, camelcase, no-unused-vars, object-shorthand, space-before-function-paren, no-return-assign, comma-dangle, consistent-return, one-var, one-var-declaration-per-line, quotes, prefer-template, prefer-arrow-callback, wrap-iife, max-len */
/* global Issuable */

((global) => {
  var issuable_created;

  issuable_created = false;

  global.Issuable = {
    init: function() {
      Issuable.initTemplates();
      Issuable.initSearch();
      Issuable.initChecks();
      Issuable.initResetFilters();
      Issuable.resetIncomingEmailToken();
      return Issuable.initLabelFilterRemove();
    },
    initTemplates: function() {
      return Issuable.labelRow = _.template('<% _.each(labels, function(label){ %> <span class="label-row btn-group" role="group" aria-label="<%- label.title %>" style="color: <%- label.text_color %>;"> <a href="#" class="btn btn-transparent has-tooltip" style="background-color: <%- label.color %>;" title="<%- label.description %>" data-container="body"> <%- label.title %> </a> <button type="button" class="btn btn-transparent label-remove js-label-filter-remove" style="background-color: <%- label.color %>;" data-label="<%- label.title %>"> <i class="fa fa-times"></i> </button> </span> <% }); %>');
    },
    initSearch: function() {
      const $searchInput = $('#issuable_search');

      Issuable.initSearchState($searchInput);

      // `immediate` param set to false debounces on the `trailing` edge, lets user finish typing
      const debouncedExecSearch = _.debounce(Issuable.executeSearch, 1000, false);

      $searchInput.off('keyup').on('keyup', debouncedExecSearch);

      // ensures existing filters are preserved when manually submitted
      $('#issuable_search_form').on('submit', (e) => {
        e.preventDefault();
        debouncedExecSearch(e);
      });
    },
    initSearchState: function($searchInput) {
      const currentSearchVal = $searchInput.val();

      Issuable.searchState = {
        elem: $searchInput,
        current: currentSearchVal
      };

      Issuable.maybeFocusOnSearch();
    },
    accessSearchPristine: function(set) {
      // store reference to previous value to prevent search on non-mutating keyup
      const state = Issuable.searchState;
      const currentSearchVal = state.elem.val();

      if (set) {
        state.current = currentSearchVal;
      } else {
        return state.current === currentSearchVal;
      }
    },
    maybeFocusOnSearch: function() {
      const currentSearchVal = Issuable.searchState.current;
      if (currentSearchVal && currentSearchVal !== '') {
        const queryLength = currentSearchVal.length;
        const $searchInput = Issuable.searchState.elem;

      /* The following ensures that the cursor is initially placed at
        * the end of search input when focus is applied. It accounts
        * for differences in browser implementations of `setSelectionRange`
        * and cursor placement for elements in focus.
      */
        $searchInput.focus();
        if ($searchInput.setSelectionRange) {
          $searchInput.setSelectionRange(queryLength, queryLength);
        } else {
          $searchInput.val(currentSearchVal);
        }
      }
    },
    executeSearch: function(e) {
      const $search = $('#issuable_search');
      const $searchName = $search.attr('name');
      const $searchValue = $search.val();
      const $filtersForm = $('.js-filter-form');
      const $input = $(`input[name='${$searchName}']`, $filtersForm);
      const isPristine = Issuable.accessSearchPristine();

      if (isPristine) {
        return;
      }

      if (!$input.length) {
        $filtersForm.append(`<input type='hidden' name='${$searchName}' value='${_.escape($searchValue)}'/>`);
      } else {
        $input.val($searchValue);
      }

      Issuable.filterResults($filtersForm);
    },
    initLabelFilterRemove: function() {
      return $(document).off('click', '.js-label-filter-remove').on('click', '.js-label-filter-remove', function(e) {
        var $button;
        $button = $(this);
        // Remove the label input box
        $('input[name="label_name[]"]').filter(function() {
          return this.value === $button.data('label');
        }).remove();
        // Submit the form to get new data
        Issuable.filterResults($('.filter-form'));
      });
    },
    filterResults: (function(_this) {
      return function(form) {
        var formAction, formData, issuesUrl;
        formData = form.serializeArray();
        formData = formData.filter(function(data) {
          return data.value !== '';
        });
        formData = $.param(formData);
        formAction = form.attr('action');
        issuesUrl = formAction;
        issuesUrl += "" + (formAction.indexOf('?') === -1 ? '?' : '&');
        issuesUrl += formData;
        return gl.utils.visitUrl(issuesUrl);
      };
    })(this),
    initResetFilters: function() {
      $('.reset-filters').on('click', function(e) {
        e.preventDefault();
        const target = e.target;
        const $form = $(target).parents('.js-filter-form');
        const baseIssuesUrl = target.href;

        $form.attr('action', baseIssuesUrl);
        gl.utils.visitUrl(baseIssuesUrl);
      });
    },
    initChecks: function() {
      this.issuableBulkActions = $('.bulk-update').data('bulkActions');
      $('.check_all_issues').off('click').on('click', function() {
        $('.selected_issue').prop('checked', this.checked);
        return Issuable.checkChanged();
      });
      return $('.selected_issue').off('change').on('change', Issuable.checkChanged.bind(this));
    },
    checkChanged: function() {
      const $checkedIssues = $('.selected_issue:checked');
      const $updateIssuesIds = $('#update_issuable_ids');
      const $issuesOtherFilters = $('.issues-other-filters');
      const $issuesBulkUpdate = $('.issues_bulk_update');

      this.issuableBulkActions.willUpdateLabels = false;
      this.issuableBulkActions.setOriginalDropdownData();

      if ($checkedIssues.length > 0) {
        const ids = $.map($checkedIssues, function(value) {
          return $(value).data('id');
        });
        $updateIssuesIds.val(ids);
        $issuesOtherFilters.hide();
        $issuesBulkUpdate.show();
      } else {
        $updateIssuesIds.val([]);
        $issuesBulkUpdate.hide();
        $issuesOtherFilters.show();
      }
      return true;
    },

    resetIncomingEmailToken: function() {
      $('.incoming-email-token-reset').on('click', function(e) {
        e.preventDefault();

        $.ajax({
          type: 'PUT',
          url: $('.incoming-email-token-reset').attr('href'),
          dataType: 'json',
          success: function(response) {
            $('#issue_email').val(response.new_issue_address).focus();
          },
          beforeSend: function() {
            $('.incoming-email-token-reset').text('resetting...');
          },
          complete: function() {
            $('.incoming-email-token-reset').text('reset it');
          }
        });
      });
    }
  };
})(window);