PageSourceSearch

https://vastapaino.fi/lib/typeahead/typeahead.bundle.js?v=1733992860

js vastapaino.fi collected 2026-10-02 06:51:01 UTC 68,456 bytes, 1,716 lines download raw bytes

vendor: 1,639 bytes, lines 1-52
1/*!
2 * typeahead.js 0.10.2
3 * https://github.com/twitter/typeahead.js
4 * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT
5 */
6
7(function($) {
8    var _ = {
9        isMsie: function() {
10            return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false;
11        },
12        isBlankString: function(str) {
13            return !str || /^\s*$/.test(str);
14        },
15        escapeRegExChars: function(str) {
16            return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
17        },
18        isString: function(obj) {
19            return typeof obj === "string";
20        },
21        isNumber: function(obj) {
22            return typeof obj === "number";
23        },
24        isArray: $.isArray,
25        isFunction: $.isFunction,
26        isObject: $.isPlainObject,
27        isUndefined: function(obj) {
28            return typeof obj === "undefined";
29        },
30        bind: $.proxy,
31        each: function(collection, cb) {
32            $.each(collection, reverseArgs);
33            function reverseArgs(index, value) {
34                return cb(value, index);
35            }
36        },
37        map: $.map,
38        filter: $.grep,
39        every: function(obj, test) {
40            var result = true;
41            if (!obj) {
42                return result;
43            }
44            $.each(obj, function(key, val) {
45                if (!(result = test.call(null, val, key, obj))) {
46                    return false;
47                }
48            });
49            return !!result;
50        },
51        some: function(obj, test) {
52            var result = false;
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        mixin: $.extend,
64        getUniqueId: function() {
65            var counter = 0;
66            return function() {
67                return counter++;
68            };
69        }(),
70        templatify: function templatify(obj) {
71            return $.isFunction(obj) ? obj : template;
72            function template() {
73                return String(obj);
74            }
75        },
76        defer: function(fn) {
77            setTimeout(fn, 0);
78        },
79        debounce: function(func, wait, immediate) {
80            var timeout, result;
81            return function() {
82                var context = this, args = arguments, later, callNow;
83                later = function() {
84                    timeout = null;
85                    if (!immediate) {
86                        result = func.apply(context, args);
87                    }
88                };
89                callNow = immediate && !timeout;
90                clearTimeout(timeout);
91                timeout = setTimeout(later, wait);
92                if (callNow) {
93                    result = func.apply(context, args);
94                }
95                return result;
96            };
97        },
98        throttle: function(func, wait) {
99            var context, args, timeout, result, previous, later;
100            previous = 0;
101            later = function() {
102                previous = new Date();
103                timeout = null;
104                result = func.apply(context, args);
105            };
106            return function() {
107                var now = new Date(), remaining = wait - (now - previous);
108                context = this;
109                args = arguments;
110                if (remaining <= 0) {
111                    clearTimeout(timeout);
112                    timeout = null;
113                    previous = now;
114                    result = func.apply(context, args);
115                } else if (!timeout) {
116                    timeout = setTimeout(later, remaining);
117                }
118                return result;
119            };
120        },
121        noop: function() {}
122    };
123    var VERSION = "0.10.2";
124    var tokenizers = function(root) {
125        return {
126            nonword: nonword,
127            whitespace: whitespace,
128            obj: {
129                nonword: getObjTokenizer(nonword),
130                whitespace: getObjTokenizer(whitespace)
131            }
132        };
133        function whitespace(s) {
134            return s.split(/\s+/);
135        }
136        function nonword(s) {
137            return s.split(/\W+/);
138        }
139        function getObjTokenizer(tokenizer) {
140            return function setKey(key) {
141                return function tokenize(o) {
142                    return tokenizer(o[key]);
143                };
144            };
145        }
146    }();
147    var LruCache = function() {
148        function LruCache(maxSize) {
149            this.maxSize = maxSize || 100;
150            this.size = 0;
151            this.hash = {};
152            this.list = new List();
153        }
154        _.mixin(LruCache.prototype, {
155            set: function set(key, val) {
156                var tailItem = this.list.tail, node;
157                if (this.size >= this.maxSize) {
158                    this.list.remove(tailItem);
159                    delete this.hash[tailItem.key];
160                }
161                if (node = this.hash[key]) {
162                    node.val = val;
163                    this.list.moveToFront(node);
164                } else {
165                    node = new Node(key, val);
166                    this.list.add(node);
167                    this.hash[key] = node;
168                    this.size++;
169                }
170            },
171            get: function get(key) {
172                var node = this.hash[key];
173                if (node) {
174                    this.list.moveToFront(node);
175                    return node.val;
176                }
177            }
178        });
179        function List() {
180            this.head = this.tail = null;
181        }
182        _.mixin(List.prototype, {
183            add: function add(node) {
184                if (this.head) {
185                    node.next = this.head;
186                    this.head.prev = node;
187                }
188                this.head = node;
189                this.tail = this.tail || node;
190            },
191            remove: function remove(node) {
192                node.prev ? node.prev.next = node.next : this.head = node.next;
193                node.next ? node.next.prev = node.prev : this.tail = node.prev;
194            },
195            moveToFront: function(node) {
196                this.remove(node);
197                this.add(node);
198            }
199        });
200        function Node(key, val) {
201            this.key = key;
202            this.val = val;
203            this.prev = this.next = null;
204        }
205        return LruCache;
206    }();
207    var PersistentStorage = function() {
208        var ls, methods;
209        try {
210            ls = window.localStorage;
211            ls.setItem("~~~", "!");
212            ls.removeItem("~~~");
213        } catch (err) {
214            ls = null;
215        }
216        function PersistentStorage(namespace) {
217            this.prefix = [ "__", namespace, "__" ].join("");
218            this.ttlKey = "__ttl__";
219            this.keyMatcher = new RegExp("^" + this.prefix);
220        }
221        if (ls && window.JSON) {
222            methods = {
223                _prefix: function(key) {
224                    return this.prefix + key;
225                },
226                _ttlKey: function(key) {
227                    return this._prefix(key) + this.ttlKey;
228                },
229                get: function(key) {
230                    if (this.isExpired(key)) {
231                        this.remove(key);
232                    }
233                    return decode(ls.getItem(this._prefix(key)));
234                },
235                set: function(key, val, ttl) {
236                    if (_.isNumber(ttl)) {
237                        ls.setItem(this._ttlKey(key), encode(now() + ttl));
238                    } else {
239                        ls.removeItem(this._ttlKey(key));
240                    }
241                    return ls.setItem(this._prefix(key), encode(val));
242                },
243                remove: function(key) {
244                    ls.removeItem(this._ttlKey(key));
245                    ls.removeItem(this._prefix(key));
246                    return this;
247                },
248                clear: function() {
249                    var i, key, keys = [], len = ls.length;
250                    for (i = 0; i < len; i++) {
251                        if ((key = ls.key(i)).match(this.keyMatcher)) {
252                            keys.push(key.replace(this.keyMatcher, ""));
253                        }
254                    }
255                    for (i = keys.length; i--; ) {
256                        this.remove(keys[i]);
257                    }
258                    return this;
259                },
260                isExpired: function(key) {
261                    var ttl = decode(ls.getItem(this._ttlKey(key)));
262                    return _.isNumber(ttl) && now() > ttl ? true : false;
263                }
264            };
265        } else {
266            methods = {
267                get: _.noop,
268                set: _.noop,
269                remove: _.noop,
270                clear: _.noop,
271                isExpired: _.noop
272            };
273        }
274        _.mixin(PersistentStorage.prototype, methods);
275        return PersistentStorage;
276        function now() {
277            return new Date().getTime();
278        }
279        function encode(val) {
280            return JSON.stringify(_.isUndefined(val) ? null : val);
281        }
282        function decode(val) {
283            return JSON.parse(val);
284        }
285    }();
286    var Transport = function() {
287        var pendingRequestsCount = 0, pendingRequests = {}, maxPendingRequests = 6, requestCache = new LruCache(10);
288        function Transport(o) {
289            o = o || {};
290            this._send = o.transport ? callbackToDeferred(o.transport) : $.ajax;
291            this._get = o.rateLimiter ? o.rateLimiter(this._get) : this._get;
292        }
293        Transport.setMaxPendingRequests = function setMaxPendingRequests(num) {
294            maxPendingRequests = num;
295        };
296        Transport.resetCache = function clearCache() {
297            requestCache = new LruCache(10);
298        };
299        _.mixin(Transport.prototype, {
300            _get: function(url, o, cb) {
301                var that = this, jqXhr;
302                if (jqXhr = pendingRequests[url]) {
303                    jqXhr.done(done).fail(fail);
304                } else if (pendingRequestsCount < maxPendingRequests) {
305                    pendingRequestsCount++;
306                    pendingRequests[url] = this._send(url, o).done(done).fail(fail).always(always);
307                } else {
308                    this.onDeckRequestArgs = [].slice.call(arguments, 0);
309                }
310                function done(resp) {
311                    cb && cb(null, resp);
312                    requestCache.set(url, resp);
313                }
314                function fail() {
315                    cb && cb(true);
316                }
317                function always() {
318                    pendingRequestsCount--;
319                    delete pendingRequests[url];
320                    if (that.onDeckRequestArgs) {
321                        that._get.apply(that, that.onDeckRequestArgs);
322                        that.onDeckRequestArgs = null;
323                    }
324                }
325            },
326            get: function(url, o, cb) {
327                var resp;
328                if (_.isFunction(o)) {
329                    cb = o;
330                    o = {};
331                }
332                if (resp = requestCache.get(url)) {
333                    _.defer(function() {
334                        cb && cb(null, resp);
335                    });
336                } else {
337                    this._get(url, o, cb);
338                }
339                return !!resp;
340            }
341        });
342        return Transport;
343        function callbackToDeferred(fn) {
344            return function customSendWrapper(url, o) {
345                var deferred = $.Deferred();
346                fn(url, o, onSuccess, onError);
347                return deferred;
348                function onSuccess(resp) {
349                    _.defer(function() {
350                        deferred.resolve(resp);
351                    });
352                }
353                function onError(err) {
354                    _.defer(function() {
355                        deferred.reject(err);
356                    });
357                }
358            };
359        }
360    }();
361    var SearchIndex = function() {
362        function SearchIndex(o) {
363            o = o || {};
364            if (!o.datumTokenizer || !o.queryTokenizer) {
365                $.error("datumTokenizer and queryTokenizer are both required");
366            }
367            this.datumTokenizer = o.datumTokenizer;
368            this.queryTokenizer = o.queryTokenizer;
369            this.reset();
370        }
371        _.mixin(SearchIndex.prototype, {
372            bootstrap: function bootstrap(o) {
373                this.datums = o.datums;
374                this.trie = o.trie;
375            },
376            add: function(data) {
377                var that = this;
378                data = _.isArray(data) ? data : [ data ];
379                _.each(data, function(datum) {
380                    var id, tokens;
381                    id = that.datums.push(datum) - 1;
382                    tokens = normalizeTokens(that.datumTokenizer(datum));
383                    _.each(tokens, function(token) {
384                        var node, chars, ch;
385                        node = that.trie;
386                        chars = token.split("");
387                        while (ch = chars.shift()) {
388                            node = node.children[ch] || (node.children[ch] = newNode());
389                            node.ids.push(id);
390                        }
391                    });
392                });
393            },
394            get: function get(query) {
395                var that = this, tokens, matches;
396                tokens = normalizeTokens(this.queryTokenizer(query));
397                _.each(tokens, function(token) {
398                    var node, chars, ch, ids;
399                    if (matches && matches.length === 0) {
400                        return false;
401                    }
402                    node = that.trie;
403                    chars = token.split("");
404                    while (node && (ch = chars.shift())) {
405                        node = node.children[ch];
406                    }
407                    if (node && chars.length === 0) {
408                        ids = node.ids.slice(0);
409                        matches = matches ? getIntersection(matches, ids) : ids;
410                    } else {
411                        matches = [];
412                        return false;
413                    }
414                });
415                return matches ? _.map(unique(matches), function(id) {
416                    return that.datums[id];
417                }) : [];
418            },
419            reset: function reset() {
420                this.datums = [];
421                this.trie = newNode();
422            },
423            serialize: function serialize() {
424                return {
425                    datums: this.datums,
426                    trie: this.trie
427                };
428            }
429        });
430        return SearchIndex;
431        function normalizeTokens(tokens) {
432            tokens = _.filter(tokens, function(token) {
433                return !!token;
434            });
435            tokens = _.map(tokens, function(token) {
436                return token.toLowerCase();
437            });
438            return tokens;
439        }
440        function newNode() {
441            return {
442                ids: [],
443                children: {}
444            };
445        }
446        function unique(array) {
447            var seen = {}, uniques = [];
448            for (var i = 0; i < array.length; i++) {
449                if (!seen[array[i]]) {
450                    seen[array[i]] = true;
451                    uniques.push(array[i]);
452                }
453            }
454            return uniques;
455        }
456        function getIntersection(arrayA, arrayB) {
457            var ai = 0, bi = 0, intersection = [];
458            arrayA = arrayA.sort(compare);
459            arrayB = arrayB.sort(compare);
460            while (ai < arrayA.length && bi < arrayB.length) {
461                if (arrayA[ai] < arrayB[bi]) {
462                    ai++;
463                } else if (arrayA[ai] > arrayB[bi]) {
464                    bi++;
465                } else {
466                    intersection.push(arrayA[ai]);
467                    ai++;
468                    bi++;
469                }
470            }
471            return intersection;
472            function compare(a, b) {
473                return a - b;
474            }
475        }
476    }();
477    var oParser = function() {
478        return {
479            local: getLocal,
480            prefetch: getPrefetch,
481            remote: getRemote
482        };
483        function getLocal(o) {
484            return o.local || null;
485        }
486        function getPrefetch(o) {
487            var prefetch, defaults;
488            defaults = {
489                url: null,
490                thumbprint: "",
491                ttl: 24 * 60 * 60 * 1e3,
492                filter: null,
493                ajax: {}
494            };
495            if (prefetch = o.prefetch || null) {
496                prefetch = _.isString(prefetch) ? {
497                    url: prefetch
498                } : prefetch;
499                prefetch = _.mixin(defaults, prefetch);
500                prefetch.thumbprint = VERSION + prefetch.thumbprint;
501                prefetch.ajax.type = prefetch.ajax.type || "GET";
502                prefetch.ajax.dataType = prefetch.ajax.dataType || "json";
503                !prefetch.url && $.error("prefetch requires url to be set");
504            }
505            return prefetch;
506        }
507        function getRemote(o) {
508            var remote, defaults;
509            defaults = {
510                url: null,
511                wildcard: "%QUERY",
512                replace: null,
513                rateLimitBy: "debounce",
514                rateLimitWait: 200,
515                send: null,
516                filter: null,
517                ajax: {}
518            };
519            if (remote = o.remote || null) {
520                remote = _.isString(remote) ? {
521                    url: remote
522                } : remote;
523                remote = _.mixin(defaults, remote);
524                remote.rateLimiter = /^throttle$/i.test(remote.rateLimitBy) ? byThrottle(remote.rateLimitWait) : byDebounce(remote.rateLimitWait);
525                remote.ajax.type = remote.ajax.type || "GET";
526                remote.ajax.dataType = remote.ajax.dataType || "json";
527                delete remote.rateLimitBy;
528                delete remote.rateLimitWait;
529                !remote.url && $.error("remote requires url to be set");
530            }
531            return remote;
532            function byDebounce(wait) {
533                return function(fn) {
534                    return _.debounce(fn, wait);
535                };
536            }
537            function byThrottle(wait) {
538                return function(fn) {
539                    return _.throttle(fn, wait);
540                };
541            }
542        }
543    }();
544    (function(root) {
545        var old, keys;
546        old = root.Bloodhound;
547        keys = {
548            data: "data",
549            protocol: "protocol",
550            thumbprint: "thumbprint"
551        };
552        root.Bloodhound = Bloodhound;
553        function Bloodhound(o) {
554            if (!o || !o.local && !o.prefetch && !o.remote) {
555                $.error("one of local, prefetch, or remote is required");
556            }
557            this.limit = o.limit || 5;
558            this.sorter = getSorter(o.sorter);
559            this.dupDetector = o.dupDetector || ignoreDuplicates;
560            this.local = oParser.local(o);
561            this.prefetch = oParser.prefetch(o);
562            this.remote = oParser.remote(o);
563            this.cacheKey = this.prefetch ? this.prefetch.cacheKey || this.prefetch.url : null;
564            this.index = new SearchIndex({
565                datumTokenizer: o.datumTokenizer,
566                queryTokenizer: o.queryTokenizer
567            });
568            this.storage = this.cacheKey ? new PersistentStorage(this.cacheKey) : null;
569        }
570        Bloodhound.noConflict = function noConflict() {
571            root.Bloodhound = old;
572            return Bloodhound;
573        };
574        Bloodhound.tokenizers = tokenizers;
575        _.mixin(Bloodhound.prototype, {
576            _loadPrefetch: function loadPrefetch(o) {
577                var that = this, serialized, deferred;
578                if (serialized = this._readFromStorage(o.thumbprint)) {
579                    this.index.bootstrap(serialized);
580                    deferred = $.Deferred().resolve();
581                } else {
582                    deferred = $.ajax(o.url, o.ajax).done(handlePrefetchResponse);
583                }
584                return deferred;
585                function handlePrefetchResponse(resp) {
586                    that.clear();
587                    that.add(o.filter ? o.filter(resp) : resp);
588                    that._saveToStorage(that.index.serialize(), o.thumbprint, o.ttl);
589                }
590            },
591            _getFromRemote: function getFromRemote(query, cb) {
592                var that = this, url, uriEncodedQuery;
593                query = query || "";
594                uriEncodedQuery = encodeURIComponent(query);
595                url = this.remote.replace ? this.remote.replace(this.remote.url, query) : this.remote.url.replace(this.remote.wildcard, uriEncodedQuery);
596                return this.transport.get(url, this.remote.ajax, handleRemoteResponse);
597                function handleRemoteResponse(err, resp) {
598                    err ? cb([]) : cb(that.remote.filter ? that.remote.filter(resp) : resp);
599                }
600            },
601            _saveToStorage: function saveToStorage(data, thumbprint, ttl) {
602                if (this.storage) {
603                    this.storage.set(keys.data, data, ttl);
604                    this.storage.set(keys.protocol, location.protocol, ttl);
605                    this.storage.set(keys.thumbprint, thumbprint, ttl);
606                }
607            },
608            _readFromStorage: function readFromStorage(thumbprint) {
609                var stored = {}, isExpired;
610                if (this.storage) {
611                    stored.data = this.storage.get(keys.data);
612                    stored.protocol = this.storage.get(keys.protocol);
613                    stored.thumbprint = this.storage.get(keys.thumbprint);
614                }
615                isExpired = stored.thumbprint !== thumbprint || stored.protocol !== location.protocol;
616                return stored.data && !isExpired ? stored.data : null;
617            },
618            _initialize: function initialize() {
619                var that = this, local = this.local, deferred;
620                deferred = this.prefetch ? this._loadPrefetch(this.prefetch) : $.Deferred().resolve();
621                local && deferred.done(addLocalToIndex);
622                this.transport = this.remote ? new Transport(this.remote) : null;
623                return this.initPromise = deferred.promise();
624                function addLocalToIndex() {
625                    that.add(_.isFunction(local) ? local() : local);
626                }
627            },
628            initialize: function initialize(force) {
629                return !this.initPromise || force ? this._initialize() : this.initPromise;
630            },
631            add: function add(data) {
632                this.index.add(data);
633            },
634            get: function get(query, cb) {
635                var that = this, matches = [], cacheHit = false;
636                matches = this.index.get(query);
637                matches = this.sorter(matches).slice(0, this.limit);
638                if (matches.length < this.limit && this.transport) {
639                    cacheHit = this._getFromRemote(query, returnRemoteMatches);
640                }
641                if (!cacheHit) {
642                    (matches.length > 0 || !this.transport) && cb && cb(matches);
643                }
644                function returnRemoteMatches(remoteMatches) {
645                    var matchesWithBackfill = matches.slice(0);
646                    _.each(remoteMatches, function(remoteMatch) {
647                        var isDuplicate;
648                        isDuplicate = _.some(matchesWithBackfill, function(match) {
649                            return that.dupDetector(remoteMatch, match);
650                        });
651                        !isDuplicate && matchesWithBackfill.push(remoteMatch);
652                        return matchesWithBackfill.length < that.limit;
653                    });
654                    cb && cb(that.sorter(matchesWithBackfill));
655                }
656            },
657            clear: function clear() {
658                this.index.reset();
659            },
660            clearPrefetchCache: function clearPrefetchCache() {
661                this.storage && this.storage.clear();
662            },
663            clearRemoteCache: function clearRemoteCache() {
664                this.transport && Transport.resetCache();
665            },
666            ttAdapter: function ttAdapter() {
667                return _.bind(this.get, this);
668            }
669        });
670        return Bloodhound;
671        function getSorter(sortFn) {
672            return _.isFunction(sortFn) ? sort : noSort;
673            function sort(array) {
674                return array.sort(sortFn);
675            }
676            function noSort(array) {
677                return array;
678            }
679        }
680        function ignoreDuplicates() {
681            return false;
682        }
683    })(this);
684    var html = {
685        wrapper: '<span class="twitter-typeahead"></span>',
686        dropdown: '<span class="tt-dropdown-menu"></span>',
687        dataset: '<div class="tt-dataset-%CLASS%"></div>',
688        suggestions: '<span class="tt-suggestions"></span>',
689        suggestion: '<div class="tt-suggestion"></div>'
690    };
691    var css = {
692        wrapper: {
693            position: "relative",
694            display: "inline-block"
695        },
696        hint: {
697            position: "absolute",
698            top: "0",
699            left: "0",
700            borderColor: "transparent",
701            boxShadow: "none"
702        },
703        input: {
704            position: "relative",
705            verticalAlign: "top",
706            backgroundColor: "transparent"
707        },
708        inputWithNoHint: {
709            position: "relative",
710            verticalAlign: "top"
711        },
712        dropdown: {
713            position: "absolute",
714            top: "100%",
715            left: "0",
716            zIndex: "100",
717            display: "none"
718        },
719        suggestions: {
720            display: "block"
721        },
722        suggestion: {
723            whiteSpace: "nowrap",
724            cursor: "pointer"
725        },
726        suggestionChild: {
727            whiteSpace: "normal"
728        },
729        ltr: {
730            left: "0",
731            right: "auto"
732        },
733        rtl: {
734            left: "auto",
735            right: " 0"
736        }
737    };
738    if (_.isMsie()) {
739        _.mixin(css.input, {
740            backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)"
741        });
742    }
743    if (_.isMsie() && _.isMsie() <= 7) {
744        _.mixin(css.input, {
745            marginTop: "-1px"
746        });
747    }
748    var EventBus = function() {
749        var namespace = "typeahead:";
750        function EventBus(o) {
751            if (!o || !o.el) {
752                $.error("EventBus initialized without el");
753            }
754            this.$el = $(o.el);
755        }
756        _.mixin(EventBus.prototype, {
757            trigger: function(type) {
758                var args = [].slice.call(arguments, 1);
759                this.$el.trigger(namespace + type, args);
760            }
761        });
762        return EventBus;
763    }();
764    var EventEmitter = function() {
765        var splitter = /\s+/, nextTick = getNextTick();
766        return {
767            onSync: onSync,
768            onAsync: onAsync,
769            off: off,
770            trigger: trigger
771        };
772        function on(method, types, cb, context) {
773            var type;
774            if (!cb) {
775                return this;
776            }
777            types = types.split(splitter);
778            cb = context ? bindContext(cb, context) : cb;
779            this._callbacks = this._callbacks || {};
780            while (type = types.shift()) {
781                this._callbacks[type] = this._callbacks[type] || {
782                    sync: [],
783                    async: []
784                };
785                this._callbacks[type][method].push(cb);
786            }
787            return this;
788        }
789        function onAsync(types, cb, context) {
790            return on.call(this, "async", types, cb, context);
791        }
792        function onSync(types, cb, context) {
793            return on.call(this, "sync", types, cb, context);
794        }
795        function off(types) {
796            var type;
797            if (!this._callbacks) {
798                return this;
799            }
800            types = types.split(splitter);
801            while (type = types.shift()) {
802                delete this._callbacks[type];
803            }
804            return this;
805        }
806        function trigger(types) {
807            var type, callbacks, args, syncFlush, asyncFlush;
808            if (!this._callbacks) {
809                return this;
810            }
811            types = types.split(splitter);
812            args = [].slice.call(arguments, 1);
813            while ((type = types.shift()) && (callbacks = this._callbacks[type])) {
814                syncFlush = getFlush(callbacks.sync, this, [ type ].
vendor: 4,669 bytes, lines 814-932
814concat(args));
815                asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args));
816                syncFlush() && nextTick(asyncFlush);
817            }
818            return this;
819        }
820        function getFlush(callbacks, context, args) {
821            return flush;
822            function flush() {
823                var cancelled;
824                for (var i = 0; !cancelled && i < callbacks.length; i += 1) {
825                    cancelled = callbacks[i].apply(context, args) === false;
826                }
827                return !cancelled;
828            }
829        }
830        function getNextTick() {
831            var nextTickFn;
832            if (window.setImmediate) {
833                nextTickFn = function nextTickSetImmediate(fn) {
834                    setImmediate(function() {
835                        fn();
836                    });
837                };
838            } else {
839                nextTickFn = function nextTickSetTimeout(fn) {
840                    setTimeout(function() {
841                        fn();
842                    }, 0);
843                };
844            }
845            return nextTickFn;
846        }
847        function bindContext(fn, context) {
848            return fn.bind ? fn.bind(context) : function() {
849                fn.apply(context, [].slice.call(arguments, 0));
850            };
851        }
852    }();
853    var highlight = function(doc) {
854        var defaults = {
855            node: null,
856            pattern: null,
857            tagName: "strong",
858            className: null,
859            wordsOnly: false,
860            caseSensitive: false
861        };
862        return function hightlight(o) {
863            var regex;
864            o = _.mixin({}, defaults, o);
865            if (!o.node || !o.pattern) {
866                return;
867            }
868            o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ];
869            regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly);
870            traverse(o.node, hightlightTextNode);
871            function hightlightTextNode(textNode) {
872                var match, patternNode;
873                if (match = regex.exec(textNode.data)) {
874                    wrapperNode = doc.createElement(o.tagName);
875                    o.className && (wrapperNode.className = o.className);
876                    patternNode = textNode.splitText(match.index);
877                    patternNode.splitText(match[0].length);
878                    wrapperNode.appendChild(patternNode.cloneNode(true));
879                    textNode.parentNode.replaceChild(wrapperNode, patternNode);
880                }
881                return !!match;
882            }
883            function traverse(el, hightlightTextNode) {
884                var childNode, TEXT_NODE_TYPE = 3;
885                for (var i = 0; i < el.childNodes.length; i++) {
886                    childNode = el.childNodes[i];
887                    if (childNode.nodeType === TEXT_NODE_TYPE) {
888                        i += hightlightTextNode(childNode) ? 1 : 0;
889                    } else {
890                        traverse(childNode, hightlightTextNode);
891                    }
892                }
893            }
894        };
895        function getRegex(patterns, caseSensitive, wordsOnly) {
896            var escapedPatterns = [], regexStr;
897            for (var i = 0; i < patterns.length; i++) {
898                escapedPatterns.push(_.escapeRegExChars(patterns[i]));
899            }
900            regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")";
901            return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i");
902        }
903    }(window.document);
904    var Input = function() {
905        var specialKeyCodeMap;
906        specialKeyCodeMap = {
907            9: "tab",
908            27: "esc",
909            37: "left",
910            39: "right",
911            13: "enter",
912            38: "up",
913            40: "down"
914        };
915        function Input(o) {
916            var that = this, onBlur, onFocus, onKeydown, onInput;
917            o = o || {};
918            if (!o.input) {
919                $.error("input is missing");
920            }
921            onBlur = _.bind(this._onBlur, this);
922            onFocus = _.bind(this._onFocus, this);
923            onKeydown = _.bind(this._onKeydown, this);
924            onInput = _.bind(this._onInput, this);
925            this.$hint = $(o.hint);
926            this.$input = $(o.input).on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown);
927            if (this.$hint.length === 0) {
928                this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop;
929            }
930            if (!_.isMsie()) {
931                this.$input.on("input.tt", onInput);
932            } else {
vendor: 1,932 bytes, lines 933-979
933                this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) {
934                    if (specialKeyCodeMap[$e.which || $e.keyCode]) {
935                        return;
936                    }
937                    _.defer(_.bind(that._onInput, that, $e));
938                });
939            }
940            this.query = this.$input.val();
941            this.$overflowHelper = buildOverflowHelper(this.$input);
942        }
943        Input.normalizeQuery = function(str) {
944            return (str || "").replace(/^\s*/g, "").replace(/\s{2,}/g, " ");
945        };
946        _.mixin(Input.prototype, EventEmitter, {
947            _onBlur: function onBlur() {
948                this.resetInputValue();
949                this.trigger("blurred");
950            },
951            _onFocus: function onFocus() {
952                this.trigger("focused");
953            },
954            _onKeydown: function onKeydown($e) {
955                var keyName = specialKeyCodeMap[$e.which || $e.keyCode];
956                this._managePreventDefault(keyName, $e);
957                if (keyName && this._shouldTrigger(keyName, $e)) {
958                    this.trigger(keyName + "Keyed", $e);
959                }
960            },
961            _onInput: function onInput() {
962                this._checkInputValue();
963            },
964            _managePreventDefault: function managePreventDefault(keyName, $e) {
965                var preventDefault, hintValue, inputValue;
966                switch (keyName) {
967                  case "tab":
968                    hintValue = this.getHint();
969                    inputValue = this.getInputValue();
970                    preventDefault = hintValue && hintValue !== inputValue && !withModifier($e);
971                    break;
972
973                  case "up":
974                  case "down":
975                    preventDefault = !withModifier($e);
976                    break;
977
978                  default:
979                    preventDefault = false;
980                }
981                preventDefault && $e.preventDefault();
982            },
983            _shouldTrigger: function shouldTrigger(keyName, $e) {
984                var trigger;
985                switch (keyName) {
986                  case "tab":
987                    trigger = !withModifier($e);
988                    break;
989
990                  default:
991                    trigger = true;
992                }
993                return trigger;
994            },
995            _checkInputValue: function checkInputValue() {
996                var inputValue, areEquivalent, hasDifferentWhitespace;
997                inputValue = this.getInputValue();
998                areEquivalent = areQueriesEquivalent(inputValue, this.query);
999                hasDifferentWhitespace = areEquivalent ? this.query.length !== inputValue.length : false;
1000                if (!areEquivalent) {
1001                    this.trigger("queryChanged", this.query = inputValue);
1002                } else if (hasDifferentWhitespace) {
1003                    this.trigger("whitespaceChanged", this.query);
1004                }
1005            },
1006            focus: function focus() {
1007                this.$input.focus();
1008            },
1009            blur: function blur() {
1010                this.$input.blur();
1011            },
1012            getQuery: function getQuery() {
1013                return this.query;
1014            },
1015            setQuery: function setQuery(query) {
1016                this.query = query;
1017            },
1018            getInputValue: function getInputValue() {
1019                return this.$input.val();
1020            },
1021            setInputValue: function setInputValue(value, silent) {
1022                this.$input.val(value);
1023                silent ? this.clearHint() : this._checkInputValue();
1024            },
1025            resetInputValue: function resetInputValue() {
1026                this.setInputValue(this.query, true);
1027            },
1028            getHint: function getHint() {
1029                return this.$hint.val();
1030            },
1031            setHint: function setHint(value) {
1032                this.$hint.val(value);
1033            },
1034            clearHint: function clearHint() {
1035                this.setHint("");
1036            },
1037            clearHintIfInvalid: function clearHintIfInvalid() {
1038                var val, hint, valIsPrefixOfHint, isValid;
1039                val = this.getInputValue();
1040                hint = this.getHint();
1041                valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0;
1042                isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow();
1043                !isValid && this.clearHint();
1044            },
1045            getLanguageDirection: function getLanguageDirection() {
1046                return (this.$input.css("direction") || "ltr").toLowerCase();
1047            },
1048            hasOverflow: function hasOverflow() {
1049                var constraint = this.$input.width() - 2;
1050                this.$overflowHelper.text(this.getInputValue());
1051                return this.$overflowHelper.width() >= constraint;
1052            },
1053            isCursorAtEnd: function() {
1054                var valueLength, selectionStart, range;
1055                valueLength = this.$input.val().length;
1056                selectionStart = this.$input[0].selectionStart;
1057                if (_.isNumber(selectionStart)) {
1058                    return selectionStart === valueLength;
1059                } else if (document.selection) {
1060                    range = document.selection.createRange();
1061                    range.moveStart("character", -valueLength);
1062                    return valueLength === range.text.length;
1063                }
1064                return true;
1065            },
1066            destroy: function destroy() {
1067                this.$hint.off(".tt");
1068                this.$input.off(".tt");
1069                this.$hint = this.$input = this.$overflowHelper = null;
1070            }
1071        });
1072        return Input;
1073        function buildOverflowHelper($input) {
1074            return $('<pre aria-hidden="true"></pre>').css({
1075                position: "absolute",
1076                visibility: "hidden",
1077                whiteSpace: "pre",
1078                fontFamily: $input.css("font-family"),
1079                fontSize: $input.css("font-size"),
1080                fontStyle: $input.css("font-style"),
1081                fontVariant: $input.css("font-variant"),
1082                fontWeight: $input.css("font-weight"),
1083                wordSpacing: $input.css("word-spacing"),
1084                letterSpacing: $input.css("letter-spacing"),
1085                textIndent: $input.css("text-indent"),
1086                textRendering: $input.css("text-rendering"),
1087                textTransform: $input.css("text-transform")
1088            }).insertAfter($input);
1089        }
1090        function areQueriesEquivalent(a, b) {
1091            return Input.normalizeQuery(a) === Input.normalizeQuery(b);
1092        }
1093        function withModifier($e) {
1094            return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey;
1095        }
1096    }();
1097    var Dataset = function() {
1098        var datasetKey = "ttDataset", valueKey = "ttValue", datumKey = "ttDatum";
1099        function Dataset(o) {
1100            o = o || {};
1101            o.templates = o.templates || {};
1102            if (!o.source) {
1103                $.error("missing source");
1104            }
1105            if (o.name && !isValidName(o.name)) {
1106                $.error("invalid dataset name: " + o.name);
1107            }
1108            this.query = null;
1109            this.highlight = !!o.highlight;
1110            this.name = o.name || _.getUniqueId();
1111            this.source = o.source;
1112            this.displayFn = getDisplayFn(o.display || o.displayKey);
1113            this.templates = getTemplates(o.templates, this.displayFn);
1114            this.$el = $(html.dataset.replace("%CLASS%", this.name));
1115        }
1116        Dataset.extractDatasetName = function extractDatasetName(el) {
1117            return $(el).data(datasetKey);
1118        };
1119        Dataset.extractValue = function extractDatum(el) {
1120            return $(el).data(valueKey);
1121        };
1122        Dataset.extractDatum = function extractDatum(el) {
1123            return $(el).data(datumKey);
1124        };
1125        _.mixin(Dataset.prototype, EventEmitter, {
1126            _render: function render(query, suggestions) {
1127                if (!this.$el) {
1128                    return;
1129                }
1130                var that = this, hasSuggestions;
1131                this.$el.empty();
1132                hasSuggestions = suggestions && suggestions.length;
1133                if (!hasSuggestions && this.templates.empty) {
1134                    this.$el.html(getEmptyHtml()).prepend(that.templates.header ? getHeaderHtml() : null).append(that.templates.footer ? getFooterHtml() : null);
1135                } else if (hasSuggestions) {
1136                    this.$el.html(getSuggestionsHtml()).prepend(that.templates.header ? getHeaderHtml() : null).append(that.templates.footer ? getFooterHtml() : null);
1137                }
1138                this.trigger("rendered");
1139                function getEmptyHtml() {
1140                    return that.templates.empty({
1141                        query: query,
1142                        isEmpty: true
1143                    });
1144                }
1145                function getSuggestionsHtml() {
1146                    var $suggestions, nodes;
1147                    $suggestions = $(html.suggestions).css(css.suggestions);
1148                    nodes = _.map(suggestions, getSuggestionNode);
1149                    $suggestions.append.apply($suggestions, nodes);
1150                    that.highlight && highlight({
1151                        node: $suggestions[0],
1152                        pattern: query
1153                    });
1154                    return $suggestions;
1155                    function getSuggestionNode(suggestion) {
1156                        var $el;
1157                        $el = $(html.suggestion).append(that.templates.suggestion(suggestion)).data(datasetKey, that.name).data(valueKey, that.displayFn(suggestion)).data(datumKey, suggestion);
1158                        $el.children().each(function() {
1159                            $(this).css(css.suggestionChild);
1160                        });
1161                        return $el;
1162                    }
1163                }
1164                function getHeaderHtml() {
1165                    return that.templates.header({
1166                        query: query,
1167                        isEmpty: !hasSuggestions
1168                    });
1169                }
1170                function getFooterHtml() {
1171                    return that.templates.footer({
1172                        query: query,
1173                        isEmpty: !hasSuggestions
1174                    });
1175                }
1176            },
1177            getRoot: function getRoot() {
1178                return this.$el;
1179            },
1180            update: function update(query) {
1181                var that = this;
1182                this.query = query;
1183                this.canceled = false;
1184                this.source(query, render);
1185                function render(suggestions) {
1186                    if (!that.canceled && query === that.query) {
1187                        that._render(query, suggestions);
1188                    }
1189                }
1190            },
1191            cancel: function cancel() {
1192                this.canceled = true;
1193            },
1194            clear: function clear() {
1195                this.cancel();
1196                this.$el.empty();
1197                this.trigger("rendered");
1198            },
1199            isEmpty: function isEmpty() {
1200                return this.$el.is(":empty");
1201            },
1202            destroy: function destroy() {
1203                this.$el = null;
1204            }
1205        });
1206        return Dataset;
1207        function getDisplayFn(display) {
1208            display = display || "value";
1209            return _.isFunction(display) ? display : displayFn;
1210            function displayFn(obj) {
1211                return obj[display];
1212            }
1213        }
1214        function getTemplates(templates, displayFn) {
1215            return {
1216                empty: templates.empty && _.templatify(templates.empty),
1217                header: templates.header && _.templatify(templates.header),
1218                footer: templates.footer && _.templatify(templates.footer),
1219                suggestion: templates.suggestion || suggestionTemplate
1220            };
1221            function suggestionTemplate(context) {
1222                return "<p>" + displayFn(context) + "</p>";
1223            }
1224        }
1225        function isValidName(str) {
1226            return /^[_a-zA-Z0-9-]+$/.test(str);
1227        }
1228    }();
1229    var Dropdown = function() {
1230        function Dropdown(o) {
1231            var that = this, onSuggestionClick, onSuggestionMouseEnter, onSuggestionMouseLeave;
1232            o = o || {};
1233            if (!o.menu) {
1234                $.error("menu is required");
1235            }
1236            this.isOpen = false;
1237            this.isEmpty = true;
1238            this.datasets = _.map(o.datasets, initializeDataset);
1239            onSuggestionClick = _.bind(this._onSuggestionClick, this);
1240            onSuggestionMouseEnter = _.bind(this._onSuggestionMouseEnter, this);
1241            onSuggestionMouseLeave = _.bind(this._onSuggestionMouseLeave, this);
1242            this.$menu = $(o.menu).on("click.tt", ".tt-suggestion", onSuggestionClick).on("mouseenter.tt", ".tt-suggestion", onSuggestionMouseEnter).on("mouseleave.tt", ".tt-suggestion", onSuggestionMouseLeave);
1243            _.each(this.datasets, function(dataset) {
1244                that.$menu.append(dataset.getRoot());
1245                dataset.onSync("rendered", that._onRendered, that);
1246            });
1247        }
1248        _.mixin(Dropdown.prototype, EventEmitter, {
1249            _onSuggestionClick: function onSuggestionClick($e) {
1250                this.trigger("suggestionClicked", $($e.currentTarget));
1251            },
1252            _onSuggestionMouseEnter: function onSuggestionMouseEnter($e) {
1253                this._removeCursor();
1254                this._setCursor($($e.currentTarget), true);
1255            },
1256            _onSuggestionMouseLeave: function onSuggestionMouseLeave() {
1257                this._removeCursor();
1258            },
1259            _onRendered: function onRendered() {
1260                this.isEmpty = _.every(this.datasets, isDatasetEmpty);
1261                this.isEmpty ? this._hide() : this.isOpen && this._show();
1262                this.trigger("datasetRendered");
1263                function isDatasetEmpty(dataset) {
1264                    return dataset.isEmpty();
1265                }
1266            },
1267            _hide: function() {
1268                this.$menu.hide();
1269            },
1270            _show: function() {
1271                this.$menu.css("display", "block");
1272            },
1273            _getSuggestions: function getSuggestions() {
1274                return this.$menu.find(".tt-suggestion");
1275            },
1276            _getCursor: function getCursor() {
1277                return this.$menu.find(".tt-cursor").first();
1278            },
1279            _setCursor: function setCursor($el, silent) {
1280                $el.first().addClass("tt-cursor");
1281                !silent && this.trigger("cursorMoved");
1282            },
vendor: 4,684 bytes, lines 1283-1396
1283            _removeCursor: function removeCursor() {
1284                this._getCursor().removeClass("tt-cursor");
1285            },
1286            _moveCursor: function moveCursor(increment) {
1287                var $suggestions, $oldCursor, newCursorIndex, $newCursor;
1288                if (!this.isOpen) {
1289                    return;
1290                }
1291                $oldCursor = this._getCursor();
1292                $suggestions = this._getSuggestions();
1293                this._removeCursor();
1294                newCursorIndex = $suggestions.index($oldCursor) + increment;
1295                newCursorIndex = (newCursorIndex + 1) % ($suggestions.length + 1) - 1;
1296                if (newCursorIndex === -1) {
1297                    this.trigger("cursorRemoved");
1298                    return;
1299                } else if (newCursorIndex < -1) {
1300                    newCursorIndex = $suggestions.length - 1;
1301                }
1302                this._setCursor($newCursor = $suggestions.eq(newCursorIndex));
1303                this._ensureVisible($newCursor);
1304            },
1305            _ensureVisible: function ensureVisible($el) {
1306                var elTop, elBottom, menuScrollTop, menuHeight;
1307                elTop = $el.position().top;
1308                elBottom = elTop + $el.outerHeight(true);
1309                menuScrollTop = this.$menu.scrollTop();
1310                menuHeight = this.$menu.height() + parseInt(this.$menu.css("paddingTop"), 10) + parseInt(this.$menu.css("paddingBottom"), 10);
1311                if (elTop < 0) {
1312                    this.$menu.scrollTop(menuScrollTop + elTop);
1313                } else if (menuHeight < elBottom) {
1314                    this.$menu.scrollTop(menuScrollTop + (elBottom - menuHeight));
1315                }
1316            },
1317            close: function close() {
1318                if (this.isOpen) {
1319                    this.isOpen = false;
1320                    this._removeCursor();
1321                    this._hide();
1322                    this.trigger("closed");
1323                }
1324            },
1325            open: function open() {
1326                if (!this.isOpen) {
1327                    this.isOpen = true;
1328                    !this.isEmpty && this._show();
1329                    this.trigger("opened");
1330                }
1331            },
1332            setLanguageDirection: function setLanguageDirection(dir) {
1333                this.$menu.css(dir === "ltr" ? css.ltr : css.rtl);
1334            },
1335            moveCursorUp: function moveCursorUp() {
1336                this._moveCursor(-1);
1337            },
1338            moveCursorDown: function moveCursorDown() {
1339                this._moveCursor(+1);
1340            },
1341            getDatumForSuggestion: function getDatumForSuggestion($el) {
1342                var datum = null;
1343                if ($el.length) {
1344                    datum = {
1345                        raw: Dataset.extractDatum($el),
1346                        value: Dataset.extractValue($el),
1347                        datasetName: Dataset.extractDatasetName($el)
1348                    };
1349                }
1350                return datum;
1351            },
1352            getDatumForCursor: function getDatumForCursor() {
1353                return this.getDatumForSuggestion(this._getCursor().first());
1354            },
1355            getDatumForTopSuggestion: function getDatumForTopSuggestion() {
1356                return this.getDatumForSuggestion(this._getSuggestions().first());
1357            },
1358            update: function update(query) {
1359                _.each(this.datasets, updateDataset);
1360                function updateDataset(dataset) {
1361                    dataset.update(query);
1362                }
1363            },
1364            empty: function empty() {
1365                _.each(this.datasets, clearDataset);
1366                this.isEmpty = true;
1367                function clearDataset(dataset) {
1368                    dataset.clear();
1369                }
1370            },
1371            isVisible: function isVisible() {
1372                return this.isOpen && !this.isEmpty;
1373            },
1374            destroy: function destroy() {
1375                this.$menu.off(".tt");
1376                this.$menu = null;
1377                _.each(this.datasets, destroyDataset);
1378                function destroyDataset(dataset) {
1379                    dataset.destroy();
1380                }
1381            }
1382        });
1383        return Dropdown;
1384        function initializeDataset(oDataset) {
1385            return new Dataset(oDataset);
1386        }
1387    }();
1388    var Typeahead = function() {
1389        var attrsKey = "ttAttrs";
1390        function Typeahead(o) {
1391            var $menu, $input, $hint;
1392            o = o || {};
1393            if (!o.input) {
1394                $.error("missing input");
1395            }
1396            this.isActivated = false;
1397            this.autoselect = !!o.autoselect;
1398            this.minLength = _.isNumber(o.minLength) ? o.minLength : 1;
1399            this.$node = buildDomStructure(o.input, o.withHint);
1400            $menu = this.$node.find(".tt-dropdown-menu");
1401            $input = this.$node.find(".tt-input");
1402            $hint = this.$node.find(".tt-hint");
1403            $input.on("blur.tt", function($e) {
1404                var active, isActive, hasActive;
1405                active = document.activeElement;
1406                isActive = $menu.is(active);
1407                hasActive = $menu.has(active).length > 0;
1408                if (_.isMsie() && (isActive || hasActive)) {
1409                    $e.preventDefault();
1410                    $e.stopImmediatePropagation();
1411                    _.defer(function() {
1412                        $input.focus();
1413                    });
1414                }
1415            });
1416            $menu.on("mousedown.tt", function($e) {
1417                $e.preventDefault();
1418            });
1419            this.eventBus = o.eventBus || new EventBus({
1420                el: $input
1421            });
1422            this.dropdown = new Dropdown({
1423                menu: $menu,
1424                datasets: o.datasets
1425            }).onSync("suggestionClicked", this._onSuggestionClicked, this).onSync("cursorMoved", this._onCursorMoved, this).onSync("cursorRemoved", this._onCursorRemoved, this).onSync("opened", this._onOpened, this).onSync("closed", this._onClosed, this).onAsync("datasetRendered", this._onDatasetRendered, this);
1426            this.input = new Input({
1427                input: $input,
1428                hint: $hint
1429            }).onSync("focused", this._onFocused, this).onSync("blurred", this._onBlurred, this).onSync("enterKeyed", this._onEnterKeyed, this).onSync("tabKeyed", this._onTabKeyed, this).onSync("escKeyed", this._onEscKeyed, this).onSync("upKeyed", this._onUpKeyed, this).onSync("downKeyed", this._onDownKeyed, this).onSync("leftKeyed", this._onLeftKeyed, this).onSync("rightKeyed", this._onRightKeyed, this).onSync("queryChanged", this._onQueryChanged, this).onSync("whitespaceChanged", this._onWhitespaceChanged, this);
1430            this._setLanguageDirection();
1431        }
1432        _.mixin(Typeahead.prototype, {
1433            _onSuggestionClicked: function onSuggestionClicked(type, $el) {
1434                var datum;
1435                if (datum = this.dropdown.getDatumForSuggestion($el)) {
1436                    this._select(datum);
1437                }
1438            },
1439            _onCursorMoved: function onCursorMoved() {
1440                var datum = this.dropdown.getDatumForCursor();
1441                this.input.setInputValue(datum.value, true);
1442                this.eventBus.trigger("cursorchanged", datum.raw, datum.datasetName);
1443            },
1444            _onCursorRemoved: function onCursorRemoved() {
1445                this.input.resetInputValue();
1446                this._updateHint();
1447            },
1448            _onDatasetRendered: function onDatasetRendered() {
1449                this._updateHint();
1450            },
1451            _onOpened: function onOpened() {
1452                this._updateHint();
1453                this.eventBus.trigger("opened");
1454            },
1455            _onClosed: function onClosed() {
1456                this.input.clearHint();
1457                this.eventBus.trigger("closed");
1458            },
1459            _onFocused: function onFocused() {
1460                this.isActivated = true;
1461                this.dropdown.open();
1462            },
1463            _onBlurred: function onBlurred() {
1464                this.isActivated = false;
1465                this.dropdown.empty();
1466                this.dropdown.close();
1467            },
1468            _onEnterKeyed: function onEnterKeyed(type, $e) {
1469                var cursorDatum, topSuggestionDatum;
1470                cursorDatum = this.dropdown.getDatumForCursor();
1471                topSuggestionDatum = this.dropdown.getDatumForTopSuggestion();
1472                if (cursorDatum) {
1473                    this._select(cursorDatum);
1474                    $e.preventDefault();
1475                } else if (this.autoselect && topSuggestionDatum) {
1476                    this._select(topSuggestionDatum);
1477                    $e.preventDefault();
1478                }
1479            },
1480            _onTabKeyed: function onTabKeyed(type, $e) {
1481                var datum;
1482                if (datum = this.dropdown.getDatumForCursor()) {
1483                    this._select(datum);
1484                    $e.preventDefault();
1485                } else {
1486                    this._autocomplete(true);
1487                }
1488            },
1489            _onEscKeyed: function onEscKeyed() {
1490                this.dropdown.close();
1491                this.input.resetInputValue();
1492            },
1493            _onUpKeyed: function onUpKeyed() {
1494                var query = this.input.getQuery();
1495                this.dropdown.isEmpty && query.length >= this.minLength ? this.dropdown.update(query) : this.dropdown.moveCursorUp();
1496                this.dropdown.open();
1497            },
1498            _onDownKeyed: function onDownKeyed() {
1499                var query = this.input.getQuery();
1500                this.dropdown.isEmpty && query.length >= this.minLength ? this.dropdown.update(query) : this.dropdown.moveCursorDown();
1501                this.dropdown.open();
1502            },
1503            _onLeftKeyed: function onLeftKeyed() {
1504                this.dir === "rtl" && this._autocomplete();
1505            },
1506            _onRightKeyed: function onRightKeyed() {
1507                this.dir === "ltr" && this._autocomplete();
1508            },
1509            _onQueryChanged: function onQueryChanged(e, query) {
1510                this.input.clearHintIfInvalid();
1511                query.length >= this.minLength ? this.dropdown.update(query) : this.dropdown.empty();
1512                this.dropdown.open();
1513                this._setLanguageDirection();
1514            },
1515            _onWhitespaceChanged: function onWhitespaceChanged() {
1516                this._updateHint();
1517                this.dropdown.open();
1518            },
1519            _setLanguageDirection: function setLanguageDirection() {
1520                var dir;
1521                if (this.dir !== (dir = this.input.getLanguageDirection())) {
1522                    this.dir = dir;
1523                    this.$node.css("direction", dir);
1524                    this.dropdown.setLanguageDirection(dir);
1525                }
1526            },
1527            _updateHint: function updateHint() {
1528                var datum, val, query, escapedQuery, frontMatchRegEx, match;
1529                datum = this.dropdown.getDatumForTopSuggestion();
1530                if (datum && this.dropdown.isVisible() && !this.input.hasOverflow()) {
1531                    val = this.input.getInputValue();
1532                    query = Input.normalizeQuery(val);
1533                    escapedQuery = _.escapeRegExChars(query);
1534                    frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i");
1535                    match = frontMatchRegEx.exec(datum.value);
1536                    match ? this.input.setHint(val + match[1]) : this.input.clearHint();
1537                } else {
1538                    this.input.clearHint();
1539                }
1540            },
1541            _autocomplete: function autocomplete(laxCursor) {
1542                var hint, query, isCursorAtEnd, datum;
1543                hint = this.input.getHint();
1544                query = this.input.getQuery();
1545                isCursorAtEnd = laxCursor || this.input.isCursorAtEnd();
1546                if (hint && query !== hint && isCursorAtEnd) {
1547                    datum = this.dropdown.getDatumForTopSuggestion();
1548                    datum && this.input.setInputValue(datum.value);
1549                    this.eventBus.trigger("autocompleted", datum.raw, datum.datasetName);
1550                }
1551            },
1552            _select: function select(datum) {
1553                this.input.setQuery(datum.value);
1554                this.input.setInputValue(datum.value, true);
1555                this._setLanguageDirection();
1556                this.eventBus.trigger("selected", datum.raw, datum.datasetName);
1557                this.dropdown.close();
1558                _.defer(_.bind(this.dropdown.empty, this.dropdown));
1559            },
1560            open: function open() {
1561                this.dropdown.open();
1562            },
1563            close: function close() {
1564                this.dropdown.close();
1565            },
1566            setVal: function setVal(val) {
1567                if (this.isActivated) {
1568                    this.input.setInputValue(val);
1569                } else {
1570                    this.input.setQuery(val);
1571                    this.input.setInputValue(val, true);
1572                }
1573                this._setLanguageDirection();
1574            },
1575            getVal: function getVal() {
1576                return this.input.getQuery();
1577            },
1578            destroy: function destroy() {
1579                this.input.destroy();
1580                this.dropdown.destroy();
1581                destroyDomStructure(this.$node);
1582                this.$node = null;
1583            }
1584        });
1585        return Typeahead;
1586        function buildDomStructure(input, withHint) {
1587            var $input, $wrapper, $dropdown, $hint;
1588            $input = $(input);
1589            $wrapper = $(html.wrapper).css(css.wrapper);
1590            $dropdown = $(html.dropdown).css(css.dropdown);
1591            $hint = $input.clone().css(css.hint).css(getBackgroun
1591dStyles($input));
1592            $hint.val("").removeData().addClass("tt-hint").removeAttr("id name placeholder").prop("disabled", true).attr({
1593                autocomplete: "off",
1594                spellcheck: "false"
1595            });
1596            $input.data(attrsKey, {
1597                dir: $input.attr("dir"),
1598                autocomplete: $input.attr("autocomplete"),
1599                spellcheck: $input.attr("spellcheck"),
1600                style: $input.attr("style")
1601            });
1602            $input.addClass("tt-input").attr({
1603                autocomplete: "off",
1604                spellcheck: false
1605            }).css(withHint ? css.input : css.inputWithNoHint);
1606            try {
1607                !$input.attr("dir") && $input.attr("dir", "auto");
1608            } catch (e) {}
1609            return $input.wrap($wrapper).parent().prepend(withHint ? $hint : null).append($dropdown);
1610        }
1611        function getBackgroundStyles($el) {
1612            return {
1613                backgroundAttachment: $el.css("background-attachment"),
1614                backgroundClip: $el.css("background-clip"),
1615                backgroundColor: $el.css("background-color"),
1616                backgroundImage: $el.css("background-image"),
1617                backgroundOrigin: $el.css("background-origin"),
1618                backgroundPosition: $el.css("background-position"),
1619                backgroundRepeat: $el.css("background-repeat"),
1620                backgroundSize: $el.css("background-size")
1621            };
1622        }
1623        function destroyDomStructure($node) {
1624            var $input = $node.find(".tt-input");
1625            _.each($input.data(attrsKey), function(val, key) {
1626                _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val);
1627            });
1628            $input.detach().removeData(attrsKey).removeClass("tt-input").insertAfter($node);
1629            $node.remove();
1630        }
1631    }();
1632    (function() {
1633        var old, typeaheadKey, methods;
1634        old = $.fn.typeahead;
1635        typeaheadKey = "ttTypeahead";
1636        methods = {
1637            initialize: function initialize(o, datasets) {
1638                datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1);
1639                o = o || {};
1640                return this.each(attach);
1641                function attach() {
1642                    var $input = $(this), eventBus, typeahead;
1643                    _.each(datasets, function(d) {
1644                        d.highlight = !!o.highlight;
1645                    });
1646                    typeahead = new Typeahead({
1647                        input: $input,
1648                        eventBus: eventBus = new EventBus({
1649                            el: $input
1650                        }),
1651                        withHint: _.isUndefined(o.hint) ? true : !!o.hint,
1652                        minLength: o.minLength,
1653                        autoselect: o.autoselect,
1654                        datasets: datasets
1655                    });
1656                    $input.data(typeaheadKey, typeahead);
1657                }
1658            },
1659            open: function open() {
1660                return this.each(openTypeahead);
1661                function openTypeahead() {
1662                    var $input = $(this), typeahead;
1663                    if (typeahead = $input.data(typeaheadKey)) {
1664                        typeahead.open();
1665                    }
1666                }
1667            },
1668            close: function close() {
1669                return this.each(closeTypeahead);
1670                function closeTypeahead() {
1671                    var $input = $(this), typeahead;
1672                    if (typeahead = $input.data(typeaheadKey)) {
1673                        typeahead.close();
1674                    }
1675                }
1676            },
1677            val: function val(newVal) {
1678                return !arguments.length ? getVal(this.first()) : this.each(setVal);
1679                function setVal() {
1680                    var $input = $(this), typeahead;
1681                    if (typeahead = $input.data(typeaheadKey)) {
1682                        typeahead.setVal(newVal);
1683                    }
1684                }
1685                function getVal($input) {
1686                    var typeahead, query;
1687                    if (typeahead = $input.data(typeaheadKey)) {
1688                        query = typeahead.getVal();
1689                    }
1690                    return query;
1691                }
1692            },
1693            destroy: function destroy() {
1694                return this.each(unattach);
1695                function unattach() {
1696                    var $input = $(this), typeahead;
1697                    if (typeahead = $input.data(typeaheadKey)) {
1698                        typeahead.destroy();
1699                        $input.removeData(typeaheadKey);
1700                    }
1701                }
1702            }
1703        };
1704        $.fn.typeahead = function(method) {
1705            if (methods[method]) {
1706                return methods[method].apply(this, [].slice.call(arguments, 1));
1707            } else {
1708                return methods.initialize.apply(this, arguments);
1709            }
1710        };
1711        $.fn.typeahead.noConflict = function noConflict() {
1712            $.fn.typeahead = old;
1713            return this;
1714        };
1715    })();
1716})(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.