PageSourceSearch

https://top15.org.il/wp-content/themes/shitufim_etyhadar/assets/js/fSelect.js

js top15.org.il collected 2026-10-06 17:43:13 UTC 15,194 bytes, 426 lines download raw bytes

1(function($) {
2
3    String.prototype.unaccented = function() {
4        var accent = [
5            /[\300-\306]/g, /[\340-\346]/g, // A, a
6            /[\310-\313]/g, /[\350-\353]/g, // E, e
7            /[\314-\317]/g, /[\354-\357]/g, // I, i
8            /[\322-\330]/g, /[\362-\370]/g, // O, o
9            /[\331-\334]/g, /[\371-\374]/g, // U, u
10            /[\321]/g, /[\361]/g, // N, n
11            /[\307]/g, /[\347]/g, // C, c
12        ];
13        var noaccent = ['A','a','E','e','I','i','O','o','U','u','N','n','C','c'];
14
15        var str = this;
16        for (var i = 0; i < accent.length; i++) {
17            str = str.replace(accent[i], noaccent[i]);
18        }
19
20        return str;
21    }
22
23    $.fn.fSelect = function(options) {
24
25        if ('string' === typeof options) {
26            var settings = options;
27        }
28        else {
29            var settings = $.extend({
30                placeholder: 'Select some options',
31                numDisplayed: 3,
32                overflowText: '{n} selected',
33                searchText: 'Search',
34                noResultsText: 'No results found',
35                showSearch: true,
36                optionFormatter: false
37            }, options);
38        }
39
40
41        /**
42         * Constructor
43         */
44        function fSelect(select, settings) {
45            this.$select = $(select);
46            this.settings = settings;
47            this.create();
48        }
49
50
51        /**
52         * Prototype class
53         */
54        fSelect.prototype = {
55            create: function() {
56                this.settings.multiple = this.$select.is('[multiple]');
57                var multiple = this.settings.multiple ? ' multiple' : '';
58                this.$select.wrap('<div class="fs-wrap' + multiple + '" tabindex="0" />');
59                this.$select.before('<div class="fs-label-wrap"><div class="fs-label">' + this.settings.placeholder + '</div><span class="fs-arrow"></span></div>');
60                this.$select.before('<div class="fs-dropdown hidden"><div class="fs-options"></div></div>');
61                this.$select.addClass('hidden');
62                this.$wrap = this.$select.closest('.fs-wrap');
63                this.$wrap.data('id', window.fSelect.num_items);
64                window.fSelect.num_items++;
65                this.reload();
66            },
67
68            reload: function() {
69                if (this.settings.showSearch) {
70                    var search = '<div class="fs-search"><input type="search" placeholder="' + this.settings.searchText + '" /></div>';
71                    this.$wrap.find('.fs-dropdown').prepend(search);
72                }
73                if ('' !== this.settings.noResultsText) {
74                    var no_results_text = '<div class="fs-no-results hidden">' + this.settings.noResultsText + '</div>';
75                    this.$wrap.find('.fs-options').before(no_results_text);
76                }
77                this.idx = 0;
78                this.optgroup = 0;
79                this.selected = [].concat(this.$select.val()); // force an array
80                var choices = this.buildOptions(this.$select);
81                this.$wrap.find('.fs-options').html(choices);
82                this.reloadDropdownLabel();
83            },
84
85            destroy: function() {
86                this.$wrap.find('.fs-label-wrap').remove();
87                this.$wrap.find('.fs-dropdown').remove();
88                this.$select.unwrap().removeClass('hidden');
89            },
90
91            buildOptions: function($element) {
92                var $this = this;
93
94                var choices = '';
95                $element.children().each(function(i, el) {
96                    var $el = $(el);
97
98                    if ('optgroup' == $el.prop('nodeName').toLowerCase()) {
99                        choices += '<div class="fs-optgroup-label" data-group="' + $this.optgroup + '">' + $el.prop('label') + '</div>';
100                        choices += $this.buildOptions($el);
101                        $this.optgroup++;
102                    }
103                    else {
104                        var val = $el.prop('value');
105
106                        // exclude the first option in multi-select mode
107                        if (0 < $this.idx || '' != val || ! $this.settings.multiple) {
108                            var disabled = $el.is(':disabled') ? ' disabled' : '';
109                            var selected = -1 < $.inArray(val, $this.selected) ? ' selected' : '';
110                            var group = ' g' + $this.optgroup;
111                            var row = '<div class="fs-option' + selected + disabled + group + '" data-value="' + val + '" data-index="' + $this.idx + '"><span class="fs-checkbox"><i></i></span><div class="fs-option-label">' + $el.html() + '</div></div>';
112
113                            if ('function' === typeof $this.settings.optionFormatter) {
114                                row = $this.settings.optionFormatter(row);
115                            }
116
117                            choices += row;
118                            $this.idx++;
119                        }
120                    }
121                });
122
123                return choices;
124            },
125
126            reloadDropdownLabel: function() {
127                var settings = this.settings;
128                var labelText = [];
129
130                this.$wrap.find('.fs-option.selected').each(function(i, el) {
131                    labelText.push($(el).find('.fs-option-label').html());
132                });
133
134                if (labelText.length < 1) {
135                    labelText = settings.placeholder;
136                }
137                else if (labelText.length > settings.numDisplayed) {
138                    labelText = settings.overflowText.replace('{n}', labelText.length);
139                }
140                else {
141                    labelText = labelText.join(', ');
142                }
143
144                this.$wrap.find('.fs-label').html(labelText);
145                this.$wrap.toggleClass('fs-default', labelText === settings.placeholder);
146                this.$select.change();
147            }
148        }
149
150
151        /**
152         * Loop through each matching element
153         */
154        return this.each(function() {
155            var data = $(this).data('fSelect');
156
157            if (!data) {
158                data = new fSelect(this, settings);
159                $(this).data('fSelect', data);
160            }
161
162            if ('string' === typeof settings) {
163                data[settings]();
164            }
165        });
166    }
167
168
169    /**
170     * Events
171     */
172    window.fSelect = {
173        'num_items': 0,
174        'active_id': null,
175        'active_el': null,
176        'last_choice': null,
177        'idx': -1
178    };
179
180    $(document).on('click', '.fs-option:not(.hidden, .disabled)', function(e) {
181        var $wrap = $(this).closest('.fs-wrap');
182        var do_close = false;
183
184        // prevent selections
185        if ($wrap.hasClass('fs-disabled')) {
186            return;
187        }
188
189        if ($wrap.hasClass('multiple')) {
190            var selected = [];
191
192            // shift + click support
193            if (e.shiftKey && null != window.fSelect.last_choice) {
194                var current_choice = parseInt($(this).attr('data-index'));
195                var addOrRemove = ! $(this).hasClass('selected');
196                var min = Math.min(window.fSelect.last_choice, current_choice);
197                var max = Math.max(window.fSelect.last_choice, current_choice);
198
199                for (i = min; i <= max; i++) {
200                    $wrap.find('.fs-option[data-index='+ i +']')
201                        .not('.hidden, .disabled')
202                        .each(function() {
203                            $(this).toggleClass('selected', addOrRemove);
204                        });
205                }
206            }
207            else {
208                window.fSelect.last_choice = parseInt($(this).attr('data-index'));
209                $(this).toggleClass('selected');
210            }
211
212            $wrap.find('.fs-option.selected').each(function(i, el) {
213                selected.push($(el).attr('data-value'));
214            });
215        }
216        else {
217            var selected = $(this).attr('data-value');
218            $wrap.find('.fs-option').removeClass('selected');
219            $(this).addClass('selected');
220            do_close = true;
221        }
222
223        $wrap.find('select').val(selected);
224        $wrap.find('select').fSelect('reloadDropdownLabel');
225
226        // fire an event
227        $(document).trigger('fs:changed', $wrap);
228
229        if (do_close) {
230            closeDropdown($wrap);
231        }
232    });
233
234    $(document).on('keyup', '.fs-search input', function(e) {
235        if (40 == e.which) { // down
236            $(this).blur();
237            return;
238        }
239
240        var $wrap = $(this).closest('.fs-wrap');
241        var matchOperators = /[|\\{}()[\]^$+*?.]/g;
242        var keywords = $(this).val().replace(matchOperators, '\\$&');
243
244        $wrap.find('.fs-option, .fs-optgroup-label').removeClass('hidden');
245
246        if ('' != keywords) {
247            $wrap.find('.fs-option').each(function() {
248                var regex = new RegExp(keywords.unaccented(), 'gi');
249                var formatedValue = $(this).find('.fs-option-label').text().unaccented();
250
251                if (null === formatedValue.match(regex)) {
252                    $(this).addClass('hidden');
253                }
254            });
255
256            $wrap.find('.fs-optgroup-label').each(function() {
257                var group = $(this).attr('data-group');
258                var num_visible = $(this).closest('.fs-options').find('.fs-option.g' + group + ':not(.hidden)').length;
259                if (num_visible < 1) {
260                    $(this).addClass('hidden');
261                }
262            });
263        }
264
265        setIndexes($wrap);
266        checkNoResults($wrap);
267    });
268
269    $(document).on('click', function(e) {
270        var $el = $(e.target);
271        var $wrap = $el.closest('.fs-wrap');
272
273        if (0 < $wrap.length) {
274
275            // user clicked another fSelect box
276            if ($wrap.data('id') !== window.fSelect.active_id) {
277                closeDropdown();
278            }
279
280            // fSelect box was toggled
281            if ($el.hasClass('fs-label') || $el.hasClass('fs-arrow')) {
282                var is_hidden = $wrap.find('.fs-dropdown').hasClass('hidden');
283
284                if (is_hidden) {
285                    openDropdown($wrap);
286                }
287                else {
288                    closeDropdown($wrap);
289                }
290            }
291        }
292        // clicked outside, close all fSelect boxes
293        else {
294            closeDropdown();
295        }
296    });
297
298    $(document).on('keydown', function(e) {
299        var $wrap = window.fSelect.active_el;
300        var $target = $(e.target);
301
302        // toggle the dropdown on space
303        if ($target.hasClass('fs-wrap')) {
304            if (32 == e.which || 13 == e.which) {
305                e.preventDefault();
306                $target.find('.fs-label').trigger('click');
307                return;
308            }
309        }
310        // preserve spaces during search
311        else if (0 < $target.closest('.fs-search').length) {
312            if (32 == e.which) {
313                return;
314            }
315        }
316        else if (null === $wrap) {
317            return;
318        }
319
320        if (38 == e.which) { // up
321            e.preventDefault();
322
323            $wrap.find('.fs-option.hl').removeClass('hl');
324
325            var $current = $wrap.find('.fs-option[data-index=' + window.fSelect.idx + ']');
326            var $prev = $current.prevAll('.fs-option:not(.hidden, .disabled)');
327
328            if ($prev.length > 0) {
329                window.fSelect.idx = parseInt($prev.attr('data-index'));
330                $wrap.find('.fs-option[data-index=' + window.fSelect.idx + ']').addClass('hl');
331                setScroll($wrap);
332            }
333            else {
334                window.fSelect.idx = -1;
335                $wrap.find('.fs-search input').focus();
336            }
337        }
338        else if (40 == e.which) { // down
339            e.preventDefault();
340
341            var $current = $wrap.find('.fs-option[data-index=' + window.fSelect.idx + ']');
342            if ($current.length < 1) {
343                var $next = $wrap.find('.fs-option:not(.hidden, .disabled):first');
344            }
345            else {
346                var $next = $current.nextAll('.fs-option:not(.hidden, .disabled)');
347            }
348
349            if ($next.length > 0) {
350                window.fSelect.idx = parseInt($next.attr('data-index'));
351                $wrap.find('.fs-option.hl').removeClass('hl');
352                $wrap.find('.fs-option[data-index=' + window.fSelect.idx + ']').addClass('hl');
353                setScroll($wrap);
354            }
355        }
356        else if (32 == e.which || 13 == e.which) { // space, enter
357            e.preventDefault();
358
359            $wrap.find('.fs-option.hl').click();
360        }
361        else if (27 == e.which) { // esc
362            closeDropdown($wrap);
363        }
364    });
365
366    function checkNoResults($wrap) {
367        var addOrRemove = $wrap.find('.fs-option:not(.hidden)').length > 0;
368        $wrap.find('.fs-no-results').toggleClass('hidden', addOrRemove);
369    }
370
371    function setIndexes($wrap) {
372        $wrap.find('.fs-option.hl').removeClass('hl');
373        $wrap.find('.fs-search input').focus();
374        window.fSelect.idx = -1;
375    }
376
377    function setScroll($wrap) {
378        var $container = $wrap.find('.fs-options');
379        var $selected = $wrap.find('.fs-option.hl');
380
381        var itemMin = $selected.offset().top + $container.scrollTop();
382        var itemMax = itemMin + $selected.outerHeight();
383        var containerMin = $container.offset().top + $container.scrollTop();
384        var containerMax = containerMin + $container.outerHeight();
385
386        if (itemMax > containerMax) { // scroll down
387            var to = $container.scrollTop() + itemMax - containerMax;
388            $container.scrollTop(to);
389        }
390        else if (itemMin < containerMin) { // scroll up
391            var to = $container.scrollTop() - containerMin - itemMin;
392            $container.scrollTop(to);
393        }
394    }
395
396    function openDropdown($wrap) {
397        window.fSelect.active_el = $wrap;
398        window.fSelect.active_id = $wrap.data('id');
399        window.fSelect.initial_values = $wrap.find('select').val();
400        $wrap.find('.fs-dropdown').removeClass('hidden');
401        $wrap.addClass('fs-open');
402        setIndexes($wrap);
403        checkNoResults($wrap);
404    }
405
406    function closeDropdown($wrap) {
407        if ('undefined' == typeof $wrap && null != window.fSelect.active_el) {
408            $wrap = window.fSelect.active_el;
409        }
410        if ('undefined' !== typeof $wrap) {
411            // only trigger if the values have changed
412            var initial_values = window.fSelect.initial_values;
413            var current_values = $wrap.find('select').val();
414            if (JSON.stringify(initial_values) != JSON.stringify(current_values)) {
415                $(document).trigger('fs:closed', $wrap);
416            }
417        }
418
419        $('.fs-wrap').removeClass('fs-open');
420        $('.fs-dropdown').addClass('hidden');
421        window.fSelect.active_el = null;
422        window.fSelect.active_id = null;
423        window.fSelect.last_choice = null;
424    }
425
426})(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.