1ready = (callback) => { 2 if (document.readyState !== 'loading') { 3 callback() 4 } else { 5 document.addEventListener('DOMContentLoaded', callback) 6 } 7} 8 9function escapeHtml(text) { 10 var map = { 11 '&': '&', 12 '<': '<', 13 '>': '>', 14 '"': '"', 15 "'": ''' 16 }; 17 18 return text.replace(/[&<>"']/g, function (m) { 19 return map[m]; 20 }); 21} 22 23ready(() => { 24 25 /** 26 * Get data from the wp_localize_script 27 * 28 * ElasticSearch.postType is the CPT list with their name and label 29 * ElasticSearch.indexName is the ElasticSearch index name 30 * */ 31 const cpt = ElasticSearch.postType 32 const indexName = ElasticSearch.indexName 33 34 /** 35 * Query the selectors that are use is this script 36 * $searchInput is the input where you write your search 37 * $searchResult is the contaire where the suggestion are inject 38 * */ 39 const $searchInput = document.querySelectorAll('.searchInput') 40 41 /** 42 * Get the parameters in the URL 43 * "s" for the search terms 44 * "page" for the number of page that we multiple by 10 to have the correct number of posts per page to display 45 * */ 46 const searchQuery = window.location.search 47 let params = new URLSearchParams(searchQuery); 48 let searchedWords = String(params.get("s")) 49 searchedWords = escapeHtml(searchedWords) 50 51 // Create the URL to the right index in the ElasticSearch API 52 let URL = `https://apis.creasrv.net/${indexName}/_search` 53 // URL += '?filter_path=hits.hits._id,' 54 // URL += 'hits.total,' 55 // URL += 'hits.hits._score,' 56 // URL += 'hits.hits._source.post_title,' 57 // URL += 'hits.hits._source.meta.top_post_group_top_post_introduction.value,' 58 // URL += 'hits.hits._source.permalink,' 59 // URL += 'hits.hits._source.post_type,' 60 61 // Create an instance of PostQuery class 62 const Query = new PostsQuery() 63 Query.URLQuery = `?s=${searchedWords}` 64 let fetchArgs = Query.fetchArgs(searchedWords) 65 66 // Init the default post offset, we increment it later 67 let offset = 0; 68 69 let n = 0; 70 /*** 71 * This listener is used to get the value from the search input and 72 * load the search results page if the result has content 73 ****/ 74 const $searchSVG = document.querySelectorAll('.search-svg') 75 $searchSVG.forEach(function (search) { 76 !!search && 77 search.addEventListener('click', function () { 78 let $searchInput = this.closest('.search-container').querySelector('.searchInput'); 79 if ($searchInput.value.length > 0) { 80 window.location.href = '/?s=' + $searchInput.value 81 } 82 }) 83 }) 84 85 if (searchedWords !== 'null') { 86 87 // Init the var where we stock the URL parameters 88 let postPerPage = 10 89 let URLcpt 90 let URLdate 91 let pages = Number(params.get("pages")) ?? 0 92 93 // Get the loaded pages' number 94 if (params.get("pages")) { 95 postPerPage = Number(params.get("pages")) * postPerPage; 96 } 97 // Get the selected CPT 98 if (params.get("cpt")) { 99 URLcpt = params.get("cpt"); 100 URLcpt = URLcpt.split(','); 101 } 102 // Get the selected range date 103 if (params.get("date")) { 104 URLdate = params.get("date"); 105 } 106 107 108 // Search page html selector 109 const $sampleContainer = document.querySelector('.hide[data-sample="searchResult"]') 110 const $resultContainer = document.querySelector('#search_publications') ?? '' 111 const $pageIntroContainer = document.querySelector('.cover__intro') ?? '' 112 const $paginationContainer = document.querySelector('#searchPagination') ?? '' 113 const $paginationButton = document.querySelector('#load-older-posts') ?? '' 114 const $foundPostsContainer = document.querySelector('#foundPosts') ?? '' 115 const $visiblePosts = document.querySelector('#posts_per_page') 116 const $postResultFilters = document.querySelector('#filters-results') 117 const $postTypesFilter = document.querySelector('#postType') 118 const $postTypesInput = document.querySelector('#postTypeInput') 119 const $dateFilter = document.querySelector('#publicationDate') 120 const $dateInput = document.querySelector('#dateInput') 121 const $customFiltersInput = document.querySelectorAll('.filter_custom .filter__input-container input') 122 const $customFilters = document.querySelectorAll('.filter_custom .filter__custom-container ul') 123 124 const $validateFilter = document.querySelector('#validateSystemeFilter') 125 const $resetFilter = document.querySelector('#resetSystemeFilter') 126 const $filters = document.querySelector('.filter__options') 127 128 129 if ($resultContainer) { 130 const selectBoxes = document.querySelectorAll('.select-box') 131 132 !!selectBoxes &&
133 selectBoxes.forEach((selectBox) => { 134 selectBox.addEventListener('click', function () { 135 if (selectBox.classList.contains('is-closed')) { 136 selectBox.classList.replace('is-closed', 'is-open') 137 } else { 138 selectBox.classList.replace('is-open', 'is-closed') 139 } 140 }) 141 }) 142 143 document.addEventListener('click', function (event) { 144 let selectBoxes = document.querySelectorAll('.select-box.is-open') 145 selectBoxes.forEach((selectBox) => { 146 if (selectBox !== null && selectBox !== event.target.closest('.select-box')) { 147 selectBox.classList.replace('is-open', 'is-closed') 148 } 149 }) 150 }) 151 152 let elementSelected = $filters.querySelectorAll('.selected') 153 // If no element fit the selector, we restore the "reset button" to its original state 154 if (elementSelected.length === 0) { 155 $resetFilter.classList.add('empty-selection') 156 $validateFilter.classList.add('empty-selection') 157 } 158 159 } 160 // Check the checkbox where the value is present in the URLcpt array 161 if ($postTypesFilter && URLcpt !== undefined) { 162 URLcpt.forEach((cpt) => { 163 $postTypesFilter.querySelector(`input[value=${cpt}]`).checked = true 164 }) 165 } 166 // Check the checkbox if the URL date is set 167 if ($dateFilter && URLdate !== undefined) { 168 $dateFilter.querySelector(`input[value=${URLdate}]`).checked = true 169 } 170 171 if ($customFilters) { 172 $customFilters.forEach(function ($customFilter) { 173 let URLCustomFilter = '' 174 let param = $customFilter.closest('.filter_custom').querySelector('.filter__input-container input').getAttribute('id') 175 if (params.get(param)) { 176 URLCustomFilter = params.get(param); 177 } 178 179 if (URLCustomFilter) { 180 $customFilter.querySelector(`input[value=${URLCustomFilter}]`).checked = true 181 } 182 }) 183 184 } 185 186 let filtersKey = ['keyword'] 187 188 /*** 189 * Function triggered after page load 190 * and on ES fetch query when one of #postType checkbox is checked 191 * 192 * Add or remove the value from : 193 * 1. The Query.URLArgs object where we store the parameters present inside the URL 194 * 2. The Query.FetchArgs object where we store the data require to query the ElasticSearch server 195 * 196 * To understand the structure of the below objects and arrays you need to understand the structure 197 * of the ElasticSearch bool query 198 * 199 * @doc https://www.elastic.co/guide/en/elasticsearch/reference/current/query-filter-context.html 200 ****/ 201 function updateCPT() { 202 // Get the $postTypesFilter checkbox NodList 203 let checkboxes = $postTypesFilter.querySelectorAll('input[type="checkbox"]:checked') 204 205 // Init values 206 let value = '' 207 208 // Init the Query.URLArgs object with the cpt entry 209 Query.URLArgs.cpt = {} 210 211 // Init the Query.URLArgs object with the parameterValue array 212 Query.URLArgs.cpt.parameterValue = [] 213 214 if (!filtersKey.includes('cpt')) { 215 filtersKey.push('cpt') 216 } 217 // Create the new bool.should entry in the bool.must array from the fetchArgs method 218 // It allows us to make cross filter query on ElasticSearch 219 // According to the ElasticSearch process 220 let newBool = { 221 bool: { 222 should: [] 223 } 224 } 225 226 // Init the var where we store the selected cpt query 227 let newMatch 228 // For each checkbox 229 for (let i = 0; i < checkboxes.length; i++) { 230 // We store the checkbox value 231 let checkbox = checkboxes[i] 232 // if it's checked 233 if (checkbox.checked === true) { 234 235 // If we are on the last checkbox element of the NodeList 236 // We don't add a comma 237 if (i === Number(checkboxes.length - 1)) { 238 value += `${checkbox.dataset.label}` 239 } else { 240 value += `${checkbox.dataset.label}, ` 241 } 242 243 // We push its value in the Query.URLArgs.cpt.parameterValue 244 Query.URLArgs.cpt.parameterValue.push(checkbox.value) 245 246 // Add the checked checkbox value in the newMatch object 247 newMatch = { 248 multi_match: { 249 query: checkbox.value, 250 fields: [ 251 'post_type' 252 ], 253 } 254 } 255 // Push this new object inside the newBool.bool.should array 256 newBool.bool.should.push(newMatch) 257 258 } 259 } 260 // If newMatch isn't undefined it means that one value at least has been selected 261 if (newMatch !== undefined) { 262 // We limit the value to 80 characters based of a Query class method 263 $postTypesInput.value = Query.truncate(value, 80, '...', ',') 264 265 266 // The $subCategoriesInput has class selected 267 $postTypesInput.classList.add('selected') 268 269 // So we can add the "should" query to the fetchArgs object used to fill the ElasticSearch query. 270 let n = filtersKey.indexOf('cpt') 271 fetchArgs.bool.must[n] = newBool 272 273 } else { 274 // Remove the argument from the URL builder 275 delete Query.URLArgs.cpt 276 277 // Restore the original $subCategoriesInput condition 278 $postTypesInput.value = '' 279 $postTypesInput.classList.remove('selected') 280 281 // Get all elements which have .selected as class 282 let elementSelected = $filters.querySelectorAll('.selected') 283 // If no element fit the selector, we restore the "reset button" to its original state 284 if (elementSelected.length === 0) { 285 $resetFilter.classList.add('empty-selection') 286 $validateFilter.classList.add('empty-selection') 287 } 288 289 fetchArgs.bool.must.splice(filtersKey.indexOf('cpt'), 1) 290 filtersKey.splice(filtersKey.indexOf('cpt'), 1) 291 } 292 } 293 294 /*** 295 * Function triggered after page load 296 * and on ES fetch query when one of #publicationDate checkbox is checked 297 * 298 * Add or remove the value from : 299 * 1. The Query.URLArgs object where we store the parameters present inside the URL 300 * 2. The Query.FetchArgs object where we store the data require to query the ElasticSearch server 301 * 302 * To understand the structure of the below objects and arrays you need to understand the structure 303 * of the ElasticSearch bool query 304 * 305 * @doc https://www.elastic.co/guide/en/elasticsearch/reference/current/query-filter-context.html 306 ****/ 307 function updateDate() { 308 // Get the $dateFilter checkbox NodList 309 let checkbox = $dateFilter.querySelectorAll('input[type="radio"]:checked') 310 311 if (checkbox.length > 0) { 312 313 314 // Init values 315 let value = checkbox[0].value 316 317 // Init the Query.URLArgs object with the cpt entry 318 Query.URLArgs.date = {} 319 320 // Init the Query.URLArgs object with the parameterValue array 321 Query.URLArgs.date.parameterValue = [] 322 323 // For each checkbox 324 if (value !== 'no_date') { 325 326 // We limit the value to 80 characters based of a Query class method 327 $postTypesInput.value = value 328 329 // The $subCategoriesInput has class selected 330 $postTypesInput.classList.add('selected') 331 332 // We push its value in the Query.URLArgs.cpt.parameterValue 333 Query.URLArgs.date.parameterValue.push(value) 334 335 // Init the filter object for the ES bool query 336 fetchArgs.bool.filter = {} 337 // Add the checked checkbox value in the ES query range filter 338 fetchArgs.bool.filter.range = {"post_date": {gte: value, lte: "now"}} 339 } else { 340 // Empty the date object of the Query.URLArgs 341 Query.URLArgs.date = {} 342 343 // Remove the filter object for the ES book query 344 if (fetchArgs.bool) { 345 delete fetchArgs.bool.filter 346 } 347 348 // Restore the original $subCategoriesInput condition 349 $dateInput.value = '' 350 $dateInput.classList.remove('selected') 351 352 // Get all elements which have .selected as class 353 let elementSelected = $filters.querySelectorAll('.selected') 354 // If no element fit the selector, we restore the "reset button" to its original state 355 if (elementSelected.length === 0) { 356 $resetFilter.classList.add('empty-selection') 357 $validateFilter.classList.add('empty-selection') 358 } 359 } 360 } 361 } 362 363 function updateCustomFilters() { 364 // Get the $dateFilter checkbox NodList 365 !!$customFilters &&
366 $customFilters.forEach(function ($customFilter) { 367 let checkboxes = $customFilter.querySelectorAll('input[type="checkbox"]:checked') 368 let param = $customFilter.closest('.filter_custom').querySelector('.filter__input-container input').getAttribute('id') 369 let value = '' 370 371 if (!filtersKey.includes(param)) { 372 filtersKey.push(param) 373 } 374 // Init the Query.URLArgs object with the cpt entry 375 Query.URLArgs[param] = {} 376 377 // Init the Query.URLArgs object with the parameterValue array 378 Query.URLArgs[param].parameterValue = [] 379 380 let newBool = { 381 bool: { 382 must: [] 383 } 384 } 385 386 // Init the var where we store the selected cpt query 387 let newMatch 388 389 390 // The $subCategoriesInput has class selected 391 $customFilter.closest('.filter_custom').querySelector('.filter__input-container input').classList.add('selected') 392 393 for (let i = 0; i < checkboxes.length; i++) { 394 // We store the checkbox value 395 let checkbox = checkboxes[i] 396 397 Query.URLArgs[param].parameterValue.push(checkbox.value) 398 399 // If we are on the last checkbox element of the NodeList 400 // We don't add a comma 401 if (i === Number(checkboxes.length - 1)) { 402 value += `${checkbox.getAttribute('id')}` 403 } else { 404 value += `${checkbox.getAttribute('id')}, ` 405 } 406 407 // Add the checked checkbox value in the newMatch object 408 newMatch = { 409 multi_match: { 410 query: checkbox.value, 411 fields: [ 412 'meta.' + param + '.value' 413 ], 414 operator: 'and' 415 } 416 } 417 // Push this new object inside the newBool.bool.should array 418 newBool.bool.must.push(newMatch) 419 420 // We limit the value to 80 characters based of a Query class method 421 $customFilter.closest('.filter_custom').querySelector('.filter__input-container input').value = Query.truncate(value, 80, '...', ',') 422 } 423 424 if (newMatch !== undefined) { 425 // We can add the "should" query to the fetchArgs object used to fill the ElasticSearch query. 426 let n = filtersKey.indexOf(param) 427 fetchArgs.bool.must[n] = newBool 428 } else { 429 // Remove the argument from the URL builder 430 delete Query.URLArgs[param] 431 432 // Restore the original $subCategoriesInput condition 433 $customFilter.closest('.filter_custom').querySelector('.filter__input-container input').value.value = '' 434 $customFilter.closest('.filter_custom').querySelector('.filter__input-container input').classList.remove('selected') 435 436 // Get all elements which have .selected as class 437 let elementSelected = $filters.querySelectorAll('.selected') 438 // If no element fit the selector, we restore the "reset button" to its original state 439 if (elementSelected.length === 0) { 440 $resetFilter.classList.add('empty-selection') 441 $validateFilter.classList.add('empty-selection') 442 } 443 444 fetchArgs.bool.must.splice(filtersKey.indexOf(param), 1) 445 filtersKey.splice(filtersKey.indexOf(param), 1) 446 } 447 }) 448 } 449 450 // After page load execute the fuction below if the #publicationDate filter is present 451 if ($dateFilter) { 452 updateDate() 453 } 454 455 // After page load execute the fuction below if the #postType filter is present 456 if ($postTypesFilter) { 457 updateCPT() 458 } 459 460 if ($customFilters) { 461 updateCustomFilters() 462 } 463 /*** 464 * Check for the filter validation button's presence and listen his click 465 * 466 * This function is used to build and submit the query to the ES server 467 * Although it builds the url parameter that will be pushed in the page url 468 ****/ 469 !!$validateFilter && 470 $validateFilter.addEventListener('click', function () { 471 472 // As this is a new search, we don't keep the post previously dis
472played 473 Query.nextPage = false 474 475 pages = 0 476 offset = 0 477 478 // Exectute the updateDate() function if #publicationDate is present 479 if ($dateFilter) { 480 updateDate() 481 } 482 483 // Exectute the updateDate() function if #postType is present 484 if ($postTypesFilter) { 485 updateCPT() 486 } 487 488 if ($customFilters) { 489 updateCustomFilters() 490 } 491 492 $resetFilter.classList.remove('empty-selection') 493 // As the functions below have already set the params for the URL, we execute the function 494 // which is in charge of building and displaying the URL params 495 Query.pushSearchArgsInUrl() 496 497 // Call the fetchES method from the Query class 498 execFetch() 499 500 }) 501 502 !!$resetFilter && 503 $resetFilter.addEventListener('click', function () { 504 pages = 0 505 offset = 0 506 postPerPage = 10 507 508 // Remove all the arguments in the URL 509 Object.keys(Query.URLArgs).forEach(function (arg) { 510 delete Query.URLArgs[arg] 511 }) 512 513 let $selectedInputs = $filters.querySelectorAll('.selected') 514 515 let $checkedInputs = $filters.querySelectorAll('input:checked') 516 517 $selectedInputs.forEach((input) => { 518 input.classList.remove('selected') 519 input.value = '' 520 }) 521 522 $checkedInputs.forEach((input) => { 523 input.checked = false 524 }) 525 526 // Exectute the updateDate() function if #publicationDate is present 527 if ($dateFilter) { 528 updateDate() 529 } 530 531 // Exectute the updateDate() function if #postType is present 532 if ($postTypesFilter) { 533 updateCPT() 534 } 535 536 if ($customFilters) { 537 updateCustomFilters() 538 } 539 540 window.history.pushState( 541 '', '', Query.URLQuery 542 ) 543 544 execFetch() 545 546 $resetFilter.classList.add('empty-selection') 547 }) 548 549 if ($sampleContainer !== null) { 550 // Call the fetchES method from the Query class 551 execFetch() 552 } 553 554 555 !!$paginationButton && ( 556 $paginationButton.addEventListener('click', function () { 557 // Call the fetchES method from the Query class 558 Query.nextPage = true 559 Query.fetchES(URL, fetchArgs, offset, postPerPage).then( 560 data => { 561 562 // Get the number of post found by the query 563 let postCount = data.hits.total.value 564 565 // Get the number of post returned by the query 566 offset = data.hits.hits.length + offset 567 568 // Add the number of post found and returned in the pagination information 569 if ($paginationContainer) { 570 // Update the displayed post information 571 $visiblePosts.innerHTML = Number($visiblePosts.innerHTML) + Number(data.hits.hits.length) 572 573 // Hide the load more posts button only if the number of returned post is equal to the posts found 574 if ($visiblePosts.innerHTML === $foundPostsContainer.innerHTML) { 575 Query.fadeOut($paginationButton) 576 } 577 } 578 579 // If the response have posts, we display them 580 if (postCount > 0) { 581 582 // List of the posts found 583 let hits = data.hits.hits 584 585 // Apped the posts in the container 586 Query.appendSearchResult($sampleContainer, $resultContainer, hits, cpt) 587 588 // Return the page number to display it in the URL 589 pages++ 590 591 Query.URLArgs.pages = {} 592 Query.URLArgs.pages.parameterValue = [] 593 594 Query.URLArgs.pages.parameterValue.push(pages) 595 Query.pushSearchArgsInUrl() 596 } else { 597 $resultContainer.innerHTML = $sampleC
597ontainer.outerHTML 598 $resultContainer.innerHTML += 'Aucun résultat pour votre recherche.' 599 } 600 } 601 ).catch(err => { 602 // TODO handle error to send 603 console.log(err) 604 }) 605 }) 606 ) 607 608 function execFetch() { 609 Query.fetchES(URL, fetchArgs, 0, postPerPage).then( 610 data => { 611 // Get the number of post found by the query 612 let postCount = data.hits.total.value 613 614 // Get the number of post returned by the query 615 616 offset = data.hits.hits.length + offset 617 618 // Add the number of post found in the result page intro with the associated searched terms 619 if ($pageIntroContainer) { 620 $pageIntroContainer.innerHTML = `${postCount} résultats pour "${searchedWords}"` 621 } 622 623 // Add the number of post found and returned in the pagination information 624 if ($paginationContainer) { 625 $foundPostsContainer.innerHTML = postCount 626 $visiblePosts.innerHTML = offset 627 } 628 629 // Display the load more posts button only if the number of returned post isn't equal to the posts found 630 if (offset !== postCount) { 631 Query.fadeIn($paginationButton) 632 } else { 633 Query.fadeOut($paginationButton) 634 } 635 636 // If the response have posts, we display them 637 if (postCount > 0) { 638 // List of the posts found 639 let hits = data.hits.hits 640 641 // Append the result 642 Query.appendSearchResult($sampleContainer, $resultContainer, hits, cpt) 643 } else { 644 $resultContainer.innerHTML = $sampleContainer.outerHTML 645 $resultContainer.innerHTML += 'Aucun résultat pour votre recherche.' 646 } 647 } 648 ).catch(err => { 649 // TODO handle error to send 650 console.log(err) 651 }) 652 } 653 } 654 655 // Function to debounce another function 656 function debounce(func, delay) { 657 let timeout; 658 return function (...args) { 659 clearTimeout(timeout); 660 timeout = setTimeout(() => func.apply(this, args), delay); 661 }; 662 } 663 664 /*** 665 * Script use to query the ElasticSearch API and to display his response 666 * */ 667 // Once an input is detect inside the search input you trigger this script 668 $searchInput.forEach(function (input) { 669 !!input && ( 670 input.addEventListener('input', debounce(async function () { 671 let $searchResult = this.closest('.container').querySelector('.searchResult'); 672 673 if ($searchResult) { 674 if (this.value.length < 3) { 675 $searchResult.innerHTML = '' 676 Query.fadeOut($searchResult) 677 this.closest('div').classList.remove('search-container-open') 678 } 679 680 if (this.value.length > 2) { 681 // Create the URL to redirect 682 let urlToRedirect = '/?s=' + this.value 683 684 fetchArgs = Query.fetchArgs(this.value) 685 686 // Call the fetchES method from the Query class 687 try { 688 const data = await Query.fetchES(URL, fetchArgs) 689 690 // If the response have posts, we display them 691 if (data.hits.total.value > 0) { 692 // Display the result container 693 Query.fadeIn($searchResult) 694 this.closest('div').classList.add('search-container-open') 695 696 // List of the posts found 697 let hits = data.hits.hits 698 699 // Empty the result container to remove the default message 700 $searchResult.innerHTML = ''; 701 702 // Build the list of posts found
703 hits.forEach((hit) => { 704 // Get the post info 705 let ID = hit._id 706 let score = hit._score 707 let title = hit._source.post_title 708 let post_type = hit._source.post_type 709 let link = hit._source.permalink 710 711 // Create the li 712 let content = '<li class="result" id="' + ID + '" data-score="' + score + '">' 713 // Create the a inside the li 714 content += '<a class="resultTitle" href="' + link + '">' + title + '</a>' 715 716 // Add the CPT name to the li 717 // To do it we use the ElasticSearch.postType array 718 // to compare the CPT name and get their label 719 let entries = Object.entries(cpt); 720 entries.forEach(([name, label]) => { 721 if (name === post_type) { 722 content += '<span class="resultCPT">' + label + '</span>' 723 } 724 }) 725 726 // Close the li 727 content += '</li>' 728 729 // Add the li to the list of result 730 $searchResult.innerHTML += content 731 }) 732 733 // In the last place of the list display a link to the search page 734 $searchResult.innerHTML += `<li class="show-all"><a href="${urlToRedirect}">Voir tous les résultats</a></li>` 735 736 737 } else { 738 // Display default message for no post in the response 739 $searchResult.innerHTML = '<li class="no-results"><span>Aucun résultat ne correspond à votre recherche</span></li>' 740 } 741 } catch (err) { 742 // TODO handle error to send 743 console.log(err) 744 } 745 } 746 } 747 }, 375)) 748 ) 749 }) 750 751 /* 752 * Script use to check the keydown event 753 * And enable to run through each element of the list helped with the arrow keys 754 * 755 * This script can't work without a success query that returns some results. 756 * */ 757 758 // Var use to know if a list's li is already selected 759 let liSelected 760 761 //Once your have press any keycaps inside the search field you trigger this script 762 $searchInput.forEach(function (input) { 763 !!input && ( 764 input.addEventListener('keydown', function (event) { 765 766 // The list of query's ElasticSearch results 767 const $searchList = this.closest('.container').querySelectorAll('.searchResult li'); 768 769 switch (event.keyCode) { 770 771 case 40: // if the DOWN key is pressed 772 // If the var liSelected isn't empty we remove the class from him and his <a> 773 if (liSelected) { 774 liSelected.classList.remove('selected'); 775 liSelected.firstChild.classList.remove('selected'); 776 // And we get the next li as liSelected and we add to him and his <a> the .selected 777 let next = liSelected.nextSibling; 778 if (next !== null) { 779 liSelected = next 780 next.classList.add('selected') 781 next.firstChild.classList.add('selected'); 782 } else { 783 // If no li left we go back to the first element of the list 784 liSelected = $searchList.item(0) 785 liSelected.classList.add('selected') 786 liSelected.firstChild.classList.add('selected'); 787 } 788 } else { 789 // If liSelected is empty we take the first element of the list 790 liSelected = $searchList.item(0); 791 liSelected.classList.add('selected') 792 liSelected.firstChild.classList.add('selected'); 793 } 794 break 795 796 case 38: // if the UP key is pressed 797 // If the var liSelected isn't empty we remove the class from him and his <a> 798 if (liSelected) { 799 liSelected.classList.remove('selected'); 800 liSelected.firstChild.classList.remove('selected'); 801 // And we get the previous li as liSelected and we add to him and his <a>
801 the .selected 802 let previous = liSelected.previousSibling; 803 if (previous !== null) { 804 liSelected = previous 805 previous.classList.add('selected'); 806 previous.firstChild.classList.add('selected'); 807 } else { 808 // If no li left we go back to the before last element of the list 809 // the last element is the link to the search page 810 let last = $searchList[$searchList.length - 1]; 811 liSelected = last 812 last.classList.add('selected'); 813 last.firstChild.classList.add('selected'); 814 } 815 // If liSelected is empty we take the before last element of the list 816 } else { 817 let last = $searchList[$searchList.length - 1]; 818 liSelected = last 819 last.classList.add('selected'); 820 last.firstChild.classList.add('selected'); 821 } 822 break 823 824 case 13 : // if enter key is pressed 825 826 // If the liSelected isn't empty we redirect to the result page 827 if (liSelected !== undefined) { 828 window.location.href = liSelected.firstChild.href; 829 } else { 830 // else we go to the search page with the query append 831 window.location.href = '/?s=' + this.value 832 } 833 break 834 835 case 27 : 836 // if escap key is pressed we unfocus the search input and hide the result page 837 Query.fadeOut(input) 838 input.blur() 839 break 840 } 841 }) 842 ) 843 844 } 845 ) 846})
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.