200 lines
4.6 KiB
JavaScript
200 lines
4.6 KiB
JavaScript
/*
|
|
* typeahead.js
|
|
* https://github.com/twitter/typeahead.js
|
|
* Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT
|
|
*/
|
|
|
|
var Bloodhound = (function() {
|
|
'use strict';
|
|
|
|
var old;
|
|
|
|
old = window && window.Bloodhound;
|
|
|
|
// constructor
|
|
// -----------
|
|
|
|
function Bloodhound(o) {
|
|
o = oParser(o);
|
|
|
|
this.sorter = o.sorter;
|
|
this.identify = o.identify;
|
|
this.sufficient = o.sufficient;
|
|
this.indexRemote = o.indexRemote;
|
|
|
|
this.local = o.local;
|
|
this.remote = o.remote ? new Remote(o.remote) : null;
|
|
this.prefetch = o.prefetch ? new Prefetch(o.prefetch) : null;
|
|
|
|
// the backing data structure used for fast pattern matching
|
|
this.index = new SearchIndex({
|
|
identify: this.identify,
|
|
datumTokenizer: o.datumTokenizer,
|
|
queryTokenizer: o.queryTokenizer
|
|
});
|
|
|
|
// hold off on intialization if the intialize option was explicitly false
|
|
o.initialize !== false && this.initialize();
|
|
}
|
|
|
|
// static methods
|
|
// --------------
|
|
|
|
Bloodhound.noConflict = function noConflict() {
|
|
window && (window.Bloodhound = old);
|
|
return Bloodhound;
|
|
};
|
|
|
|
Bloodhound.tokenizers = tokenizers;
|
|
|
|
// instance methods
|
|
// ----------------
|
|
|
|
_.mixin(Bloodhound.prototype, {
|
|
|
|
// ### super secret stuff used for integration with jquery plugin
|
|
|
|
__ttAdapter: function ttAdapter() {
|
|
var that = this;
|
|
|
|
return this.remote ? withAsync : withoutAsync;
|
|
|
|
function withAsync(query, sync, async) {
|
|
return that.search(query, sync, async);
|
|
}
|
|
|
|
function withoutAsync(query, sync) {
|
|
return that.search(query, sync);
|
|
}
|
|
},
|
|
|
|
// ### private
|
|
|
|
_loadPrefetch: function loadPrefetch() {
|
|
var that = this, deferred, serialized;
|
|
|
|
deferred = $.Deferred();
|
|
|
|
if (!this.prefetch) {
|
|
deferred.resolve();
|
|
}
|
|
|
|
else if (serialized = this.prefetch.fromCache()) {
|
|
this.index.bootstrap(serialized);
|
|
deferred.resolve();
|
|
}
|
|
|
|
else {
|
|
this.prefetch.fromNetwork(done);
|
|
}
|
|
|
|
return deferred.promise();
|
|
|
|
function done(err, data) {
|
|
if (err) { return deferred.reject(); }
|
|
|
|
that.add(data);
|
|
that.prefetch.store(that.index.serialize());
|
|
deferred.resolve();
|
|
}
|
|
},
|
|
|
|
_initialize: function initialize() {
|
|
var that = this, deferred;
|
|
|
|
// in case this is a reinitialization, clear previous data
|
|
this.clear();
|
|
|
|
(this.initPromise = this._loadPrefetch())
|
|
.done(addLocalToIndex); // local must be added to index after prefetch
|
|
|
|
return this.initPromise;
|
|
|
|
function addLocalToIndex() { that.add(that.local); }
|
|
},
|
|
|
|
// ### public
|
|
|
|
initialize: function initialize(force) {
|
|
return !this.initPromise || force ? this._initialize() : this.initPromise;
|
|
},
|
|
|
|
// TODO: before initialize what happens?
|
|
add: function add(data) {
|
|
this.index.add(data);
|
|
return this;
|
|
},
|
|
|
|
get: function get(ids) {
|
|
ids = _.isArray(ids) ? ids : [].slice.call(arguments);
|
|
return this.index.get(ids);
|
|
},
|
|
|
|
search: function search(query, sync, async) {
|
|
var that = this, local;
|
|
|
|
sync = sync || _.noop;
|
|
async = async || _.noop;
|
|
|
|
local = this.sorter(this.index.search(query));
|
|
|
|
// return a copy to guarantee no changes within this scope
|
|
// as this array will get used when processing the remote results
|
|
sync(this.remote ? local.slice() : local);
|
|
|
|
if (this.remote && local.length < this.sufficient) {
|
|
this.remote.get(query, processRemote);
|
|
}
|
|
|
|
else if (this.remote) {
|
|
// #149: prevents outdated rate-limited requests from being sent
|
|
this.remote.cancelLastRequest();
|
|
}
|
|
|
|
return this;
|
|
|
|
function processRemote(remote) {
|
|
var nonDuplicates = [];
|
|
|
|
// exclude duplicates
|
|
_.each(remote, function(r) {
|
|
!_.some(local, function(l) {
|
|
return that.identify(r) === that.identify(l);
|
|
}) && nonDuplicates.push(r);
|
|
});
|
|
|
|
// #1148: Should Bloodhound index remote datums?
|
|
that.indexRemote && that.add(nonDuplicates);
|
|
|
|
async(nonDuplicates);
|
|
}
|
|
},
|
|
|
|
all: function all() {
|
|
return this.index.all();
|
|
},
|
|
|
|
clear: function clear() {
|
|
this.index.reset();
|
|
return this;
|
|
},
|
|
|
|
clearPrefetchCache: function clearPrefetchCache() {
|
|
this.prefetch && this.prefetch.clear();
|
|
return this;
|
|
},
|
|
|
|
clearRemoteCache: function clearRemoteCache() {
|
|
Transport.resetCache();
|
|
return this;
|
|
},
|
|
|
|
// DEPRECATED: will be removed in v1
|
|
ttAdapter: function ttAdapter() {
|
|
return this.__ttAdapter();
|
|
}
|
|
});
|
|
|
|
return Bloodhound;
|
|
})();
|