PageSourceSearch

https://www.visitmo.com/wp-content/plugins/facetwp/assets/vendor/fComplete/fComplete.js?ver=4.3.4

js visitmo.com collected 2026-10-02 03:05:08 UTC 9,895 bytes, 283 lines download raw bytes

1window.fComplete = (() => {
2
3    class fComplete {
4
5        constructor(selector, options) {
6            let that = this;
7
8            var defaults = {
9                data: [],
10                minChars: 3,
11                maxResults: 10,
12                searchDelay: 200,
13                loadingText: 'Loading...',
14                minCharsText: 'Enter {n} or more characters',
15                noResultsText: 'No results',
16                beforeRender: null,
17                onSelect: null
18            };
19
20            that.settings = Object.assign({}, defaults, options);
21            that.settings.minChars = Math.max(1, that.settings.minChars);
22            that.settings.maxResults = Math.max(1, that.settings.maxResults);
23            that.settings.searchDelay = Math.max(0, that.settings.searchDelay);
24
25            if ('string' === typeof selector) {
26                var nodes = Array.from(document.querySelectorAll(selector));
27            }
28            else if (selector instanceof Node) {
29                var nodes = [selector];
30            }
31            else if (Array.isArray(selector)) {
32                var nodes = selector;
33            }
34            else {
35                var nodes = [];
36            }
37
38            if ('undefined' === typeof window.fCompleteInit) {
39                window.fCompleteInit = {
40                    lastFocus: null,
41                    eventsBound: true
42                };
43                that.bindEvents();
44            }
45
46            nodes.forEach((input) => {
47                that.input = input;
48                input.fcomplete = that;
49                input.classList.add('fcomplete-enabled');
50                that.create();
51            });
52        }
53
54        create() {
55            var that = this;
56
57            var html = `
58            <div class="fcomplete-wrap fcomplete-hidden">
59                <div class="fcomplete-status"></div>
60                <div class="fcomplete-results"></div>
61            </div>
62            `;
63
64            var rect = that.input.getBoundingClientRect();
65
66            var tpl = document.createElement('template');
67            tpl.innerHTML = html;
68            var wrap = tpl.content.querySelector('.fcomplete-wrap');
69            wrap.style.minWidth = rect.width + 'px';
70            that.input.parentNode.insertBefore(wrap, that.input.nextSibling);
71
72            // add a relationship link
73            that.input._rel = wrap;
74            wrap._rel = that.input;
75        }
76
77        destroy() {
78            this.input._rel.remove();
79            delete this.input._rel;
80        }
81
82        reload() {
83            this.destroy();
84            this.create();
85        }
86
87        open() {
88            this.input._rel.classList.remove('fcomplete-hidden');
89        }
90
91        close() {
92            window.fCompleteInit.lastFocus = null;
93            this.input._rel.classList.add('fcomplete-hidden');
94        }
95
96        setStatus(text) {
97            var text = text.replace('{n}', this.settings.minChars);
98            var node = this.input._rel.querySelector('.fcomplete-status');
99            node.innerHTML = text;
100
101            var method = (text) ? 'remove' : 'add';
102            node.classList[method]('fcomplete-hidden');
103        }
104
105        render(data) {
106            var data = (this.settings.beforeRender) ? this.settings.beforeRender(data) : data;
107            var wrap = this.input._rel;
108
109            if (data.length) {
110                var html = '';
111                var len = Math.min(data.length, this.settings.maxResults);
112
113                for (var i = 0; i < len; i++) {
114                    html += `<div class="fcomplete-result" data-value="${data[i].value}" tabindex="-1">${data[i].label}</div>`;
115                }
116
117                wrap.querySelector('.fcomplete-results').innerHTML = html;
118                this.setStatus('');
119            }
120            else {
121                wrap.querySelector('.fcomplete-results').innerHTML = '';
122                this.setStatus(this.settings.noResultsText);
123            }
124
125            this.input.fcomplete.open();
126        }
127
128        getAdjacentSibling(which) {
129            var that = this;
130            var which = which || 'next';
131            var sibling = window.fCompleteInit.lastFocus;
132            var selector = '.fcomplete-result';
133
134            if (sibling) {
135                sibling = sibling[which + 'ElementSibling'];
136
137                while (sibling) {
138                    if (sibling.matches(selector)) break;
139                    sibling = sibling[which + 'ElementSibling'];
140                }
141
142                return sibling;
143            }
144            else if ('next' == which) {
145                sibling = that.input._rel.querySelector(selector);
146            }
147
148            return sibling;
149        }
150
151        debounce(func, wait) {
152            var timeout;
153            return (...args) => {
154                var boundFunc = func.bind(this, ...args);
155                clearTimeout(timeout);
156                timeout = setTimeout(boundFunc, wait);
157            }
158        }
159
160        trigger(eventName, ...args) {
161            document.dispatchEvent(new CustomEvent(eventName, {detail: [...args]}));
162        }
163
164        on(eventName, elementSelector, handler) {
165            document.addEventListener(eventName, function(e) {
166                // loop parent nodes from the target to the delegation node
167                for (var target = e.target; target && target != this; target = target.parentNode) {
168                    if (target.matches(elementSelector)) {
169                        handler.call(target, e);
170                        break;
171                    }
172                }
173            }, false);
174        }
175
176        bindEvents() {
177            let that = this;
178
179            that.on('click', '*', function(e) {
180                var wrap = this.closest('.fcomplete-wrap');
181                var isInput = this.classList.contains('fcomplete-enabled');
182
183                if (isInput) {
184                    var input = this;
185                    var settings = input.fcomplete.settings;
186                    var status = (settings.minChars > input.value.length) ? settings.minCharsText : '';
187                    input.fcomplete.setStatus(status);
188                    this.fcomplete.open();
189                }
190                else if (!wrap && !isInput) {
191                    document.querySelectorAll('.fcomplete-wrap').forEach((node) => node.classList.add('fcomplete-hidden'));
192                }
193            });
194
195            that.on('click', '.fcomplete-result', function(e) {
196                var wrap = e.target.closest('.fcomplete-wrap');
197                var input = wrap._rel;
198                input.value = e.target.getAttribute('data-value');
199
200                if (typeof input.fcomplete.settings.onSelect === 'function') {
201                    input.fcomplete.settings.onSelect();
202                }
203
204                input.fcomplete.close();
205            });
206            that.on('keydown', '*', function(e) {
207                var wrap = this.closest('.fcomplete-wrap');
208                var isInput = this.classList.contains('fcomplete-enabled');
209
210                if (!wrap && !isInput) return;
211
212                var input = (wrap) ? wrap._rel : this;
213                wrap = (wrap) ? wrap : input._rel;
214
215                if (-1 < [13, 38, 40, 27].indexOf(e.which)) {
216                    e.preventDefault();
217                }
218
219                if (13 == e.which) { // enter
220                    if (this.classList.contains('fcomplete-result')) {
221                        this.click();
222                    }
223                }
224                else if (38 == e.which) { // up
225                    if (this.classList.contains('fcomplete-result')) {
226                        var sibling = wrap._rel.fcomplete.getAdjacentSibling('previous');
227                        window.fCompleteInit.lastFocus = sibling; // stop at the search box
228                        (sibling) ? sibling.focus() : input.focus();
229                    }
230                }
231                else if (40 == e.which) { // down
232                    if (this === input) {
233                        var firstResult = wrap.querySelector('.fcomplete-result');
234
235                        if (firstResult) {
236                            firstResult.focus();
237                            window.fCompleteInit.lastFocus = firstResult;
238                        }
239                    }
240                    else if (this.classList.contains('fcomplete-result')) {
241                        var sibling = wrap._rel.fcomplete.getAdjacentSibling('next');
242
243                        if (sibling) {
244                            sibling.focus();
245                            window.fCompleteInit.lastFocus = sibling; // stop at the bottom
246                        }
247                    }
248                }
249                else if (9 == e.which || 27 == e.which) { // tab, esc
250                    wrap._rel.fcomplete.close();
251                }
252            });
253
254            that.on('keyup', '.fcomplete-enabled', that.debounce(function(e) {
255                if (-1 < [13, 38, 40, 27].indexOf(e.which)) {
256                    return;
257                }
258
259                var input = e.target;
260                var settings = input.fcomplete.settings;
261
262                if (settings.minChars  <= input.value.length) {
263                    if (Array.isArray(settings.data)) {
264                        input.fcomplete.render(settings.data);
265                    }
266                    else if (typeof settings.data === 'function') {
267                        input.fcomplete.setStatus(settings.loadingText);
268                        settings.data();
269                    }
270                }
271                else {
272                    input.fcomplete.render([]);
273                    input.fcomplete.setStatus(settings.minCharsText);
274                }
275            }, that.settings.searchDelay));
276        }
277    }
278
279    var $ = (selector, options) => new fComplete(selector, options);
280
281    return $;
282
283})();

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.