From 6fb47427c7a29d76fb86d61c8da9d59a57392c95 Mon Sep 17 00:00:00 2001 From: Clement Ho <ClemMakesApps@gmail.com> Date: Wed, 9 Nov 2016 17:07:30 -0600 Subject: [PATCH] Refactor tokenizer --- .../filtered_search_manager.js.es6 | 120 ++++-------------- .../filtered_search_tokenizer.es6 | 90 +++++++++++++ 2 files changed, 115 insertions(+), 95 deletions(-) create mode 100644 app/assets/javascripts/filtered_search/filtered_search_tokenizer.es6 diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js.es6 b/app/assets/javascripts/filtered_search/filtered_search_manager.js.es6 index 1b58fc01608..58c64ea078d 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_manager.js.es6 +++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js.es6 @@ -18,13 +18,21 @@ param: 'name[]', }]; + function clearSearch(event) { + event.stopPropagation(); + event.preventDefault(); + + document.querySelector('.filtered-search').value = ''; + document.querySelector('.clear-search').classList.add('hidden'); + } + function toggleClearSearchButton(event) { - const clearSearch = document.querySelector('.clear-search'); + const clearSearchButton = document.querySelector('.clear-search'); if (event.target.value) { - clearSearch.classList.remove('hidden'); + clearSearchButton.classList.remove('hidden'); } else { - clearSearch.classList.add('hidden'); + clearSearchButton.classList.add('hidden'); } } @@ -74,105 +82,24 @@ class FilteredSearchManager { constructor() { + this.tokenizer = new gl.FilteredSearchTokenizer(validTokenKeys); this.bindEvents(); loadSearchParamsFromURL(); - this.clearTokens(); } bindEvents() { - const input = document.querySelector('.filtered-search'); - const clearSearch = document.querySelector('.clear-search'); - - input.addEventListener('input', this.tokenize.bind(this)); - input.addEventListener('input', toggleClearSearchButton); - input.addEventListener('keydown', this.checkForEnter.bind(this)); + const filteredSearchInput = document.querySelector('.filtered-search'); - clearSearch.addEventListener('click', this.clearSearch.bind(this)); - } - - clearSearch(event) { - event.stopPropagation(); - event.preventDefault(); - - this.clearTokens(); - document.querySelector('.filtered-search').value = ''; - document.querySelector('.clear-search').classList.add('hidden'); - } + filteredSearchInput.addEventListener('input', this.processInput.bind(this)); + filteredSearchInput.addEventListener('input', toggleClearSearchButton); + filteredSearchInput.addEventListener('keydown', this.checkForEnter.bind(this)); - clearTokens() { - this.tokens = []; - this.searchToken = ''; + document.querySelector('.clear-search').addEventListener('click', clearSearch); } - tokenize(event) { - // Re-calculate tokens - this.clearTokens(); - + processInput(event) { const input = event.target.value; - const inputs = input.split(' '); - let searchTerms = ''; - let lastQuotation = ''; - let incompleteToken = false; - - const addSearchTerm = function addSearchTerm(term) { - // Add space for next term - searchTerms += `${term} `; - }; - - inputs.forEach((i) => { - if (incompleteToken) { - const prevToken = this.tokens[this.tokens.length - 1]; - prevToken.value += ` ${i}`; - - // Remove last quotation - const lastQuotationRegex = new RegExp(lastQuotation, 'g'); - prevToken.value = prevToken.value.replace(lastQuotationRegex, ''); - this.tokens[this.tokens.length - 1] = prevToken; - - // Check to see if this quotation completes the token value - if (i.indexOf(lastQuotation)) { - incompleteToken = !incompleteToken; - } - - return; - } - - const colonIndex = i.indexOf(':'); - - if (colonIndex !== -1) { - const tokenKey = i.slice(0, colonIndex).toLowerCase(); - const tokenValue = i.slice(colonIndex + 1); - const match = validTokenKeys.find(v => v.key === tokenKey); - - if (tokenValue.indexOf('"') !== -1) { - lastQuotation = '"'; - incompleteToken = true; - } else if (tokenValue.indexOf('\'') !== -1) { - lastQuotation = '\''; - incompleteToken = true; - } - - if (match && tokenValue.length > 0) { - this.tokens.push({ - key: match.key, - value: tokenValue, - }); - } else { - addSearchTerm(i); - } - } else { - addSearchTerm(i); - } - }, this); - - this.searchToken = searchTerms.trim(); - this.printTokens(); - } - - printTokens() { - console.log('tokens:'); - this.tokens.forEach(token => console.log(token)); - console.log(`search: ${this.searchToken}`); + this.tokenizer.processTokens(input); } checkForEnter(event) { @@ -193,6 +120,9 @@ const defaultState = 'opened'; let currentState = defaultState; + const tokens = this.tokenizer.getTokens(); + const searchToken = this.tokenizer.getSearchToken(); + if (stateIndex !== -1) { const remaining = currentPath.slice(stateIndex + 6); const separatorIndex = remaining.indexOf('&'); @@ -201,13 +131,13 @@ } path += `&state=${currentState}`; - this.tokens.forEach((token) => { + tokens.forEach((token) => { const param = validTokenKeys.find(t => t.key === token.key).param; path += `&${token.key}_${param}=${encodeURIComponent(token.value)}`; }); - if (this.searchToken) { - path += `&search=${encodeURIComponent(this.searchToken)}`; + if (searchToken) { + path += `&search=${encodeURIComponent(searchToken)}`; } window.location = path; diff --git a/app/assets/javascripts/filtered_search/filtered_search_tokenizer.es6 b/app/assets/javascripts/filtered_search/filtered_search_tokenizer.es6 new file mode 100644 index 00000000000..f6cc1b8860d --- /dev/null +++ b/app/assets/javascripts/filtered_search/filtered_search_tokenizer.es6 @@ -0,0 +1,90 @@ +/* eslint-disable no-param-reassign */ +((global) => { + class FilteredSearchTokenizer { + constructor(validTokenKeys) { + this.validTokenKeys = validTokenKeys; + this.resetTokens(); + } + + getTokens() { + return this.tokens; + } + + getSearchToken() { + return this.searchToken; + } + + resetTokens() { + this.tokens = []; + this.searchToken = ''; + } + + printTokens() { + console.log('tokens:'); + this.tokens.forEach(token => console.log(token)); + console.log(`search: ${this.searchToken}`); + } + + processTokens(input) { + // Re-calculate tokens + this.resetTokens(); + + const inputs = input.split(' '); + let searchTerms = ''; + let lastQuotation = ''; + let incompleteToken = false; + + inputs.forEach((i) => { + if (incompleteToken) { + const prevToken = this.tokens[this.tokens.length - 1]; + prevToken.value += ` ${i}`; + + // Remove last quotation + const lastQuotationRegex = new RegExp(lastQuotation, 'g'); + prevToken.value = prevToken.value.replace(lastQuotationRegex, ''); + this.tokens[this.tokens.length - 1] = prevToken; + + // Check to see if this quotation completes the token value + if (i.indexOf(lastQuotation)) { + incompleteToken = !incompleteToken; + } + + return; + } + + const colonIndex = i.indexOf(':'); + + if (colonIndex !== -1) { + const tokenKey = i.slice(0, colonIndex).toLowerCase(); + const tokenValue = i.slice(colonIndex + 1); + const match = this.validTokenKeys.find(v => v.key === tokenKey); + + if (tokenValue.indexOf('"') !== -1) { + lastQuotation = '"'; + incompleteToken = true; + } else if (tokenValue.indexOf('\'') !== -1) { + lastQuotation = '\''; + incompleteToken = true; + } + + if (match && tokenValue.length > 0) { + this.tokens.push({ + key: match.key, + value: tokenValue, + }); + + return; + } + } + + // Add space for next term + searchTerms += `${i} `; + }, this); + + this.searchToken = searchTerms.trim(); + this.printTokens(); + } + } + + global.FilteredSearchTokenizer = FilteredSearchTokenizer; +})(window.gl || (window.gl = {})); -- 2.30.9