PageSourceSearch

https://www.caiso.com/theme/js/custom-search.js?v=1.1.16-1

js caiso.com collected 2026-09-24 08:40:05 UTC 47,123 bytes, 1,124 lines download raw bytes

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.