algoliasearch-rails 1.7.2 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9862c9e947777afdb827b6ca9c9879e989573ce3
4
- data.tar.gz: bbfcbd83a12f2ecaf38ed346a80167b214303d30
3
+ metadata.gz: 7e6a128b2bfe1e2ecf10c3e27ec35860245fd518
4
+ data.tar.gz: a90c923b44b691e3731b35727d3fbd511fb0c2c5
5
5
  SHA512:
6
- metadata.gz: ce9950505a350f386a8a778213a2352c98a0fac56fbdef99b0ba899270ed939b4bc92202e72ce16ad214fb7cad2ecbd9b816a7b811c3136ce12b263a9373a116
7
- data.tar.gz: c1d747eedcec3a4fb90d9394e6f1476c72f22142e1dd2551c6661219c5a765f0472cf2c7ee583a115f0da47061eabfca4865ceb1b4be2ac5d4f9533025dfaf24
6
+ metadata.gz: 9d47ac7a0de3b463a2edaa9b1870be20eada52bcff7f49cf31012e75f0ac6f85b10bf0d4a43deb07114955fe57227eae96b35591b2d813bf6d7c4395004da48b
7
+ data.tar.gz: 04d6de5aab6d594f5537c5f455f383600c0963af19039f592a2f1ccfe72803d3f393f91206c64a5e1bb7b4bffeb4640ebcdd6589be3938e9f8acc339d3a5fef5
data/ChangeLog CHANGED
@@ -1,14 +1,20 @@
1
1
  CHANGELOG
2
2
 
3
+ 2014-02-12 1.8.0
4
+
5
+ * Upgrade to official Twitter Typeahead.js 0.10.1 (/!\ API has changed)
6
+ * Fixed reindexing rake task (#12)
7
+
3
8
  2014-02-04 1.7.2
4
9
 
5
10
  * Add a ```raw_search``` method, retrieving the JSON raw answer
11
+ * Support STI subclasses (#11)
6
12
  * Updated dependencies
7
13
 
8
14
  2014-01-31 1.7.1
9
15
 
10
16
  * Ensure methods are not conflicting with already defined ones (use algolia_METHOD_NAME)
11
- * Add ```:if``` and ```:unless``` options to control objects indexing
17
+ * Add ```:if``` and ```:unless``` options to control objects indexing (#10)
12
18
 
13
19
  2014-01-07 1.7.0
14
20
 
data/README.md CHANGED
@@ -399,11 +399,11 @@ p Contact.search("jon doe", hitsPerPage: 5, page: 2)
399
399
  Typeahead UI
400
400
  -------------
401
401
 
402
- Require ```algolia/algoliasearch.min``` (see [algoliasearch-client-js](https://github.com/algolia/algoliasearch-client-js)) and ```algolia/typeahead.js``` (a modified version of typeahead.js with custom transports, see the [pull request](https://github.com/twitter/typeahead.js/pull/473)) somewhere in your JavaScript manifest, for example in ```application.js``` if you are using Rails 3.1+:
402
+ Require ```algolia/algoliasearch.min``` (see [algoliasearch-client-js](https://github.com/algolia/algoliasearch-client-js)) and ```algolia/typeahead.jquery.js``` somewhere in your JavaScript manifest, for example in ```application.js``` if you are using Rails 3.1+:
403
403
 
404
404
  ```javascript
405
405
  //= require algolia/algoliasearch.min
406
- //= require algolia/typeahead.min
406
+ //= require algolia/typeahead.jquery
407
407
  ```
408
408
 
409
409
  We recommend the usage of [hogan](http://twitter.github.io/hogan.js/), a JavaScript templating engine from Twitter.
@@ -416,15 +416,19 @@ Turns any ```input[type="text"]``` element into a typeahead, for example:
416
416
 
417
417
  ```javascript
418
418
  <input name="email" placeholder="test@example.org" id="user_email" />
419
+
419
420
  <script type="text/javascript">
420
421
  $(document).ready(function() {
421
422
  var client = new AlgoliaSearch('YourApplicationID', 'SearchOnlyApplicationKey');
422
- $('input#user_email').typeahead({
423
- name: 'emails',
424
- remote: client.initIndex('<%= Contact.index_name %>').getTypeaheadTransport(),
425
- engine: Hogan,
426
- template: '{{{_highlightResult.email.value}}} ({{{_highlightResult.first_name.value}}} {{{_highlightResult.last_name.value}}})',
427
- valueKey: 'email'
423
+ var template = Hogan.compile('{{{_highlightResult.email.value}}} ({{{_highlightResult.first_name.value}}} {{{_highlightResult.last_name.value}}})');
424
+ $('input#user_email').typeahead(null, {
425
+ source: client.initIndex('<%= Contact.index_name %>').ttAdapter(),
426
+ displayKey: 'email',
427
+ templates: {
428
+ suggestion: function(hit) {
429
+ return template.render(hit);
430
+ }
431
+ }
428
432
  });
429
433
  });
430
434
  </script>
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.7.2
1
+ 1.8.0
@@ -6,11 +6,11 @@
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "algoliasearch-rails"
9
- s.version = "1.7.2"
9
+ s.version = "1.8.0"
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-01-07"
13
+ s.date = "2014-02-12"
14
14
  s.description = "AlgoliaSearch integration to your favorite ORM"
15
15
  s.email = "contact@algolia.com"
16
16
  s.extra_rdoc_files = [
@@ -42,8 +42,10 @@ Gem::Specification.new do |s|
42
42
  "spec/utilities_spec.rb",
43
43
  "vendor/assets/javascripts/algolia/algoliasearch.js",
44
44
  "vendor/assets/javascripts/algolia/algoliasearch.min.js",
45
- "vendor/assets/javascripts/algolia/typeahead.js",
46
- "vendor/assets/javascripts/algolia/typeahead.min.js"
45
+ "vendor/assets/javascripts/algolia/typeahead.jquery.js",
46
+ "vendor/assets/javascripts/algolia/typeahead.bundle.js",
47
+ "vendor/assets/javascripts/algolia/typeahead.bundle.min.js",
48
+ "vendor/assets/javascripts/algolia/bloodhound.js"
47
49
  ]
48
50
  s.homepage = "http://github.com/algolia/algoliasearch-rails"
49
51
  s.licenses = ["MIT"]
@@ -56,7 +58,7 @@ Gem::Specification.new do |s|
56
58
 
57
59
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
58
60
  s.add_runtime_dependency(%q<json>, [">= 1.5.1"])
59
- s.add_runtime_dependency(%q<algoliasearch>, [">= 1.1.7"])
61
+ s.add_runtime_dependency(%q<algoliasearch>, [">= 1.2.0"])
60
62
  s.add_development_dependency(%q<will_paginate>, [">= 2.3.15"])
61
63
  s.add_development_dependency(%q<kaminari>, [">= 0"])
62
64
  s.add_development_dependency "travis"
@@ -64,11 +66,11 @@ Gem::Specification.new do |s|
64
66
  s.add_development_dependency "rdoc"
65
67
  else
66
68
  s.add_dependency(%q<json>, [">= 1.5.1"])
67
- s.add_dependency(%q<algoliasearch>, [">= 1.1.7"])
69
+ s.add_dependency(%q<algoliasearch>, [">= 1.2.0"])
68
70
  end
69
71
  else
70
72
  s.add_dependency(%q<json>, [">= 1.5.1"])
71
- s.add_dependency(%q<algoliasearch>, [">= 1.1.7"])
73
+ s.add_dependency(%q<algoliasearch>, [">= 1.2.0"])
72
74
  end
73
75
  end
74
76
 
@@ -3,27 +3,13 @@ namespace :algoliasearch do
3
3
  desc "Reindex all models"
4
4
  task :reindex => :environment do
5
5
  puts "reindexing all models"
6
- load_models
7
6
  AlgoliaSearch::Utilities.reindex_all_models
8
7
  end
9
8
 
10
9
  desc "Clear all indexes"
11
10
  task :clear_indexes => :environment do
12
11
  puts "clearing all indexes"
13
- load_models
14
12
  AlgoliaSearch::Utilities.clear_all_indexes
15
13
  end
16
-
17
- def load_models
18
- app_root = Rails.root
19
- dirs = ["#{app_root}/app/models/"] + Dir.glob("#{app_root}/vendor/plugins/*/app/models/")
20
-
21
- dirs.each do |base|
22
- Dir["#{base}**/*.rb"].each do |file|
23
- model_name = file.gsub(/^#{base}([\w_\/\\]+)\.rb/, '\1')
24
- next if model_name.nil?
25
- model_name.camelize.constantize
26
- end
27
- end
28
- end
14
+
29
15
  end
@@ -21,7 +21,7 @@
21
21
  * THE SOFTWARE.
22
22
  */
23
23
 
24
- var ALGOLIA_VERSION = '2.3.8';
24
+ var ALGOLIA_VERSION = '2.4.0';
25
25
 
26
26
  /*
27
27
  * Copyright (c) 2013 Algolia
@@ -820,64 +820,20 @@ AlgoliaSearch.prototype.Index.prototype = {
820
820
  },
821
821
 
822
822
  /*
823
- * Get transport layer for Typeahead.js
824
- * @param args (optional) if set, contains an object with query parameters (see search for details)
825
- * @param propertyName(optional) if set, contains the name of property that will be used for
823
+ * Get a Typeahead.js adapter
824
+ * @param searchParams contains an object with query parameters (see search for details)
826
825
  */
827
- getTypeaheadTransport: function(args, valueOption) {
828
- this.typeAheadArgs = args;
829
- if (typeof valueOption !== 'undefined') {
830
- this.typeAheadValueOption = valueOption;
831
- }
832
- return this;
833
- },
834
- /*
835
- * Update parameter of transport layer for Typeahead.js
836
- * @param args contains an object with query parameters (see search for details)
837
- * @param propertyName(optional) if set, contains the name of property that will be used for
838
- */
839
- setTypeaheadParams: function(args, valueOption) {
840
- this.typeAHeadArgs = args;
841
- if (typeof valueOption !== 'undefined') {
842
- this.typeAheadValueOption = valueOption;
843
- }
844
- },
845
- // Method used by Typeahead.js.
846
- get: function(query, processRemoteData, that, cb, suggestions) {
847
- self = this;
848
- this.search(query, function(success, content) {
849
- if (success) {
850
- for (var i = 0; i < content.hits.length; ++i) {
851
- // Add an attribute value with the first string
852
- var obj = content.hits[i],
853
- found = false;
854
-
855
- if (typeof obj.value === 'undefined') {
856
- if (self.typeAheadValueOption != null) {
857
- if (typeof self.typeAheadValueOption === 'function') {
858
- obj.value = self.typeAheadValueOption(obj);
859
- found = true;
860
- } else if (typeof obj[self.typeAheadValueOption] !== 'undefined') {
861
- obj.value = obj[self.typeAheadValueOption];
862
- found = true;
863
- }
864
- }
865
- if (! found) {
866
- for (var propertyName in obj) {
867
- if (!found && obj.hasOwnProperty(propertyName) && typeof obj[propertyName] === 'string') {
868
- obj.value = obj[propertyName];
869
- found = true;
870
- }
871
- }
872
- }
873
- }
874
- suggestions.push(that._transformDatum(obj));
826
+ ttAdapter: function(params) {
827
+ var self = this;
828
+ return function(query, cb) {
829
+ self.search(query, function(success, content) {
830
+ if (success) {
831
+ cb(content.hits);
875
832
  }
876
- cb && cb(suggestions);
877
- }
878
- }, self.typeAheadArgs);
879
- return true;
833
+ }, params);
834
+ };
880
835
  },
836
+
881
837
  /*
882
838
  * Wait the publication of a task on the server.
883
839
  * All server task are asynchronous and you can check with this method that the task is published.
@@ -1,7 +1,7 @@
1
1
  /*!
2
- * algoliasearch 2.3.8
2
+ * algoliasearch 2.4.0
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){if("object"==typeof a&&"matchedWords"in a&&"value"in a){for(var c=!1,e=0;e<a.matchedWords.length;++e){var f=a.matchedWords[e];f in b||(b[f]=1,c=!0)}return c?[a.value]:[]}if(a instanceof Array){for(var g=[],h=0;h<a.length;++h){var i=d(a[h],b);g=g.concat(i)}return g}if("object"==typeof a){var g=[];for(prop in a)a.hasOwnProperty(prop)&&(g=g.concat(d(a[prop],b)));return g}return[]}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.3.8",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})},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})},getTypeaheadTransport:function(a,b){return this.typeAheadArgs=a,"undefined"!=typeof b&&(this.typeAheadValueOption=b),this},setTypeaheadParams:function(a,b){this.typeAHeadArgs=a,"undefined"!=typeof b&&(this.typeAheadValueOption=b)},get:function(a,b,c,d,e){return self=this,this.search(a,function(a,b){if(a){for(var f=0;f<b.hits.length;++f){var g=b.hits[f],h=!1;if("undefined"==typeof g.value&&(null!=self.typeAheadValueOption&&("function"==typeof self.typeAheadValueOption?(g.value=self.typeAheadValueOption(g),h=!0):"undefined"!=typeof g[self.typeAheadValueOption]&&(g.value=g[self.typeAheadValueOption],h=!0)),!h))for(var i in g)!h&&g.hasOwnProperty(i)&&"string"==typeof g[i]&&(g.value=g[i],h=!0);e.push(c._transformDatum(g))}d&&d(e)}},self.typeAheadArgs),!0},waitTask:function(a,b){var c=this;this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/task/"+a,callback:function(d,e){if(d&&"published"===e.status)b(!0,e);else{if(d&&e.pendingTask)return c.waitTask(a,b);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(a){var b;window.AlgoliaSearchHelper=function(c,d,e){var f={facets:[],disjunctiveFacets:[],hitsPerPage:20};this.init(c,d,a.extend({},f,e)),b=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){this.q=a,this.searchCallback=b,this.page=0,this.refinements={},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 a=0;a<this.options.disjunctiveFacets.length;++a)this.client.addQueryInBatch(this.index,this.q,this._getDisjunctiveFacetSearchParams(this.options.disjunctiveFacets[a]));this.client.sendQueriesBatch(function(a,c){if(!a)return b.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],b.disjunctiveRefinements[f])for(var g in b.disjunctiveRefinements[f])!d.disjunctiveFacets[f][g]&&b.disjunctiveRefinements[f][g]&&(d.disjunctiveFacets[f][g]=0);b.searchCallback(!0,d)})},_getHitsSearchParams:function(){return{hitsPerPage:this.options.hitsPerPage,page:this.page,facets:this.options.facets,facetFilters:this._getFacetFilters()}},_getDisjunctiveFacetSearchParams:function(a){return{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}}}(jQuery);
7
+ function AlgoliaExplainResults(a,b,c){function d(a,b){if("object"==typeof a&&"matchedWords"in a&&"value"in a){for(var c=!1,e=0;e<a.matchedWords.length;++e){var f=a.matchedWords[e];f in b||(b[f]=1,c=!0)}return c?[a.value]:[]}if(a instanceof Array){for(var g=[],h=0;h<a.length;++h){var i=d(a[h],b);g=g.concat(i)}return g}if("object"==typeof a){var g=[];for(prop in a)a.hasOwnProperty(prop)&&(g=g.concat(d(a[prop],b)));return g}return[]}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.0",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})},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){if(d&&"published"===e.status)b(!0,e);else{if(d&&e.pendingTask)return c.waitTask(a,b);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(a){var b;window.AlgoliaSearchHelper=function(c,d,e){var f={facets:[],disjunctiveFacets:[],hitsPerPage:20};this.init(c,d,a.extend({},f,e)),b=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){this.q=a,this.searchCallback=b,this.page=0,this.refinements={},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 a=0;a<this.options.disjunctiveFacets.length;++a)this.client.addQueryInBatch(this.index,this.q,this._getDisjunctiveFacetSearchParams(this.options.disjunctiveFacets[a]));this.client.sendQueriesBatch(function(a,c){if(!a)return b.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],b.disjunctiveRefinements[f])for(var g in b.disjunctiveRefinements[f])!d.disjunctiveFacets[f][g]&&b.disjunctiveRefinements[f][g]&&(d.disjunctiveFacets[f][g]=0);b.searchCallback(!0,d)})},_getHitsSearchParams:function(){return{hitsPerPage:this.options.hitsPerPage,page:this.page,facets:this.options.facets,facetFilters:this._getFacetFilters()}},_getDisjunctiveFacetSearchParams:function(a){return{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}}}(jQuery);
@@ -0,0 +1,651 @@
1
+ /*!
2
+ * typeahead.js 0.10.1
3
+ * https://github.com/twitter/typeahead.js
4
+ * Copyright 2013 Twitter, Inc. and other contributors; Licensed MIT
5
+ */
6
+
7
+ (function($) {
8
+ var _ = {
9
+ isMsie: function() {
10
+ return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false;
11
+ },
12
+ isBlankString: function(str) {
13
+ return !str || /^\s*$/.test(str);
14
+ },
15
+ escapeRegExChars: function(str) {
16
+ return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
17
+ },
18
+ isString: function(obj) {
19
+ return typeof obj === "string";
20
+ },
21
+ isNumber: function(obj) {
22
+ return typeof obj === "number";
23
+ },
24
+ isArray: $.isArray,
25
+ isFunction: $.isFunction,
26
+ isObject: $.isPlainObject,
27
+ isUndefined: function(obj) {
28
+ return typeof obj === "undefined";
29
+ },
30
+ bind: $.proxy,
31
+ each: function(collection, cb) {
32
+ $.each(collection, reverseArgs);
33
+ function reverseArgs(index, value) {
34
+ return cb(value, index);
35
+ }
36
+ },
37
+ map: $.map,
38
+ filter: $.grep,
39
+ every: function(obj, test) {
40
+ var result = true;
41
+ if (!obj) {
42
+ return result;
43
+ }
44
+ $.each(obj, function(key, val) {
45
+ if (!(result = test.call(null, val, key, obj))) {
46
+ return false;
47
+ }
48
+ });
49
+ return !!result;
50
+ },
51
+ some: function(obj, test) {
52
+ var result = false;
53
+ if (!obj) {
54
+ return result;
55
+ }
56
+ $.each(obj, function(key, val) {
57
+ if (result = test.call(null, val, key, obj)) {
58
+ return false;
59
+ }
60
+ });
61
+ return !!result;
62
+ },
63
+ mixin: $.extend,
64
+ getUniqueId: function() {
65
+ var counter = 0;
66
+ return function() {
67
+ return counter++;
68
+ };
69
+ }(),
70
+ templatify: function templatify(obj) {
71
+ return $.isFunction(obj) ? obj : template;
72
+ function template() {
73
+ return String(obj);
74
+ }
75
+ },
76
+ defer: function(fn) {
77
+ setTimeout(fn, 0);
78
+ },
79
+ debounce: function(func, wait, immediate) {
80
+ var timeout, result;
81
+ return function() {
82
+ var context = this, args = arguments, later, callNow;
83
+ later = function() {
84
+ timeout = null;
85
+ if (!immediate) {
86
+ result = func.apply(context, args);
87
+ }
88
+ };
89
+ callNow = immediate && !timeout;
90
+ clearTimeout(timeout);
91
+ timeout = setTimeout(later, wait);
92
+ if (callNow) {
93
+ result = func.apply(context, args);
94
+ }
95
+ return result;
96
+ };
97
+ },
98
+ throttle: function(func, wait) {
99
+ var context, args, timeout, result, previous, later;
100
+ previous = 0;
101
+ later = function() {
102
+ previous = new Date();
103
+ timeout = null;
104
+ result = func.apply(context, args);
105
+ };
106
+ return function() {
107
+ var now = new Date(), remaining = wait - (now - previous);
108
+ context = this;
109
+ args = arguments;
110
+ if (remaining <= 0) {
111
+ clearTimeout(timeout);
112
+ timeout = null;
113
+ previous = now;
114
+ result = func.apply(context, args);
115
+ } else if (!timeout) {
116
+ timeout = setTimeout(later, remaining);
117
+ }
118
+ return result;
119
+ };
120
+ },
121
+ noop: function() {}
122
+ };
123
+ var VERSION = "0.10.1";
124
+ var LruCache = function(root, undefined) {
125
+ function LruCache(maxSize) {
126
+ this.maxSize = maxSize || 100;
127
+ this.size = 0;
128
+ this.hash = {};
129
+ this.list = new List();
130
+ }
131
+ _.mixin(LruCache.prototype, {
132
+ set: function set(key, val) {
133
+ var tailItem = this.list.tail, node;
134
+ if (this.size >= this.maxSize) {
135
+ this.list.remove(tailItem);
136
+ delete this.hash[tailItem.key];
137
+ }
138
+ if (node = this.hash[key]) {
139
+ node.val = val;
140
+ this.list.moveToFront(node);
141
+ } else {
142
+ node = new Node(key, val);
143
+ this.list.add(node);
144
+ this.hash[key] = node;
145
+ this.size++;
146
+ }
147
+ },
148
+ get: function get(key) {
149
+ var node = this.hash[key];
150
+ if (node) {
151
+ this.list.moveToFront(node);
152
+ return node.val;
153
+ }
154
+ }
155
+ });
156
+ function List() {
157
+ this.head = this.tail = null;
158
+ }
159
+ _.mixin(List.prototype, {
160
+ add: function add(node) {
161
+ if (this.head) {
162
+ node.next = this.head;
163
+ this.head.prev = node;
164
+ }
165
+ this.head = node;
166
+ this.tail = this.tail || node;
167
+ },
168
+ remove: function remove(node) {
169
+ node.prev ? node.prev.next = node.next : this.head = node.next;
170
+ node.next ? node.next.prev = node.prev : this.tail = node.prev;
171
+ },
172
+ moveToFront: function(node) {
173
+ this.remove(node);
174
+ this.add(node);
175
+ }
176
+ });
177
+ function Node(key, val) {
178
+ this.key = key;
179
+ this.val = val;
180
+ this.prev = this.next = null;
181
+ }
182
+ return LruCache;
183
+ }(this);
184
+ var PersistentStorage = function() {
185
+ var ls, methods;
186
+ try {
187
+ ls = window.localStorage;
188
+ ls.setItem("~~~", "!");
189
+ ls.removeItem("~~~");
190
+ } catch (err) {
191
+ ls = null;
192
+ }
193
+ function PersistentStorage(namespace) {
194
+ this.prefix = [ "__", namespace, "__" ].join("");
195
+ this.ttlKey = "__ttl__";
196
+ this.keyMatcher = new RegExp("^" + this.prefix);
197
+ }
198
+ if (ls && window.JSON) {
199
+ methods = {
200
+ _prefix: function(key) {
201
+ return this.prefix + key;
202
+ },
203
+ _ttlKey: function(key) {
204
+ return this._prefix(key) + this.ttlKey;
205
+ },
206
+ get: function(key) {
207
+ if (this.isExpired(key)) {
208
+ this.remove(key);
209
+ }
210
+ return decode(ls.getItem(this._prefix(key)));
211
+ },
212
+ set: function(key, val, ttl) {
213
+ if (_.isNumber(ttl)) {
214
+ ls.setItem(this._ttlKey(key), encode(now() + ttl));
215
+ } else {
216
+ ls.removeItem(this._ttlKey(key));
217
+ }
218
+ return ls.setItem(this._prefix(key), encode(val));
219
+ },
220
+ remove: function(key) {
221
+ ls.removeItem(this._ttlKey(key));
222
+ ls.removeItem(this._prefix(key));
223
+ return this;
224
+ },
225
+ clear: function() {
226
+ var i, key, keys = [], len = ls.length;
227
+ for (i = 0; i < len; i++) {
228
+ if ((key = ls.key(i)).match(this.keyMatcher)) {
229
+ keys.push(key.replace(this.keyMatcher, ""));
230
+ }
231
+ }
232
+ for (i = keys.length; i--; ) {
233
+ this.remove(keys[i]);
234
+ }
235
+ return this;
236
+ },
237
+ isExpired: function(key) {
238
+ var ttl = decode(ls.getItem(this._ttlKey(key)));
239
+ return _.isNumber(ttl) && now() > ttl ? true : false;
240
+ }
241
+ };
242
+ } else {
243
+ methods = {
244
+ get: _.noop,
245
+ set: _.noop,
246
+ remove: _.noop,
247
+ clear: _.noop,
248
+ isExpired: _.noop
249
+ };
250
+ }
251
+ _.mixin(PersistentStorage.prototype, methods);
252
+ return PersistentStorage;
253
+ function now() {
254
+ return new Date().getTime();
255
+ }
256
+ function encode(val) {
257
+ return JSON.stringify(_.isUndefined(val) ? null : val);
258
+ }
259
+ function decode(val) {
260
+ return JSON.parse(val);
261
+ }
262
+ }();
263
+ var Transport = function() {
264
+ var pendingRequestsCount = 0, pendingRequests = {}, maxPendingRequests = 6, requestCache = new LruCache(10);
265
+ function Transport(o) {
266
+ o = o || {};
267
+ this._send = o.transport ? callbackToDeferred(o.transport) : $.ajax;
268
+ this._get = o.rateLimiter ? o.rateLimiter(this._get) : this._get;
269
+ }
270
+ Transport.setMaxPendingRequests = function setMaxPendingRequests(num) {
271
+ maxPendingRequests = num;
272
+ };
273
+ Transport.resetCache = function clearCache() {
274
+ requestCache = new LruCache(10);
275
+ };
276
+ _.mixin(Transport.prototype, {
277
+ _get: function(url, o, cb) {
278
+ var that = this, jqXhr;
279
+ if (jqXhr = pendingRequests[url]) {
280
+ jqXhr.done(done);
281
+ } else if (pendingRequestsCount < maxPendingRequests) {
282
+ pendingRequestsCount++;
283
+ pendingRequests[url] = this._send(url, o).done(done).always(always);
284
+ } else {
285
+ this.onDeckRequestArgs = [].slice.call(arguments, 0);
286
+ }
287
+ function done(resp) {
288
+ cb && cb(resp);
289
+ requestCache.set(url, resp);
290
+ }
291
+ function always() {
292
+ pendingRequestsCount--;
293
+ delete pendingRequests[url];
294
+ if (that.onDeckRequestArgs) {
295
+ that._get.apply(that, that.onDeckRequestArgs);
296
+ that.onDeckRequestArgs = null;
297
+ }
298
+ }
299
+ },
300
+ get: function(url, o, cb) {
301
+ var that = this, resp;
302
+ if (_.isFunction(o)) {
303
+ cb = o;
304
+ o = {};
305
+ }
306
+ if (resp = requestCache.get(url)) {
307
+ _.defer(function() {
308
+ cb && cb(resp);
309
+ });
310
+ } else {
311
+ this._get(url, o, cb);
312
+ }
313
+ return !!resp;
314
+ }
315
+ });
316
+ return Transport;
317
+ function callbackToDeferred(fn) {
318
+ return function customSendWrapper(url, o) {
319
+ var deferred = $.Deferred();
320
+ fn(url, o, onSuccess, onError);
321
+ return deferred;
322
+ function onSuccess(resp) {
323
+ _.defer(function() {
324
+ deferred.resolve(resp);
325
+ });
326
+ }
327
+ function onError(err) {
328
+ _.defer(function() {
329
+ deferred.reject(err);
330
+ });
331
+ }
332
+ };
333
+ }
334
+ }();
335
+ var SearchIndex = function() {
336
+ function SearchIndex(o) {
337
+ o = o || {};
338
+ if (!o.datumTokenizer || !o.queryTokenizer) {
339
+ $.error("datumTokenizer and queryTokenizer are both required");
340
+ }
341
+ this.datumTokenizer = o.datumTokenizer;
342
+ this.queryTokenizer = o.queryTokenizer;
343
+ this.datums = [];
344
+ this.trie = newNode();
345
+ }
346
+ _.mixin(SearchIndex.prototype, {
347
+ bootstrap: function bootstrap(o) {
348
+ this.datums = o.datums;
349
+ this.trie = o.trie;
350
+ },
351
+ add: function(data) {
352
+ var that = this;
353
+ data = _.isArray(data) ? data : [ data ];
354
+ _.each(data, function(datum) {
355
+ var id, tokens;
356
+ id = that.datums.push(datum) - 1;
357
+ tokens = normalizeTokens(that.datumTokenizer(datum));
358
+ _.each(tokens, function(token) {
359
+ var node, chars, ch, ids;
360
+ node = that.trie;
361
+ chars = token.split("");
362
+ while (ch = chars.shift()) {
363
+ node = node.children[ch] || (node.children[ch] = newNode());
364
+ node.ids.push(id);
365
+ }
366
+ });
367
+ });
368
+ },
369
+ get: function get(query) {
370
+ var that = this, tokens, matches;
371
+ tokens = normalizeTokens(this.queryTokenizer(query));
372
+ _.each(tokens, function(token) {
373
+ var node, chars, ch, ids;
374
+ if (matches && matches.length === 0) {
375
+ return false;
376
+ }
377
+ node = that.trie;
378
+ chars = token.split("");
379
+ while (node && (ch = chars.shift())) {
380
+ node = node.children[ch];
381
+ }
382
+ if (node && chars.length === 0) {
383
+ ids = node.ids.slice(0);
384
+ matches = matches ? getIntersection(matches, ids) : ids;
385
+ } else {
386
+ matches = [];
387
+ return false;
388
+ }
389
+ });
390
+ return matches ? _.map(unique(matches), function(id) {
391
+ return that.datums[id];
392
+ }) : [];
393
+ },
394
+ serialize: function serialize() {
395
+ return {
396
+ datums: this.datums,
397
+ trie: this.trie
398
+ };
399
+ }
400
+ });
401
+ return SearchIndex;
402
+ function normalizeTokens(tokens) {
403
+ tokens = _.filter(tokens, function(token) {
404
+ return !!token;
405
+ });
406
+ tokens = _.map(tokens, function(token) {
407
+ return token.toLowerCase();
408
+ });
409
+ return tokens;
410
+ }
411
+ function newNode() {
412
+ return {
413
+ ids: [],
414
+ children: {}
415
+ };
416
+ }
417
+ function unique(array) {
418
+ var seen = {}, uniques = [];
419
+ for (var i = 0; i < array.length; i++) {
420
+ if (!seen[array[i]]) {
421
+ seen[array[i]] = true;
422
+ uniques.push(array[i]);
423
+ }
424
+ }
425
+ return uniques;
426
+ }
427
+ function getIntersection(arrayA, arrayB) {
428
+ var ai = 0, bi = 0, intersection = [];
429
+ arrayA = arrayA.sort(compare);
430
+ arrayB = arrayB.sort(compare);
431
+ while (ai < arrayA.length && bi < arrayB.length) {
432
+ if (arrayA[ai] < arrayB[bi]) {
433
+ ai++;
434
+ } else if (arrayA[ai] > arrayB[bi]) {
435
+ bi++;
436
+ } else {
437
+ intersection.push(arrayA[ai]);
438
+ ai++;
439
+ bi++;
440
+ }
441
+ }
442
+ return intersection;
443
+ function compare(a, b) {
444
+ return a - b;
445
+ }
446
+ }
447
+ }();
448
+ var oParser = function() {
449
+ return {
450
+ local: getLocal,
451
+ prefetch: getPrefetch,
452
+ remote: getRemote
453
+ };
454
+ function getLocal(o) {
455
+ var local = o.local || null;
456
+ if (_.isFunction(local)) {
457
+ local = local.call(null);
458
+ }
459
+ return local;
460
+ }
461
+ function getPrefetch(o) {
462
+ var prefetch, defaults;
463
+ defaults = {
464
+ url: null,
465
+ thumbprint: "",
466
+ ttl: 24 * 60 * 60 * 1e3,
467
+ filter: null,
468
+ ajax: {}
469
+ };
470
+ if (prefetch = o.prefetch || null) {
471
+ prefetch = _.isString(prefetch) ? {
472
+ url: prefetch
473
+ } : prefetch;
474
+ prefetch = _.mixin(defaults, prefetch);
475
+ prefetch.thumbprint = VERSION + prefetch.thumbprint;
476
+ prefetch.ajax.type = prefetch.ajax.type || "GET";
477
+ prefetch.ajax.dataType = prefetch.ajax.dataType || "json";
478
+ !prefetch.url && $.error("prefetch requires url to be set");
479
+ }
480
+ return prefetch;
481
+ }
482
+ function getRemote(o) {
483
+ var remote, defaults;
484
+ defaults = {
485
+ url: null,
486
+ wildcard: "%QUERY",
487
+ replace: null,
488
+ rateLimitBy: "debounce",
489
+ rateLimitWait: 300,
490
+ send: null,
491
+ filter: null,
492
+ ajax: {}
493
+ };
494
+ if (remote = o.remote || null) {
495
+ remote = _.isString(remote) ? {
496
+ url: remote
497
+ } : remote;
498
+ remote = _.mixin(defaults, remote);
499
+ remote.rateLimiter = /^throttle$/i.test(remote.rateLimitBy) ? byThrottle(remote.rateLimitWait) : byDebounce(remote.rateLimitWait);
500
+ remote.ajax.type = remote.ajax.type || "GET";
501
+ remote.ajax.dataType = remote.ajax.dataType || "json";
502
+ delete remote.rateLimitBy;
503
+ delete remote.rateLimitWait;
504
+ !remote.url && $.error("remote requires url to be set");
505
+ }
506
+ return remote;
507
+ function byDebounce(wait) {
508
+ return function(fn) {
509
+ return _.debounce(fn, wait);
510
+ };
511
+ }
512
+ function byThrottle(wait) {
513
+ return function(fn) {
514
+ return _.throttle(fn, wait);
515
+ };
516
+ }
517
+ }
518
+ }();
519
+ var Bloodhound = window.Bloodhound = function() {
520
+ var keys;
521
+ keys = {
522
+ data: "data",
523
+ protocol: "protocol",
524
+ thumbprint: "thumbprint"
525
+ };
526
+ function Bloodhound(o) {
527
+ if (!o || !o.local && !o.prefetch && !o.remote) {
528
+ $.error("one of local, prefetch, or remote is required");
529
+ }
530
+ this.limit = o.limit || 5;
531
+ this.sorter = getSorter(o.sorter);
532
+ this.dupDetector = o.dupDetector || ignoreDuplicates;
533
+ this.local = oParser.local(o);
534
+ this.prefetch = oParser.prefetch(o);
535
+ this.remote = oParser.remote(o);
536
+ this.cacheKey = this.prefetch ? this.prefetch.cacheKey || this.prefetch.url : null;
537
+ this.index = new SearchIndex({
538
+ datumTokenizer: o.datumTokenizer,
539
+ queryTokenizer: o.queryTokenizer
540
+ });
541
+ this.storage = this.cacheKey ? new PersistentStorage(this.cacheKey) : null;
542
+ }
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
+ }
550
+ };
551
+ _.mixin(Bloodhound.prototype, {
552
+ _loadPrefetch: function loadPrefetch(o) {
553
+ var that = this, serialized, deferred;
554
+ if (serialized = this._readFromStorage(o.thumbprint)) {
555
+ this.index.bootstrap(serialized);
556
+ deferred = $.Deferred().resolve();
557
+ } else {
558
+ deferred = $.ajax(o.url, o.ajax).done(handlePrefetchResponse);
559
+ }
560
+ return deferred;
561
+ function handlePrefetchResponse(resp) {
562
+ var filtered;
563
+ filtered = o.filter ? o.filter(resp) : resp;
564
+ that.add(filtered);
565
+ that._saveToStorage(that.index.serialize(), o.thumbprint, o.ttl);
566
+ }
567
+ },
568
+ _getFromRemote: function getFromRemote(query, cb) {
569
+ var that = this, url, uriEncodedQuery;
570
+ query = query || "";
571
+ uriEncodedQuery = encodeURIComponent(query);
572
+ url = this.remote.replace ? this.remote.replace(this.remote.url, query) : this.remote.url.replace(this.remote.wildcard, uriEncodedQuery);
573
+ 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);
577
+ }
578
+ },
579
+ _saveToStorage: function saveToStorage(data, thumbprint, ttl) {
580
+ if (this.storage) {
581
+ this.storage.set(keys.data, data, ttl);
582
+ this.storage.set(keys.protocol, location.protocol, ttl);
583
+ this.storage.set(keys.thumbprint, thumbprint, ttl);
584
+ }
585
+ },
586
+ _readFromStorage: function readFromStorage(thumbprint) {
587
+ var stored = {}, isExpired;
588
+ if (this.storage) {
589
+ stored.data = this.storage.get(keys.data);
590
+ stored.protocol = this.storage.get(keys.protocol);
591
+ stored.thumbprint = this.storage.get(keys.thumbprint);
592
+ }
593
+ isExpired = stored.thumbprint !== thumbprint || stored.protocol !== location.protocol;
594
+ return stored.data && !isExpired ? stored.data : null;
595
+ },
596
+ initialize: function initialize() {
597
+ var that = this, deferred;
598
+ deferred = this.prefetch ? this._loadPrefetch(this.prefetch) : $.Deferred().resolve();
599
+ this.local && deferred.done(addLocalToIndex);
600
+ this.transport = this.remote ? new Transport(this.remote) : null;
601
+ this.initialize = function initialize() {
602
+ return deferred.promise();
603
+ };
604
+ return deferred.promise();
605
+ function addLocalToIndex() {
606
+ that.add(that.local);
607
+ }
608
+ },
609
+ add: function add(data) {
610
+ this.index.add(data);
611
+ },
612
+ get: function get(query, cb) {
613
+ var that = this, matches, cacheHit = false;
614
+ matches = this.index.get(query);
615
+ matches = this.sorter(matches).slice(0, this.limit);
616
+ if (matches.length < this.limit && this.transport) {
617
+ cacheHit = this._getFromRemote(query, returnRemoteMatches);
618
+ }
619
+ !cacheHit && cb && cb(matches);
620
+ function returnRemoteMatches(remoteMatches) {
621
+ var matchesWithBackfill = matches.slice(0);
622
+ _.each(remoteMatches, function(remoteMatch) {
623
+ var isDuplicate;
624
+ isDuplicate = _.some(matchesWithBackfill, function(match) {
625
+ return that.dupDetector(remoteMatch, match);
626
+ });
627
+ !isDuplicate && matchesWithBackfill.push(remoteMatch);
628
+ return matchesWithBackfill.length < that.limit;
629
+ });
630
+ cb && cb(that.sorter(matchesWithBackfill));
631
+ }
632
+ },
633
+ ttAdapter: function ttAdapter() {
634
+ return _.bind(this.get, this);
635
+ }
636
+ });
637
+ return Bloodhound;
638
+ function getSorter(sortFn) {
639
+ return _.isFunction(sortFn) ? sort : noSort;
640
+ function sort(array) {
641
+ return array.sort(sortFn);
642
+ }
643
+ function noSort(array) {
644
+ return array;
645
+ }
646
+ }
647
+ function ignoreDuplicates() {
648
+ return false;
649
+ }
650
+ }();
651
+ })(window.jQuery);