diff --git a/Gruntfile.js b/Gruntfile.js index f074763b..efdcc884 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -135,7 +135,6 @@ module.exports = function(grunt) { bloodhound: { src: '<%= tempDir %>/bloodhound.js', objectToExport: 'Bloodhound', - amdModuleId: 'bloodhound', deps: { default: ['$'], amd: ['jquery'], @@ -145,7 +144,6 @@ module.exports = function(grunt) { }, typeahead: { src: '<%= tempDir %>/typeahead.jquery.js', - amdModuleId: 'typeahead.js', deps: { default: ['$'], amd: ['jquery'], diff --git a/doc/bloodhound.md b/doc/bloodhound.md index ebb4df6d..5d1b10b7 100644 --- a/doc/bloodhound.md +++ b/doc/bloodhound.md @@ -193,6 +193,9 @@ options you can configure. the internal search index is insufficient or, if more configurability is needed, a [remote options hash](#remote). +* `indexRemote` – Adds the data loaded from `remote` to the search index (where + `local` and `prefetch` are stored for retrieval). Defaults to `false`. + [compare function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort @@ -252,15 +255,15 @@ When configuring `remote`, the following options are available. * `url` – The URL remote data should be loaded from. **Required.** * `prepare` – A function that provides a hook to allow you to prepare the - settings object passed to `transport` when a request is about to be made. - The function signature should be `prepare(query, settings)`, where `query` is - the query `#search` was called with and `settings` is the default settings - object created internally by the Bloodhound instance. The `prepare` function - should return a settings object. Defaults to the [identity function]. + settings object passed to `transport` when a request is about to be made. + The function signature should be `prepare(query, settings)`, where `query` is + the query `#search` was called with and `settings` is the default settings + object created internally by the Bloodhound instance. The `prepare` function + should return a settings object. Defaults to the [identity function]. * `wildcard` – A convenience option for `prepare`. If set, `prepare` will be a - function that replaces the value of this option in `url` with the URI encoded - query. + function that replaces the value of this option in `url` with the URI encoded + query. * `rateLimitBy` – The method used to rate-limit network requests. Can be either `debounce` or `throttle`. Defaults to `debounce`. @@ -269,8 +272,8 @@ When configuring `remote`, the following options are available. `rateLimitBy`. Defaults to `300`. * `transform` – A function with the signature `transform(response)` that allows - you to transform the remote response before the Bloodhound instance operates - on it. Defaults to the [identity function]. + you to transform the remote response before the Bloodhound instance operates + on it. Defaults to the [identity function]. diff --git a/src/bloodhound/bloodhound.js b/src/bloodhound/bloodhound.js index 912d9d4a..d6f5cda7 100644 --- a/src/bloodhound/bloodhound.js +++ b/src/bloodhound/bloodhound.js @@ -20,6 +20,7 @@ var Bloodhound = (function() { 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; @@ -132,6 +133,9 @@ var Bloodhound = (function() { 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 @@ -159,7 +163,10 @@ var Bloodhound = (function() { }) && nonDuplicates.push(r); }); - async && async(nonDuplicates); + // #1148: Should Bloodhound index remote datums? + that.indexRemote && that.add(nonDuplicates); + + async(nonDuplicates); } }, diff --git a/src/bloodhound/options_parser.js b/src/bloodhound/options_parser.js index 74107f7b..d08462b3 100644 --- a/src/bloodhound/options_parser.js +++ b/src/bloodhound/options_parser.js @@ -16,6 +16,7 @@ var oParser = (function() { datumTokenizer: null, queryTokenizer: null, sufficient: 5, + indexRemote: false, sorter: null, local: [], prefetch: null, diff --git a/src/bloodhound/remote.js b/src/bloodhound/remote.js index 66fc6194..4fb97441 100644 --- a/src/bloodhound/remote.js +++ b/src/bloodhound/remote.js @@ -14,6 +14,7 @@ var Remote = (function() { this.url = o.url; this.prepare = o.prepare; this.transform = o.transform; + this.indexResponse = o.indexResponse; this.transport = new Transport({ cache: o.cache, diff --git a/src/typeahead/dataset.js b/src/typeahead/dataset.js index fce09c30..40702cc4 100644 --- a/src/typeahead/dataset.js +++ b/src/typeahead/dataset.js @@ -269,7 +269,6 @@ var Dataset = (function() { // do not render the suggestions as they've become outdated if (!canceled && rendered < that.limit) { that.cancel = $.noop; - rendered += suggestions.length; that._append(query, suggestions.slice(0, that.limit - rendered)); that.async && that.trigger('asyncReceived', query); diff --git a/src/typeahead/menu.js b/src/typeahead/menu.js index b0574e27..c1dfd7e8 100644 --- a/src/typeahead/menu.js +++ b/src/typeahead/menu.js @@ -121,6 +121,7 @@ var Menu = (function() { }, open: function open() { + this.$node.scrollTop(0); this.$node.addClass(this.classes.open); }, diff --git a/src/typeahead/plugin.js b/src/typeahead/plugin.js index 9c1db197..443c0eb5 100644 --- a/src/typeahead/plugin.js +++ b/src/typeahead/plugin.js @@ -170,7 +170,7 @@ } else { - ttEach(this, function(t) { t.setVal(newVal); }); + ttEach(this, function(t) { t.setVal(_.toStr(newVal)); }); return this; } }, diff --git a/test/bloodhound/bloodhound_spec.js b/test/bloodhound/bloodhound_spec.js index ffd5fc2c..a80a0229 100644 --- a/test/bloodhound/bloodhound_spec.js +++ b/test/bloodhound/bloodhound_spec.js @@ -289,6 +289,57 @@ describe('Bloodhound', function() { function fakeGet(o, cb) { cb(fixtures.data.animals); } }); + + it('should not add remote data to index if indexRemote is false', function() { + this.bloodhound = build({ + identify: function(d) { return d.value; }, + remote: '/remote' + }); + this.bloodhound.remote.get.andCallFake(fakeGet); + + spyOn(this.bloodhound, 'add'); + this.bloodhound.search('dog'); + + expect(this.bloodhound.add).not.toHaveBeenCalled(); + + function fakeGet(o, cb) { cb(fixtures.data.animals); } + }); + + it('should add remote data to index if indexRemote is true', function() { + this.bloodhound = build({ + identify: function(d) { return d.value; }, + indexRemote: true, + remote: '/remote' + }); + this.bloodhound.remote.get.andCallFake(fakeGet); + + spyOn(this.bloodhound, 'add'); + this.bloodhound.search('dog'); + + expect(this.bloodhound.add).toHaveBeenCalledWith(fixtures.data.animals); + + function fakeGet(o, cb) { cb(fixtures.data.animals); } + }); + + it('should not add duplicates from remote to index', function() { + this.bloodhound = build({ + identify: function(d) { return d.value; }, + indexRemote: true, + local: fixtures.data.animals, + remote: '/remote' + }); + this.bloodhound.remote.get.andCallFake(fakeGet); + + spyOn(this.bloodhound, 'add'); + this.bloodhound.search('dog'); + + expect(this.bloodhound.add).toHaveBeenCalledWith([ + { value: 'cat' }, + { value: 'moose' } + ]); + + function fakeGet(o, cb) { cb(fixtures.data.animals); } + }); }); // helper functions diff --git a/test/typeahead/results_spec.js b/test/typeahead/menu_spec.js similarity index 97% rename from test/typeahead/results_spec.js rename to test/typeahead/menu_spec.js index f379fc39..5ca48526 100644 --- a/test/typeahead/results_spec.js +++ b/test/typeahead/menu_spec.js @@ -125,6 +125,13 @@ describe('Menu', function() { }); describe('#open', function() { + it('should set scroll top of node to 0', function() { + spyOn(this.view.$node, 'scrollTop'); + this.view.open(); + + expect(this.view.$node.scrollTop).toHaveBeenCalledWith(0); + }); + it('should add open class to node', function() { this.$node.removeClass(www.classes.open); this.view.open(); diff --git a/test/typeahead/plugin_spec.js b/test/typeahead/plugin_spec.js index 7399be83..ade34896 100644 --- a/test/typeahead/plugin_spec.js +++ b/test/typeahead/plugin_spec.js @@ -157,6 +157,14 @@ describe('$plugin', function() { expect(this.$input.typeahead('val')).toBe('foo'); }); + it('#val(q) should coerce null and undefined to empty string', function() { + this.$input.typeahead('val', null); + expect(this.$input.typeahead('val')).toBe(''); + + this.$input.typeahead('val', undefined); + expect(this.$input.typeahead('val')).toBe(''); + }); + it('#destroy should revert modified attributes', function() { expect(this.$input).toHaveAttr('autocomplete', 'off'); expect(this.$input).toHaveAttr('dir');