algoliasearch-rails 1.9.3 → 1.9.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4c717e0d5fe37fae7e75570ceba23c498cecaa4f
4
- data.tar.gz: c16f748ce4eda2d00cdd79ca26615993c4b8a421
3
+ metadata.gz: 6826be3868adf59a195a87a6ff18d66368bc645d
4
+ data.tar.gz: 02d75cf295aa27237df63c1184e1fbf6ac936e75
5
5
  SHA512:
6
- metadata.gz: 50b34c87258d9849cedf846bc335b17f1e18bff4227ba539c6eed9db86e1c7eb6194694287107e0930df2a61e3a6cf8a26fe8437ce16da4de5c1ae3eee76ec4d
7
- data.tar.gz: 3046f5be4d6c3f169e2904274ef22a5ae0559b662ab06b59c5d8e7985d0feac59995a709e1c1a512a2a4120bbc3042e86e408ba80d9886affdde2ad1a8abf86c
6
+ metadata.gz: a4b81c53b990ca11d49242b5e2954e27d102568675cd4e06c0529bd21b1aae3bd9614c0a105ba6b44012c576e2ecaf45bf26dda67a342c54de5109e046dbaa87
7
+ data.tar.gz: 7db698c108c3c7d40494c6d4ceb9f1bb59010efe39d530cc6860cfe538fbab216c483361b83d7301310c5e1b4dae026d7d35c6a905f1ca83104dd60641f9b4e1
data/ChangeLog CHANGED
@@ -1,5 +1,12 @@
1
1
  CHANGELOG
2
2
 
3
+ 2014-03-30 1.9.4
4
+
5
+ * Ability to index an array of objects using the `index_objects` method. Ref #15
6
+ * Upgrade typeahead.js to 0.10.2 (fixed flickering)
7
+ * Upgrade algoliasearch-client-ruby to 1.2.8 (fixed unhandled exception)
8
+ * Upgrade algoliasearch-client-js to 2.4.5 (embed last ExplainResults helper fixes)
9
+
3
10
  2014-03-26 1.9.3
4
11
 
5
12
  * Ruby 1.8 compatibility
@@ -35,7 +35,7 @@ GEM
35
35
  i18n (~> 0.6, >= 0.6.4)
36
36
  multi_json (~> 1.0)
37
37
  addressable (2.3.6)
38
- algoliasearch (1.2.7)
38
+ algoliasearch (1.2.8)
39
39
  httpclient (~> 2.3)
40
40
  json (>= 1.5.1)
41
41
  arel (3.0.3)
data/README.md CHANGED
@@ -233,6 +233,20 @@ class Post < ActiveRecord::Base
233
233
  end
234
234
  ```
235
235
 
236
+ You can index a subset of your records using either:
237
+
238
+ ```ruby
239
+ # will generate batch API calls (recommended)
240
+ MyModel.where('updated_at > ?', 10.minutes.ago).reindex!
241
+ ```
242
+
243
+ or
244
+
245
+ ```ruby
246
+ MyModel.index_objects MyModel.limit(5)
247
+ ```
248
+
249
+
236
250
  Configuration example
237
251
  ---------------------
238
252
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.9.3
1
+ 1.9.4
@@ -6,11 +6,11 @@
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "algoliasearch-rails"
9
- s.version = "1.9.3"
9
+ s.version = "1.9.4"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
12
  s.authors = ["Algolia"]
13
- s.date = "2014-03-26"
13
+ s.date = "2014-03-30"
14
14
  s.description = "AlgoliaSearch integration to your favorite ORM"
15
15
  s.email = "contact@algolia.com"
16
16
  s.extra_rdoc_files = [
@@ -124,6 +124,7 @@ module AlgoliaSearch
124
124
  class <<base
125
125
  alias_method :without_auto_index, :algolia_without_auto_index unless method_defined? :without_auto_index
126
126
  alias_method :reindex!, :algolia_reindex! unless method_defined? :reindex!
127
+ alias_method :index_objects, :algolia_index_objects unless method_defined? :index_objects
127
128
  alias_method :index!, :algolia_index! unless method_defined? :index!
128
129
  alias_method :remove_from_index!, :algolia_remove_from_index! unless method_defined? :remove_from_index!
129
130
  alias_method :clear_index!, :algolia_clear_index! unless method_defined? :clear_index!
@@ -185,6 +186,11 @@ module AlgoliaSearch
185
186
  @algolia_index.wait_task(last_task["taskID"]) if last_task and synchronous == true
186
187
  end
187
188
 
189
+ def algolia_index_objects(objects, synchronous = false)
190
+ task = @algolia_index.save_objects(objects.map { |o| algolia_index_settings.get_attributes(o).merge 'objectID' => algolia_object_id_of(o) })
191
+ @algolia_index.wait_task(task["taskID"]) if synchronous == true
192
+ end
193
+
188
194
  def algolia_index!(object, synchronous = false)
189
195
  return if @algolia_without_auto_index_scope
190
196
  object_id = algolia_object_id_of(object)
@@ -21,7 +21,7 @@
21
21
  * THE SOFTWARE.
22
22
  */
23
23
 
24
- var ALGOLIA_VERSION = '2.4.4';
24
+ var ALGOLIA_VERSION = '2.4.5';
25
25
 
26
26
  /*
27
27
  * Copyright (c) 2013 Algolia
@@ -121,15 +121,23 @@ function AlgoliaExplainResults(hit, titleAttribute, otherAttributes) {
121
121
  }
122
122
 
123
123
  function _getHitExplanationForOneAttr(hit, foundWords, attr) {
124
+ var base = hit._highlightResult || hit;
124
125
  if (attr.indexOf('.') === -1) {
125
- if (attr in hit._highlightResult) {
126
- return _getHitExplanationForOneAttr_recurse(hit._highlightResult[attr], foundWords);
126
+ if (attr in base) {
127
+ return _getHitExplanationForOneAttr_recurse(base[attr], foundWords);
127
128
  }
128
129
  return [];
129
130
  }
130
131
  var array = attr.split('.');
131
- var obj = hit._highlightResult;
132
+ var obj = base;
132
133
  for (var i = 0; i < array.length; ++i) {
134
+ if (Object.prototype.toString.call(obj) === '[object Array]') {
135
+ var res = [];
136
+ for (var j = 0; j < obj.length; ++j) {
137
+ res = res.concat(_getHitExplanationForOneAttr(obj[j], foundWords, array.slice(i).join('.')));
138
+ }
139
+ return res;
140
+ }
133
141
  if (array[i] in obj) {
134
142
  obj = obj[array[i]];
135
143
  } else {
@@ -1,7 +1,7 @@
1
1
  /*!
2
- * algoliasearch 2.4.4
2
+ * algoliasearch 2.4.5
3
3
  * https://github.com/algolia/algoliasearch-client-js
4
4
  * Copyright 2014 Algolia SAS; Licensed MIT
5
5
  */
6
6
 
7
- function AlgoliaExplainResults(a,b,c){function d(a,b){var c=[];if("object"==typeof a&&"matchedWords"in a&&"value"in a){for(var e=!1,f=0;f<a.matchedWords.length;++f){var g=a.matchedWords[f];g in b||(b[g]=1,e=!0)}e&&c.push(a.value)}else if("[object Array]"===Object.prototype.toString.call(a))for(var h=0;h<a.length;++h){var i=d(a[h],b);c=c.concat(i)}else if("object"==typeof a)for(var j in a)a.hasOwnProperty(j)&&(c=c.concat(d(a[j],b)));return c}function e(a,b,c){if(-1===c.indexOf("."))return c in a._highlightResult?d(a._highlightResult[c],b):[];for(var e=c.split("."),f=a._highlightResult,g=0;g<e.length;++g){if(!(e[g]in f))return[];f=f[e[g]]}return d(f,b)}var f={},g={},h=e(a,g,b);if(f.title=h.length>0?h[0]:"",f.subtitles=[],"undefined"!=typeof c)for(var i=0;i<c.length;++i)for(var j=e(a,g,c[i]),k=0;k<j.length;++k)f.subtitles.push({attr:c[i],value:j[k]});return f}var ALGOLIA_VERSION="2.4.4",AlgoliaSearch=function(a,b,c,d,e){this.applicationID=a,this.apiKey=b,this._isUndefined(e)&&(e=[a+"-1.algolia.io",a+"-2.algolia.io",a+"-3.algolia.io"]),this.hosts=[];for(var f=0;f<e.length;++f)Math.random()>.5&&this.hosts.reverse(),this._isUndefined(c)||null==c?this.hosts.push(("https:"==document.location.protocol?"https":"http")+"://"+e[f]):"https"===c||"HTTPS"===c?this.hosts.push("https://"+e[f]):this.hosts.push("http://"+e[f]);Math.random()>.5&&this.hosts.reverse(),(this._isUndefined(d)||d)&&this._jsonRequest({method:"GET",url:"/1/isalive"}),this.extraHeaders=[]};AlgoliaSearch.prototype={deleteIndex:function(a,b){this._jsonRequest({method:"DELETE",url:"/1/indexes/"+encodeURIComponent(a),callback:b})},moveIndex:function(a,b,c){var d={operation:"move",destination:b};this._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(a)+"/operation",body:d,callback:c})},copyIndex:function(a,b,c){var d={operation:"copy",destination:b};this._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(a)+"/operation",body:d,callback:c})},getLogs:function(a,b,c){this._isUndefined(b)&&(b=0),this._isUndefined(c)&&(c=10),this._jsonRequest({method:"GET",url:"/1/logs?offset="+b+"&length="+c,callback:a})},listIndexes:function(a){this._jsonRequest({method:"GET",url:"/1/indexes/",callback:a})},initIndex:function(a){return new this.Index(this,a)},listUserKeys:function(a){this._jsonRequest({method:"GET",url:"/1/keys",callback:a})},getUserKeyACL:function(a,b){this._jsonRequest({method:"GET",url:"/1/keys/"+a,callback:b})},deleteUserKey:function(a,b){this._jsonRequest({method:"DELETE",url:"/1/keys/"+a,callback:b})},addUserKey:function(a,b){var c={};c.acl=a,this._jsonRequest({method:"POST",url:"/1/keys",body:c,callback:b})},addUserKeyWithValidity:function(a,b,c,d,e){var f=this,g={};g.acl=a,g.validity=b,g.maxQueriesPerIPPerHour=c,g.maxHitsPerQuery=d,this._jsonRequest({method:"POST",url:"/1/indexes/"+f.indexName+"/keys",body:g,callback:e})},setSecurityTags:function(a){if("[object Array]"===Object.prototype.toString.call(a)){for(var b=[],c=0;c<a.length;++c)if("[object Array]"===Object.prototype.toString.call(a[c])){for(var d=[],e=0;e<a[c].length;++e)d.push(a[c][e]);b.push("("+d.join(",")+")")}else b.push(a[c]);a=b.join(",")}this.setExtraHeader("X-Algolia-TagFilters",a)},setUserToken:function(a){this.setExtraHeader("X-Algolia-UserToken",a)},startQueriesBatch:function(){this.batch=[]},addQueryInBatch:function(a,b,c){var d="query="+encodeURIComponent(b);this._isUndefined(c)||null==c||(d=this._getSearchParams(c,d)),this.batch.push({indexName:a,params:d})},clearCache:function(){this.cache={}},sendQueriesBatch:function(a,b){for(var c=this,d={requests:[],apiKey:this.apiKey,appID:this.applicationID},e=0;e<c.batch.length;++e)d.requests.push(c.batch[e]);if(window.clearTimeout(c.onDelayTrigger),!this._isUndefined(b)&&null!=b&&b>0){var f=window.setTimeout(function(){c._sendQueriesBatch(d,a)},b);c.onDelayTrigger=f}else this._sendQueriesBatch(d,a)},Index:function(a,b){this.indexName=b,this.as=a,this.typeAheadArgs=null,this.typeAheadValueOption=null},setExtraHeader:function(a,b){this.extraHeaders.push({key:a,value:b})},_sendQueriesBatch:function(a,b){this._jsonRequest({cache:this.cache,method:"POST",url:"/1/indexes/*/queries",body:a,callback:b})},_jsonRequest:function(a){var b=this,c=a.callback,d=null,e=a.url;if(this._isUndefined(a.body)||(e=a.url+"_body_"+JSON.stringify(a.body)),!this._isUndefined(a.cache)&&(d=a.cache,!this._isUndefined(d[e])))return this._isUndefined(c)||c(!0,d[e]),void 0;var f=function(g){var h=0;return b._isUndefined(g)||(h=g),b.hosts.length<=h?(b._isUndefined(c)||c(!1,{message:"Cannot contact server"}),void 0):(a.callback=function(g,i,j,k){i||b._isUndefined(k)||console.log("Error: "+k.message),i&&!b._isUndefined(a.cache)&&(d[e]=k),!i&&g&&h+1<b.hosts.length?f(h+1):b._isUndefined(c)||c(i,k)},a.hostname=b.hosts[h],b._jsonRequestByHost(a),void 0)};f()},_jsonRequestByHost:function(a){var b=null,c=this;this._isUndefined(a.body)||(b=JSON.stringify(a.body));var d=a.hostname+a.url,e=null;if(e=new XMLHttpRequest,"withCredentials"in e){e.open(a.method,d,!0),e.setRequestHeader("X-Algolia-API-Key",this.apiKey),e.setRequestHeader("X-Algolia-Application-Id",this.applicationID);for(var f=0;f<this.extraHeaders.length;++f)e.setRequestHeader(this.extraHeaders[f].key,this.extraHeaders[f].value);null!=b&&e.setRequestHeader("Content-type","application/json")}else"undefined"!=typeof XDomainRequest?(e=new XDomainRequest,e.open(a.method,d)):console.log("your browser is too old to support CORS requests");e.send(b),e.onload=function(b){if(c._isUndefined(b)||null==b.target)a.callback(!1,!0,b,JSON.parse(e.responseText));else{var d=0===b.target.status||503===b.target.status,f=200===b.target.status||201===b.target.status;a.callback(d,f,b.target,null!=b.target.response?JSON.parse(b.target.response):null)}},e.onerror=function(){a.callback(!0,!1,null,{message:"Could not connect to Host"})}},_getSearchParams:function(a,b){if(this._isUndefined(a)||null==a)return b;for(var c in a)null!=c&&a.hasOwnProperty(c)&&(b+=0===b.length?"?":"&",b+=c+"="+encodeURIComponent("[object Array]"===Object.prototype.toString.call(a[c])?JSON.stringify(a[c]):a[c]));return b},_isUndefined:function(a){return void 0===a},applicationID:null,apiKey:null,hosts:[],cache:{},extraHeaders:[]},AlgoliaSearch.prototype.Index.prototype={clearCache:function(){this.cache={}},addObject:function(a,b,c){var d=this;this.as._isUndefined(c)?this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(d.indexName),body:a,callback:b}):this.as._jsonRequest({method:"PUT",url:"/1/indexes/"+encodeURIComponent(d.indexName)+"/"+encodeURIComponent(c),body:a,callback:b})},addObjects:function(a,b){for(var c=this,d={requests:[]},e=0;e<a.length;++e){var f={action:"addObject",body:a[e]};d.requests.push(f)}this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/batch",body:d,callback:b})},getObject:function(a,b,c){var d=this,e="";if(!this.as._isUndefined(c)){e="?attributes=";for(var f=0;f<c.length;++f)0!==f&&(e+=","),e+=c[f]}this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(d.indexName)+"/"+encodeURIComponent(a)+e,callback:b})},partialUpdateObject:function(a,b){var c=this;this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/"+encodeURIComponent(a.objectID)+"/partial",body:a,callback:b})},partialUpdateObjects:function(a,b){for(var c=this,d={requests:[]},e=0;e<a.length;++e){var f={action:"partialUpdateObject",objectID:a[e].objectID,body:a[e]};d.requests.push(f)}this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/batch",body:d,callback:b})},saveObject:function(a,b){var c=this;this.as._jsonRequest({method:"PUT",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/"+encodeURIComponent(a.objectID),body:a,callback:b})},saveObjects:function(a,b){for(var c=this,d={requests:[]},e=0;e<a.length;++e){var f={action:"updateObject",objectID:a[e].objectID,body:a[e]};d.requests.push(f)}this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/batch",body:d,callback:b})},deleteObject:function(a,b){if(null==a||0===a.length)return b(!1,{message:"empty objectID"}),void 0;var c=this;this.as._jsonRequest({method:"DELETE",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/"+encodeURIComponent(a),callback:b})},search:function(a,b,c,d){var e=this,f="query="+encodeURIComponent(a);if(this.as._isUndefined(c)||null==c||(f=this.as._getSearchParams(c,f)),window.clearTimeout(e.onDelayTrigger),!this.as._isUndefined(d)&&null!=d&&d>0){var g=window.setTimeout(function(){e._search(f,b)},d);e.onDelayTrigger=g}else this._search(f,b)},browse:function(a,b,c){var d=this,e="?page="+a;_.isUndefined(c)||(e+="&hitsPerPage="+c),this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(d.indexName)+"/browse"+e,callback:b})},ttAdapter:function(a){var b=this;return function(c,d){b.search(c,function(a,b){a&&d(b.hits)},a)}},waitTask:function(a,b){var c=this;this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/task/"+a,callback:function(d,e){d?"published"===e.status?b(!0,e):setTimeout(function(){c.waitTask(a,b)},100):b(!1,e)}})},clearIndex:function(a){var b=this;this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(b.indexName)+"/clear",callback:a})},getSettings:function(a){var b=this;this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(b.indexName)+"/settings",callback:a})},setSettings:function(a,b){var c=this;this.as._jsonRequest({method:"PUT",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/settings",body:a,callback:b})},listUserKeys:function(a){var b=this;this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(b.indexName)+"/keys",callback:a})},getUserKeyACL:function(a,b){var c=this;this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/keys/"+a,callback:b})},deleteUserKey:function(a,b){var c=this;this.as._jsonRequest({method:"DELETE",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/keys/"+a,callback:b})},addUserKey:function(a,b){var c=this,d={};d.acl=a,this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/keys",body:d,callback:b})},addUserKeyWithValidity:function(a,b,c,d,e){var f=this,g={};g.acl=a,g.validity=b,g.maxQueriesPerIPPerHour=c,g.maxHitsPerQuery=d,this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(f.indexName)+"/keys",body:g,callback:e})},_search:function(a,b){this.as._jsonRequest({cache:this.cache,method:"POST",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/query",body:{params:a,apiKey:this.as.apiKey,appID:this.as.applicationID},callback:b})},as:null,indexName:null,cache:{},typeAheadArgs:null,typeAheadValueOption:null,emptyConstructor:function(){}},function(){var a,b=function(a){a=a||{};for(var b=1;b<arguments.length;b++)if(arguments[b])for(var c in arguments[b])arguments[b].hasOwnProperty(c)&&(a[c]=arguments[b][c]);return a};window.AlgoliaSearchHelper=function(c,d,e){var f={facets:[],disjunctiveFacets:[],hitsPerPage:20};this.init(c,d,b({},f,e)),a=this},AlgoliaSearchHelper.prototype={init:function(a,b,c){this.client=a,this.index=b,this.options=c,this.page=0,this.refinements={},this.disjunctiveRefinements={}},search:function(a,b,c){this.q=a,this.searchCallback=b,this.searchParams=c||{},this.page=this.page||0,this.refinements=this.refinements||{},this.disjunctiveRefinements=this.disjunctiveRefinements||{},this._search()},toggleRefine:function(a,b){for(var c=0;c<this.options.facets.length;++c)if(this.options.facets[c]==a){var d=a+":"+b;return this.refinements[d]=!this.refinements[d],this.page=0,this._search(),!0}this.disjunctiveRefinements[a]=this.disjunctiveRefinements[a]||{};for(var e=0;e<this.options.disjunctiveFacets.length;++e)if(this.options.disjunctiveFacets[e]==a)return this.disjunctiveRefinements[a][b]=!this.disjunctiveRefinements[a][b],this.page=0,this._search(),!0;return!1},isRefined:function(a,b){var c=a+":"+b;return this.refinements[c]?!0:this.disjunctiveRefinements[a]&&this.disjunctiveRefinements[a][b]?!0:!1},nextPage:function(){this._gotoPage(this.page+1)},previousPage:function(){this.page>0&&this._gotoPage(this.page-1)},_gotoPage:function(a){this.page=a,this._search()},_search:function(){this.client.startQueriesBatch(),this.client.addQueryInBatch(this.index,this.q,this._getHitsSearchParams());for(var b=0;b<this.options.disjunctiveFacets.length;++b)this.client.addQueryInBatch(this.index,this.q,this._getDisjunctiveFacetSearchParams(this.options.disjunctiveFacets[b]));this.client.sendQueriesBatch(function(b,c){if(!b)return a.searchCallback(!1,c),void 0;var d=c.results[0];d.disjunctiveFacets={};for(var e=1;e<c.results.length;++e)for(var f in c.results[e].facets)if(d.disjunctiveFacets[f]=c.results[e].facets[f],a.disjunctiveRefinements[f])for(var g in a.disjunctiveRefinements[f])!d.disjunctiveFacets[f][g]&&a.disjunctiveRefinements[f][g]&&(d.disjunctiveFacets[f][g]=0);a.searchCallback(!0,d)})},_getHitsSearchParams:function(){return b({},this.searchParams,{hitsPerPage:this.options.hitsPerPage,page:this.page,facets:this.options.facets,facetFilters:this._getFacetFilters()})},_getDisjunctiveFacetSearchParams:function(a){return b({},this.searchParams,{hitsPerPage:1,page:0,facets:a,facetFilters:this._getFacetFilters(a)})},_getFacetFilters:function(a){var b=[];for(var c in this.refinements)this.refinements[c]&&b.push(c);for(var d in this.disjunctiveRefinements)if(d!=a){var e=[];for(var f in this.disjunctiveRefinements[d])this.disjunctiveRefinements[d][f]&&e.push(d+":"+f);e.length>0&&b.push(e)}return b}}}();
7
+ function AlgoliaExplainResults(a,b,c){function d(a,b){var c=[];if("object"==typeof a&&"matchedWords"in a&&"value"in a){for(var e=!1,f=0;f<a.matchedWords.length;++f){var g=a.matchedWords[f];g in b||(b[g]=1,e=!0)}e&&c.push(a.value)}else if("[object Array]"===Object.prototype.toString.call(a))for(var h=0;h<a.length;++h){var i=d(a[h],b);c=c.concat(i)}else if("object"==typeof a)for(var j in a)a.hasOwnProperty(j)&&(c=c.concat(d(a[j],b)));return c}function e(a,b,c){var f=a._highlightResult||a;if(-1===c.indexOf("."))return c in f?d(f[c],b):[];for(var g=c.split("."),h=f,i=0;i<g.length;++i){if("[object Array]"===Object.prototype.toString.call(h)){for(var j=[],k=0;k<h.length;++k)j=j.concat(e(h[k],b,g.slice(i).join(".")));return j}if(!(g[i]in h))return[];h=h[g[i]]}return d(h,b)}var f={},g={},h=e(a,g,b);if(f.title=h.length>0?h[0]:"",f.subtitles=[],"undefined"!=typeof c)for(var i=0;i<c.length;++i)for(var j=e(a,g,c[i]),k=0;k<j.length;++k)f.subtitles.push({attr:c[i],value:j[k]});return f}var ALGOLIA_VERSION="2.4.5",AlgoliaSearch=function(a,b,c,d,e){this.applicationID=a,this.apiKey=b,this._isUndefined(e)&&(e=[a+"-1.algolia.io",a+"-2.algolia.io",a+"-3.algolia.io"]),this.hosts=[];for(var f=0;f<e.length;++f)Math.random()>.5&&this.hosts.reverse(),this._isUndefined(c)||null==c?this.hosts.push(("https:"==document.location.protocol?"https":"http")+"://"+e[f]):"https"===c||"HTTPS"===c?this.hosts.push("https://"+e[f]):this.hosts.push("http://"+e[f]);Math.random()>.5&&this.hosts.reverse(),(this._isUndefined(d)||d)&&this._jsonRequest({method:"GET",url:"/1/isalive"}),this.extraHeaders=[]};AlgoliaSearch.prototype={deleteIndex:function(a,b){this._jsonRequest({method:"DELETE",url:"/1/indexes/"+encodeURIComponent(a),callback:b})},moveIndex:function(a,b,c){var d={operation:"move",destination:b};this._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(a)+"/operation",body:d,callback:c})},copyIndex:function(a,b,c){var d={operation:"copy",destination:b};this._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(a)+"/operation",body:d,callback:c})},getLogs:function(a,b,c){this._isUndefined(b)&&(b=0),this._isUndefined(c)&&(c=10),this._jsonRequest({method:"GET",url:"/1/logs?offset="+b+"&length="+c,callback:a})},listIndexes:function(a){this._jsonRequest({method:"GET",url:"/1/indexes/",callback:a})},initIndex:function(a){return new this.Index(this,a)},listUserKeys:function(a){this._jsonRequest({method:"GET",url:"/1/keys",callback:a})},getUserKeyACL:function(a,b){this._jsonRequest({method:"GET",url:"/1/keys/"+a,callback:b})},deleteUserKey:function(a,b){this._jsonRequest({method:"DELETE",url:"/1/keys/"+a,callback:b})},addUserKey:function(a,b){var c={};c.acl=a,this._jsonRequest({method:"POST",url:"/1/keys",body:c,callback:b})},addUserKeyWithValidity:function(a,b,c,d,e){var f=this,g={};g.acl=a,g.validity=b,g.maxQueriesPerIPPerHour=c,g.maxHitsPerQuery=d,this._jsonRequest({method:"POST",url:"/1/indexes/"+f.indexName+"/keys",body:g,callback:e})},setSecurityTags:function(a){if("[object Array]"===Object.prototype.toString.call(a)){for(var b=[],c=0;c<a.length;++c)if("[object Array]"===Object.prototype.toString.call(a[c])){for(var d=[],e=0;e<a[c].length;++e)d.push(a[c][e]);b.push("("+d.join(",")+")")}else b.push(a[c]);a=b.join(",")}this.setExtraHeader("X-Algolia-TagFilters",a)},setUserToken:function(a){this.setExtraHeader("X-Algolia-UserToken",a)},startQueriesBatch:function(){this.batch=[]},addQueryInBatch:function(a,b,c){var d="query="+encodeURIComponent(b);this._isUndefined(c)||null==c||(d=this._getSearchParams(c,d)),this.batch.push({indexName:a,params:d})},clearCache:function(){this.cache={}},sendQueriesBatch:function(a,b){for(var c=this,d={requests:[],apiKey:this.apiKey,appID:this.applicationID},e=0;e<c.batch.length;++e)d.requests.push(c.batch[e]);if(window.clearTimeout(c.onDelayTrigger),!this._isUndefined(b)&&null!=b&&b>0){var f=window.setTimeout(function(){c._sendQueriesBatch(d,a)},b);c.onDelayTrigger=f}else this._sendQueriesBatch(d,a)},Index:function(a,b){this.indexName=b,this.as=a,this.typeAheadArgs=null,this.typeAheadValueOption=null},setExtraHeader:function(a,b){this.extraHeaders.push({key:a,value:b})},_sendQueriesBatch:function(a,b){this._jsonRequest({cache:this.cache,method:"POST",url:"/1/indexes/*/queries",body:a,callback:b})},_jsonRequest:function(a){var b=this,c=a.callback,d=null,e=a.url;if(this._isUndefined(a.body)||(e=a.url+"_body_"+JSON.stringify(a.body)),!this._isUndefined(a.cache)&&(d=a.cache,!this._isUndefined(d[e])))return this._isUndefined(c)||c(!0,d[e]),void 0;var f=function(g){var h=0;return b._isUndefined(g)||(h=g),b.hosts.length<=h?(b._isUndefined(c)||c(!1,{message:"Cannot contact server"}),void 0):(a.callback=function(g,i,j,k){i||b._isUndefined(k)||console.log("Error: "+k.message),i&&!b._isUndefined(a.cache)&&(d[e]=k),!i&&g&&h+1<b.hosts.length?f(h+1):b._isUndefined(c)||c(i,k)},a.hostname=b.hosts[h],b._jsonRequestByHost(a),void 0)};f()},_jsonRequestByHost:function(a){var b=null,c=this;this._isUndefined(a.body)||(b=JSON.stringify(a.body));var d=a.hostname+a.url,e=null;if(e=new XMLHttpRequest,"withCredentials"in e){e.open(a.method,d,!0),e.setRequestHeader("X-Algolia-API-Key",this.apiKey),e.setRequestHeader("X-Algolia-Application-Id",this.applicationID);for(var f=0;f<this.extraHeaders.length;++f)e.setRequestHeader(this.extraHeaders[f].key,this.extraHeaders[f].value);null!=b&&e.setRequestHeader("Content-type","application/json")}else"undefined"!=typeof XDomainRequest?(e=new XDomainRequest,e.open(a.method,d)):console.log("your browser is too old to support CORS requests");e.send(b),e.onload=function(b){if(c._isUndefined(b)||null==b.target)a.callback(!1,!0,b,JSON.parse(e.responseText));else{var d=0===b.target.status||503===b.target.status,f=200===b.target.status||201===b.target.status;a.callback(d,f,b.target,null!=b.target.response?JSON.parse(b.target.response):null)}},e.onerror=function(){a.callback(!0,!1,null,{message:"Could not connect to Host"})}},_getSearchParams:function(a,b){if(this._isUndefined(a)||null==a)return b;for(var c in a)null!=c&&a.hasOwnProperty(c)&&(b+=0===b.length?"?":"&",b+=c+"="+encodeURIComponent("[object Array]"===Object.prototype.toString.call(a[c])?JSON.stringify(a[c]):a[c]));return b},_isUndefined:function(a){return void 0===a},applicationID:null,apiKey:null,hosts:[],cache:{},extraHeaders:[]},AlgoliaSearch.prototype.Index.prototype={clearCache:function(){this.cache={}},addObject:function(a,b,c){var d=this;this.as._isUndefined(c)?this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(d.indexName),body:a,callback:b}):this.as._jsonRequest({method:"PUT",url:"/1/indexes/"+encodeURIComponent(d.indexName)+"/"+encodeURIComponent(c),body:a,callback:b})},addObjects:function(a,b){for(var c=this,d={requests:[]},e=0;e<a.length;++e){var f={action:"addObject",body:a[e]};d.requests.push(f)}this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/batch",body:d,callback:b})},getObject:function(a,b,c){var d=this,e="";if(!this.as._isUndefined(c)){e="?attributes=";for(var f=0;f<c.length;++f)0!==f&&(e+=","),e+=c[f]}this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(d.indexName)+"/"+encodeURIComponent(a)+e,callback:b})},partialUpdateObject:function(a,b){var c=this;this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/"+encodeURIComponent(a.objectID)+"/partial",body:a,callback:b})},partialUpdateObjects:function(a,b){for(var c=this,d={requests:[]},e=0;e<a.length;++e){var f={action:"partialUpdateObject",objectID:a[e].objectID,body:a[e]};d.requests.push(f)}this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/batch",body:d,callback:b})},saveObject:function(a,b){var c=this;this.as._jsonRequest({method:"PUT",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/"+encodeURIComponent(a.objectID),body:a,callback:b})},saveObjects:function(a,b){for(var c=this,d={requests:[]},e=0;e<a.length;++e){var f={action:"updateObject",objectID:a[e].objectID,body:a[e]};d.requests.push(f)}this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/batch",body:d,callback:b})},deleteObject:function(a,b){if(null==a||0===a.length)return b(!1,{message:"empty objectID"}),void 0;var c=this;this.as._jsonRequest({method:"DELETE",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/"+encodeURIComponent(a),callback:b})},search:function(a,b,c,d){var e=this,f="query="+encodeURIComponent(a);if(this.as._isUndefined(c)||null==c||(f=this.as._getSearchParams(c,f)),window.clearTimeout(e.onDelayTrigger),!this.as._isUndefined(d)&&null!=d&&d>0){var g=window.setTimeout(function(){e._search(f,b)},d);e.onDelayTrigger=g}else this._search(f,b)},browse:function(a,b,c){var d=this,e="?page="+a;_.isUndefined(c)||(e+="&hitsPerPage="+c),this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(d.indexName)+"/browse"+e,callback:b})},ttAdapter:function(a){var b=this;return function(c,d){b.search(c,function(a,b){a&&d(b.hits)},a)}},waitTask:function(a,b){var c=this;this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/task/"+a,callback:function(d,e){d?"published"===e.status?b(!0,e):setTimeout(function(){c.waitTask(a,b)},100):b(!1,e)}})},clearIndex:function(a){var b=this;this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(b.indexName)+"/clear",callback:a})},getSettings:function(a){var b=this;this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(b.indexName)+"/settings",callback:a})},setSettings:function(a,b){var c=this;this.as._jsonRequest({method:"PUT",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/settings",body:a,callback:b})},listUserKeys:function(a){var b=this;this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(b.indexName)+"/keys",callback:a})},getUserKeyACL:function(a,b){var c=this;this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/keys/"+a,callback:b})},deleteUserKey:function(a,b){var c=this;this.as._jsonRequest({method:"DELETE",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/keys/"+a,callback:b})},addUserKey:function(a,b){var c=this,d={};d.acl=a,this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/keys",body:d,callback:b})},addUserKeyWithValidity:function(a,b,c,d,e){var f=this,g={};g.acl=a,g.validity=b,g.maxQueriesPerIPPerHour=c,g.maxHitsPerQuery=d,this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(f.indexName)+"/keys",body:g,callback:e})},_search:function(a,b){this.as._jsonRequest({cache:this.cache,method:"POST",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/query",body:{params:a,apiKey:this.as.apiKey,appID:this.as.applicationID},callback:b})},as:null,indexName:null,cache:{},typeAheadArgs:null,typeAheadValueOption:null,emptyConstructor:function(){}},function(){var a,b=function(a){a=a||{};for(var b=1;b<arguments.length;b++)if(arguments[b])for(var c in arguments[b])arguments[b].hasOwnProperty(c)&&(a[c]=arguments[b][c]);return a};window.AlgoliaSearchHelper=function(c,d,e){var f={facets:[],disjunctiveFacets:[],hitsPerPage:20};this.init(c,d,b({},f,e)),a=this},AlgoliaSearchHelper.prototype={init:function(a,b,c){this.client=a,this.index=b,this.options=c,this.page=0,this.refinements={},this.disjunctiveRefinements={}},search:function(a,b,c){this.q=a,this.searchCallback=b,this.searchParams=c||{},this.page=this.page||0,this.refinements=this.refinements||{},this.disjunctiveRefinements=this.disjunctiveRefinements||{},this._search()},toggleRefine:function(a,b){for(var c=0;c<this.options.facets.length;++c)if(this.options.facets[c]==a){var d=a+":"+b;return this.refinements[d]=!this.refinements[d],this.page=0,this._search(),!0}this.disjunctiveRefinements[a]=this.disjunctiveRefinements[a]||{};for(var e=0;e<this.options.disjunctiveFacets.length;++e)if(this.options.disjunctiveFacets[e]==a)return this.disjunctiveRefinements[a][b]=!this.disjunctiveRefinements[a][b],this.page=0,this._search(),!0;return!1},isRefined:function(a,b){var c=a+":"+b;return this.refinements[c]?!0:this.disjunctiveRefinements[a]&&this.disjunctiveRefinements[a][b]?!0:!1},nextPage:function(){this._gotoPage(this.page+1)},previousPage:function(){this.page>0&&this._gotoPage(this.page-1)},_gotoPage:function(a){this.page=a,this._search()},_search:function(){this.client.startQueriesBatch(),this.client.addQueryInBatch(this.index,this.q,this._getHitsSearchParams());for(var b=0;b<this.options.disjunctiveFacets.length;++b)this.client.addQueryInBatch(this.index,this.q,this._getDisjunctiveFacetSearchParams(this.options.disjunctiveFacets[b]));this.client.sendQueriesBatch(function(b,c){if(!b)return a.searchCallback(!1,c),void 0;var d=c.results[0];d.disjunctiveFacets={};for(var e=1;e<c.results.length;++e)for(var f in c.results[e].facets)if(d.disjunctiveFacets[f]=c.results[e].facets[f],a.disjunctiveRefinements[f])for(var g in a.disjunctiveRefinements[f])!d.disjunctiveFacets[f][g]&&a.disjunctiveRefinements[f][g]&&(d.disjunctiveFacets[f][g]=0);a.searchCallback(!0,d)})},_getHitsSearchParams:function(){return b({},this.searchParams,{hitsPerPage:this.options.hitsPerPage,page:this.page,facets:this.options.facets,facetFilters:this._getFacetFilters()})},_getDisjunctiveFacetSearchParams:function(a){return b({},this.searchParams,{hitsPerPage:1,page:0,facets:a,facetFilters:this._getFacetFilters(a)})},_getFacetFilters:function(a){var b=[];for(var c in this.refinements)this.refinements[c]&&b.push(c);for(var d in this.disjunctiveRefinements)if(d!=a){var e=[];for(var f in this.disjunctiveRefinements[d])this.disjunctiveRefinements[d][f]&&e.push(d+":"+f);e.length>0&&b.push(e)}return b}}}();
@@ -1,7 +1,7 @@
1
1
  /*!
2
- * typeahead.js 0.10.1
2
+ * typeahead.js 0.10.2
3
3
  * https://github.com/twitter/typeahead.js
4
- * Copyright 2013 Twitter, Inc. and other contributors; Licensed MIT
4
+ * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT
5
5
  */
6
6
 
7
7
  (function($) {
@@ -120,8 +120,31 @@
120
120
  },
121
121
  noop: function() {}
122
122
  };
123
- var VERSION = "0.10.1";
124
- var LruCache = function(root, undefined) {
123
+ var VERSION = "0.10.2";
124
+ var tokenizers = function(root) {
125
+ return {
126
+ nonword: nonword,
127
+ whitespace: whitespace,
128
+ obj: {
129
+ nonword: getObjTokenizer(nonword),
130
+ whitespace: getObjTokenizer(whitespace)
131
+ }
132
+ };
133
+ function whitespace(s) {
134
+ return s.split(/\s+/);
135
+ }
136
+ function nonword(s) {
137
+ return s.split(/\W+/);
138
+ }
139
+ function getObjTokenizer(tokenizer) {
140
+ return function setKey(key) {
141
+ return function tokenize(o) {
142
+ return tokenizer(o[key]);
143
+ };
144
+ };
145
+ }
146
+ }();
147
+ var LruCache = function() {
125
148
  function LruCache(maxSize) {
126
149
  this.maxSize = maxSize || 100;
127
150
  this.size = 0;
@@ -180,7 +203,7 @@
180
203
  this.prev = this.next = null;
181
204
  }
182
205
  return LruCache;
183
- }(this);
206
+ }();
184
207
  var PersistentStorage = function() {
185
208
  var ls, methods;
186
209
  try {
@@ -277,17 +300,20 @@
277
300
  _get: function(url, o, cb) {
278
301
  var that = this, jqXhr;
279
302
  if (jqXhr = pendingRequests[url]) {
280
- jqXhr.done(done);
303
+ jqXhr.done(done).fail(fail);
281
304
  } else if (pendingRequestsCount < maxPendingRequests) {
282
305
  pendingRequestsCount++;
283
- pendingRequests[url] = this._send(url, o).done(done).always(always);
306
+ pendingRequests[url] = this._send(url, o).done(done).fail(fail).always(always);
284
307
  } else {
285
308
  this.onDeckRequestArgs = [].slice.call(arguments, 0);
286
309
  }
287
310
  function done(resp) {
288
- cb && cb(resp);
311
+ cb && cb(null, resp);
289
312
  requestCache.set(url, resp);
290
313
  }
314
+ function fail() {
315
+ cb && cb(true);
316
+ }
291
317
  function always() {
292
318
  pendingRequestsCount--;
293
319
  delete pendingRequests[url];
@@ -298,14 +324,14 @@
298
324
  }
299
325
  },
300
326
  get: function(url, o, cb) {
301
- var that = this, resp;
327
+ var resp;
302
328
  if (_.isFunction(o)) {
303
329
  cb = o;
304
330
  o = {};
305
331
  }
306
332
  if (resp = requestCache.get(url)) {
307
333
  _.defer(function() {
308
- cb && cb(resp);
334
+ cb && cb(null, resp);
309
335
  });
310
336
  } else {
311
337
  this._get(url, o, cb);
@@ -340,8 +366,7 @@
340
366
  }
341
367
  this.datumTokenizer = o.datumTokenizer;
342
368
  this.queryTokenizer = o.queryTokenizer;
343
- this.datums = [];
344
- this.trie = newNode();
369
+ this.reset();
345
370
  }
346
371
  _.mixin(SearchIndex.prototype, {
347
372
  bootstrap: function bootstrap(o) {
@@ -356,7 +381,7 @@
356
381
  id = that.datums.push(datum) - 1;
357
382
  tokens = normalizeTokens(that.datumTokenizer(datum));
358
383
  _.each(tokens, function(token) {
359
- var node, chars, ch, ids;
384
+ var node, chars, ch;
360
385
  node = that.trie;
361
386
  chars = token.split("");
362
387
  while (ch = chars.shift()) {
@@ -391,6 +416,10 @@
391
416
  return that.datums[id];
392
417
  }) : [];
393
418
  },
419
+ reset: function reset() {
420
+ this.datums = [];
421
+ this.trie = newNode();
422
+ },
394
423
  serialize: function serialize() {
395
424
  return {
396
425
  datums: this.datums,
@@ -452,11 +481,7 @@
452
481
  remote: getRemote
453
482
  };
454
483
  function getLocal(o) {
455
- var local = o.local || null;
456
- if (_.isFunction(local)) {
457
- local = local.call(null);
458
- }
459
- return local;
484
+ return o.local || null;
460
485
  }
461
486
  function getPrefetch(o) {
462
487
  var prefetch, defaults;
@@ -516,13 +541,15 @@
516
541
  }
517
542
  }
518
543
  }();
519
- var Bloodhound = window.Bloodhound = function() {
520
- var keys;
544
+ (function(root) {
545
+ var old, keys;
546
+ old = root.Bloodhound;
521
547
  keys = {
522
548
  data: "data",
523
549
  protocol: "protocol",
524
550
  thumbprint: "thumbprint"
525
551
  };
552
+ root.Bloodhound = Bloodhound;
526
553
  function Bloodhound(o) {
527
554
  if (!o || !o.local && !o.prefetch && !o.remote) {
528
555
  $.error("one of local, prefetch, or remote is required");
@@ -540,14 +567,11 @@
540
567
  });
541
568
  this.storage = this.cacheKey ? new PersistentStorage(this.cacheKey) : null;
542
569
  }
543
- Bloodhound.tokenizers = {
544
- whitespace: function whitespaceTokenizer(s) {
545
- return s.split(/\s+/);
546
- },
547
- nonword: function nonwordTokenizer(s) {
548
- return s.split(/\W+/);
549
- }
570
+ Bloodhound.noConflict = function noConflict() {
571
+ root.Bloodhound = old;
572
+ return Bloodhound;
550
573
  };
574
+ Bloodhound.tokenizers = tokenizers;
551
575
  _.mixin(Bloodhound.prototype, {
552
576
  _loadPrefetch: function loadPrefetch(o) {
553
577
  var that = this, serialized, deferred;
@@ -559,9 +583,8 @@
559
583
  }
560
584
  return deferred;
561
585
  function handlePrefetchResponse(resp) {
562
- var filtered;
563
- filtered = o.filter ? o.filter(resp) : resp;
564
- that.add(filtered);
586
+ that.clear();
587
+ that.add(o.filter ? o.filter(resp) : resp);
565
588
  that._saveToStorage(that.index.serialize(), o.thumbprint, o.ttl);
566
589
  }
567
590
  },
@@ -571,9 +594,8 @@
571
594
  uriEncodedQuery = encodeURIComponent(query);
572
595
  url = this.remote.replace ? this.remote.replace(this.remote.url, query) : this.remote.url.replace(this.remote.wildcard, uriEncodedQuery);
573
596
  return this.transport.get(url, this.remote.ajax, handleRemoteResponse);
574
- function handleRemoteResponse(resp) {
575
- var filtered = that.remote.filter ? that.remote.filter(resp) : resp;
576
- cb(filtered);
597
+ function handleRemoteResponse(err, resp) {
598
+ err ? cb([]) : cb(that.remote.filter ? that.remote.filter(resp) : resp);
577
599
  }
578
600
  },
579
601
  _saveToStorage: function saveToStorage(data, thumbprint, ttl) {
@@ -593,30 +615,32 @@
593
615
  isExpired = stored.thumbprint !== thumbprint || stored.protocol !== location.protocol;
594
616
  return stored.data && !isExpired ? stored.data : null;
595
617
  },
596
- initialize: function initialize() {
597
- var that = this, deferred;
618
+ _initialize: function initialize() {
619
+ var that = this, local = this.local, deferred;
598
620
  deferred = this.prefetch ? this._loadPrefetch(this.prefetch) : $.Deferred().resolve();
599
- this.local && deferred.done(addLocalToIndex);
621
+ local && deferred.done(addLocalToIndex);
600
622
  this.transport = this.remote ? new Transport(this.remote) : null;
601
- this.initialize = function initialize() {
602
- return deferred.promise();
603
- };
604
- return deferred.promise();
623
+ return this.initPromise = deferred.promise();
605
624
  function addLocalToIndex() {
606
- that.add(that.local);
625
+ that.add(_.isFunction(local) ? local() : local);
607
626
  }
608
627
  },
628
+ initialize: function initialize(force) {
629
+ return !this.initPromise || force ? this._initialize() : this.initPromise;
630
+ },
609
631
  add: function add(data) {
610
632
  this.index.add(data);
611
633
  },
612
634
  get: function get(query, cb) {
613
- var that = this, matches, cacheHit = false;
635
+ var that = this, matches = [], cacheHit = false;
614
636
  matches = this.index.get(query);
615
637
  matches = this.sorter(matches).slice(0, this.limit);
616
638
  if (matches.length < this.limit && this.transport) {
617
639
  cacheHit = this._getFromRemote(query, returnRemoteMatches);
618
640
  }
619
- !cacheHit && cb && cb(matches);
641
+ if (!cacheHit) {
642
+ (matches.length > 0 || !this.transport) && cb && cb(matches);
643
+ }
620
644
  function returnRemoteMatches(remoteMatches) {
621
645
  var matchesWithBackfill = matches.slice(0);
622
646
  _.each(remoteMatches, function(remoteMatch) {
@@ -630,6 +654,15 @@
630
654
  cb && cb(that.sorter(matchesWithBackfill));
631
655
  }
632
656
  },
657
+ clear: function clear() {
658
+ this.index.reset();
659
+ },
660
+ clearPrefetchCache: function clearPrefetchCache() {
661
+ this.storage && this.storage.clear();
662
+ },
663
+ clearRemoteCache: function clearRemoteCache() {
664
+ this.transport && Transport.resetCache();
665
+ },
633
666
  ttAdapter: function ttAdapter() {
634
667
  return _.bind(this.get, this);
635
668
  }
@@ -647,5 +680,5 @@
647
680
  function ignoreDuplicates() {
648
681
  return false;
649
682
  }
650
- }();
683
+ })(this);
651
684
  })(window.jQuery);
@@ -1,7 +1,7 @@
1
1
  /*!
2
- * typeahead.js 0.10.1
2
+ * typeahead.js 0.10.2
3
3
  * https://github.com/twitter/typeahead.js
4
- * Copyright 2013 Twitter, Inc. and other contributors; Licensed MIT
4
+ * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT
5
5
  */
6
6
 
7
7
  (function($) {
@@ -120,8 +120,31 @@
120
120
  },
121
121
  noop: function() {}
122
122
  };
123
- var VERSION = "0.10.1";
124
- var LruCache = function(root, undefined) {
123
+ var VERSION = "0.10.2";
124
+ var tokenizers = function(root) {
125
+ return {
126
+ nonword: nonword,
127
+ whitespace: whitespace,
128
+ obj: {
129
+ nonword: getObjTokenizer(nonword),
130
+ whitespace: getObjTokenizer(whitespace)
131
+ }
132
+ };
133
+ function whitespace(s) {
134
+ return s.split(/\s+/);
135
+ }
136
+ function nonword(s) {
137
+ return s.split(/\W+/);
138
+ }
139
+ function getObjTokenizer(tokenizer) {
140
+ return function setKey(key) {
141
+ return function tokenize(o) {
142
+ return tokenizer(o[key]);
143
+ };
144
+ };
145
+ }
146
+ }();
147
+ var LruCache = function() {
125
148
  function LruCache(maxSize) {
126
149
  this.maxSize = maxSize || 100;
127
150
  this.size = 0;
@@ -180,7 +203,7 @@
180
203
  this.prev = this.next = null;
181
204
  }
182
205
  return LruCache;
183
- }(this);
206
+ }();
184
207
  var PersistentStorage = function() {
185
208
  var ls, methods;
186
209
  try {
@@ -277,17 +300,20 @@
277
300
  _get: function(url, o, cb) {
278
301
  var that = this, jqXhr;
279
302
  if (jqXhr = pendingRequests[url]) {
280
- jqXhr.done(done);
303
+ jqXhr.done(done).fail(fail);
281
304
  } else if (pendingRequestsCount < maxPendingRequests) {
282
305
  pendingRequestsCount++;
283
- pendingRequests[url] = this._send(url, o).done(done).always(always);
306
+ pendingRequests[url] = this._send(url, o).done(done).fail(fail).always(always);
284
307
  } else {
285
308
  this.onDeckRequestArgs = [].slice.call(arguments, 0);
286
309
  }
287
310
  function done(resp) {
288
- cb && cb(resp);
311
+ cb && cb(null, resp);
289
312
  requestCache.set(url, resp);
290
313
  }
314
+ function fail() {
315
+ cb && cb(true);
316
+ }
291
317
  function always() {
292
318
  pendingRequestsCount--;
293
319
  delete pendingRequests[url];
@@ -298,14 +324,14 @@
298
324
  }
299
325
  },
300
326
  get: function(url, o, cb) {
301
- var that = this, resp;
327
+ var resp;
302
328
  if (_.isFunction(o)) {
303
329
  cb = o;
304
330
  o = {};
305
331
  }
306
332
  if (resp = requestCache.get(url)) {
307
333
  _.defer(function() {
308
- cb && cb(resp);
334
+ cb && cb(null, resp);
309
335
  });
310
336
  } else {
311
337
  this._get(url, o, cb);
@@ -340,8 +366,7 @@
340
366
  }
341
367
  this.datumTokenizer = o.datumTokenizer;
342
368
  this.queryTokenizer = o.queryTokenizer;
343
- this.datums = [];
344
- this.trie = newNode();
369
+ this.reset();
345
370
  }
346
371
  _.mixin(SearchIndex.prototype, {
347
372
  bootstrap: function bootstrap(o) {
@@ -356,7 +381,7 @@
356
381
  id = that.datums.push(datum) - 1;
357
382
  tokens = normalizeTokens(that.datumTokenizer(datum));
358
383
  _.each(tokens, function(token) {
359
- var node, chars, ch, ids;
384
+ var node, chars, ch;
360
385
  node = that.trie;
361
386
  chars = token.split("");
362
387
  while (ch = chars.shift()) {
@@ -391,6 +416,10 @@
391
416
  return that.datums[id];
392
417
  }) : [];
393
418
  },
419
+ reset: function reset() {
420
+ this.datums = [];
421
+ this.trie = newNode();
422
+ },
394
423
  serialize: function serialize() {
395
424
  return {
396
425
  datums: this.datums,
@@ -452,11 +481,7 @@
452
481
  remote: getRemote
453
482
  };
454
483
  function getLocal(o) {
455
- var local = o.local || null;
456
- if (_.isFunction(local)) {
457
- local = local.call(null);
458
- }
459
- return local;
484
+ return o.local || null;
460
485
  }
461
486
  function getPrefetch(o) {
462
487
  var prefetch, defaults;
@@ -516,13 +541,15 @@
516
541
  }
517
542
  }
518
543
  }();
519
- var Bloodhound = window.Bloodhound = function() {
520
- var keys;
544
+ (function(root) {
545
+ var old, keys;
546
+ old = root.Bloodhound;
521
547
  keys = {
522
548
  data: "data",
523
549
  protocol: "protocol",
524
550
  thumbprint: "thumbprint"
525
551
  };
552
+ root.Bloodhound = Bloodhound;
526
553
  function Bloodhound(o) {
527
554
  if (!o || !o.local && !o.prefetch && !o.remote) {
528
555
  $.error("one of local, prefetch, or remote is required");
@@ -540,14 +567,11 @@
540
567
  });
541
568
  this.storage = this.cacheKey ? new PersistentStorage(this.cacheKey) : null;
542
569
  }
543
- Bloodhound.tokenizers = {
544
- whitespace: function whitespaceTokenizer(s) {
545
- return s.split(/\s+/);
546
- },
547
- nonword: function nonwordTokenizer(s) {
548
- return s.split(/\W+/);
549
- }
570
+ Bloodhound.noConflict = function noConflict() {
571
+ root.Bloodhound = old;
572
+ return Bloodhound;
550
573
  };
574
+ Bloodhound.tokenizers = tokenizers;
551
575
  _.mixin(Bloodhound.prototype, {
552
576
  _loadPrefetch: function loadPrefetch(o) {
553
577
  var that = this, serialized, deferred;
@@ -559,9 +583,8 @@
559
583
  }
560
584
  return deferred;
561
585
  function handlePrefetchResponse(resp) {
562
- var filtered;
563
- filtered = o.filter ? o.filter(resp) : resp;
564
- that.add(filtered);
586
+ that.clear();
587
+ that.add(o.filter ? o.filter(resp) : resp);
565
588
  that._saveToStorage(that.index.serialize(), o.thumbprint, o.ttl);
566
589
  }
567
590
  },
@@ -571,9 +594,8 @@
571
594
  uriEncodedQuery = encodeURIComponent(query);
572
595
  url = this.remote.replace ? this.remote.replace(this.remote.url, query) : this.remote.url.replace(this.remote.wildcard, uriEncodedQuery);
573
596
  return this.transport.get(url, this.remote.ajax, handleRemoteResponse);
574
- function handleRemoteResponse(resp) {
575
- var filtered = that.remote.filter ? that.remote.filter(resp) : resp;
576
- cb(filtered);
597
+ function handleRemoteResponse(err, resp) {
598
+ err ? cb([]) : cb(that.remote.filter ? that.remote.filter(resp) : resp);
577
599
  }
578
600
  },
579
601
  _saveToStorage: function saveToStorage(data, thumbprint, ttl) {
@@ -593,30 +615,32 @@
593
615
  isExpired = stored.thumbprint !== thumbprint || stored.protocol !== location.protocol;
594
616
  return stored.data && !isExpired ? stored.data : null;
595
617
  },
596
- initialize: function initialize() {
597
- var that = this, deferred;
618
+ _initialize: function initialize() {
619
+ var that = this, local = this.local, deferred;
598
620
  deferred = this.prefetch ? this._loadPrefetch(this.prefetch) : $.Deferred().resolve();
599
- this.local && deferred.done(addLocalToIndex);
621
+ local && deferred.done(addLocalToIndex);
600
622
  this.transport = this.remote ? new Transport(this.remote) : null;
601
- this.initialize = function initialize() {
602
- return deferred.promise();
603
- };
604
- return deferred.promise();
623
+ return this.initPromise = deferred.promise();
605
624
  function addLocalToIndex() {
606
- that.add(that.local);
625
+ that.add(_.isFunction(local) ? local() : local);
607
626
  }
608
627
  },
628
+ initialize: function initialize(force) {
629
+ return !this.initPromise || force ? this._initialize() : this.initPromise;
630
+ },
609
631
  add: function add(data) {
610
632
  this.index.add(data);
611
633
  },
612
634
  get: function get(query, cb) {
613
- var that = this, matches, cacheHit = false;
635
+ var that = this, matches = [], cacheHit = false;
614
636
  matches = this.index.get(query);
615
637
  matches = this.sorter(matches).slice(0, this.limit);
616
638
  if (matches.length < this.limit && this.transport) {
617
639
  cacheHit = this._getFromRemote(query, returnRemoteMatches);
618
640
  }
619
- !cacheHit && cb && cb(matches);
641
+ if (!cacheHit) {
642
+ (matches.length > 0 || !this.transport) && cb && cb(matches);
643
+ }
620
644
  function returnRemoteMatches(remoteMatches) {
621
645
  var matchesWithBackfill = matches.slice(0);
622
646
  _.each(remoteMatches, function(remoteMatch) {
@@ -630,6 +654,15 @@
630
654
  cb && cb(that.sorter(matchesWithBackfill));
631
655
  }
632
656
  },
657
+ clear: function clear() {
658
+ this.index.reset();
659
+ },
660
+ clearPrefetchCache: function clearPrefetchCache() {
661
+ this.storage && this.storage.clear();
662
+ },
663
+ clearRemoteCache: function clearRemoteCache() {
664
+ this.transport && Transport.resetCache();
665
+ },
633
666
  ttAdapter: function ttAdapter() {
634
667
  return _.bind(this.get, this);
635
668
  }
@@ -647,13 +680,13 @@
647
680
  function ignoreDuplicates() {
648
681
  return false;
649
682
  }
650
- }();
683
+ })(this);
651
684
  var html = {
652
685
  wrapper: '<span class="twitter-typeahead"></span>',
653
686
  dropdown: '<span class="tt-dropdown-menu"></span>',
654
687
  dataset: '<div class="tt-dataset-%CLASS%"></div>',
655
688
  suggestions: '<span class="tt-suggestions"></span>',
656
- suggestion: '<div class="tt-suggestion">%BODY%</div>'
689
+ suggestion: '<div class="tt-suggestion"></div>'
657
690
  };
658
691
  var css = {
659
692
  wrapper: {
@@ -771,7 +804,7 @@
771
804
  return this;
772
805
  }
773
806
  function trigger(types) {
774
- var that = this, type, callbacks, args, syncFlush, asyncFlush;
807
+ var type, callbacks, args, syncFlush, asyncFlush;
775
808
  if (!this._callbacks) {
776
809
  return this;
777
810
  }
@@ -795,7 +828,7 @@
795
828
  }
796
829
  }
797
830
  function getNextTick() {
798
- var nextTickFn, messageChannel;
831
+ var nextTickFn;
799
832
  if (window.setImmediate) {
800
833
  nextTickFn = function nextTickSetImmediate(fn) {
801
834
  setImmediate(function() {
@@ -892,7 +925,7 @@
892
925
  this.$hint = $(o.hint);
893
926
  this.$input = $(o.input).on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown);
894
927
  if (this.$hint.length === 0) {
895
- this.setHintValue = this.getHintValue = this.clearHint = _.noop;
928
+ this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop;
896
929
  }
897
930
  if (!_.isMsie()) {
898
931
  this.$input.on("input.tt", onInput);
@@ -911,11 +944,11 @@
911
944
  return (str || "").replace(/^\s*/g, "").replace(/\s{2,}/g, " ");
912
945
  };
913
946
  _.mixin(Input.prototype, EventEmitter, {
914
- _onBlur: function onBlur($e) {
947
+ _onBlur: function onBlur() {
915
948
  this.resetInputValue();
916
949
  this.trigger("blurred");
917
950
  },
918
- _onFocus: function onFocus($e) {
951
+ _onFocus: function onFocus() {
919
952
  this.trigger("focused");
920
953
  },
921
954
  _onKeydown: function onKeydown($e) {
@@ -925,14 +958,14 @@
925
958
  this.trigger(keyName + "Keyed", $e);
926
959
  }
927
960
  },
928
- _onInput: function onInput($e) {
961
+ _onInput: function onInput() {
929
962
  this._checkInputValue();
930
963
  },
931
964
  _managePreventDefault: function managePreventDefault(keyName, $e) {
932
965
  var preventDefault, hintValue, inputValue;
933
966
  switch (keyName) {
934
967
  case "tab":
935
- hintValue = this.getHintValue();
968
+ hintValue = this.getHint();
936
969
  inputValue = this.getInputValue();
937
970
  preventDefault = hintValue && hintValue !== inputValue && !withModifier($e);
938
971
  break;
@@ -987,19 +1020,27 @@
987
1020
  },
988
1021
  setInputValue: function setInputValue(value, silent) {
989
1022
  this.$input.val(value);
990
- !silent && this._checkInputValue();
1023
+ silent ? this.clearHint() : this._checkInputValue();
991
1024
  },
992
- getHintValue: function getHintValue() {
1025
+ resetInputValue: function resetInputValue() {
1026
+ this.setInputValue(this.query, true);
1027
+ },
1028
+ getHint: function getHint() {
993
1029
  return this.$hint.val();
994
1030
  },
995
- setHintValue: function setHintValue(value) {
1031
+ setHint: function setHint(value) {
996
1032
  this.$hint.val(value);
997
1033
  },
998
- resetInputValue: function resetInputValue() {
999
- this.$input.val(this.query);
1000
- },
1001
1034
  clearHint: function clearHint() {
1002
- this.$hint.val("");
1035
+ this.setHint("");
1036
+ },
1037
+ clearHintIfInvalid: function clearHintIfInvalid() {
1038
+ var val, hint, valIsPrefixOfHint, isValid;
1039
+ val = this.getInputValue();
1040
+ hint = this.getHint();
1041
+ valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0;
1042
+ isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow();
1043
+ !isValid && this.clearHint();
1003
1044
  },
1004
1045
  getLanguageDirection: function getLanguageDirection() {
1005
1046
  return (this.$input.css("direction") || "ltr").toLowerCase();
@@ -1033,7 +1074,7 @@
1033
1074
  return $('<pre aria-hidden="true"></pre>').css({
1034
1075
  position: "absolute",
1035
1076
  visibility: "hidden",
1036
- whiteSpace: "nowrap",
1077
+ whiteSpace: "pre",
1037
1078
  fontFamily: $input.css("font-family"),
1038
1079
  fontSize: $input.css("font-size"),
1039
1080
  fontStyle: $input.css("font-style"),
@@ -1112,10 +1153,8 @@
1112
1153
  });
1113
1154
  return $suggestions;
1114
1155
  function getSuggestionNode(suggestion) {
1115
- var $el, innerHtml, outerHtml;
1116
- innerHtml = that.templates.suggestion(suggestion);
1117
- outerHtml = html.suggestion.replace("%BODY%", innerHtml);
1118
- $el = $(outerHtml).data(datasetKey, that.name).data(valueKey, that.displayFn(suggestion)).data(datumKey, suggestion);
1156
+ var $el;
1157
+ $el = $(html.suggestion).append(that.templates.suggestion(suggestion)).data(datasetKey, that.name).data(valueKey, that.displayFn(suggestion)).data(datumKey, suggestion);
1119
1158
  $el.children().each(function() {
1120
1159
  $(this).css(css.suggestionChild);
1121
1160
  });
@@ -1141,13 +1180,21 @@
1141
1180
  update: function update(query) {
1142
1181
  var that = this;
1143
1182
  this.query = query;
1144
- this.source(query, renderIfQueryIsSame);
1145
- function renderIfQueryIsSame(suggestions) {
1146
- query === that.query && that._render(query, suggestions);
1183
+ this.canceled = false;
1184
+ this.source(query, render);
1185
+ function render(suggestions) {
1186
+ if (!that.canceled && query === that.query) {
1187
+ that._render(query, suggestions);
1188
+ }
1147
1189
  }
1148
1190
  },
1191
+ cancel: function cancel() {
1192
+ this.canceled = true;
1193
+ },
1149
1194
  clear: function clear() {
1150
- this._render(this.query || "");
1195
+ this.cancel();
1196
+ this.$el.empty();
1197
+ this.trigger("rendered");
1151
1198
  },
1152
1199
  isEmpty: function isEmpty() {
1153
1200
  return this.$el.is(":empty");
@@ -1206,7 +1253,7 @@
1206
1253
  this._removeCursor();
1207
1254
  this._setCursor($($e.currentTarget), true);
1208
1255
  },
1209
- _onSuggestionMouseLeave: function onSuggestionMouseLeave($e) {
1256
+ _onSuggestionMouseLeave: function onSuggestionMouseLeave() {
1210
1257
  this._removeCursor();
1211
1258
  },
1212
1259
  _onRendered: function onRendered() {
@@ -1341,17 +1388,34 @@
1341
1388
  var Typeahead = function() {
1342
1389
  var attrsKey = "ttAttrs";
1343
1390
  function Typeahead(o) {
1344
- var $menu, $input, $hint, datasets;
1391
+ var $menu, $input, $hint;
1345
1392
  o = o || {};
1346
1393
  if (!o.input) {
1347
1394
  $.error("missing input");
1348
1395
  }
1396
+ this.isActivated = false;
1349
1397
  this.autoselect = !!o.autoselect;
1350
1398
  this.minLength = _.isNumber(o.minLength) ? o.minLength : 1;
1351
1399
  this.$node = buildDomStructure(o.input, o.withHint);
1352
1400
  $menu = this.$node.find(".tt-dropdown-menu");
1353
1401
  $input = this.$node.find(".tt-input");
1354
1402
  $hint = this.$node.find(".tt-hint");
1403
+ $input.on("blur.tt", function($e) {
1404
+ var active, isActive, hasActive;
1405
+ active = document.activeElement;
1406
+ isActive = $menu.is(active);
1407
+ hasActive = $menu.has(active).length > 0;
1408
+ if (_.isMsie() && (isActive || hasActive)) {
1409
+ $e.preventDefault();
1410
+ $e.stopImmediatePropagation();
1411
+ _.defer(function() {
1412
+ $input.focus();
1413
+ });
1414
+ }
1415
+ });
1416
+ $menu.on("mousedown.tt", function($e) {
1417
+ $e.preventDefault();
1418
+ });
1355
1419
  this.eventBus = o.eventBus || new EventBus({
1356
1420
  el: $input
1357
1421
  });
@@ -1363,15 +1427,7 @@
1363
1427
  input: $input,
1364
1428
  hint: $hint
1365
1429
  }).onSync("focused", this._onFocused, this).onSync("blurred", this._onBlurred, this).onSync("enterKeyed", this._onEnterKeyed, this).onSync("tabKeyed", this._onTabKeyed, this).onSync("escKeyed", this._onEscKeyed, this).onSync("upKeyed", this._onUpKeyed, this).onSync("downKeyed", this._onDownKeyed, this).onSync("leftKeyed", this._onLeftKeyed, this).onSync("rightKeyed", this._onRightKeyed, this).onSync("queryChanged", this._onQueryChanged, this).onSync("whitespaceChanged", this._onWhitespaceChanged, this);
1366
- $menu.on("mousedown.tt", function($e) {
1367
- if (_.isMsie() && _.isMsie() < 9) {
1368
- $input[0].onbeforedeactivate = function() {
1369
- window.event.returnValue = false;
1370
- $input[0].onbeforedeactivate = null;
1371
- };
1372
- }
1373
- $e.preventDefault();
1374
- });
1430
+ this._setLanguageDirection();
1375
1431
  }
1376
1432
  _.mixin(Typeahead.prototype, {
1377
1433
  _onSuggestionClicked: function onSuggestionClicked(type, $el) {
@@ -1382,7 +1438,6 @@
1382
1438
  },
1383
1439
  _onCursorMoved: function onCursorMoved() {
1384
1440
  var datum = this.dropdown.getDatumForCursor();
1385
- this.input.clearHint();
1386
1441
  this.input.setInputValue(datum.value, true);
1387
1442
  this.eventBus.trigger("cursorchanged", datum.raw, datum.datasetName);
1388
1443
  },
@@ -1402,10 +1457,12 @@
1402
1457
  this.eventBus.trigger("closed");
1403
1458
  },
1404
1459
  _onFocused: function onFocused() {
1405
- this.dropdown.empty();
1460
+ this.isActivated = true;
1406
1461
  this.dropdown.open();
1407
1462
  },
1408
1463
  _onBlurred: function onBlurred() {
1464
+ this.isActivated = false;
1465
+ this.dropdown.empty();
1409
1466
  this.dropdown.close();
1410
1467
  },
1411
1468
  _onEnterKeyed: function onEnterKeyed(type, $e) {
@@ -1426,7 +1483,7 @@
1426
1483
  this._select(datum);
1427
1484
  $e.preventDefault();
1428
1485
  } else {
1429
- this._autocomplete();
1486
+ this._autocomplete(true);
1430
1487
  }
1431
1488
  },
1432
1489
  _onEscKeyed: function onEscKeyed() {
@@ -1435,19 +1492,13 @@
1435
1492
  },
1436
1493
  _onUpKeyed: function onUpKeyed() {
1437
1494
  var query = this.input.getQuery();
1438
- if (!this.dropdown.isOpen && query.length >= this.minLength) {
1439
- this.dropdown.update(query);
1440
- }
1495
+ this.dropdown.isEmpty && query.length >= this.minLength ? this.dropdown.update(query) : this.dropdown.moveCursorUp();
1441
1496
  this.dropdown.open();
1442
- this.dropdown.moveCursorUp();
1443
1497
  },
1444
1498
  _onDownKeyed: function onDownKeyed() {
1445
1499
  var query = this.input.getQuery();
1446
- if (!this.dropdown.isOpen && query.length >= this.minLength) {
1447
- this.dropdown.update(query);
1448
- }
1500
+ this.dropdown.isEmpty && query.length >= this.minLength ? this.dropdown.update(query) : this.dropdown.moveCursorDown();
1449
1501
  this.dropdown.open();
1450
- this.dropdown.moveCursorDown();
1451
1502
  },
1452
1503
  _onLeftKeyed: function onLeftKeyed() {
1453
1504
  this.dir === "rtl" && this._autocomplete();
@@ -1456,9 +1507,8 @@
1456
1507
  this.dir === "ltr" && this._autocomplete();
1457
1508
  },
1458
1509
  _onQueryChanged: function onQueryChanged(e, query) {
1459
- this.input.clearHint();
1460
- this.dropdown.empty();
1461
- query.length >= this.minLength && this.dropdown.update(query);
1510
+ this.input.clearHintIfInvalid();
1511
+ query.length >= this.minLength ? this.dropdown.update(query) : this.dropdown.empty();
1462
1512
  this.dropdown.open();
1463
1513
  this._setLanguageDirection();
1464
1514
  },
@@ -1475,29 +1525,31 @@
1475
1525
  }
1476
1526
  },
1477
1527
  _updateHint: function updateHint() {
1478
- var datum, inputValue, query, escapedQuery, frontMatchRegEx, match;
1528
+ var datum, val, query, escapedQuery, frontMatchRegEx, match;
1479
1529
  datum = this.dropdown.getDatumForTopSuggestion();
1480
1530
  if (datum && this.dropdown.isVisible() && !this.input.hasOverflow()) {
1481
- inputValue = this.input.getInputValue();
1482
- query = Input.normalizeQuery(inputValue);
1531
+ val = this.input.getInputValue();
1532
+ query = Input.normalizeQuery(val);
1483
1533
  escapedQuery = _.escapeRegExChars(query);
1484
- frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.*$)", "i");
1534
+ frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i");
1485
1535
  match = frontMatchRegEx.exec(datum.value);
1486
- this.input.setHintValue(inputValue + (match ? match[1] : ""));
1536
+ match ? this.input.setHint(val + match[1]) : this.input.clearHint();
1537
+ } else {
1538
+ this.input.clearHint();
1487
1539
  }
1488
1540
  },
1489
- _autocomplete: function autocomplete() {
1490
- var hint, query, datum;
1491
- hint = this.input.getHintValue();
1541
+ _autocomplete: function autocomplete(laxCursor) {
1542
+ var hint, query, isCursorAtEnd, datum;
1543
+ hint = this.input.getHint();
1492
1544
  query = this.input.getQuery();
1493
- if (hint && query !== hint && this.input.isCursorAtEnd()) {
1545
+ isCursorAtEnd = laxCursor || this.input.isCursorAtEnd();
1546
+ if (hint && query !== hint && isCursorAtEnd) {
1494
1547
  datum = this.dropdown.getDatumForTopSuggestion();
1495
1548
  datum && this.input.setInputValue(datum.value);
1496
1549
  this.eventBus.trigger("autocompleted", datum.raw, datum.datasetName);
1497
1550
  }
1498
1551
  },
1499
1552
  _select: function select(datum) {
1500
- this.input.clearHint();
1501
1553
  this.input.setQuery(datum.value);
1502
1554
  this.input.setInputValue(datum.value, true);
1503
1555
  this._setLanguageDirection();
@@ -1511,11 +1563,17 @@
1511
1563
  close: function close() {
1512
1564
  this.dropdown.close();
1513
1565
  },
1514
- getQuery: function getQuery() {
1515
- return this.input.getQuery();
1566
+ setVal: function setVal(val) {
1567
+ if (this.isActivated) {
1568
+ this.input.setInputValue(val);
1569
+ } else {
1570
+ this.input.setQuery(val);
1571
+ this.input.setInputValue(val, true);
1572
+ }
1573
+ this._setLanguageDirection();
1516
1574
  },
1517
- setQuery: function setQuery(val) {
1518
- this.input.setInputValue(val);
1575
+ getVal: function getVal() {
1576
+ return this.input.getQuery();
1519
1577
  },
1520
1578
  destroy: function destroy() {
1521
1579
  this.input.destroy();
@@ -1617,17 +1675,17 @@
1617
1675
  }
1618
1676
  },
1619
1677
  val: function val(newVal) {
1620
- return !arguments.length ? getQuery(this.first()) : this.each(setQuery);
1621
- function setQuery() {
1678
+ return !arguments.length ? getVal(this.first()) : this.each(setVal);
1679
+ function setVal() {
1622
1680
  var $input = $(this), typeahead;
1623
1681
  if (typeahead = $input.data(typeaheadKey)) {
1624
- typeahead.setQuery(newVal);
1682
+ typeahead.setVal(newVal);
1625
1683
  }
1626
1684
  }
1627
- function getQuery($input) {
1685
+ function getVal($input) {
1628
1686
  var typeahead, query;
1629
1687
  if (typeahead = $input.data(typeaheadKey)) {
1630
- query = typeahead.getQuery();
1688
+ query = typeahead.getVal();
1631
1689
  }
1632
1690
  return query;
1633
1691
  }