From bf9d8fcc070fa826c342282e102fe00e1af76d10 Mon Sep 17 00:00:00 2001
From: Matt Morgan <mlmorg@gmail.com>
Date: Thu, 26 Apr 2012 14:09:20 -0400
Subject: [PATCH] Alter typeahead to accept synchronous/asynchronous data
 source via function/callback

---
 docs/templates/pages/javascript.mustache |  6 ++--
 js/bootstrap-typeahead.js                | 16 +++++++----
 js/tests/unit/bootstrap-typeahead.js     | 36 ++++++++++++++++++++++++
 3 files changed, 50 insertions(+), 8 deletions(-)

diff --git a/docs/templates/pages/javascript.mustache b/docs/templates/pages/javascript.mustache
index 1ae3ffa5a8..e6f1576890 100644
--- a/docs/templates/pages/javascript.mustache
+++ b/docs/templates/pages/javascript.mustache
@@ -1385,9 +1385,9 @@ $('.carousel').carousel({
             <tbody>
               <tr>
                <td>{{_i}}source{{/i}}</td>
-               <td>{{_i}}array{{/i}}</td>
+               <td>{{_i}}array, function{{/i}}</td>
                <td>[ ]</td>
-               <td>{{_i}}The data source to query against.{{/i}}</td>
+               <td>{{_i}}The data source to query against. May be an array of strings or a function. The function is passed two arguments, the <code>query</code> value in the input field and the <code>process</code> callback. The function may be used synchronously by returning the data source directly or asynchronously via the <code>process</code> callback's single argument.{{/i}}</td>
              </tr>
              <tr>
                <td>{{_i}}items{{/i}}</td>
@@ -1426,4 +1426,4 @@ $('.carousel').carousel({
           <p>{{_i}}Initializes an input with a typeahead.{{/i}}</p>
         </div>
       </div>
-    </section>
\ No newline at end of file
+    </section>
diff --git a/js/bootstrap-typeahead.js b/js/bootstrap-typeahead.js
index 95a0fcdb78..281bdd6b3e 100644
--- a/js/bootstrap-typeahead.js
+++ b/js/bootstrap-typeahead.js
@@ -77,9 +77,7 @@
     }
 
   , lookup: function (event) {
-      var that = this
-        , items
-        , q
+      var items
 
       this.query = this.$element.val()
 
@@ -87,7 +85,15 @@
         return this.shown ? this.hide() : this
       }
 
-      items = $.grep(this.source, function (item) {
+      items = $.isFunction(this.source) ? this.source(this.query, $.proxy(this.process, this)) : this.source
+
+      return items ? this.process(items) : this
+    }
+
+  , process: function (items) {
+      var that = this
+
+      items = $.grep(items, function (item) {
         return that.matcher(item)
       })
 
@@ -282,4 +288,4 @@
     })
   })
 
-}(window.jQuery);
\ No newline at end of file
+}(window.jQuery);
diff --git a/js/tests/unit/bootstrap-typeahead.js b/js/tests/unit/bootstrap-typeahead.js
index 4e2428d6a4..25d4cafd8e 100644
--- a/js/tests/unit/bootstrap-typeahead.js
+++ b/js/tests/unit/bootstrap-typeahead.js
@@ -52,6 +52,42 @@ $(function () {
         typeahead.$menu.remove()
       })
 
+      test("should accept data source via synchronous function", function () {
+        var $input = $('<input />').typeahead({
+              source: function () {
+                return ['aa', 'ab', 'ac']
+              }
+            })
+          , typeahead = $input.data('typeahead')
+
+        $input.val('a')
+        typeahead.lookup()
+
+        ok(typeahead.$menu.is(":visible"), 'typeahead is visible')
+        equals(typeahead.$menu.find('li').length, 3, 'has 3 items in menu')
+        equals(typeahead.$menu.find('.active').length, 1, 'one item is active')
+
+        typeahead.$menu.remove()
+      })
+
+      test("should accept data source via asynchronous function", function () {
+        var $input = $('<input />').typeahead({
+              source: function (query, process) {
+                process(['aa', 'ab', 'ac'])
+              }
+            })
+          , typeahead = $input.data('typeahead')
+
+        $input.val('a')
+        typeahead.lookup()
+
+        ok(typeahead.$menu.is(":visible"), 'typeahead is visible')
+        equals(typeahead.$menu.find('li').length, 3, 'has 3 items in menu')
+        equals(typeahead.$menu.find('.active').length, 1, 'one item is active')
+
+        typeahead.$menu.remove()
+      })
+
       test("should not explode when regex chars are entered", function () {
         var $input = $('<input />').typeahead({
               source: ['aa', 'ab', 'ac', 'mdo*', 'fat+']
-- 
GitLab