filtered_search_manager.js.es6 7.0 KB
Newer Older
C
Clement Ho 已提交
1
/* eslint-disable no-param-reassign */
C
Clement Ho 已提交
2
((global) => {
C
Clement Ho 已提交
3
  // TODO: Encapsulate inside class?
C
Clement Ho 已提交
4
  function toggleClearSearchButton(e) {
C
Clement Ho 已提交
5
    const clearSearchButton = document.querySelector('.clear-search');
C
Clement Ho 已提交
6

C
Clement Ho 已提交
7 8 9 10 11
    if (e.target.value) {
      clearSearchButton.classList.remove('hidden');
    } else {
      clearSearchButton.classList.add('hidden');
    }
C
Clement Ho 已提交
12 13 14 15 16 17 18 19 20 21 22 23 24
  }

  function loadSearchParamsFromURL() {
    // We can trust that each param has one & since values containing & will be encoded
    // Remove the first character of search as it is always ?
    const params = window.location.search.slice(1).split('&');
    let inputValue = '';

    params.forEach((p) => {
      const split = p.split('=');
      const key = decodeURIComponent(split[0]);
      const value = split[1];

C
Clement Ho 已提交
25
      // Check if it matches edge conditions listed in gl.FilteredSearchTokenKeys.get()
C
Clement Ho 已提交
26
      let conditionIndex = 0;
C
Clement Ho 已提交
27
      const validCondition = gl.FilteredSearchTokenKeys.get()
C
Clement Ho 已提交
28
        .filter(v => v.conditions && v.conditions.filter((c, index) => {
C
Clement Ho 已提交
29
          // Return TokenKeys that have conditions that much the URL
C
Clement Ho 已提交
30 31 32 33 34
          if (c.url === p) {
            conditionIndex = index;
          }
          return c.url === p;
        })[0])[0];
C
Clement Ho 已提交
35 36

      if (validCondition) {
C
Clement Ho 已提交
37
        // Parse params based on rules provided in the conditions key of gl.FilteredSearchTokenKeys.get()
C
Clement Ho 已提交
38
        inputValue += `${validCondition.key}:${validCondition.conditions[conditionIndex].keyword}`;
39
        inputValue += ' ';
C
Clement Ho 已提交
40 41 42 43
      } else {
        // Sanitize value since URL converts spaces into +
        // Replace before decode so that we know what was originally + versus the encoded +
        const sanitizedValue = value ? decodeURIComponent(value.replace(/[+]/g, ' ')) : value;
C
Clement Ho 已提交
44
        const match = gl.FilteredSearchTokenKeys.get().filter(t => key === `${t.key}_${t.param}`)[0];
C
Clement Ho 已提交
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63

        if (match) {
          const sanitizedKey = key.slice(0, key.indexOf('_'));
          const valueHasSpace = sanitizedValue.indexOf(' ') !== -1;
          const symbol = match.symbol;

          const preferredQuotations = '"';
          let quotationsToUse = preferredQuotations;

          if (valueHasSpace) {
            // Prefer ", but use ' if required
            quotationsToUse = sanitizedValue.indexOf(preferredQuotations) === -1 ? preferredQuotations : '\'';
          }

          inputValue += valueHasSpace ? `${sanitizedKey}:${symbol}${quotationsToUse}${sanitizedValue}${quotationsToUse}` : `${sanitizedKey}:${symbol}${sanitizedValue}`;
          inputValue += ' ';
        } else if (!match && key === 'search') {
          inputValue += sanitizedValue;
          inputValue += ' ';
C
Clement Ho 已提交
64 65 66 67 68 69 70 71 72 73 74
        }
      }
    });

    // Trim the last space value
    document.querySelector('.filtered-search').value = inputValue.trim();

    if (inputValue.trim()) {
      document.querySelector('.clear-search').classList.remove('hidden');
    }
  }
C
Clement Ho 已提交
75 76 77

  class FilteredSearchManager {
    constructor() {
C
Clement Ho 已提交
78
      this.tokenizer = gl.FilteredSearchTokenizer;
C
Clement Ho 已提交
79 80
      this.filteredSearchInput = document.querySelector('.filtered-search');
      this.clearSearchButton = document.querySelector('.clear-search');
C
Clement Ho 已提交
81
      this.dropdownManager = new gl.FilteredSearchDropdownManager();
C
Clement Ho 已提交
82

C
Clement Ho 已提交
83
      this.bindEvents();
C
Clement Ho 已提交
84
      loadSearchParamsFromURL();
C
Clement Ho 已提交
85
      this.dropdownManager.setDropdown();
86

C
Clement Ho 已提交
87 88
      this.cleanupWrapper = this.cleanup.bind(this);
      document.addEventListener('page:fetch', this.cleanupWrapper);
89 90 91
    }

    cleanup() {
92
      this.unbindEvents();
C
Clement Ho 已提交
93 94
      document.removeEventListener('page:fetch', this.cleanupWrapper);
    }
95

C
Clement Ho 已提交
96
    bindEvents() {
C
Clement Ho 已提交
97
      this.setDropdownWrapper = this.dropdownManager.setDropdown.bind(this.dropdownManager);
98 99 100 101 102
      this.checkForEnterWrapper = this.checkForEnter.bind(this);
      this.clearSearchWrapper = this.clearSearch.bind(this);
      this.checkForBackspaceWrapper = this.checkForBackspace.bind(this);

      this.filteredSearchInput.addEventListener('input', this.setDropdownWrapper);
C
Clement Ho 已提交
103
      this.filteredSearchInput.addEventListener('input', toggleClearSearchButton);
104 105 106 107 108 109 110 111 112 113 114
      this.filteredSearchInput.addEventListener('keydown', this.checkForEnterWrapper);
      this.filteredSearchInput.addEventListener('keyup', this.checkForBackspaceWrapper);
      this.clearSearchButton.addEventListener('click', this.clearSearchWrapper);
    }

    unbindEvents() {
      this.filteredSearchInput.removeEventListener('input', this.setDropdownWrapper);
      this.filteredSearchInput.removeEventListener('input', toggleClearSearchButton);
      this.filteredSearchInput.removeEventListener('keydown', this.checkForEnterWrapper);
      this.filteredSearchInput.removeEventListener('keyup', this.checkForBackspaceWrapper);
      this.clearSearchButton.removeEventListener('click', this.clearSearchWrapper);
C
Clement Ho 已提交
115 116 117 118 119 120
    }

    clearSearch(e) {
      e.stopPropagation();
      e.preventDefault();

C
Clement Ho 已提交
121 122
      this.filteredSearchInput.value = '';
      this.clearSearchButton.classList.add('hidden');
123

C
Clement Ho 已提交
124
      this.dropdownManager.resetDropdowns();
C
Clement Ho 已提交
125 126
    }

127 128 129
    checkForBackspace(e) {
      if (e.keyCode === 8) {
        // Reposition dropdown so that it is aligned with cursor
C
Clement Ho 已提交
130
        this.dropdownManager.updateCurrentDropdownOffset();
131 132 133
      }
    }

C
Clement Ho 已提交
134 135 136 137
    checkForEnter(e) {
      if (e.keyCode === 13) {
        e.stopPropagation();
        e.preventDefault();
138 139

        // Prevent droplab from opening dropdown
C
Clement Ho 已提交
140
        this.dropdownManager.destroyDroplab();
141

C
Clement Ho 已提交
142 143 144 145 146
        this.search();
      }
    }

    search() {
C
Clement Ho 已提交
147 148 149 150 151 152 153 154
      let path = '?scope=all&utf8=✓';

      // Check current state
      const currentPath = window.location.search;
      const stateIndex = currentPath.indexOf('state=');
      const defaultState = 'opened';
      let currentState = defaultState;

C
Clement Ho 已提交
155
      const { tokens, searchToken } = this.tokenizer.processTokens(this.filteredSearchInput.value);
C
Clement Ho 已提交
156

C
Clement Ho 已提交
157
      if (stateIndex !== -1) {
C
Clement Ho 已提交
158 159
        // Get currentState from url params if available
        const remaining = currentPath.slice(stateIndex + 'state='.length);
C
Clement Ho 已提交
160 161 162 163 164
        const separatorIndex = remaining.indexOf('&');

        currentState = separatorIndex === -1 ? remaining : remaining.slice(0, separatorIndex);
      }

C
Clement Ho 已提交
165
      path += `&state=${currentState}`;
C
Clement Ho 已提交
166
      tokens.forEach((token) => {
C
Clement Ho 已提交
167
        const match = gl.FilteredSearchTokenKeys.get().filter(t => t.key === token.key)[0];
C
Clement Ho 已提交
168 169 170
        let tokenPath = '';

        if (token.wildcard && match.conditions) {
C
Clement Ho 已提交
171 172
          const condition = match.conditions
            .filter(c => c.keyword === token.value.toLowerCase())[0];
C
Clement Ho 已提交
173 174 175 176 177 178 179 180 181 182 183 184

          if (condition) {
            tokenPath = `${condition.url}`;
          }
        } else if (!token.wildcard) {
          // Remove the wildcard token
          tokenPath = `${token.key}_${match.param}=${encodeURIComponent(token.value.slice(1))}`;
        } else {
          tokenPath = `${token.key}_${match.param}=${encodeURIComponent(token.value)}`;
        }

        path += `&${tokenPath}`;
C
Clement Ho 已提交
185 186
      });

C
Clement Ho 已提交
187 188
      if (searchToken) {
        path += `&search=${encodeURIComponent(searchToken)}`;
C
Clement Ho 已提交
189 190 191 192 193 194 195
      }

      window.location = path;
    }
  }

  global.FilteredSearchManager = FilteredSearchManager;
C
Clement Ho 已提交
196
})(window.gl || (window.gl = {}));