JAVASCRIPT   23

typeahead js

Guest on 13th August 2022 12:43:18 AM

  1. /*!
  2.  * typeahead.js 0.11.1
  3.  * https://github.com/twitter/typeahead.js
  4.  * Copyright  Twitter, Inc. and other contributors; Licensed MIT
  5.  */
  6.  
  7. (function(root, factory) {
  8.     if (typeof define === "function" && define.amd) {
  9.         define("bloodhound", [ "jquery" ], function(a0) {
  10.             return root["Bloodhound"] = factory(a0);
  11.         });
  12.     } else if (typeof exports === "object") {
  13.         module.exports = factory(require("jquery"));
  14.     } else {
  15.         root["Bloodhound"] = factory(jQuery);
  16.     }
  17. })(this, function($) {
  18.     var _ = function() {
  19.         "use strict";
  20.         return {
  21.             isMsie: function() {
  22.                 return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false;
  23.             },
  24.             isBlankString: function(str) {
  25.                 return !str || /^\s*$/.test(str);
  26.             },
  27.             escapeRegExChars: function(str) {
  28.                 return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
  29.             },
  30.             isString: function(obj) {
  31.                 return typeof obj === "string";
  32.             },
  33.             isNumber: function(obj) {
  34.                 return typeof obj === "number";
  35.             },
  36.             isArray: $.isArray,
  37.             isFunction: $.isFunction,
  38.             isObject: $.isPlainObject,
  39.             isUndefined: function(obj) {
  40.                 return typeof obj === "undefined";
  41.             },
  42.             isElement: function(obj) {
  43.                 return !!(obj && obj.nodeType === 1);
  44.             },
  45.             isJQuery: function(obj) {
  46.                 return obj instanceof $;
  47.             },
  48.             toStr: function toStr(s) {
  49.                 return _.isUndefined(s) || s === null ? "" : s + "";
  50.             },
  51.             bind: $.proxy,
  52.             each: function(collection, cb) {
  53.                 $.each(collection, reverseArgs);
  54.                 function reverseArgs(index, value) {
  55.                     return cb(value, index);
  56.                 }
  57.             },
  58.             map: $.map,
  59.             filter: $.grep,
  60.             every: function(obj, test) {
  61.                 var result = true;
  62.                 if (!obj) {
  63.                     return result;
  64.                 }
  65.                 $.each(obj, function(key, val) {
  66.                     if (!(result = test.call(null, val, key, obj))) {
  67.                         return false;
  68.                     }
  69.                 });
  70.                 return !!result;
  71.             },
  72.             some: function(obj, test) {
  73.                 var result = false;
  74.                 if (!obj) {
  75.                     return result;
  76.                 }
  77.                 $.each(obj, function(key, val) {
  78.                     if (result = test.call(null, val, key, obj)) {
  79.                         return false;
  80.                     }
  81.                 });
  82.                 return !!result;
  83.             },
  84.             mixin: $.extend,
  85.             identity: function(x) {
  86.                 return x;
  87.             },
  88.             clone: function(obj) {
  89.                 return $.extend(true, {}, obj);
  90.             },
  91.             getIdGenerator: function() {
  92.                 var counter = 0;
  93.                 return function() {
  94.                     return counter++;
  95.                 };
  96.             },
  97.             templatify: function templatify(obj) {
  98.                 return $.isFunction(obj) ? obj : template;
  99.                 function template() {
  100.                     return String(obj);
  101.                 }
  102.             },
  103.             defer: function(fn) {
  104.                 setTimeout(fn, 0);
  105.             },
  106.             debounce: function(func, wait, immediate) {
  107.                 var timeout, result;
  108.                 return function() {
  109.                     var context = this, args = arguments, later, callNow;
  110.                     later = function() {
  111.                         timeout = null;
  112.                         if (!immediate) {
  113.                             result = func.apply(context, args);
  114.                         }
  115.                     };
  116.                     callNow = immediate && !timeout;
  117.                     clearTimeout(timeout);
  118.                     timeout = setTimeout(later, wait);
  119.                     if (callNow) {
  120.                         result = func.apply(context, args);
  121.                     }
  122.                     return result;
  123.                 };
  124.             },
  125.             throttle: function(func, wait) {
  126.                 var context, args, timeout, result, previous, later;
  127.                 previous = 0;
  128.                 later = function() {
  129.                     previous = new Date();
  130.                     timeout = null;
  131.                     result = func.apply(context, args);
  132.                 };
  133.                 return function() {
  134.                     var now = new Date(), remaining = wait - (now - previous);
  135.                     context = this;
  136.                     args = arguments;
  137.                     if (remaining <= 0) {
  138.                         clearTimeout(timeout);
  139.                         timeout = null;
  140.                         previous = now;
  141.                         result = func.apply(context, args);
  142.                     } else if (!timeout) {
  143.                         timeout = setTimeout(later, remaining);
  144.                     }
  145.                     return result;
  146.                 };
  147.             },
  148.             stringify: function(val) {
  149.                 return _.isString(val) ? val : JSON.stringify(val);
  150.             },
  151.             noop: function() {}
  152.         };
  153.     }();
  154.     var VERSION = "0.11.1";
  155.     var tokenizers = function() {
  156.         "use strict";
  157.         return {
  158.             nonword: nonword,
  159.             whitespace: whitespace,
  160.             obj: {
  161.                 nonword: getObjTokenizer(nonword),
  162.                 whitespace: getObjTokenizer(whitespace)
  163.             }
  164.         };
  165.         function whitespace(str) {
  166.             str = _.toStr(str);
  167.             return str ? str.split(/\s+/) : [];
  168.         }
  169.         function nonword(str) {
  170.             str = _.toStr(str);
  171.             return str ? str.split(/\W+/) : [];
  172.         }
  173.         function getObjTokenizer(tokenizer) {
  174.             return function setKey(keys) {
  175.                 keys = _.isArray(keys) ? keys : [].slice.call(arguments, 0);
  176.                 return function tokenize(o) {
  177.                     var tokens = [];
  178.                     _.each(keys, function(k) {
  179.                         tokens = tokens.concat(tokenizer(_.toStr(o[k])));
  180.                     });
  181.                     return tokens;
  182.                 };
  183.             };
  184.         }
  185.     }();
  186.     var LruCache = function() {
  187.         "use strict";
  188.         function LruCache(maxSize) {
  189.             this.maxSize = _.isNumber(maxSize) ? maxSize : 100;
  190.             this.reset();
  191.             if (this.maxSize <= 0) {
  192.                 this.set = this.get = $.noop;
  193.             }
  194.         }
  195.         _.mixin(LruCache.prototype, {
  196.             set: function set(key, val) {
  197.                 var tailItem = this.list.tail, node;
  198.                 if (this.size >= this.maxSize) {
  199.                     this.list.remove(tailItem);
  200.                     delete this.hash[tailItem.key];
  201.                     this.size--;
  202.                 }
  203.                 if (node = this.hash[key]) {
  204.                     node.val = val;
  205.                     this.list.moveToFront(node);
  206.                 } else {
  207.                     node = new Node(key, val);
  208.                     this.list.add(node);
  209.                     this.hash[key] = node;
  210.                     this.size++;
  211.                 }
  212.             },
  213.             get: function get(key) {
  214.                 var node = this.hash[key];
  215.                 if (node) {
  216.                     this.list.moveToFront(node);
  217.                     return node.val;
  218.                 }
  219.             },
  220.             reset: function reset() {
  221.                 this.size = 0;
  222.                 this.hash = {};
  223.                 this.list = new List();
  224.             }
  225.         });
  226.         function List() {
  227.             this.head = this.tail = null;
  228.         }
  229.         _.mixin(List.prototype, {
  230.             add: function add(node) {
  231.                 if (this.head) {
  232.                     node.next = this.head;
  233.                     this.head.prev = node;
  234.                 }
  235.                 this.head = node;
  236.                 this.tail = this.tail || node;
  237.             },
  238.             remove: function remove(node) {
  239.                 node.prev ? node.prev.next = node.next : this.head = node.next;
  240.                 node.next ? node.next.prev = node.prev : this.tail = node.prev;
  241.             },
  242.             moveToFront: function(node) {
  243.                 this.remove(node);
  244.                 this.add(node);
  245.             }
  246.         });
  247.         function Node(key, val) {
  248.             this.key = key;
  249.             this.val = val;
  250.             this.prev = this.next = null;
  251.         }
  252.         return LruCache;
  253.     }();
  254.     var PersistentStorage = function() {
  255.         "use strict";
  256.         var LOCAL_STORAGE;
  257.         try {
  258.             LOCAL_STORAGE = window.localStorage;
  259.             LOCAL_STORAGE.setItem("~~~", "!");
  260.             LOCAL_STORAGE.removeItem("~~~");
  261.         } catch (err) {
  262.             LOCAL_STORAGE = null;
  263.         }
  264.         function PersistentStorage(namespace, override) {
  265.             this.prefix = [ "__", namespace, "__" ].join("");
  266.             this.ttlKey = "__ttl__";
  267.             this.keyMatcher = new RegExp("^" + _.escapeRegExChars(this.prefix));
  268.             this.ls = override || LOCAL_STORAGE;
  269.             !this.ls && this._noop();
  270.         }
  271.         _.mixin(PersistentStorage.prototype, {
  272.             _prefix: function(key) {
  273.                 return this.prefix + key;
  274.             },
  275.             _ttlKey: function(key) {
  276.                 return this._prefix(key) + this.ttlKey;
  277.             },
  278.             _noop: function() {
  279.                 this.get = this.set = this.remove = this.clear = this.isExpired = _.noop;
  280.             },
  281.             _safeSet: function(key, val) {
  282.                 try {
  283.                     this.ls.setItem(key, val);
  284.                 } catch (err) {
  285.                     if (err.name === "QuotaExceededError") {
  286.                         this.clear();
  287.                         this._noop();
  288.                     }
  289.                 }
  290.             },
  291.             get: function(key) {
  292.                 if (this.isExpired(key)) {
  293.                     this.remove(key);
  294.                 }
  295.                 return decode(this.ls.getItem(this._prefix(key)));
  296.             },
  297.             set: function(key, val, ttl) {
  298.                 if (_.isNumber(ttl)) {
  299.                     this._safeSet(this._ttlKey(key), encode(now() + ttl));
  300.                 } else {
  301.                     this.ls.removeItem(this._ttlKey(key));
  302.                 }
  303.                 return this._safeSet(this._prefix(key), encode(val));
  304.             },
  305.             remove: function(key) {
  306.                 this.ls.removeItem(this._ttlKey(key));
  307.                 this.ls.removeItem(this._prefix(key));
  308.                 return this;
  309.             },
  310.             clear: function() {
  311.                 var i, keys = gatherMatchingKeys(this.keyMatcher);
  312.                 for (i = keys.length; i--; ) {
  313.                     this.remove(keys[i]);
  314.                 }
  315.                 return this;
  316.             },
  317.             isExpired: function(key) {
  318.                 var ttl = decode(this.ls.getItem(this._ttlKey(key)));
  319.                 return _.isNumber(ttl) && now() > ttl ? true : false;
  320.             }
  321.         });
  322.         return PersistentStorage;
  323.         function now() {
  324.             return new Date().getTime();
  325.         }
  326.         function encode(val) {
  327.             return JSON.stringify(_.isUndefined(val) ? null : val);
  328.         }
  329.         function decode(val) {
  330.             return $.parseJSON(val);
  331.         }
  332.         function gatherMatchingKeys(keyMatcher) {
  333.             var i, key, keys = [], len = LOCAL_STORAGE.length;
  334.             for (i = 0; i < len; i++) {
  335.                 if ((key = LOCAL_STORAGE.key(i)).match(keyMatcher)) {
  336.                     keys.push(key.replace(keyMatcher, ""));
  337.                 }
  338.             }
  339.             return keys;
  340.         }
  341.     }();
  342.     var Transport = function() {
  343.         "use strict";
  344.         var pendingRequestsCount = 0, pendingRequests = {}, maxPendingRequests = 6, sharedCache = new LruCache(10);
  345.         function Transport(o) {
  346.             o = o || {};
  347.             this.cancelled = false;
  348.             this.lastReq = null;
  349.             this._send = o.transport;
  350.             this._get = o.limiter ? o.limiter(this._get) : this._get;
  351.             this._cache = o.cache === false ? new LruCache(0) : sharedCache;
  352.         }
  353.         Transport.setMaxPendingRequests = function setMaxPendingRequests(num) {
  354.             maxPendingRequests = num;
  355.         };
  356.         Transport.resetCache = function resetCache() {
  357.             sharedCache.reset();
  358.         };
  359.         _.mixin(Transport.prototype, {
  360.             _fingerprint: function fingerprint(o) {
  361.                 o = o || {};
  362.                 return o.url + o.type + $.param(o.data || {});
  363.             },
  364.             _get: function(o, cb) {
  365.                 var that = this, fingerprint, jqXhr;
  366.                 fingerprint = this._fingerprint(o);
  367.                 if (this.cancelled || fingerprint !== this.lastReq) {
  368.                     return;
  369.                 }
  370.                 if (jqXhr = pendingRequests[fingerprint]) {
  371.                     jqXhr.done(done).fail(fail);
  372.                 } else if (pendingRequestsCount < maxPendingRequests) {
  373.                     pendingRequestsCount++;
  374.                     pendingRequests[fingerprint] = this._send(o).done(done).fail(fail).always(always);
  375.                 } else {
  376.                     this.onDeckRequestArgs = [].slice.call(arguments, 0);
  377.                 }
  378.                 function done(resp) {
  379.                     cb(null, resp);
  380.                     that._cache.set(fingerprint, resp);
  381.                 }
  382.                 function fail() {
  383.                     cb(true);
  384.                 }
  385.                 function always() {
  386.                     pendingRequestsCount--;
  387.                     delete pendingRequests[fingerprint];
  388.                     if (that.onDeckRequestArgs) {
  389.                         that._get.apply(that, that.onDeckRequestArgs);
  390.                         that.onDeckRequestArgs = null;
  391.                     }
  392.                 }
  393.             },
  394.             get: function(o, cb) {
  395.                 var resp, fingerprint;
  396.                 cb = cb || $.noop;
  397.                 o = _.isString(o) ? {
  398.                     url: o
  399.                 } : o || {};
  400.                 fingerprint = this._fingerprint(o);
  401.                 this.cancelled = false;
  402.                 this.lastReq = fingerprint;
  403.                 if (resp = this._cache.get(fingerprint)) {
  404.                     cb(null, resp);
  405.                 } else {
  406.                     this._get(o, cb);
  407.                 }
  408.             },
  409.             cancel: function() {
  410.                 this.cancelled = true;
  411.             }
  412.         });
  413.         return Transport;
  414.     }();
  415.     var SearchIndex = window.SearchIndex = function() {
  416.         "use strict";
  417.         var CHILDREN = "c", IDS = "i";
  418.         function SearchIndex(o) {
  419.             o = o || {};
  420.             if (!o.datumTokenizer || !o.queryTokenizer) {
  421.                 $.error("datumTokenizer and queryTokenizer are both required");
  422.             }
  423.             this.identify = o.identify || _.stringify;
  424.             this.datumTokenizer = o.datumTokenizer;
  425.             this.queryTokenizer = o.queryTokenizer;
  426.             this.reset();
  427.         }
  428.         _.mixin(SearchIndex.prototype, {
  429.             bootstrap: function bootstrap(o) {
  430.                 this.datums = o.datums;
  431.                 this.trie = o.trie;
  432.             },
  433.             add: function(data) {
  434.                 var that = this;
  435.                 data = _.isArray(data) ? data : [ data ];
  436.                 _.each(data, function(datum) {
  437.                     var id, tokens;
  438.                     that.datums[id = that.identify(datum)] = datum;
  439.                     tokens = normalizeTokens(that.datumTokenizer(datum));
  440.                     _.each(tokens, function(token) {
  441.                         var node, chars, ch;
  442.                         node = that.trie;
  443.                         chars = token.split("");
  444.                         while (ch = chars.shift()) {
  445.                             node = node[CHILDREN][ch] || (node[CHILDREN][ch] = newNode());
  446.                             node[IDS].push(id);
  447.                         }
  448.                     });
  449.                 });
  450.             },
  451.             get: function get(ids) {
  452.                 var that = this;
  453.                 return _.map(ids, function(id) {
  454.                     return that.datums[id];
  455.                 });
  456.             },
  457.             search: function search(query) {
  458.                 var that = this, tokens, matches;
  459.                 tokens = normalizeTokens(this.queryTokenizer(query));
  460.                 _.each(tokens, function(token) {
  461.                     var node, chars, ch, ids;
  462.                     if (matches && matches.length === 0) {
  463.                         return false;
  464.                     }
  465.                     node = that.trie;
  466.                     chars = token.split("");
  467.                     while (node && (ch = chars.shift())) {
  468.                         node = node[CHILDREN][ch];
  469.                     }
  470.                     if (node && chars.length === 0) {
  471.                         ids = node[IDS].slice(0);
  472.                         matches = matches ? getIntersection(matches, ids) : ids;
  473.                     } else {
  474.                         matches = [];
  475.                         return false;
  476.                     }
  477.                 });
  478.                 return matches ? _.map(unique(matches), function(id) {
  479.                     return that.datums[id];
  480.                 }) : [];
  481.             },
  482.             all: function all() {
  483.                 var values = [];
  484.                 for (var key in this.datums) {
  485.                     values.push(this.datums[key]);
  486.                 }
  487.                 return values;
  488.             },
  489.             reset: function reset() {
  490.                 this.datums = {};
  491.                 this.trie = newNode();
  492.             },
  493.             serialize: function serialize() {
  494.                 return {
  495.                     datums: this.datums,
  496.                     trie: this.trie
  497.                 };
  498.             }
  499.         });
  500.         return SearchIndex;
  501.         function normalizeTokens(tokens) {
  502.             tokens = _.filter(tokens, function(token) {
  503.                 return !!token;
  504.             });
  505.             tokens = _.map(tokens, function(token) {
  506.                 return token.toLowerCase();
  507.             });
  508.             return tokens;
  509.         }
  510.         function newNode() {
  511.             var node = {};
  512.             node[IDS] = [];
  513.             node[CHILDREN] = {};
  514.             return node;
  515.         }
  516.         function unique(array) {
  517.             var seen = {}, uniques = [];
  518.             for (var i = 0, len = array.length; i < len; i++) {
  519.                 if (!seen[array[i]]) {
  520.                     seen[array[i]] = true;
  521.                     uniques.push(array[i]);
  522.                 }
  523.             }
  524.             return uniques;
  525.         }
  526.         function getIntersection(arrayA, arrayB) {
  527.             var ai = 0, bi = 0, intersection = [];
  528.             arrayA = arrayA.sort();
  529.             arrayB = arrayB.sort();
  530.             var lenArrayA = arrayA.length, lenArrayB = arrayB.length;
  531.             while (ai < lenArrayA && bi < lenArrayB) {
  532.                 if (arrayA[ai] < arrayB[bi]) {
  533.                     ai++;
  534.                 } else if (arrayA[ai] > arrayB[bi]) {
  535.                     bi++;
  536.                 } else {
  537.                     intersection.push(arrayA[ai]);
  538.                     ai++;
  539.                     bi++;
  540.                 }
  541.             }
  542.             return intersection;
  543.         }
  544.     }();
  545.     var Prefetch = function() {
  546.         "use strict";
  547.         var keys;
  548.         keys = {
  549.             data: "data",
  550.             protocol: "protocol",
  551.             thumbprint: "thumbprint"
  552.         };
  553.         function Prefetch(o) {
  554.             this.url = o.url;
  555.             this.ttl = o.ttl;
  556.             this.cache = o.cache;
  557.             this.prepare = o.prepare;
  558.             this.transform = o.transform;
  559.             this.transport = o.transport;
  560.             this.thumbprint = o.thumbprint;
  561.             this.storage = new PersistentStorage(o.cacheKey);
  562.         }
  563.         _.mixin(Prefetch.prototype, {
  564.             _settings: function settings() {
  565.                 return {
  566.                     url: this.url,
  567.                     type: "GET",
  568.                     dataType: "json"
  569.                 };
  570.             },
  571.             store: function store(data) {
  572.                 if (!this.cache) {
  573.                     return;
  574.                 }
  575.                 this.storage.set(keys.data, data, this.ttl);
  576.                 this.storage.set(keys.protocol, location.protocol, this.ttl);
  577.                 this.storage.set(keys.thumbprint, this.thumbprint, this.ttl);
  578.             },
  579.             fromCache: function fromCache() {
  580.                 var stored = {}, isExpired;
  581.                 if (!this.cache) {
  582.                     return null;
  583.                 }
  584.                 stored.data = this.storage.get(keys.data);
  585.                 stored.protocol = this.storage.get(keys.protocol);
  586.                 stored.thumbprint = this.storage.get(keys.thumbprint);
  587.                 isExpired = stored.thumbprint !== this.thumbprint || stored.protocol !== location.protocol;
  588.                 return stored.data && !isExpired ? stored.data : null;
  589.             },
  590.             fromNetwork: function(cb) {
  591.                 var that = this, settings;
  592.                 if (!cb) {
  593.                     return;
  594.                 }
  595.                 settings = this.prepare(this._settings());
  596.                 this.transport(settings).fail(onError).done(onResponse);
  597.                 function onError() {
  598.                     cb(true);
  599.                 }
  600.                 function onResponse(resp) {
  601.                     cb(null, that.transform(resp));
  602.                 }
  603.             },
  604.             clear: function clear() {
  605.                 this.storage.clear();
  606.                 return this;
  607.             }
  608.         });
  609.         return Prefetch;
  610.     }();
  611.     var Remote = function() {
  612.         "use strict";
  613.         function Remote(o) {
  614.             this.url = o.url;
  615.             this.prepare = o.prepare;
  616.             this.transform = o.transform;
  617.             this.transport = new Transport({
  618.                 cache: o.cache,
  619.                 limiter: o.limiter,
  620.                 transport: o.transport
  621.             });
  622.         }
  623.         _.mixin(Remote.prototype, {
  624.             _settings: function settings() {
  625.                 return {
  626.                     url: this.url,
  627.                     type: "GET",
  628.                     dataType: "json"
  629.                 };
  630.             },
  631.             get: function get(query, cb) {
  632.                 var that = this, settings;
  633.                 if (!cb) {
  634.                     return;
  635.                 }
  636.                 query = query || "";
  637.                 settings = this.prepare(query, this._settings());
  638.                 return this.transport.get(settings, onResponse);
  639.                 function onResponse(err, resp) {
  640.                     err ? cb([]) : cb(that.transform(resp));
  641.                 }
  642.             },
  643.             cancelLastRequest: function cancelLastRequest() {
  644.                 this.transport.cancel();
  645.             }
  646.         });
  647.         return Remote;
  648.     }();
  649.     var oParser = function() {
  650.         "use strict";
  651.         return function parse(o) {
  652.             var defaults, sorter;
  653.             defaults = {
  654.                 initialize: true,
  655.                 identify: _.stringify,
  656.                 datumTokenizer: null,
  657.                 queryTokenizer: null,
  658.                 sufficient: 5,
  659.                 sorter: null,
  660.                 local: [],
  661.                 prefetch: null,
  662.                 remote: null
  663.             };
  664.             o = _.mixin(defaults, o || {});
  665.             !o.datumTokenizer && $.error("datumTokenizer is required");
  666.             !o.queryTokenizer && $.error("queryTokenizer is required");
  667.             sorter = o.sorter;
  668.             o.sorter = sorter ? function(x) {
  669.                 return x.sort(sorter);
  670.             } : _.identity;
  671.             o.local = _.isFunction(o.local) ? o.local() : o.local;
  672.             o.prefetch = parsePrefetch(o.prefetch);
  673.             o.remote = parseRemote(o.remote);
  674.             return o;
  675.         };
  676.         function parsePrefetch(o) {
  677.             var defaults;
  678.             if (!o) {
  679.                 return null;
  680.             }
  681.             defaults = {
  682.                 url: null,
  683.                 ttl: 24 * 60 * 60 * 1e3,
  684.                 cache: true,
  685.                 cacheKey: null,
  686.                 thumbprint: "",
  687.                 prepare: _.identity,
  688.                 transform: _.identity,
  689.                 transport: null
  690.             };
  691.             o = _.isString(o) ? {
  692.                 url: o
  693.             } : o;
  694.             o = _.mixin(defaults, o);
  695.             !o.url && $.error("prefetch requires url to be set");
  696.             o.transform = o.filter || o.transform;
  697.             o.cacheKey = o.cacheKey || o.url;
  698.             o.thumbprint = VERSION + o.thumbprint;
  699.             o.transport = o.transport ? callbackToDeferred(o.transport) : $.ajax;
  700.             return o;
  701.         }
  702.         function parseRemote(o) {
  703.             var defaults;
  704.             if (!o) {
  705.                 return;
  706.             }
  707.             defaults = {
  708.                 url: null,
  709.                 cache: true,
  710.                 prepare: null,
  711.                 replace: null,
  712.                 wildcard: null,
  713.                 limiter: null,
  714.                 rateLimitBy: "debounce",
  715.                 rateLimitWait: 300,
  716.                 transform: _.identity,
  717.                 transport: null
  718.             };
  719.             o = _.isString(o) ? {
  720.                 url: o
  721.             } : o;
  722.             o = _.mixin(defaults, o);
  723.             !o.url && $.error("remote requires url to be set");
  724.             o.transform = o.filter || o.transform;
  725.             o.prepare = toRemotePrepare(o);
  726.             o.limiter = toLimiter(o);
  727.             o.transport = o.transport ? callbackToDeferred(o.transport) : $.ajax;
  728.             delete o.replace;
  729.             delete o.wildcard;
  730.             delete o.rateLimitBy;
  731.             delete o.rateLimitWait;
  732.             return o;
  733.         }
  734.         function toRemotePrepare(o) {
  735.             var prepare, replace, wildcard;
  736.             prepare = o.prepare;
  737.             replace = o.replace;
  738.             wildcard = o.wildcard;
  739.             if (prepare) {
  740.                 return prepare;
  741.             }
  742.             if (replace) {
  743.                 prepare = prepareByReplace;
  744.             } else if (o.wildcard) {
  745.                 prepare = prepareByWildcard;
  746.             } else {
  747.                 prepare = idenityPrepare;
  748.             }
  749.             return prepare;
  750.             function prepareByReplace(query, settings) {
  751.                 settings.url = replace(settings.url, query);
  752.                 return settings;
  753.             }
  754.             function prepareByWildcard(query, settings) {
  755.                 settings.url = settings.url.replace(wildcard, encodeURIComponent(query));
  756.                 return settings;
  757.             }
  758.             function idenityPrepare(query, settings) {
  759.                 return settings;
  760.             }
  761.         }
  762.         function toLimiter(o) {
  763.             var limiter, method, wait;
  764.             limiter = o.limiter;
  765.             method = o.rateLimitBy;
  766.             wait = o.rateLimitWait;
  767.             if (!limiter) {
  768.                 limiter = /^throttle$/i.test(method) ? throttle(wait) : debounce(wait);
  769.             }
  770.             return limiter;
  771.             function debounce(wait) {
  772.                 return function debounce(fn) {
  773.                     return _.debounce(fn, wait);
  774.                 };
  775.             }
  776.             function throttle(wait) {
  777.                 return function throttle(fn) {
  778.                     return _.throttle(fn, wait);
  779.                 };
  780.             }
  781.         }
  782.         function callbackToDeferred(fn) {
  783.             return function wrapper(o) {
  784.                 var deferred = $.Deferred();
  785.                 fn(o, onSuccess, onError);
  786.                 return deferred;
  787.                 function onSuccess(resp) {
  788.                     _.defer(function() {
  789.                         deferred.resolve(resp);
  790.                     });
  791.                 }
  792.                 function onError(err) {
  793.                     _.defer(function() {
  794.                         deferred.reject(err);
  795.                     });
  796.                 }
  797.             };
  798.         }
  799.     }();
  800.     var Bloodhound = function() {
  801.         "use strict";
  802.         var old;
  803.         old = window && window.Bloodhound;
  804.         function Bloodhound(o) {
  805.             o = oParser(o);
  806.             this.sorter = o.sorter;
  807.             this.identify = o.identify;
  808.             this.sufficient = o.sufficient;
  809.             this.local = o.local;
  810.             this.remote = o.remote ? new Remote(o.remote) : null;
  811.             this.prefetch = o.prefetch ? new Prefetch(o.prefetch) : null;
  812.             this.index = new SearchIndex({
  813.                 identify: this.identify,
  814.                 datumTokenizer: o.datumTokenizer,
  815.                 queryTokenizer: o.queryTokenizer
  816.             });
  817.             o.initialize !== false && this.initialize();
  818.         }
  819.         Bloodhound.noConflict = function noConflict() {
  820.             window && (window.Bloodhound = old);
  821.             return Bloodhound;
  822.         };
  823.         Bloodhound.tokenizers = tokenizers;
  824.         _.mixin(Bloodhound.prototype, {
  825.             __ttAdapter: function ttAdapter() {
  826.                 var that = this;
  827.                 return this.remote ? withAsync : withoutAsync;
  828.                 function withAsync(query, sync, async) {
  829.                     return that.search(query, sync, async);
  830.                 }
  831.                 function withoutAsync(query, sync) {
  832.                     return that.search(query, sync);
  833.                 }
  834.             },
  835.             _loadPrefetch: function loadPrefetch() {
  836.                 var that = this, deferred, serialized;
  837.                 deferred = $.Deferred();
  838.                 if (!this.prefetch) {
  839.                     deferred.resolve();
  840.                 } else if (serialized = this.prefetch.fromCache()) {
  841.                     this.index.bootstrap(serialized);
  842.                     deferred.resolve();
  843.                 } else {
  844.                     this.prefetch.fromNetwork(done);
  845.                 }
  846.                 return deferred.promise();
  847.                 function done(err, data) {
  848.                     if (err) {
  849.                         return deferred.reject();
  850.                     }
  851.                     that.add(data);
  852.                     that.prefetch.store(that.index.serialize());
  853.                     deferred.resolve();
  854.                 }
  855.             },
  856.             _initialize: function initialize() {
  857.                 var that = this, deferred;
  858.                 this.clear();
  859.                 (this.initPromise = this._loadPrefetch()).done(addLocalToIndex);
  860.                 return this.initPromise;
  861.                 function addLocalToIndex() {
  862.                     that.add(that.local);
  863.                 }
  864.             },
  865.             initialize: function initialize(force) {
  866.                 return !this.initPromise || force ? this._initialize() : this.initPromise;
  867.             },
  868.             add: function add(data) {
  869.                 this.index.add(data);
  870.                 return this;
  871.             },
  872.             get: function get(ids) {
  873.                 ids = _.isArray(ids) ? ids : [].slice.call(arguments);
  874.                 return this.index.get(ids);
  875.             },
  876.             search: function search(query, sync, async) {
  877.                 var that = this, local;
  878.                 local = this.sorter(this.index.search(query));
  879.                 sync(this.remote ? local.slice() : local);
  880.                 if (this.remote && local.length < this.sufficient) {
  881.                     this.remote.get(query, processRemote);
  882.                 } else if (this.remote) {
  883.                     this.remote.cancelLastRequest();
  884.                 }
  885.                 return this;
  886.                 function processRemote(remote) {
  887.                     var nonDuplicates = [];
  888.                     _.each(remote, function(r) {
  889.                         !_.some(local, function(l) {
  890.                             return that.identify(r) === that.identify(l);
  891.                         }) && nonDuplicates.push(r);
  892.                     });
  893.                     async && async(nonDuplicates);
  894.                 }
  895.             },
  896.             all: function all() {
  897.                 return this.index.all();
  898.             },
  899.             clear: function clear() {
  900.                 this.index.reset();
  901.                 return this;
  902.             },
  903.             clearPrefetchCache: function clearPrefetchCache() {
  904.                 this.prefetch && this.prefetch.clear();
  905.                 return this;
  906.             },
  907.             clearRemoteCache: function clearRemoteCache() {
  908.                 Transport.resetCache();
  909.                 return this;
  910.             },
  911.             ttAdapter: function ttAdapter() {
  912.                 return this.__ttAdapter();
  913.             }
  914.         });
  915.         return Bloodhound;
  916.     }();
  917.     return Bloodhound;
  918. });
  919.  
  920. (function(root, factory) {
  921.     if (typeof define === "function" && define.amd) {
  922.         define("typeahead.js", [ "jquery" ], function(a0) {
  923.             return factory(a0);
  924.         });
  925.     } else if (typeof exports === "object") {
  926.         module.exports = factory(require("jquery"));
  927.     } else {
  928.         factory(jQuery);
  929.     }
  930. })(this, function($) {
  931.     var _ = function() {
  932.         "use strict";
  933.         return {
  934.             isMsie: function() {
  935.                 return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false;
  936.             },
  937.             isBlankString: function(str) {
  938.                 return !str || /^\s*$/.test(str);
  939.             },
  940.             escapeRegExChars: function(str) {
  941.                 return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
  942.             },
  943.             isString: function(obj) {
  944.                 return typeof obj === "string";
  945.             },
  946.             isNumber: function(obj) {
  947.                 return typeof obj === "number";
  948.             },
  949.             isArray: $.isArray,
  950.             isFunction: $.isFunction,
  951.             isObject: $.isPlainObject,
  952.             isUndefined: function(obj) {
  953.                 return typeof obj === "undefined";
  954.             },
  955.             isElement: function(obj) {
  956.                 return !!(obj && obj.nodeType === 1);
  957.             },
  958.             isJQuery: function(obj) {
  959.                 return obj instanceof $;
  960.             },
  961.             toStr: function toStr(s) {
  962.                 return _.isUndefined(s) || s === null ? "" : s + "";
  963.             },
  964.             bind: $.proxy,
  965.             each: function(collection, cb) {
  966.                 $.each(collection, reverseArgs);
  967.                 function reverseArgs(index, value) {
  968.                     return cb(value, index);
  969.                 }
  970.             },
  971.             map: $.map,
  972.             filter: $.grep,
  973.             every: function(obj, test) {
  974.                 var result = true;
  975.                 if (!obj) {
  976.                     return result;
  977.                 }
  978.                 $.each(obj, function(key, val) {
  979.                     if (!(result = test.call(null, val, key, obj))) {
  980.                         return false;
  981.                     }
  982.                 });
  983.                 return !!result;
  984.             },
  985.             some: function(obj, test) {
  986.                 var result = false;
  987.                 if (!obj) {
  988.                     return result;
  989.                 }
  990.                 $.each(obj, function(key, val) {
  991.                     if (result = test.call(null, val, key, obj)) {
  992.                         return false;
  993.                     }
  994.                 });
  995.                 return !!result;
  996.             },
  997.             mixin: $.extend,
  998.             identity: function(x) {
  999.                 return x;
  1000.             },
  1001.             clone: function(obj) {
  1002.                 return $.extend(true, {}, obj);
  1003.             },
  1004.             getIdGenerator: function() {
  1005.                 var counter = 0;
  1006.                 return function() {
  1007.                     return counter++;
  1008.                 };
  1009.             },
  1010.             templatify: function templatify(obj) {
  1011.                 return $.isFunction(obj) ? obj : template;
  1012.                 function template() {
  1013.                     return String(obj);
  1014.                 }
  1015.             },
  1016.             defer: function(fn) {
  1017.                 setTimeout(fn, 0);
  1018.             },
  1019.             debounce: function(func, wait, immediate) {
  1020.                 var timeout, result;
  1021.                 return function() {
  1022.                     var context = this, args = arguments, later, callNow;
  1023.                     later = function() {
  1024.                         timeout = null;
  1025.                         if (!immediate) {
  1026.                             result = func.apply(context, args);
  1027.                         }
  1028.                     };
  1029.                     callNow = immediate && !timeout;
  1030.                     clearTimeout(timeout);
  1031.                     timeout = setTimeout(later, wait);
  1032.                     if (callNow) {
  1033.                         result = func.apply(context, args);
  1034.                     }
  1035.                     return result;
  1036.                 };
  1037.             },
  1038.             throttle: function(func, wait) {
  1039.                 var context, args, timeout, result, previous, later;
  1040.                 previous = 0;
  1041.                 later = function() {
  1042.                     previous = new Date();
  1043.                     timeout = null;
  1044.                     result = func.apply(context, args);
  1045.                 };
  1046.                 return function() {
  1047.                     var now = new Date(), remaining = wait - (now - previous);
  1048.                     context = this;
  1049.                     args = arguments;
  1050.                     if (remaining <= 0) {
  1051.                         clearTimeout(timeout);
  1052.                         timeout = null;
  1053.                         previous = now;
  1054.                         result = func.apply(context, args);
  1055.                     } else if (!timeout) {
  1056.                         timeout = setTimeout(later, remaining);
  1057.                     }
  1058.                     return result;
  1059.                 };
  1060.             },
  1061.             stringify: function(val) {
  1062.                 return _.isString(val) ? val : JSON.stringify(val);
  1063.             },
  1064.             noop: function() {}
  1065.         };
  1066.     }();
  1067.     var WWW = function() {
  1068.         "use strict";
  1069.         var defaultClassNames = {
  1070.             wrapper: "twitter-typeahead",
  1071.             input: "tt-input",
  1072.             hint: "tt-hint",
  1073.             menu: "tt-menu",
  1074.             dataset: "tt-dataset",
  1075.             suggestion: "tt-suggestion",
  1076.             selectable: "tt-selectable",
  1077.             empty: "tt-empty",
  1078.             open: "tt-open",
  1079.             cursor: "tt-cursor",
  1080.             highlight: "tt-highlight"
  1081.         };
  1082.         return build;
  1083.         function build(o) {
  1084.             var www, classes;
  1085.             classes = _.mixin({}, defaultClassNames, o);
  1086.             www = {
  1087.                 css: buildCss(),
  1088.                 classes: classes,
  1089.                 html: buildHtml(classes),
  1090.                 selectors: buildSelectors(classes)
  1091.             };
  1092.             return {
  1093.                 css: www.css,
  1094.                 html: www.html,
  1095.                 classes: www.classes,
  1096.                 selectors: www.selectors,
  1097.                 mixin: function(o) {
  1098.                     _.mixin(o, www);
  1099.                 }
  1100.             };
  1101.         }
  1102.         function buildHtml(c) {
  1103.             return {
  1104.                 wrapper: '<span class="' + c.wrapper + '"></span>',
  1105.                 menu: '<div class="' + c.menu + '"></div>'
  1106.             };
  1107.         }
  1108.         function buildSelectors(classes) {
  1109.             var selectors = {};
  1110.             _.each(classes, function(v, k) {
  1111.                 selectors[k] = "." + v;
  1112.             });
  1113.             return selectors;
  1114.         }
  1115.         function buildCss() {
  1116.             var css = {
  1117.                 wrapper: {
  1118.                     position: "relative",
  1119.                     display: "inline-block"
  1120.                 },
  1121.                 hint: {
  1122.                     position: "absolute",
  1123.                     top: "0",
  1124.                     left: "0",
  1125.                     borderColor: "transparent",
  1126.                     boxShadow: "none",
  1127.                     opacity: "1"
  1128.                 },
  1129.                 input: {
  1130.                     position: "relative",
  1131.                     verticalAlign: "top",
  1132.                     backgroundColor: "transparent"
  1133.                 },
  1134.                 inputWithNoHint: {
  1135.                     position: "relative",
  1136.                     verticalAlign: "top"
  1137.                 },
  1138.                 menu: {
  1139.                     position: "absolute",
  1140.                     top: "100%",
  1141.                     left: "0",
  1142.                     zIndex: "100",
  1143.                     display: "none"
  1144.                 },
  1145.                 ltr: {
  1146.                     left: "0",
  1147.                     right: "auto"
  1148.                 },
  1149.                 rtl: {
  1150.                     left: "auto",
  1151.                     right: " 0"
  1152.                 }
  1153.             };
  1154.             if (_.isMsie()) {
  1155.                 _.mixin(css.input, {
  1156.                     backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)"
  1157.                 });
  1158.             }
  1159.             return css;
  1160.         }
  1161.     }();
  1162.     var EventBus = function() {
  1163.         "use strict";
  1164.         var namespace, deprecationMap;
  1165.         namespace = "typeahead:";
  1166.         deprecationMap = {
  1167.             render: "rendered",
  1168.             cursorchange: "cursorchanged",
  1169.             select: "selected",
  1170.             autocomplete: "autocompleted"
  1171.         };
  1172.         function EventBus(o) {
  1173.             if (!o || !o.el) {
  1174.                 $.error("EventBus initialized without el");
  1175.             }
  1176.             this.$el = $(o.el);
  1177.         }
  1178.         _.mixin(EventBus.prototype, {
  1179.             _trigger: function(type, args) {
  1180.                 var $e;
  1181.                 $e = $.Event(namespace + type);
  1182.                 (args = args || []).unshift($e);
  1183.                 this.$el.trigger.apply(this.$el, args);
  1184.                 return $e;
  1185.             },
  1186.             before: function(type) {
  1187.                 var args, $e;
  1188.                 args = [].slice.call(arguments, 1);
  1189.                 $e = this._trigger("before" + type, args);
  1190.                 return $e.isDefaultPrevented();
  1191.             },
  1192.             trigger: function(type) {
  1193.                 var deprecatedType;
  1194.                 this._trigger(type, [].slice.call(arguments, 1));
  1195.                 if (deprecatedType = deprecationMap[type]) {
  1196.                     this._trigger(deprecatedType, [].slice.call(arguments, 1));
  1197.                 }
  1198.             }
  1199.         });
  1200.         return EventBus;
  1201.     }();
  1202.     var EventEmitter = function() {
  1203.         "use strict";
  1204.         var splitter = /\s+/, nextTick = getNextTick();
  1205.         return {
  1206.             onSync: onSync,
  1207.             onAsync: onAsync,
  1208.             off: off,
  1209.             trigger: trigger
  1210.         };
  1211.         function on(method, types, cb, context) {
  1212.             var type;
  1213.             if (!cb) {
  1214.                 return this;
  1215.             }
  1216.             types = types.split(splitter);
  1217.             cb = context ? bindContext(cb, context) : cb;
  1218.             this._callbacks = this._callbacks || {};
  1219.             while (type = types.shift()) {
  1220.                 this._callbacks[type] = this._callbacks[type] || {
  1221.                     sync: [],
  1222.                     async: []
  1223.                 };
  1224.                 this._callbacks[type][method].push(cb);
  1225.             }
  1226.             return this;
  1227.         }
  1228.         function onAsync(types, cb, context) {
  1229.             return on.call(this, "async", types, cb, context);
  1230.         }
  1231.         function onSync(types, cb, context) {
  1232.             return on.call(this, "sync", types, cb, context);
  1233.         }
  1234.         function off(types) {
  1235.             var type;
  1236.             if (!this._callbacks) {
  1237.                 return this;
  1238.             }
  1239.             types = types.split(splitter);
  1240.             while (type = types.shift()) {
  1241.                 delete this._callbacks[type];
  1242.             }
  1243.             return this;
  1244.         }
  1245.         function trigger(types) {
  1246.             var type, callbacks, args, syncFlush, asyncFlush;
  1247.             if (!this._callbacks) {
  1248.                 return this;
  1249.             }
  1250.             types = types.split(splitter);
  1251.             args = [].slice.call(arguments, 1);
  1252.             while ((type = types.shift()) && (callbacks = this._callbacks[type])) {
  1253.                 syncFlush = getFlush(callbacks.sync, this, [ type ].concat(args));
  1254.                 asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args));
  1255.                 syncFlush() && nextTick(asyncFlush);
  1256.             }
  1257.             return this;
  1258.         }
  1259.         function getFlush(callbacks, context, args) {
  1260.             return flush;
  1261.             function flush() {
  1262.                 var cancelled;
  1263.                 for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) {
  1264.                     cancelled = callbacks[i].apply(context, args) === false;
  1265.                 }
  1266.                 return !cancelled;
  1267.             }
  1268.         }
  1269.         function getNextTick() {
  1270.             var nextTickFn;
  1271.             if (window.setImmediate) {
  1272.                 nextTickFn = function nextTickSetImmediate(fn) {
  1273.                     setImmediate(function() {
  1274.                         fn();
  1275.                     });
  1276.                 };
  1277.             } else {
  1278.                 nextTickFn = function nextTickSetTimeout(fn) {
  1279.                     setTimeout(function() {
  1280.                         fn();
  1281.                     }, 0);
  1282.                 };
  1283.             }
  1284.             return nextTickFn;
  1285.         }
  1286.         function bindContext(fn, context) {
  1287.             return fn.bind ? fn.bind(context) : function() {
  1288.                 fn.apply(context, [].slice.call(arguments, 0));
  1289.             };
  1290.         }
  1291.     }();
  1292.     var highlight = function(doc) {
  1293.         "use strict";
  1294.         var defaults = {
  1295.             node: null,
  1296.             pattern: null,
  1297.             tagName: "strong",
  1298.             className: null,
  1299.             wordsOnly: false,
  1300.             caseSensitive: false
  1301.         };
  1302.         return function hightlight(o) {
  1303.             var regex;
  1304.             o = _.mixin({}, defaults, o);
  1305.             if (!o.node || !o.pattern) {
  1306.                 return;
  1307.             }
  1308.             o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ];
  1309.             regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly);
  1310.             traverse(o.node, hightlightTextNode);
  1311.             function hightlightTextNode(textNode) {
  1312.                 var match, patternNode, wrapperNode;
  1313.                 if (match = regex.exec(textNode.data)) {
  1314.                     wrapperNode = doc.createElement(o.tagName);
  1315.                     o.className && (wrapperNode.className = o.className);
  1316.                     patternNode = textNode.splitText(match.index);
  1317.                     patternNode.splitText(match[0].length);
  1318.                     wrapperNode.appendChild(patternNode.cloneNode(true));
  1319.                     textNode.parentNode.replaceChild(wrapperNode, patternNode);
  1320.                 }
  1321.                 return !!match;
  1322.             }
  1323.             function traverse(el, hightlightTextNode) {
  1324.                 var childNode, TEXT_NODE_TYPE = 3;
  1325.                 for (var i = 0; i < el.childNodes.length; i++) {
  1326.                     childNode = el.childNodes[i];
  1327.                     if (childNode.nodeType === TEXT_NODE_TYPE) {
  1328.                         i += hightlightTextNode(childNode) ? 1 : 0;
  1329.                     } else {
  1330.                         traverse(childNode, hightlightTextNode);
  1331.                     }
  1332.                 }
  1333.             }
  1334.         };
  1335.         function getRegex(patterns, caseSensitive, wordsOnly) {
  1336.             var escapedPatterns = [], regexStr;
  1337.             for (var i = 0, len = patterns.length; i < len; i++) {
  1338.                 escapedPatterns.push(_.escapeRegExChars(patterns[i]));
  1339.             }
  1340.             regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")";
  1341.             return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i");
  1342.         }
  1343.     }(window.document);
  1344.     var Input = function() {
  1345.         "use strict";
  1346.         var specialKeyCodeMap;
  1347.         specialKeyCodeMap = {
  1348.             9: "tab",
  1349.             27: "esc",
  1350.             37: "left",
  1351.             39: "right",
  1352.             13: "enter",
  1353.             38: "up",
  1354.             40: "down"
  1355.         };
  1356.         function Input(o, www) {
  1357.             o = o || {};
  1358.             if (!o.input) {
  1359.                 $.error("input is missing");
  1360.             }
  1361.             www.mixin(this);
  1362.             this.$hint = $(o.hint);
  1363.             this.$input = $(o.input);
  1364.             this.query = this.$input.val();
  1365.             this.queryWhenFocused = this.hasFocus() ? this.query : null;
  1366.             this.$overflowHelper = buildOverflowHelper(this.$input);
  1367.             this._checkLanguageDirection();
  1368.             if (this.$hint.length === 0) {
  1369.                 this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop;
  1370.             }
  1371.         }
  1372.         Input.normalizeQuery = function(str) {
  1373.             return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " ");
  1374.         };
  1375.         _.mixin(Input.prototype, EventEmitter, {
  1376.             _onBlur: function onBlur() {
  1377.                 this.resetInputValue();
  1378.                 this.trigger("blurred");
  1379.             },
  1380.             _onFocus: function onFocus() {
  1381.                 this.queryWhenFocused = this.query;
  1382.                 this.trigger("focused");
  1383.             },
  1384.             _onKeydown: function onKeydown($e) {
  1385.                 var keyName = specialKeyCodeMap[$e.which || $e.keyCode];
  1386.                 this._managePreventDefault(keyName, $e);
  1387.                 if (keyName && this._shouldTrigger(keyName, $e)) {
  1388.                     this.trigger(keyName + "Keyed", $e);
  1389.                 }
  1390.             },
  1391.             _onInput: function onInput() {
  1392.                 this._setQuery(this.getInputValue());
  1393.                 this.clearHintIfInvalid();
  1394.                 this._checkLanguageDirection();
  1395.             },
  1396.             _managePreventDefault: function managePreventDefault(keyName, $e) {
  1397.                 var preventDefault;
  1398.                 switch (keyName) {
  1399.                   case "up":
  1400.                   case "down":
  1401.                     preventDefault = !withModifier($e);
  1402.                     break;
  1403.  
  1404.                   default:
  1405.                     preventDefault = false;
  1406.                 }
  1407.                 preventDefault && $e.preventDefault();
  1408.             },
  1409.             _shouldTrigger: function shouldTrigger(keyName, $e) {
  1410.                 var trigger;
  1411.                 switch (keyName) {
  1412.                   case "tab":
  1413.                     trigger = !withModifier($e);
  1414.                     break;
  1415.  
  1416.                   default:
  1417.                     trigger = true;
  1418.                 }
  1419.                 return trigger;
  1420.             },
  1421.             _checkLanguageDirection: function checkLanguageDirection() {
  1422.                 var dir = (this.$input.css("direction") || "ltr").toLowerCase();
  1423.                 if (this.dir !== dir) {
  1424.                     this.dir = dir;
  1425.                     this.$hint.attr("dir", dir);
  1426.                     this.trigger("langDirChanged", dir);
  1427.                 }
  1428.             },
  1429.             _setQuery: function setQuery(val, silent) {
  1430.                 var areEquivalent, hasDifferentWhitespace;
  1431.                 areEquivalent = areQueriesEquivalent(val, this.query);
  1432.                 hasDifferentWhitespace = areEquivalent ? this.query.length !== val.length : false;
  1433.                 this.query = val;
  1434.                 if (!silent && !areEquivalent) {
  1435.                     this.trigger("queryChanged", this.query);
  1436.                 } else if (!silent && hasDifferentWhitespace) {
  1437.                     this.trigger("whitespaceChanged", this.query);
  1438.                 }
  1439.             },
  1440.             bind: function() {
  1441.                 var that = this, onBlur, onFocus, onKeydown, onInput;
  1442.                 onBlur = _.bind(this._onBlur, this);
  1443.                 onFocus = _.bind(this._onFocus, this);
  1444.                 onKeydown = _.bind(this._onKeydown, this);
  1445.                 onInput = _.bind(this._onInput, this);
  1446.                 this.$input.on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown);
  1447.                 if (!_.isMsie() || _.isMsie() > 9) {
  1448.                     this.$input.on("input.tt", onInput);
  1449.                 } else {
  1450.                     this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) {
  1451.                         if (specialKeyCodeMap[$e.which || $e.keyCode]) {
  1452.                             return;
  1453.                         }
  1454.                         _.defer(_.bind(that._onInput, that, $e));
  1455.                     });
  1456.                 }
  1457.                 return this;
  1458.             },
  1459.             focus: function focus() {
  1460.                 this.$input.focus();
  1461.             },
  1462.             blur: function blur() {
  1463.                 this.$input.blur();
  1464.             },
  1465.             getLangDir: function getLangDir() {
  1466.                 return this.dir;
  1467.             },
  1468.             getQuery: function getQuery() {
  1469.                 return this.query || "";
  1470.             },
  1471.             setQuery: function setQuery(val, silent) {
  1472.                 this.setInputValue(val);
  1473.                 this._setQuery(val, silent);
  1474.             },
  1475.             hasQueryChangedSinceLastFocus: function hasQueryChangedSinceLastFocus() {
  1476.                 return this.query !== this.queryWhenFocused;
  1477.             },
  1478.             getInputValue: function getInputValue() {
  1479.                 return this.$input.val();
  1480.             },
  1481.             setInputValue: function setInputValue(value) {
  1482.                 this.$input.val(value);
  1483.                 this.clearHintIfInvalid();
  1484.                 this._checkLanguageDirection();
  1485.             },
  1486.             resetInputValue: function resetInputValue() {
  1487.                 this.setInputValue(this.query);
  1488.             },
  1489.             getHint: function getHint() {
  1490.                 return this.$hint.val();
  1491.             },
  1492.             setHint: function setHint(value) {
  1493.                 this.$hint.val(value);
  1494.             },
  1495.             clearHint: function clearHint() {
  1496.                 this.setHint("");
  1497.             },
  1498.             clearHintIfInvalid: function clearHintIfInvalid() {
  1499.                 var val, hint, valIsPrefixOfHint, isValid;
  1500.                 val = this.getInputValue();
  1501.                 hint = this.getHint();
  1502.                 valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0;
  1503.                 isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow();
  1504.                 !isValid && this.clearHint();
  1505.             },
  1506.             hasFocus: function hasFocus() {
  1507.                 return this.$input.is(":focus");
  1508.             },
  1509.             hasOverflow: function hasOverflow() {
  1510.                 var constraint = this.$input.width() - 2;
  1511.                 this.$overflowHelper.text(this.getInputValue());
  1512.                 return this.$overflowHelper.width() >= constraint;
  1513.             },
  1514.             isCursorAtEnd: function() {
  1515.                 var valueLength, selectionStart, range;
  1516.                 valueLength = this.$input.val().length;
  1517.                 selectionStart = this.$input[0].selectionStart;
  1518.                 if (_.isNumber(selectionStart)) {
  1519.                     return selectionStart === valueLength;
  1520.                 } else if (document.selection) {
  1521.                     range = document.selection.createRange();
  1522.                     range.moveStart("character", -valueLength);
  1523.                     return valueLength === range.text.length;
  1524.                 }
  1525.                 return true;
  1526.             },
  1527.             destroy: function destroy() {
  1528.                 this.$hint.off(".tt");
  1529.                 this.$input.off(".tt");
  1530.                 this.$overflowHelper.remove();
  1531.                 this.$hint = this.$input = this.$overflowHelper = $("<div>");
  1532.             }
  1533.         });
  1534.         return Input;
  1535.         function buildOverflowHelper($input) {
  1536.             return $('<pre aria-hidden="true"></pre>').css({
  1537.                 position: "absolute",
  1538.                 visibility: "hidden",
  1539.                 whiteSpace: "pre",
  1540.                 fontFamily: $input.css("font-family"),
  1541.                 fontSize: $input.css("font-size"),
  1542.                 fontStyle: $input.css("font-style"),
  1543.                 fontVariant: $input.css("font-variant"),
  1544.                 fontWeight: $input.css("font-weight"),
  1545.                 wordSpacing: $input.css("word-spacing"),
  1546.                 letterSpacing: $input.css("letter-spacing"),
  1547.                 textIndent: $input.css("text-indent"),
  1548.                 textRendering: $input.css("text-rendering"),
  1549.                 textTransform: $input.css("text-transform")
  1550.             }).insertAfter($input);
  1551.         }
  1552.         function areQueriesEquivalent(a, b) {
  1553.             return Input.normalizeQuery(a) === Input.normalizeQuery(b);
  1554.         }
  1555.         function withModifier($e) {
  1556.             return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey;
  1557.         }
  1558.     }();
  1559.     var Dataset = function() {
  1560.         "use strict";
  1561.         var keys, nameGenerator;
  1562.         keys = {
  1563.             val: "tt-selectable-display",
  1564.             obj: "tt-selectable-object"
  1565.         };
  1566.         nameGenerator = _.getIdGenerator();
  1567.         function Dataset(o, www) {
  1568.             o = o || {};
  1569.             o.templates = o.templates || {};
  1570.             o.templates.notFound = o.templates.notFound || o.templates.empty;
  1571.             if (!o.source) {
  1572.                 $.error("missing source");
  1573.             }
  1574.             if (!o.node) {
  1575.                 $.error("missing node");
  1576.             }
  1577.             if (o.name && !isValidName(o.name)) {
  1578.                 $.error("invalid dataset name: " + o.name);
  1579.             }
  1580.             www.mixin(this);
  1581.             this.highlight = !!o.highlight;
  1582.             this.name = o.name || nameGenerator();
  1583.             this.limit = o.limit || 1000;
  1584.             this.displayFn = getDisplayFn(o.display || o.displayKey);
  1585.             this.templates = getTemplates(o.templates, this.displayFn);
  1586.             this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source;
  1587.             this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async;
  1588.             this._resetLastSuggestion();
  1589.             this.$el = $(o.node).addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name);
  1590.         }
  1591.         Dataset.extractData = function extractData(el) {
  1592.             var $el = $(el);
  1593.             if ($el.data(keys.obj)) {
  1594.                 return {
  1595.                     val: $el.data(keys.val) || "",
  1596.                     obj: $el.data(keys.obj) || null
  1597.                 };
  1598.             }
  1599.             return null;
  1600.         };
  1601.         _.mixin(Dataset.prototype, EventEmitter, {
  1602.             _overwrite: function overwrite(query, suggestions) {
  1603.                 suggestions = suggestions || [];
  1604.                 if (suggestions.length) {
  1605.                     this._renderSuggestions(query, suggestions);
  1606.                 } else if (this.async && this.templates.pending) {
  1607.                     this._renderPending(query);
  1608.                 } else if (!this.async && this.templates.notFound) {
  1609.                     this._renderNotFound(query);
  1610.                 } else {
  1611.                     this._empty();
  1612.                 }
  1613.                 this.trigger("rendered", this.name, suggestions, false);
  1614.             },
  1615.             _append: function append(query, suggestions) {
  1616.                 suggestions = suggestions || [];
  1617.                 if (suggestions.length && this.$lastSuggestion.length) {
  1618.                     this._appendSuggestions(query, suggestions);
  1619.                 } else if (suggestions.length) {
  1620.                     this._renderSuggestions(query, suggestions);
  1621.                 } else if (!this.$lastSuggestion.length && this.templates.notFound) {
  1622.                     this._renderNotFound(query);
  1623.                 }
  1624.                 this.trigger("rendered", this.name, suggestions, true);
  1625.             },
  1626.             _renderSuggestions: function renderSuggestions(query, suggestions) {
  1627.                 var $fragment;
  1628.                 $fragment = this._getSuggestionsFragment(query, suggestions);
  1629.                 this.$lastSuggestion = $fragment.children().last();
  1630.                 this.$el.html($fragment).prepend(this._getHeader(query, suggestions)).append(this._getFooter(query, suggestions));
  1631.             },
  1632.             _appendSuggestions: function appendSuggestions(query, suggestions) {
  1633.                 var $fragment, $lastSuggestion;
  1634.                 $fragment = this._getSuggestionsFragment(query, suggestions);
  1635.                 $lastSuggestion = $fragment.children().last();
  1636.                 this.$lastSuggestion.after($fragment);
  1637.                 this.$lastSuggestion = $lastSuggestion;
  1638.             },
  1639.             _renderPending: function renderPending(query) {
  1640.                 var template = this.templates.pending;
  1641.                 this._resetLastSuggestion();
  1642.                 template && this.$el.html(template({
  1643.                     query: query,
  1644.                     dataset: this.name
  1645.                 }));
  1646.             },
  1647.             _renderNotFound: function renderNotFound(query) {
  1648.                 var template = this.templates.notFound;
  1649.                 this._resetLastSuggestion();
  1650.                 template && this.$el.html(template({
  1651.                     query: query,
  1652.                     dataset: this.name
  1653.                 }));
  1654.             },
  1655.             _empty: function empty() {
  1656.                 this.$el.empty();
  1657.                 this._resetLastSuggestion();
  1658.             },
  1659.             _getSuggestionsFragment: function getSuggestionsFragment(query, suggestions) {
  1660.                 var that = this, fragment;
  1661.                 fragment = document.createDocumentFragment();
  1662.                 _.each(suggestions, function getSuggestionNode(suggestion) {
  1663.                     var $el, context;
  1664.                     context = that._injectQuery(query, suggestion);
  1665.                     $el = $(that.templates.suggestion(context)).data(keys.obj, suggestion).data(keys.val, that.displayFn(suggestion)).addClass(that.classes.suggestion + " " + that.classes.selectable);
  1666.                     fragment.appendChild($el[0]);
  1667.                 });
  1668.                 this.highlight && highlight({
  1669.                     className: this.classes.highlight,
  1670.                     node: fragment,
  1671.                     pattern: query
  1672.                 });
  1673.                 return $(fragment);
  1674.             },
  1675.             _getFooter: function getFooter(query, suggestions) {
  1676.                 return this.templates.footer ? this.templates.footer({
  1677.                     query: query,
  1678.                     suggestions: suggestions,
  1679.                     dataset: this.name
  1680.                 }) : null;
  1681.             },
  1682.             _getHeader: function getHeader(query, suggestions) {
  1683.                 return this.templates.header ? this.templates.header({
  1684.                     query: query,
  1685.                     suggestions: suggestions,
  1686.                     dataset: this.name
  1687.                 }) : null;
  1688.             },
  1689.             _resetLastSuggestion: function resetLastSuggestion() {
  1690.                 this.$lastSuggestion = $();
  1691.             },
  1692.             _injectQuery: function injectQuery(query, obj) {
  1693.                 return _.isObject(obj) ? _.mixin({
  1694.                     _query: query
  1695.                 }, obj) : obj;
  1696.             },
  1697.             update: function update(query) {
  1698.                 var that = this, canceled = false, syncCalled = false, rendered = 0;
  1699.                 this.cancel();
  1700.                 this.cancel = function cancel() {
  1701.                     canceled = true;
  1702.                     that.cancel = $.noop;
  1703.                     that.async && that.trigger("asyncCanceled", query);
  1704.                 };
  1705.                 this.source(query, sync, async);
  1706.                 !syncCalled && sync([]);
  1707.                 function sync(suggestions) {
  1708.                     if (syncCalled) {
  1709.                         return;
  1710.                     }
  1711.                     syncCalled = true;
  1712.                     suggestions = (suggestions || []).slice(0, that.limit);
  1713.                     rendered = suggestions.length;
  1714.                     that._overwrite(query, suggestions);
  1715.                     if (rendered < that.limit && that.async) {
  1716.                         that.trigger("asyncRequested", query);
  1717.                     }
  1718.                 }
  1719.                 function async(suggestions) {
  1720.                     suggestions = suggestions || [];
  1721.                     if (!canceled && rendered < that.limit) {
  1722.                         that.cancel = $.noop;
  1723.                         rendered += suggestions.length;
  1724.                         that._append(query, suggestions.slice(0, that.limit - rendered));
  1725.                         that.async && that.trigger("asyncReceived", query);
  1726.                     }
  1727.                 }
  1728.             },
  1729.             cancel: $.noop,
  1730.             clear: function clear() {
  1731.                 this._empty();
  1732.                 this.cancel();
  1733.                 this.trigger("cleared");
  1734.             },
  1735.             isEmpty: function isEmpty() {
  1736.                 return this.$el.is(":empty");
  1737.             },
  1738.             destroy: function destroy() {
  1739.                 this.$el = $("<div>");
  1740.             }
  1741.         });
  1742.         return Dataset;
  1743.         function getDisplayFn(display) {
  1744.             display = display || _.stringify;
  1745.             return _.isFunction(display) ? display : displayFn;
  1746.             function displayFn(obj) {
  1747.                 return obj[display];
  1748.             }
  1749.         }
  1750.         function getTemplates(templates, displayFn) {
  1751.             return {
  1752.                 notFound: templates.notFound && _.templatify(templates.notFound),
  1753.                 pending: templates.pending && _.templatify(templates.pending),
  1754.                 header: templates.header && _.templatify(templates.header),
  1755.                 footer: templates.footer && _.templatify(templates.footer),
  1756.                 suggestion: templates.suggestion || suggestionTemplate
  1757.             };
  1758.             function suggestionTemplate(context) {
  1759.                 return $("<div>").text(displayFn(context));
  1760.             }
  1761.         }
  1762.         function isValidName(str) {
  1763.             return /^[_a-zA-Z0-9-]+$/.test(str);
  1764.         }
  1765.     }();
  1766.     var Menu = function() {
  1767.         "use strict";
  1768.         function Menu(o, www) {
  1769.             var that = this;
  1770.             o = o || {};
  1771.             if (!o.node) {
  1772.                 $.error("node is required");
  1773.             }
  1774.             www.mixin(this);
  1775.             this.$node = $(o.node);
  1776.             this.query = null;
  1777.             this.datasets = _.map(o.datasets, initializeDataset);
  1778.             function initializeDataset(oDataset) {
  1779.                 var node = that.$node.find(oDataset.node).first();
  1780.                 oDataset.node = node.length ? node : $("<div>").appendTo(that.$node);
  1781.                 return new Dataset(oDataset, www);
  1782.             }
  1783.         }
  1784.         _.mixin(Menu.prototype, EventEmitter, {
  1785.             _onSelectableClick: function onSelectableClick($e) {
  1786.                 this.trigger("selectableClicked", $($e.currentTarget));
  1787.             },
  1788.             _onRendered: function onRendered(type, dataset, suggestions, async) {
  1789.                 this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty());
  1790.                 this.trigger("datasetRendered", dataset, suggestions, async);
  1791.             },
  1792.             _onCleared: function onCleared() {
  1793.                 this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty());
  1794.                 this.trigger("datasetCleared");
  1795.             },
  1796.             _propagate: function propagate() {
  1797.                 this.trigger.apply(this, arguments);
  1798.             },
  1799.             _allDatasetsEmpty: function allDatasetsEmpty() {
  1800.                 return _.every(this.datasets, isDatasetEmpty);
  1801.                 function isDatasetEmpty(dataset) {
  1802.                     return dataset.isEmpty();
  1803.                 }
  1804.             },
  1805.             _getSelectables: function getSelectables() {
  1806.                 return this.$node.find(this.selectors.selectable);
  1807.             },
  1808.             _removeCursor: function _removeCursor() {
  1809.                 var $selectable = this.getActiveSelectable();
  1810.                 $selectable && $selectable.removeClass(this.classes.cursor);
  1811.             },
  1812.             _ensureVisible: function ensureVisible($el) {
  1813.                 var elTop, elBottom, nodeScrollTop, nodeHeight;
  1814.                 elTop = $el.position().top;
  1815.                 elBottom = elTop + $el.outerHeight(true);
  1816.                 nodeScrollTop = this.$node.scrollTop();
  1817.                 nodeHeight = this.$node.height() + parseInt(this.$node.css("paddingTop"), 10) + parseInt(this.$node.css("paddingBottom"), 10);
  1818.                 if (elTop < 0) {
  1819.                     this.$node.scrollTop(nodeScrollTop + elTop);
  1820.                 } else if (nodeHeight < elBottom) {
  1821.                     this.$node.scrollTop(nodeScrollTop + (elBottom - nodeHeight));
  1822.                 }
  1823.             },
  1824.             bind: function() {
  1825.                 var that = this, onSelectableClick;
  1826.                 onSelectableClick = _.bind(this._onSelectableClick, this);
  1827.                 this.$node.on("click.tt", this.selectors.selectable, onSelectableClick);
  1828.                 _.each(this.datasets, function(dataset) {
  1829.                     dataset.onSync("asyncRequested", that._propagate, that).onSync("asyncCanceled", that._propagate, that).onSync("asyncReceived", that._propagate, that).onSync("rendered", that._onRendered, that).onSync("cleared", that._onCleared, that);
  1830.                 });
  1831.                 return this;
  1832.             },
  1833.             isOpen: function isOpen() {
  1834.                 return this.$node.hasClass(this.classes.open);
  1835.             },
  1836.             open: function open() {
  1837.                 this.$node.addClass(this.classes.open);
  1838.             },
  1839.             close: function close() {
  1840.                 this.$node.removeClass(this.classes.open);
  1841.                 this._removeCursor();
  1842.             },
  1843.             setLanguageDirection: function setLanguageDirection(dir) {
  1844.                 this.$node.attr("dir", dir);
  1845.             },
  1846.             selectableRelativeToCursor: function selectableRelativeToCursor(delta) {
  1847.                 var $selectables, $oldCursor, oldIndex, newIndex;
  1848.                 $oldCursor = this.getActiveSelectable();
  1849.                 $selectables = this._getSelectables();
  1850.                 oldIndex = $oldCursor ? $selectables.index($oldCursor) : -1;
  1851.                 newIndex = oldIndex + delta;
  1852.                 newIndex = (newIndex + 1) % ($selectables.length + 1) - 1;
  1853.                 newIndex = newIndex < -1 ? $selectables.length - 1 : newIndex;
  1854.                 return newIndex === -1 ? null : $selectables.eq(newIndex);
  1855.             },
  1856.             setCursor: function setCursor($selectable) {
  1857.                 this._removeCursor();
  1858.                 if ($selectable = $selectable && $selectable.first()) {
  1859.                     $selectable.addClass(this.classes.cursor);
  1860.                     this._ensureVisible($selectable);
  1861.                 }
  1862.             },
  1863.             getSelectableData: function getSelectableData($el) {
  1864.                 return $el && $el.length ? Dataset.extractData($el) : null;
  1865.             },
  1866.             getActiveSelectable: function getActiveSelectable() {
  1867.                 var $selectable = this._getSelectables().filter(this.selectors.cursor).first();
  1868.                 return $selectable.length ? $selectable : null;
  1869.             },
  1870.             getTopSelectable: function getTopSelectable() {
  1871.                 var $selectable = this._getSelectables().first();
  1872.                 return $selectable.length ? $selectable : null;
  1873.             },
  1874.             update: function update(query) {
  1875.                 var isValidUpdate = query !== this.query;
  1876.                 if (isValidUpdate) {
  1877.                     this.query = query;
  1878.                     _.each(this.datasets, updateDataset);
  1879.                 }
  1880.                 return isValidUpdate;
  1881.                 function updateDataset(dataset) {
  1882.                     dataset.update(query);
  1883.                 }
  1884.             },
  1885.             empty: function empty() {
  1886.                 _.each(this.datasets, clearDataset);
  1887.                 this.query = null;
  1888.                 this.$node.addClass(this.classes.empty);
  1889.                 function clearDataset(dataset) {
  1890.                     dataset.clear();
  1891.                 }
  1892.             },
  1893.             destroy: function destroy() {
  1894.                 this.$node.off(".tt");
  1895.                 this.$node = $("<div>");
  1896.                 _.each(this.datasets, destroyDataset);
  1897.                 function destroyDataset(dataset) {
  1898.                     dataset.destroy();
  1899.                 }
  1900.             }
  1901.         });
  1902.         return Menu;
  1903.     }();
  1904.     var DefaultMenu = function() {
  1905.         "use strict";
  1906.         var s = Menu.prototype;
  1907.         function DefaultMenu() {
  1908.             Menu.apply(this, [].slice.call(arguments, 0));
  1909.         }
  1910.         _.mixin(DefaultMenu.prototype, Menu.prototype, {
  1911.             open: function open() {
  1912.                 !this._allDatasetsEmpty() && this._show();
  1913.                 return s.open.apply(this, [].slice.call(arguments, 0));
  1914.             },
  1915.             close: function close() {
  1916.                 this._hide();
  1917.                 return s.close.apply(this, [].slice.call(arguments, 0));
  1918.             },
  1919.             _onRendered: function onRendered() {
  1920.                 if (this._allDatasetsEmpty()) {
  1921.                     this._hide();
  1922.                 } else {
  1923.                     this.isOpen() && this._show();
  1924.                 }
  1925.                 return s._onRendered.apply(this, [].slice.call(arguments, 0));
  1926.             },
  1927.             _onCleared: function onCleared() {
  1928.                 if (this._allDatasetsEmpty()) {
  1929.                     this._hide();
  1930.                 } else {
  1931.                     this.isOpen() && this._show();
  1932.                 }
  1933.                 return s._onCleared.apply(this, [].slice.call(arguments, 0));
  1934.             },
  1935.             setLanguageDirection: function setLanguageDirection(dir) {
  1936.                 this.$node.css(dir === "ltr" ? this.css.ltr : this.css.rtl);
  1937.                 return s.setLanguageDirection.apply(this, [].slice.call(arguments, 0));
  1938.             },
  1939.             _hide: function hide() {
  1940.                 this.$node.hide();
  1941.             },
  1942.             _show: function show() {
  1943.                 this.$node.css("display", "block");
  1944.             }
  1945.         });
  1946.         return DefaultMenu;
  1947.     }();
  1948.     var Typeahead = function() {
  1949.         "use strict";
  1950.         function Typeahead(o, www) {
  1951.             var onFocused, onBlurred, onEnterKeyed, onTabKeyed, onEscKeyed, onUpKeyed, onDownKeyed, onLeftKeyed, onRightKeyed, onQueryChanged, onWhitespaceChanged;
  1952.             o = o || {};
  1953.             if (!o.input) {
  1954.                 $.error("missing input");
  1955.             }
  1956.             if (!o.menu) {
  1957.                 $.error("missing menu");
  1958.             }
  1959.             if (!o.eventBus) {
  1960.                 $.error("missing event bus");
  1961.             }
  1962.             www.mixin(this);
  1963.             this.eventBus = o.eventBus;
  1964.             this.minLength = _.isNumber(o.minLength) ? o.minLength : 1;
  1965.             this.input = o.input;
  1966.             this.menu = o.menu;
  1967.             this.enabled = true;
  1968.             this.active = false;
  1969.             this.input.hasFocus() && this.activate();
  1970.             this.dir = this.input.getLangDir();
  1971.             this._hacks();
  1972.             this.menu.bind().onSync("selectableClicked", this._onSelectableClicked, this).onSync("asyncRequested", this._onAsyncRequested, this).onSync("asyncCanceled", this._onAsyncCanceled, this).onSync("asyncReceived", this._onAsyncReceived, this).onSync("datasetRendered", this._onDatasetRendered, this).onSync("datasetCleared", this._onDatasetCleared, this);
  1973.             onFocused = c(this, "activate", "open", "_onFocused");
  1974.             onBlurred = c(this, "deactivate", "_onBlurred");
  1975.             onEnterKeyed = c(this, "isActive", "isOpen", "_onEnterKeyed");
  1976.             onTabKeyed = c(this, "isActive", "isOpen", "_onTabKeyed");
  1977.             onEscKeyed = c(this, "isActive", "_onEscKeyed");
  1978.             onUpKeyed = c(this, "isActive", "open", "_onUpKeyed");
  1979.             onDownKeyed = c(this, "isActive", "open", "_onDownKeyed");
  1980.             onLeftKeyed = c(this, "isActive", "isOpen", "_onLeftKeyed");
  1981.             onRightKeyed = c(this, "isActive", "isOpen", "_onRightKeyed");
  1982.             onQueryChanged = c(this, "_openIfActive", "_onQueryChanged");
  1983.             onWhitespaceChanged = c(this, "_openIfActive", "_onWhitespaceChanged");
  1984.             this.input.bind().onSync("focused", onFocused, this).onSync("blurred", onBlurred, this).onSync("enterKeyed", onEnterKeyed, this).onSync("tabKeyed", onTabKeyed, this).onSync("escKeyed", onEscKeyed, this).onSync("upKeyed", onUpKeyed, this).onSync("downKeyed", onDownKeyed, this).onSync("leftKeyed", onLeftKeyed, this).onSync("rightKeyed", onRightKeyed, this).onSync("queryChanged", onQueryChanged, this).onSync("whitespaceChanged", onWhitespaceChanged, this).onSync("langDirChanged", this._onLangDirChanged, this);
  1985.         }
  1986.         _.mixin(Typeahead.prototype, {
  1987.             _hacks: function hacks() {
  1988.                 var $input, $menu;
  1989.                 $input = this.input.$input || $("<div>");
  1990.                 $menu = this.menu.$node || $("<div>");
  1991.                 $input.on("blur.tt", function($e) {
  1992.                     var active, isActive, hasActive;
  1993.                     active = document.activeElement;
  1994.                     isActive = $menu.is(active);
  1995.                     hasActive = $menu.has(active).length > 0;
  1996.                     if (_.isMsie() && (isActive || hasActive)) {
  1997.                         $e.preventDefault();
  1998.                         $e.stopImmediatePropagation();
  1999.                         _.defer(function() {
  2000.                             $input.focus();
  2001.                         });
  2002.                     }
  2003.                 });
  2004.                 $menu.on("mousedown.tt", function($e) {
  2005.                     $e.preventDefault();
  2006.                 });
  2007.             },
  2008.             _onSelectableClicked: function onSelectableClicked(type, $el) {
  2009.                 this.select($el);
  2010.             },
  2011.             _onDatasetCleared: function onDatasetCleared() {
  2012.                 this._updateHint();
  2013.             },
  2014.             _onDatasetRendered: function onDatasetRendered(type, dataset, suggestions, async) {
  2015.                 this._updateHint();
  2016.                 this.eventBus.trigger("render", suggestions, async, dataset);
  2017.             },
  2018.             _onAsyncRequested: function onAsyncRequested(type, dataset, query) {
  2019.                 this.eventBus.trigger("asyncrequest", query, dataset);
  2020.             },
  2021.             _onAsyncCanceled: function onAsyncCanceled(type, dataset, query) {
  2022.                 this.eventBus.trigger("asynccancel", query, dataset);
  2023.             },
  2024.             _onAsyncReceived: function onAsyncReceived(type, dataset, query) {
  2025.                 this.eventBus.trigger("asyncreceive", query, dataset);
  2026.             },
  2027.             _onFocused: function onFocused() {
  2028.                 this._minLengthMet() && this.menu.update(this.input.getQuery());
  2029.             },
  2030.             _onBlurred: function onBlurred() {
  2031.                 if (this.input.hasQueryChangedSinceLastFocus()) {
  2032.                     this.eventBus.trigger("change", this.input.getQuery());
  2033.                 }
  2034.             },
  2035.             _onEnterKeyed: function onEnterKeyed(type, $e) {
  2036.                 var $selectable;
  2037.                 if ($selectable = this.menu.getActiveSelectable()) {
  2038.                     this.select($selectable) && $e.preventDefault();
  2039.                 }
  2040.             },
  2041.             _onTabKeyed: function onTabKeyed(type, $e) {
  2042.                 var $selectable;
  2043.                 if ($selectable = this.menu.getActiveSelectable()) {
  2044.                     this.select($selectable) && $e.preventDefault();
  2045.                 } else if ($selectable = this.menu.getTopSelectable()) {
  2046.                     this.autocomplete($selectable) && $e.preventDefault();
  2047.                 }
  2048.             },
  2049.             _onEscKeyed: function onEscKeyed() {
  2050.                 this.close();
  2051.             },
  2052.             _onUpKeyed: function onUpKeyed() {
  2053.                 this.moveCursor(-1);
  2054.             },
  2055.             _onDownKeyed: function onDownKeyed() {
  2056.                 this.moveCursor(+1);
  2057.             },
  2058.             _onLeftKeyed: function onLeftKeyed() {
  2059.                 if (this.dir === "rtl" && this.input.isCursorAtEnd()) {
  2060.                     this.autocomplete(this.menu.getTopSelectable());
  2061.                 }
  2062.             },
  2063.             _onRightKeyed: function onRightKeyed() {
  2064.                 if (this.dir === "ltr" && this.input.isCursorAtEnd()) {
  2065.                     this.autocomplete(this.menu.getTopSelectable());
  2066.                 }
  2067.             },
  2068.             _onQueryChanged: function onQueryChanged(e, query) {
  2069.                 this._minLengthMet(query) ? this.menu.update(query) : this.menu.empty();
  2070.             },
  2071.             _onWhitespaceChanged: function onWhitespaceChanged() {
  2072.                 this._updateHint();
  2073.             },
  2074.             _onLangDirChanged: function onLangDirChanged(e, dir) {
  2075.                 if (this.dir !== dir) {
  2076.                     this.dir = dir;
  2077.                     this.menu.setLanguageDirection(dir);
  2078.                 }
  2079.             },
  2080.             _openIfActive: function openIfActive() {
  2081.                 this.isActive() && this.open();
  2082.             },
  2083.             _minLengthMet: function minLengthMet(query) {
  2084.                 query = _.isString(query) ? query : this.input.getQuery() || "";
  2085.                 return query.length >= this.minLength;
  2086.             },
  2087.             _updateHint: function updateHint() {
  2088.                 var $selectable, data, val, query, escapedQuery, frontMatchRegEx, match;
  2089.                 $selectable = this.menu.getTopSelectable();
  2090.                 data = this.menu.getSelectableData($selectable);
  2091.                 val = this.input.getInputValue();
  2092.                 if (data && !_.isBlankString(val) && !this.input.hasOverflow()) {
  2093.                     query = Input.normalizeQuery(val);
  2094.                     escapedQuery = _.escapeRegExChars(query);
  2095.                     frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i");
  2096.                     match = frontMatchRegEx.exec(data.val);
  2097.                     match && this.input.setHint(val + match[1]);
  2098.                 } else {
  2099.                     this.input.clearHint();
  2100.                 }
  2101.             },
  2102.             isEnabled: function isEnabled() {
  2103.                 return this.enabled;
  2104.             },
  2105.             enable: function enable() {
  2106.                 this.enabled = true;
  2107.             },
  2108.             disable: function disable() {
  2109.                 this.enabled = false;
  2110.             },
  2111.             isActive: function isActive() {
  2112.                 return this.active;
  2113.             },
  2114.             activate: function activate() {
  2115.                 if (this.isActive()) {
  2116.                     return true;
  2117.                 } else if (!this.isEnabled() || this.eventBus.before("active")) {
  2118.                     return false;
  2119.                 } else {
  2120.                     this.active = true;
  2121.                     this.eventBus.trigger("active");
  2122.                     return true;
  2123.                 }
  2124.             },
  2125.             deactivate: function deactivate() {
  2126.                 if (!this.isActive()) {
  2127.                     return true;
  2128.                 } else if (this.eventBus.before("idle")) {
  2129.                     return false;
  2130.                 } else {
  2131.                     this.active = false;
  2132.                     this.close();
  2133.                     this.eventBus.trigger("idle");
  2134.                     return true;
  2135.                 }
  2136.             },
  2137.             isOpen: function isOpen() {
  2138.                 return this.menu.isOpen();
  2139.             },
  2140.             open: function open() {
  2141.                 if (!this.isOpen() && !this.eventBus.before("open")) {
  2142.                     this.menu.open();
  2143.                     this._updateHint();
  2144.                     this.eventBus.trigger("open");
  2145.                 }
  2146.                 return this.isOpen();
  2147.             },
  2148.             close: function close() {
  2149.                 if (this.isOpen() && !this.eventBus.before("close")) {
  2150.                     this.menu.close();
  2151.                     this.input.clearHint();
  2152.                     this.input.resetInputValue();
  2153.                     this.eventBus.trigger("close");
  2154.                 }
  2155.                 return !this.isOpen();
  2156.             },
  2157.             setVal: function setVal(val) {
  2158.                 this.input.setQuery(_.toStr(val));
  2159.             },
  2160.             getVal: function getVal() {
  2161.                 return this.input.getQuery();
  2162.             },
  2163.             select: function select($selectable) {
  2164.                 var data = this.menu.getSelectableData($selectable);
  2165.                 if (data && !this.eventBus.before("select", data.obj)) {
  2166.                     this.input.setQuery(data.val, true);
  2167.                     this.eventBus.trigger("select", data.obj);
  2168.                     this.close();
  2169.                     return true;
  2170.                 }
  2171.                 return false;
  2172.             },
  2173.             autocomplete: function autocomplete($selectable) {
  2174.                 var query, data, isValid;
  2175.                 query = this.input.getQuery();
  2176.                 data = this.menu.getSelectableData($selectable);
  2177.                 isValid = data && query !== data.val;
  2178.                 if (isValid && !this.eventBus.before("autocomplete", data.obj)) {
  2179.                     this.input.setQuery(data.val);
  2180.                     this.eventBus.trigger("autocomplete", data.obj);
  2181.                     return true;
  2182.                 }
  2183.                 return false;
  2184.             },
  2185.             moveCursor: function moveCursor(delta) {
  2186.                 var query, $candidate, data, payload, cancelMove;
  2187.                 query = this.input.getQuery();
  2188.                 $candidate = this.menu.selectableRelativeToCursor(delta);
  2189.                 data = this.menu.getSelectableData($candidate);
  2190.                 payload = data ? data.obj : null;
  2191.                 cancelMove = this._minLengthMet() && this.menu.update(query);
  2192.                 if (!cancelMove && !this.eventBus.before("cursorchange", payload)) {
  2193.                     this.menu.setCursor($candidate);
  2194.                     if (data) {
  2195.                         this.input.setInputValue(data.val);
  2196.                     } else {
  2197.                         this.input.resetInputValue();
  2198.                         this._updateHint();
  2199.                     }
  2200.                     this.eventBus.trigger("cursorchange", payload);
  2201.                     return true;
  2202.                 }
  2203.                 return false;
  2204.             },
  2205.             destroy: function destroy() {
  2206.                 this.input.destroy();
  2207.                 this.menu.destroy();
  2208.             }
  2209.         });
  2210.         return Typeahead;
  2211.         function c(ctx) {
  2212.             var methods = [].slice.call(arguments, 1);
  2213.             return function() {
  2214.                 var args = [].slice.call(arguments);
  2215.                 _.each(methods, function(method) {
  2216.                     return ctx[method].apply(ctx, args);
  2217.                 });
  2218.             };
  2219.         }
  2220.     }();
  2221.     (function() {
  2222.         "use strict";
  2223.         var old, keys, methods;
  2224.         old = $.fn.typeahead;
  2225.         keys = {
  2226.             www: "tt-www",
  2227.             attrs: "tt-attrs",
  2228.             typeahead: "tt-typeahead"
  2229.         };
  2230.         methods = {
  2231.             initialize: function initialize(o, datasets) {
  2232.                 var www;
  2233.                 datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1);
  2234.                 o = o || {};
  2235.                 www = WWW(o.classNames);
  2236.                 return this.each(attach);
  2237.                 function attach() {
  2238.                     var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, typeahead, MenuConstructor;
  2239.                     _.each(datasets, function(d) {
  2240.                         d.highlight = !!o.highlight;
  2241.                     });
  2242.                     $input = $(this);
  2243.                     $wrapper = $(www.html.wrapper);
  2244.                     $hint = $elOrNull(o.hint);
  2245.                     $menu = $elOrNull(o.menu);
  2246.                     defaultHint = o.hint !== false && !$hint;
  2247.                     defaultMenu = o.menu !== false && !$menu;
  2248.                     defaultHint && ($hint = buildHintFromInput($input, www));
  2249.                     defaultMenu && ($menu = $(www.html.menu).css(www.css.menu));
  2250.                     $hint && $hint.val("");
  2251.                     $input = prepInput($input, www);
  2252.                     if (defaultHint || defaultMenu) {
  2253.                         $wrapper.css(www.css.wrapper);
  2254.                         $input.css(defaultHint ? www.css.input : www.css.inputWithNoHint);
  2255.                         $input.wrap($wrapper).parent().prepend(defaultHint ? $hint : null).append(defaultMenu ? $menu : null);
  2256.                     }
  2257.                     MenuConstructor = defaultMenu ? DefaultMenu : Menu;
  2258.                     eventBus = new EventBus({
  2259.                         el: $input
  2260.                     });
  2261.                     input = new Input({
  2262.                         hint: $hint,
  2263.                         input: $input
  2264.                     }, www);
  2265.                     menu = new MenuConstructor({
  2266.                         node: $menu,
  2267.                         datasets: datasets
  2268.                     }, www);
  2269.                     typeahead = new Typeahead({
  2270.                         input: input,
  2271.                         menu: menu,
  2272.                         eventBus: eventBus,
  2273.                         minLength: o.minLength
  2274.                     }, www);
  2275.                     $input.data(keys.www, www);
  2276.                     $input.data(keys.typeahead, typeahead);
  2277.                 }
  2278.             },
  2279.             isEnabled: function isEnabled() {
  2280.                 var enabled;
  2281.                 ttEach(this.first(), function(t) {
  2282.                     enabled = t.isEnabled();
  2283.                 });
  2284.                 return enabled;
  2285.             },
  2286.             enable: function enable() {
  2287.                 ttEach(this, function(t) {
  2288.                     t.enable();
  2289.                 });
  2290.                 return this;
  2291.             },
  2292.             disable: function disable() {
  2293.                 ttEach(this, function(t) {
  2294.                     t.disable();
  2295.                 });
  2296.                 return this;
  2297.             },
  2298.             isActive: function isActive() {
  2299.                 var active;
  2300.                 ttEach(this.first(), function(t) {
  2301.                     active = t.isActive();
  2302.                 });
  2303.                 return active;
  2304.             },
  2305.             activate: function activate() {
  2306.                 ttEach(this, function(t) {
  2307.                     t.activate();
  2308.                 });
  2309.                 return this;
  2310.             },
  2311.             deactivate: function deactivate() {
  2312.                 ttEach(this, function(t) {
  2313.                     t.deactivate();
  2314.                 });
  2315.                 return this;
  2316.             },
  2317.             isOpen: function isOpen() {
  2318.                 var open;
  2319.                 ttEach(this.first(), function(t) {
  2320.                     open = t.isOpen();
  2321.                 });
  2322.                 return open;
  2323.             },
  2324.             open: function open() {
  2325.                 ttEach(this, function(t) {
  2326.                     t.open();
  2327.                 });
  2328.                 return this;
  2329.             },
  2330.             close: function close() {
  2331.                 ttEach(this, function(t) {
  2332.                     t.close();
  2333.                 });
  2334.                 return this;
  2335.             },
  2336.             select: function select(el) {
  2337.                 var success = false, $el = $(el);
  2338.                 ttEach(this.first(), function(t) {
  2339.                     success = t.select($el);
  2340.                 });
  2341.                 return success;
  2342.             },
  2343.             autocomplete: function autocomplete(el) {
  2344.                 var success = false, $el = $(el);
  2345.                 ttEach(this.first(), function(t) {
  2346.                     success = t.autocomplete($el);
  2347.                 });
  2348.                 return success;
  2349.             },
  2350.             moveCursor: function moveCursoe(delta) {
  2351.                 var success = false;
  2352.                 ttEach(this.first(), function(t) {
  2353.                     success = t.moveCursor(delta);
  2354.                 });
  2355.                 return success;
  2356.             },
  2357.             val: function val(newVal) {
  2358.                 var query;
  2359.                 if (!arguments.length) {
  2360.                     ttEach(this.first(), function(t) {
  2361.                         query = t.getVal();
  2362.                     });
  2363.                     return query;
  2364.                 } else {
  2365.                     ttEach(this, function(t) {
  2366.                         t.setVal(newVal);
  2367.                     });
  2368.                     return this;
  2369.                 }
  2370.             },
  2371.             destroy: function destroy() {
  2372.                 ttEach(this, function(typeahead, $input) {
  2373.                     revert($input);
  2374.                     typeahead.destroy();
  2375.                 });
  2376.                 return this;
  2377.             }
  2378.         };
  2379.         $.fn.typeahead = function(method) {
  2380.             if (methods[method]) {
  2381.                 return methods[method].apply(this, [].slice.call(arguments, 1));
  2382.             } else {
  2383.                 return methods.initialize.apply(this, arguments);
  2384.             }
  2385.         };
  2386.         $.fn.typeahead.noConflict = function noConflict() {
  2387.             $.fn.typeahead = old;
  2388.             return this;
  2389.         };
  2390.         function ttEach($els, fn) {
  2391.             $els.each(function() {
  2392.                 var $input = $(this), typeahead;
  2393.                 (typeahead = $input.data(keys.typeahead)) && fn(typeahead, $input);
  2394.             });
  2395.         }
  2396.         function buildHintFromInput($input, www) {
  2397.             return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint).css(getBackgroundStyles($input)).prop("readonly", true).removeAttr("id name placeholder required").attr({
  2398.                 autocomplete: "off",
  2399.                 spellcheck: "false",
  2400.                 tabindex: -1
  2401.             });
  2402.         }
  2403.         function prepInput($input, www) {
  2404.             $input.data(keys.attrs, {
  2405.                 dir: $input.attr("dir"),
  2406.                 autocomplete: $input.attr("autocomplete"),
  2407.                 spellcheck: $input.attr("spellcheck"),
  2408.                 style: $input.attr("style")
  2409.             });
  2410.             $input.addClass(www.classes.input).attr({
  2411.                 autocomplete: "off",
  2412.                 spellcheck: false
  2413.             });
  2414.             try {
  2415.                 !$input.attr("dir") && $input.attr("dir", "auto");
  2416.             } catch (e) {}
  2417.             return $input;
  2418.         }
  2419.         function getBackgroundStyles($el) {
  2420.             return {
  2421.                 backgroundAttachment: $el.css("background-attachment"),
  2422.                 backgroundClip: $el.css("background-clip"),
  2423.                 backgroundColor: $el.css("background-color"),
  2424.                 backgroundImage: $el.css("background-image"),
  2425.                 backgroundOrigin: $el.css("background-origin"),
  2426.                 backgroundPosition: $el.css("background-position"),
  2427.                 backgroundRepeat: $el.css("background-repeat"),
  2428.                 backgroundSize: $el.css("background-size")
  2429.             };
  2430.         }
  2431.         function revert($input) {
  2432.             var www, $wrapper;
  2433.             www = $input.data(keys.www);
  2434.             $wrapper = $input.parent().filter(www.selectors.wrapper);
  2435.             _.each($input.data(keys.attrs), function(val, key) {
  2436.                 _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val);
  2437.             });
  2438.             $input.removeData(keys.typeahead).removeData(keys.www).removeData(keys.attr).removeClass(www.classes.input);
  2439.             if ($wrapper.length) {
  2440.                 $input.detach().insertAfter($wrapper);
  2441.                 $wrapper.remove();
  2442.             }
  2443.         }
  2444.         function $elOrNull(obj) {
  2445.             var isValid, $el;
  2446.             isValid = _.isJQuery(obj) || _.isElement(obj);
  2447.             $el = isValid ? $(obj).first() : [];
  2448.             return $el.length ? $el : null;
  2449.         }
  2450.     })();
  2451. });

Raw Paste


Login or Register to edit or fork this paste. It's free.