PageSourceSearch

https://www.artisanduvegetal-feurs.fr/UiCommon/Scripts/bootstrap-typeahead/bootstrap-typeahead.js

js artisanduvegetal-feurs.fr collected 2026-09-27 13:57:58 UTC 18,864 bytes, 537 lines download raw bytes

1/*!
2 * bootstrap-typeahead.js v0.0.5 (http://www.upbootstrap.com)
3 * Copyright 2012-2015 Twitter Inc.
4 * Licensed under MIT (https://github.com/biggora/bootstrap-ajax-typeahead/blob/master/LICENSE)
5 * See Demo: http://plugins.upbootstrap.com/bootstrap-ajax-typeahead
6 * Updated: 2015-04-05 11:43:56
7 *
8 * Modifications by Paul Warelis and Alexey Gordeyev
9 */
10!function ($) {
11    "use strict"; // jshint ;_;
12
13    /* TYPEAHEAD PUBLIC CLASS DEFINITION
14     * ================================= */
15
16    var Typeahead = function (element, options) {
17        //deal with scrollBar
18        var defaultOptions = $.fn.typeahead.defaults;
19        if (options.scrollBar) {
20            options.items = 100;
21            options.menu = '<ul class="typeahead dropdown-menu" style="max-height:220px;overflow:auto;"></ul>';
22        }
23
24        var that = this;
25        that.$element = $(element);
26        that.options = $.extend({}, $.fn.typeahead.defaults, options);
27        that.$menu = $(that.options.menu).insertAfter(that.$element);
28
29        // Method overrides
30        that.eventSupported = that.options.eventSupported || that.eventSupported;
31        that.grepper = that.options.grepper || that.grepper;
32        that.highlighter = that.options.highlighter || that.highlighter;
33        that.lookup = that.options.lookup || that.lookup;
34        that.matcher = that.options.matcher || that.matcher;
35        that.render = that.options.render || that.render;
36        that.onSelect = that.options.onSelect || null;
37        that.sorter = that.options.sorter || that.sorter;
38        that.source = that.options.source || that.source;
39        that.displayField = that.options.displayField || that.displayField;
40        that.valueField = that.options.valueField || that.valueField;
41
42        if (that.options.ajax) {
43            var ajax = that.options.ajax;
44
45            if (typeof ajax === 'string') {
46                that.ajax = $.extend({}, $.fn.typeahead.defaults.ajax, {
47                    url: ajax
48                });
49            } else {
50                if (typeof ajax.displayField === 'string') {
51                    that.displayField = that.options.displayField = ajax.displayField;
52                }
53                if (typeof ajax.valueField === 'string') {
54                    that.valueField = that.options.valueField = ajax.valueField;
55                }
56
57                that.ajax = $.extend({}, $.fn.typeahead.defaults.ajax, ajax);
58            }
59
60            if (!that.ajax.url) {
61                that.ajax = null;
62            }
63            that.query = "";
64        } else {
65            that.source = that.options.source;
66            that.ajax = null;
67        }
68        that.shown = false;
69        that.listen();
70    };
71
72    Typeahead.prototype = {
73        constructor: Typeahead,
74        //=============================================================================================================
75        //  Utils
76        //  Check if an event is supported by the browser eg. 'keypress'
77        //  * This was included to handle the "exhaustive deprecation" of jQuery.browser in jQuery 1.8
78        //=============================================================================================================
79        eventSupported: function (eventName) {
80            var isSupported = (eventName in this.$element);
81
82            if (!isSupported) {
83                this.$element.setAttribute(eventName, 'return;');
84                isSupported = typeof this.$element[eventName] === 'function';
85            }
86
87            return isSupported;
88        },
89        select: function () {
90            var $selectedItem = this.$menu.find('.active');
91            if ($selectedItem.length) {
92                var value = $selectedItem.attr('data-value');
93                var text = this.$menu.find('.active a').text();
94
95                if (this.options.onSelect) {
96                    this.options.onSelect({
97                        value: value,
98                        text: text
99                    });
100                }
101                this.$element
102                    .val(this.updater(text))
103                    .change();
104            }
105            return this.hide();
106        },
107        updater: function (item) {
108            return item;
109        },
110        show: function () {
111            var pos = $.extend({}, this.$element.position(), {
112                height: this.$element[0].offsetHeight
113            });
114
115            this.$menu.css({
116                top: pos.top + pos.height,
117                left: pos.left
118            });
119
120            if (this.options.alignWidth) {
121                var width = $(this.$element[0]).outerWidth();
122                this.$menu.css({
123                    width: width
124                });
125            }
126
127            new Tether({
128                element: this.$menu[0],
129                target: this.$element[0],
130                attachment: 'top left',
131                targetAttachment: 'bottom left',
132                constraints: [
133                  {
134                      to: 'scrollParent',
135                      attachment: 'together',
136                      pin: true
137                  }
138                ]
139            })
140            this.$menu.show();
141            this.shown = true;
142            return this;
143        },
144        hide: function () {
145            this.$menu.hide();
146            this.shown = false;
147            return this;
148        },
149        ajaxLookup: function () {
150            var query = $.trim(this.$element.val());
151
152            //if (query === this.query) {
153            //    return this;
154            //}
155
156            // Query changed
157            this.query = query;
158
159            // Cancel last timer if set
160            if (this.ajax.timerId) {
161                clearTimeout(this.ajax.timerId);
162                this.ajax.timerId = null;
163            }
164
165            if (!query || query.length < this.ajax.triggerLength) {
166                // cancel the ajax callback if in progress
167                if (this.ajax.xhr) {
168                    this.ajax.xhr.abort();
169                    this.ajax.xhr = null;
170                    this.ajaxToggleLoadClass(false);
171                }
172
173                return this.shown ? this.hide() : this;
174            }
175
176            function execute() {
177                this.ajaxToggleLoadClass(true);
178
179                // Cancel last call if already in progress
180                if (this.ajax.xhr)
181                    this.ajax.xhr.abort();
182
183                var params = this.ajax.preDispatch ? this.ajax.preDispatch(query) : {
184                    query: query
185                };
186                this.ajax.xhr = $.ajax({
187                    url: this.ajax.url,
188                    data: params,
189                    success: $.proxy(this.ajaxSource, this),
190                    type: this.ajax.method || 'get',
191                    dataType: 'json'
192                });
193                this.ajax.timerId = null;
194            }
195
196            // Query is good to send, set a timer
197            this.ajax.timerId = setTimeout($.proxy(execute, this), this.ajax.timeout);
198
199            return this;
200        },
201        ajaxSource: function (data) {
202            this.ajaxToggleLoadClass(false);
203            var that = this, items;
204            if (!that.ajax.xhr)
205                return;
206            if (that.ajax.preProcess) {
207                data = that.ajax.preProcess(data);
208            }
209            // Save for selection retreival
210            that.ajax.data = data;
211
212            // Manipulate objects
213            items = that.grepper(that.ajax.data) || [];
214            if (!items.length) {
215                return that.shown ? that.hide() : that;
216            }
217
218            that.ajax.xhr = null;
219            return that.render(items.slice(0, that.options.items)).show();
220        },
221        ajaxToggleLoadClass: function (enable) {
222            if (!this.ajax.loadingClass)
223                return;
224            this.$element.toggleClass(this.ajax.loadingClass, enable);
225        },
226        lookup: function (event) {
227            var that = this, items;
228            if (that.ajax) {
229                that.ajaxLookup();
230            }
231            else {
232                that.query = that.$element.val();
233
234                if (!that.query) {
235                    return that.shown ? that.hide() : that;
236                }
237
238                items = that.grepper(that.source);
239
240                if (!items) {
241                    return that.shown ? that.hide() : that;
242                }
243                //Bhanu added a custom message- Result not Found when no result is found
244                if (items.length == 0) {
245                    items[0] = { 'id': -21, 'name': "Result not Found" }
246                }
247                return that.render(items.slice(0, that.options.items)).show();
248            }
249        },
250        matcher: function (item) {
251            return ~item.toLowerCase().indexOf(this.query.toLowerCase());
252        },
253        sorter: function (items) {
254            if (!this.options.ajax) {
255                var beginswith = [],
256                    caseSensitive = [],
257                    caseInsensitive = [],
258                    item;
259
260                while (item = items.shift()) {
261                    if (!item.toLowerCase().indexOf(this.query.toLowerCase()))
262                        beginswith.push(item);
263                    else if (~item.indexOf(this.query))
264                        caseSensitive.push(item);
265                    else
266                        caseInsensitive.push(item);
267                }
268
269                return beginswith.concat(caseSensitive, caseInsensitive);
270            } else {
271                return items;
272            }
273        },
274        highlighter: function (item) {
275            var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&');
276            return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) {
277                return '<strong>' + match + '</strong>';
278            });
279        },
280        render: function (items) {
281            var that = this, display, isString = typeof that.options.displayField === 'string';
282
283            items = $(items).map(function (i, item) {
284                if (typeof item === 'object') {
285                    display = isString ? item[that.options.displayField] : that.options.displayField(item);
286                    i = $(that.options.item).attr('data-value', item[that.options.valueField]);
287                } else {
288                    display = item;
289                    i = $(that.options.item).attr('data-value', item);
290                }
291                i.find('a').html(that.highlighter(display));
292                return i[0];
293            });
294
295            if (that.options.autoselect) items.first().addClass('active');
296
297            this.$menu.html(items);
298            return this;
299        },
300        //------------------------------------------------------------------
301        //  Filters relevent results
302        //
303        grepper: function (data) {
304            var that = this, items, display, isString = typeof that.options.displayField === 'string';
305
306            if (isString && data && data.length) {
307                if (data[0].hasOwnProperty(that.options.displayField)) {
308                    items = $.grep(data, function (item) {
309                        display = isString ? item[that.options.displayField] : that.options.displayField(item);
310                        return that.matcher(display);
311                    });
312                } else if (typeof data[0] === 'string') {
313                    items = $.grep(data, function (item) {
314                        return that.matcher(item);
315                    });
316                } else {
317                    return null;
318                }
319            } else {
320                return null;
321            }
322            return this.sorter(items);
323        },
324        next: function (event) {
325            var active = this.$menu.find('.active').removeClass('active'),
326                next = active.next();
327
328            if (!next.length) {
329                next = $(this.$menu.find('li')[0]);
330            }
331
332            if (this.options.scrollBar) {
333                var index = this.$menu.children("li").index(next);
334                if (index % 8 == 0) {
335                    this.$menu.scrollTop(index * 26);
336                }
337            }
338
339            next.addClass('active');
340        },
341        prev: function (event) {
342            var active = this.$menu.find('.active').removeClass('active'),
343                prev = active.prev();
344
345            if (!prev.length) {
346                prev = this.$menu.find('li').last();
347            }
348
349            if (this.options.scrollBar) {
350                var $li = this.$menu.children("li");
351                var total = $li.length - 1;
352                var index = $li.index(prev);
353
354                if ((total - index) % 8 == 0) {
355                    this.$menu.scrollTop((index - 7) * 26);
356                }
357            }
358
359            prev.addClass('active');
360        },
361        listen: function () {
362            this.$element
363                .on('focus', $.proxy(this.focus, this))
364                .on('blur', $.proxy(this.blur, this))
365                .on('keypress', $.proxy(this.keypress, this))
366                .on('keyup', $.proxy(this.keyup, this));
367
368            if (this.eventSupported('keydown')) {
369                this.$element.on('keydown', $.proxy(this.keydown, this))
370            }
371
372            this.$menu
373                .on('click', $.proxy(this.click, this))
374                .on('mouseenter', 'li', $.proxy(this.mouseenter, this))
375                .on('mouseleave', 'li', $.proxy(this.mouseleave, this))
376        },
377        move: function (e) {
378            if (!this.shown)
379                return
380
381            switch (e.keyCode) {
382                case 9: // tab
383                case 13: // enter
384                case 27: // escape
385                    e.preventDefault();
386                    break
387
388                case 38: // up arrow
389                    e.preventDefault()
390                    this.prev()
391                    break
392
393                case 40: // down arrow
394                    e.preventDefault()
395                    this.next()
396                    break
397            }
398
399            e.stopPropagation();
400        },
401        keydown: function (e) {
402            this.suppressKeyPressRepeat = ~$.inArray(e.keyCode, [40, 38, 9, 13, 27])
403            this.move(e)
404        },
405        keypress: function (e) {
406            if (this.suppressKeyPressRepeat)
407                return
408            this.move(e)
409        },
410        keyup: function (e) {
411            switch (e.keyCode) {
412                case 40: // down arrow
413                case 38: // up arrow
414                case 16: // shift
415                case 17: // ctrl
416                case 18: // alt
417                    break
418
419                case 9: // tab
420                case 13: // enter
421                    if (!this.shown)
422                        return
423                    this.select()
424                    break
425
426                case 27: // escape
427                    if (!this.shown)
428                        return
429                    this.hide()
430                    break
431
432                default:
433                    if (this.ajax)
434                        this.ajaxLookup()
435                    else
436                        this.lookup()
437            }
438
439            e.stopPropagation()
440            e.preventDefault()
441        },
442        focus: function (e) {
443            this.focused = true
444        },
445        blur: function (e) {
446            this.focused = false
447            if (!this.mousedover && this.shown)
448                this.hide()
449        },
450        click: function (e) {
451            e.stopPropagation()
452            e.preventDefault()
453            this.select()
454            this.$element.focus()
455        },
456        mouseenter: function (e) {
457            this.mousedover = true
458            this.$menu.find('.active').removeClass('active')
459            $(e.currentTarget).addClass('active')
460        },
461        mouseleave: function (e) {
462            this.mousedover = false
463            if (!this.focused && this.shown)
464                this.hide()
465        },
466        destroy: function () {
467            this.$element
468                .off('focus', $.proxy(this.focus, this))
469                .off('blur', $.proxy(this.blur, this))
470                .off('keypress', $.proxy(this.keypress, this))
471                .off('keyup', $.proxy(this.keyup, this));
472
473            if (this.eventSupported('keydown')) {
474                this.$element.off('keydown', $.proxy(this.keydown, this))
475            }
476
477            this.$menu
478                .off('click', $.proxy(this.click, this))
479                .off('mouseenter', 'li', $.proxy(this.mouseenter, this))
480                .off('mouseleave', 'li', $.proxy(this.mouseleave, this))
481            this.$element.removeData('typeahead');
482        }
483    };
484
485    /* TYPEAHEAD PLUGIN DEFINITION
486     * =========================== */
487
488    $.fn.typeahead = function (option) {
489        return this.each(function () {
490            var $this = $(this),
491                data = $this.data('typeahead'),
492                options = typeof option === 'object' && option;
493            if (!data)
494                $this.data('typeahead', (data = new Typeahead(this, options)));
495            if (typeof option === 'string')
496                data[option]();
497        });
498    };
499
500    $.fn.typeahead.defaults = {
501        source: [],
502        items: 10,
503        scrollBar: false,
504        alignWidth: true,
505        menu: '<ul class="typeahead dropdown-menu"></ul>',
506        item: '<li><a href="#"></a></li>',
507        valueField: 'id',
508        displayField: 'name',
509        autoselect: true,
510        onSelect: function () {
511        },
512        ajax: {
513            url: null,
514            timeout: 300,
515            method: 'get',
516            triggerLength: 1,
517            loadingClass: null,
518            preDispatch: null,
519            preProcess: null
520        }
521    };
522
523    $.fn.typeahead.Constructor = Typeahead;
524
525    /* TYPEAHEAD DATA-API
526     * ================== */
527
528    $(function () {
529        $('body').on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) {
530            var $this = $(this);
531            if ($this.data('typeahead'))
532                return;
533            e.preventDefault();
534            $this.typeahead($this.data());
535        });
536    });
537}(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.