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