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