1(() => { 2 const searchClass = '.search'; 3 const searchWrapper = document.querySelector(searchClass); 4 5 // const containerClass = '.search__container'; 6 // const searchBarClass = '.search__bar'; 7 const inputClass = '.search__input'; 8 const inputFieldClass = '.search__input__field'; 9 const queryFieldClass = '.search__input__query'; 10 const inputClearClass = '.search__clear'; 11 const paramsClass = '.search__params'; 12 const dropdownClass = '.search__dropdown'; 13 const searchTermDropdownClass = '.search-category__dropdown'; 14 15 const queryClass = '[data-search-field="query"]'; 16 const targetClass = '[data-search-field="target"]'; 17 const conjugateClass = '[data-search-field="conjugate"]'; 18 const hostClass = '[data-search-field="host"]'; 19 const speciesClass = '[data-search-field="species-reactivity"]'; 20 const secondaryabSpeciesClass = '[data-search-field="secondaryab-species"]'; 21 const secondaryabConjugateClass = '[data-search-field="secondaryab-conjugate"]'; 22 const flexableCompatibleClass = '[data-search-field="compatible-species"]'; 23 const flexableConjugateClass = '[data-search-field="flexable-conjugate"]'; 24 25 const mobileFilterOverlay = '.mobile-filter-overlay'; 26 const mobileDropdown = '.search-category__mobile-dropdown'; 27 const mobileCancelClass = '.search__cancel'; 28 29 const mediaQuery = '(max-width: 767px)'; 30 const mediaQueryList = window.matchMedia(mediaQuery); 31 32 const defaultPlaceholder = 'Search by target, catalog number or keyword'; 33 34 const pageType = document.body.dataset.pagetype || null; 35 36 let typingTimeout = null; 37 38 // DEVS to udpate 39 const apiUrl = "/api/categoty"; 40 41 const handleFilterParentClick = (e) => { 42 43 if (!e.currentTarget.classList.contains('search__input')) { 44 const isOpen = e.target.classList.contains('is-open'); 45 46 closeAllFilters(conjugateClass); 47 closeAllFilters(hostClass); 48 closeAllFilters(speciesClass); 49 closeAllFilters(secondaryabSpeciesClass); 50 closeAllFilters(secondaryabConjugateClass); 51 closeAllFilters(flexableCompatibleClass); 52 closeAllFilters(flexableConjugateClass); 53 54 if (!isOpen) { 55 openFilter(e.currentTarget); 56 57 requestAnimationFrame(() => { 58 addClickToClose(e.currentTarget); 59 }) 60 } 61 } 62 } 63 64 const openFilter = el => el.classList.add('is-open'); 65 const closeFilter = el => el.classList.remove('is-open'); 66 const closeAllFilters = () => searchWrapper.querySelectorAll(dropdownClass).forEach(el => closeFilter(el)); 67 68 const resetFilter = (el) => el.classList.remove(...['active', 'is-open', 'has-clear', 'has-count']); 69 const resetAllFilters = () => searchWrapper.querySelectorAll(paramsClass).forEach(filter => resetFilter(filter)); 70 71 const showFilter = el => searchWrapper.querySelector(el).classList.add('active'); 72 const hideFilter = className => searchWrapper.querySelectorAll(className).forEach(el => { 73 el.classList.remove('active'); // dont change 74 resetFilterCheckboxes(el); 75 udpateFilterCount(el) 76 }); 77 78 const resetFilterCheckboxes = el => { 79 el.querySelectorAll('input[type="checkbox"]').forEach((item) => item.checked = false); 80 } 81 82 const clearInput = el => el.value = ''; 83 84 const setPlaceholder = (text) => { 85 searchWrapper.querySelectorAll(queryFieldClass).forEach(input => input.setAttribute('placeholder', text)); 86 } 87 88 const populateResultsDropdown = (results) => { 89 const iconPath = '/img/icons/Icon_Search.svg'; // DEVS to udpate 90 const template = ` 91 <div class="search__results__result"> 92 <a href="[URL]"> 93 <div class="search__results__icon"> 94 <img src="${iconPath}" width="14" /> 95 </div> 96 <div class="search__results__text"> 97 [RESULT] <span class="ptg-green">in [CATEGORY]</span> 98 </div></a> 99 </div>`; 100
101 const resultsContainer = searchWrapper.querySelectorAll('.search__results'); 102 let templateHtml = "", templateCopy; 103 104 results.data.map(resultItem => { 105 templateCopy = template; 106 templateCopy = templateCopy.replace('[RESULT]', resultItem.keyword) 107 templateCopy = templateCopy.replace('[CATEGORY]', resultItem.title) 108 templateCopy = templateCopy.replace('[URL]', resultItem.url) 109 templateHtml += templateCopy; 110 }); 111 112 resultsContainer.forEach(input => input.innerHTML = templateHtml); 113 document.querySelectorAll(inputClass).forEach(parent => openFilter(parent)); 114 } 115 116 const changeFilterCols = (paramNames) => { 117 hideFilter(targetClass); 118 hideFilter(conjugateClass); 119 hideFilter(hostClass); 120 hideFilter(speciesClass); 121 hideFilter(secondaryabSpeciesClass); 122 hideFilter(secondaryabConjugateClass); 123 hideFilter(flexableConjugateClass); 124 hideFilter(flexableCompatibleClass); 125 hideFilter(queryClass); 126 127 const paramString = paramNames.split(', ').map(name => `[data-search-field="${name}"]`).join(', '); 128 searchWrapper.querySelectorAll(paramString).forEach(param => param.classList.add('active')); 129 } 130 131 // Add rounded border to search bar container 132 mediaQueryList.addEventListener('change', (e) => { 133 setTimeout(() => { 134 $(paramsClass).removeClass('rounded-search').filter(':visible').first().addClass('rounded-search'); 135 }, 120); 136 }); 137 138 const handleSearchTermMobileClick = (e) => { 139 if (mediaQueryList.matches) { 140 document.querySelector(mobileFilterOverlay).classList.add('active'); 141 document.querySelector(mobileFilterOverlay).querySelector(`${queryClass} input`).focus(); 142 } 143 } 144 145 const handleSearchTermKeyboardInput = (e) => { 146 const searchTerm = document.getElementById('searchTerm').innerText; 147 if (searchTerm != "All") { 148 return; 149 } 150 clearTimeout(typingTimeout); 151 typingTimeout = setTimeout(() => { 152 153 fetchAutocompleteResults(e.target); 154 }, 150); 155 }; 156 157 const fetchAutocompleteResults = (el) => { 158 document.body.classList.add('busy'); 159 160 const inputParent = el.closest(paramsClass); 161 162 // if (matchMedia.matches) { 163 // const resultsContainer = document.querySelector('.search__results'); 164 // resultsContainer.innerHTML = 'Loading…'; 165 // } 166 167 if (el.value.length > 0) { 168 inputParent && inputParent.classList.add('has-clear') 169 170 fetch(`${apiUrl}?keyword=${el.value}`) 171 .then(res => res.json()) 172 .then(res => populateResultsDropdown(res)) 173 .finally(() => document.body.classList.remove('busy')) 174 } 175 else { 176 document.body.classList.remove('busy'); 177 inputParent.classList.remove('has-clear'); 178 closeFilter(inputParent); 179 } 180 } 181 182 const handleMobileCancelButton = () => { 183 document.querySelector(mobileFilterOverlay).classList.remove('active'); 184 searchWrapper.querySelector('form').reset(); 185 resetAllFilters(); 186 187 searchWrapper.querySelectorAll(queryClass).forEach((queryEl) => queryEl.classList.add('active')); 188 } 189 190 const addClickToClose = (el) => { 191 192 $(document).on('keyup.searchdd', function (e) { 193 if (e.which == 27) { 194 handleDropdownClose(el); 195 } 196 }); 197 198 $(document).on('click.searchdd', function (e) { 199 if (!el.contains(e.target)) { 200 handleDropdownClose(el); 201 } 202 }); 203 } 204 205 const handleDropdownClose = (el) => { 206 closeFilter(el); 207 udpateFilterCount(el); 208 209 $(document).off('.searchdd'); 210 } 211 212 const handleClearSearchTerm = (e) => { 213 const seearchTermParent = e.target.closest(paramsClass); 214 seearchTermParent.classList.remove('is-open'); 215 seearchTermParent.classList.remove('has-clear'); 216
217 document.querySelectorAll(inputFieldClass).forEach(input => clearInput(input)); 218 } 219 220 const handleSearchTermDropdownClick = (e) => { 221 const searchTermEl = e.currentTarget; 222 const categoryDropdownParent = searchTermEl.closest('.search__params'); 223 224 // Remove all active class, then add active to chosen item 225 searchTermEl 226 .closest('.search__dropdown') 227 .querySelectorAll('span').forEach(el => el.classList.remove('active')); 228 229 searchTermEl.classList.add('active'); 230 231 //Update visiblity on filter cols 232 changeFilterCols(searchTermEl.dataset.params) 233 234 document.getElementById('searchTerm').innerText = searchTermEl.title; 235 document.querySelector('input[name="category"]').value = searchTermEl.dataset.category; 236 237 requestAnimationFrame(() => { 238 closeFilter(categoryDropdownParent); 239 240 setPlaceholder(searchTermEl.dataset.placeholder || defaultPlaceholder) 241 }) 242 } 243 244 const udpateFilterCount = (dropdown) => { 245 const countEl = dropdown.querySelector('.search-term-layout__count'); 246 const checkedInputs = dropdown.querySelectorAll(`input[type="checkbox"]:checked`); 247 248 if (checkedInputs.length > 0) { 249 countEl.innerText = checkedInputs.length; 250 dropdown.classList.add('has-count'); 251 } 252 else { 253 dropdown.classList.remove('has-count'); 254 } 255 } 256 257 const handleFilterMenuConfirmButton = (e) => { 258 const dropdown = e.target.closest(paramsClass); 259 260 requestAnimationFrame(() => { 261 closeFilter(dropdown); 262 udpateFilterCount(dropdown) 263 }); 264 } 265 266 const handleFilterMenuClearButton = (e) => { 267 const dropdown = e.target.closest(paramsClass); 268 dropdown.classList.remove('has-count'); 269 270 const countEl = dropdown.querySelector('.search-term-layout__count'); 271 countEl.innerText = 0; 272 273 resetFilterCheckboxes(dropdown) 274 } 275 276 const handleMobileCategoryChange = (e) => { 277 setPlaceholder(e.target[e.target.selectedIndex].dataset.placeholder || defaultPlaceholder); 278 searchWrapper.querySelector(`input[name='category']`).value = e.target.value; //updates hidden input with val 279 280 //changeFilterCols(e.target[e.target.selectedIndex].dataset.params) 281 282 //const submitBtn = searchWrapper.querySelector('.mobile-filter-overlay__submit'); 283 //if (e.target[e.target.selectedIndex].dataset.params === 'query') { 284 // submitBtn.classList.remove('active') 285 //} 286 //else { 287 // submitBtn.classList.add('active') 288 //} 289 } 290 291 const checkPageType = () => { 292 if (pageType) { 293 if (mediaQueryList.matches) { 294 $(`${mobileDropdown} [data-landing="${pageType}"]`).trigger('click'); 295 } 296 else { 297 $(`.search [data-landing="${pageType}"]`).trigger('click'); 298 } 299 } 300 } 301 302 $(searchClass).on('click', '.search__dropdown__actions__clear', handleFilterMenuClearButton); 303 $(searchClass).on('click', '.search__dropdown__actions__confirm', handleFilterMenuConfirmButton); 304 305 $(searchClass).on('click', paramsClass, handleFilterParentClick) 306 $(mobileCancelClass).on('click', handleMobileCancelButton); 307 308 $(`${mobileDropdown} select`).on('change', handleMobileCategoryChange) 309 310 $(searchTermDropdownClass).on('click', 'span', handleSearchTermDropdownClick); 311 $(inputClearClass).on('click', handleClearSearchTerm); 312 313 $(queryFieldClass).on('keydown', handleSearchTermKeyboardInput); 314 //$(queryFieldClass).on('click', handleSearchTermMobileClick);//áÃÃûñÃÃÃ÷ 315 316 window.addEventListener('DOMContentLoaded', checkPageType); 317 318 var _lastRealInteraction = 0; 319 $(document).on('mousedown keydown touchstart', function(e) { 320 if (e.originalEvent && e.originalEvent.isTrusted) { 321 _lastRealInteraction = Date.now(); 322 } 323 });
324$('form[action="/results"]').on('submit', function(e) { 325 e.preventDefault(); 326 var isTrusted = e.originalEvent && e.originalEvent.isTrusted; 327 var hasRecentInteraction = (Date.now() - _lastRealInteraction) < 300; 328 if (isTrusted && hasRecentInteraction) { 329 try { localStorage.setItem('isHumanClick', 'true'); } catch(ex) {} 330 } else { 331 try { localStorage.removeItem('isHumanClick'); } catch(ex) {} 332 } 333 this.submit(); 334 }); 335$(document).on('click', 'a.example-text', function(e) { 336Â Â Â Â Â Â e.preventDefault(); 337Â Â Â Â Â Â var $this = $(this); 338Â Â Â Â Â Â var targetHref = $this.attr('href'); 339Â Â Â Â Â Â 340Â Â Â Â Â Â var isTrusted = e.originalEvent && e.originalEvent.isTrusted; 341Â Â Â Â Â Â var hasRecentInteraction = (Date.now() - _lastRealInteraction) < 300; 342Â Â Â Â Â Â 343Â Â Â Â Â Â if (isTrusted && hasRecentInteraction) { 344Â Â Â Â Â Â Â Â try { localStorage.setItem('isHumanClick', 'true'); } catch(ex) {} 345Â Â Â Â Â Â } else { 346Â Â Â Â Â Â Â Â try { localStorage.removeItem('isHumanClick'); } catch(ex) {} 347Â Â Â Â Â Â } 348Â Â Â Â Â Â 349Â Â Â Â Â Â window.location.href = targetHref; 350Â Â Â Â }); 351 const urlCategory = getRequest("category"); 352 if (urlCategory !== undefined && urlCategory != null) { 353 354 const searchTermEl = document.querySelector(searchTermDropdownClass).querySelector('span[data-category="' + urlCategory + '"]'); 355 const searchDropdownParent = searchTermEl.closest('.search__category'); 356 357 handleDropdownClose(searchDropdownParent); 358 359 [...document.querySelector(searchTermDropdownClass).querySelectorAll('span')].map(el => el.classList.remove('active')); 360 document.getElementById('searchTerm').innerText = searchTermEl.title; 361 document.querySelector('input[name="category"]').value = searchTermEl.dataset.category; 362 searchTermEl.classList.add('active'); 363 364 //updatePlaceholder(searchTermEl.dataset.placeholder || defaultPlaceholder) 365 //Update visiblity on filter cols 366 changeFilterCols(searchTermEl.dataset.params) 367 } 368})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.