PageSourceSearch

https://bayalagbuteegch.mn/js/typeahead.js

js bayalagbuteegch.mn collected 2026-10-04 13:27:50 UTC 49,024 bytes, 1,140 lines download raw bytes

1/*!
2 * typeahead.js 0.9.3
3 * https://github.com/twitter/typeahead
4 * Copyright 2013 Twitter, Inc. and other contributors; Licensed MIT
5 */
vendor: 48,884 bytes, lines 6-1140
6
7
8(function($) {
9    var VERSION = "0.9.3";
10    var utils = {
11        isMsie: function() {
12            var match = /(msie) ([\w.]+)/i.exec(navigator.userAgent);
13            return match ? parseInt(match[2], 10) : false;
14        },
15        isBlankString: function(str) {
16            return !str || /^\s*$/.test(str);
17        },
18        escapeRegExChars: function(str) {
19            return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
20        },
21        isString: function(obj) {
22            return typeof obj === "string";
23        },
24        isNumber: function(obj) {
25            return typeof obj === "number";
26        },
27        isArray: $.isArray,
28        isFunction: $.isFunction,
29        isObject: $.isPlainObject,
30        isUndefined: function(obj) {
31            return typeof obj === "undefined";
32        },
33        bind: $.proxy,
34        bindAll: function(obj) {
35            var val;
36            for (var key in obj) {
37                $.isFunction(val = obj[key]) && (obj[key] = $.proxy(val, obj));
38            }
39        },
40        indexOf: function(haystack, needle) {
41            for (var i = 0; i < haystack.length; i++) {
42                if (haystack[i] === needle) {
43                    return i;
44                }
45            }
46            return -1;
47        },
48        each: $.each,
49        map: $.map,
50        filter: $.grep,
51        every: function(obj, test) {
52            var result = true;
53            if (!obj) {
54                return result;
55            }
56            $.each(obj, function(key, val) {
57                if (!(result = test.call(null, val, key, obj))) {
58                    return false;
59                }
60            });
61            return !!result;
62        },
63        some: function(obj, test) {
64            var result = false;
65            if (!obj) {
66                return result;
67            }
68            $.each(obj, function(key, val) {
69                if (result = test.call(null, val, key, obj)) {
70                    return false;
71                }
72            });
73            return !!result;
74        },
75        mixin: $.extend,
76        getUniqueId: function() {
77            var counter = 0;
78            return function() {
79                return counter++;
80            };
81        }(),
82        defer: function(fn) {
83            setTimeout(fn, 0);
84        },
85        debounce: function(func, wait, immediate) {
86            var timeout, result;
87            return function() {
88                var context = this, args = arguments, later, callNow;
89                later = function() {
90                    timeout = null;
91                    if (!immediate) {
92                        result = func.apply(context, args);
93                    }
94                };
95                callNow = immediate && !timeout;
96                clearTimeout(timeout);
97                timeout = setTimeout(later, wait);
98                if (callNow) {
99                    result = func.apply(context, args);
100                }
101                return result;
102            };
103        },
104        throttle: function(func, wait) {
105            var context, args, timeout, result, previous, later;
106            previous = 0;
107            later = function() {
108                previous = new Date();
109                timeout = null;
110                result = func.apply(context, args);
111            };
112            return function() {
113                var now = new Date(), remaining = wait - (now - previous);
114                context = this;
115                args = arguments;
116                if (remaining <= 0) {
117                    clearTimeout(timeout);
118                    timeout = null;
119                    previous = now;
120                    result = func.apply(context, args);
121                } else if (!timeout) {
122                    timeout = setTimeout(later, remaining);
123                }
124                return result;
125            };
126        },
127        tokenizeQuery: function(str) {
128            return $.trim(str).toLowerCase().split(/[\s]+/);
129        },
130        tokenizeText: function(str) {
131            return $.trim(str).toLowerCase().split(/[\s\-_]+/);
132        },
133        getProtocol: function() {
134            return location.protocol;
135        },
136        noop: function() {}
137    };
138    var EventTarget = function() {
139        var eventSplitter = /\s+/;
140        return {
141            on: function(events, callback) {
142                var event;
143                if (!callback) {
144                    return this;
145                }
146                this._callbacks = this._callbacks || {};
147                events = events.split(eventSplitter);
148                while (event = events.shift()) {
149                    this._callbacks[event] = this._callbacks[event] || [];
150                    this._callbacks[event].push(callback);
151                }
152                return this;
153            },
154            trigger: function(events, data) {
155                var event, callbacks;
156                if (!this._callbacks) {
157                    return this;
158                }
159                events = events.split(eventSplitter);
160                while (event = events.shift()) {
161                    if (callbacks = this._callbacks[event]) {
162                        for (var i = 0; i < callbacks.length; i += 1) {
163                            callbacks[i].call(this, {
164                                type: event,
165                                data: data
166                            });
167                        }
168                    }
169                }
170                return this;
171            }
172        };
173    }();
174    var EventBus = function() {
175        var namespace = "typeahead:";
176        function EventBus(o) {
177            if (!o || !o.el) {
178                $.error("EventBus initialized without el");
179            }
180            this.$el = $(o.el);
181        }
182        utils.mixin(EventBus.prototype, {
183            trigger: function(type) {
184                var args = [].slice.call(arguments, 1);
185                this.$el.trigger(namespace + type, args);
186            }
187        });
188        return EventBus;
189    }();
190    var PersistentStorage = function() {
191        var ls, methods;
192        try {
193            ls = window.localStorage;
194            ls.setItem("~~~", "!");
195            ls.removeItem("~~~");
196        } catch (err) {
197            ls = null;
198        }
199        function PersistentStorage(namespace) {
200            this.prefix = [ "__", namespace, "__" ].join("");
201            this.ttlKey = "__ttl__";
202            this.keyMatcher = new RegExp("^" + this.prefix);
203        }
204        if (ls && window.JSON) {
205            methods = {
206                _prefix: function(key) {
207                    return this.prefix + key;
208                },
209                _ttlKey: function(key) {
210                    return this._prefix(key) + this.ttlKey;
211                },
212                get: function(key) {
213                    if (this.isExpired(key)) {
214                        this.remove(key);
215                    }
216                    return decode(ls.getItem(this._prefix(key)));
217                },
218                set: function(key, val, ttl) {
219                    if (utils.isNumber(ttl)) {
220                        ls.setItem(this._ttlKey(key), encode(now() + ttl));
221                    } else {
222                        ls.removeItem(this._ttlKey(key));
223                    }
224                    return ls.setItem(this._prefix(key), encode(val));
225                },
226                remove: function(key) {
227                    ls.removeItem(this._ttlKey(key));
228                    ls.removeItem(this._prefix(key));
229                    return this;
230                },
231                clear: function() {
232                    var i, key, keys = [], len = ls.length;
233                    for (i = 0; i < len; i++) {
234                        if ((key = ls.key(i)).match(this.keyMatcher)) {
235                            keys.push(key.replace(this.keyMatcher, ""));
236                        }
237                    }
238                    for (i = keys.length; i--; ) {
239                        this.remove(keys[i]);
240                    }
241                    return this;
242                },
243                isExpired: function(key) {
244                    var ttl = decode(ls.getItem(this._ttlKey(key)));
245                    return utils.isNumber(ttl) && now() > ttl ? true : false;
246                }
247            };
248        } else {
249            methods = {
250                get: utils.noop,
251                set: utils.noop,
252                remove: utils.noop,
253                clear: utils.noop,
254                isExpired: utils.noop
255            };
256        }
257        utils.mixin(PersistentStorage.prototype, methods);
258        return PersistentStorage;
259        function now() {
260            return new Date().getTime();
261        }
262        function encode(val) {
263            return JSON.stringify(utils.isUndefined(val) ? null : val);
264        }
265        function decode(val) {
266            return JSON.parse(val);
267        }
268    }();
269    var RequestCache = function() {
270        function RequestCache(o) {
271            utils.bindAll(this);
272            o = o || {};
273            this.sizeLimit = o.sizeLimit || 10;
274            this.cache = {};
275            this.cachedKeysByAge = [];
276        }
277        utils.mixin(RequestCache.prototype, {
278            get: function(url) {
279                return this.cache[url];
280            },
281            set: function(url, resp) {
282                var requestToEvict;
283                if (this.cachedKeysByAge.length === this.sizeLimit) {
284                    requestToEvict = this.cachedKeysByAge.shift();
285                    delete this.cache[requestToEvict];
286                }
287                this.cache[url] = resp;
288                this.cachedKeysByAge.push(url);
289            }
290        });
291        return RequestCache;
292    }();
293    var Transport = function() {
294        var pendingRequestsCount = 0, pendingRequests = {}, maxPendingRequests, requestCache;
295        function Transport(o) {
296            utils.bindAll(this);
297            o = utils.isString(o) ? {
298                url: o
299            } : o;
300            requestCache = requestCache || new RequestCache();
301            maxPendingRequests = utils.isNumber(o.maxParallelRequests) ? o.maxParallelRequests : maxPendingRequests || 6;
302            this.url = o.url;
303            this.wildcard = o.wildcard || "%QUERY";
304            this.filter = o.filter;
305            this.replace = o.replace;
306            this.ajaxSettings = {
307                type: "get",
308                cache: o.cache,
309                timeout: o.timeout,
310                dataType: o.dataType || "json",
311                beforeSend: o.beforeSend
312            };
313            this._get = (/^throttle$/i.test(o.rateLimitFn) ? utils.throttle : utils.debounce)(this._get, o.rateLimitWait || 300);
314        }
315        utils.mixin(Transport.prototype, {
316            _get: function(url, cb) {
317                var that = this;
318                if (belowPendingRequestsThreshold()) {
319                    this._sendRequest(url).done(done);
320                } else {
321                    this.onDeckRequestArgs = [].slice.call(arguments, 0);
322                }
323                function done(resp) {
324                    var data = that.filter ? that.filter(resp) : resp;
325                    cb && cb(data);
326                    requestCache.set(url, resp);
327                }
328            },
329            _sendRequest: function(url) {
330                var that = this, jqXhr = pendingRequests[url];
331                if (!jqXhr) {
332                    incrementPendingRequests();
333                    jqXhr = pendingRequests[url] = $.ajax(url, this.ajaxSettings).always(always);
334                }
335                return jqXhr;
336                function always() {
337                    decrementPendingRequests();
338                    pendingRequests[url] = null;
339                    if (that.onDeckRequestArgs) {
340                        that._get.apply(that, that.onDeckRequestArgs);
341                        that.onDeckRequestArgs = null;
342                    }
343                }
344            },
345            get: function(query, cb) {
346                var that = this, encodedQuery = encodeURIComponent(query || ""), url, resp;
347                cb = cb || utils.noop;
348                url = this.replace ? this.replace(this.url, encodedQuery) : this.url.replace(this.wildcard, encodedQuery);
349                if (resp = requestCache.get(url)) {
350                    utils.defer(function() {
351                        cb(that.filter ? that.filter(resp) : resp);
352                    });
353                } else {
354                    this._get(url, cb);
355                }
356                return !!resp;
357            }
358        });
359        return Transport;
360        function incrementPendingRequests() {
361            pendingRequestsCount++;
362        }
363        function decrementPendingRequests() {
364            pendingRequestsCount--;
365        }
366        function belowPendingRequestsThreshold() {
367            return pendingRequestsCount < maxPendingRequests;
368        }
369    }();
370    var Dataset = function() {
371        var keys = {
372            thumbprint: "thumbprint",
373            protocol: "protocol",
374            itemHash: "itemHash",
375            adjacencyList: "adjacencyList"
376        };
377        function Dataset(o) {
378            utils.bindAll(this);
379            if (utils.isString(o.template) && !o.engine) {
380                $.error("no template engine specified");
381            }
382            if (!o.local && !o.prefetch && !o.remote) {
383                $.error("one of local, prefetch, or remote is required");
384            }
385            this.name = o.name || utils.getUniqueId();
386            this.limit = o.limit || 5;
387            this.minLength = o.minLength || 1;
388            this.header = o.header;
389            this.footer = o.footer;
390            this.valueKey = o.valueKey || "value";
391            this.template = compileTemplate(o.template, o.engine, this.valueKey);
392            this.local = o.local;
393            this.prefetch = o.prefetch;
394            this.remote = o.remote;
395            this.itemHash = {};
396            this.adjacencyList = {};
397            this.storage = o.name ? new PersistentStorage(o.name) : null;
398        }
399        utils.mixin(Dataset.prototype, {
400            _processLocalData: function(data) {
401                this._mergeProcessedData(this._processData(data));
402            },
403            _loadPrefetchData: function(o) {
404                var that = this, thumbprint = VERSION + (o.thumbprint || ""), storedThumbprint, storedProtocol, storedItemHash, storedAdjacencyList, isExpired, deferred;
405                if (this.storage) {
406                    storedThumbprint = this.storage.get(keys.thumbprint);
407                    storedProtocol = this.storage.get(keys.protocol);
408                    storedItemHash = this.storage.get(keys.itemHash);
409                    storedAdjacencyList = this.storage.get(keys.adjacencyList);
410                }
411                isExpired = storedThumbprint !== thumbprint || storedProtocol !== utils.getProtocol();
412                o = utils.isString(o) ? {
413                    url: o
414                } : o;
415                o.ttl = utils.isNumber(o.ttl) ? o.ttl : 24 * 60 * 60 * 1e3;
416                if (storedItemHash && storedAdjacencyList && !isExpired) {
417                    this._mergeProcessedData({
418                        itemHash: storedItemHash,
419                        adjacencyList: storedAdjacencyList
420                    });
421                    deferred = $.Deferred().resolve();
422                } else {
423                    deferred = $.getJSON(o.url).done(processPrefetchData);
424                }
425                return deferred;
426                function processPrefetchData(data) {
427                    var filteredData = o.filter ? o.filter(data) : data, processedData = that._processData(filteredData), itemHash = processedData.itemHash, adjacencyList = processedData.adjacencyList;
428                    if (that.storage) {
429                        that.storage.set(keys.itemHash, itemHash, o.ttl);
430                        that.storage.set(keys.adjacencyList, adjacencyList, o.ttl);
431                        that.storage.set(keys.thumbprint, thumbprint, o.ttl);
432                        that.storage.set(keys.protocol, utils.getProtocol(), o.ttl);
433                    }
434                    that._mergeProcessedData(processedData);
435                }
436            },
437            _transformDatum: function(datum) {
438                var value = utils.isString(datum) ? datum : datum[this.valueKey], tokens = datum.tokens || utils.tokenizeText(value), item = {
439                    value: value,
440                    tokens: tokens
441                };
442                if (utils.isString(datum)) {
443                    item.datum = {};
444                    item.datum[this.valueKey] = datum;
445                } else {
446                    item.datum = datum;
447                }
448                item.tokens = utils.filter(item.tokens, function(token) {
449                    return !utils.isBlankString(token);
450                });
451                item.tokens = utils.map(item.tokens, function(token) {
452                    return token.toLowerCase();
453                });
454                return item;
455            },
456            _processData: function(data) {
457                var that = this, itemHash = {}, adjacencyList = {};
458                utils.each(data, function(i, datum) {
459                    var item = that._transformDatum(datum), id = utils.getUniqueId(item.value);
460                    itemHash[id] = item;
461                    utils.each(item.tokens, function(i, token) {
462                        var character = token.charAt(0), adjacency = adjacencyList[character] || (adjacencyList[character] = [ id ]);
463                        !~utils.indexOf(adjacency, id) && adjacency.push(id);
464                    });
465                });
466                return {
467                    itemHash: itemHash,
468                    adjacencyList: adjacencyList
469                };
470            },
471            _mergeProcessedData: function(processedData) {
472                var that = this;
473                utils.mixin(this.itemHash, processedData.itemHash);
474                utils.each(processedData.adjacencyList, function(character, adjacency) {
475                    var masterAdjacency = that.adjacencyList[character];
476                    that.adjacencyList[character] = masterAdjacency ? masterAdjacency.concat(adjacency) : adjacency;
477                });
478            },
479            _getLocalSuggestions: function(terms) {
480                var that = this, firstChars = [], lists = [], shortestList, suggestions = [];
481                utils.each(terms, function(i, term) {
482                    var firstChar = term.charAt(0);
483                    !~utils.indexOf(firstChars, firstChar) && firstChars.push(firstChar);
484                });
485                utils.each(firstChars, function(i, firstChar) {
486                    var list = that.adjacencyList[firstChar];
487                    if (!list) {
488                        return false;
489                    }
490                    lists.push(list);
491                    if (!shortestList || list.length < shortestList.length) {
492                        shortestList = list;
493                    }
494                });
495                if (lists.length < firstChars.length) {
496                    return [];
497                }
498                utils.each(shortestList, function(i, id) {
499                    var item = that.itemHash[id], isCandidate, isMatch;
500                    isCandidate = utils.every(lists, function(list) {
501                        return ~utils.indexOf(list, id);
502                    });
503                    isMatch = isCandidate && utils.every(terms, function(term) {
504                        return utils.some(item.tokens, function(token) {
505                            return token.indexOf(term) === 0;
506                        });
507                    });
508                    isMatch && suggestions.push(item);
509                });
510                return suggestions;
511            },
512            initialize: function() {
513                var deferred;
514                this.local && this._processLocalData(this.local);
515                this.transport = this.remote ? new Transport(this.remote) : null;
516                deferred = this.prefetch ? this._loadPrefetchData(this.prefetch) : $.Deferred().resolve();
517                this.local = this.prefetch = this.remote = null;
518                this.initialize = function() {
519                    return deferred;
520                };
521                return deferred;
522            },
523            getSuggestions: function(query, cb) {
524                var that = this, terms, suggestions, cacheHit = false;
525                if (query.length < this.minLength) {
526                    return;
527                }
528                terms = utils.tokenizeQuery(query);
529                suggestions = this._getLocalSuggestions(terms).slice(0, this.limit);
530                if (suggestions.length < this.limit && this.transport) {
531                    cacheHit = this.transport.get(query, processRemoteData);
532                }
533                !cacheHit && cb && cb(suggestions);
534                function processRemoteData(data) {
535                    suggestions = suggestions.slice(0);
536                    utils.each(data, function(i, datum) {
537                        var item = that._transformDatum(datum), isDuplicate;
538                        isDuplicate = utils.some(suggestions, function(suggestion) {
539                            return item.value === suggestion.value;
540                        });
541                        !isDuplicate && suggestions.push(item);
542                        return suggestions.length < that.limit;
543                    });
544                    cb && cb(suggestions);
545                }
546            }
547        });
548        return Dataset;
549        function compileTemplate(template, engine, valueKey) {
550            var renderFn, compiledTemplate;
551            if (utils.isFunction(template)) {
552                renderFn = template;
553            } else if (utils.isString(template)) {
554                compiledTemplate = engine.compile(template);
555                renderFn = utils.bind(compiledTemplate.render, compiledTemplate);
556            } else {
557                renderFn = function(context) {
558                    return "<p>" + context[valueKey] + "</p>";
559                };
560            }
561            return renderFn;
562        }
563    }();
564    var InputView = function() {
565        function InputView(o) {
566            var that = this;
567            utils.bindAll(this);
568            this.specialKeyCodeMap = {
569                9: "tab",
570                27: "esc",
571                37: "left",
572                39: "right",
573                13: "enter",
574                38: "up",
575                40: "down"
576            };
577            this.$hint = $(o.hint);
578            this.$input = $(o.input).on("blur.tt", this._handleBlur).on("focus.tt", this._handleFocus).on("keydown.tt", this._handleSpecialKeyEvent);
579            if (!utils.isMsie()) {
580                this.$input.on("input.tt", this._compareQueryToInputValue);
581            } else {
582                this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) {
583                    if (that.specialKeyCodeMap[$e.which || $e.keyCode]) {
584                        return;
585                    }
586                    utils.defer(that._compareQueryToInputValue);
587                });
588            }
589            this.query = this.$input.val();
590            this.$overflowHelper = buildOverflowHelper(this.$input);
591        }
592        utils.mixin(InputView.prototype, EventTarget, {
593            _handleFocus: function() {
594                this.trigger("focused");
595            },
596            _handleBlur: function() {
597                this.trigger("blured");
598            },
599            _handleSpecialKeyEvent: function($e) {
600                var keyName = this.specialKeyCodeMap[$e.which || $e.keyCode];
601                keyName && this.trigger(keyName + "Keyed", $e);
602            },
603            _compareQueryToInputValue: function() {
604                var inputValue = this.getInputValue(), isSameQuery = compareQueries(this.query, inputValue), isSameQueryExceptWhitespace = isSameQuery ? this.query.length !== inputValue.length : false;
605                if (isSameQueryExceptWhitespace) {
606                    this.trigger("whitespaceChanged", {
607                        value: this.query
608                    });
609                } else if (!isSameQuery) {
610                    this.trigger("queryChanged", {
611                        value: this.query = inputValue
612                    });
613                }
614            },
615            destroy: function() {
616                this.$hint.off(".tt");
617                this.$input.off(".tt");
618                this.$hint = this.$input = this.$overflowHelper = null;
619            },
620            focus: function() {
621                this.$input.focus();
622            },
623            blur: function() {
624                this.$input.blur();
625            },
626            getQuery: function() {
627                return this.query;
628            },
629            setQuery: function(query) {
630                this.query = query;
631            },
632            getInputValue: function() {
633                return this.$input.val();
634            },
635            setInputValue: function(value, silent) {
636                this.$input.val(value);
637                !silent && this._compareQueryToInputValue();
638            },
639            getHintValue: function() {
640                return this.$hint.val();
641            },
642            setHintValue: function(value) {
643                this.$hint.val(value);
644            },
645            getLanguageDirection: function() {
646                return (this.$input.css("direction") || "ltr").toLowerCase();
647            },
648            isOverflow: function() {
649                this.$overflowHelper.text(this.getInputValue());
650                return this.$overflowHelper.width() > this.$input.width();
651            },
652            isCursorAtEnd: function() {
653                var valueLength = this.$input.val().length, selectionStart = this.$input[0].selectionStart, range;
654                if (utils.isNumber(selectionStart)) {
655                    return selectionStart === valueLength;
656                } else if (document.selection) {
657                    range = document.selection.createRange();
658                    range.moveStart("character", -valueLength);
659                    return valueLength === range.text.length;
660                }
661                return true;
662            }
663        });
664        return InputView;
665        function buildOverflowHelper($input) {
666            return $("<span></span>").css({
667                position: "absolute",
668                left: "-9999px",
669                visibility: "hidden",
670                whiteSpace: "nowrap",
671                fontFamily: $input.css("font-family"),
672                fontSize: $input.css("font-size"),
673                fontStyle: $input.css("font-style"),
674                fontVariant: $input.css("font-variant"),
675                fontWeight: $input.css("font-weight"),
676                wordSpacing: $input.css("word-spacing"),
677                letterSpacing: $input.css("letter-spacing"),
678                textIndent: $input.css("text-indent"),
679                textRendering: $input.css("text-rendering"),
680                textTransform: $input.css("text-transform")
681            }).insertAfter($input);
682        }
683        function compareQueries(a, b) {
684            a = (a || "").replace(/^\s*/g, "").replace(/\s{2,}/g, " ");
685            b = (b || "").replace(/^\s*/g, "").replace(/\s{2,}/g, " ");
686            return a === b;
687        }
688    }();
689    var DropdownView = function() {
690        var html = {
691            suggestionsList: '<span class="tt-suggestions"></span>'
692        }, css = {
693            suggestionsList: {
694                display: "block"
695            },
696            suggestion: {
697                whiteSpace: "nowrap",
698                cursor: "pointer"
699            },
700            suggestionChild: {
701                whiteSpace: "normal"
702            }
703        };
704        function DropdownView(o) {
705            utils.bindAll(this);
706            this.isOpen = false;
707            this.isEmpty = true;
708            this.isMouseOverDropdown = false;
709            this.$menu = $(o.menu).on("mouseenter.tt", this._handleMouseenter).on("mouseleave.tt", this._handleMouseleave).on("click.tt", ".tt-suggestion", this._handleSelection).on("mouseover.tt", ".tt-suggestion", this._handleMouseover);
710        }
711        utils.mixin(DropdownView.prototype, EventTarget, {
712            _handleMouseenter: function() {
713                this.isMouseOverDropdown = true;
714            },
715            _handleMouseleave: function() {
716                this.isMouseOverDropdown = false;
717            },
718            _handleMouseover: function($e) {
719                var $suggestion = $($e.currentTarget);
720                this._getSuggestions().removeClass("tt-is-under-cursor");
721                $suggestion.addClass("tt-is-under-cursor");
722            },
723            _handleSelection: function($e) {
724                var $suggestion = $($e.currentTarget);
725                this.trigger("suggestionSelected", extractSuggestion($suggestion));
726            },
727            _show: function() {
728                this.$menu.css("display", "block");
729            },
730            _hide: function() {
731                this.$menu.hide();
732            },
733            _moveCursor: function(increment) {
734                var $suggestions, $cur, nextIndex, $underCursor;
735                if (!this.isVisible()) {
736                    return;
737                }
738                $suggestions = this._getSuggestions();
739                $cur = $suggestions.filter(".tt-is-under-cursor");
740                $cur.removeClass("tt-is-under-cursor");
741                nextIndex = $suggestions.index($cur) + increment;
742                nextIndex = (nextIndex + 1) % ($suggestions.length + 1) - 1;
743                if (nextIndex === -1) {
744                    this.trigger("cursorRemoved");
745                    return;
746                } else if (nextIndex < -1) {
747                    nextIndex = $suggestions.length - 1;
748                }
749                $underCursor = $suggestions.eq(nextIndex).addClass("tt-is-under-cursor");
750                this._ensureVisibility($underCursor);
751                this.trigger("cursorMoved", extractSuggestion($underCursor));
752            },
753            _getSuggestions: function() {
754                return this.$menu.find(".tt-suggestions > .tt-suggestion");
755            },
756            _ensureVisibility: function($el) {
757                var menuHeight = this.$menu.height() + parseInt(this.$menu.css("paddingTop"), 10) + parseInt(this.$menu.css("paddingBottom"), 10), menuScrollTop = this.$menu.scrollTop(), elTop = $el.position().top, elBottom = elTop + $el.outerHeight(true);
758                if (elTop < 0) {
759                    this.$menu.scrollTop(menuScrollTop + elTop);
760                } else if (menuHeight < elBottom) {
761                    this.$menu.scrollTop(menuScrollTop + (elBottom - menuHeight));
762                }
763            },
764            destroy: function() {
765                this.$menu.off(".tt");
766                this.$menu = null;
767            },
768            isVisible: function() {
769                return this.isOpen && !this.isEmpty;
770            },
771            closeUnlessMouseIsOverDropdown: function() {
772                if (!this.isMouseOverDropdown) {
773                    this.close();
774                }
775            },
776            close: function() {
777                if (this.isOpen) {
778                    this.isOpen = false;
779                    this.isMouseOverDropdown = false;
780                    this._hide();
781                    this.$menu.find(".tt-suggestions > .tt-suggestion").removeClass("tt-is-under-cursor");
782                    this.trigger("closed");
783                }
784            },
785            open: function() {
786                if (!this.isOpen) {
787                    this.isOpen = true;
788                    !this.isEmpty && this._show();
789                    this.trigger("opened");
790                }
791            },
792            setLanguageDirection: function(dir) {
793                var ltrCss = {
794                    left: "0",
795                    right: "auto"
796                }, rtlCss = {
797                    left: "auto",
798                    right: " 0"
799                };
800                dir === "ltr" ? this.$menu.css(ltrCss) : this.$menu.css(rtlCss);
801            },
802            moveCursorUp: function() {
803                this._moveCursor(-1);
804            },
805            moveCursorDown: function() {
806                this._moveCursor(+1);
807            },
808            getSuggestionUnderCursor: function() {
809                var $suggestion = this._getSuggestions().filter(".tt-is-under-cursor").first();
810                return $suggestion.length > 0 ? extractSuggestion($suggestion) : null;
811            },
812            getFirstSuggestion: function() {
813                var $suggestion = this._getSuggestions().first();
814                return $suggestion.length > 0 ? extractSuggestion($suggestion) : null;
815            },
816            renderSuggestions: function(dataset, suggestions) {
817                var datasetClassName = "tt-dataset-" + dataset.name, wrapper = '<div class="tt-suggestion">%body</div>', compiledHtml, $suggestionsList, $dataset = this.$menu.find("." + datasetClassName), elBuilder, fragment, $el;
818                if ($dataset.length === 0) {
819                    $suggestionsList = $(html.suggestionsList).css(css.suggestionsList);
820                    $dataset = $("<div></div>").addClass(datasetClassName).append(dataset.header).append($suggestionsList).append(dataset.footer).appendTo(this.$menu);
821                }
822                if (suggestions.length > 0) {
823                    this.isEmpty = false;
824                    this.isOpen && this._show();
825                    elBuilder = document.createElement("div");
826                    fragment = document.createDocumentFragment();
827                    utils.each(suggestions, function(i, suggestion) {
828                        suggestion.dataset = dataset.name;
829                        compiledHtml = dataset.template(suggestion.datum);
830                        elBuilder.innerHTML = wrapper.replace("%body", compiledHtml);
831                        $el = $(elBuilder.firstChild).css(css.suggestion).data("suggestion", suggestion);
832                        $el.children().each(function() {
833                            $(this).css(css.suggestionChild);
834                        });
835                        fragment.appendChild($el[0]);
836                    });
837                    $dataset.show().find(".tt-suggestions").html(fragment);
838                } else {
839                    this.clearSuggestions(dataset.name);
840                }
841                this.trigger("suggestionsRendered");
842            },
843            clearSuggestions: function(datasetName) {
844                var $datasets = datasetName ? this.$menu.find(".tt-dataset-" + datasetName) : this.$menu.find('[class^="tt-dataset-"]'), $suggestions = $datasets.find(".tt-suggestions");
845                $datasets.hide();
846                $suggestions.empty();
847                if (this._getSuggestions().length === 0) {
848                    this.isEmpty = true;
849                    this._hide();
850                }
851            }
852        });
853        return DropdownView;
854        function extractSuggestion($el) {
855            return $el.data("suggestion");
856        }
857    }();
858    var TypeaheadView = function() {
859        var html = {
860            wrapper: '<span class="twitter-typeahead"></span>',
861            hint: '<input class="tt-hint" type="text" autocomplete="off" spellcheck="off" disabled>',
862            dropdown: '<span class="tt-dropdown-menu"></span>'
863        }, css = {
864            wrapper: {
865                position: "relative",
866                display: "inline-block"
867            },
868            hint: {
869                position: "absolute",
870                top: "0",
871                left: "0",
872                borderColor: "transparent",
873                boxShadow: "none"
874            },
875            query: {
876                position: "relative",
877                verticalAlign: "top",
878                backgroundColor: "transparent"
879            },
880            dropdown: {
881                position: "absolute",
882                top: "100%",
883                left: "0",
884                zIndex: "100",
885                display: "none"
886            }
887        };
888        if (utils.isMsie()) {
889            utils.mixin(css.query, {
890                backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)"
891            });
892        }
893        if (utils.isMsie() && utils.isMsie() <= 7) {
894            utils.mixin(css.wrapper, {
895                display: "inline",
896                zoom: "1"
897            });
898            utils.mixin(css.query, {
899                marginTop: "-1px"
900            });
901        }
902        function TypeaheadView(o) {
903            var $menu, $input, $hint;
904            utils.bindAll(this);
905            this.$node = buildDomStructure(o.input);
906            this.datasets = o.datasets;
907            this.dir = null;
908            this.eventBus = o.eventBus;
909            $menu = this.$node.find(".tt-dropdown-menu");
910            $input = this.$node.find(".tt-query");
911            $hint = this.$node.find(".tt-hint");
912            this.dropdownView = new DropdownView({
913                menu: $menu
914            }).on("suggestionSelected", this._handleSelection).on("cursorMoved", this._clearHint).on("cursorMoved", this._setInputValueToSuggestionUnderCursor).on("cursorRemoved", this._setInputValueToQuery).on("cursorRemoved", this._updateHint).on("suggestionsRendered", this._updateHint).on("opened", this._updateHint).on("closed", this._clearHint).on("opened closed", this._propagateEvent);
915            this.inputView = new InputView({
916                input: $input,
917                hint: $hint
918            }).on("focused", this._openDropdown).on("blured", this._closeDropdown).on("blured", this._setInputValueToQuery).on("enterKeyed tabKeyed", this._handleSelection).on("queryChanged", this._clearHint).on("queryChanged", this._clearSuggestions).on("queryChanged", this._getSuggestions).on("whitespaceChanged", this._updateHint).on("queryChanged whitespaceChanged", this._openDropdown).on("queryChanged whitespaceChanged", this._setLanguageDirection).on("escKeyed", this._closeDropdown).on("escKeyed", this._setInputValueToQuery).on("tabKeyed upKeyed downKeyed", this._managePreventDefault).on("upKeyed downKeyed", this._moveDropdownCursor).on("upKeyed downKeyed", this._openDropdown).on("tabKeyed leftKeyed rightKeyed", this._autocomplete);
919        }
920        utils.mixin(TypeaheadView.prototype, EventTarget, {
921            _managePreventDefault: function(e) {
922                var $e = e.data, hint, inputValue, preventDefault = false;
923                switch (e.type) {
924                  case "tabKeyed":
925                    hint = this.inputView.getHintValue();
926                    inputValue = this.inputView.getInputValue();
927                    preventDefault = hint && hint !== inputValue;
928                    break;
929
930                  case "upKeyed":
931                  case "downKeyed":
932                    preventDefault = !$e.shiftKey && !$e.ctrlKey && !$e.metaKey;
933                    break;
934                }
935                preventDefault && $e.preventDefault();
936            },
937            _setLanguageDirection: function() {
938                var dir = this.inputView.getLanguageDirection();
939                if (dir !== this.dir) {
940                    this.dir = dir;
941                    this.$node.css("direction", dir);
942                    this.dropdownView.setLanguageDirection(dir);
943                }
944            },
945            _updateHint: function() {
946                var suggestion = this.dropdownView.getFirstSuggestion(), hint = suggestion ? suggestion.value : null, dropdownIsVisible = this.dropdownView.isVisible(), inputHasOverflow = this.inputView.isOverflow(), inputValue, query, escapedQuery, beginsWithQuery, match;
947                if (hint && dropdownIsVisible && !inputHasOverflow) {
948                    inputValue = this.inputView.getInputValue();
949                    query = inputValue.replace(/\s{2,}/g, " ").replace(/^\s+/g, "");
950                    escapedQuery = utils.escapeRegExChars(query);
951                    beginsWithQuery = new RegExp("^(?:" + escapedQuery + ")(.*$)", "i");
952                    match = beginsWithQuery.exec(hint);
953                    this.inputView.setHintValue(inputValue + (match ? match[1] : ""));
954                }
955            },
956            _clearHint: function() {
957                this.inputView.setHintValue("");
958            },
959            _clearSuggestions: function() {
960                this.dropdownView.clearSuggestions();
961            },
962            _setInputValueToQuery: function() {
963                this.inputView.setInputValue(this.inputView.getQuery());
964            },
965            _setInputValueToSuggestionUnderCursor: function(e) {
966                var suggestion = e.data;
967                this.inputView.setInputValue(suggestion.value, true);
968            },
969            _openDropdown: function() {
970                this.dropdownView.open();
971            },
972            _closeDropdown: function(e) {
973                this.dropdownView[e.type === "blured" ? "closeUnlessMouseIsOverDropdown" : "close"]();
974            },
975            _moveDropdownCursor: function(e) {
976                var $e = e.data;
977                if (!$e.shiftKey && !$e.ctrlKey && !$e.metaKey) {
978                    this.dropdownView[e.type === "upKeyed" ? "moveCursorUp" : "moveCursorDown"]();
979                }
980            },
981            _handleSelection: function(e) {
982                var byClick = e.type === "suggestionSelected", suggestion = byClick ? e.data : this.dropdownView.getSuggestionUnderCursor();
983                if (suggestion) {
984                    this.inputView.setInputValue(suggestion.value);
985                    byClick ? this.inputView.focus() : e.data.preventDefault();
986                    byClick && utils.isMsie() ? utils.defer(this.dropdownView.close) : this.dropdownView.close();
987                    this.eventBus.trigger("selected", suggestion.datum, suggestion.dataset);
988                }
989            },
990            _getSuggestions: function() {
991                var that = this, query = this.inputView.getQuery();
992                if (utils.isBlankString(query)) {
993                    return;
994                }
995                utils.each(this.datasets, function(i, dataset) {
996                    dataset.getSuggestions(query, function(suggestions) {
997                        if (query === that.inputView.getQuery()) {
998                            that.dropdownView.renderSuggestions(dataset, suggestions);
999                        }
1000                    });
1001                });
1002            },
1003            _autocomplete: function(e) {
1004                var isCursorAtEnd, ignoreEvent, query, hint, suggestion;
1005                if (e.type === "rightKeyed" || e.type === "leftKeyed") {
1006                    isCursorAtEnd = this.inputView.isCursorAtEnd();
1007                    ignoreEvent = this.inputView.getLanguageDirection() === "ltr" ? e.type === "leftKeyed" : e.type === "rightKeyed";
1008                    if (!isCursorAtEnd || ignoreEvent) {
1009                        return;
1010                    }
1011                }
1012                query = this.inputView.getQuery();
1013                hint = this.inputView.getHintValue();
1014                if (hint !== "" && query !== hint) {
1015                    suggestion = this.dropdownView.getFirstSuggestion();
1016                    this.inputView.setInputValue(suggestion.value);
1017                    this.eventBus.trigger("autocompleted", suggestion.datum, suggestion.dataset);
1018                }
1019            },
1020            _propagateEvent: function(e) {
1021                this.eventBus.trigger(e.type);
1022            },
1023            destroy: function() {
1024                this.inputView.destroy();
1025                this.dropdownView.destroy();
1026                destroyDomStructure(this.$node);
1027                this.$node = null;
1028            },
1029            setQuery: function(query) {
1030                this.inputView.setQuery(query);
1031                this.inputView.setInputValue(query);
1032                this._clearHint();
1033                this._clearSuggestions();
1034                this._getSuggestions();
1035            }
1036        });
1037        return TypeaheadView;
1038        function buildDomStructure(input) {
1039            var $wrapper = $(html.wrapper), $dropdown = $(html.dropdown), $input = $(input), $hint = $(html.hint);
1040            $wrapper = $wrapper.css(css.wrapper);
1041            $dropdown = $dropdown.css(css.dropdown);
1042            $hint.css(css.hint).css({
1043                backgroundAttachment: $input.css("background-attachment"),
1044                backgroundClip: $input.css("background-clip"),
1045                backgroundColor: $input.css("background-color"),
1046                backgroundImage: $input.css("background-image"),
1047                backgroundOrigin: $input.css("background-origin"),
1048                backgroundPosition: $input.css("background-position"),
1049                backgroundRepeat: $input.css("background-repeat"),
1050                backgroundSize: $input.css("background-size")
1051            });
1052            $input.data("ttAttrs", {
1053                dir: $input.attr("dir"),
1054                autocomplete: $input.attr("autocomplete"),
1055                spellcheck: $input.attr("spellcheck"),
1056                style: $input.attr("style")
1057            });
1058            $input.addClass("tt-query").attr({
1059                autocomplete: "off",
1060                spellcheck: false
1061            }).css(css.query);
1062            try {
1063                !$input.attr("dir") && $input.attr("dir", "auto");
1064            } catch (e) {}
1065            return $input.wrap($wrapper).parent().prepend($hint).append($dropdown);
1066        }
1067        function destroyDomStructure($node) {
1068            var $input = $node.find(".tt-query");
1069            utils.each($input.data("ttAttrs"), function(key, val) {
1070                utils.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val);
1071            });
1072            $input.detach().removeData("ttAttrs").removeClass("tt-query").insertAfter($node);
1073            $node.remove();
1074        }
1075    }();
1076    (function() {
1077        var cache = {}, viewKey = "ttView", methods;
1078        methods = {
1079            initialize: function(datasetDefs) {
1080                var datasets;
1081                datasetDefs = utils.isArray(datasetDefs) ? datasetDefs : [ datasetDefs ];
1082                if (datasetDefs.length === 0) {
1083                    $.error("no datasets provided");
1084                }
1085                datasets = utils.map(datasetDefs, function(o) {
1086                    var dataset = cache[o.name] ? cache[o.name] : new Dataset(o);
1087                    if (o.name) {
1088                        cache[o.name] = dataset;
1089                    }
1090                    return dataset;
1091                });
1092                return this.each(initialize);
1093                function initialize() {
1094                    var $input = $(this), deferreds, eventBus = new EventBus({
1095                        el: $input
1096                    });
1097                    deferreds = utils.map(datasets, function(dataset) {
1098                        return dataset.initialize();
1099                    });
1100                    $input.data(viewKey, new TypeaheadView({
1101                        input: $input,
1102                        eventBus: eventBus = new EventBus({
1103                            el: $input
1104                        }),
1105                        datasets: datasets
1106                    }));
1107                    $.when.apply($, deferreds).always(function() {
1108                        utils.defer(function() {
1109                            eventBus.trigger("initialized");
1110                        });
1111                    });
1112                }
1113            },
1114            destroy: function() {
1115                return this.each(destroy);
1116                function destroy() {
1117                    var $this = $(this), view = $this.data(viewKey);
1118                    if (view) {
1119                        view.destroy();
1120                        $this.removeData(viewKey);
1121                    }
1122                }
1123            },
1124            setQuery: function(query) {
1125                return this.each(setQuery);
1126                function setQuery() {
1127                    var view = $(this).data(viewKey);
1128                    view && view.setQuery(query);
1129                }
1130            }
1131        };
1132        jQuery.fn.typeahead = function(method) {
1133            if (methods[method]) {
1134                return methods[method].apply(this, [].slice.call(arguments, 1));
1135            } else {
1136                return methods.initialize.apply(this, arguments);
1137            }
1138        };
1139    })();
1140})(window.jQuery);
1140

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.