From d789c4b2e8a05204270f5c1264b68c18f6ca85a5 Mon Sep 17 00:00:00 2001 From: Jake Harding Date: Tue, 12 Mar 2013 22:13:40 -0700 Subject: [PATCH] Trigger typeahead:initialized. --- src/dataset.js | 37 +++++++++++++++------ src/typeahead.js | 34 +++++++++++++------ src/typeahead_view.js | 4 +-- test/dataset_spec.js | 65 +++++++++++++++++++++++-------------- test/playground.html | 14 ++++---- test/typeahead_view_spec.js | 1 + 6 files changed, 103 insertions(+), 52 deletions(-) diff --git a/src/dataset.js b/src/dataset.js index 57d09f40..5fded1d0 100644 --- a/src/dataset.js +++ b/src/dataset.js @@ -10,13 +10,22 @@ var Dataset = (function() { utils.bindAll(this); if (o.template && !o.engine) { - throw new Error('no template engine specified'); + $.error('no template engine specified'); + } + + if (!o.local && !o.prefetch && !o.remote) { + $.error('one of local, prefetch, or remote is requried'); } this.name = o.name; this.limit = o.limit || 5; this.template = compileTemplate(o.template, o.engine); + // used in #initialize + this.local = o.local; + this.prefetch = o.prefetch; + this.remote = o.remote; + this.keys = { version: 'version', protocol: 'protocol', @@ -40,6 +49,7 @@ var Dataset = (function() { _loadPrefetchData: function(o) { var that = this, + deferred, version = this.storage.get(this.keys.version), protocol = this.storage.get(this.keys.protocol), itemHash = this.storage.get(this.keys.itemHash), @@ -55,12 +65,16 @@ var Dataset = (function() { itemHash: itemHash, adjacencyList: adjacencyList }); + + deferred = $.Deferred().resolve(); } else { - $.getJSON(o.url).done(processPrefetchData); + deferred = $.getJSON(o.url).done(processPrefetchData); } + return deferred; + function processPrefetchData(data) { var filteredData = o.filter ? o.filter(data) : data, processedData = that._processData(filteredData), @@ -256,17 +270,20 @@ var Dataset = (function() { // the contents of this function are broken out of the constructor // to help improve the testability of datasets - initialize: function(o) { - if (!o.local && !o.prefetch && !o.remote) { - throw new Error('one of local, prefetch, or remote is requried'); - } + initialize: function() { + var deferred; + + this.local && this._processLocalData(this.local); + this.transport = this.remote ? new Transport(this.remote) : null; - this.transport = o.remote ? new Transport(o.remote) : null; + deferred = this.prefetch ? + this._loadPrefetchData(this.prefetch) : + $.Deferred().resolve(); - o.local && this._processLocalData(o.local); - o.prefetch && this._loadPrefetchData(o.prefetch); + this.local = this.prefetch = this.remote = null; + this.initialize = function() { return deferred; }; - return this; + return deferred; }, getSuggestions: function(query, callback) { diff --git a/src/typeahead.js b/src/typeahead.js index 66aff87b..062f4a9e 100644 --- a/src/typeahead.js +++ b/src/typeahead.js @@ -5,7 +5,7 @@ */ (function() { - var datasetCache = {}, methods; + var datasetCache = {}, viewKey = 'ttView', methods; methods = { initialize: function(datasetDefs) { @@ -14,11 +14,11 @@ datasetDefs = utils.isArray(datasetDefs) ? datasetDefs : [datasetDefs]; if (this.length === 0) { - throw new Error('typeahead initialized without DOM element'); + $.error('typeahead initialized without DOM element'); } if (datasetDefs.length === 0) { - throw new Error('no datasets provided'); + $.error('no datasets provided'); } datasets = utils.map(datasetDefs, function(o) { @@ -26,25 +26,39 @@ return datasetCache[o.name] ? datasetCache[o.name] : - datasetCache[o.name] = new Dataset(o).initialize(o); + datasetCache[o.name] = new Dataset(o); }); - return this.each(function() { + return this.each(initialize); + + function initialize() { var $input = $(this), - view = new TypeaheadView({ input: $input, datasets: datasets }); + deferreds, + eventBus = new EventBus({ el: $input }); - $input.data('ttView', view); - }); + deferreds = utils.map(datasets, function(dataset) { + return dataset.initialize(); + }); + + $input.data(viewKey, new TypeaheadView({ + input: $input, + eventBus: eventBus = new EventBus({ el: $input }), + datasets: datasets + })); + + $.when.apply($, deferreds) + .always(function() { eventBus.trigger('initialized'); }); + } }, destroy: function() { this.each(function() { var $this = $(this), - view = $this.data('ttView'); + view = $this.data(viewKey); if (view) { view.destroy(); - $this.removeData('ttView'); + $this.removeData(viewKey); } }); } diff --git a/src/typeahead_view.js b/src/typeahead_view.js index 2c47f628..d9eb0c27 100644 --- a/src/typeahead_view.js +++ b/src/typeahead_view.js @@ -66,12 +66,12 @@ var TypeaheadView = (function() { this.datasets = o.datasets; this.dir = null; + this.eventBus = o.eventBus; + $menu = this.$node.find('.tt-dropdown-menu'); $input = this.$node.find('.tt-query'); $hint = this.$node.find('.tt-hint'); - this.eventBus = new EventBus({ el: $input }); - this.dropdownView = new DropdownView({ menu: $menu }) .on('suggestionSelected', this._handleSelection) .on('cursorMoved', this._clearHint) diff --git a/test/dataset_spec.js b/test/dataset_spec.js index 7f341a9b..5208203f 100644 --- a/test/dataset_spec.js +++ b/test/dataset_spec.js @@ -70,6 +70,16 @@ describe('Dataset', function() { }); }); + describe('when called without local, prefetch, or remote', function() { + beforeEach(function() { + this.fn = function() { this.dataset = new Dataset(); }; + }); + + it('should throw an error', function() { + expect(this.fn).toThrow(); + }); + }); + describe('when called with no template', function() { beforeEach(function() { this.dataset = new Dataset({ local: fixtureData }); @@ -100,23 +110,22 @@ describe('Dataset', function() { }); describe('#initialize', function() { - beforeEach(function() { - this.dataset = new Dataset({ name: '#initialize' }); - }); + it('should return Deferred instance', function() { + var returnVal; - describe('when called without local, prefetch, or remote', function() { - beforeEach(function() { - this.fn = function() { this.dataset.initialize({}); }; - }); + this.dataset = new Dataset({ local: fixtureData }); + returnVal = this.dataset.initialize(); - it('should throw an error', function() { - expect(this.fn).toThrow(); - }); + // eh, have to rely on duck typing unfortunately + expect(returnVal.fail).toBeDefined(); + expect(returnVal.done).toBeDefined(); + expect(returnVal.always).toBeDefined(); }); describe('when called with local', function() { beforeEach(function() { - this.dataset.initialize({ local: fixtureData }); + this.dataset = new Dataset({ local: fixtureData }); + this.dataset.initialize(); }); it('should process and merge the data', function() { @@ -128,8 +137,9 @@ describe('Dataset', function() { describe('when called with prefetch', function() { describe('if data is available in storage', function() { beforeEach(function() { + this.dataset = new Dataset({ prefetch: '/prefetch.json' }); this.dataset.storage.get.andCallFake(mockStorageFns.getHit); - this.dataset.initialize({ prefetch: '/prefetch.json' }); + this.dataset.initialize(); }); it('should not make ajax request', function() { @@ -146,10 +156,6 @@ describe('Dataset', function() { // default ttl var ttl = 24 * 60 * 60 * 1000; - beforeEach(function() { - this.dataset.storage.get.andCallFake(mockStorageFns.getMiss); - }); - describe('if filter was passed in', function() { var filteredAdjacencyList = { f: ['filter'] }, filteredItemHash = { @@ -157,13 +163,16 @@ describe('Dataset', function() { }; beforeEach(function() { - this.dataset.initialize({ + this.dataset = new Dataset({ prefetch: { url: '/prefetch.json', filter: function(data) { return ['filter']; } } }); + this.dataset.storage.get.andCallFake(mockStorageFns.getMiss); + this.dataset.initialize(); + this.request = mostRecentAjaxRequest(); this.request.response(prefetchResp); }); @@ -194,7 +203,11 @@ describe('Dataset', function() { describe('if filter was not passed in', function() { beforeEach(function() { - this.dataset.initialize({ prefetch: '/prefetch.json' }); + this.dataset = new Dataset({ prefetch: '/prefetch.json' }); + + this.dataset.storage.get.andCallFake(mockStorageFns.getMiss); + + this.dataset.initialize(); this.request = mostRecentAjaxRequest(); this.request.response(prefetchResp); @@ -228,7 +241,8 @@ describe('Dataset', function() { describe('when called with remote', function() { beforeEach(function() { - this.dataset.initialize({ remote: '/remote' }); + this.dataset = new Dataset({ remote: '/remote' }); + this.dataset.initialize(); }); it('should initialize the transport', function() { @@ -239,7 +253,8 @@ describe('Dataset', function() { describe('Datasource options', function() { beforeEach(function() { - this.dataset = new Dataset({}).initialize({ local: fixtureData }); + this.dataset = new Dataset({ local: fixtureData }); + this.dataset.initialize(); }); it('allow for a custom matching function to be defined', function() { @@ -272,8 +287,8 @@ describe('Dataset', function() { describe('Matching, ranking, combining, returning results', function() { beforeEach(function() { - this.dataset = new Dataset({}) - .initialize({ local: fixtureData, remote: '/remote' }); + this.dataset = new Dataset({ local: fixtureData, remote: '/remote' }); + this.dataset.initialize(); }); it('network requests are not triggered with enough local results', function() { @@ -362,7 +377,8 @@ describe('Dataset', function() { var fixtureData = ['course-106', 'user_name', 'One-Two', 'two three']; beforeEach(function() { - this.dataset = new Dataset({}).initialize({ local: fixtureData }); + this.dataset = new Dataset({ local: fixtureData }); + this.dataset.initialize(); }); it('normalizes capitalization to match items', function() { @@ -407,7 +423,8 @@ describe('Dataset', function() { var fixtureData = [{ value: 'course-106', tokens: ['course-106'] }]; beforeEach(function() { - this.dataset = new Dataset({}).initialize({ local: fixtureData }); + this.dataset = new Dataset({ local: fixtureData }); + this.dataset.initialize(); }); it('matches items with dashes', function() { diff --git a/test/playground.html b/test/playground.html index 496c4183..5fb47880 100644 --- a/test/playground.html +++ b/test/playground.html @@ -47,6 +47,13 @@