PageSourceSearch

https://www.macfound.org/js/fellows-cp-global.js?v=17646368

js macfound.org collected 2026-09-24 08:32:22 UTC 29,753 bytes, 657 lines download raw bytes

1/*HEADERSTART
2   Name: felows-cp-global.js
3   Desc: Contains fellow functionality from the 2022 rewrite
4   Notes:
5HEADER END*/
6function fellows_cp_search(options) {
7    "use strict";
8    // NOTE: Calling our own copy of the CPS object to avoid the 200 row limit
9    var cps = new FellowsCrownPeakSearch();
10    var defaultOptions = {
11        collection: "",
12        proxyPage: "",
13        filters: [],
14        facet: false,
15        facets: [],
16        formatter: {},
17        highlight: false,
18        maxFacets: 15,
19        noResults: "No results found.",
20        numRows: 10,
21        parameters: "",
22        selectors: {
23            loadMore: "",
24            results: ""
25        },
26        sortBy: ["score desc"],
27        spellcheck: false,
28        filterMultiSelectColor: "#d6002a"
29    };
30    var _justInit = true;
31    var _options = {};
32    var _lastTerm = "";
33    var _facetsFetched = false;
34    var _page = 1;
35    var _defaultFilterQueries = [];
36    var _filterQueries = [];
37    var _filterMultiSelect = [];
38    // Initialise if we were given options
39    if (options) _options = initSearch(options);
40    function validateOptions() {
41        // TODO: validate what we have been given
42        if (!_options.collection) console.error("Missing collection");
43    }
44    /**
45     * Initialize a search using the provided options, plus items from the default options if required
46     * @param {any} options - the options object to be used
47     */
48    function initSearch(options) {
49        var i;
50        // Merge the provided options with the defaults
51        _options = mergeOptions(options);
52        // Apply the standard settings to the CPS object
53        cps.maxFacets(_options.maxFacets);
54        cps.highlight(_options.highlight);
55        cps.spellcheck(_options.spellcheck);
56                    cps.endpoint('https://searchg2.crownpeak.net/%%COLLECTION%%/%%HANDLER%%?%%QUERY%%');
57                cps.sort(_options.sortBy);
58        cps.collection(_options.collection);
59        cps.rows(_options.numRows);
60        // Push the filters onto our set of default filter queries
61        for (i = 0; i < _options.filters.length; i++) {
62            _defaultFilterQueries.push(_options.filters[i]);
63        }
64        if (_options.facet) {
65            var facets = [];
66            // Apply facet sorts via the parameters string
67            for (i = 0; i < _options.facets.length; i++) {
68                if (_options.facets[i].field) {
69                    facets.push(_options.facets[i].field);
70                    if (_options.facets[i].sort)
71                        _options.parameters += "&f." + _options.facets[i].field + ".facet.sort=" + options.facets[i].sort;
72                }
73                if (!_options.selectors.facets) _options.selectors.facets = [];
74                _options.selectors.facets.push(_options.facets[i].selector);
75            }
76            cps.facets(facets);
77            // Store facets so we can replay them after clearing
78            cps.facetsCache = facets;
79        } else {
80            cps.facets([]);
81        }
82        cps.parameters(_options.parameters);
83        // Validate the options as they now stand
84        validateOptions();
85        // Wire up the Load More UI to get the next page of results
86        connectLoadMore(_options.selectors.loadMore);
87        return _options;
88    }
89    /**
90     * Get a list of filter queries from the facet UI. Depends on an implementation using checkboxes
91     * @param {any} selectors - array of selectors containing the facet UI elements
92     */
93    function getFilterQueries(selectors) {
94        var $checked = $(selectors.join(",")).find("input[type=checkbox]:checked,input[type=radio]:checked");
95        var fqs = [];
96        // Build a collection of fields and values
97        for (var i = 0, len = $checked.length; i < len; i++) {
98            var $field = $checked.eq(i);
99            var value = $field.data("value")
100            if (value) {
101                var field = $field.data("field");
102                var filter = fqs[field] || [];
103                filter.push(value.indexOf('[') === 0 ? value : '"' + value + '"');
104                fqs[field] = filter;
105            }
106        }
107        // Convert the array into Search G2 filter queries
108        var result = [];
109        for (var key in fqs) {
110            var values = fqs[key];
111            result.push(key + ":" + (values.length === 1 ? (values[0]) : ('(' + values.join('" OR "') + ')')));
112        }
113        return result;
114    }
115    /**
116     * A facet has been clicked, so we need to update our query
117     * @param {any} element - the element for the facet that was clicked
118     */
119    function facetClicked(element) {
120        var $element = $(element);
121        if (element.tagName === "INPUT" && (element.type === "checkbox" || element.type === "radio")) {
122            var checked = element.checked === true;
123            // TODO: a more generic way to detect single vs multiple facets would be better
124            if ($element.hasClass("singlefacet")) {
125                if (checked) {
126                    // Checked for single facets means all others must be unchecked
127                    $element.closest("ul").find("input[type=" + element.type + "]:checked").not($element).prop("checked", false);
128                } else {
129                    // Unchecked means we need to make sure at least one is checked
130                    $element.closest("ul").find("input[type=" + element.type + "]").first().prop("checked", true);
131                }
132            }
133            // Re-run the search from the top with the newly updated filter queries
134            search(null, 1, getFilterQueries(_options.selectors.facets));
135        }
136        else if (element.tagName === "A") {
137            // multi-select option
138            if (typeof $element.data('multi-select') !== 'undefined') {
139                MultiselectLinkFilters(element);
140            }
141            // "ALL" Link
142            else if ($element.data("field").length === 0) {
143                search(null, 1, []);
144            }
145            // Single Filter Link
146            else {
147                var filterStr = ($element.data("no-str")) ? ($element.data("field") + ":" + $element.data("value")) : ($element.data("field") + ":\"" + $element.data("value") + "\"");
148                search(null, 1, [filterStr]);
149            }
150        }
151    }
152    function MultiselectLinkFilters(element) {
153        var $element = $(element);
154        // ALL Click
155        if ($element.data("field").length === 0) {
156            _filterMultiSelect.map(function (item, index) {
157                return (item.field === $element.data('filter-field')) ? index : -1;
158            }).filter(function (index) {
159                return index >= 0;
160            }).reverse().forEach(function (index) {
161                _filterMultiSelect.splice(index, 1);
162            });
163            $element.closest("ul").find("a:not([data-field=''])").css({ "color": "" }).data('selected', '0');
164            $element.css({ "color": _options.filterMultiSelectColor });
165        }
166        // Single Item Click
167        else {
168            var filterGroupList = $.grep(_filterMultiSelect, function (n, i) { return n.field === $element.data("field"); });
169            var filterGroup = (filterGroupList.length > 0) ? filterGroupList[0] : { field: $element.data("field"), values: [] };
170            $element.closest("ul").find("a[data-field='']").first().css({ "color": "" });
171            $element.css({ "color": _options.filterMultiSelectColor });
172            if ($.grep(filterGroup.values, function (n, i) { return n === $element.data("value"); }).length > 0) {
173                filterGroup.values.splice($.inArray($element.data("value"), filterGroup.values), 1);
174                $element.data('selected', '0');
175                $element.css({ "color": "" });
176                // if none selected, select ALL
177                if ($element.closest("ul").find("a").filter(function () { return $(this).data('selected') === '1'; }).length === 0) { $element.closest("ul").find("a[data-field='']").css({ "color": _options.filterMultiSelectColor }); }
178            }
179            else {
180                filterGroup.values.push($element.data("value"));
181                $element.data('selected', '1');
182                $element.css({ "color": _options.filterMultiSelectColor });
183            }
184            // update _filterMultiSelect
185            if (filterGroupList.length === 0) {
186                _filterMultiSelect.push(filterGroup);
187            }
188            else {
189                $.each(_filterMultiSelect, function (index, filter) {
190                    if (filter.field === $element.data("field")) { filter.values = filterGroup.values; }
191                });
192            }
193        }
194        search(null, 1);
195    }
196    function GetMultiselectFiltersQuery() {
197        return $.grep(_filterMultiSelect, function (item, index) {
198            return item.values.length > 0;
199        }).map(function (item, index) {
200            return item.field + ":(" + item.values.join(' OR ') + ")";
201        });
202    }
203    /**
204     * The Load More UI element was clicked, so fetch the next page of results, and append them to the results UI
205     * @param {any} element - the Load More ui element that was clicked
206     */
207    function loadMoreClicked(element) {
208        search(null, _page + 1, null, null, true);
209    }
210    /**
211     * Execute a search using the provided items
212     * @param {any} term - a search term. Leave falsy to re-run a previous search, or to use *:* if not set
213     * @param {any} page - the page of results to show. Leave falsy to use page 1
214     * @param {any} filterQueries - the array of filter queries to apply, in additional to the deaults. Leave falsy to re-run a previous search
215     * @param {any}
215 facet - whether to re-run faceting - defaults to false
216     * @param {any} append - whether to append results or replace them - depends to false - i.e. replace
217     */
218    function search(term, page, filterQueries, facet, append) {
219        //if (_justInit) {
220        //    _lastTerm = this.getParameterByName('q');
221        //    var searchField = this.getParameterByName('fn');
222        //    if (_lastTerm && _lastTerm !== '' && searchField && searchField !== '') { _lastTerm = searchField + ':"' + _lastTerm + '"'; }
223        //    _justInit = false;
224        //}
225        // Only fetch facets if we have some to fetch and we didn't already get them (or we're asked to get them again)
226        cps.facets(cps.facetsCache && (facet || !_facetsFetched) ? cps.facetsCache : []);
227        if (term) _lastTerm = term;
228        else if (!term && _lastTerm) term = _lastTerm;
229        else _lastTerm = term = "*:*";
230        if (page) _page = page;
231        else page = _page;
232        if (filterQueries || filterQueries == [])
233            _filterQueries = filterQueries;
234        else
235            filterQueries = _filterQueries;
236        var fq = _defaultFilterQueries.concat(filterQueries).concat(GetMultiselectFiltersQuery());
237        // Run the query, and queue up our function to run when the results are ready
238        cps.query(term, page, fq).done(function (data) {
239            if (data.pager.page > 1) {
240                data.pager.pages.unshift('<<', '<');
241            }
242            if (data.pager.page < data.pager.total) {
243                data.pager.pages.push('>', '>>');
244            }
245            drawFacets(data);
246            drawResults(data, append);
247            drawLoadMore(data);
248        });
249    }
250    /**
251     * Re-run the previous search from page 1, using the provided sort order
252     * @param {any} order - the sort order
253     */
254    function sort(order) {
255        cps.sort(order);
256        search(null, 1);
257    }
258    /**
259     * Format a string using the provided template and any number of arguments
260     * @param {any} source - the template to be used for formatting
261     * @param {any} params - parameters for formatting.
262     * If this is a JSON object, parameters can be displayed with [property]
263     * If this is a parameter array, parameters can be displayed with [0], [1], etc.
264     */
265    function format(source) {
266        if (!source || !source.replace) return "";
267        if (arguments.length === 2 && typeof arguments[1] === "object") {
268            var obj = arguments[1];
269            return source.replace(/\[([^\]]+)\]/g, function (match, p1) {
270                var value = obj[p1];
271                return typeof value === "undefined" && (!obj.hasOwnProperty || !obj.hasOwnProperty(p1)) ? match : (value || "");
272            });
273        } else {
274            for (var i = 1, len = arguments.length; i < len; i++) {
275                source = source.replace(new RegExp("\\[" + (i - 1) + "\\]", "g"), arguments[i].toString());
276            }
277        }
278        return source;
279    }
280    /**
281     * Merge the provided options with those in the default options object
282     * @param {any} options
283     */
284    function mergeOptions(options) {
285        var i, len;
286        options = $.extend(true, defaultOptions, options);
287        if (options.proxyPage.length === 0) {
288            options.proxyPage = document.location.origin;
289        }
290        return options;
291    }
292    /**
293     * Format a date for display
294     * @param {any} date - a string representation of a date, to be converted with new Date(date)
295     * @param {any} cd - set to true to use the News date format. Defaults to false
296     */
297    function formatDate(date, cd) {
298        date = new Date(date);
299        var result = [];
300        if (cd) {
301            result.push(
302                ['Jan.', 'Feb.', 'Mar.', 'Apr.', 'May.', 'Jun.', 'Jul.', 'Aug.', 'Sep.', 'Oct.', 'Nov.', 'Dec.'][date.getMonth()]
303            );
304            result.push(date.getDate() + ",");
305        } else {
306            result.push(date.getDate());
307            result.push(
308                ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][date.getMonth()]
309            );
310        }
311        result.push(date.getFullYear());
312        return result.join(' ');
313    }
314    /**
315     * Connect the UI for the facets. They must contain checkboxes or A elements
316     * @param {any} selector - the selector pointing to the facets container element
317     */
318    function connectFacets(selector, facetBeforeClick) {
319        $(selector).find("input[type=checkbox],input[type=radio],a").unbind("click").click(function (event) {
320            if (facetBeforeClick) { facetBeforeClick(); }
321            facetClicked(event.srcElement || event.target);
322        });
323    }
324    /**
325     * Connect the UI for the Load More. It must contain an A element
326     * @param {any} selector - the selector pointing to the Load More element
327     */
328    function connectLoadMore(selector) {
329        $(selector).find("a").unbind("click").click(function (event) {
330            loadMoreClicked(event.srcElement || event.target);
331        });
332    }
333    /**
334     * Draw the collection(s) of facets from the data returned from Search G2
335     * @param {any} data - the entire object returned from Search G2
336     */
337    function drawFacets(data) {
338        var html = [];
339        var i, len, j, lenJ
340        if (data && data.facet_counts && data.facet_counts.facet_fields) {
341            _facetsFetched = true;
342            var fields = data.facet_counts.facet_fields;
343            for (i = 0, len = _options.facets.length; i < len; i++) {
344                html = [];
345                var facet = _options.facets[i];
346                if (facet.field) {
347                    for (j = 0, lenJ = fields[facet.field].length; j < lenJ; j += 2) {
348                        html.push(facet.formatter(j, facet.field, fields[facet.field][j], fields[facet.field][j], fields[facet.field][j + 1]));
349                    }
350                } else if (facet.custom) {
351                    for (j = 0, lenJ = facet.values.length; j < lenJ; j++) {
352                        html.push(facet.formatter(j, facet.custom, facet.values[j].text, facet.values[j].value, NaN));
353                    }
354                }
355                html = html.join("");
356                // afterResults function
357                if (facet.callbacks && facet.callbacks.afterResults) { html = facet.callbacks.afterResults(html) || html; }
358                if (facet.selector) {
359                    $(facet.selector).html(html);
360                    connectFacets(facet.selector, facet.callbacks.beforeClick);
361                }
362                // afterDrawResults function
363                if (facet.callbacks && facet.callbacks.afterDrawResults) { facet.callbacks.afterDrawResults(data, html); }
364            }
365        }
366    }
367    /**
368     * Draw the results UI from the data returned from Search G2
369     * @param {any} data - the entire object returned from Search G2
370     * @param {any} append - set to true to append the results to the UI, otherwise they will be replaced. Defaults to false
371     */
372    function drawResults(data, append) {
373        if (_options.callbacks && _options.callbacks.beforeResults)
374            data = _options.callbacks.beforeResults(data) || data;
375        var html = [];
376        if (data && data.response && data.response.docs && data.response.docs.length) {
377            var formatter = options.formatter || function (i, doc) { return doc[0]; };
378            for (var i = 0, len = data.response.docs.length; i < len; i++) {
379                html.push(formatter(i, data.response.docs[i], data));
380            }
381        } else {
382            html = [_options.noResults];
383        }
384        html = html.join("");
385        if (_options.callbacks && _options.callbacks.afterResults)
386            html = options.callbacks.afterResults(data, html) || html;
387        if (_options.selectors && _options.selectors.results) {
388            if (append) $(_options.selectors.results).append(html);
389            else $(_options.selectors.results).html(html);
390        }
391        if (_options.callbacks && _options.callbacks.afterDrawResults)
392            options.callbacks.afterDrawResults(data, html);
393    }
394    /**
395     * Draw the Load More UI by showing/hiding it if more results are available
396     * @param {any} data - the entire object returned from Search G2
397     */
398    function drawLoadMore(data) {
399        var $loadMore = $(_options.selectors.loadMore);
400        if ($loadMore.length > 0 && data && data.response && data.response.numFound && data.response.docs) {
401            $loadMore.toggle((data.response.start || 0) + data.response.docs.length < data.response.numFound);
402        }
403    }
404    // Publicly available things
405    this.init = initSearch;
406    this.search = search;
407    this.sort = sort;
408    // pagination
409    this.page = _page;
410    this.options = _options;
411    // Public utility functions
412    this.urldecode = function (url) { return decodeURIComponent(url.replace(/\+/g, ' ')); };
413    this.urlencode = function (url) { return encodeURIComponent(url.replace(/\+/g, ' ')); };
414    this.formatDate = formatDate;
415    this.connectFacets = connectFacets;
416    this.getParameterByName = function (key) {
417        function parseParams() {
418            var params = {},
419                e,
420                a = /\+/g,  // Regex for replacing addition symbol with a space
421                r = /([^&=]+)=?([^&]*)/g,
422                d = function (s) { return decodeURIComponent(s.replace(a, " ")); },
423                q = window.location.search.substring(1);
424            while (e = r.exec(q))
425                params[d(e[1])] = d(e[2]);
426            return params;
427        }
428        if (!this.queryStringParams)
429            this.queryStringParams = parseParams();
430        return this.queryStringParams[key];
431    };
432    this.precis = function (s, z) {
433        var SIZE = (z) ? z : 120;
434        if (!s || !s.substr) return "";
435        if (s.length < SIZE) return s;
436        return s.substr(0, SIZE) + "...";
437    };
438};
439var paginationInit = true;
440function InitPagination(itemsCount, searchObject) {
441    if (searchObject == undefined) {
442        searchObject = cpsearch;
443    }
444    $("#results-pagination").hide();
445    //var numFound = 0;
446    if (itemsCount <= searchObject.options.numRows) {
447        $("#results-pagination").hide();
448    }
449    else {
450        $("#results-pagination").show();
451        $('#results-pagination').pagination({
452            dataSource: new Array(itemsCount),
453            pageSize: searchObject.options.numRows,
454            pageNumber: searchObject.page,
455            showPrevious: true,
456            showNext: true,
457            pageRange: 2,
458            autoHidePrevious: false,
459            autoHideNext: false,
460            // prevText: "<",
461            // nextText: ">",
462            prevText: "",
463            nextText: "",
464            callback: function (response, pagination) {
465            },
466            beforeNextOnClick: function (pagination, pageNbr) {
467                paginationInit = false;
468                $([document.documentElement, document.body]).animate({
469                    scrollTop: $(searchObject.options.selectors.results).offset().top
470                }, 100);
471                searchObject.search(null, pageNbr, null, null, false);
472            },
473            beforePreviousOnClick: function (pagination, pageNbr) {
474                paginationInit = false;
475                $([document.documentElement, document.body]).animate({
476                    scrollTop: $(searchObject.options.selectors.results).offset().top
477                }, 100);
478                searchObject.search(null, pageNbr, null, null, false);
479            },
480            beforePageOnClick: function (pagination, pageNbr) {
481                paginationInit = false;
482                $([document.documentElement, document.body]).animate({
483                    scrollTop: $(searchObject.options.selectors.results).offset().top
484                }, 100);
485                searchObject.search(null, pageNbr, null, null, false);
486            }
487        })
488    }
489}
490getParameterByName = function (key) {
491    function parseParams() {
492        var params = {},
493            e,
494            a = /\+/g,  // Regex for replacing addition symbol with a space
495            r = /([^&=]+)=?([^&]*)/g,
496            d = function (s) { return decodeURIComponent(s.replace(a, " ")); },
497            q = window.location.search.substring(1);
498        while (e = r.exec(q))
499            params[d(e[1])] = d(e[2]);
500        return params;
501    }
502    if (!this.queryStringParams)
503        this.queryStringParams = parseParams();
504    return this.queryStringParams[key];
505};
506var cpsearch;
507jQuery(document).ready(function ($) {
508    if ($('#related_news').length > 0) {
509        var $related = $('#related_news_result');
510        if ($related.attr('data-filter-custom')) {
511            var arrFilters = $related.attr('data-filter-custom').split(',');
512            for (var i = 0; i < arrFilters.length; i++) {
513                arrRelatedFilters.push(arrFilters[i]);
514            }
515            if (getParameterByName('news') == 'all') {
516                defaultRelatedNewsOptions.numRows = 60;
517                defaultRelatedNewsOptions.formatter = function (i, doc) {
518                    var html = [];
519                    html.push('<a href="' + doc.custom_s_url + '" class="grid-item has-triple-space-below">');
520                    html.push('<div class="grid-item__image">');
521                    if (doc.custom_s_key_image) html.push('<img src="' + doc.custom_s_key_image + '" alt="alt tag placeholder" />');
522                    html.push('</div>');
523                    html.push('<div class="grid-item__title has-space-above has-half-space-below h3">' + doc.custom_t_title + '</div>');
524                    html.push('<div class="grid-item__subtitle has-half-space-below">');
525                    var source = doc.custom_s_source_title ? doc.custom_s_source_title : doc.custom_s_article_type_display ? doc.custom_s_article_type_display : '';
526                    source = (doc.custom_s_source_is_pub == "true" ? '<em>' : '') + source + (doc.custom_s_source_is_pub == "true" ? '</em>' : '');
527                    html.push('<strong>' + source + '</strong>');
528                    html.push('</div>');
529                    html.push('<div class="grid-item__subtitle tile_third_level has-half-space-below">' + doc.custom_s_date + '</div>');
530                    html.push('</a>');
531                    return html.join("");
532                };
533                //Modify staff page
534                $('#staff_bio').hide();
535                $('#related_programs').hide();
536                $('#related_news').removeClass('related-news');
537                $('#related_news .text--center').removeClass('text--center').addClass('text--left');
538                $('#related_news .related-news__title').removeClass('related-news__title').addClass('related-news__staff__title');
539                $('#related_news .layout-flexbox__columns--three').removeClass('layout-flexbox__columns--three').addClass('layout-flexbox__columns--four');
540                $('#related_news .related-news__more').hide();
541            }
542            cpsearch = new cp_search(defaultRelatedNewsOptions);
543            cpsearch.search('*');
544        }
545    }
546    if ($('#grantee_news_result').length > 0) {
547        var $filter = $('#grantee_news_result');
548        if ($filter.attr('data-filter-custom')) {
549            var arrFilters = $filter.attr('data-filter-custom').split(',');
550            for (var i = 0; i < arrFilters.length; i++) {
551                arrFilter.push(arrFilters[i]);
552            }
553            cpsearch = new cp_search(defaultGranteeNewsOptions);
554            cpsearch.search('*');
555        }
556    }
557    if ($('#dynamic_promos').length > 0) {
558        var $filter = $('#dynamic_promos');
559        if ($filter.attr('data-filter-custom')) {
560            var arrFilters = $filter.attr('data-filter-custom').split(',');
561            for (var i = 0; i < arrFilters.length; i++) {
562                arrFilter.push(arrFilters[i]);
563            }
564            defaultDynamicPromoOptions.numRows = $('#dynamic_promos').attr('data-count');
565            cpsearch = new cp_search(defaultDynamicPromoOptions);
566            cpsearch.search('*');
567        }
568    }
569    if ($('#recipient_news_result').length > 0) {
570        var $filter = $('#recipient_news_result');
571        if ($filter.attr('data-filter-custom')) {
572            var arrFilters = $filter.attr('data-filter-custom').split(',');
573            for (var i = 0; i < arrFilters.length; i++) {
574                arrFilter.push(arrFilters[i]);
575            }
576            cpsearch = new cp_search(defaultMaceiNewsOptions);
577            cpsearch.search('*');
578        }
579    }
580    getRandomMaceiRecipient().refresh();
581});
582var formatter = new Intl.NumberFormat('en-US', {
583    style: 'currency',
584    currency: 'USD',
585    // These options are needed to round to whole numbers if that's what you want.
586    minimumFractionDigits: 0,
587    maximumFractionDigits: 0,
588});
589String.prototype.endsWith = function (suffix) {
590    return this.indexOf(suffix, this.length - suffix.length) !== -1;
591};
592function getUrlVar(key) {
593    var result = new RegExp(key + "=([^&]*)", "i").exec(window.location.search);
594    return result && unescape(decodeURIComponent(result[1])) || null;
595}
596function getRandomMaceiRecipient() {
597    var resultsSelector = '#random_macei_recipient';
598    return {
599        'refresh': function () {
600            if ($(resultsSelector).length > 0) {
601                searchoptions = this.get_search_options();
602                cpsearch = new cp_search(searchoptions);
603                cpsearch.search('*');
604            }
605        },
606        'get_search_options': function (querystring) {
607            currentrecipientid = $(resultsSelector).data('currentrecipientid');
608            programId = $(resultsSelector).data('programid');
609            customfilters = ['custom_s_template:\"macei recipient page\"'];
610            if (programId != "") {
611                customfilters.push('custom_ss_program_id:\"' + programId + '\"');
612            }
613            customfilters.push('-id:\"' + currentrecipientid + '\"');
614            q = '*';
615            return {
616                collection: 'live-macfound-rt',
617                filters: customfilters,
618                facet: false,
619                facets: [],
620                formatter: function (i, doc, data) {
621                    var html = [];
622                    if (doc.id == randomRecipient.id) {
623                        html.push('<a class="promo-box__promo" href="' + doc.custom_s_url + '" style="background-image: url(' + doc.custom_s_display_photo + ')">');
624                        html.push('    <div class="promo-box__content">');
625                        html.push('        <div class="promo-box__title">' + doc.custom_s_name + '</div>');
626                        html.push('        <div class="promo-box__subtitle">' + doc.custom_s_tag_line + '</div>');
627                        html.push('    </div>');
628                        html.push(' </a>');
629                    }
630                    return html.join("");
631                },
632                callbacks: {
633                    beforeResults: function (data) {
634                        randomRecipient = data.response.docs[Math.floor(Math.random() * data.response.docs.length)];
635                    },
636                    afterResults: function (data, html) {
637                    },
638                    afterDrawResults: function (data, html) {
639                        $html = $(resultsSelector).children(".promo-box__promo");
640                        $html.parent().after($html);
641                        $(resultsSelector).remove();
642                    }
643                },
644                selectors: { loadMore: '', results: resultsSelector },
645                highlight: false,
646                maxFacets: 15,
647                noResults: "",
648                numRows: 1000,
649                sortBy: [""],
650                spellcheck: false,
651                parameters: '&indent=true',
652                qOverride: '',
653                term: q
654            };
655        }
656    }
657}

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.