PageSourceSearch

https://campus.gov.il/wp-content/themes/campusnew/js/search-autocomplete.js?var=1

js campus.gov.il collected 2026-10-02 06:35:11 UTC 6,198 bytes, 135 lines download raw bytes

1/* Shared catalog autocomplete for the site's search fields.
2 *
3 * The suggestion widget used to be wired to a single element by ID
4 * (#newCatalogSearchText), so every other search box on the site — the sticky header, the
5 * mobile search layer, the 404 page — had no autocomplete at all. This exposes the widget
6 * as a reusable initialiser and binds it to all of them.
7 *
8 * Fields are matched by name, not id: header.php declares id="srch" twice (desktop + mobile
9 * layer) and 404.php a third time, so an id-based selector can only ever reach the first.
10 *
11 * Requires jQuery + jQuery UI, both loaded site-wide from header.php.
12 */
13(function ($) {
14    'use strict';
15
16    if (!$ || !$.fn || !$.fn.autocomplete) return;
17
18    var ENDPOINT = 'ajax_catalog_autocomplete_results/';
19    /** Suggestion labels are long ("תוכנית לימוד - …"); this is the narrowest readable menu. */
20    var MIN_MENU_WIDTH = 360;
21    /** Breathing room kept between the menu and the viewport edges. */
22    var VIEWPORT_MARGIN = 12;
23
24    /** z-index of the nearest fixed/sticky ancestor, or null when the field is in normal flow. */
25    function stickyAncestorZIndex(el) {
26        for (var node = el.parentElement; node && node !== document.body; node = node.parentElement) {
27            var cs = window.getComputedStyle(node);
28            if (cs.position === 'fixed' || cs.position === 'sticky') {
29                var z = parseInt(cs.zIndex, 10);
30                return isNaN(z) ? null : z;
31            }
32        }
33        return null;
34    }
35
36    /**
37     * Build the shared autocomplete options.
38     *
39     * @param {function} [extraData] Returns additional request params (the catalog field sends
40     *                               its active filters; everywhere else sends nothing extra).
41     */
42    function options(extraData) {
43        return {
44            minLength: 2,
45            source: function (request, response) {
46                var data = {
47                    term: request.term,
48                    lang: typeof CURRENTLANG !== 'undefined' ? CURRENTLANG : ''
49                };
50                if (typeof extraData === 'function') {
51                    var extra = extraData() || {};
52                    for (var k in extra) if (Object.prototype.hasOwnProperty.call(extra, k)) data[k] = extra[k];
53                }
54                $.ajax({
55                    url: (typeof HOMEURL !== 'undefined' ? HOMEURL : '/') + ENDPOINT,
56                    dataType: 'json',
57                    data: data,
58                    success: function (items) {
59                        response($.map(items, function (item) {
60                            return {
61                                type: item.type,
62                                typeText: item.typeText,
63                                label: item.value,
64                                value: item.value,
65                                url: item.url
66                            };
67                        }));
68                    },
69                    // Never leave the widget spinning on a failed request.
70                    error: function () { response([]); }
71                });
72            },
73            open: function () {
74                var $input  = $(this);
75                var $widget = $input.autocomplete('widget');
76
77                // Narrow fields (the sticky header's is ~160px) truncate every suggestion, so the
78                // menu gets a comfortable minimum width instead of matching the input. It grows
79                // away from the field's start edge — leftwards in RTL — so that edge stays
80                // aligned with the input and only the far edge moves.
81                var inputWidth = $input.outerWidth();
82                var maxWidth   = $(window).width() - (VIEWPORT_MARGIN * 2);
83                var width      = Math.min(Math.max(MIN_MENU_WIDTH, inputWidth), maxWidth);
84
85                var offset = $input.offset();
86                var rtl    = (window.getComputedStyle(this).direction === 'rtl');
87                var left   = rtl ? (offset.left + inputWidth - width) : offset.left;
88
89                // Keep it on screen on narrow viewports.
90                var scrollX = $(window).scrollLeft();
91                left = Math.max(scrollX + VIEWPORT_MARGIN,
92                                Math.min(left, scrollX + $(window).width() - VIEWPORT_MARGIN - width));
93
94                var css = { 'width': width + 'px', 'transform': 'none' };
95                // The sticky header sits at a very high z-index, so a default-stacked menu has
96                // its first rows hidden behind it. Lift the menu above whichever fixed/sticky
97                // ancestor the field lives in; fields in normal flow keep the default stacking.
98                var z = stickyAncestorZIndex(this);
99                if (z !== null) css['z-index'] = z + 1;
100
101                $widget.css(css).offset({ top: $widget.offset().top, left: left });
102            },
103            select: function (event, ui) {
104                $(this).val($('<div/>').html(ui.item.value).text());
105                if (ui.item.url) window.location.href = ui.item.url;
106                else event.preventDefault();
107            }
108        };
109    }
110
111    /** Apply the widget, including the shared suggestion markup. */
112    function init(input, extraData) {
113        var $input = $(input);
114        if (!$input.length || $input.data('uiAutocomplete')) return $input;   // never double-bind
115        $input.autocomplete(options(extraData));
116        $input.data('ui-autocomplete')._renderItem = function (ul, item) {
117            return $('<li>')
118                .append('<a class="' + item.type + '"><span>' + item.typeText + '</span> - ' + item.label + '</a>')
119                .appendTo(ul);
120        };
121        return $input;
122    }
123
124    window.cilInitSearchAutocomplete = init;
125
126    $(function () {
127        // Every site search box except the catalog one, which common.js wires itself because it
128        // also carries filter state and its own required-field error handling.
129        $('input[name="srch"]').each(function () {
130            if (this.id === 'newCatalogSearchText') return;
131            init(this);
132        });
133    });
134
135})(window.jQuery);

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.