1 /* SEARCH ALGOLIA *******************************************************/ 2 3 // convert file size to human readable text 4 function formatBytes(a,b=2){ 5 if(undefined===a)return""; 6 if(0===a)return"0 Bytes"; 7 8 const c=0>b?0:b,d=Math.floor(Math.log(a)/Math.log(1024));return parseFloat((a/Math.pow(1024,d)).toFixed(c))+" "+["Bytes","KB","MB","GB","TB","PB","EB","ZB","YB"][d] 9 } 10 11 // add commas to numbers 12 function formatNumbersWithCommas(num) { 13 return num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,') 14 } 15 16 17 //if ($('#content.algolia').length) { 18 19 20 21 22 //const INSTANT_SEARCH_INDEX_NAME = "dev_docs"; 23 //const INSTANT_SEARCH_HIERARCHICAL_ATTRIBUTE = "hierarchicalCategories.lvl0"; 24 25 var instantSearchRouter = instantsearch.routers.history(); // Safari doesn't like const for this as it may try and redefine it 26 27 let dateNow = new Date(); 28 dateNow.toLocaleString('en-US', { timeZone: 'America/Los_Angeles' }); 29 30 /* 31 const templeteTimestamp = document.getElementById('content'); 32 if(templeteTimestamp.dataset.timestamp) { 33 dateNow = new Date(parseInt(templeteTimestamp.dataset.timestamp)); 34 }*/ 35 const dateNowBeginDay = new Date(dateNow.getFullYear() 36 ,dateNow.getMonth() 37 ,dateNow.getDate() 38 ,0,0,0); 39 const dateNowEndDay = new Date(dateNow.getFullYear() 40 ,dateNow.getMonth() 41 ,dateNow.getDate() 42 ,23,59,59); 43 44 45 const getFormattedDateForPicker = (date) => { 46 //jquery date picker requires YYYY-MM-DD 47 return date.getFullYear() 48 + "-" 49 + ("0" + (date.getMonth() + 1)).slice(-2) 50 + "-" 51 + ("0" + date.getDate()).slice(-2); 52 } 53 54 // Make UNIX Timestamps. JS does milliseconds divide by 1000 and eliminate decimals. 55 // const dateNowMS = (Date.now() / 1000).toFixed(0); 56 // const endDate24HoursAgo = dateNowMS - (24 * 60 * 60); 57 const dateNowMS = dateNow.getTime(); 58 const minuteMS = 1000 * 60; 59 const hourMS = minuteMS * 60; 60 const dayMS = hourMS * 24; 61 //set our date to one month ago 62 const monthPrevious = new Date(dateNow.setMonth(dateNow.getMonth() - 1)); 63 const monthPreviousMS = monthPrevious.getTime(); 64 const yearMS = dayMS * 365; 65 66 const endDate24HoursAgo = dateNowMS - dayMS; 67 const endDate1WeekAgo = dateNowMS - (dayMS * 7); 68 const endDate1MonthAgo = monthPreviousMS; 69 const endDate1YearAgo = dateNowMS - yearMS; 70 71 let customDateMSFrom = new Date(dateNowMS); //default to 72 let threeYearsAgo = new Date(customDateMSFrom); 73 threeYearsAgo.setFullYear(customDateMSFrom.getFullYear() - 3); 74 customDateMSFrom = threeYearsAgo.getTime(); 75 let customDateMSTo = dateNowMS; //default to 76 77 78 // FOR TESTING 79 //const index = searchClient.initIndex(indexEnv); 80 81 /*index.search('about').then(({ hits }) => { 82 console.log(hits); 83 });*/ 84 85 86 // END FOR TESTING 87 88 89 90 91 // Custom Autocomplete 92 93 // Set the InstantSearch index UI state from external events. 94 function setInstantSearchUiState(indexUiState) { 95 96 search.setUiState(uiState => ({ 97 ...uiState, 98 [indexEnv]: { 99 ...uiState[indexEnv], 100 // We reset the page when the search state changes. 101 page: 1, 102 ...indexUiState, 103 }, 104 })); 105 } 106 107 // Return the InstantSearch index UI state. 108 function getInstantSearchUiState() { 109 const uiState = instantSearchRouter.read(); 110 //return (uiState && uiState[indexEnv]) || {}; 111 return (uiState); 112 } 113 114 const searchPageState = getInstantSearchUiState(); 115 //console.log(searchPageState); 116 117 /*const autocompleteSearch = autocomplete({ 118 container: '#autocomplete', 119 getSources() { 120 return [ 121 { 122 sourceId: 'querySuggestions', 123 getItemInputValue: ({ item }) => item.query, 124 getItems({ query }) { 125 return getAlgoliaResults({ 126 searchClient, 127 queries: [ 128 { 129 indexName: 'stage_www_query_suggestions', 130 query, 131 params: { 132 hitsPerPage: 4, 133 }, 134 }, 135 ], 136 }); 137 }, 138 templates: { 139 item({ item, components }) {
140 return components.ReverseHighlight({ hit: item, attribute: 'query' }); 141 }, 142 }, 143 }, 144 ]; 145 }, 146 });*/ 147 148 /*const renderIndexListItem = ({ indexId, hits }) => ` 149 <li> 150 Index: ${indexId} 151 <ol> 152 ${hits 153 .map( 154 hit => 155 `<li>${hit.query}</li>` 156 ) 157 .join('')} 158 </ol> 159 </li> 160 `; 161 const renderAutocomplete = (renderOptions, isFirstRender) => { 162 const { indices, currentRefinement, refine, widgetParams } = renderOptions; 163 164 //widgetParams.container.innerHTML = '...'; 165 //const container = widgetParams.container; 166 const container = document.querySelector('#autocomplete'); 167 if (isFirstRender) { 168 const input = document.createElement('input'); 169 const ul = document.createElement('ul'); 170 171 input.addEventListener('input', event => { 172 refine(event.currentTarget.value); 173 }); 174 175 container.appendChild(input); 176 container.appendChild(ul); 177 } 178 179 container.querySelector('input').value = currentRefinement; 180 container.querySelector('ul').innerHTML = indices 181 .map(renderIndexListItem) 182 .join(''); 183 }; 184 185 const customAutocomplete = connectAutocomplete( 186 renderAutocomplete 187 );*/ 188 189 /*const querySuggestionsPlugin = createQuerySuggestionsPlugin({ 190 searchClient, 191 indexName: 'stage_www_query_suggestions', 192 getSearchParams({ state }) { 193 return { hitsPerPage: state.query ? 5 : 10 }; 194 }, 195 transformSource({ source, onTapAhead }) { 196 console.log(source); 197 return { 198 ...source, 199 templates: { 200 ...source.templates, 201 item(params) { 202 const { item, html } = params; 203 204 return html` 205 <div class="aa-ItemLink"> 206 ${source.templates.item(params).props.children} 207 </div>`; 208 } 209 }, 210 onSelect({ item }) { 211 // Assuming the `setSearchState` function updates the search page state. 212 //setSearchState({ 213 // query: item.query, 214 //}); 215 //console.log(search); 216 //search.helper.setQueryParameter(item.query); 217 //search.helper.search(); 218 search.helper.setQuery(item.query).search() 219 }, 220 }; 221 }, 222 }); 223 224 autocomplete({ 225 container: '#autocomplete', 226 plugins: [querySuggestionsPlugin], 227 openOnFocus: true, 228 });*/ 229 230 231 // Custom Stats Widget 232 const renderStats = (renderOptions, isFirstRender) => { 233 const { page, nbPages, nbHits, widgetParams } = renderOptions; 234 const container = document.querySelector(widgetParams.container); 235 236 let nbHitsCommas = formatNumbersWithCommas(nbHits); 237 let perPage = 25; 238 let pageStart = perPage * (page + 1) - perPage + 1; 239 let pageEnd = perPage * (page + 1); 240 if(pageEnd >= nbHits) { 241 pageEnd = nbHitsCommas; 242 } 243 244 container.innerHTML = ` 245 <div>${pageStart}-${pageEnd} of ${nbHitsCommas} results</div> 246 `; 247 }; 248 249 // Grab all Custom Widgets 250 const customStats = instantsearch.connectors.connectStats(renderStats); 251 252 // URL Routing 253 instantsearch.routers.history({ 254 createURL({ qsModule, location, routeState }) { 255 const { origin, pathname, hash } = location; 256 const indexState = routeState['instant_search'] || {}; 257 const queryString = qsModule.stringify(routeState); 258 259 if (!indexState.query) { 260 return `${origin}${pathname}${hash}`; 261 } 262 263 return `${origin}${pathname}?${queryString}${hash}`; 264 }, 265 }); 266 267 268 269 var search = instantsearch({ 270 indexName: indexEnv, 271 //IE11 requires the colon and stating the var again 272 searchClient: searchClient, 273 routing: { 274 //router: instantsearch.routers.history(), 275 stateMapping: { 276 stateToRoute: function (uiState) { 277 let searchFormInputValue = uiState[indexEnv].query; 278 const regex = /(system.*?\()/gi; 279 let reMatch = (searchFormInputValue) ? searchFormInputValue.match(regex) : null; 280 281 if(reMatch) { 282 reMatch.forEach(m => { 283 const regexNoCaretParentheses = /(?<!\^)\(/gi; 284 mFix = m.replace(regexNoCaretParentheses,'^('); 285 searchFormInputValue = searchFormInputValue.replace(m,mFix); 286 }) 287 } 288 return { 289 q: searchFormInputValue, 290 }; 291 }, 292 routeToState: function (routeState) { 293 return { 294 [indexEnv]: { 295 query: routeState.q, 296 }, 297 }; 298 }, 299 }, 300 } 301 }); 302 303 const dateListPicker = instantsearch.connectors.connectRange( 304 (renderOptions, isFirstRender) => { 305 //const container = document.querySelector('#refinement-list-date'); 306 307 const { start, range, refine, widgetParams } = renderOptions; 308 const [min, max] = start; 309 const container = document.querySelector(widgetParams.container); 310 //const url = "/search?q="+renderOptions.instantSearchInstance.renderState[indexEnv].searchBox.query; 311 312 if (isFirstRender) { 313 const form = document.createElement('form'); 314 container.appendChild(form); 315 316 form.addEventListener('submit', event => { 317 event.preventDefault(); 318 319 const rawMinInputValue = parseInt(minDate.valueAsNumber); 320 const rawMaxInputValue = parseInt(maxDate.valueAsNumber); 321 322 refine([ 323 Number.isFinite(rawMinInputValue) ? rawMinInputValue : undefined, 324 Number.isFinite(rawMaxInputValue) ? rawMaxInputValue : undefined 325 ]); 326 applyDateRangeStylesOnNextLoop(); 327 }); 328 } 329 330 //TODO: Need to finish Custom sorting & show/hide of inputs 331 332 container.querySelector('form').innerHTML = ` 333 <ul> 334 <li><a data-track-section="search-refiner-date" data-track-id="search-date-24-hours" data-track-type="filter" data-track-destination="search-results" tabindex="0" id="hours-24" class="date-item" data-min="${endDate24HoursAgo}"><span class="filter-list-item">Past 24 hours</span></a></li> 335 <li><a data-track-section="search-refiner-date" data-track-id="search-date-past-week" data-track-type="filter" data-track-destination="search-results" tabindex="0" class="date-item" data-min="${endDate1WeekAgo}"><span class="filter-list-item">Past week</span></a></li> 336 <li><a data-track-section="search-refiner-date" data-track-id="search-date-past-month" data-track-type="filter" data-track-destination="search-results" tabindex="0" class="date-item" data-min="${endDate1MonthAgo}"><span class="filter-list-item">Past month</span></a></li> 337 <li><a data-track-section="search-refiner-date" data-track-id="search-date-past-year" data-track-type="filter" data-track-destination="search-results" tabindex="0" class="date-item" data-min="${endDate1YearAgo}"><span class="filter-list-item">Past year</span></a></li> 338 <li><a data-track-section="search-refiner-date" data-track-id="search-date-custom" data-track-type="filter" data-track-destination="search-results" tabindex="0" id="custom-date-label" class="date-item" data-min="${customDateMSFrom}" data-max="${customDateMSTo}"><span class="filter-list-item">Custom range</span></a> 339 <ul id="custom-date-range-items" class="d-none"> 340 <li> 341 <div> 342 <label for="custom-date-input-from" style="padding-right: 10px;">From</label> 343 <input data-track-section="search-refiner-date" data-track-id="search-date-custom-from" data-track-type="filter" data-track-destination="search-results" id="custom-date-input-from" type="date" value="${getFormattedDateForPicker(new Date(customDateMSFrom))}"> 344 </div> 345 <div> 346 <label for="custom-date-input-to" style="padding-right: 10px;">To</label> 347 <input data-track-section="search-refiner-date" data-track-id="search-date-custom-to" data-track-type="filter" data-track-destination="search-results" id="custom-date-input-to" type="date" value="${getFormattedDateForPicker(new Date(customDateMSTo))}"> 348 </div> 349 <button data-track-section="search-refiner-date" data-track-id="search-date-update-date" data-track-type="form-submit" data-track-destination="search-results" class="btn btn-sm btn-secondary" id="custom-date-submit" type="submit">Update date</button> 350 </li> 351 </ul> 352 </li> 353 354 </ul> 355 <input class="d-none" id="minDate" 356 name="min" 357 type="number" 358 value="${Number.isFinite(min) ? min : ''}" 359 /> 360 <input class="d-none" id="maxDate" 361 name="max" 362 type="number" 363 value="${Number.isFinite(max) ? max : ''}" 364 /> 365 366 `; 367 //let timer; 368 const applyDateRangeStylesOnNextLoop = () => { 369 //clearTimeout(timer); 370 //timer = setTimeout(() => { 371 const customDateInputElementFrom = container.querySelector("#custom-date-range-items"); 372 container.querySelector("#custom-date-label").classList.add("active"); 373 customDateInputElementFrom.classList.remove("d-none"); 374 //container.querySelector("#custom-date-submit").classList.remove("d-none"); 375 //container.querySelector('form').requestSubmit(); 376 //}, 1000); 377 } 378 379
380 [...container.querySelectorAll('a.date-item')].forEach(element => { 381 if(parseInt(element.dataset.min) == parseInt(minDate.value)) { 382 element.classList.add("active"); 383 if (element.id === "custom-date-label") { 384 const customDateInputElements = container.querySelector("#custom-date-range-items"); 385 customDateInputElements.classList.remove("d-none"); 386 container.querySelector("#custom-date-input-from").focus(); 387 //container.querySelector("#custom-date-submit").classList.remove("d-none"); 388 } 389 } else { 390 element.classList.remove("active"); 391 if (element.id === "custom-date-label") { 392 const customDateInputElements = container.querySelector("#custom-date-range-items"); 393 customDateInputElements.classList.add("d-none"); 394 //container.querySelector("#custom-date-submit").classList.add("d-none"); 395 } 396 } 397 398 element.addEventListener('click', event => { 399 event.preventDefault(); 400 401 if (event.currentTarget.classList.contains('active')) { 402 minDate.value = ''; 403 } else { 404 minDate.value = parseInt(event.currentTarget.dataset.min); 405 } 406 407 maxDate.value = ''; 408 409 container.querySelector('form').requestSubmit(); 410 }); 411 }); 412 413 container.querySelector("#custom-date-input-from").addEventListener("input", event => { 414 event.preventDefault(); 415 416 const selectedDateMS = event.currentTarget.value; 417 customDateMSFrom = new Date(selectedDateMS).getTime() + (new Date(selectedDateMS).getTimezoneOffset() * 60 * 1000); 418 minDate.value = customDateMSFrom; 419 420 //container.querySelector('form').requestSubmit(); 421 //applyDateRangeStylesOnNextLoop(); 422 }); 423 424 container.querySelector("#custom-date-input-to").addEventListener("change", event => { 425 event.preventDefault(); 426 427 const selectedDateMS = new Date(event.currentTarget.value).getTime(); 428 customDateMSTo = new Date(selectedDateMS).getTime() + (new Date(selectedDateMS).getTimezoneOffset() * 60 * 1000); 429 maxDate.value = selectedDateMS; 430 431 //container.querySelector('form').requestSubmit(); 432 //applyDateRangeStylesOnNextLoop(); 433 }); 434 }); 435 436 437 let timerId; 438 const d = new Date(); 439 const postDate = Math.floor(d.setDate(d.getDate())); 440 // Archive filter 441 /*const selectArchiveEl = document.querySelector("#search-filter-archived"); 442 let archiveFilter = 'AND archived:false'; 443 444 selectArchiveEl.addEventListener("change", (event) => { 445 if(event.target.value == 'all') { 446 archiveFilter = ''; 447 } else if(event.target.value == 'archiveOnly') { 448 archiveFilter = 'AND archived:true'; 449 } else { 450 archiveFilter = 'AND archived:false'; 451 } 452 console.log('Query: ',searchPageState.q, `post_date_timestamp_utc < ${postDate} ${archiveFilter}`); 453 //setInstantSearchUiState({ filters: `post_date_timestamp_utc < ${postDate} ${archiveFilter}` }); 454 console.log(search); 455 //search.addWidgets([ 456 // instantsearch.widgets.configure({ 457 // filters: `post_date_timestamp_utc < ${postDate} ${archiveFilter}`, 458 // }) 459 // ]); 460 461 });*/ 462 463 // Mount a virtual search box to manipulate InstantSearch's `query` UI 464 // state parameter. 465 const virtualSearchBox = connectSearchBox(() => {}); 466 467 let hitEl = document.getElementById('hits'); 468 let proEl = document.createElement("div"); 469 proEl.id = "proHit"; 470 hitEl.parentNode.insertBefore(proEl, hitEl); 471 472 search.addWidgets([ 473 virtualSearchBox({}), 474 instantsearch.widgets.refinementList({ 475 container: '#refinement-list-type', 476 attribute: 'doc_type', 477 sortBy: ['count:desc', 'name:asc'], 478 templates: { 479 item(item, { html }) { 480 const { url, label, count, isRefined } = item; 481 return html` 482 <a data-track-id="search-doctype-${label.toLowerCase().replaceAll(' ','-')}" data-track-type="filter" data-track-destination="search-results" href="${url}" class="${isRefined ? 'active' : ''}"> 483 <span class="filter-list-item">${label}</span> 484 <span class="filter-list-amount">${count}</span> 485 </a>`; 486 }, 487 } 488 }), 489 instantsearch.widgets.refinementList({ 490 container: '#refinement-list-sub-index', 491 attribute: 'sub_index', 492 sortBy: ['count:desc', 'name:asc'], 493 templates: { 494 item(item, { html }) { 495 const { url, label, count, isRefined } = item; 496 let relabel = ''; 497 if(label === 'CalendarItem') { 498 relabel = 'Event'; 499 } else { 500 relabel = label; 501 } 502 return html` 503 <a data-track-id="search-type-${relabel.toLowerCase().replaceAll(' ','-')}" data-track-type="filter" data-track-destination="search-results" href="${url}" class="${isRefined ? 'active' : ''}"> 504 <span class="filter-list-item">${relabel}</span> 505 <span class="filter-list-amount">${count}</span> 506 </a> 507 `; 508 }, 509 } 510 /*templates: { 511 item: `<a href="{{url}}" class="{{#isRefined}}active{{/isRefined}}"> 512 <span class="filter-list-item">{{label}}</span> 513 <span class="filter-list-amount">{{count}}</span> 514 </a>`, 515 }*/ 516 }), 517 instantsearch.widgets.hierarchicalMenu({ 518 container: '#refinement-list-folders', 519 attributes: [ 520 'folders.lvl0', 521 'folders.lvl1', 522 'folders.lvl2', 523 'folders.lvl3', 524 'folders.lvl4', 525 ], 526 templates: { 527 item(item, { html }) { 528 const { url, label, count, isRefined } = item; 529 return html` 530 <a data-track-id="search-folder-${label.toLowerCase().replaceAll(' ','-')}" data-track-type="filter" data-track-destination="search-results" href="${url}" class="${isRefined ? 'active' : ''}"> 531 <span class="filter-list-item">${label}</span> 532 <span class="filter-list-amount">${count}</span> 533 </a>`; 534 }, 535 } 536 }), 537 instantsearch.widgets.refinementList({ 538 container: '#refinement-list-format', 539 attribute: 'format', 540 sortBy: ['count:desc', 'name:asc'], 541 templates: { 542 item(item, { html }) { 543 const { url, label, count, isRefined } = item; 544 return html` 545 <a data-track-id="search-format-${label.toLowerCase().replaceAll(' ','-')}" data-track-type="filter" data-track-destination="search-results" href="${url}" class="${isRefined ? 'active' : ''}"> 546 <span class="filter-list-item">${label}</span> 547 <span class="filter-list-amount">${count}</span> 548 </a>`; 549 }, 550 } 551 }), 552 instantsearch.widgets.refinementList({ 553 container: '#refinement-list-archived', 554 attribute: 'archived', 555 sortBy: ['count:desc', 'name:asc'], 556 /*templates: { 557 item: `<a href="{{url}}" class="{{#isRefined}}active{{/isRefined}}"> 558 <span class="filter-list-item">{{label}}</span> 559 <span class="filter-list-amount">{{count}}</span> 560 </a>`, 561 }*/ 562 templates: { 563 item(item, { html }) { 564 const { url, label, count, isRefined } = item; 565 let relabel = ''; 566 if(label === 'false') { 567 relabel = 'Not archived'; 568 } else { 569 relabel = 'Archived'; 570 } 571 return html` 572 <a data-track-id="search-archived-${relabel.toLowerCase().replaceAll(' ','-')}" data-track-type="filter" data-track-destination="search-results" href="${url}" class="${isRefined ? 'active' : ''}"> 573 <span class="filter-list-item">${relabel}</span> 574 <span class="filter-list-amount">${count}</span> 575 </a> 576 `; 577 }, 578 }, 579 transformItems(items) { 580 const facetFilters = 581 (search.helper.lastResults.explain && 582 search.helper.lastResults.explain.params.rules.facetFilters) || 583 []; 584 return items.map((item) => ({ 585 ...item, 586 isRefined: 587 item.isRefined || 588 facetFilters.includes(`archived:${item.value}`), 589 })); 590 }, 591 }), 592 593 instantsearch.widgets.configure({ 594 //filters: 'NOT format:pdf', 595 //filters: `post_date_timestamp_utc <= ${dateNow.getTime()}`, 596 explain: true, 597 //filters: `post_date_timestamp_utc < ${postDate}`, 598 //filters: "archived: false", 599 hitsPerPage: 25, 600 attributesToSnippet: [ 601 'content:50', 602 'description:50' 603 ], 604 snippetEllipsisText: '…', 605 attributesToHighlight: [ 606 'title',
607 'filename', 608 'content', 609 'description' 610 ], 611 highlightPreTag: '<em class="search-highlight">', 612 highlightPostTag: '</em>', 613 disjunctiveFacetsRefinements: { 614 archived: ['false'], 615 }, 616 }), 617 618 619 instantsearch.widgets.queryRuleCustomData({ 620 container: '#proHit', 621 templates: { 622 default: ({ items }, { html }) => { 623 let output = ''; 624 items.map((item) => { 625 //console.log(item.title) 626 let icon = ''; 627 if(item.section_type == "topics") { 628 icon = html`<div class="d-inline-block align-text-bottom me-1" style="max-width: 24px;"> 629 <svg data-name="ico-archive" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="var(--blue-800)" width="24" height="24"> 630 <path d="m20.97,9.34l-2-6c-.07-.2-.26-.34-.47-.34h-8c-.09,0-.18.03-.25.08-.07-.04-.16-.08-.25-.08h-4.5c-.21,0-.41.14-.47.34l-2,6s0,.05-.01.08c0,.03-.02.05-.02.08v11c0,.28.22.5.5.5h17c.28,0,.5-.22.5-.5v-11s-.01-.05-.02-.08c0-.03,0-.05-.01-.08ZM5.86,4h4.14c.09,0,.18-.03.25-.08.07.04.16.08.25.08h7.64l.33,1H5.53l.33-1Zm-.67,2h13.61l.33,1H4.86l.33-1Zm-.67,2h14.95l.33,1H4.19l.33-1Zm15.47,12H4v-10h16v10Z"/> 631 <path d="m9,15h6c.58,0,1-.42,1-1v-1c0-.58-.42-1-1-1h-6c-.58,0-1,.42-1,1v1c0,.58.42,1,1,1Zm0-2h6l.03,1s0,0-.03,0h-6v-1Z"/> 632 </svg> 633 </div>`; 634 } 635 let trackId = 'document'; 636 if(item.cms_Type == 'page' ) { 637 if(item.entry_type == 'notice') { 638 trackId = 'notice'; 639 } else if(item.section_type == "library") { 640 trackId = 'library'; 641 } else if(item.section_type == "topics") { 642 trackId = 'folder'; 643 } else if(item.entry_type == "page_post_newsRelease") { 644 trackId = 'news-release'; 645 } else if(item.entry_type == "page_post_story" || item.entry_type == "page_post_update") { 646 trackId = 'blog'; 647 } else if(item.section_type == "calendarTopics") { 648 trackId = 'calendar-topic'; 649 } else { 650 trackId = 'page'; 651 } 652 } 653 654 output = html`<article class="search-result search-result-featured display-type-asset"> 655 <div class="featured-wrapper p-2"> 656 <div class="featured-title">Featured result</div> 657 <div class="d-flex"> 658 ${icon} 659 <div> 660 <div class="search-result-title"> 661 <a data-track-id="${ item.doc_type ? item.doc_type.join('-').toLowerCase().replaceAll(' ','-') : trackId}" href="${item.url}">${item.title}</a> 662 </div> 663 <div class="search-result-info"> 664 <div> 665 ${item.result_type ? `<div class="search-result-type tag align-baseline">${item.result_type}</div>` : ``} 666 </div> 667 <div class="search-result-summary">${item.description}</div> 668 </div> 669 </div> 670 </div> 671 </div> 672 </article>`; 673 //return html`<p>${item.title}</p>` 674 675 }) 676 return output; 677 }, 678 }, 679 }), 680 681 682 /*instantsearch.widgets 683 .index({ indexName: 'stage_www_query_suggestions' }) 684 .addWidgets([ 685 customAutocomplete({ 686 container: '#autocomplete', 687 onSelectChange({ query }) { 688 console.log(query); 689 search.helper.setQuery(query).search(); 690 }, 691 }), 692 ]),*/ 693 694 //instantsearch.widgets.virtualSearchBox(), 695 696 /*instantsearch.widgets.searchBox({ 697 container: '#searchbox', 698 autofocus: true, 699 searchAsYouType: false, 700 showReset: false, 701 showSubmit: true, 702 showLoadingIndicator: true, 703 placeholder: 'Search for webpages, documents, etc.', 704 queryHook(query, refine) { 705 clearTimeout(timerId); 706 timerId = setTimeout(() => refine(query), 500); 707 }, 708 cssClasses: { 709 form: 'input-group input-group-search-dropdown', 710 input: 'search-input', 711 submit: 'btn btn-search' 712 }, 713 }),*/ 714 715 instantsearch.widgets.sortBy({ 716 container: '#sortBy', 717 items: [ 718 { label: 'Relevance', value: indexEnv }, 719 { label: 'Newest (date posted)', value: indexEnv +'_postdate_desc' }, 720 { label: 'Oldest (date posted)', value: indexEnv +'_postdate_asc' }, 721 ],
722 cssClasses: { 723 select: [ 724 'form-select', 725 'form-select-xs' 726 ] 727 } 728 }), 729 730 customStats({ 731 container: '#resultsAmount', 732 }), 733 734 dateListPicker({ 735 attribute: 'post_date_timestamp_utc', 736 container: '#refinement-list-date', 737 }), 738 739 740 instantsearch.widgets.pagination({ 741 container: '#pagination', 742 showFirst: false, 743 showLast: false, 744 templates: { 745 previous: 'Previous', 746 next: 'Next', 747 }, 748 cssClasses: { 749 list: 'pagination pagination-sm', 750 item: 'page-item', 751 link: 'page-link', 752 selectedItem: 'active', 753 disabledItem: 'disabled' 754 }, 755 }), 756 757 instantsearch.widgets.hits({ 758 container: '#hits', 759 transformItems: items => { 760 const dateOptions = { 761 year: "numeric", month: "numeric", day: "numeric", 762 hour: '2-digit', minute: '2-digit', timeZone: 'America/Los_Angeles' 763 } 764 for (let i = 0; i < items.length; i++) { 765 items[i].filesize = items[i].filesize ? formatBytes(items[i].filesize) : undefined; 766 //items[i].post_date = items[i].post_date ? new Date(items[i].post_date_timestamp).toLocaleString('en-US', dateOptions) + " PT" : undefined; 767 items[i].post_date_pst = items[i].post_date_pst ? items[i].post_date_pst +' PT' : undefined; 768 //items[i].date_modified = items[i].date_modified ? new Date(items[i].date_modified_timestamp).toLocaleString('en-US', dateOptions) + " PT" : undefined; 769 items[i].date_modified_pst = items[i].date_modified_pst ? items[i].date_modified_pst + " PT" : undefined; 770 } 771 return items; 772 }, 773 templates: { 774 header: "Title", 775 item: function(hit) { 776 const descOutput = hit.description ? hit.description : hit.content; 777 const descAttr = hit.description ? 'description' : 'content'; 778 const startDateArr = hit.start_datetime_pst ? hit.start_datetime_pst.split(' ') : undefined; 779 const endTime = hit.end_datetime_pst ? hit.end_datetime_pst.split(' ')[1]+" "+hit.end_datetime_pst.split(' ')[2] : undefined; 780 781 if(hit.cms_Type == 'calendar' || hit.cms_Type == 'calender') { 782 return ` 783 <article class="search-result display-type-${ hit.cms_Type }"> 784 <div> 785 <div class="search-result-title"> 786 <a data-track-id="event" href="${ hit.url }" class="d-flex"> 787 <div class="d-inline-block align-text-bottom me-1" style="max-width: 24px;"> 788 <svg data-name="ico-calendar" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="var(--blue-800)" width="24" height="24"> 789 <path d="m21.5,3h-3.5v-.5c0-.28-.22-.5-.5-.5h-1c-.28,0-.5.22-.5.5v.5h-8v-.5c0-.28-.22-.5-.5-.5h-1c-.28,0-.5.22-.5.5v.5h-3.5c-.28,0-.5.22-.5.5v17c0,.28.22.5.5.5h19c.28,0,.5-.22.5-.5V3.5c0-.28-.22-.5-.5-.5Zm-13.5,1h8v1h-8v-1Zm-5,0h3v1h-3v-1Zm18,16H3V6h18v14Zm0-15h-3v-1h3v1Z"/> 790 <rect x="4.5" y="8" width="1" height="1"/> 791 <rect x="8" y="8" width="1" height="1"/> 792 <rect x="11.5" y="8" width="1" height="1"/> 793 <rect x="15" y="8" width="1" height="1"/> 794 <rect x="18.5" y="8" width="1" height="1"/> 795 <rect x="4.5" y="11" width="1" height="1"/> 796 <rect x="8" y="11" width="1" height="1"/> 797 <rect x="11.5" y="11" width="1" height="1"/> 798 <rect x="15" y="11" width="1" height="1"/> 799 <rect x="18.5" y="11" width="1" height="1"/> 800 <rect x="4.5" y="14" width="1" height="1"/> 801 <rect x="8" y="14" width="1" height="1"/> 802 <rect x="11.5" y="14" width="1" height="1"/> 803 <rect x="15" y="14" width="1" height="1"/> 804 <rect x="18.5" y="14" width="1" height="1"/> 805 <rect x="4.5" y="17" width="1" height="1"/> 806 <rect x="8" y="17" width="1" height="1"/> 807 <rect x="11.5" y="17" width="1" height="1"/> 808 <rect x="15" y="17" width="1" height="1"/> 809 <rect x="18.5" y="17" width="1" height="1"/> 810 </svg> 811 </div> 812 <div> 813 ${instantsearch.highlight({ attribute: 'title', hit })} 814 </div> 815 </a> 816 </div> 817 <div class="search-result-start-date"> 818 <div class="d-inline-block search-result-start-date-date">${startDateArr[0]}</div> 819 <div class="d-inline-block search-result-start-date-time">${startDateArr[1]+" "+startDateArr[2]} - ${endTime}</div> 820 </div> 821 <div class="search-result-info"> 822 <!--${hit.post_date_pst ? `<div class="search-result-date-posted">Posted: ${hit.post_date_pst}</div>` : ''}--> 823 ${descOutput ? `<div class="search-result-summary">${instantsearch.snippet({ "attribute": descAttr, hit })}</div>` : ''} 824 </div> 825 </div> 826 </article> 827 `; 828 } else if(hit.section_type == 'terms') { 829 return ` 830 <article class="search-result display-type-${ hit.cms_Type }-${ hit.section_type} d-flex"> 831 <div class="d-inline-block align-text-bottom me-1" style="max-width: 24px;"> 832 <svg data-name="ico-glossary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="var(--blue-800)" width="24" height="24"> 833 <path d="M21.5,6h-1.5v-1.5c0-.28-.22-.5-.5-.5h-3.5c-2.31,0-3.45.82-4,1.5-.55-.68-1.69-1.5-4-1.5h-3.5c-.28,0-.5.22-.5.5v1.5h-1.5c-.28,0-.5.22-.5.5v14c0,.28.22.5.5.5h19c.28,0,.5-.22.5-.5V6.5c0-.28-.22-.5-.5-.5ZM16,5h3v13h-3c-1.76,0-2.84.48-3.5,1V6.58c.1-.26.72-1.58,3.5-1.58ZM5,5h3c2.77,0,3.4,1.31,3.5,1.58v12.43c-.66-.53-1.74-1-3.5-1h-3V5ZM3,7h1v11.5c0,.28.22.5.5.5h3.5c1.78,0,2.67.54,3.11,1H3V7ZM21,20h-8.12c.44-.46,1.33-1,3.12-1h3.5c.28,0,.5-.22.5-.5V7h1v13Z"/> 834 </svg> 835 </div> 836 <div> 837 <div class="tag">Glossary</div> 838 <div class="search-result-title"> 839 <a data-track-id="term" href="${ hit.url }">${instantsearch.highlight({ attribute: 'title', hit })}</a> 840 ${hit.filesize ? `<small class="search-result-size">${hit.filesize}</small>` : ''} 841 </div> 842 <div class="search-result-info"> 843 ${descOutput ? `<div class="search-result-summary">${instantsearch.snippet({ "attribute": descAttr, hit })}</div>` : ''} 844 </div> 845 </div> 846 </article> 847 `; 848 } else { 849 let folders = ''; 850 if(hit.related_folders) {
851 hit.related_folders.forEach(e => { 852 folders+=`<li><a href="${e.url}" data-track-id="folder">${e.title}</a></li>`; 853 }); 854 } 855 let icon = ''; 856 let trackId = 'document'; 857 if(hit.cms_Type == 'page' ) { 858 if(hit.entry_type == 'notice') { 859 icon = ` 860 <div class="d-inline-block align-text-bottom me-1" style="max-width: 24px;"> 861 <svg data-name="ico-announcement" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="var(--blue-800)" width="24" height="24"> 862 <path d="m5,16.95v3.55c0,.28.22.5.5.5h3c.28,0,.5-.22.5-.5v-3.5h.4l6.91,2.96c.06.03.13.04.2.04.1,0,.19-.03.28-.08.14-.09.22-.25.22-.42V4.5c0-.17-.08-.33-.22-.42-.14-.09-.32-.11-.47-.04l-6.91,2.96h-3.9c-1.93,0-3.5,1.57-3.5,3.5v3c0,1.76,1.31,3.2,3,3.45Zm5-9.12l6-2.57v13.48l-6-2.57V7.83Zm-2,12.17h-2v-3h2v3ZM3,10.5c0-1.38,1.12-2.5,2.5-2.5h3.5v8h-3.5c-1.38,0-2.5-1.12-2.5-2.5v-3Z"/> 863 <path d="m21.5,11.5h-3c-.28,0-.5.22-.5.5s.22.5.5.5h3c.28,0,.5-.22.5-.5s-.22-.5-.5-.5Z"/> 864 <path d="m20.15,5.15l-2,2c-.2.2-.2.51,0,.71.1.1.23.15.35.15s.26-.05.35-.15l2-2c.2-.2.2-.51,0-.71s-.51-.2-.71,0Z"/> 865 <path d="m20.5,19c.13,0,.26-.05.35-.15.2-.2.2-.51,0-.71l-2-2c-.2-.2-.51-.2-.71,0s-.2.51,0,.71l2,2c.1.1.23.15.35.15Z"/> 866 </svg> 867 </div>`; 868 trackId = 'notice'; 869 } else if(hit.section_type == "library") { 870 icon = `<div class="d-inline-block align-text-bottom me-1" style="max-width: 24px;"> 871 <svg data-name="ico-archive" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="var(--blue-800)" width="24" height="24"> 872 <path d="m20.97,9.34l-2-6c-.07-.2-.26-.34-.47-.34h-8c-.09,0-.18.03-.25.08-.07-.04-.16-.08-.25-.08h-4.5c-.21,0-.41.14-.47.34l-2,6s0,.05-.01.08c0,.03-.02.05-.02.08v11c0,.28.22.5.5.5h17c.28,0,.5-.22.5-.5v-11s-.01-.05-.02-.08c0-.03,0-.05-.01-.08ZM5.86,4h4.14c.09,0,.18-.03.25-.08.07.04.16.08.25.08h7.64l.33,1H5.53l.33-1Zm-.67,2h13.61l.33,1H4.86l.33-1Zm-.67,2h14.95l.33,1H4.19l.33-1Zm15.47,12H4v-10h16v10Z"/> 873 <path d="m9,15h6c.58,0,1-.42,1-1v-1c0-.58-.42-1-1-1h-6c-.58,0-1,.42-1,1v1c0,.58.42,1,1,1Zm0-2h6l.03,1s0,0-.03,0h-6v-1Z"/> 874 </svg> 875 </div>`; 876 trackId = 'library'; 877 } else if(hit.entry_type == "page_post_newsRelease") { 878 icon = `<div class="d-inline-block align-text-bottom me-1" style="max-width: 24px;"> 879 <svg data-name="ico-news" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="var(--blue-800)" width="24" height="24"> 880 <path d="m14.5,10h4c.28,0,.5-.22.5-.5s-.22-.5-.5-.5h-4c-.28,0-.5.22-.5.5s.22.5.5.5Z"/> 881 <path d="m14.5,8h4c.28,0,.5-.22.5-.5s-.22-.5-.5-.5h-4c-.28,0-.5.22-.5.5s.22.5.5.5Z"/> 882 <path d="m14.5,12h4c.28,0,.5-.22.5-.5s-.22-.5-.5-.5h-4c-.28,0-.5.22-.5.5s.22.5.5.5Z"/> 883 <path d="m8.5,14h10c.28,0,.5-.22.5-.5s-.22-.5-.5-.5h-10c-.28,0-.5.22-.5.5s.22.5.5.5Z"/> 884 <path d="m8.5,16h10c.28,0,.5-.22.5-.5s-.22-.5-.5-.5h-10c-.28,0-.5.22-.5.5s.22.5.5.5Z"/> 885 <path d="m8.5,18h10c.28,0,.5-.22.5-.5s-.22-.5-.5-.5h-10c-.28,0-.5.22-.5.5s.22.5.5.5Z"/> 886 <path d="m8.5,12h4c.28,0,.5-.22.5-.5v-4c0-.28-.22-.5-.5-.5h-4c-.28,0-.5.22-.5.5v4c0,.28.22.5.5.5Zm.5-4h3v3h-3v-3Z"/> 887 <path d="m20.5,4H6.5c-.28,0-.5.22-.5.5v1.5h-2.5c-.28,0-.5.22-.5.5v11.5c0,1.1.9,2,2,2,.11,0,.22-.02.33-.03.05.02.11.03.17.03h13.5c1.1,0,2-.9,2-2V4.5c0-.28-.22-.5-.5-.5Zm-14.5,14c0,.55-.45,1-1,1s-1-.45-1-1V7h2v11Zm14,0c0,.55-.45,1-1,1H6.72c.17-.3.28-.63.28-1V5h13v13Z"/> 888 </svg> 889 </div>`; 890 trackId = 'news-release'; 891 } else if(hit.entry_type == "page_post_story" || hit.entry_type == "page_post_update") { 892 icon = `<div class="d-inline-block align-text-bottom me-1" style="max-width: 24px;"> 893 <svg data-name="ico-energy-matters" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="var(--blue-800)" width="24" height="24"> 894 <path d="M20.88,12.85s-.88-1.1-.88-3.35c0-.28-.22-.5-.5-.5.28,0,.5-.22.5-.5s-.22-.5-.5-.5h-1c.28,0,.5-.22.5-.5s-.22-.5-.5-.5h-1v-3h4c.28,0,.5-.22.5-.5s-.22-.5-.5-.5H2.5c-.28,0-.5.22-.5.5s.22.5.5.5h4v2h-.5c-.28,0-.5.22-.5.5s.22.5.5.5h-.5c-.28,0-.5.22-.5.5s.22.5.5.5c-.28,0-.5.22-.5.5,0,.65-.62,1.45-.86,1.7-.74.75-1.14,1.75-1.14,2.8,0,2.21,1.79,4,4,4s4-1.79,4-4c0-1.07-.41-2.07-1.17-2.82-.24-.25-.83-1.03-.83-1.68,0-.28-.22-.5-.5-.5.28,0,.5-.22.5-.5s-.22-.5-.5-.5h-.5c.28,0,.5-.22.5-.5s-.22-.5-.5-.5h-.5v-2h9v3h-1c-.28,0-.5.22-.5.5s.22.5.5.5h-1c-.28,0-.5.22-.5.5s.22.5.5.5c-.28,0-.5.22-.5.5,0,2.26-.88,3.34-.89,3.36-.72.88-1.11,2-1.11,3.14,0,2.76,2.24,5,5,5s5-2.24,5-5c0-1.14-.39-2.26-1.11-3.14,0,0,0,0,0,0ZM9.11,10.87h0c.57.58.88,1.33.88,2.13,0,1.65-1.35,3-3,3s-3-1.35-3-3c0-.79.3-1.54.86-2.1.1-.1.86-.93,1.08-1.9h.56v3h-1c-.28,0-.5.22-.5.5s.22.5.5.5h3c.28,0,.5-.22.5-.5s-.22-.5-.5-.5h-1v-3h.56c.21.96.96,1.77,1.05,1.87ZM17,20c-2.21,0-4-1.79-4-4,0-.93.31-1.79.88-2.5.04-.05.99-1.18,1.11-3.5h1.51v5h-1.5c-.28,0-.5.22-.5.5s.22.5.5.5h4c.28,0,.5-.22.5-.5s-.22-.5-.5-.5h-1.5v-5h1.51c.12,2.24,1.01,3.38,1.1,3.49.58.72.89,1.59.89,2.51,
8940,2.21-1.79,4-4,4Z"/> 895 </svg> 896 </div>`; 897 trackId = 'blog'; 898 } else if(hit.section_type == "calendarTopics") { 899 icon = `<div class="d-inline-block align-text-bottom me-1" style="max-width: 24px;"> 900 <svg data-name="ico-calendar-topic" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="var(--blue-800)" width="24" height="24"> 901 <path d="M21.5,3h-3.5v-.5c0-.28-.22-.5-.5-.5h-1c-.28,0-.5.22-.5.5v.5h-8v-.5c0-.28-.22-.5-.5-.5h-1c-.28,0-.5.22-.5.5v.5h-3.5c-.28,0-.5.22-.5.5v17c0,.28.22.5.5.5h19c.28,0,.5-.22.5-.5V3.5c0-.28-.22-.5-.5-.5ZM16,5h-8v-1h8v1ZM3,4h3v1h-3v-1ZM21,20H3V6h18v14ZM18,5v-1h3v1h-3Z"/> 902 <path d="M15.5,7c-1.38,0-2.5,1.12-2.5,2.5s1.12,2.5,2.5,2.5,2.5-1.12,2.5-2.5-1.12-2.5-2.5-2.5ZM15.5,11c-.83,0-1.5-.67-1.5-1.5s.67-1.5,1.5-1.5,1.5.67,1.5,1.5-.67,1.5-1.5,1.5Z"/> 903 <path d="M15.5,14c-1.38,0-2.5,1.12-2.5,2.5s1.12,2.5,2.5,2.5,2.5-1.12,2.5-2.5-1.12-2.5-2.5-2.5ZM15.5,18c-.83,0-1.5-.67-1.5-1.5s.67-1.5,1.5-1.5,1.5.67,1.5,1.5-.67,1.5-1.5,1.5Z"/> 904 <path d="M8.5,7c-1.38,0-2.5,1.12-2.5,2.5s1.12,2.5,2.5,2.5,2.5-1.12,2.5-2.5-1.12-2.5-2.5-2.5ZM8.5,11c-.83,0-1.5-.67-1.5-1.5s.67-1.5,1.5-1.5,1.5.67,1.5,1.5-.67,1.5-1.5,1.5Z"/> 905 <path d="M8.5,14c-1.38,0-2.5,1.12-2.5,2.5s1.12,2.5,2.5,2.5,2.5-1.12,2.5-2.5-1.12-2.5-2.5-2.5ZM8.5,18c-.83,0-1.5-.67-1.5-1.5s.67-1.5,1.5-1.5,1.5.67,1.5,1.5-.67,1.5-1.5,1.5Z"/> 906 </svg> 907 </div>`; 908 trackId = 'calendar-topic'; 909 } else { 910 icon = ` 911 <div class="d-inline-block align-text-bottom me-1" style="max-width: 24px;"> 912 <svg data-name="ico-web-page" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="var(--blue-800)" width="24" height="24"> 913 <path d="M21.5,4H2.5c-.28,0-.5.22-.5.5v15c0,.28.22.5.5.5h19c.28,0,.5-.22.5-.5V4.5c0-.28-.22-.5-.5-.5ZM3,19V7h4v12H3ZM21,19h-13v-5.97h13v5.97ZM21,12.03h-13v-5.03h13v5.03ZM20,6v-1h1v1h-1Z"/> 914 </svg> 915 </div>`; 916 trackId = 'page'; 917 } 918 } 919 let newWindow = false; 920 if (hit.url.indexOf('https://stakeholdercenter.caiso.com/') == 0) { 921 newWindow = true; 922 } 923 return ` 924 <article class="search-result display-type-${ hit.cms_Type } d-flex"> 925 <div> 926 <div class="search-result-title"> 927 <a data-track-id="${ hit.doc_type ? hit.doc_type.join('-').toLowerCase().replaceAll(' ','-') : trackId}" href="${ hit.url }" class="d-inline" ${newWindow ? 'target="_blank"' : ''}> 928 ${icon} 929 <div class="d-inline"> 930 ${hit.entry_type == "page_post_newsRelease" ? 'News release | ': ''} 931 ${hit.entry_type == "page_post_story" || hit.entry_type == "page_post_update" ? 'Blog | ': ''} 932 ${hit.entry_type == "notice" ? 'Notice | ': ''} 933 ${hit.section_type == "calendarTopics" ? 'Calendar topic | ': ''} 934 ${instantsearch.highlight({ attribute: 'title', hit })} 935 </div> 936 </a> 937 ${hit.filesize ? `<small class="search-result-size">${hit.filesize}</small>` : ''} 938 </div> 939 <div class="search-result-info"> 940 ${hit.filename ? `<div class="search-result-filename">${instantsearch.highlight({ "attribute": 'filename', hit })}</div>` : ''} 941 <div> 942 ${hit.doc_type ? `<div class="search-result-type tag align-baseline">${hit.doc_type}</div>` : ''} 943 ${((hit.cms_Type == 'page' && hit.post_date_pst) && (hit.entry_type == 'notice' || hit.entry_type == 'page_post_newsRelease' || hit.entry_type == 'page_post_story' || hit.entry_type == 'page_post_update')) 944 ? `<div class="search-result-date-posted align-baseline">Posted: ${hit.post_date_pst}</div>` 945 : ''} 946 ${((hit.cms_Type !== 'page' && hit.post_date_pst)) ? `<div class="search-result-date-posted align-baseline">Posted: ${hit.post_date_pst}</div>` : ''} 947 ${(hit.sourceSite == "Stakeholder") ? `<div class="search-result-stakeholder-type tag align-baseline">
947${hit.status ? `<span class="search-result-status">${hit.status} </span>` : ``}${ (hit.stakeholder_Type == "Initiative") ? "Initiative" : "Recurring process"}</div>` : ''} 948 ${(hit.sourceSite == "Stakeholder" && hit.start_datetime_pst) ? `<div class="search-result-date-posted align-baseline">Started: ${hit.start_datetime_pst}</div>` : ''} 949 </div> 950 ${descOutput ? `<div class="search-result-summary">${instantsearch.snippet({ "attribute": descAttr, hit })}</div>` : ''} 951 ${folders ? `<ul class="search-result-folders">${folders}</ul>` : ''} 952 </div> 953 </div> 954 </article> 955 `; 956 } 957 }, 958 empty: function(noHit) { 959 const qDisplay = noHit.query.replaceAll("^(","("); 960 return `<div class="p-5 bg-light text-center"> 961 <p>No results have been found for "${ qDisplay }"</p> 962 </div>` 963 } 964 } 965 }), 966 967 instantsearch.widgets.currentRefinements({ 968 container: '#currentRefinements', 969 transformItems: items => { 970 for (let i = 0; i < items.length; i++) { 971 if(items[i].attribute == "post_date_timestamp_utc") { 972 973 //add chip refinement labels e.g. Date: >= 4/23/23 974 if (items[i].refinements.length >= 2) { 975 items[i].label = "Date"; 976 items[i].refinements[0].label = ` From ` + new Date(items[i].refinements[0].value).toLocaleString('en-US', { timeZone: 'America/Los_Angeles' }).split(",")[0]; 977 items[i].refinements[1].label = ` To ` + new Date(items[i].refinements[1].value).toLocaleString('en-US', { timeZone: 'America/Los_Angeles' }).split(",")[0]; 978 } else { 979 items[i].label = "Date"; 980 items[i].refinements[0].label = ` >= ` + new Date(items[i].refinements[0].value).toLocaleString('en-US', { timeZone: 'America/Los_Angeles' }).split(",")[0]; 981 } 982 983 } 984 if(items[i].attribute == "sub_index") { 985 items[i].label = "Type"; 986 for (let j = 0; j < items[i].refinements.length; j++) { 987 if(items[i].refinements[j].value == "CalendarItem") { 988 items[i].refinements[j].label = "Event"; 989 } 990 } 991 } 992 if(items[i].attribute == "doc_type") { 993 items[i].label = "Document type"; 994 } 995 if(items[i].attribute == "folders.lvl0") { 996 items[i].label = "Topic"; 997 } 998 if(items[i].attribute == "archived") { 999 for (let j = 0; j < items[i].refinements.length; j++) { 1000 if(items[i].refinements[j].value == "true") { 1001 items[i].refinements[j].label = "Archived"; 1002 } else { 1003 items[i].refinements[0].label = "Not archived"; 1004 } 1005 } 1006 } 1007 } 1008 return items; 1009 }, 1010 }), 1011 1012 1013 ]) 1014 1015 1016 1017 1018 1019 // Suggestions 1020 1021 1022 1023 /*const suggestions = instantsearch({ 1024 indexName: 'stage_www_query_suggestions', 1025 searchClient, 1026 }); 1027 1028 suggestions.addWidgets([ 1029 instantsearch.widgets.configure({ 1030 hitsPerPage: 5, 1031 }), 1032 autocomplete({ 1033 container: $('#autocomplete'), 1034 onSelectChange({ query }) { 1035 search.helper.setQuery(query).search(); 1036 }, 1037 }), 1038 ]); 1039 1040 suggestions.start();*/ 1041 1042 search.start(); 1043 1044 function debouncePromise(fn, time) { 1045 let timerId = undefined; 1046 1047 return function debounced(...args) { 1048 if (timerId) { 1049 clearTimeout(timerId); 1050 } 1051 1052 return new Promise((resolve) => { 1053 timerId = setTimeout(() => resolve(fn(...args)), time); 1054 }); 1055 }; 1056 } 1057 1058 const debounced = debouncePromise((items) => Promise.resolve(items), 300); 1059 1060 let skipInstantSearchUiStateUpdate = false; 1061 const { setQuery } = autocomplete({ 1062 container: '#autocomplete', 1063 placeholder: 'Search for pages, documents, events ...', 1064 detachedMediaQuery: 'none', 1065 initialState: { 1066 query: searchPageState.q ? searchPageState.q.replaceAll('^','') || '' : '', 1067 }, 1068 /*cssClasses: { 1069 form: 'input-group input-group-search-dropdown', 1070 input: 'search-input', 1071 submit: 'btn btn-search' 1072 },*/ 1073 onSubmit({ state }) { 1074 setInstantSearchUiState({ query: state.query }); 1075 }, 1076 onReset() { 1077 setInstantSearchUiState({ query: '' }); 1078 }, 1079 onStateChange({ prevState, state }) { 1080 //if (!skipInstantSearchUiStateUpdate && prevState.query !== state.query) { 1081 // setInstantSearchUiState({ query: state.query }); 1082 //} 1083 //skipInstantSearchUiStateUpdate = false; 1084 }, 1085 getSources() { 1086 return debounced([ 1087 { 1088 sourceId: 'querySuggestions', 1089 getItemInputValue: ({ item }) => item.query, 1090 onSelect({ item }) { 1091 //search.helper.setQuery(query).search(); 1092 setInstantSearchUiState({ query: item.query }); 1093 }, 1094 getItems({ query }) { 1095 return getAlgoliaResults({ 1096 searchClient, 1097 queries: [ 1098 { 1099 indexName: `${indexEnv}_query_suggestions`, 1100 query, 1101 params: { 1102 hitsPerPage: 4, 1103 }, 1104 }, 1105 ], 1106 }); 1107 }, 1108 templates: { 1109 item({ item, components }) { 1110 return components.Highlight({ hit: item, attribute: 'query' }); 1111 }, 1112 }, 1113 }, 1114 ]); 1115 }, 1116 }) 1117 1118 // This keeps Autocomplete aware of state changes coming from routing 1119 // and updates its query accordingly 1120 window.addEventListener('popstate', () => { 1121 skipInstantSearchUiStateUpdate = true; 1122 setQuery(search.helper?.state.query || ''); 1123 }); 1124 //}
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.