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