PageSourceSearch

https://www.civicfcu.org/modules/custom/algolia_search_interface/js/algolia.js?tlu4ug

js civicfcu.org collected 2026-09-24 10:25:36 UTC 7,887 bytes, 250 lines download raw bytes

1/**    global javascript instantsearch algoliasearch */
2const indexname = drupalSettings.algolia.config.indexname;
3const appId = drupalSettings.algolia.config.appId;
4const apiKey = drupalSettings.algolia.config.apiKey;
5const template = drupalSettings.algolia.config.template;
6const pagination = drupalSettings.algolia.config.pagination;
7const { liteClient: algoliasearch } = window["algoliasearch/lite"];
8
9if(indexname && appId && apiKey && template) {
10    const algoliaClient = algoliasearch(
11      appId,
12      apiKey
13    );
14
15    const searchClient = {
16      ...algoliaClient,
17      search(requests) {
18        if (requests.every(({ params }) => !params.query)) {
19          return Promise.resolve({
20            results: requests.map(() => ({
21              hits: [],
22              nbHits: 0,
23              nbPages: 0,
24              page: 0,
25              processingTimeMS: 0,
26              hitsPerPage: 0,
27              exhaustiveNbHits: false,
28              query: '',
29              params: '',
30            })),
31          });
32        }
33
34        return algoliaClient.search(requests);
35      },
36    };
37
38
39    const search = instantsearch({
40        indexName: indexname,
41        searchClient,
42    });
43    /**
44     * Facet values we want to re-map
45     */
46    const facetMapping = {
47      blog: 'Blogs',
48      faq: 'FAQs',
49      page: 'Civic Info',
50      product: 'Products',
51      sections_page: 'Services'
52    };
53
54
55    search.addWidgets([
56        instantsearch.widgets.searchBox({
57            container: '#ais-search',
58            placeholder: 'Search',
59            wrapInput: false,
60            showSubmit: false,
61            showReset: false,
62        }),
63
64        instantsearch.widgets.refinementList({
65          container: '#facet-content-types',
66          attribute: 'left_filters',
67          transformItems(items) {
68            return items.map(item => ({
69              ...item,
70              // The label to use
71              label: facetMapping[item.value],
72              highlighted: facetMapping[item.value]
73                ? facetMapping[item.value]
74                : item.value,
75            }));
76          },
77        })
78
79    ]);
80
81    /**
82     * check to enable pagination
83     * pagination limit configurable on algolia account
84     */
85    if(pagination && pagination != 0) {
86        search.addWidgets([
87            instantsearch.widgets.pagination({
88                container: '#algolia-pagination',
89                showFirst: false,
90                showLast: false,
91                padding: 2
92            })
93        ]);
94    }
95
96    /* Hits widget */
97    search.addWidget(
98        instantsearch.widgets.hits({
99            container: '#algolia-hits',
100            templates: {
101                empty: `
102                    <div class="no-results">
103                        <p>We could not find a match for "{{query}}". Please try another search.</p>
104                        <ul>
105                            <li>Try different or more general keywords</li>
106                            <li>Try fewer keywords</li>
107                        </ul>
108                    </div>
109                `,
110                item(hit, { html, components }) {
111                    return html`
112                        <h2 class="ais-Hits-title">
113                            <a href=${hit.url}>${components.Highlight({ attribute: 'title', hit })}</a>
114                        </h2>
115                        <p>${components.Snippet({ attribute: 'rendered_item', hit })}</p>
116                    `;
117                },
118            },
119        })
120    );
121
122    /**
123     *   Business/Personal menu items with a reset as "All"
124     */
125    search.addWidget(
126        instantsearch.widgets.menu({
127            container: '#menu',
128            attribute: 'member_type_filter',
129            sortBy: ['count:desc', 'name:asc'],
130            templates: {
131                item: `
132                    <a class="{{cssClasses.link}}" href="{{url}}">
133                        <span class="{{cssClasses.label}}">{{label}}</span>
134                    </a>
135                `,
136            },
137            // Probably not needed any longer, but leaving here for reference if we ever need to do it again.
138            // transformItems(items) {
139            //     var orderedList = ['Business Products','Personal'];
140            //     var returnedItems =[];
141            //     for(ol in orderedList) {
142            //         for (il in items) {
143            //             if (orderedList[ol] == items[il].value) {
144            //                 if (items[il].value == 'Business Products'){
145            //                     items[il].label = 'Business'
146            //                 }
147            //                 returnedItems.push(items[il]);
148            //             }
149            //         }
150            //     }
151            //     return returnedItems;
152            // },
153        })
154    );
155
156    search.addWidget(
157        instantsearch.widgets.clearRefinements({
158            container: '#clearRefinements',
159            cssClasses: {
160                link: 'ais-clear-all--link__active'
161            },
162            templates: {
163                resetLabel: 'All',
164            },
165            autoHideContainer: false
166        })
167    );
168
169    /* Stats widget */
170    search.addWidget(
171        instantsearch.widgets.stats({
172            container: '#stats',
173            templates: {
174                text: `
175                    {{#areHitsSorted}}
176                        {{#hasNoSortedResults}}No Relevant Results{{/hasNoSortedResults}}
177                        {{#hasOneSortedResults}}1 Relevant Result{{/hasOneSortedResults}}
178                        {{#hasManySortedResults}}{{#helpers.formatNumber}}{{nbSortedHits}}{{/helpers.formatNumber}} Relevant Results{{/hasManySortedResults}}
179                        Sorted Out Of {{#helpers.formatNumber}}{{nbHits}}{{/helpers.formatNumber}}
180                    {{/areHitsSorted}}
181                    {{^areHitsSorted}}
182                        {{#hasNoResults}}No Results{{/hasNoResults}}
183                        {{#hasOneResult}}1 Result{{/hasOneResult}}
184                        {{#hasManyResults}}{{#helpers.formatNumber}}{{nbHits}}{{/helpers.formatNumber}} Results{{/hasManyResults}}
185                    {{/areHitsSorted}}
186                    Below
187                `,
188            },
189        })
190    );
191
192    // Create the render function
193    const renderSortBy = (renderOptions, isFirstRender) => {
194        const {
195            options,
196            currentRefinement,
197            hasNoResults,
198            refine,
199            widgetParams,
200        } = renderOptions;
201
202        if (isFirstRender) {
203            const sortby = document.createElement('ul');
204
205            sortby.addEventListener('click', event => {
206                refine(event.target.getAttribute('data-value'));
207            });
208
209            widgetParams.container.appendChild(sortby);
210        }
211
212        const sortby = widgetParams.container.querySelector('ul');
213
214        sortby.disabled = hasNoResults;
215
216        sortby.innerHTML = `
217            ${options
218                .map(
219                    option => `
220                        <li
221                            data-value="${option.value}"
222                            ${option.value === currentRefinement ? ' class="selected"' : ''}
223                        >
224                            ${option.label}
225                        </li>
226                    `
227                )
228                .join('')}
229        `;
230    };
231
232// Create the custom widget
233    const customSortBy = instantsearch.connectors.connectSortBy(renderSortBy);
234
235    search.addWidgets([
236        customSortBy({
237            container: document.querySelector('#sort-by'),
238            items: [
239                { label: 'MOST RELEVANT', value: indexname },
240                { label: 'LATEST', value: indexname +'_latest'},
241            ],
242        })
243    ]);
244
245    search.start();
246
247} else
248{
249    throw "Algolia settings missing";
250}

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.