corejs-typeahead-rails 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,953 @@
1
+ /*!
2
+ * typeahead.js 1.3.1
3
+ * https://github.com/corejavascript/typeahead.js
4
+ * Copyright 2013-2020 Twitter, Inc. and other contributors; Licensed MIT
5
+ */
6
+
7
+
8
+ (function(root, factory) {
9
+ if (typeof define === "function" && define.amd) {
10
+ define([ "jquery" ], function(a0) {
11
+ return root["Bloodhound"] = factory(a0);
12
+ });
13
+ } else if (typeof module === "object" && module.exports) {
14
+ module.exports = factory(require("jquery"));
15
+ } else {
16
+ root["Bloodhound"] = factory(root["jQuery"]);
17
+ }
18
+ })(this, function($) {
19
+ var _ = function() {
20
+ "use strict";
21
+ return {
22
+ isMsie: function() {
23
+ return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false;
24
+ },
25
+ isBlankString: function(str) {
26
+ return !str || /^\s*$/.test(str);
27
+ },
28
+ escapeRegExChars: function(str) {
29
+ return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
30
+ },
31
+ isString: function(obj) {
32
+ return typeof obj === "string";
33
+ },
34
+ isNumber: function(obj) {
35
+ return typeof obj === "number";
36
+ },
37
+ isArray: $.isArray,
38
+ isFunction: $.isFunction,
39
+ isObject: $.isPlainObject,
40
+ isUndefined: function(obj) {
41
+ return typeof obj === "undefined";
42
+ },
43
+ isElement: function(obj) {
44
+ return !!(obj && obj.nodeType === 1);
45
+ },
46
+ isJQuery: function(obj) {
47
+ return obj instanceof $;
48
+ },
49
+ toStr: function toStr(s) {
50
+ return _.isUndefined(s) || s === null ? "" : s + "";
51
+ },
52
+ bind: $.proxy,
53
+ each: function(collection, cb) {
54
+ $.each(collection, reverseArgs);
55
+ function reverseArgs(index, value) {
56
+ return cb(value, index);
57
+ }
58
+ },
59
+ map: $.map,
60
+ filter: $.grep,
61
+ every: function(obj, test) {
62
+ var result = true;
63
+ if (!obj) {
64
+ return result;
65
+ }
66
+ $.each(obj, function(key, val) {
67
+ if (!(result = test.call(null, val, key, obj))) {
68
+ return false;
69
+ }
70
+ });
71
+ return !!result;
72
+ },
73
+ some: function(obj, test) {
74
+ var result = false;
75
+ if (!obj) {
76
+ return result;
77
+ }
78
+ $.each(obj, function(key, val) {
79
+ if (result = test.call(null, val, key, obj)) {
80
+ return false;
81
+ }
82
+ });
83
+ return !!result;
84
+ },
85
+ mixin: $.extend,
86
+ identity: function(x) {
87
+ return x;
88
+ },
89
+ clone: function(obj) {
90
+ return $.extend(true, {}, obj);
91
+ },
92
+ getIdGenerator: function() {
93
+ var counter = 0;
94
+ return function() {
95
+ return counter++;
96
+ };
97
+ },
98
+ templatify: function templatify(obj) {
99
+ return $.isFunction(obj) ? obj : template;
100
+ function template() {
101
+ return String(obj);
102
+ }
103
+ },
104
+ defer: function(fn) {
105
+ setTimeout(fn, 0);
106
+ },
107
+ debounce: function(func, wait, immediate) {
108
+ var timeout, result;
109
+ return function() {
110
+ var context = this, args = arguments, later, callNow;
111
+ later = function() {
112
+ timeout = null;
113
+ if (!immediate) {
114
+ result = func.apply(context, args);
115
+ }
116
+ };
117
+ callNow = immediate && !timeout;
118
+ clearTimeout(timeout);
119
+ timeout = setTimeout(later, wait);
120
+ if (callNow) {
121
+ result = func.apply(context, args);
122
+ }
123
+ return result;
124
+ };
125
+ },
126
+ throttle: function(func, wait) {
127
+ var context, args, timeout, result, previous, later;
128
+ previous = 0;
129
+ later = function() {
130
+ previous = new Date();
131
+ timeout = null;
132
+ result = func.apply(context, args);
133
+ };
134
+ return function() {
135
+ var now = new Date(), remaining = wait - (now - previous);
136
+ context = this;
137
+ args = arguments;
138
+ if (remaining <= 0) {
139
+ clearTimeout(timeout);
140
+ timeout = null;
141
+ previous = now;
142
+ result = func.apply(context, args);
143
+ } else if (!timeout) {
144
+ timeout = setTimeout(later, remaining);
145
+ }
146
+ return result;
147
+ };
148
+ },
149
+ stringify: function(val) {
150
+ return _.isString(val) ? val : JSON.stringify(val);
151
+ },
152
+ guid: function() {
153
+ function _p8(s) {
154
+ var p = (Math.random().toString(16) + "000000000").substr(2, 8);
155
+ return s ? "-" + p.substr(0, 4) + "-" + p.substr(4, 4) : p;
156
+ }
157
+ return "tt-" + _p8() + _p8(true) + _p8(true) + _p8();
158
+ },
159
+ noop: function() {}
160
+ };
161
+ }();
162
+ var VERSION = "1.3.1";
163
+ var tokenizers = function() {
164
+ "use strict";
165
+ return {
166
+ nonword: nonword,
167
+ whitespace: whitespace,
168
+ ngram: ngram,
169
+ obj: {
170
+ nonword: getObjTokenizer(nonword),
171
+ whitespace: getObjTokenizer(whitespace),
172
+ ngram: getObjTokenizer(ngram)
173
+ }
174
+ };
175
+ function whitespace(str) {
176
+ str = _.toStr(str);
177
+ return str ? str.split(/\s+/) : [];
178
+ }
179
+ function nonword(str) {
180
+ str = _.toStr(str);
181
+ return str ? str.split(/\W+/) : [];
182
+ }
183
+ function ngram(str) {
184
+ str = _.toStr(str);
185
+ var tokens = [], word = "";
186
+ _.each(str.split(""), function(char) {
187
+ if (char.match(/\s+/)) {
188
+ word = "";
189
+ } else {
190
+ tokens.push(word + char);
191
+ word += char;
192
+ }
193
+ });
194
+ return tokens;
195
+ }
196
+ function getObjTokenizer(tokenizer) {
197
+ return function setKey(keys) {
198
+ keys = _.isArray(keys) ? keys : [].slice.call(arguments, 0);
199
+ return function tokenize(o) {
200
+ var tokens = [];
201
+ _.each(keys, function(k) {
202
+ tokens = tokens.concat(tokenizer(_.toStr(o[k])));
203
+ });
204
+ return tokens;
205
+ };
206
+ };
207
+ }
208
+ }();
209
+ var LruCache = function() {
210
+ "use strict";
211
+ function LruCache(maxSize) {
212
+ this.maxSize = _.isNumber(maxSize) ? maxSize : 100;
213
+ this.reset();
214
+ if (this.maxSize <= 0) {
215
+ this.set = this.get = $.noop;
216
+ }
217
+ }
218
+ _.mixin(LruCache.prototype, {
219
+ set: function set(key, val) {
220
+ var tailItem = this.list.tail, node;
221
+ if (this.size >= this.maxSize) {
222
+ this.list.remove(tailItem);
223
+ delete this.hash[tailItem.key];
224
+ this.size--;
225
+ }
226
+ if (node = this.hash[key]) {
227
+ node.val = val;
228
+ this.list.moveToFront(node);
229
+ } else {
230
+ node = new Node(key, val);
231
+ this.list.add(node);
232
+ this.hash[key] = node;
233
+ this.size++;
234
+ }
235
+ },
236
+ get: function get(key) {
237
+ var node = this.hash[key];
238
+ if (node) {
239
+ this.list.moveToFront(node);
240
+ return node.val;
241
+ }
242
+ },
243
+ reset: function reset() {
244
+ this.size = 0;
245
+ this.hash = {};
246
+ this.list = new List();
247
+ }
248
+ });
249
+ function List() {
250
+ this.head = this.tail = null;
251
+ }
252
+ _.mixin(List.prototype, {
253
+ add: function add(node) {
254
+ if (this.head) {
255
+ node.next = this.head;
256
+ this.head.prev = node;
257
+ }
258
+ this.head = node;
259
+ this.tail = this.tail || node;
260
+ },
261
+ remove: function remove(node) {
262
+ node.prev ? node.prev.next = node.next : this.head = node.next;
263
+ node.next ? node.next.prev = node.prev : this.tail = node.prev;
264
+ },
265
+ moveToFront: function(node) {
266
+ this.remove(node);
267
+ this.add(node);
268
+ }
269
+ });
270
+ function Node(key, val) {
271
+ this.key = key;
272
+ this.val = val;
273
+ this.prev = this.next = null;
274
+ }
275
+ return LruCache;
276
+ }();
277
+ var PersistentStorage = function() {
278
+ "use strict";
279
+ var LOCAL_STORAGE;
280
+ try {
281
+ LOCAL_STORAGE = window.localStorage;
282
+ LOCAL_STORAGE.setItem("~~~", "!");
283
+ LOCAL_STORAGE.removeItem("~~~");
284
+ } catch (err) {
285
+ LOCAL_STORAGE = null;
286
+ }
287
+ function PersistentStorage(namespace, override) {
288
+ this.prefix = [ "__", namespace, "__" ].join("");
289
+ this.ttlKey = "__ttl__";
290
+ this.keyMatcher = new RegExp("^" + _.escapeRegExChars(this.prefix));
291
+ this.ls = override || LOCAL_STORAGE;
292
+ !this.ls && this._noop();
293
+ }
294
+ _.mixin(PersistentStorage.prototype, {
295
+ _prefix: function(key) {
296
+ return this.prefix + key;
297
+ },
298
+ _ttlKey: function(key) {
299
+ return this._prefix(key) + this.ttlKey;
300
+ },
301
+ _noop: function() {
302
+ this.get = this.set = this.remove = this.clear = this.isExpired = _.noop;
303
+ },
304
+ _safeSet: function(key, val) {
305
+ try {
306
+ this.ls.setItem(key, val);
307
+ } catch (err) {
308
+ if (err.name === "QuotaExceededError") {
309
+ this.clear();
310
+ this._noop();
311
+ }
312
+ }
313
+ },
314
+ get: function(key) {
315
+ if (this.isExpired(key)) {
316
+ this.remove(key);
317
+ }
318
+ return decode(this.ls.getItem(this._prefix(key)));
319
+ },
320
+ set: function(key, val, ttl) {
321
+ if (_.isNumber(ttl)) {
322
+ this._safeSet(this._ttlKey(key), encode(now() + ttl));
323
+ } else {
324
+ this.ls.removeItem(this._ttlKey(key));
325
+ }
326
+ return this._safeSet(this._prefix(key), encode(val));
327
+ },
328
+ remove: function(key) {
329
+ this.ls.removeItem(this._ttlKey(key));
330
+ this.ls.removeItem(this._prefix(key));
331
+ return this;
332
+ },
333
+ clear: function() {
334
+ var i, keys = gatherMatchingKeys(this.keyMatcher);
335
+ for (i = keys.length; i--; ) {
336
+ this.remove(keys[i]);
337
+ }
338
+ return this;
339
+ },
340
+ isExpired: function(key) {
341
+ var ttl = decode(this.ls.getItem(this._ttlKey(key)));
342
+ return _.isNumber(ttl) && now() > ttl ? true : false;
343
+ }
344
+ });
345
+ return PersistentStorage;
346
+ function now() {
347
+ return new Date().getTime();
348
+ }
349
+ function encode(val) {
350
+ return JSON.stringify(_.isUndefined(val) ? null : val);
351
+ }
352
+ function decode(val) {
353
+ return $.parseJSON(val);
354
+ }
355
+ function gatherMatchingKeys(keyMatcher) {
356
+ var i, key, keys = [], len = LOCAL_STORAGE.length;
357
+ for (i = 0; i < len; i++) {
358
+ if ((key = LOCAL_STORAGE.key(i)).match(keyMatcher)) {
359
+ keys.push(key.replace(keyMatcher, ""));
360
+ }
361
+ }
362
+ return keys;
363
+ }
364
+ }();
365
+ var Transport = function() {
366
+ "use strict";
367
+ var pendingRequestsCount = 0, pendingRequests = {}, sharedCache = new LruCache(10);
368
+ function Transport(o) {
369
+ o = o || {};
370
+ this.maxPendingRequests = o.maxPendingRequests || 6;
371
+ this.cancelled = false;
372
+ this.lastReq = null;
373
+ this._send = o.transport;
374
+ this._get = o.limiter ? o.limiter(this._get) : this._get;
375
+ this._cache = o.cache === false ? new LruCache(0) : sharedCache;
376
+ }
377
+ Transport.setMaxPendingRequests = function setMaxPendingRequests(num) {
378
+ this.maxPendingRequests = num;
379
+ };
380
+ Transport.resetCache = function resetCache() {
381
+ sharedCache.reset();
382
+ };
383
+ _.mixin(Transport.prototype, {
384
+ _fingerprint: function fingerprint(o) {
385
+ o = o || {};
386
+ return o.url + o.type + $.param(o.data || {});
387
+ },
388
+ _get: function(o, cb) {
389
+ var that = this, fingerprint, jqXhr;
390
+ fingerprint = this._fingerprint(o);
391
+ if (this.cancelled || fingerprint !== this.lastReq) {
392
+ return;
393
+ }
394
+ if (jqXhr = pendingRequests[fingerprint]) {
395
+ jqXhr.done(done).fail(fail);
396
+ } else if (pendingRequestsCount < this.maxPendingRequests) {
397
+ pendingRequestsCount++;
398
+ pendingRequests[fingerprint] = this._send(o).done(done).fail(fail).always(always);
399
+ } else {
400
+ this.onDeckRequestArgs = [].slice.call(arguments, 0);
401
+ }
402
+ function done(resp) {
403
+ cb(null, resp);
404
+ that._cache.set(fingerprint, resp);
405
+ }
406
+ function fail() {
407
+ cb(true);
408
+ }
409
+ function always() {
410
+ pendingRequestsCount--;
411
+ delete pendingRequests[fingerprint];
412
+ if (that.onDeckRequestArgs) {
413
+ that._get.apply(that, that.onDeckRequestArgs);
414
+ that.onDeckRequestArgs = null;
415
+ }
416
+ }
417
+ },
418
+ get: function(o, cb) {
419
+ var resp, fingerprint;
420
+ cb = cb || $.noop;
421
+ o = _.isString(o) ? {
422
+ url: o
423
+ } : o || {};
424
+ fingerprint = this._fingerprint(o);
425
+ this.cancelled = false;
426
+ this.lastReq = fingerprint;
427
+ if (resp = this._cache.get(fingerprint)) {
428
+ cb(null, resp);
429
+ } else {
430
+ this._get(o, cb);
431
+ }
432
+ },
433
+ cancel: function() {
434
+ this.cancelled = true;
435
+ }
436
+ });
437
+ return Transport;
438
+ }();
439
+ var SearchIndex = window.SearchIndex = function() {
440
+ "use strict";
441
+ var CHILDREN = "c", IDS = "i";
442
+ function SearchIndex(o) {
443
+ o = o || {};
444
+ if (!o.datumTokenizer || !o.queryTokenizer) {
445
+ $.error("datumTokenizer and queryTokenizer are both required");
446
+ }
447
+ this.identify = o.identify || _.stringify;
448
+ this.datumTokenizer = o.datumTokenizer;
449
+ this.queryTokenizer = o.queryTokenizer;
450
+ this.matchAnyQueryToken = o.matchAnyQueryToken;
451
+ this.reset();
452
+ }
453
+ _.mixin(SearchIndex.prototype, {
454
+ bootstrap: function bootstrap(o) {
455
+ this.datums = o.datums;
456
+ this.trie = o.trie;
457
+ },
458
+ add: function(data) {
459
+ var that = this;
460
+ data = _.isArray(data) ? data : [ data ];
461
+ _.each(data, function(datum) {
462
+ var id, tokens;
463
+ that.datums[id = that.identify(datum)] = datum;
464
+ tokens = normalizeTokens(that.datumTokenizer(datum));
465
+ _.each(tokens, function(token) {
466
+ var node, chars, ch;
467
+ node = that.trie;
468
+ chars = token.split("");
469
+ while (ch = chars.shift()) {
470
+ node = node[CHILDREN][ch] || (node[CHILDREN][ch] = newNode());
471
+ node[IDS].push(id);
472
+ }
473
+ });
474
+ });
475
+ },
476
+ get: function get(ids) {
477
+ var that = this;
478
+ return _.map(ids, function(id) {
479
+ return that.datums[id];
480
+ });
481
+ },
482
+ search: function search(query) {
483
+ var that = this, tokens, matches;
484
+ tokens = normalizeTokens(this.queryTokenizer(query));
485
+ _.each(tokens, function(token) {
486
+ var node, chars, ch, ids;
487
+ if (matches && matches.length === 0 && !that.matchAnyQueryToken) {
488
+ return false;
489
+ }
490
+ node = that.trie;
491
+ chars = token.split("");
492
+ while (node && (ch = chars.shift())) {
493
+ node = node[CHILDREN][ch];
494
+ }
495
+ if (node && chars.length === 0) {
496
+ ids = node[IDS].slice(0);
497
+ matches = matches ? getIntersection(matches, ids) : ids;
498
+ } else {
499
+ if (!that.matchAnyQueryToken) {
500
+ matches = [];
501
+ return false;
502
+ }
503
+ }
504
+ });
505
+ return matches ? _.map(unique(matches), function(id) {
506
+ return that.datums[id];
507
+ }) : [];
508
+ },
509
+ all: function all() {
510
+ var values = [];
511
+ for (var key in this.datums) {
512
+ values.push(this.datums[key]);
513
+ }
514
+ return values;
515
+ },
516
+ reset: function reset() {
517
+ this.datums = {};
518
+ this.trie = newNode();
519
+ },
520
+ serialize: function serialize() {
521
+ return {
522
+ datums: this.datums,
523
+ trie: this.trie
524
+ };
525
+ }
526
+ });
527
+ return SearchIndex;
528
+ function normalizeTokens(tokens) {
529
+ tokens = _.filter(tokens, function(token) {
530
+ return !!token;
531
+ });
532
+ tokens = _.map(tokens, function(token) {
533
+ return token.toLowerCase();
534
+ });
535
+ return tokens;
536
+ }
537
+ function newNode() {
538
+ var node = {};
539
+ node[IDS] = [];
540
+ node[CHILDREN] = {};
541
+ return node;
542
+ }
543
+ function unique(array) {
544
+ var seen = {}, uniques = [];
545
+ for (var i = 0, len = array.length; i < len; i++) {
546
+ if (!seen[array[i]]) {
547
+ seen[array[i]] = true;
548
+ uniques.push(array[i]);
549
+ }
550
+ }
551
+ return uniques;
552
+ }
553
+ function getIntersection(arrayA, arrayB) {
554
+ var ai = 0, bi = 0, intersection = [];
555
+ arrayA = arrayA.sort();
556
+ arrayB = arrayB.sort();
557
+ var lenArrayA = arrayA.length, lenArrayB = arrayB.length;
558
+ while (ai < lenArrayA && bi < lenArrayB) {
559
+ if (arrayA[ai] < arrayB[bi]) {
560
+ ai++;
561
+ } else if (arrayA[ai] > arrayB[bi]) {
562
+ bi++;
563
+ } else {
564
+ intersection.push(arrayA[ai]);
565
+ ai++;
566
+ bi++;
567
+ }
568
+ }
569
+ return intersection;
570
+ }
571
+ }();
572
+ var Prefetch = function() {
573
+ "use strict";
574
+ var keys;
575
+ keys = {
576
+ data: "data",
577
+ protocol: "protocol",
578
+ thumbprint: "thumbprint"
579
+ };
580
+ function Prefetch(o) {
581
+ this.url = o.url;
582
+ this.ttl = o.ttl;
583
+ this.cache = o.cache;
584
+ this.prepare = o.prepare;
585
+ this.transform = o.transform;
586
+ this.transport = o.transport;
587
+ this.thumbprint = o.thumbprint;
588
+ this.storage = new PersistentStorage(o.cacheKey);
589
+ }
590
+ _.mixin(Prefetch.prototype, {
591
+ _settings: function settings() {
592
+ return {
593
+ url: this.url,
594
+ type: "GET",
595
+ dataType: "json"
596
+ };
597
+ },
598
+ store: function store(data) {
599
+ if (!this.cache) {
600
+ return;
601
+ }
602
+ this.storage.set(keys.data, data, this.ttl);
603
+ this.storage.set(keys.protocol, location.protocol, this.ttl);
604
+ this.storage.set(keys.thumbprint, this.thumbprint, this.ttl);
605
+ },
606
+ fromCache: function fromCache() {
607
+ var stored = {}, isExpired;
608
+ if (!this.cache) {
609
+ return null;
610
+ }
611
+ stored.data = this.storage.get(keys.data);
612
+ stored.protocol = this.storage.get(keys.protocol);
613
+ stored.thumbprint = this.storage.get(keys.thumbprint);
614
+ isExpired = stored.thumbprint !== this.thumbprint || stored.protocol !== location.protocol;
615
+ return stored.data && !isExpired ? stored.data : null;
616
+ },
617
+ fromNetwork: function(cb) {
618
+ var that = this, settings;
619
+ if (!cb) {
620
+ return;
621
+ }
622
+ settings = this.prepare(this._settings());
623
+ this.transport(settings).fail(onError).done(onResponse);
624
+ function onError() {
625
+ cb(true);
626
+ }
627
+ function onResponse(resp) {
628
+ cb(null, that.transform(resp));
629
+ }
630
+ },
631
+ clear: function clear() {
632
+ this.storage.clear();
633
+ return this;
634
+ }
635
+ });
636
+ return Prefetch;
637
+ }();
638
+ var Remote = function() {
639
+ "use strict";
640
+ function Remote(o) {
641
+ this.url = o.url;
642
+ this.prepare = o.prepare;
643
+ this.transform = o.transform;
644
+ this.indexResponse = o.indexResponse;
645
+ this.transport = new Transport({
646
+ cache: o.cache,
647
+ limiter: o.limiter,
648
+ transport: o.transport,
649
+ maxPendingRequests: o.maxPendingRequests
650
+ });
651
+ }
652
+ _.mixin(Remote.prototype, {
653
+ _settings: function settings() {
654
+ return {
655
+ url: this.url,
656
+ type: "GET",
657
+ dataType: "json"
658
+ };
659
+ },
660
+ get: function get(query, cb) {
661
+ var that = this, settings;
662
+ if (!cb) {
663
+ return;
664
+ }
665
+ query = query || "";
666
+ settings = this.prepare(query, this._settings());
667
+ return this.transport.get(settings, onResponse);
668
+ function onResponse(err, resp) {
669
+ err ? cb([]) : cb(that.transform(resp));
670
+ }
671
+ },
672
+ cancelLastRequest: function cancelLastRequest() {
673
+ this.transport.cancel();
674
+ }
675
+ });
676
+ return Remote;
677
+ }();
678
+ var oParser = function() {
679
+ "use strict";
680
+ return function parse(o) {
681
+ var defaults, sorter;
682
+ defaults = {
683
+ initialize: true,
684
+ identify: _.stringify,
685
+ datumTokenizer: null,
686
+ queryTokenizer: null,
687
+ matchAnyQueryToken: false,
688
+ sufficient: 5,
689
+ indexRemote: false,
690
+ sorter: null,
691
+ local: [],
692
+ prefetch: null,
693
+ remote: null
694
+ };
695
+ o = _.mixin(defaults, o || {});
696
+ !o.datumTokenizer && $.error("datumTokenizer is required");
697
+ !o.queryTokenizer && $.error("queryTokenizer is required");
698
+ sorter = o.sorter;
699
+ o.sorter = sorter ? function(x) {
700
+ return x.sort(sorter);
701
+ } : _.identity;
702
+ o.local = _.isFunction(o.local) ? o.local() : o.local;
703
+ o.prefetch = parsePrefetch(o.prefetch);
704
+ o.remote = parseRemote(o.remote);
705
+ return o;
706
+ };
707
+ function parsePrefetch(o) {
708
+ var defaults;
709
+ if (!o) {
710
+ return null;
711
+ }
712
+ defaults = {
713
+ url: null,
714
+ ttl: 24 * 60 * 60 * 1e3,
715
+ cache: true,
716
+ cacheKey: null,
717
+ thumbprint: "",
718
+ prepare: _.identity,
719
+ transform: _.identity,
720
+ transport: null
721
+ };
722
+ o = _.isString(o) ? {
723
+ url: o
724
+ } : o;
725
+ o = _.mixin(defaults, o);
726
+ !o.url && $.error("prefetch requires url to be set");
727
+ o.transform = o.filter || o.transform;
728
+ o.cacheKey = o.cacheKey || o.url;
729
+ o.thumbprint = VERSION + o.thumbprint;
730
+ o.transport = o.transport ? callbackToDeferred(o.transport) : $.ajax;
731
+ return o;
732
+ }
733
+ function parseRemote(o) {
734
+ var defaults;
735
+ if (!o) {
736
+ return;
737
+ }
738
+ defaults = {
739
+ url: null,
740
+ cache: true,
741
+ prepare: null,
742
+ replace: null,
743
+ wildcard: null,
744
+ limiter: null,
745
+ rateLimitBy: "debounce",
746
+ rateLimitWait: 300,
747
+ transform: _.identity,
748
+ transport: null
749
+ };
750
+ o = _.isString(o) ? {
751
+ url: o
752
+ } : o;
753
+ o = _.mixin(defaults, o);
754
+ !o.url && $.error("remote requires url to be set");
755
+ o.transform = o.filter || o.transform;
756
+ o.prepare = toRemotePrepare(o);
757
+ o.limiter = toLimiter(o);
758
+ o.transport = o.transport ? callbackToDeferred(o.transport) : $.ajax;
759
+ delete o.replace;
760
+ delete o.wildcard;
761
+ delete o.rateLimitBy;
762
+ delete o.rateLimitWait;
763
+ return o;
764
+ }
765
+ function toRemotePrepare(o) {
766
+ var prepare, replace, wildcard;
767
+ prepare = o.prepare;
768
+ replace = o.replace;
769
+ wildcard = o.wildcard;
770
+ if (prepare) {
771
+ return prepare;
772
+ }
773
+ if (replace) {
774
+ prepare = prepareByReplace;
775
+ } else if (o.wildcard) {
776
+ prepare = prepareByWildcard;
777
+ } else {
778
+ prepare = identityPrepare;
779
+ }
780
+ return prepare;
781
+ function prepareByReplace(query, settings) {
782
+ settings.url = replace(settings.url, query);
783
+ return settings;
784
+ }
785
+ function prepareByWildcard(query, settings) {
786
+ settings.url = settings.url.replace(wildcard, encodeURIComponent(query));
787
+ return settings;
788
+ }
789
+ function identityPrepare(query, settings) {
790
+ return settings;
791
+ }
792
+ }
793
+ function toLimiter(o) {
794
+ var limiter, method, wait;
795
+ limiter = o.limiter;
796
+ method = o.rateLimitBy;
797
+ wait = o.rateLimitWait;
798
+ if (!limiter) {
799
+ limiter = /^throttle$/i.test(method) ? throttle(wait) : debounce(wait);
800
+ }
801
+ return limiter;
802
+ function debounce(wait) {
803
+ return function debounce(fn) {
804
+ return _.debounce(fn, wait);
805
+ };
806
+ }
807
+ function throttle(wait) {
808
+ return function throttle(fn) {
809
+ return _.throttle(fn, wait);
810
+ };
811
+ }
812
+ }
813
+ function callbackToDeferred(fn) {
814
+ return function wrapper(o) {
815
+ var deferred = $.Deferred();
816
+ fn(o, onSuccess, onError);
817
+ return deferred;
818
+ function onSuccess(resp) {
819
+ _.defer(function() {
820
+ deferred.resolve(resp);
821
+ });
822
+ }
823
+ function onError(err) {
824
+ _.defer(function() {
825
+ deferred.reject(err);
826
+ });
827
+ }
828
+ };
829
+ }
830
+ }();
831
+ var Bloodhound = function() {
832
+ "use strict";
833
+ var old;
834
+ old = window && window.Bloodhound;
835
+ function Bloodhound(o) {
836
+ o = oParser(o);
837
+ this.sorter = o.sorter;
838
+ this.identify = o.identify;
839
+ this.sufficient = o.sufficient;
840
+ this.indexRemote = o.indexRemote;
841
+ this.local = o.local;
842
+ this.remote = o.remote ? new Remote(o.remote) : null;
843
+ this.prefetch = o.prefetch ? new Prefetch(o.prefetch) : null;
844
+ this.index = new SearchIndex({
845
+ identify: this.identify,
846
+ datumTokenizer: o.datumTokenizer,
847
+ queryTokenizer: o.queryTokenizer
848
+ });
849
+ o.initialize !== false && this.initialize();
850
+ }
851
+ Bloodhound.noConflict = function noConflict() {
852
+ window && (window.Bloodhound = old);
853
+ return Bloodhound;
854
+ };
855
+ Bloodhound.tokenizers = tokenizers;
856
+ _.mixin(Bloodhound.prototype, {
857
+ __ttAdapter: function ttAdapter() {
858
+ var that = this;
859
+ return this.remote ? withAsync : withoutAsync;
860
+ function withAsync(query, sync, async) {
861
+ return that.search(query, sync, async);
862
+ }
863
+ function withoutAsync(query, sync) {
864
+ return that.search(query, sync);
865
+ }
866
+ },
867
+ _loadPrefetch: function loadPrefetch() {
868
+ var that = this, deferred, serialized;
869
+ deferred = $.Deferred();
870
+ if (!this.prefetch) {
871
+ deferred.resolve();
872
+ } else if (serialized = this.prefetch.fromCache()) {
873
+ this.index.bootstrap(serialized);
874
+ deferred.resolve();
875
+ } else {
876
+ this.prefetch.fromNetwork(done);
877
+ }
878
+ return deferred.promise();
879
+ function done(err, data) {
880
+ if (err) {
881
+ return deferred.reject();
882
+ }
883
+ that.add(data);
884
+ that.prefetch.store(that.index.serialize());
885
+ deferred.resolve();
886
+ }
887
+ },
888
+ _initialize: function initialize() {
889
+ var that = this, deferred;
890
+ this.clear();
891
+ (this.initPromise = this._loadPrefetch()).done(addLocalToIndex);
892
+ return this.initPromise;
893
+ function addLocalToIndex() {
894
+ that.add(that.local);
895
+ }
896
+ },
897
+ initialize: function initialize(force) {
898
+ return !this.initPromise || force ? this._initialize() : this.initPromise;
899
+ },
900
+ add: function add(data) {
901
+ this.index.add(data);
902
+ return this;
903
+ },
904
+ get: function get(ids) {
905
+ ids = _.isArray(ids) ? ids : [].slice.call(arguments);
906
+ return this.index.get(ids);
907
+ },
908
+ search: function search(query, sync, async) {
909
+ var that = this, local;
910
+ sync = sync || _.noop;
911
+ async = async || _.noop;
912
+ local = this.sorter(this.index.search(query));
913
+ sync(this.remote ? local.slice() : local);
914
+ if (this.remote && local.length < this.sufficient) {
915
+ this.remote.get(query, processRemote);
916
+ } else if (this.remote) {
917
+ this.remote.cancelLastRequest();
918
+ }
919
+ return this;
920
+ function processRemote(remote) {
921
+ var nonDuplicates = [];
922
+ _.each(remote, function(r) {
923
+ !_.some(local, function(l) {
924
+ return that.identify(r) === that.identify(l);
925
+ }) && nonDuplicates.push(r);
926
+ });
927
+ that.indexRemote && that.add(nonDuplicates);
928
+ async(nonDuplicates);
929
+ }
930
+ },
931
+ all: function all() {
932
+ return this.index.all();
933
+ },
934
+ clear: function clear() {
935
+ this.index.reset();
936
+ return this;
937
+ },
938
+ clearPrefetchCache: function clearPrefetchCache() {
939
+ this.prefetch && this.prefetch.clear();
940
+ return this;
941
+ },
942
+ clearRemoteCache: function clearRemoteCache() {
943
+ Transport.resetCache();
944
+ return this;
945
+ },
946
+ ttAdapter: function ttAdapter() {
947
+ return this.__ttAdapter();
948
+ }
949
+ });
950
+ return Bloodhound;
951
+ }();
952
+ return Bloodhound;
953
+ });