algoliasearch-rails 1.11.17 → 1.11.18

Sign up to get free protection for your applications and to get access to all the features.
Files changed (26) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog +4 -0
  3. data/README.md +48 -6
  4. data/VERSION +1 -1
  5. data/algoliasearch-rails.gemspec +22 -6
  6. data/vendor/assets/javascripts/algolia/algoliasearch.angular.js +1446 -1378
  7. data/vendor/assets/javascripts/algolia/algoliasearch.angular.min.js +2 -2
  8. data/vendor/assets/javascripts/algolia/algoliasearch.jquery.js +1446 -1378
  9. data/vendor/assets/javascripts/algolia/algoliasearch.jquery.min.js +2 -2
  10. data/vendor/assets/javascripts/algolia/algoliasearch.js +1446 -1378
  11. data/vendor/assets/javascripts/algolia/algoliasearch.min.js +2 -2
  12. data/vendor/assets/javascripts/algolia/bloodhound.min.js +7 -0
  13. data/vendor/assets/javascripts/algolia/typeahead.jquery.min.js +7 -0
  14. data/vendor/assets/javascripts/algolia/v2/algoliasearch.angular.js +2667 -0
  15. data/vendor/assets/javascripts/algolia/v2/algoliasearch.angular.min.js +7 -0
  16. data/vendor/assets/javascripts/algolia/v2/algoliasearch.jquery.js +2667 -0
  17. data/vendor/assets/javascripts/algolia/v2/algoliasearch.jquery.min.js +7 -0
  18. data/vendor/assets/javascripts/algolia/v2/algoliasearch.js +2653 -0
  19. data/vendor/assets/javascripts/algolia/v2/algoliasearch.min.js +7 -0
  20. data/vendor/assets/javascripts/algolia/v3/algoliasearch.angular.js +1717 -0
  21. data/vendor/assets/javascripts/algolia/v3/algoliasearch.angular.min.js +37 -0
  22. data/vendor/assets/javascripts/algolia/v3/algoliasearch.jquery.js +1702 -0
  23. data/vendor/assets/javascripts/algolia/v3/algoliasearch.jquery.min.js +37 -0
  24. data/vendor/assets/javascripts/algolia/v3/algoliasearch.js +2732 -0
  25. data/vendor/assets/javascripts/algolia/v3/algoliasearch.min.js +50 -0
  26. metadata +36 -22
@@ -1,7 +1,7 @@
1
1
  /*!
2
- * algoliasearch 2.9.2
2
+ * algoliasearch 2.9.4
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){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.9.2",AlgoliaSearch=function(a,b,c,d,e){var f=this;this.applicationID=a,this.apiKey=b,this.dsn=!0,this.dsnHost=null,this.hosts=[],this.currentHostIndex=0,this.requestTimeoutInMs=2e3,this.extraHeaders=[],this.jsonp=null,this.options={},this.cache={};var g,h="net";if("string"==typeof c)g=c;else{var i=c||{};this.options=i,this._isUndefined(i.method)||(g=i.method),this._isUndefined(i.tld)||(h=i.tld),this._isUndefined(i.dsn)||(this.dsn=i.dsn),this._isUndefined(i.hosts)||(e=i.hosts),this._isUndefined(i.dsnHost)||(this.dsnHost=i.dsnHost),this._isUndefined(i.requestTimeoutInMs)||(this.requestTimeoutInMs=+i.requestTimeoutInMs),this._isUndefined(i.jsonp)||(this.jsonp=i.jsonp)}this._isUndefined(e)&&(e=[this.applicationID+"-1.algolia."+h,this.applicationID+"-2.algolia."+h,this.applicationID+"-3.algolia."+h]),this.host_protocol="http://",this._isUndefined(g)||null===g?this.host_protocol=("https:"==document.location.protocol?"https":"http")+"://":("https"===g||"HTTPS"===g)&&(this.host_protocol="https://");for(var j=0;j<e.length;++j)Math.random()>.5&&this.hosts.reverse(),this.hosts.push(this.host_protocol+e[j]);Math.random()>.5&&this.hosts.reverse(),(this.dsn||null!=this.dsnHost)&&this.hosts.unshift(this.dsnHost?this.host_protocol+this.dsnHost:this.host_protocol+this.applicationID+"-dsn.algolia."+h),this.options.angular&&this.options.angular.$injector.invoke(["$http","$q",function(a,b){f.options.angular.$q=b,f.options.angular.$http=a}])};AlgoliaSearch.prototype={deleteIndex:function(a,b){return this._jsonRequest({method:"DELETE",url:"/1/indexes/"+encodeURIComponent(a),callback:b})},moveIndex:function(a,b,c){var d={operation:"move",destination:b};return 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};return this._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(a)+"/operation",body:d,callback:c})},getLogs:function(a,b,c){return 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,b){var c="undefined"!=typeof b?"?page="+b:"";return this._jsonRequest({method:"GET",url:"/1/indexes"+c,callback:a})},initIndex:function(a){return new this.Index(this,a)},listUserKeys:function(a){return this._jsonRequest({method:"GET",url:"/1/keys",callback:a})},getUserKeyACL:function(a,b){return this._jsonRequest({method:"GET",url:"/1/keys/"+a,callback:b})},deleteUserKey:function(a,b){return this._jsonRequest({method:"DELETE",url:"/1/keys/"+a,callback:b})},addUserKey:function(a,b){var c={};return 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={};return 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.tagFilters=a},setUserToken:function(a){this.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:[]},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))return this._sendQueriesBatch(d,a);var f=window.setTimeout(function(){c._sendQueriesBatch(d,a)},b);c.onDelayTrigger=f},setRequestTimeout:function(a){a&&(this.requestTimeoutInMs=parseInt(a,10))},Index:function(a,b){this.indexName=b,this.as=a,this.typeAheadArgs=null,this.typeAheadValueOption=null,this.cache={}},setExtraHeader:function(a,b){this.extraHeaders.push({key:a,value:b})},_sendQueriesBatch:function(a,b){if(null===this.jsonp){var c=this;return this._jsonRequest({cache:this.cache,method:"POST",url:"/1/indexes/*/queries",body:a,callback:function(d,e){d?(c.jsonp=!1,b&&b(d,e)):(c.jsonp=!0,c._sendQueriesBatch(a,b))}})}if(this.jsonp){for(var d="",e=0;e<a.requests.length;++e){var f="/1/indexes/"+encodeURIComponent(a.requests[e].indexName)+"?"+a.requests[e].params;d+=e+"="+encodeURIComponent(f)+"&"}var g={params:d};return this._jsonRequest({cache:this.cache,method:"GET",url:"/1/indexes/*",body:g,callback:b})}return 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,f=null;if(this.options.jQuery?(f=this.options.jQuery.$.Deferred(),f.promise=f.promise()):this.options.angular&&(f=this.options.angular.$q.defer()),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&&setTimeout(function(){c(!0,d[e])},1),f&&f.resolve(d[e]),f&&f.promise;a.successiveRetryCount=0;var g=function(){if(a.successiveRetryCount>=b.hosts.length){var h={message:"Cannot connect the Algolia's Search API. Please send an email to support@algolia.com to report the issue."};return!b._isUndefined(c)&&c&&(a.successiveRetryCount=0,c(!1,h)),void(f&&f.reject(h))}a.callback=function(h,i,j,k){i&&!b._isUndefined(a.cache)&&(d[e]=k),!i&&h?(b.currentHostIndex=++b.currentHostIndex%b.hosts.length,a.successiveRetryCount+=1,g()):(a.successiveRetryCount=0,f&&(i?f.resolve(k):f.reject(k)),!b._isUndefined(c)&&c&&c(i,k))},a.hostname=b.hosts[b.currentHostIndex],b._jsonRequestByHost(a)};return g(),f&&f.promise},_jsonRequestByHost:function(a){var b=a.hostname+a.url;this.jsonp?this._makeJsonpRequestByHost(b,a):this.options.jQuery?this._makejQueryRequestByHost(b,a):this.options.angular?this._makeAngularRequestByHost(b,a):this._makeXmlHttpRequestByHost(b,a)},_makeAngularRequestByHost:function(a,b){var c=null;this._isUndefined(b.body)||(c=JSON.stringify(b.body)),a+=(-1===a.indexOf("?")?"?":"&")+"X-Algolia-API-Key="+this.apiKey,a+="&X-Algolia-Application-Id="+this.applicationID,this.userToken&&(a+="&X-Algolia-UserToken="+encodeURIComponent(this.userToken)),this.tagFilters&&(a+="&X-Algolia-TagFilters="+encodeURIComponent(this.tagFilters));for(var d=0;d<this.extraHeaders.length;++d)a+="&"+this.extraHeaders[d].key+"="+this.extraHeaders[d].value;this.options.angular.$http({url:a,method:b.method,data:c,cache:!1,timeout:this.requestTimeoutInMs*(b.successiveRetryCount+1)}).then(function(a){b.callback(!1,!0,null,a.data)},function(a){0===a.status?b.callback(!0,!1,null,a.data):400==a.status||403===a.status||404===a.status?b.callback(!1,!1,null,a.data):b.callback(!0,!1,null,a.data)})},_makejQueryRequestByHost:function(a,b){var c=null;this._isUndefined(b.body)||(c=JSON.stringify(b.body)),a+=(-1===a.indexOf("?")?"?":"&")+"X-Algolia-API-Key="+this.apiKey,a+="&X-Algolia-Application-Id="+this.applicationID,this.userToken&&(a+="&X-Algolia-UserToken="+encodeURIComponent(this.userToken)),this.tagFilters&&(a+="&X-Algolia-TagFilters="+encodeURIComponent(this.tagFilters));for(var d=0;d<this.extraHeaders.length;++d)a+="&"+this.extraHeaders[d].key+"="+this.extraHeaders[d].value;this.options.jQuery.$.ajax(a,{type:b.method,timeout:this.requestTimeoutInMs*(b.successiveRetryCount+1),dataType:"json",data:c,error:function(c,d,e){"timeout"===d?b.callback(!0,!1,null,{message:"Timeout - Could not connect to endpoint "+a}):400===c.status||403===c.status||404===c.status?b.callback(!1,!1,null,c.responseJSON):b.callback(!0,!1,null,{message:e})},success:function(a){b.callback(!1,!0,null,a)}})},_makeJsonpRequestByHost:function(a,b){if("GET"!==b.method)return void b.callback(!0,!1,null,{message:"Method "+b.method+" "+a+" is not supported by JSONP."});this.jsonpCounter=this.jsonpCounter||0,this.jsonpCounter+=1;var c=document.getElementsByTagName("head")[0],d=document.createElement("script"),e="algoliaJSONP_"+this.jsonpCounter,f=!1,g=null;window[e]=function(a){b.callback(!1,!0,null,a);try{delete window[e]}catch(c){window[e]=void 0}},d.type="text/javascript",d.src=a+"?callback="+e+"&X-Algolia-Application-Id="+this.applicationID+"&X-Algolia-API-Key="+this.apiKey,this.tagFilters&&(d.src+="&X-Algolia-TagFilters="+encodeURIComponent(this.tagFilters)),this.userToken&&(d.src+="&X-Algolia-UserToken="+encodeURIComponent(this.userToken));for(var h=0;h<this.extraHeaders.length;++h)d.src+="&"+this.extraHeaders[h].key+"="+this.extraHeaders[h].value;b.body&&b.body.params&&(d.src+="&"+b.body.params),g=setTimeout(function(){d.onload=d.onreadystatechange=d.onerror=null,window[e]=function(){try{delete window[e]}catch(a){window[e]=void 0}},b.callback(!0,!1,null,{message:"Timeout - Failed to load JSONP script."}),c.removeChild(d),clearTimeout(g),g=null},this.requestTimeoutInMs),d.onload=d.onreadystatechange=function(){if(clearTimeout(g),g=null,!(f||this.readyState&&"loaded"!=this.readyState&&"complete"!=this.readyState)){if(f=!0,"undefined"==typeof window[e+"_loaded"]){b.callback(!0,!1,null,{message:"Failed to load JSONP script."});try{delete window[e]}catch(a){window[e]=void 0}}else try{delete window[e+"_loaded"]}catch(a){window[e+"_loaded"]=void 0}d.onload=d.onreadystatechange=null,c.removeChild(d)}},d.onerror=function(){clearTimeout(g),g=null,b.callback(!0,!1,null,{message:"Failed to load JSONP script."}),c.removeChild(d);try{delete window[e]}catch(a){window[e]=void 0}},c.appendChild(d)},_makeXmlHttpRequestByHost:function(a,b){var c=this,d=window.XMLHttpRequest?new XMLHttpRequest:{},e=null,f=null;this._isUndefined(b.body)||(e=JSON.stringify(b.body)),a+=(-1===a.indexOf("?")?"?":"&")+"X-Algolia-API-Key="+this.apiKey,a+="&X-Algolia-Application-Id="+this.applicationID,this.userToken&&(a+="&X-Algolia-UserToken="+encodeURIComponent(this.userToken)),this.tagFilters&&(a+="&X-Algolia-TagFilters="+encodeURIComponent(this.tagFilters));for(var g=0;g<this.extraHeaders.length;++g)a+="&"+this.extraHeaders[g].key+"="+this.extraHeaders[g].value;if("withCredentials"in d)d.open(b.method,a,!0),d.timeout=this.requestTimeoutInMs*(b.successiveRetryCount+1),null!==e&&d.setRequestHeader("Content-type","application/x-www-form-urlencoded");else{if("undefined"==typeof XDomainRequest)return void b.callback(!1,!1,null,{message:"CORS not supported"});d=new XDomainRequest,d.open(b.method,a)}f=setTimeout(function(){return d.abort(),d.aborted===!0?void stopLoadAnimation():(b.callback(!0,!1,null,{message:"Timeout - Could not connect to endpoint "+a}),clearTimeout(f),void(f=null))},this.requestTimeoutInMs*(b.successiveRetryCount+1)),d.onload=function(a){if(clearTimeout(f),f=null,c._isUndefined(a)||null===a.target)b.callback(!1,!0,a,JSON.parse(d.responseText));else{var e=!1,g=null;"undefined"!=typeof XDomainRequest?(g=a.target.responseText,e=g&&g.length>0):(g=a.target.response,e=200===a.target.status||201===a.target.status);var h=!e&&400!==a.target.status&&403!==a.target.status&&404!==a.target.status;b.callback(h,e,a.target,g?JSON.parse(g):null)}},d.ontimeout=function(){},d.onerror=function(a){clearTimeout(f),f=null,b.callback(!0,!1,null,{message:"Could not connect to host",error:a})},d.send(e)},_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,tagFilters:null,userToken:null,hosts:[],extraHeaders:[]},AlgoliaSearch.prototype.Index.prototype={clearCache:function(){this.cache={}},addObject:function(a,b,c){var d=this;return this.as._jsonRequest(this.as._isUndefined(c)?{method:"POST",url:"/1/indexes/"+encodeURIComponent(d.indexName),body:a,callback:b}:{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)}return this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/batch",body:d,callback:b})},getObject:function(a,b,c){"[object Array]"!==Object.prototype.toString.call(b)||c||(c=b,b=null);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]}if(null===this.as.jsonp)return this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(d.indexName)+"/"+encodeURIComponent(a)+e,callback:b});var g={params:e};return this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(d.indexName)+"/"+encodeURIComponent(a),callback:b,body:g})},partialUpdateObject:function(a,b){var c=this;return 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)}return this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/batch",body:d,callback:b})},saveObject:function(a,b){var c=this;return 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)}return 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 void b(!1,{message:"empty objectID"});var c=this;return this.as._jsonRequest({method:"DELETE",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/"+encodeURIComponent(a),callback:b})},search:function(a,b,c,d){(void 0===a||null===a)&&(a=""),"function"==typeof a&&(b=a,a=""),"object"!=typeof b||!this.as._isUndefined(c)&&c||(c=b,b=null);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))return this._search(f,b);var g=window.setTimeout(function(){e._search(f,b)},d);e.onDelayTrigger=g},browse:function(a,b,c){+b>0&&(this.as._isUndefined(c)||!c)&&(c=b,b=null);var d=this,e="?page="+a;return this.as._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;return 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;return this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(b.indexName)+"/clear",callback:a})},getSettings:function(a){var b=this;return this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(b.indexName)+"/settings",callback:a})},setSettings:function(a,b){var c=this;return this.as._jsonRequest({method:"PUT",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/settings",body:a,callback:b})},listUserKeys:function(a){var b=this;return this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(b.indexName)+"/keys",callback:a})},getUserKeyACL:function(a,b){var c=this;return this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/keys/"+a,callback:b})},deleteUserKey:function(a,b){var c=this;return this.as._jsonRequest({method:"DELETE",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/keys/"+a,callback:b})},addUserKey:function(a,b){var c=this,d={};return 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={};return 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){var c={params:a};if(null===this.as.jsonp){var d=this;return this.as._jsonRequest({cache:this.cache,method:"POST",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/query",body:c,callback:function(c,e){c?(d.as.jsonp=!1,b&&b(c,e)):(d.as.jsonp=!0,d._search(a,b))}})}return this.as._jsonRequest(this.as.jsonp?{cache:this.cache,method:"GET",url:"/1/indexes/"+encodeURIComponent(this.indexName),body:c,callback:b}:{cache:this.cache,method:"POST",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/query",body:c,callback:b})},as:null,indexName:null,typeAheadArgs:null,typeAheadValueOption:null},function(){var a=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(b,c,d){var e={facets:[],disjunctiveFacets:[],hitsPerPage:20,defaultFacetFilters:[]};this.init(b,c,a({},e,d))},AlgoliaSearchHelper.prototype={init:function(a,b,c){this.client=a,this.index=b,this.options=c,this.page=0,this.refinements={},this.excludes={},this.disjunctiveRefinements={},this.extraQueries=[]},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()},clearRefinements:function(){this.disjunctiveRefinements={},this.refinements={}},addDisjunctiveRefine:function(a,b){this.disjunctiveRefinements=this.disjunctiveRefinements||{},this.disjunctiveRefinements[a]=this.disjunctiveRefinements[a]||{},this.disjunctiveRefinements[a][b]=!0},removeDisjunctiveRefine:function(a,b){this.disjunctiveRefinements=this.disjunctiveRefinements||{},this.disjunctiveRefinements[a]=this.disjunctiveRefinements[a]||{};try{delete this.disjunctiveRefinements[a][b]}catch(c){this.disjunctiveRefinements[a][b]=void 0}},addRefine:function(a,b){var c=a+":"+b;this.refinements=this.refinements||{},this.refinements[c]=!0},removeRefine:function(a,b){var c=a+":"+b;this.refinements=this.refinements||{},this.refinements[c]=!1},addExclude:function(a,b){var c=a+":-"+b;this.excludes=this.excludes||{},this.excludes[c]=!0},removeExclude:function(a,b){var c=a+":-"+b;this.excludes=this.excludes||{},this.excludes[c]=!1},toggleExclude: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.excludes[d]=!this.excludes[d],this.page=0,this._search(),!0}return!1},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},isExcluded:function(a,b){var c=a+":-"+b;return this.excludes[c]?!0:!1},nextPage:function(){this._gotoPage(this.page+1)},previousPage:function(){this.page>0&&this._gotoPage(this.page-1)},gotoPage:function(a){this._gotoPage(a)},setPage:function(a){this.page=a},setIndex:function(a){this.index=a},getIndex:function(){return this.index},clearExtraQueries:function(){this.extraQueries=[]},addExtraQuery:function(a,b,c){this.extraQueries.push({index:a,query:b,params:c||{}})},_gotoPage:function(a){this.page=a,this._search()},_search:function(){this.client.startQueriesBatch(),this.client.addQueryInBatch(this.index,this.q,this._getHitsSearchParams());var a=[],b={},c=0;for(c=0;c<this.options.disjunctiveFacets.length;++c){var d=this.options.disjunctiveFacets[c];this._hasDisjunctiveRefinements(d)?a.push(d):b[d]=!0}for(c=0;c<a.length;++c)this.client.addQueryInBatch(this.index,this.q,this._getDisjunctiveFacetSearchParams(a[c]));for(c=0;c<this.extraQueries.length;++c)this.client.addQueryInBatch(this.extraQueries[c].index,this.extraQueries[c].query,this.extraQueries[c].params);var e=this;this.client.sendQueriesBatch(function(d,f){if(!d)return void e.searchCallback(!1,f);var g=f.results[0];g.disjunctiveFacets=g.disjunctiveFacets||{},g.facetStats=g.facetStats||{};for(var h in b)if(g.facets[h]&&!g.disjunctiveFacets[h]){g.disjunctiveFacets[h]=g.facets[h];try{delete g.facets[h]}catch(i){g.facets[h]=void 0}}for(c=0;c<a.length;++c){for(var j in f.results[c+1].facets)if(g.disjunctiveFacets[j]=f.results[c+1].facets[j],e.disjunctiveRefinements[j])for(var k in e.disjunctiveRefinements[j])!g.disjunctiveFacets[j][k]&&e.disjunctiveRefinements[j][k]&&(g.disjunctiveFacets[j][k]=0);for(var l in f.results[c+1].facets_stats)g.facetStats[l]=f.results[c+1].facets_stats[l]}for(var m in e.excludes)if(e.excludes[m]){var i=m.indexOf(":-"),h=m.slice(0,i),k=m.slice(i+2);g.facets[h]=g.facets[h]||{},g.facets[h][k]||(g.facets[h][k]=0)}if(0===e.extraQueries.length)e.searchCallback(!0,g);else{var n={results:[g]};for(c=0;c<e.extraQueries.length;++c)n.results.push(f.results[1+a.length+c]);e.searchCallback(!0,n)}})},_getHitsSearchParams:function(){var b=[],c=0;for(c=0;c<this.options.facets.length;++c)b.push(this.options.facets[c]);for(c=0;c<this.options.disjunctiveFacets.length;++c){var d=this.options.disjunctiveFacets[c];this._hasDisjunctiveRefinements(d)||b.push(d)}return a({},{hitsPerPage:this.options.hitsPerPage,page:this.page,facets:b,facetFilters:this._getFacetFilters()},this.searchParams)},_getDisjunctiveFacetSearchParams:function(b){return a({},this.searchParams,{hitsPerPage:1,page:0,attributesToRetrieve:[],attributesToHighlight:[],attributesToSnippet:[],facets:b,facetFilters:this._getFacetFilters(b)})},_hasDisjunctiveRefinements:function(a){for(var b in this.disjunctiveRefinements[a])if(this.disjunctiveRefinements[a][b])return!0;return!1},_getFacetFilters:function(a){var b=[];if(this.options.defaultFacetFilters)for(var c=0;c<this.options.defaultFacetFilters.length;++c)b.push(this.options.defaultFacetFilters[c]);for(var d in this.refinements)this.refinements[d]&&b.push(d);for(var d in this.excludes)this.excludes[d]&&b.push(d);for(var e in this.disjunctiveRefinements)if(e!=a){var f=[];for(var g in this.disjunctiveRefinements[e])this.disjunctiveRefinements[e][g]&&f.push(e+":"+g);f.length>0&&b.push(f)}return b}}}(),function(){window.AlgoliaPlaces=function(a,b){this.init(a,b)},AlgoliaPlaces.prototype={init:function(a,b){this.client=new AlgoliaSearch(a,b,"http",!0,["places-1.algolia.io","places-2.algolia.io","places-3.algolia.io"]),this.cache={}},search:function(a,b,c){var d="query="+encodeURIComponent(a);this.client._isUndefined(c)||null==c||(d=this.client._getSearchParams(c,d));var e={params:d,apiKey:this.client.apiKey,appID:this.client.applicationID};this.client._jsonRequest({cache:this.cache,method:"POST",url:"/1/places/query",body:e,callback:b,removeCustomHTTPHeaders:!0})}}}(),"object"!=typeof JSON&&(JSON={}),function(){"use strict";function f(a){return 10>a?"0"+a:a}function quote(a){return escapable.lastIndex=0,escapable.test(a)?'"'+a.replace(escapable,function(a){var b=meta[a];return"string"==typeof b?b:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function str(a,b){var c,d,e,f,g,h=gap,i=b[a];switch(i&&"object"==typeof i&&"function"==typeof i.toJSON&&(i=i.toJSON(a)),"function"==typeof rep&&(i=rep.call(b,a,i)),typeof i){case"string":return quote(i);case"number":return isFinite(i)?String(i):"null";case"boolean":case"null":return String(i);case"object":if(!i)return"null";if(gap+=indent,g=[],"[object Array]"===Object.prototype.toString.apply(i)){for(f=i.length,c=0;f>c;c+=1)g[c]=str(c,i)||"null";return e=0===g.length?"[]":gap?"[\n"+gap+g.join(",\n"+gap)+"\n"+h+"]":"["+g.join(",")+"]",gap=h,e}if(rep&&"object"==typeof rep)for(f=rep.length,c=0;f>c;c+=1)"string"==typeof rep[c]&&(d=rep[c],e=str(d,i),e&&g.push(quote(d)+(gap?": ":":")+e));else for(d in i)Object.prototype.hasOwnProperty.call(i,d)&&(e=str(d,i),e&&g.push(quote(d)+(gap?": ":":")+e));return e=0===g.length?"{}":gap?"{\n"+gap+g.join(",\n"+gap)+"\n"+h+"}":"{"+g.join(",")+"}",gap=h,e}}"function"!=typeof Date.prototype.toJSON&&(Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){return this.valueOf()});var cx,escapable,gap,indent,meta,rep;"function"!=typeof JSON.stringify&&(escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,meta={"\b":"\\b"," ":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},JSON.stringify=function(a,b,c){var d;if(gap="",indent="","number"==typeof c)for(d=0;c>d;d+=1)indent+=" ";else"string"==typeof c&&(indent=c);if(rep=b,b&&"function"!=typeof b&&("object"!=typeof b||"number"!=typeof b.length))throw new Error("JSON.stringify");return str("",{"":a})}),"function"!=typeof JSON.parse&&(cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,JSON.parse=function(text,reviver){function walk(a,b){var c,d,e=a[b];if(e&&"object"==typeof e)for(c in e)Object.prototype.hasOwnProperty.call(e,c)&&(d=walk(e,c),void 0!==d?e[c]=d:delete e[c]);return reviver.call(a,b,e)}var j;if(text=String(text),cx.lastIndex=0,cx.test(text)&&(text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})),/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return j=eval("("+text+")"),"function"==typeof reviver?walk({"":j},""):j;throw new SyntaxError("JSON.parse")})}();
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.9.4",AlgoliaSearch=function(a,b,c,d,e){var f=this;this.applicationID=a,this.apiKey=b,this.dsn=!0,this.dsnHost=null,this.hosts=[],this.currentHostIndex=0,this.requestTimeoutInMs=2e3,this.extraHeaders=[],this.jsonp=null,this.options={},this.cache={};var g,h="net";if("string"==typeof c)g=c;else{var i=c||{};this.options=i,this._isUndefined(i.method)||(g=i.method),this._isUndefined(i.tld)||(h=i.tld),this._isUndefined(i.dsn)||(this.dsn=i.dsn),this._isUndefined(i.hosts)||(e=i.hosts),this._isUndefined(i.dsnHost)||(this.dsnHost=i.dsnHost),this._isUndefined(i.requestTimeoutInMs)||(this.requestTimeoutInMs=+i.requestTimeoutInMs),this._isUndefined(i.jsonp)||(this.jsonp=i.jsonp)}this._isUndefined(e)&&(e=[this.applicationID+"-1.algolia."+h,this.applicationID+"-2.algolia."+h,this.applicationID+"-3.algolia."+h]),this.host_protocol="http://",this._isUndefined(g)||null===g?this.host_protocol=("https:"==document.location.protocol?"https":"http")+"://":("https"===g||"HTTPS"===g)&&(this.host_protocol="https://");for(var j=0;j<e.length;++j)Math.random()>.5&&this.hosts.reverse(),this.hosts.push(this.host_protocol+e[j]);Math.random()>.5&&this.hosts.reverse(),(this.dsn||null!=this.dsnHost)&&this.hosts.unshift(this.dsnHost?this.host_protocol+this.dsnHost:this.host_protocol+this.applicationID+"-dsn.algolia."+h),this.options.angular&&this.options.angular.$injector.invoke(["$http","$q",function(a,b){f.options.angular.$q=b,f.options.angular.$http=a}])};AlgoliaSearch.JSONPCounter=0,AlgoliaSearch.prototype={deleteIndex:function(a,b){return this._jsonRequest({method:"DELETE",url:"/1/indexes/"+encodeURIComponent(a),callback:b})},moveIndex:function(a,b,c){var d={operation:"move",destination:b};return 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};return this._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(a)+"/operation",body:d,callback:c})},getLogs:function(a,b,c){return 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,b){var c="undefined"!=typeof b?"?page="+b:"";return this._jsonRequest({method:"GET",url:"/1/indexes"+c,callback:a})},initIndex:function(a){return new this.Index(this,a)},listUserKeys:function(a){return this._jsonRequest({method:"GET",url:"/1/keys",callback:a})},getUserKeyACL:function(a,b){return this._jsonRequest({method:"GET",url:"/1/keys/"+a,callback:b})},deleteUserKey:function(a,b){return this._jsonRequest({method:"DELETE",url:"/1/keys/"+a,callback:b})},addUserKey:function(a,b){return this.addUserKeyWithValidity(a,0,0,0,b)},addUserKeyWithValidity:function(a,b,c,d,e){var f={};return f.acl=a,f.validity=b,f.maxQueriesPerIPPerHour=c,f.maxHitsPerQuery=d,this._jsonRequest({method:"POST",url:"/1/keys",body:f,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.tagFilters=a},setUserToken:function(a){this.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:[]},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))return this._sendQueriesBatch(d,a);var f=window.setTimeout(function(){c._sendQueriesBatch(d,a)},b);c.onDelayTrigger=f},setRequestTimeout:function(a){a&&(this.requestTimeoutInMs=parseInt(a,10))},Index:function(a,b){this.indexName=b,this.as=a,this.typeAheadArgs=null,this.typeAheadValueOption=null,this.cache={}},setExtraHeader:function(a,b){this.extraHeaders.push({key:a,value:b})},_sendQueriesBatch:function(a,b){if(null===this.jsonp){var c=this;return this._jsonRequest({cache:this.cache,method:"POST",url:"/1/indexes/*/queries",body:a,callback:function(d,e){d?(c.jsonp=!1,b&&b(d,e)):(c.jsonp=!0,c._sendQueriesBatch(a,b))}})}if(this.jsonp){for(var d="",e=0;e<a.requests.length;++e){var f="/1/indexes/"+encodeURIComponent(a.requests[e].indexName)+"?"+a.requests[e].params;d+=e+"="+encodeURIComponent(f)+"&"}var g={params:d};return this._jsonRequest({cache:this.cache,method:"GET",url:"/1/indexes/*",body:g,callback:b})}return 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,f=null;if(this.options.jQuery?(f=this.options.jQuery.$.Deferred(),f.promise=f.promise()):this.options.angular&&(f=this.options.angular.$q.defer()),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&&setTimeout(function(){c(!0,d[e])},1),f&&f.resolve(d[e]),f&&f.promise;a.successiveRetryCount=0;var g=function(){if(a.successiveRetryCount>=b.hosts.length){var h={message:"Cannot connect the Algolia's Search API. Please send an email to support@algolia.com to report the issue."};return!b._isUndefined(c)&&c&&(a.successiveRetryCount=0,c(!1,h)),void(f&&f.reject(h))}a.callback=function(h,i,j){i&&!b._isUndefined(a.cache)&&(d[e]=j),!i&&h?(b.currentHostIndex=++b.currentHostIndex%b.hosts.length,a.successiveRetryCount+=1,g()):(a.successiveRetryCount=0,f&&(i?f.resolve(j):f.reject(j)),!b._isUndefined(c)&&c&&c(i,j))},a.hostname=b.hosts[b.currentHostIndex],b._jsonRequestByHost(a)};return g(),f&&f.promise},_jsonRequestByHost:function(a){var b=a.hostname+a.url;this.jsonp?this._makeJsonpRequestByHost(b,a):this.options.jQuery?this._makejQueryRequestByHost(b,a):this.options.angular?this._makeAngularRequestByHost(b,a):this._makeXmlHttpRequestByHost(b,a)},_makeAngularRequestByHost:function(a,b){var c=null;this._isUndefined(b.body)||(c=JSON.stringify(b.body)),a+=(-1===a.indexOf("?")?"?":"&")+"X-Algolia-API-Key="+this.apiKey,a+="&X-Algolia-Application-Id="+this.applicationID,this.userToken&&(a+="&X-Algolia-UserToken="+encodeURIComponent(this.userToken)),this.tagFilters&&(a+="&X-Algolia-TagFilters="+encodeURIComponent(this.tagFilters));for(var d=0;d<this.extraHeaders.length;++d)a+="&"+this.extraHeaders[d].key+"="+this.extraHeaders[d].value;this.options.angular.$http({url:a,method:b.method,data:c,cache:!1,timeout:this.requestTimeoutInMs*(b.successiveRetryCount+1)}).then(function(a){b.callback(!1,!0,a.data)},function(a){0===a.status?b.callback(!0,!1,a.data):400==a.status||403===a.status||404===a.status?b.callback(!1,!1,a.data):b.callback(!0,!1,a.data)})},_makejQueryRequestByHost:function(a,b){var c=null;this._isUndefined(b.body)||(c=JSON.stringify(b.body)),a+=(-1===a.indexOf("?")?"?":"&")+"X-Algolia-API-Key="+this.apiKey,a+="&X-Algolia-Application-Id="+this.applicationID,this.userToken&&(a+="&X-Algolia-UserToken="+encodeURIComponent(this.userToken)),this.tagFilters&&(a+="&X-Algolia-TagFilters="+encodeURIComponent(this.tagFilters));for(var d=0;d<this.extraHeaders.length;++d)a+="&"+this.extraHeaders[d].key+"="+this.extraHeaders[d].value;this.options.jQuery.$.ajax(a,{type:b.method,timeout:this.requestTimeoutInMs*(b.successiveRetryCount+1),dataType:"json",data:c,error:function(c,d,e){"timeout"===d?b.callback(!0,!1,{message:"Timeout - Could not connect to endpoint "+a}):400===c.status||403===c.status||404===c.status?b.callback(!1,!1,c.responseJSON):b.callback(!0,!1,{message:e})},success:function(a){b.callback(!1,!0,a)}})},_makeJsonpRequestByHost:function(a,b){if("GET"!==b.method)return void b.callback(!0,!1,{message:"Method "+b.method+" "+a+" is not supported by JSONP."});var c=!1,d=!1;AlgoliaSearch.JSONPCounter+=1;var e,f,g,h=document.getElementsByTagName("head")[0],i=document.createElement("script"),j="algoliaJSONP_"+AlgoliaSearch.JSONPCounter,k=!1;window[j]=function(a){try{delete window[j]}catch(e){window[j]=void 0}if(!d){var f=a&&a.message&&a.status||a&&200,g=200===f,h=!g&&400!==f&&403!==f&&404!==f;c=!0,b.callback(h,g,a)}},i.type="text/javascript",a+="?callback="+j+"&X-Algolia-Application-Id="+this.applicationID+"&X-Algolia-API-Key="+this.apiKey,this.tagFilters&&(a+="&X-Algolia-TagFilters="+encodeURIComponent(this.tagFilters)),this.userToken&&(a+="&X-Algolia-UserToken="+encodeURIComponent(this.userToken));for(var l=0;l<this.extraHeaders.length;++l)a+="&"+this.extraHeaders[l].key+"="+this.extraHeaders[l].value;b.body&&b.body.params&&(a+="&"+b.body.params),e=setTimeout(function(){d=!0,g(),b.callback(!0,!1,{message:"Timeout - Failed to load JSONP script."})},this.requestTimeoutInMs),f=function(){k||d||(k=!0,g(),c||b.callback(!0,!1,{message:"Failed to load JSONP script."}))},g=function(){clearTimeout(e),i.onload=null,i.onreadystatechange=null,i.onerror=null,h.removeChild(i);try{delete window[j],delete window[j+"_loaded"]}catch(a){window[j]=null,window[j+"_loaded"]=null}},i.onreadystatechange=function(){("loaded"===this.readyState||"complete"===this.readyState)&&f()},i.onload=function(){f()},i.onerror=function(){k||d||(g(),b.callback(!0,!1,{message:"Failed to load JSONP script."}))},i.async=!0,i.defer=!0,i.src=a,h.appendChild(i)},_makeXmlHttpRequestByHost:function(a,b){if(!this._support.cors&&!this._support.hasXDomainRequest)return void b.callback(!1,!1,{message:"CORS not supported"});var c,d,e,f=null,g=this._support.cors?new XMLHttpRequest:new XDomainRequest,h=this;this._isUndefined(b.body)||(f=JSON.stringify(b.body)),a+=(-1===a.indexOf("?")?"?":"&")+"X-Algolia-API-Key="+this.apiKey,a+="&X-Algolia-Application-Id="+this.applicationID,this.userToken&&(a+="&X-Algolia-UserToken="+encodeURIComponent(this.userToken)),this.tagFilters&&(a+="&X-Algolia-TagFilters="+encodeURIComponent(this.tagFilters));for(var i=0;i<this.extraHeaders.length;++i)a+="&"+this.extraHeaders[i].key+"="+this.extraHeaders[i].value;e=function(){h._support.timeout||(d=!0,g.abort()),b.callback(!0,!1,{message:"Timeout - Could not connect to endpoint "+a})},g instanceof XMLHttpRequest?g.open(b.method,a,!0):g.open(b.method,a),this._support.cors&&null!==f&&"GET"!==b.method&&g.setRequestHeader("Content-type","application/x-www-form-urlencoded"),g.onload=function(){if(!d){h._support.timeout||clearTimeout(c);var a=null;try{a=JSON.parse(g.responseText)}catch(e){}var f=g.status||a&&a.message&&a.status||a&&200,i=200===f||201===f,j=!i&&400!==f&&403!==f&&404!==f;b.callback(j,i,a)}},this._support.timeout?(g.timeout=this.requestTimeoutInMs*(b.successiveRetryCount+1),g.ontimeout=e):c=setTimeout(e,this.requestTimeoutInMs*(b.successiveRetryCount+1)),g.onerror=function(a){d||(h._support.timeout||clearTimeout(c),b.callback(!0,!1,{message:"Could not connect to host",error:a}))},g.send(f)},_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},_support:{hasXMLHttpRequest:"XMLHttpRequest"in window,hasXDomainRequest:"XDomainRequest"in window,cors:"withCredentials"in new XMLHttpRequest,timeout:"timeout"in new XMLHttpRequest}},AlgoliaSearch.prototype.Index.prototype={clearCache:function(){this.cache={}},addObject:function(a,b,c){var d=this;return this.as._jsonRequest(this.as._isUndefined(c)?{method:"POST",url:"/1/indexes/"+encodeURIComponent(d.indexName),body:a,callback:b}:{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)}return this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/batch",body:d,callback:b})},getObject:function(a,b,c){"[object Array]"!==Object.prototype.toString.call(b)||c||(c=b,b=null);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]}return this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(d.indexName)+"/"+encodeURIComponent(a)+e,callback:b})},partialUpdateObject:function(a,b){var c=this;return 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)}return this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/batch",body:d,callback:b})},saveObject:function(a,b){var c=this;return 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)}return 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 void b(!1,{message:"empty objectID"});var c=this;return this.as._jsonRequest({method:"DELETE",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/"+encodeURIComponent(a),callback:b})},search:function(a,b,c,d){(void 0===a||null===a)&&(a=""),"function"==typeof a&&(b=a,a=""),"object"!=typeof b||!this.as._isUndefined(c)&&c||(c=b,b=null);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))return this._search(f,b);var g=window.setTimeout(function(){e._search(f,b)},d);e.onDelayTrigger=g},browse:function(a,b,c){+b>0&&(this.as._isUndefined(c)||!c)&&(c=b,b=null);var d=this,e="?page="+a;return this.as._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){d(a?b.hits:b&&b.message)},a)}},waitTask:function(a,b){var c=this;return 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;return this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(b.indexName)+"/clear",callback:a})},getSettings:function(a){var b=this;return this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(b.indexName)+"/settings",callback:a})},setSettings:function(a,b){var c=this;return this.as._jsonRequest({method:"PUT",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/settings",body:a,callback:b})},listUserKeys:function(a){var b=this;return this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(b.indexName)+"/keys",callback:a})},getUserKeyACL:function(a,b){var c=this;return this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/keys/"+a,callback:b})},deleteUserKey:function(a,b){var c=this;return this.as._jsonRequest({method:"DELETE",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/keys/"+a,callback:b})},addUserKey:function(a,b){var c=this,d={};return 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={};return 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){var c={params:a};if(null===this.as.jsonp){var d=this;return this.as._jsonRequest({cache:this.cache,method:"POST",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/query",body:c,callback:function(c,e){var f=e&&e.status;c||f&&4===Math.floor(f/100)||1===Math.floor(f/100)?(d.as.jsonp=!1,b&&b(c,e)):(d.as.jsonp=!0,d._search(a,b))}})}return this.as._jsonRequest(this.as.jsonp?{cache:this.cache,method:"GET",url:"/1/indexes/"+encodeURIComponent(this.indexName),body:c,callback:b}:{cache:this.cache,method:"POST",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/query",body:c,callback:b})},as:null,indexName:null,typeAheadArgs:null,typeAheadValueOption:null},function(){var a=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(b,c,d){var e={facets:[],disjunctiveFacets:[],hitsPerPage:20,defaultFacetFilters:[]};this.init(b,c,a({},e,d))},AlgoliaSearchHelper.prototype={init:function(a,b,c){this.client=a,this.index=b,this.options=c,this.page=0,this.refinements={},this.excludes={},this.disjunctiveRefinements={},this.extraQueries=[]},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()},clearRefinements:function(){this.disjunctiveRefinements={},this.refinements={}},addDisjunctiveRefine:function(a,b){this.disjunctiveRefinements=this.disjunctiveRefinements||{},this.disjunctiveRefinements[a]=this.disjunctiveRefinements[a]||{},this.disjunctiveRefinements[a][b]=!0},removeDisjunctiveRefine:function(a,b){this.disjunctiveRefinements=this.disjunctiveRefinements||{},this.disjunctiveRefinements[a]=this.disjunctiveRefinements[a]||{};try{delete this.disjunctiveRefinements[a][b]}catch(c){this.disjunctiveRefinements[a][b]=void 0}},addRefine:function(a,b){var c=a+":"+b;this.refinements=this.refinements||{},this.refinements[c]=!0},removeRefine:function(a,b){var c=a+":"+b;this.refinements=this.refinements||{},this.refinements[c]=!1},addExclude:function(a,b){var c=a+":-"+b;this.excludes=this.excludes||{},this.excludes[c]=!0},removeExclude:function(a,b){var c=a+":-"+b;this.excludes=this.excludes||{},this.excludes[c]=!1},toggleExclude: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.excludes[d]=!this.excludes[d],this.page=0,this._search(),!0}return!1},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},isExcluded:function(a,b){var c=a+":-"+b;return this.excludes[c]?!0:!1},nextPage:function(){this._gotoPage(this.page+1)},previousPage:function(){this.page>0&&this._gotoPage(this.page-1)},gotoPage:function(a){this._gotoPage(a)},setPage:function(a){this.page=a},setIndex:function(a){this.index=a},getIndex:function(){return this.index},clearExtraQueries:function(){this.extraQueries=[]},addExtraQuery:function(a,b,c){this.extraQueries.push({index:a,query:b,params:c||{}})},_gotoPage:function(a){this.page=a,this._search()},_search:function(){this.client.startQueriesBatch(),this.client.addQueryInBatch(this.index,this.q,this._getHitsSearchParams());var a=[],b={},c=0;for(c=0;c<this.options.disjunctiveFacets.length;++c){var d=this.options.disjunctiveFacets[c];this._hasDisjunctiveRefinements(d)?a.push(d):b[d]=!0}for(c=0;c<a.length;++c)this.client.addQueryInBatch(this.index,this.q,this._getDisjunctiveFacetSearchParams(a[c]));for(c=0;c<this.extraQueries.length;++c)this.client.addQueryInBatch(this.extraQueries[c].index,this.extraQueries[c].query,this.extraQueries[c].params);var e=this;this.client.sendQueriesBatch(function(d,f){if(!d)return void e.searchCallback(!1,f);var g=f.results[0];g.disjunctiveFacets=g.disjunctiveFacets||{},g.facets_stats=g.facets_stats||{};for(var h in b)if(g.facets[h]&&!g.disjunctiveFacets[h]){g.disjunctiveFacets[h]=g.facets[h];try{delete g.facets[h]}catch(i){g.facets[h]=void 0}}for(c=0;c<a.length;++c){for(var j in f.results[c+1].facets)if(g.disjunctiveFacets[j]=f.results[c+1].facets[j],e.disjunctiveRefinements[j])for(var k in e.disjunctiveRefinements[j])!g.disjunctiveFacets[j][k]&&e.disjunctiveRefinements[j][k]&&(g.disjunctiveFacets[j][k]=0);for(var l in f.results[c+1].facets_stats)g.facets_stats[l]=f.results[c+1].facets_stats[l]}g.facetStats=g.facets_stats;for(var m in e.excludes)if(e.excludes[m]){var i=m.indexOf(":-"),h=m.slice(0,i),k=m.slice(i+2);g.facets[h]=g.facets[h]||{},g.facets[h][k]||(g.facets[h][k]=0)}if(0===e.extraQueries.length)e.searchCallback(!0,g);else{var n={results:[g]};for(c=0;c<e.extraQueries.length;++c)n.results.push(f.results[1+a.length+c]);e.searchCallback(!0,n)}})},_getHitsSearchParams:function(){var b=[],c=0;for(c=0;c<this.options.facets.length;++c)b.push(this.options.facets[c]);for(c=0;c<this.options.disjunctiveFacets.length;++c){var d=this.options.disjunctiveFacets[c];this._hasDisjunctiveRefinements(d)||b.push(d)}return a({},{hitsPerPage:this.options.hitsPerPage,page:this.page,facets:b,facetFilters:this._getFacetFilters()},this.searchParams)},_getDisjunctiveFacetSearchParams:function(b){return a({},this.searchParams,{hitsPerPage:1,page:0,attributesToRetrieve:[],attributesToHighlight:[],attributesToSnippet:[],facets:b,facetFilters:this._getFacetFilters(b),analytics:!1})},_hasDisjunctiveRefinements:function(a){for(var b in this.disjunctiveRefinements[a])if(this.disjunctiveRefinements[a][b])return!0;return!1},_getFacetFilters:function(a){var b=[];if(this.options.defaultFacetFilters)for(var c=0;c<this.options.defaultFacetFilters.length;++c)b.push(this.options.defaultFacetFilters[c]);for(var d in this.refinements)this.refinements[d]&&b.push(d);for(var d in this.excludes)this.excludes[d]&&b.push(d);for(var e in this.disjunctiveRefinements)if(e!=a){var f=[];for(var g in this.disjunctiveRefinements[e])this.disjunctiveRefinements[e][g]&&f.push(e+":"+g);f.length>0&&b.push(f)}return b}}}(),function(){window.AlgoliaPlaces=function(a,b){this.init(a,b)},AlgoliaPlaces.prototype={init:function(a,b){this.client=new AlgoliaSearch(a,b,"http",!0,["places-1.algolia.io","places-2.algolia.io","places-3.algolia.io"]),this.cache={}},search:function(a,b,c){var d="query="+encodeURIComponent(a);this.client._isUndefined(c)||null==c||(d=this.client._getSearchParams(c,d));var e={params:d,apiKey:this.client.apiKey,appID:this.client.applicationID};this.client._jsonRequest({cache:this.cache,method:"POST",url:"/1/places/query",body:e,callback:b,removeCustomHTTPHeaders:!0})}}}(),"object"!=typeof JSON&&(JSON={}),function(){"use strict";function f(a){return 10>a?"0"+a:a}function quote(a){return escapable.lastIndex=0,escapable.test(a)?'"'+a.replace(escapable,function(a){var b=meta[a];return"string"==typeof b?b:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function str(a,b){var c,d,e,f,g,h=gap,i=b[a];switch(i&&"object"==typeof i&&"function"==typeof i.toJSON&&(i=i.toJSON(a)),"function"==typeof rep&&(i=rep.call(b,a,i)),typeof i){case"string":return quote(i);case"number":return isFinite(i)?String(i):"null";case"boolean":case"null":return String(i);case"object":if(!i)return"null";if(gap+=indent,g=[],"[object Array]"===Object.prototype.toString.apply(i)){for(f=i.length,c=0;f>c;c+=1)g[c]=str(c,i)||"null";return e=0===g.length?"[]":gap?"[\n"+gap+g.join(",\n"+gap)+"\n"+h+"]":"["+g.join(",")+"]",gap=h,e}if(rep&&"object"==typeof rep)for(f=rep.length,c=0;f>c;c+=1)"string"==typeof rep[c]&&(d=rep[c],e=str(d,i),e&&g.push(quote(d)+(gap?": ":":")+e));else for(d in i)Object.prototype.hasOwnProperty.call(i,d)&&(e=str(d,i),e&&g.push(quote(d)+(gap?": ":":")+e));return e=0===g.length?"{}":gap?"{\n"+gap+g.join(",\n"+gap)+"\n"+h+"}":"{"+g.join(",")+"}",gap=h,e}}"function"!=typeof Date.prototype.toJSON&&(Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){return this.valueOf()});var cx,escapable,gap,indent,meta,rep;"function"!=typeof JSON.stringify&&(escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,meta={"\b":"\\b"," ":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},JSON.stringify=function(a,b,c){var d;if(gap="",indent="","number"==typeof c)for(d=0;c>d;d+=1)indent+=" ";else"string"==typeof c&&(indent=c);if(rep=b,b&&"function"!=typeof b&&("object"!=typeof b||"number"!=typeof b.length))throw new Error("JSON.stringify");return str("",{"":a})}),"function"!=typeof JSON.parse&&(cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,JSON.parse=function(text,reviver){function walk(a,b){var c,d,e=a[b];if(e&&"object"==typeof e)for(c in e)Object.prototype.hasOwnProperty.call(e,c)&&(d=walk(e,c),void 0!==d?e[c]=d:delete e[c]);return reviver.call(a,b,e)}var j;if(text=String(text),cx.lastIndex=0,cx.test(text)&&(text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})),/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return j=eval("("+text+")"),"function"==typeof reviver?walk({"":j},""):j;throw new SyntaxError("JSON.parse")})}();
@@ -0,0 +1,7 @@
1
+ /*!
2
+ * typeahead.js 0.10.4
3
+ * https://github.com/twitter/typeahead.js
4
+ * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT
5
+ */
6
+
7
+ !function(a){var b=function(){"use strict";return{isMsie:function(){return/(msie|trident)/i.test(navigator.userAgent)?navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2]:!1},isBlankString:function(a){return!a||/^\s*$/.test(a)},escapeRegExChars:function(a){return a.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")},isString:function(a){return"string"==typeof a},isNumber:function(a){return"number"==typeof a},isArray:a.isArray,isFunction:a.isFunction,isObject:a.isPlainObject,isUndefined:function(a){return"undefined"==typeof a},toStr:function(a){return b.isUndefined(a)||null===a?"":a+""},bind:a.proxy,each:function(b,c){function d(a,b){return c(b,a)}a.each(b,d)},map:a.map,filter:a.grep,every:function(b,c){var d=!0;return b?(a.each(b,function(a,e){return(d=c.call(null,e,a,b))?void 0:!1}),!!d):d},some:function(b,c){var d=!1;return b?(a.each(b,function(a,e){return(d=c.call(null,e,a,b))?!1:void 0}),!!d):d},mixin:a.extend,getUniqueId:function(){var a=0;return function(){return a++}}(),templatify:function(b){function c(){return String(b)}return a.isFunction(b)?b:c},defer:function(a){setTimeout(a,0)},debounce:function(a,b,c){var d,e;return function(){var f,g,h=this,i=arguments;return f=function(){d=null,c||(e=a.apply(h,i))},g=c&&!d,clearTimeout(d),d=setTimeout(f,b),g&&(e=a.apply(h,i)),e}},throttle:function(a,b){var c,d,e,f,g,h;return g=0,h=function(){g=new Date,e=null,f=a.apply(c,d)},function(){var i=new Date,j=b-(i-g);return c=this,d=arguments,0>=j?(clearTimeout(e),e=null,g=i,f=a.apply(c,d)):e||(e=setTimeout(h,j)),f}},noop:function(){}}}(),c="0.10.4",d=function(){"use strict";function a(a){return a=b.toStr(a),a?a.split(/\s+/):[]}function c(a){return a=b.toStr(a),a?a.split(/\W+/):[]}function d(a){return function(){var c=[].slice.call(arguments,0);return function(d){var e=[];return b.each(c,function(c){e=e.concat(a(b.toStr(d[c])))}),e}}}return{nonword:c,whitespace:a,obj:{nonword:d(c),whitespace:d(a)}}}(),e=function(){"use strict";function c(c){this.maxSize=b.isNumber(c)?c:100,this.reset(),this.maxSize<=0&&(this.set=this.get=a.noop)}function d(){this.head=this.tail=null}function e(a,b){this.key=a,this.val=b,this.prev=this.next=null}return b.mixin(c.prototype,{set:function(a,b){var c,d=this.list.tail;this.size>=this.maxSize&&(this.list.remove(d),delete this.hash[d.key]),(c=this.hash[a])?(c.val=b,this.list.moveToFront(c)):(c=new e(a,b),this.list.add(c),this.hash[a]=c,this.size++)},get:function(a){var b=this.hash[a];return b?(this.list.moveToFront(b),b.val):void 0},reset:function(){this.size=0,this.hash={},this.list=new d}}),b.mixin(d.prototype,{add:function(a){this.head&&(a.next=this.head,this.head.prev=a),this.head=a,this.tail=this.tail||a},remove:function(a){a.prev?a.prev.next=a.next:this.head=a.next,a.next?a.next.prev=a.prev:this.tail=a.prev},moveToFront:function(a){this.remove(a),this.add(a)}}),c}(),f=function(){"use strict";function a(a){this.prefix=["__",a,"__"].join(""),this.ttlKey="__ttl__",this.keyMatcher=new RegExp("^"+b.escapeRegExChars(this.prefix))}function c(){return(new Date).getTime()}function d(a){return JSON.stringify(b.isUndefined(a)?null:a)}function e(a){return JSON.parse(a)}var f,g;try{f=window.localStorage,f.setItem("~~~","!"),f.removeItem("~~~")}catch(h){f=null}return g=f&&window.JSON?{_prefix:function(a){return this.prefix+a},_ttlKey:function(a){return this._prefix(a)+this.ttlKey},get:function(a){return this.isExpired(a)&&this.remove(a),e(f.getItem(this._prefix(a)))},set:function(a,e,g){return b.isNumber(g)?f.setItem(this._ttlKey(a),d(c()+g)):f.removeItem(this._ttlKey(a)),f.setItem(this._prefix(a),d(e))},remove:function(a){return f.removeItem(this._ttlKey(a)),f.removeItem(this._prefix(a)),this},clear:function(){var a,b,c=[],d=f.length;for(a=0;d>a;a++)(b=f.key(a)).match(this.keyMatcher)&&c.push(b.replace(this.keyMatcher,""));for(a=c.length;a--;)this.remove(c[a]);return this},isExpired:function(a){var d=e(f.getItem(this._ttlKey(a)));return b.isNumber(d)&&c()>d?!0:!1}}:{get:b.noop,set:b.noop,remove:b.noop,clear:b.noop,isExpired:b.noop},b.mixin(a.prototype,g),a}(),g=function(){"use strict";function c(b){b=b||{},this.cancelled=!1,this.lastUrl=null,this._send=b.transport?d(b.transport):a.ajax,this._get=b.rateLimiter?b.rateLimiter(this._get):this._get,this._cache=b.cache===!1?new e(0):i}function d(c){return function(d,e){function f(a){b.defer(function(){h.resolve(a)})}function g(a){b.defer(function(){h.reject(a)})}var h=a.Deferred();return c(d,e,f,g),h}}var f=0,g={},h=6,i=new e(10);return c.setMaxPendingRequests=function(a){h=a},c.resetCache=function(){i.reset()},b.mixin(c.prototype,{_get:function(a,b,c){function d(b){c&&c(null,b),k._cache.set(a,b)}function e(){c&&c(!0)}function i(){f--,delete g[a],k.onDeckRequestArgs&&(k._get.apply(k,k.onDeckRequestArgs),k.onDeckRequestArgs=null)}var j,k=this;this.cancelled||a!==this.lastUrl||((j=g[a])?j.done(d).fail(e):h>f?(f++,g[a]=this._send(a,b).done(d).fail(e).always(i)):this.onDeckRequestArgs=[].slice.call(arguments,0))},get:function(a,c,d){var e;return b.isFunction(c)&&(d=c,c={}),this.cancelled=!1,this.lastUrl=a,(e=this._cache.get(a))?b.defer(function(){d&&d(null,e)}):this._get(a,c,d),!!e},cancel:function(){this.cancelled=!0}}),c}(),h=function(){"use strict";function c(b){b=b||{},b.datumTokenizer&&b.queryTokenizer||a.error("datumTokenizer and queryTokenizer are both required"),this.datumTokenizer=b.datumTokenizer,this.queryTokenizer=b.queryTokenizer,this.reset()}function d(a){return a=b.filter(a,function(a){return!!a}),a=b.map(a,function(a){return a.toLowerCase()})}function e(){return{ids:[],children:{}}}function f(a){for(var b={},c=[],d=0,e=a.length;e>d;d++)b[a[d]]||(b[a[d]]=!0,c.push(a[d]));return c}function g(a,b){function c(a,b){return a-b}var d=0,e=0,f=[];a=a.sort(c),b=b.sort(c);for(var g=a.length,h=b.length;g>d&&h>e;)a[d]<b[e]?d++:a[d]>b[e]?e++:(f.push(a[d]),d++,e++);return f}return b.mixin(c.prototype,{bootstrap:function(a){this.datums=a.datums,this.trie=a.trie},add:function(a){var c=this;a=b.isArray(a)?a:[a],b.each(a,function(a){var f,g;f=c.datums.push(a)-1,g=d(c.datumTokenizer(a)),b.each(g,function(a){var b,d,g;for(b=c.trie,d=a.split("");g=d.shift();)b=b.children[g]||(b.children[g]=e()),b.ids.push(f)})})},get:function(a){var c,e,h=this;return c=d(this.queryTokenizer(a)),b.each(c,function(a){var b,c,d,f;if(e&&0===e.length)return!1;for(b=h.trie,c=a.split("");b&&(d=c.shift());)b=b.children[d];return b&&0===c.length?(f=b.ids.slice(0),void(e=e?g(e,f):f)):(e=[],!1)}),e?b.map(f(e),function(a){return h.datums[a]}):[]},reset:function(){this.datums=[],this.trie=e()},serialize:function(){return{datums:this.datums,trie:this.trie}}}),c}(),i=function(){"use strict";function d(a){return a.local||null}function e(d){var e,f;return f={url:null,thumbprint:"",ttl:864e5,filter:null,ajax:{}},(e=d.prefetch||null)&&(e=b.isString(e)?{url:e}:e,e=b.mixin(f,e),e.thumbprint=c+e.thumbprint,e.ajax.type=e.ajax.type||"GET",e.ajax.dataType=e.ajax.dataType||"json",!e.url&&a.error("prefetch requires url to be set")),e}function f(c){function d(a){return function(c){return b.debounce(c,a)}}function e(a){return function(c){return b.throttle(c,a)}}var f,g;return g={url:null,cache:!0,wildcard:"%QUERY",replace:null,rateLimitBy:"debounce",rateLimitWait:300,send:null,filter:null,ajax:{}},(f=c.remote||null)&&(f=b.isString(f)?{url:f}:f,f=b.mixin(g,f),f.rateLimiter=/^throttle$/i.test(f.rateLimitBy)?e(f.rateLimitWait):d(f.rateLimitWait),f.ajax.type=f.ajax.type||"GET",f.ajax.dataType=f.ajax.dataType||"json",delete f.rateLimitBy,delete f.rateLimitWait,!f.url&&a.error("remote requires url to be set")),f}return{local:d,prefetch:e,remote:f}}();!function(c){"use strict";function e(b){b&&(b.local||b.prefetch||b.remote)||a.error("one of local, prefetch, or remote is required"),this.limit=b.limit||5,this.sorter=j(b.sorter),this.dupDetector=b.dupDetector||k,this.local=i.local(b),this.prefetch=i.prefetch(b),this.remote=i.remote(b),this.cacheKey=this.prefetch?this.prefetch.cacheKey||this.prefetch.url:null,this.index=new h({datumTokenizer:b.datumTokenizer,queryTokenizer:b.queryTokenizer}),this.storage=this.cacheKey?new f(this.cacheKey):null}function j(a){function c(b){return b.sort(a)}function d(a){return a}return b.isFunction(a)?c:d}function k(){return!1}var l,m;return l=c.Bloodhound,m={data:"data",protocol:"protocol",thumbprint:"thumbprint"},c.Bloodhound=e,e.noConflict=function(){return c.Bloodhound=l,e},e.tokenizers=d,b.mixin(e.prototype,{_loadPrefetch:function(b){function c(a){f.clear(),f.add(b.filter?b.filter(a):a),f._saveToStorage(f.index.serialize(),b.thumbprint,b.ttl)}var d,e,f=this;return(d=this._readFromStorage(b.thumbprint))?(this.index.bootstrap(d),e=a.Deferred().resolve()):e=a.ajax(b.url,b.ajax).done(c),e},_getFromRemote:function(a,b){function c(a,c){b(a?[]:f.remote.filter?f.remote.filter(c):c)}var d,e,f=this;if(this.transport)return a=a||"",e=encodeURIComponent(a),d=this.remote.replace?this.remote.replace(this.remote.url,a):this.remote.url.replace(this.remote.wildcard,e),this.transport.get(d,this.remote.ajax,c)},_cancelLastRemoteRequest:function(){this.transport&&this.transport.cancel()},_saveToStorage:function(a,b,c){this.storage&&(this.storage.set(m.data,a,c),this.storage.set(m.protocol,location.protocol,c),this.storage.set(m.thumbprint,b,c))},_readFromStorage:function(a){var b,c={};return this.storage&&(c.data=this.storage.get(m.data),c.protocol=this.storage.get(m.protocol),c.thumbprint=this.storage.get(m.thumbprint)),b=c.thumbprint!==a||c.protocol!==location.protocol,c.data&&!b?c.data:null},_initialize:function(){function c(){e.add(b.isFunction(f)?f():f)}var d,e=this,f=this.local;return d=this.prefetch?this._loadPrefetch(this.prefetch):a.Deferred().resolve(),f&&d.done(c),this.transport=this.remote?new g(this.remote):null,this.initPromise=d.promise()},initialize:function(a){return!this.initPromise||a?this._initialize():this.initPromise},add:function(a){this.index.add(a)},get:function(a,c){function d(a){var d=f.slice(0);b.each(a,function(a){var c;return c=b.some(d,function(b){return e.dupDetector(a,b)}),!c&&d.push(a),d.length<e.limit}),c&&c(e.sorter(d))}var e=this,f=[],g=!1;f=this.index.get(a),f=this.sorter(f).slice(0,this.limit),f.length<this.limit?g=this._getFromRemote(a,d):this._cancelLastRemoteRequest(),g||(f.length>0||!this.transport)&&c&&c(f)},clear:function(){this.index.reset()},clearPrefetchCache:function(){this.storage&&this.storage.clear()},clearRemoteCache:function(){this.transport&&g.resetCache()},ttAdapter:function(){return b.bind(this.get,this)}}),e}(this)}(window.jQuery);
@@ -0,0 +1,7 @@
1
+ /*!
2
+ * typeahead.js 0.10.4
3
+ * https://github.com/twitter/typeahead.js
4
+ * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT
5
+ */
6
+
7
+ !function(a){var b=function(){"use strict";return{isMsie:function(){return/(msie|trident)/i.test(navigator.userAgent)?navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2]:!1},isBlankString:function(a){return!a||/^\s*$/.test(a)},escapeRegExChars:function(a){return a.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")},isString:function(a){return"string"==typeof a},isNumber:function(a){return"number"==typeof a},isArray:a.isArray,isFunction:a.isFunction,isObject:a.isPlainObject,isUndefined:function(a){return"undefined"==typeof a},toStr:function(a){return b.isUndefined(a)||null===a?"":a+""},bind:a.proxy,each:function(b,c){function d(a,b){return c(b,a)}a.each(b,d)},map:a.map,filter:a.grep,every:function(b,c){var d=!0;return b?(a.each(b,function(a,e){return(d=c.call(null,e,a,b))?void 0:!1}),!!d):d},some:function(b,c){var d=!1;return b?(a.each(b,function(a,e){return(d=c.call(null,e,a,b))?!1:void 0}),!!d):d},mixin:a.extend,getUniqueId:function(){var a=0;return function(){return a++}}(),templatify:function(b){function c(){return String(b)}return a.isFunction(b)?b:c},defer:function(a){setTimeout(a,0)},debounce:function(a,b,c){var d,e;return function(){var f,g,h=this,i=arguments;return f=function(){d=null,c||(e=a.apply(h,i))},g=c&&!d,clearTimeout(d),d=setTimeout(f,b),g&&(e=a.apply(h,i)),e}},throttle:function(a,b){var c,d,e,f,g,h;return g=0,h=function(){g=new Date,e=null,f=a.apply(c,d)},function(){var i=new Date,j=b-(i-g);return c=this,d=arguments,0>=j?(clearTimeout(e),e=null,g=i,f=a.apply(c,d)):e||(e=setTimeout(h,j)),f}},noop:function(){}}}(),c=function(){return{wrapper:'<span class="twitter-typeahead"></span>',dropdown:'<span class="tt-dropdown-menu"></span>',dataset:'<div class="tt-dataset-%CLASS%"></div>',suggestions:'<span class="tt-suggestions"></span>',suggestion:'<div class="tt-suggestion"></div>'}}(),d=function(){"use strict";var a={wrapper:{position:"relative",display:"inline-block"},hint:{position:"absolute",top:"0",left:"0",borderColor:"transparent",boxShadow:"none",opacity:"1"},input:{position:"relative",verticalAlign:"top",backgroundColor:"transparent"},inputWithNoHint:{position:"relative",verticalAlign:"top"},dropdown:{position:"absolute",top:"100%",left:"0",zIndex:"100",display:"none"},suggestions:{display:"block"},suggestion:{whiteSpace:"nowrap",cursor:"pointer"},suggestionChild:{whiteSpace:"normal"},ltr:{left:"0",right:"auto"},rtl:{left:"auto",right:" 0"}};return b.isMsie()&&b.mixin(a.input,{backgroundImage:"url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)"}),b.isMsie()&&b.isMsie()<=7&&b.mixin(a.input,{marginTop:"-1px"}),a}(),e=function(){"use strict";function c(b){b&&b.el||a.error("EventBus initialized without el"),this.$el=a(b.el)}var d="typeahead:";return b.mixin(c.prototype,{trigger:function(a){var b=[].slice.call(arguments,1);this.$el.trigger(d+a,b)}}),c}(),f=function(){"use strict";function a(a,b,c,d){var e;if(!c)return this;for(b=b.split(i),c=d?h(c,d):c,this._callbacks=this._callbacks||{};e=b.shift();)this._callbacks[e]=this._callbacks[e]||{sync:[],async:[]},this._callbacks[e][a].push(c);return this}function b(b,c,d){return a.call(this,"async",b,c,d)}function c(b,c,d){return a.call(this,"sync",b,c,d)}function d(a){var b;if(!this._callbacks)return this;for(a=a.split(i);b=a.shift();)delete this._callbacks[b];return this}function e(a){var b,c,d,e,g;if(!this._callbacks)return this;for(a=a.split(i),d=[].slice.call(arguments,1);(b=a.shift())&&(c=this._callbacks[b]);)e=f(c.sync,this,[b].concat(d)),g=f(c.async,this,[b].concat(d)),e()&&j(g);return this}function f(a,b,c){function d(){for(var d,e=0,f=a.length;!d&&f>e;e+=1)d=a[e].apply(b,c)===!1;return!d}return d}function g(){var a;return a=window.setImmediate?function(a){setImmediate(function(){a()})}:function(a){setTimeout(function(){a()},0)}}function h(a,b){return a.bind?a.bind(b):function(){a.apply(b,[].slice.call(arguments,0))}}var i=/\s+/,j=g();return{onSync:c,onAsync:b,off:d,trigger:e}}(),g=function(a){"use strict";function c(a,c,d){for(var e,f=[],g=0,h=a.length;h>g;g++)f.push(b.escapeRegExChars(a[g]));return e=d?"\\b("+f.join("|")+")\\b":"("+f.join("|")+")",c?new RegExp(e):new RegExp(e,"i")}var d={node:null,pattern:null,tagName:"strong",className:null,wordsOnly:!1,caseSensitive:!1};return function(e){function f(b){var c,d,f;return(c=h.exec(b.data))&&(f=a.createElement(e.tagName),e.className&&(f.className=e.className),d=b.splitText(c.index),d.splitText(c[0].length),f.appendChild(d.cloneNode(!0)),b.parentNode.replaceChild(f,d)),!!c}function g(a,b){for(var c,d=3,e=0;e<a.childNodes.length;e++)c=a.childNodes[e],c.nodeType===d?e+=b(c)?1:0:g(c,b)}var h;e=b.mixin({},d,e),e.node&&e.pattern&&(e.pattern=b.isArray(e.pattern)?e.pattern:[e.pattern],h=c(e.pattern,e.caseSensitive,e.wordsOnly),g(e.node,f))}}(window.document),h=function(){"use strict";function c(c){var e,f,g,i,j=this;c=c||{},c.input||a.error("input is missing"),e=b.bind(this._onBlur,this),f=b.bind(this._onFocus,this),g=b.bind(this._onKeydown,this),i=b.bind(this._onInput,this),this.$hint=a(c.hint),this.$input=a(c.input).on("blur.tt",e).on("focus.tt",f).on("keydown.tt",g),0===this.$hint.length&&(this.setHint=this.getHint=this.clearHint=this.clearHintIfInvalid=b.noop),b.isMsie()?this.$input.on("keydown.tt keypress.tt cut.tt paste.tt",function(a){h[a.which||a.keyCode]||b.defer(b.bind(j._onInput,j,a))}):this.$input.on("input.tt",i),this.query=this.$input.val(),this.$overflowHelper=d(this.$input)}function d(b){return a('<pre aria-hidden="true"></pre>').css({position:"absolute",visibility:"hidden",whiteSpace:"pre",fontFamily:b.css("font-family"),fontSize:b.css("font-size"),fontStyle:b.css("font-style"),fontVariant:b.css("font-variant"),fontWeight:b.css("font-weight"),wordSpacing:b.css("word-spacing"),letterSpacing:b.css("letter-spacing"),textIndent:b.css("text-indent"),textRendering:b.css("text-rendering"),textTransform:b.css("text-transform")}).insertAfter(b)}function e(a,b){return c.normalizeQuery(a)===c.normalizeQuery(b)}function g(a){return a.altKey||a.ctrlKey||a.metaKey||a.shiftKey}var h;return h={9:"tab",27:"esc",37:"left",39:"right",13:"enter",38:"up",40:"down"},c.normalizeQuery=function(a){return(a||"").replace(/^\s*/g,"").replace(/\s{2,}/g," ")},b.mixin(c.prototype,f,{_onBlur:function(){this.resetInputValue(),this.trigger("blurred")},_onFocus:function(){this.trigger("focused")},_onKeydown:function(a){var b=h[a.which||a.keyCode];this._managePreventDefault(b,a),b&&this._shouldTrigger(b,a)&&this.trigger(b+"Keyed",a)},_onInput:function(){this._checkInputValue()},_managePreventDefault:function(a,b){var c,d,e;switch(a){case"tab":d=this.getHint(),e=this.getInputValue(),c=d&&d!==e&&!g(b);break;case"up":case"down":c=!g(b);break;default:c=!1}c&&b.preventDefault()},_shouldTrigger:function(a,b){var c;switch(a){case"tab":c=!g(b);break;default:c=!0}return c},_checkInputValue:function(){var a,b,c;a=this.getInputValue(),b=e(a,this.query),c=b?this.query.length!==a.length:!1,this.query=a,b?c&&this.trigger("whitespaceChanged",this.query):this.trigger("queryChanged",this.query)},focus:function(){this.$input.focus()},blur:function(){this.$input.blur()},getQuery:function(){return this.query},setQuery:function(a){this.query=a},getInputValue:function(){return this.$input.val()},setInputValue:function(a,b){this.$input.val(a),b?this.clearHint():this._checkInputValue()},resetInputValue:function(){this.setInputValue(this.query,!0)},getHint:function(){return this.$hint.val()},setHint:function(a){this.$hint.val(a)},clearHint:function(){this.setHint("")},clearHintIfInvalid:function(){var a,b,c,d;a=this.getInputValue(),b=this.getHint(),c=a!==b&&0===b.indexOf(a),d=""!==a&&c&&!this.hasOverflow(),!d&&this.clearHint()},getLanguageDirection:function(){return(this.$input.css("direction")||"ltr").toLowerCase()},hasOverflow:function(){var a=this.$input.width()-2;return this.$overflowHelper.text(this.getInputValue()),this.$overflowHelper.width()>=a},isCursorAtEnd:function(){var a,c,d;return a=this.$input.val().length,c=this.$input[0].selectionStart,b.isNumber(c)?c===a:document.selection?(d=document.selection.createRange(),d.moveStart("character",-a),a===d.text.length):!0},destroy:function(){this.$hint.off(".tt"),this.$input.off(".tt"),this.$hint=this.$input=this.$overflowHelper=null}}),c}(),i=function(){"use strict";function e(d){d=d||{},d.templates=d.templates||{},d.source||a.error("missing source"),d.name&&!j(d.name)&&a.error("invalid dataset name: "+d.name),this.query=null,this.highlight=!!d.highlight,this.name=d.name||b.getUniqueId(),this.source=d.source,this.displayFn=h(d.display||d.displayKey),this.templates=i(d.templates,this.displayFn),this.$el=a(c.dataset.replace("%CLASS%",this.name))}function h(a){function c(b){return b[a]}return a=a||"value",b.isFunction(a)?a:c}function i(a,c){function d(a){return"<p>"+c(a)+"</p>"}return{empty:a.empty&&b.templatify(a.empty),header:a.header&&b.templatify(a.header),footer:a.footer&&b.templatify(a.footer),suggestion:a.suggestion||d}}function j(a){return/^[_a-zA-Z0-9-]+$/.test(a)}var k="ttDataset",l="ttValue",m="ttDatum";return e.extractDatasetName=function(b){return a(b).data(k)},e.extractValue=function(b){return a(b).data(l)},e.extractDatum=function(b){return a(b).data(m)},b.mixin(e.prototype,f,{_render:function(e,f){function h(){return p.templates.empty({query:e,isEmpty:!0})}function i(){function h(b){var e;return e=a(c.suggestion).append(p.templates.suggestion(b)).data(k,p.name).data(l,p.displayFn(b)).data(m,b),e.children().each(function(){a(this).css(d.suggestionChild)}),e}var i,j;return i=a(c.suggestions).css(d.suggestions),j=b.map(f,h),i.append.apply(i,j),p.highlight&&g({className:"tt-highlight",node:i[0],pattern:e}),i}function j(){return p.templates.header({query:e,isEmpty:!o})}function n(){return p.templates.footer({query:e,isEmpty:!o})}if(this.$el){var o,p=this;this.$el.empty(),o=f&&f.length,!o&&this.templates.empty?this.$el.html(h()).prepend(p.templates.header?j():null).append(p.templates.footer?n():null):o&&this.$el.html(i()).prepend(p.templates.header?j():null).append(p.templates.footer?n():null),this.trigger("rendered")}},getRoot:function(){return this.$el},update:function(a){function b(b){c.canceled||a!==c.query||c._render(a,b)}var c=this;this.query=a,this.canceled=!1,this.source(a,b)},cancel:function(){this.canceled=!0},clear:function(){this.cancel(),this.$el.empty(),this.trigger("rendered")},isEmpty:function(){return this.$el.is(":empty")},destroy:function(){this.$el=null}}),e}(),j=function(){"use strict";function c(c){var d,f,g,h=this;c=c||{},c.menu||a.error("menu is required"),this.isOpen=!1,this.isEmpty=!0,this.datasets=b.map(c.datasets,e),d=b.bind(this._onSuggestionClick,this),f=b.bind(this._onSuggestionMouseEnter,this),g=b.bind(this._onSuggestionMouseLeave,this),this.$menu=a(c.menu).on("click.tt",".tt-suggestion",d).on("mouseenter.tt",".tt-suggestion",f).on("mouseleave.tt",".tt-suggestion",g),b.each(this.datasets,function(a){h.$menu.append(a.getRoot()),a.onSync("rendered",h._onRendered,h)})}function e(a){return new i(a)}return b.mixin(c.prototype,f,{_onSuggestionClick:function(b){this.trigger("suggestionClicked",a(b.currentTarget))},_onSuggestionMouseEnter:function(b){this._removeCursor(),this._setCursor(a(b.currentTarget),!0)},_onSuggestionMouseLeave:function(){this._removeCursor()},_onRendered:function(){function a(a){return a.isEmpty()}this.isEmpty=b.every(this.datasets,a),this.isEmpty?this._hide():this.isOpen&&this._show(),this.trigger("datasetRendered")},_hide:function(){this.$menu.hide()},_show:function(){this.$menu.css("display","block")},_getSuggestions:function(){return this.$menu.find(".tt-suggestion")},_getCursor:function(){return this.$menu.find(".tt-cursor").first()},_setCursor:function(a,b){a.first().addClass("tt-cursor"),!b&&this.trigger("cursorMoved")},_removeCursor:function(){this._getCursor().removeClass("tt-cursor")},_moveCursor:function(a){var b,c,d,e;if(this.isOpen){if(c=this._getCursor(),b=this._getSuggestions(),this._removeCursor(),d=b.index(c)+a,d=(d+1)%(b.length+1)-1,-1===d)return void this.trigger("cursorRemoved");-1>d&&(d=b.length-1),this._setCursor(e=b.eq(d)),this._ensureVisible(e)}},_ensureVisible:function(a){var b,c,d,e;b=a.position().top,c=b+a.outerHeight(!0),d=this.$menu.scrollTop(),e=this.$menu.height()+parseInt(this.$menu.css("paddingTop"),10)+parseInt(this.$menu.css("paddingBottom"),10),0>b?this.$menu.scrollTop(d+b):c>e&&this.$menu.scrollTop(d+(c-e))},close:function(){this.isOpen&&(this.isOpen=!1,this._removeCursor(),this._hide(),this.trigger("closed"))},open:function(){this.isOpen||(this.isOpen=!0,!this.isEmpty&&this._show(),this.trigger("opened"))},setLanguageDirection:function(a){this.$menu.css("ltr"===a?d.ltr:d.rtl)},moveCursorUp:function(){this._moveCursor(-1)},moveCursorDown:function(){this._moveCursor(1)},getDatumForSuggestion:function(a){var b=null;return a.length&&(b={raw:i.extractDatum(a),value:i.extractValue(a),datasetName:i.extractDatasetName(a)}),b},getDatumForCursor:function(){return this.getDatumForSuggestion(this._getCursor().first())},getDatumForTopSuggestion:function(){return this.getDatumForSuggestion(this._getSuggestions().first())},update:function(a){function c(b){b.update(a)}b.each(this.datasets,c)},empty:function(){function a(a){a.clear()}b.each(this.datasets,a),this.isEmpty=!0},isVisible:function(){return this.isOpen&&!this.isEmpty},destroy:function(){function a(a){a.destroy()}this.$menu.off(".tt"),this.$menu=null,b.each(this.datasets,a)}}),c}(),k=function(){"use strict";function f(c){var d,f,i;c=c||{},c.input||a.error("missing input"),this.isActivated=!1,this.autoselect=!!c.autoselect,this.minLength=b.isNumber(c.minLength)?c.minLength:1,this.$node=g(c.input,c.withHint),d=this.$node.find(".tt-dropdown-menu"),f=this.$node.find(".tt-input"),i=this.$node.find(".tt-hint"),f.on("blur.tt",function(a){var c,e,g;c=document.activeElement,e=d.is(c),g=d.has(c).length>0,b.isMsie()&&(e||g)&&(a.preventDefault(),a.stopImmediatePropagation(),b.defer(function(){f.focus()}))}),d.on("mousedown.tt",function(a){a.preventDefault()}),this.eventBus=c.eventBus||new e({el:f}),this.dropdown=new j({menu:d,datasets:c.datasets}).onSync("suggestionClicked",this._onSuggestionClicked,this).onSync("cursorMoved",this._onCursorMoved,this).onSync("cursorRemoved",this._onCursorRemoved,this).onSync("opened",this._onOpened,this).onSync("closed",this._onClosed,this).onAsync("datasetRendered",this._onDatasetRendered,this),this.input=new h({input:f,hint:i}).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),this._setLanguageDirection()}function g(b,e){var f,g,h,j;f=a(b),g=a(c.wrapper).css(d.wrapper),h=a(c.dropdown).css(d.dropdown),j=f.clone().css(d.hint).css(i(f)),j.val("").removeData().addClass("tt-hint").removeAttr("id name placeholder required").prop("readonly",!0).attr({autocomplete:"off",spellcheck:"false",tabindex:-1}),f.data(l,{dir:f.attr("dir"),autocomplete:f.attr("autocomplete"),spellcheck:f.attr("spellcheck"),style:f.attr("style")}),f.addClass("tt-input").attr({autocomplete:"off",spellcheck:!1}).css(e?d.input:d.inputWithNoHint);try{!f.attr("dir")&&f.attr("dir","auto")}catch(k){}return f.wrap(g).parent().prepend(e?j:null).append(h)}function i(a){return{backgroundAttachment:a.css("background-attachment"),backgroundClip:a.css("background-clip"),backgroundColor:a.css("background-color"),backgroundImage:a.css("background-image"),backgroundOrigin:a.css("background-origin"),backgroundPosition:a.css("background-position"),backgroundRepeat:a.css("background-repeat"),backgroundSize:a.css("background-size")}}function k(a){var c=a.find(".tt-input");b.each(c.data(l),function(a,d){b.isUndefined(a)?c.removeAttr(d):c.attr(d,a)}),c.detach().removeData(l).removeClass("tt-input").insertAfter(a),a.remove()}var l="ttAttrs";return b.mixin(f.prototype,{_onSuggestionClicked:function(a,b){var c;(c=this.dropdown.getDatumForSuggestion(b))&&this._select(c)},_onCursorMoved:function(){var a=this.dropdown.getDatumForCursor();this.input.setInputValue(a.value,!0),this.eventBus.trigger("cursorchanged",a.raw,a.datasetName)},_onCursorRemoved:function(){this.input.resetInputValue(),this._updateHint()},_onDatasetRendered:function(){this._updateHint()},_onOpened:function(){this._updateHint(),this.eventBus.trigger("opened")},_onClosed:function(){this.input.clearHint(),this.eventBus.trigger("closed")},_onFocused:function(){this.isActivated=!0,this.dropdown.open()},_onBlurred:function(){this.isActivated=!1,this.dropdown.empty(),this.dropdown.close()},_onEnterKeyed:function(a,b){var c,d;c=this.dropdown.getDatumForCursor(),d=this.dropdown.getDatumForTopSuggestion(),c?(this._select(c),b.preventDefault()):this.autoselect&&d&&(this._select(d),b.preventDefault())},_onTabKeyed:function(a,b){var c;(c=this.dropdown.getDatumForCursor())?(this._select(c),b.preventDefault()):this._autocomplete(!0)},_onEscKeyed:function(){this.dropdown.close(),this.input.resetInputValue()},_onUpKeyed:function(){var a=this.input.getQuery();this.dropdown.isEmpty&&a.length>=this.minLength?this.dropdown.update(a):this.dropdown.moveCursorUp(),this.dropdown.open()},_onDownKeyed:function(){var a=this.input.getQuery();this.dropdown.isEmpty&&a.length>=this.minLength?this.dropdown.update(a):this.dropdown.moveCursorDown(),this.dropdown.open()},_onLeftKeyed:function(){"rtl"===this.dir&&this._autocomplete()},_onRightKeyed:function(){"ltr"===this.dir&&this._autocomplete()},_onQueryChanged:function(a,b){this.input.clearHintIfInvalid(),b.length>=this.minLength?this.dropdown.update(b):this.dropdown.empty(),this.dropdown.open(),this._setLanguageDirection()},_onWhitespaceChanged:function(){this._updateHint(),this.dropdown.open()},_setLanguageDirection:function(){var a;this.dir!==(a=this.input.getLanguageDirection())&&(this.dir=a,this.$node.css("direction",a),this.dropdown.setLanguageDirection(a))},_updateHint:function(){var a,c,d,e,f,g;a=this.dropdown.getDatumForTopSuggestion(),a&&this.dropdown.isVisible()&&!this.input.hasOverflow()?(c=this.input.getInputValue(),d=h.normalizeQuery(c),e=b.escapeRegExChars(d),f=new RegExp("^(?:"+e+")(.+$)","i"),g=f.exec(a.value),g?this.input.setHint(c+g[1]):this.input.clearHint()):this.input.clearHint()},_autocomplete:function(a){var b,c,d,e;b=this.input.getHint(),c=this.input.getQuery(),d=a||this.input.isCursorAtEnd(),b&&c!==b&&d&&(e=this.dropdown.getDatumForTopSuggestion(),e&&this.input.setInputValue(e.value),this.eventBus.trigger("autocompleted",e.raw,e.datasetName))},_select:function(a){this.input.setQuery(a.value),this.input.setInputValue(a.value,!0),this._setLanguageDirection(),this.eventBus.trigger("selected",a.raw,a.datasetName),this.dropdown.close(),b.defer(b.bind(this.dropdown.empty,this.dropdown))},open:function(){this.dropdown.open()},close:function(){this.dropdown.close()},setVal:function(a){a=b.toStr(a),this.isActivated?this.input.setInputValue(a):(this.input.setQuery(a),this.input.setInputValue(a,!0)),this._setLanguageDirection()},getVal:function(){return this.input.getQuery()},destroy:function(){this.input.destroy(),this.dropdown.destroy(),k(this.$node),this.$node=null}}),f}();!function(){"use strict";var c,d,f;c=a.fn.typeahead,d="ttTypeahead",f={initialize:function(c,f){function g(){var g,h,i=a(this);b.each(f,function(a){a.highlight=!!c.highlight}),h=new k({input:i,eventBus:g=new e({el:i}),withHint:b.isUndefined(c.hint)?!0:!!c.hint,minLength:c.minLength,autoselect:c.autoselect,datasets:f}),i.data(d,h)}return f=b.isArray(f)?f:[].slice.call(arguments,1),c=c||{},this.each(g)},open:function(){function b(){var b,c=a(this);(b=c.data(d))&&b.open()}return this.each(b)},close:function(){function b(){var b,c=a(this);(b=c.data(d))&&b.close()}return this.each(b)},val:function(b){function c(){var c,e=a(this);(c=e.data(d))&&c.setVal(b)}function e(a){var b,c;return(b=a.data(d))&&(c=b.getVal()),c}return arguments.length?this.each(c):e(this.first())},destroy:function(){function b(){var b,c=a(this);(b=c.data(d))&&(b.destroy(),c.removeData(d))}return this.each(b)}},a.fn.typeahead=function(b){var c;return f[b]&&"initialize"!==b?(c=this.filter(function(){return!!a(this).data(d)}),f[b].apply(c,[].slice.call(arguments,1))):f.initialize.apply(this,arguments)},a.fn.typeahead.noConflict=function(){return a.fn.typeahead=c,this}}()}(window.jQuery);
@@ -0,0 +1,2667 @@
1
+ /*
2
+ * Copyright (c) 2013 Algolia
3
+ * http://www.algolia.com/
4
+ *
5
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ * of this software and associated documentation files (the "Software"), to deal
7
+ * in the Software without restriction, including without limitation the rights
8
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ * copies of the Software, and to permit persons to whom the Software is
10
+ * furnished to do so, subject to the following conditions:
11
+ *
12
+ * The above copyright notice and this permission notice shall be included in
13
+ * all copies or substantial portions of the Software.
14
+ *
15
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ * THE SOFTWARE.
22
+ */
23
+
24
+ var ALGOLIA_VERSION = '2.9.4';
25
+
26
+ /*
27
+ * Copyright (c) 2013 Algolia
28
+ * http://www.algolia.com/
29
+ *
30
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
31
+ * of this software and associated documentation files (the "Software"), to deal
32
+ * in the Software without restriction, including without limitation the rights
33
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
34
+ * copies of the Software, and to permit persons to whom the Software is
35
+ * furnished to do so, subject to the following conditions:
36
+ *
37
+ * The above copyright notice and this permission notice shall be included in
38
+ * all copies or substantial portions of the Software.
39
+ *
40
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
41
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
42
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
43
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
44
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
45
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
46
+ * THE SOFTWARE.
47
+ */
48
+
49
+ /*
50
+ * Algolia Search library initialization
51
+ * @param applicationID the application ID you have in your admin interface
52
+ * @param apiKey a valid API key for the service
53
+ * @param methodOrOptions the hash of parameters for initialization. It can contains:
54
+ * - method (optional) specify if the protocol used is http or https (http by default to make the first search query faster).
55
+ * You need to use https is you are doing something else than just search queries.
56
+ * - hosts (optional) the list of hosts that you have received for the service
57
+ * - dsn (optional) set to true if your account has the Distributed Search Option
58
+ * - dsnHost (optional) override the automatic computation of dsn hostname
59
+ */
60
+ var AlgoliaSearch = function(applicationID, apiKey, methodOrOptions, resolveDNS, hosts) {
61
+ var self = this;
62
+ this.applicationID = applicationID;
63
+ this.apiKey = apiKey;
64
+ this.dsn = true;
65
+ this.dsnHost = null;
66
+ this.hosts = [];
67
+ this.currentHostIndex = 0;
68
+ this.requestTimeoutInMs = 2000;
69
+ this.extraHeaders = [];
70
+ this.jsonp = null;
71
+ this.options = {};
72
+
73
+ // make sure every client instance has it's own cache
74
+ this.cache = {};
75
+
76
+ var method;
77
+ var tld = 'net';
78
+ if (typeof methodOrOptions === 'string') { // Old initialization
79
+ method = methodOrOptions;
80
+ } else {
81
+ // Take all option from the hash
82
+ var options = methodOrOptions || {};
83
+ this.options = options;
84
+ if (!this._isUndefined(options.method)) {
85
+ method = options.method;
86
+ }
87
+ if (!this._isUndefined(options.tld)) {
88
+ tld = options.tld;
89
+ }
90
+ if (!this._isUndefined(options.dsn)) {
91
+ this.dsn = options.dsn;
92
+ }
93
+ if (!this._isUndefined(options.hosts)) {
94
+ hosts = options.hosts;
95
+ }
96
+ if (!this._isUndefined(options.dsnHost)) {
97
+ this.dsnHost = options.dsnHost;
98
+ }
99
+ if (!this._isUndefined(options.requestTimeoutInMs)) {
100
+ this.requestTimeoutInMs = +options.requestTimeoutInMs;
101
+ }
102
+ if (!this._isUndefined(options.jsonp)) {
103
+ this.jsonp = options.jsonp;
104
+ }
105
+ }
106
+ // If hosts is undefined, initialize it with applicationID
107
+ if (this._isUndefined(hosts)) {
108
+ hosts = [
109
+ this.applicationID + '-1.algolia.' + tld,
110
+ this.applicationID + '-2.algolia.' + tld,
111
+ this.applicationID + '-3.algolia.' + tld
112
+ ];
113
+ }
114
+ // detect is we use http or https
115
+ this.host_protocol = 'http://';
116
+ if (this._isUndefined(method) || method === null) {
117
+ this.host_protocol = ('https:' == document.location.protocol ? 'https' : 'http') + '://';
118
+ } else if (method === 'https' || method === 'HTTPS') {
119
+ this.host_protocol = 'https://';
120
+ }
121
+ // Add hosts in random order
122
+ for (var i = 0; i < hosts.length; ++i) {
123
+ if (Math.random() > 0.5) {
124
+ this.hosts.reverse();
125
+ }
126
+ this.hosts.push(this.host_protocol + hosts[i]);
127
+ }
128
+ if (Math.random() > 0.5) {
129
+ this.hosts.reverse();
130
+ }
131
+ // then add Distributed Search Network host if there is one
132
+ if (this.dsn || this.dsnHost != null) {
133
+ if (this.dsnHost) {
134
+ this.hosts.unshift(this.host_protocol + this.dsnHost);
135
+ } else {
136
+ this.hosts.unshift(this.host_protocol + this.applicationID + '-dsn.algolia.' + tld);
137
+ }
138
+ }
139
+ // angular dependencies injection
140
+ if (this.options.angular) {
141
+ this.options.angular.$injector.invoke(['$http', '$q', function ($http, $q) {
142
+ self.options.angular.$q = $q;
143
+ self.options.angular.$http = $http;
144
+ }]);
145
+ }
146
+ };
147
+
148
+ // This holds the number of JSONP requests done accross clients
149
+ // It's used as part of the ?callback=JSONP_$JSONPCounter when we do JSONP requests
150
+ AlgoliaSearch.JSONPCounter = 0;
151
+
152
+ function AlgoliaExplainResults(hit, titleAttribute, otherAttributes) {
153
+
154
+ function _getHitExplanationForOneAttr_recurse(obj, foundWords) {
155
+ var res = [];
156
+ if (typeof obj === 'object' && 'matchedWords' in obj && 'value' in obj) {
157
+ var match = false;
158
+ for (var j = 0; j < obj.matchedWords.length; ++j) {
159
+ var word = obj.matchedWords[j];
160
+ if (!(word in foundWords)) {
161
+ foundWords[word] = 1;
162
+ match = true;
163
+ }
164
+ }
165
+ if (match) {
166
+ res.push(obj.value);
167
+ }
168
+ } else if (Object.prototype.toString.call(obj) === '[object Array]') {
169
+ for (var i = 0; i < obj.length; ++i) {
170
+ var array = _getHitExplanationForOneAttr_recurse(obj[i], foundWords);
171
+ res = res.concat(array);
172
+ }
173
+ } else if (typeof obj === 'object') {
174
+ for (var prop in obj) {
175
+ if (obj.hasOwnProperty(prop)){
176
+ res = res.concat(_getHitExplanationForOneAttr_recurse(obj[prop], foundWords));
177
+ }
178
+ }
179
+ }
180
+ return res;
181
+ }
182
+
183
+ function _getHitExplanationForOneAttr(hit, foundWords, attr) {
184
+ var base = hit._highlightResult || hit;
185
+ if (attr.indexOf('.') === -1) {
186
+ if (attr in base) {
187
+ return _getHitExplanationForOneAttr_recurse(base[attr], foundWords);
188
+ }
189
+ return [];
190
+ }
191
+ var array = attr.split('.');
192
+ var obj = base;
193
+ for (var i = 0; i < array.length; ++i) {
194
+ if (Object.prototype.toString.call(obj) === '[object Array]') {
195
+ var res = [];
196
+ for (var j = 0; j < obj.length; ++j) {
197
+ res = res.concat(_getHitExplanationForOneAttr(obj[j], foundWords, array.slice(i).join('.')));
198
+ }
199
+ return res;
200
+ }
201
+ if (array[i] in obj) {
202
+ obj = obj[array[i]];
203
+ } else {
204
+ return [];
205
+ }
206
+ }
207
+ return _getHitExplanationForOneAttr_recurse(obj, foundWords);
208
+ }
209
+
210
+ var res = {};
211
+ var foundWords = {};
212
+ var title = _getHitExplanationForOneAttr(hit, foundWords, titleAttribute);
213
+ res.title = (title.length > 0) ? title[0] : '';
214
+ res.subtitles = [];
215
+
216
+ if (typeof otherAttributes !== 'undefined') {
217
+ for (var i = 0; i < otherAttributes.length; ++i) {
218
+ var attr = _getHitExplanationForOneAttr(hit, foundWords, otherAttributes[i]);
219
+ for (var j = 0; j < attr.length; ++j) {
220
+ res.subtitles.push({ attr: otherAttributes[i], value: attr[j] });
221
+ }
222
+ }
223
+ }
224
+ return res;
225
+ }
226
+
227
+
228
+ AlgoliaSearch.prototype = {
229
+ /*
230
+ * Delete an index
231
+ *
232
+ * @param indexName the name of index to delete
233
+ * @param callback the result callback with two arguments
234
+ * success: boolean set to true if the request was successfull
235
+ * content: the server answer that contains the task ID
236
+ */
237
+ deleteIndex: function(indexName, callback) {
238
+ return this._jsonRequest({ method: 'DELETE',
239
+ url: '/1/indexes/' + encodeURIComponent(indexName),
240
+ callback: callback });
241
+ },
242
+ /**
243
+ * Move an existing index.
244
+ * @param srcIndexName the name of index to copy.
245
+ * @param dstIndexName the new index name that will contains a copy of srcIndexName (destination will be overriten if it already exist).
246
+ * @param callback the result callback with two arguments
247
+ * success: boolean set to true if the request was successfull
248
+ * content: the server answer that contains the task ID
249
+ */
250
+ moveIndex: function(srcIndexName, dstIndexName, callback) {
251
+ var postObj = {operation: 'move', destination: dstIndexName};
252
+ return this._jsonRequest({ method: 'POST',
253
+ url: '/1/indexes/' + encodeURIComponent(srcIndexName) + '/operation',
254
+ body: postObj,
255
+ callback: callback });
256
+
257
+ },
258
+ /**
259
+ * Copy an existing index.
260
+ * @param srcIndexName the name of index to copy.
261
+ * @param dstIndexName the new index name that will contains a copy of srcIndexName (destination will be overriten if it already exist).
262
+ * @param callback the result callback with two arguments
263
+ * success: boolean set to true if the request was successfull
264
+ * content: the server answer that contains the task ID
265
+ */
266
+ copyIndex: function(srcIndexName, dstIndexName, callback) {
267
+ var postObj = {operation: 'copy', destination: dstIndexName};
268
+ return this._jsonRequest({ method: 'POST',
269
+ url: '/1/indexes/' + encodeURIComponent(srcIndexName) + '/operation',
270
+ body: postObj,
271
+ callback: callback });
272
+ },
273
+ /**
274
+ * Return last log entries.
275
+ * @param offset Specify the first entry to retrieve (0-based, 0 is the most recent log entry).
276
+ * @param length Specify the maximum number of entries to retrieve starting at offset. Maximum allowed value: 1000.
277
+ * @param callback the result callback with two arguments
278
+ * success: boolean set to true if the request was successfull
279
+ * content: the server answer that contains the task ID
280
+ */
281
+ getLogs: function(callback, offset, length) {
282
+ if (this._isUndefined(offset)) {
283
+ offset = 0;
284
+ }
285
+ if (this._isUndefined(length)) {
286
+ length = 10;
287
+ }
288
+
289
+ return this._jsonRequest({ method: 'GET',
290
+ url: '/1/logs?offset=' + offset + '&length=' + length,
291
+ callback: callback });
292
+ },
293
+ /*
294
+ * List all existing indexes (paginated)
295
+ *
296
+ * @param callback the result callback with two arguments
297
+ * success: boolean set to true if the request was successfull
298
+ * content: the server answer with index list or error description if success is false.
299
+ * @param page The page to retrieve, starting at 0.
300
+ */
301
+ listIndexes: function(callback, page) {
302
+ var params = typeof page !== 'undefined' ? '?page=' + page : '';
303
+ return this._jsonRequest({ method: 'GET',
304
+ url: '/1/indexes' + params,
305
+ callback: callback });
306
+ },
307
+
308
+ /*
309
+ * Get the index object initialized
310
+ *
311
+ * @param indexName the name of index
312
+ * @param callback the result callback with one argument (the Index instance)
313
+ */
314
+ initIndex: function(indexName) {
315
+ return new this.Index(this, indexName);
316
+ },
317
+ /*
318
+ * List all existing user keys with their associated ACLs
319
+ *
320
+ * @param callback the result callback with two arguments
321
+ * success: boolean set to true if the request was successfull
322
+ * content: the server answer with user keys list or error description if success is false.
323
+ */
324
+ listUserKeys: function(callback) {
325
+ return this._jsonRequest({ method: 'GET',
326
+ url: '/1/keys',
327
+ callback: callback });
328
+ },
329
+ /*
330
+ * Get ACL of a user key
331
+ *
332
+ * @param callback the result callback with two arguments
333
+ * success: boolean set to true if the request was successfull
334
+ * content: the server answer with user keys list or error description if success is false.
335
+ */
336
+ getUserKeyACL: function(key, callback) {
337
+ return this._jsonRequest({ method: 'GET',
338
+ url: '/1/keys/' + key,
339
+ callback: callback });
340
+ },
341
+ /*
342
+ * Delete an existing user key
343
+ *
344
+ * @param callback the result callback with two arguments
345
+ * success: boolean set to true if the request was successfull
346
+ * content: the server answer with user keys list or error description if success is false.
347
+ */
348
+ deleteUserKey: function(key, callback) {
349
+ return this._jsonRequest({ method: 'DELETE',
350
+ url: '/1/keys/' + key,
351
+ callback: callback });
352
+ },
353
+ /*
354
+ * Add an existing user key
355
+ *
356
+ * @param acls the list of ACL for this key. Defined by an array of strings that
357
+ * can contains the following values:
358
+ * - search: allow to search (https and http)
359
+ * - addObject: allows to add/update an object in the index (https only)
360
+ * - deleteObject : allows to delete an existing object (https only)
361
+ * - deleteIndex : allows to delete index content (https only)
362
+ * - settings : allows to get index settings (https only)
363
+ * - editSettings : allows to change index settings (https only)
364
+ * @param callback the result callback with two arguments
365
+ * success: boolean set to true if the request was successfull
366
+ * content: the server answer with user keys list or error description if success is false.
367
+ */
368
+ addUserKey: function(acls, callback) {
369
+ return this.addUserKeyWithValidity(acls, 0, 0, 0, callback);
370
+ },
371
+ /*
372
+ * Add an existing user key
373
+ *
374
+ * @param acls the list of ACL for this key. Defined by an array of strings that
375
+ * can contains the following values:
376
+ * - search: allow to search (https and http)
377
+ * - addObject: allows to add/update an object in the index (https only)
378
+ * - deleteObject : allows to delete an existing object (https only)
379
+ * - deleteIndex : allows to delete index content (https only)
380
+ * - settings : allows to get index settings (https only)
381
+ * - editSettings : allows to change index settings (https only)
382
+ * @param validity the number of seconds after which the key will be automatically removed (0 means no time limit for this key)
383
+ * @param maxQueriesPerIPPerHour Specify the maximum number of API calls allowed from an IP address per hour.
384
+ * @param maxHitsPerQuery Specify the maximum number of hits this API key can retrieve in one call.
385
+ * @param callback the result callback with two arguments
386
+ * success: boolean set to true if the request was successfull
387
+ * content: the server answer with user keys list or error description if success is false.
388
+ */
389
+ addUserKeyWithValidity: function(acls, validity, maxQueriesPerIPPerHour, maxHitsPerQuery, callback) {
390
+ var aclsObject = {};
391
+ aclsObject.acl = acls;
392
+ aclsObject.validity = validity;
393
+ aclsObject.maxQueriesPerIPPerHour = maxQueriesPerIPPerHour;
394
+ aclsObject.maxHitsPerQuery = maxHitsPerQuery;
395
+ return this._jsonRequest({ method: 'POST',
396
+ url: '/1/keys',
397
+ body: aclsObject,
398
+ callback: callback });
399
+ },
400
+
401
+ /**
402
+ * Set the extra security tagFilters header
403
+ * @param {string|array} tags The list of tags defining the current security filters
404
+ */
405
+ setSecurityTags: function(tags) {
406
+ if (Object.prototype.toString.call(tags) === '[object Array]') {
407
+ var strTags = [];
408
+ for (var i = 0; i < tags.length; ++i) {
409
+ if (Object.prototype.toString.call(tags[i]) === '[object Array]') {
410
+ var oredTags = [];
411
+ for (var j = 0; j < tags[i].length; ++j) {
412
+ oredTags.push(tags[i][j]);
413
+ }
414
+ strTags.push('(' + oredTags.join(',') + ')');
415
+ } else {
416
+ strTags.push(tags[i]);
417
+ }
418
+ }
419
+ tags = strTags.join(',');
420
+ }
421
+ this.tagFilters = tags;
422
+ },
423
+
424
+ /**
425
+ * Set the extra user token header
426
+ * @param {string} userToken The token identifying a uniq user (used to apply rate limits)
427
+ */
428
+ setUserToken: function(userToken) {
429
+ this.userToken = userToken;
430
+ },
431
+
432
+ /*
433
+ * Initialize a new batch of search queries
434
+ */
435
+ startQueriesBatch: function() {
436
+ this.batch = [];
437
+ },
438
+ /*
439
+ * Add a search query in the batch
440
+ *
441
+ * @param query the full text query
442
+ * @param args (optional) if set, contains an object with query parameters:
443
+ * - attributes: an array of object attribute names to retrieve
444
+ * (if not set all attributes are retrieve)
445
+ * - attributesToHighlight: an array of object attribute names to highlight
446
+ * (if not set indexed attributes are highlighted)
447
+ * - minWordSizefor1Typo: the minimum number of characters to accept one typo.
448
+ * Defaults to 3.
449
+ * - minWordSizefor2Typos: the minimum number of characters to accept two typos.
450
+ * Defaults to 7.
451
+ * - getRankingInfo: if set, the result hits will contain ranking information in
452
+ * _rankingInfo attribute
453
+ * - page: (pagination parameter) page to retrieve (zero base). Defaults to 0.
454
+ * - hitsPerPage: (pagination parameter) number of hits per page. Defaults to 10.
455
+ */
456
+ addQueryInBatch: function(indexName, query, args) {
457
+ var params = 'query=' + encodeURIComponent(query);
458
+ if (!this._isUndefined(args) && args !== null) {
459
+ params = this._getSearchParams(args, params);
460
+ }
461
+ this.batch.push({ indexName: indexName, params: params });
462
+ },
463
+ /*
464
+ * Clear all queries in cache
465
+ */
466
+ clearCache: function() {
467
+ this.cache = {};
468
+ },
469
+ /*
470
+ * Launch the batch of queries using XMLHttpRequest.
471
+ * (Optimized for browser using a POST query to minimize number of OPTIONS queries)
472
+ *
473
+ * @param callback the function that will receive results
474
+ * @param delay (optional) if set, wait for this delay (in ms) and only send the batch if there was no other in the meantime.
475
+ */
476
+ sendQueriesBatch: function(callback, delay) {
477
+ var as = this;
478
+ var params = {requests: []};
479
+ for (var i = 0; i < as.batch.length; ++i) {
480
+ params.requests.push(as.batch[i]);
481
+ }
482
+ window.clearTimeout(as.onDelayTrigger);
483
+ if (!this._isUndefined(delay) && delay !== null && delay > 0) {
484
+ var onDelayTrigger = window.setTimeout( function() {
485
+ as._sendQueriesBatch(params, callback);
486
+ }, delay);
487
+ as.onDelayTrigger = onDelayTrigger;
488
+ } else {
489
+ return this._sendQueriesBatch(params, callback);
490
+ }
491
+ },
492
+
493
+ /**
494
+ * Set the number of milliseconds a request can take before automatically being terminated.
495
+ *
496
+ * @param {Number} milliseconds
497
+ */
498
+ setRequestTimeout: function(milliseconds)
499
+ {
500
+ if (milliseconds) {
501
+ this.requestTimeoutInMs = parseInt(milliseconds, 10);
502
+ }
503
+ },
504
+
505
+ /*
506
+ * Index class constructor.
507
+ * You should not use this method directly but use initIndex() function
508
+ */
509
+ Index: function(algoliasearch, indexName) {
510
+ this.indexName = indexName;
511
+ this.as = algoliasearch;
512
+ this.typeAheadArgs = null;
513
+ this.typeAheadValueOption = null;
514
+
515
+ // make sure every index instance has it's own cache
516
+ this.cache = {};
517
+ },
518
+ /**
519
+ * Add an extra field to the HTTP request
520
+ *
521
+ * @param key the header field name
522
+ * @param value the header field value
523
+ */
524
+ setExtraHeader: function(key, value) {
525
+ this.extraHeaders.push({ key: key, value: value});
526
+ },
527
+
528
+ _sendQueriesBatch: function(params, callback) {
529
+ if (this.jsonp === null) {
530
+ var self = this;
531
+ return this._jsonRequest({ cache: this.cache,
532
+ method: 'POST',
533
+ url: '/1/indexes/*/queries',
534
+ body: params,
535
+ callback: function(success, content) {
536
+ if (!success) {
537
+ // retry first with JSONP
538
+ self.jsonp = true;
539
+ self._sendQueriesBatch(params, callback);
540
+ } else {
541
+ self.jsonp = false;
542
+ callback && callback(success, content);
543
+ }
544
+ }
545
+ });
546
+ } else if (this.jsonp) {
547
+ var jsonpParams = '';
548
+ for (var i = 0; i < params.requests.length; ++i) {
549
+ var q = '/1/indexes/' + encodeURIComponent(params.requests[i].indexName) + '?' + params.requests[i].params;
550
+ jsonpParams += i + '=' + encodeURIComponent(q) + '&';
551
+ }
552
+ var pObj = {params: jsonpParams};
553
+ return this._jsonRequest({ cache: this.cache,
554
+ method: 'GET',
555
+ url: '/1/indexes/*',
556
+ body: pObj,
557
+ callback: callback });
558
+ } else {
559
+ return this._jsonRequest({ cache: this.cache,
560
+ method: 'POST',
561
+ url: '/1/indexes/*/queries',
562
+ body: params,
563
+ callback: callback});
564
+ }
565
+ },
566
+ /*
567
+ * Wrapper that try all hosts to maximize the quality of service
568
+ */
569
+ _jsonRequest: function(opts) {
570
+ var self = this;
571
+ var callback = opts.callback;
572
+ var cache = null;
573
+ var cacheID = opts.url;
574
+ var deferred = null;
575
+ if (this.options.jQuery) {
576
+ deferred = this.options.jQuery.$.Deferred();
577
+ deferred.promise = deferred.promise(); // promise is a property in angular
578
+ } else if (this.options.angular) {
579
+ deferred = this.options.angular.$q.defer();
580
+ }
581
+
582
+ if (!this._isUndefined(opts.body)) {
583
+ cacheID = opts.url + '_body_' + JSON.stringify(opts.body);
584
+ }
585
+ if (!this._isUndefined(opts.cache)) {
586
+ cache = opts.cache;
587
+ if (!this._isUndefined(cache[cacheID])) {
588
+ if (!this._isUndefined(callback) && callback) {
589
+ setTimeout(function () { callback(true, cache[cacheID]); }, 1);
590
+ }
591
+ deferred && deferred.resolve(cache[cacheID]);
592
+ return deferred && deferred.promise;
593
+ }
594
+ }
595
+
596
+ opts.successiveRetryCount = 0;
597
+ var impl = function() {
598
+ if (opts.successiveRetryCount >= self.hosts.length) {
599
+ var error = { message: 'Cannot connect the Algolia\'s Search API. Please send an email to support@algolia.com to report the issue.' };
600
+ if (!self._isUndefined(callback) && callback) {
601
+ opts.successiveRetryCount = 0;
602
+ callback(false, error);
603
+ }
604
+ deferred && deferred.reject(error);
605
+ return;
606
+ }
607
+ opts.callback = function(retry, success, body) {
608
+ if (success && !self._isUndefined(opts.cache)) {
609
+ cache[cacheID] = body;
610
+ }
611
+ if (!success && retry) {
612
+ self.currentHostIndex = ++self.currentHostIndex % self.hosts.length;
613
+ opts.successiveRetryCount += 1;
614
+ impl();
615
+ } else {
616
+ opts.successiveRetryCount = 0;
617
+ deferred && (success ? deferred.resolve(body) : deferred.reject(body));
618
+ if (!self._isUndefined(callback) && callback) {
619
+ callback(success, body);
620
+ }
621
+ }
622
+ };
623
+ opts.hostname = self.hosts[self.currentHostIndex];
624
+ self._jsonRequestByHost(opts);
625
+ };
626
+ impl();
627
+
628
+ return deferred && deferred.promise;
629
+ },
630
+
631
+ _jsonRequestByHost: function(opts) {
632
+ var self = this;
633
+ var url = opts.hostname + opts.url;
634
+
635
+ if (this.jsonp) {
636
+ this._makeJsonpRequestByHost(url, opts);
637
+ } else if (this.options.jQuery) {
638
+ this._makejQueryRequestByHost(url, opts);
639
+ } else if (this.options.angular) {
640
+ this._makeAngularRequestByHost(url, opts);
641
+ } else {
642
+ this._makeXmlHttpRequestByHost(url, opts);
643
+ }
644
+ },
645
+
646
+ /**
647
+ * Make a $http
648
+ *
649
+ * @param url request url (includes endpoint and path)
650
+ * @param opts all request opts
651
+ */
652
+ _makeAngularRequestByHost: function(url, opts) {
653
+ var self = this;
654
+ var body = null;
655
+
656
+ if (!this._isUndefined(opts.body)) {
657
+ body = JSON.stringify(opts.body);
658
+ }
659
+
660
+ url += ((url.indexOf('?') === -1) ? '?' : '&') + 'X-Algolia-API-Key=' + this.apiKey;
661
+ url += '&X-Algolia-Application-Id=' + this.applicationID;
662
+ if (this.userToken) {
663
+ url += '&X-Algolia-UserToken=' + encodeURIComponent(this.userToken);
664
+ }
665
+ if (this.tagFilters) {
666
+ url += '&X-Algolia-TagFilters=' + encodeURIComponent(this.tagFilters);
667
+ }
668
+ for (var i = 0; i < this.extraHeaders.length; ++i) {
669
+ url += '&' + this.extraHeaders[i].key + '=' + this.extraHeaders[i].value;
670
+ }
671
+ this.options.angular.$http({
672
+ url: url,
673
+ method: opts.method,
674
+ data: body,
675
+ cache: false,
676
+ timeout: (this.requestTimeoutInMs * (opts.successiveRetryCount + 1))
677
+ }).then(function(response) {
678
+ opts.callback(false, true, response.data);
679
+ }, function(response) {
680
+ if (response.status === 0) {
681
+ // xhr.timeout is not handled by Angular.js right now
682
+ // let's retry
683
+ opts.callback(true, false, response.data);
684
+ } else if (response.status == 400 || response.status === 403 || response.status === 404) {
685
+ opts.callback(false, false, response.data);
686
+ } else {
687
+ opts.callback(true, false, response.data);
688
+ }
689
+ });
690
+ },
691
+
692
+ /**
693
+ * Make a $.ajax
694
+ *
695
+ * @param url request url (includes endpoint and path)
696
+ * @param opts all request opts
697
+ */
698
+ _makejQueryRequestByHost: function(url, opts) {
699
+ var self = this;
700
+ var body = null;
701
+
702
+ if (!this._isUndefined(opts.body)) {
703
+ body = JSON.stringify(opts.body);
704
+ }
705
+
706
+ url += ((url.indexOf('?') === -1) ? '?' : '&') + 'X-Algolia-API-Key=' + this.apiKey;
707
+ url += '&X-Algolia-Application-Id=' + this.applicationID;
708
+ if (this.userToken) {
709
+ url += '&X-Algolia-UserToken=' + encodeURIComponent(this.userToken);
710
+ }
711
+ if (this.tagFilters) {
712
+ url += '&X-Algolia-TagFilters=' + encodeURIComponent(this.tagFilters);
713
+ }
714
+ for (var i = 0; i < this.extraHeaders.length; ++i) {
715
+ url += '&' + this.extraHeaders[i].key + '=' + this.extraHeaders[i].value;
716
+ }
717
+ this.options.jQuery.$.ajax(url, {
718
+ type: opts.method,
719
+ timeout: (this.requestTimeoutInMs * (opts.successiveRetryCount + 1)),
720
+ dataType: 'json',
721
+ data: body,
722
+ error: function(xhr, textStatus, error) {
723
+ if (textStatus === 'timeout') {
724
+ opts.callback(true, false, { 'message': 'Timeout - Could not connect to endpoint ' + url } );
725
+ } else if (xhr.status === 400 || xhr.status === 403 || xhr.status === 404) {
726
+ opts.callback(false, false, xhr.responseJSON );
727
+ } else {
728
+ opts.callback(true, false, { 'message': error } );
729
+ }
730
+ },
731
+ success: function(data, textStatus, xhr) {
732
+ opts.callback(false, true, data);
733
+ }
734
+ });
735
+ },
736
+
737
+ /**
738
+ * Make a JSONP request
739
+ *
740
+ * @param url request url (includes endpoint and path)
741
+ * @param opts all request options
742
+ */
743
+ _makeJsonpRequestByHost: function(url, opts) {
744
+ if (opts.method !== 'GET') {
745
+ opts.callback(true, false, { 'message': 'Method ' + opts.method + ' ' + url + ' is not supported by JSONP.' });
746
+ return;
747
+ }
748
+
749
+ var cbCalled = false;
750
+ var timedOut = false;
751
+
752
+ AlgoliaSearch.JSONPCounter += 1;
753
+ var head = document.getElementsByTagName('head')[0];
754
+ var script = document.createElement('script');
755
+ var cb = 'algoliaJSONP_' + AlgoliaSearch.JSONPCounter;
756
+ var done = false;
757
+ var ontimeout;
758
+ var success;
759
+ var clean;
760
+
761
+ window[cb] = function(data) {
762
+ try { delete window[cb]; } catch (e) { window[cb] = undefined; }
763
+
764
+ if (timedOut) {
765
+ return;
766
+ }
767
+
768
+ var status =
769
+ data && data.message && data.status ||
770
+ data && 200;
771
+
772
+ var ok = status === 200;
773
+ var retry = !ok && status !== 400 && status !== 403 && status !== 404;
774
+ cbCalled = true;
775
+ opts.callback(retry, ok, data);
776
+ };
777
+
778
+ script.type = 'text/javascript';
779
+ url += '?callback=' + cb + '&X-Algolia-Application-Id=' + this.applicationID + '&X-Algolia-API-Key=' + this.apiKey;
780
+
781
+ if (this.tagFilters) {
782
+ url += '&X-Algolia-TagFilters=' + encodeURIComponent(this.tagFilters);
783
+ }
784
+
785
+ if (this.userToken) {
786
+ url += '&X-Algolia-UserToken=' + encodeURIComponent(this.userToken);
787
+ }
788
+
789
+ for (var i = 0; i < this.extraHeaders.length; ++i) {
790
+ url += '&' + this.extraHeaders[i].key + '=' + this.extraHeaders[i].value;
791
+ }
792
+
793
+ if (opts.body && opts.body.params) {
794
+ url += '&' + opts.body.params;
795
+ }
796
+
797
+ ontimeout = setTimeout(function() {
798
+ timedOut = true;
799
+ clean();
800
+
801
+ opts.callback(true, false, { 'message': 'Timeout - Failed to load JSONP script.' });
802
+ }, this.requestTimeoutInMs);
803
+
804
+ success = function() {
805
+ if (done || timedOut) {
806
+ return;
807
+ }
808
+
809
+ done = true;
810
+ clean();
811
+
812
+ // script loaded but did not call the fn => script loading error
813
+ if (!cbCalled) {
814
+ opts.callback(true, false, { 'message': 'Failed to load JSONP script.' });
815
+ }
816
+ };
817
+
818
+ clean = function() {
819
+ clearTimeout(ontimeout);
820
+ script.onload = null;
821
+ script.onreadystatechange = null;
822
+ script.onerror = null;
823
+ head.removeChild(script);
824
+
825
+ try {
826
+ delete window[cb];
827
+ delete window[cb + '_loaded'];
828
+ } catch (e) {
829
+ window[cb] = null;
830
+ window[cb + '_loaded'] = null;
831
+ }
832
+ };
833
+
834
+ // script onreadystatechange needed only for
835
+ // <= IE8
836
+ // https://github.com/angular/angular.js/issues/4523
837
+ script.onreadystatechange = function() {
838
+ if (this.readyState === 'loaded' || this.readyState === 'complete') {
839
+ success();
840
+ }
841
+ };
842
+
843
+ script.onload = function() {
844
+ success();
845
+ };
846
+
847
+ script.onerror = function() {
848
+ if (done || timedOut) {
849
+ return;
850
+ }
851
+
852
+ clean();
853
+ opts.callback(true, false, { 'message': 'Failed to load JSONP script.' });
854
+ };
855
+
856
+ script.async = true;
857
+ script.defer = true;
858
+ script.src = url;
859
+
860
+ head.appendChild(script);
861
+ },
862
+
863
+ /**
864
+ * Make a XmlHttpRequest
865
+ *
866
+ * @param url request url (includes endpoint and path)
867
+ * @param opts all request opts
868
+ */
869
+ _makeXmlHttpRequestByHost: function(url, opts) {
870
+ // no cors or XDomainRequest, no request
871
+ if (!this._support.cors && !this._support.hasXDomainRequest) {
872
+ // very old browser, not supported
873
+ opts.callback(false, false, { 'message': 'CORS not supported' });
874
+ return;
875
+ }
876
+
877
+ var body = null;
878
+ var request = this._support.cors ? new XMLHttpRequest() : new XDomainRequest();
879
+ var ontimeout;
880
+ var self = this;
881
+ var timedOut;
882
+ var timeoutListener;
883
+
884
+ if (!this._isUndefined(opts.body)) {
885
+ body = JSON.stringify(opts.body);
886
+ }
887
+
888
+ url += (url.indexOf('?') === -1 ? '?' : '&') + 'X-Algolia-API-Key=' + this.apiKey;
889
+ url += '&X-Algolia-Application-Id=' + this.applicationID;
890
+
891
+ if (this.userToken) {
892
+ url += '&X-Algolia-UserToken=' + encodeURIComponent(this.userToken);
893
+ }
894
+
895
+ if (this.tagFilters) {
896
+ url += '&X-Algolia-TagFilters=' + encodeURIComponent(this.tagFilters);
897
+ }
898
+
899
+ for (var i = 0; i < this.extraHeaders.length; ++i) {
900
+ url += '&' + this.extraHeaders[i].key + '=' + this.extraHeaders[i].value;
901
+ }
902
+
903
+ timeoutListener = function() {
904
+ if (!self._support.timeout) {
905
+ timedOut = true;
906
+ request.abort();
907
+ }
908
+
909
+ opts.callback(true, false, { 'message': 'Timeout - Could not connect to endpoint ' + url } );
910
+ };
911
+
912
+ // do not rely on default XHR async flag, as some analytics code like hotjar
913
+ // breaks it and set it to false by default
914
+ if (request instanceof XMLHttpRequest) {
915
+ request.open(opts.method, url, true);
916
+ } else {
917
+ request.open(opts.method, url);
918
+ }
919
+
920
+ if (this._support.cors && body !== null && opts.method !== 'GET') {
921
+ request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
922
+ }
923
+
924
+ // event object not received in IE8, at least
925
+ // but we do not use it, still important to note
926
+ request.onload = function(/*event*/) {
927
+ // When browser does not supports request.timeout, we can
928
+ // have both a load and timeout event
929
+ if (timedOut) {
930
+ return;
931
+ }
932
+
933
+ if (!self._support.timeout) {
934
+ clearTimeout(ontimeout);
935
+ }
936
+
937
+ var response = null;
938
+
939
+ try {
940
+ response = JSON.parse(request.responseText);
941
+ } catch(e) {}
942
+
943
+ var status =
944
+ // XHR provides a `status` property
945
+ request.status ||
946
+
947
+ // XDR does not have a `status` property,
948
+ // we rely on our own API response `status`, only
949
+ // provided when an error occurs, so we expect a .message
950
+ response && response.message && response.status ||
951
+
952
+ // XDR default to success when no response.status
953
+ response && 200;
954
+
955
+ var success = status === 200 || status === 201;
956
+ var retry = !success && status !== 400 && status !== 403 && status !== 404;
957
+
958
+ opts.callback(retry, success, response);
959
+ };
960
+
961
+ if (this._support.timeout) {
962
+ // .timeout supported by both XHR and XDR,
963
+ // we do receive timeout event, tested
964
+ request.timeout = this.requestTimeoutInMs * (opts.successiveRetryCount + 1);
965
+
966
+ request.ontimeout = timeoutListener;
967
+ } else {
968
+ ontimeout = setTimeout(timeoutListener, this.requestTimeoutInMs * (opts.successiveRetryCount + 1));
969
+ }
970
+
971
+ request.onerror = function(event) {
972
+ if (timedOut) {
973
+ return;
974
+ }
975
+
976
+ if (!self._support.timeout) {
977
+ clearTimeout(ontimeout);
978
+ }
979
+
980
+ // error event is trigerred both with XDR/XHR on:
981
+ // - DNS error
982
+ // - unallowed cross domain request
983
+ opts.callback(true, false, { 'message': 'Could not connect to host', 'error': event } );
984
+ };
985
+
986
+ request.send(body);
987
+ },
988
+
989
+ /*
990
+ * Transform search param object in query string
991
+ */
992
+ _getSearchParams: function(args, params) {
993
+ if (this._isUndefined(args) || args === null) {
994
+ return params;
995
+ }
996
+ for (var key in args) {
997
+ if (key !== null && args.hasOwnProperty(key)) {
998
+ params += (params.length === 0) ? '?' : '&';
999
+ params += key + '=' + encodeURIComponent(Object.prototype.toString.call(args[key]) === '[object Array]' ? JSON.stringify(args[key]) : args[key]);
1000
+ }
1001
+ }
1002
+ return params;
1003
+ },
1004
+ _isUndefined: function(obj) {
1005
+ return obj === void 0;
1006
+ },
1007
+
1008
+ _support: {
1009
+ hasXMLHttpRequest: 'XMLHttpRequest' in window,
1010
+ hasXDomainRequest: 'XDomainRequest' in window,
1011
+ cors: 'withCredentials' in new XMLHttpRequest(),
1012
+ timeout: 'timeout' in new XMLHttpRequest()
1013
+ }
1014
+ };
1015
+
1016
+ /*
1017
+ * Contains all the functions related to one index
1018
+ * You should use AlgoliaSearch.initIndex(indexName) to retrieve this object
1019
+ */
1020
+ AlgoliaSearch.prototype.Index.prototype = {
1021
+ /*
1022
+ * Clear all queries in cache
1023
+ */
1024
+ clearCache: function() {
1025
+ this.cache = {};
1026
+ },
1027
+ /*
1028
+ * Add an object in this index
1029
+ *
1030
+ * @param content contains the javascript object to add inside the index
1031
+ * @param callback (optional) the result callback with two arguments:
1032
+ * success: boolean set to true if the request was successfull
1033
+ * content: the server answer that contains 3 elements: createAt, taskId and objectID
1034
+ * @param objectID (optional) an objectID you want to attribute to this object
1035
+ * (if the attribute already exist the old object will be overwrite)
1036
+ */
1037
+ addObject: function(content, callback, objectID) {
1038
+ var indexObj = this;
1039
+ if (this.as._isUndefined(objectID)) {
1040
+ return this.as._jsonRequest({ method: 'POST',
1041
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName),
1042
+ body: content,
1043
+ callback: callback });
1044
+ } else {
1045
+ return this.as._jsonRequest({ method: 'PUT',
1046
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(objectID),
1047
+ body: content,
1048
+ callback: callback });
1049
+ }
1050
+
1051
+ },
1052
+ /*
1053
+ * Add several objects
1054
+ *
1055
+ * @param objects contains an array of objects to add
1056
+ * @param callback (optional) the result callback with two arguments:
1057
+ * success: boolean set to true if the request was successfull
1058
+ * content: the server answer that updateAt and taskID
1059
+ */
1060
+ addObjects: function(objects, callback) {
1061
+ var indexObj = this;
1062
+ var postObj = {requests:[]};
1063
+ for (var i = 0; i < objects.length; ++i) {
1064
+ var request = { action: 'addObject',
1065
+ body: objects[i] };
1066
+ postObj.requests.push(request);
1067
+ }
1068
+ return this.as._jsonRequest({ method: 'POST',
1069
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
1070
+ body: postObj,
1071
+ callback: callback });
1072
+ },
1073
+ /*
1074
+ * Get an object from this index
1075
+ *
1076
+ * @param objectID the unique identifier of the object to retrieve
1077
+ * @param callback (optional) the result callback with two arguments
1078
+ * success: boolean set to true if the request was successfull
1079
+ * content: the object to retrieve or the error message if a failure occured
1080
+ * @param attributes (optional) if set, contains the array of attribute names to retrieve
1081
+ */
1082
+ getObject: function(objectID, callback, attributes) {
1083
+ if (Object.prototype.toString.call(callback) === '[object Array]' && !attributes) {
1084
+ attributes = callback;
1085
+ callback = null;
1086
+ }
1087
+ var indexObj = this;
1088
+ var params = '';
1089
+ if (!this.as._isUndefined(attributes)) {
1090
+ params = '?attributes=';
1091
+ for (var i = 0; i < attributes.length; ++i) {
1092
+ if (i !== 0) {
1093
+ params += ',';
1094
+ }
1095
+ params += attributes[i];
1096
+ }
1097
+ }
1098
+
1099
+ return this.as._jsonRequest({ method: 'GET',
1100
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(objectID) + params,
1101
+ callback: callback });
1102
+ },
1103
+
1104
+ /*
1105
+ * Update partially an object (only update attributes passed in argument)
1106
+ *
1107
+ * @param partialObject contains the javascript attributes to override, the
1108
+ * object must contains an objectID attribute
1109
+ * @param callback (optional) the result callback with two arguments:
1110
+ * success: boolean set to true if the request was successfull
1111
+ * content: the server answer that contains 3 elements: createAt, taskId and objectID
1112
+ */
1113
+ partialUpdateObject: function(partialObject, callback) {
1114
+ var indexObj = this;
1115
+ return this.as._jsonRequest({ method: 'POST',
1116
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(partialObject.objectID) + '/partial',
1117
+ body: partialObject,
1118
+ callback: callback });
1119
+ },
1120
+ /*
1121
+ * Partially Override the content of several objects
1122
+ *
1123
+ * @param objects contains an array of objects to update (each object must contains a objectID attribute)
1124
+ * @param callback (optional) the result callback with two arguments:
1125
+ * success: boolean set to true if the request was successfull
1126
+ * content: the server answer that updateAt and taskID
1127
+ */
1128
+ partialUpdateObjects: function(objects, callback) {
1129
+ var indexObj = this;
1130
+ var postObj = {requests:[]};
1131
+ for (var i = 0; i < objects.length; ++i) {
1132
+ var request = { action: 'partialUpdateObject',
1133
+ objectID: objects[i].objectID,
1134
+ body: objects[i] };
1135
+ postObj.requests.push(request);
1136
+ }
1137
+ return this.as._jsonRequest({ method: 'POST',
1138
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
1139
+ body: postObj,
1140
+ callback: callback });
1141
+ },
1142
+ /*
1143
+ * Override the content of object
1144
+ *
1145
+ * @param object contains the javascript object to save, the object must contains an objectID attribute
1146
+ * @param callback (optional) the result callback with two arguments:
1147
+ * success: boolean set to true if the request was successfull
1148
+ * content: the server answer that updateAt and taskID
1149
+ */
1150
+ saveObject: function(object, callback) {
1151
+ var indexObj = this;
1152
+ return this.as._jsonRequest({ method: 'PUT',
1153
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(object.objectID),
1154
+ body: object,
1155
+ callback: callback });
1156
+ },
1157
+ /*
1158
+ * Override the content of several objects
1159
+ *
1160
+ * @param objects contains an array of objects to update (each object must contains a objectID attribute)
1161
+ * @param callback (optional) the result callback with two arguments:
1162
+ * success: boolean set to true if the request was successfull
1163
+ * content: the server answer that updateAt and taskID
1164
+ */
1165
+ saveObjects: function(objects, callback) {
1166
+ var indexObj = this;
1167
+ var postObj = {requests:[]};
1168
+ for (var i = 0; i < objects.length; ++i) {
1169
+ var request = { action: 'updateObject',
1170
+ objectID: objects[i].objectID,
1171
+ body: objects[i] };
1172
+ postObj.requests.push(request);
1173
+ }
1174
+ return this.as._jsonRequest({ method: 'POST',
1175
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
1176
+ body: postObj,
1177
+ callback: callback });
1178
+ },
1179
+ /*
1180
+ * Delete an object from the index
1181
+ *
1182
+ * @param objectID the unique identifier of object to delete
1183
+ * @param callback (optional) the result callback with two arguments:
1184
+ * success: boolean set to true if the request was successfull
1185
+ * content: the server answer that contains 3 elements: createAt, taskId and objectID
1186
+ */
1187
+ deleteObject: function(objectID, callback) {
1188
+ if (objectID === null || objectID.length === 0) {
1189
+ callback(false, { message: 'empty objectID'});
1190
+ return;
1191
+ }
1192
+ var indexObj = this;
1193
+ return this.as._jsonRequest({ method: 'DELETE',
1194
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(objectID),
1195
+ callback: callback });
1196
+ },
1197
+ /*
1198
+ * Search inside the index using XMLHttpRequest request (Using a POST query to
1199
+ * minimize number of OPTIONS queries: Cross-Origin Resource Sharing).
1200
+ *
1201
+ * @param query the full text query
1202
+ * @param callback the result callback with two arguments:
1203
+ * success: boolean set to true if the request was successfull. If false, the content contains the error.
1204
+ * content: the server answer that contains the list of results.
1205
+ * @param args (optional) if set, contains an object with query parameters:
1206
+ * - page: (integer) Pagination parameter used to select the page to retrieve.
1207
+ * Page is zero-based and defaults to 0. Thus, to retrieve the 10th page you need to set page=9
1208
+ * - hitsPerPage: (integer) Pagination parameter used to select the number of hits per page. Defaults to 20.
1209
+ * - attributesToRetrieve: a string that contains the list of object attributes you want to retrieve (let you minimize the answer size).
1210
+ * Attributes are separated with a comma (for example "name,address").
1211
+ * You can also use an array (for example ["name","address"]).
1212
+ * By default, all attributes are retrieved. You can also use '*' to retrieve all values when an attributesToRetrieve setting is specified for your index.
1213
+ * - attributesToHighlight: a string that contains the list of attributes you want to highlight according to the query.
1214
+ * Attributes are separated by a comma. You can also use an array (for example ["name","address"]).
1215
+ * If an attribute has no match for the query, the raw value is returned. By default all indexed text attributes are highlighted.
1216
+ * You can use `*` if you want to highlight all textual attributes. Numerical attributes are not highlighted.
1217
+ * A matchLevel is returned for each highlighted attribute and can contain:
1218
+ * - full: if all the query terms were found in the attribute,
1219
+ * - partial: if only some of the query terms were found,
1220
+ * - none: if none of the query terms were found.
1221
+ * - attributesToSnippet: a string that contains the list of attributes to snippet alongside the number of words to return (syntax is `attributeName:nbWords`).
1222
+ * Attributes are separated by a comma (Example: attributesToSnippet=name:10,content:10).
1223
+ * You can also use an array (Example: attributesToSnippet: ['name:10','content:10']). By default no snippet is computed.
1224
+ * - minWordSizefor1Typo: the minimum number of characters in a query word to accept one typo in this word. Defaults to 3.
1225
+ * - minWordSizefor2Typos: the minimum number of characters in a query word to accept two typos in this word. Defaults to 7.
1226
+ * - getRankingInfo: if set to 1, the result hits will contain ranking information in _rankingInfo attribute.
1227
+ * - aroundLatLng: search for entries around a given latitude/longitude (specified as two floats separated by a comma).
1228
+ * For example aroundLatLng=47.316669,5.016670).
1229
+ * You can specify the maximum distance in meters with the aroundRadius parameter (in meters) and the precision for ranking with aroundPrecision
1230
+ * (for example if you set aroundPrecision=100, two objects that are distant of less than 100m will be considered as identical for "geo" ranking parameter).
1231
+ * At indexing, you should specify geoloc of an object with the _geoloc attribute (in the form {"_geoloc":{"lat":48.853409, "lng":2.348800}})
1232
+ * - insideBoundingBox: search entries inside a given area defined by the two extreme points of a rectangle (defined by 4 floats: p1Lat,p1Lng,p2Lat,p2Lng).
1233
+ * For example insideBoundingBox=47.3165,4.9665,47.3424,5.0201).
1234
+ * At indexing, you should specify geoloc of an object with the _geoloc attribute (in the form {"_geoloc":{"lat":48.853409, "lng":2.348800}})
1235
+ * - numericFilters: a string that contains the list of numeric filters you want to apply separated by a comma.
1236
+ * The syntax of one filter is `attributeName` followed by `operand` followed by `value`. Supported operands are `<`, `<=`, `=`, `>` and `>=`.
1237
+ * You can have multiple conditions on one attribute like for example numericFilters=price>100,price<1000.
1238
+ * You can also use an array (for example numericFilters: ["price>100","price<1000"]).
1239
+ * - tagFilters: filter the query by a set of tags. You can AND tags by separating them by commas.
1240
+ * To OR tags, you must add parentheses. For example, tags=tag1,(tag2,tag3) means tag1 AND (tag2 OR tag3).
1241
+ * You can also use an array, for example tagFilters: ["tag1",["tag2","tag3"]] means tag1 AND (tag2 OR tag3).
1242
+ * At indexing, tags should be added in the _tags** attribute of objects (for example {"_tags":["tag1","tag2"]}).
1243
+ * - facetFilters: filter the query by a list of facets.
1244
+ * Facets are separated by commas and each facet is encoded as `attributeName:value`.
1245
+ * For example: `facetFilters=category:Book,author:John%20Doe`.
1246
+ * You can also use an array (for example `["category:Book","author:John%20Doe"]`).
1247
+ * - facets: List of object attributes that you want to use for faceting.
1248
+ * Comma separated list: `"category,author"` or array `['category','author']`
1249
+ * Only attributes that have been added in **attributesForFaceting** index setting can be used in this parameter.
1250
+ * You can also use `*` to perform faceting on all attributes specified in **attributesForFaceting**.
1251
+ * - queryType: select how the query words are interpreted, it can be one of the following value:
1252
+ * - prefixAll: all query words are interpreted as prefixes,
1253
+ * - prefixLast: only the last word is interpreted as a prefix (default behavior),
1254
+ * - prefixNone: no query word is interpreted as a prefix. This option is not recommended.
1255
+ * - optionalWords: a string that contains the list of words that should be considered as optional when found in the query.
1256
+ * Comma separated and array are accepted.
1257
+ * - distinct: If set to 1, enable the distinct feature (disabled by default) if the attributeForDistinct index setting is set.
1258
+ * This feature is similar to the SQL "distinct" keyword: when enabled in a query with the distinct=1 parameter,
1259
+ * all hits containing a duplicate value for the attributeForDistinct attribute are removed from results.
1260
+ * For example, if the chosen attribute is show_name and several hits have the same value for show_name, then only the best
1261
+ * one is kept and others are removed.
1262
+ * - restrictSearchableAttributes: List of attributes you want to use for textual search (must be a subset of the attributesToIndex index setting)
1263
+ * either comma separated or as an array
1264
+ * @param delay (optional) if set, wait for this delay (in ms) and only send the query if there was no other in the meantime.
1265
+ */
1266
+ search: function(query, callback, args, delay) {
1267
+ if (query === undefined || query === null) {
1268
+ query = '';
1269
+ }
1270
+
1271
+ // no query = getAllObjects
1272
+ if (typeof query === 'function') {
1273
+ callback = query;
1274
+ query = '';
1275
+ }
1276
+
1277
+ if (typeof callback === 'object' && (this.as._isUndefined(args) || !args)) {
1278
+ args = callback;
1279
+ callback = null;
1280
+ }
1281
+
1282
+ var indexObj = this;
1283
+ var params = 'query=' + encodeURIComponent(query);
1284
+ if (!this.as._isUndefined(args) && args !== null) {
1285
+ params = this.as._getSearchParams(args, params);
1286
+ }
1287
+ window.clearTimeout(indexObj.onDelayTrigger);
1288
+ if (!this.as._isUndefined(delay) && delay !== null && delay > 0) {
1289
+ var onDelayTrigger = window.setTimeout( function() {
1290
+ indexObj._search(params, callback);
1291
+ }, delay);
1292
+ indexObj.onDelayTrigger = onDelayTrigger;
1293
+ } else {
1294
+ return this._search(params, callback);
1295
+ }
1296
+ },
1297
+
1298
+ /*
1299
+ * Browse all index content
1300
+ *
1301
+ * @param page Pagination parameter used to select the page to retrieve.
1302
+ * Page is zero-based and defaults to 0. Thus, to retrieve the 10th page you need to set page=9
1303
+ * @param hitsPerPage: Pagination parameter used to select the number of hits per page. Defaults to 1000.
1304
+ */
1305
+ browse: function(page, callback, hitsPerPage) {
1306
+ if (+callback > 0 && (this.as._isUndefined(hitsPerPage) || !hitsPerPage)) {
1307
+ hitsPerPage = callback;
1308
+ callback = null;
1309
+ }
1310
+ var indexObj = this;
1311
+ var params = '?page=' + page;
1312
+ if (!this.as._isUndefined(hitsPerPage)) {
1313
+ params += '&hitsPerPage=' + hitsPerPage;
1314
+ }
1315
+ return this.as._jsonRequest({ method: 'GET',
1316
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/browse' + params,
1317
+ callback: callback });
1318
+ },
1319
+
1320
+ /*
1321
+ * Get a Typeahead.js adapter
1322
+ * @param searchParams contains an object with query parameters (see search for details)
1323
+ */
1324
+ ttAdapter: function(params) {
1325
+ var self = this;
1326
+ return function(query, cb) {
1327
+ self.search(query, function(success, content) {
1328
+ if (success) {
1329
+ cb(content.hits);
1330
+ } else {
1331
+ cb(content && content.message);
1332
+ }
1333
+ }, params);
1334
+ };
1335
+ },
1336
+
1337
+ /*
1338
+ * Wait the publication of a task on the server.
1339
+ * All server task are asynchronous and you can check with this method that the task is published.
1340
+ *
1341
+ * @param taskID the id of the task returned by server
1342
+ * @param callback the result callback with with two arguments:
1343
+ * success: boolean set to true if the request was successfull
1344
+ * content: the server answer that contains the list of results
1345
+ */
1346
+ waitTask: function(taskID, callback) {
1347
+ var indexObj = this;
1348
+ return this.as._jsonRequest({ method: 'GET',
1349
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/task/' + taskID,
1350
+ callback: function(success, body) {
1351
+ if (success) {
1352
+ if (body.status === 'published') {
1353
+ callback(true, body);
1354
+ } else {
1355
+ setTimeout(function() { indexObj.waitTask(taskID, callback); }, 100);
1356
+ }
1357
+ } else {
1358
+ callback(false, body);
1359
+ }
1360
+ }});
1361
+ },
1362
+
1363
+ /*
1364
+ * This function deletes the index content. Settings and index specific API keys are kept untouched.
1365
+ *
1366
+ * @param callback (optional) the result callback with two arguments
1367
+ * success: boolean set to true if the request was successfull
1368
+ * content: the settings object or the error message if a failure occured
1369
+ */
1370
+ clearIndex: function(callback) {
1371
+ var indexObj = this;
1372
+ return this.as._jsonRequest({ method: 'POST',
1373
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/clear',
1374
+ callback: callback });
1375
+ },
1376
+ /*
1377
+ * Get settings of this index
1378
+ *
1379
+ * @param callback (optional) the result callback with two arguments
1380
+ * success: boolean set to true if the request was successfull
1381
+ * content: the settings object or the error message if a failure occured
1382
+ */
1383
+ getSettings: function(callback) {
1384
+ var indexObj = this;
1385
+ return this.as._jsonRequest({ method: 'GET',
1386
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/settings',
1387
+ callback: callback });
1388
+ },
1389
+
1390
+ /*
1391
+ * Set settings for this index
1392
+ *
1393
+ * @param settigns the settings object that can contains :
1394
+ * - minWordSizefor1Typo: (integer) the minimum number of characters to accept one typo (default = 3).
1395
+ * - minWordSizefor2Typos: (integer) the minimum number of characters to accept two typos (default = 7).
1396
+ * - hitsPerPage: (integer) the number of hits per page (default = 10).
1397
+ * - attributesToRetrieve: (array of strings) default list of attributes to retrieve in objects.
1398
+ * If set to null, all attributes are retrieved.
1399
+ * - attributesToHighlight: (array of strings) default list of attributes to highlight.
1400
+ * If set to null, all indexed attributes are highlighted.
1401
+ * - attributesToSnippet**: (array of strings) default list of attributes to snippet alongside the number of words to return (syntax is attributeName:nbWords).
1402
+ * By default no snippet is computed. If set to null, no snippet is computed.
1403
+ * - attributesToIndex: (array of strings) the list of fields you want to index.
1404
+ * If set to null, all textual and numerical attributes of your objects are indexed, but you should update it to get optimal results.
1405
+ * This parameter has two important uses:
1406
+ * - Limit the attributes to index: For example if you store a binary image in base64, you want to store it and be able to
1407
+ * retrieve it but you don't want to search in the base64 string.
1408
+ * - Control part of the ranking*: (see the ranking parameter for full explanation) Matches in attributes at the beginning of
1409
+ * the list will be considered more important than matches in attributes further down the list.
1410
+ * In one attribute, matching text at the beginning of the attribute will be considered more important than text after, you can disable
1411
+ * this behavior if you add your attribute inside `unordered(AttributeName)`, for example attributesToIndex: ["title", "unordered(text)"].
1412
+ * - attributesForFaceting: (array of strings) The list of fields you want to use for faceting.
1413
+ * All strings in the attribute selected for faceting are extracted and added as a facet. If set to null, no attribute is used for faceting.
1414
+ * - attributeForDistinct: (string) The attribute name used for the Distinct feature. This feature is similar to the SQL "distinct" keyword: when enabled
1415
+ * in query with the distinct=1 parameter, all hits containing a duplicate value for this attribute are removed from results.
1416
+ * For example, if the chosen attribute is show_name and several hits have the same value for show_name, then only the best one is kept and others are removed.
1417
+ * - ranking: (array of strings) controls the way results are sorted.
1418
+ * We have six available criteria:
1419
+ * - typo: sort according to number of typos,
1420
+ * - geo: sort according to decreassing distance when performing a geo-location based search,
1421
+ * - proximity: sort according to the proximity of query words in hits,
1422
+ * - attribute: sort according to the order of attributes defined by attributesToIndex,
1423
+ * - exact:
1424
+ * - if the user query contains one word: sort objects having an attribute that is exactly the query word before others.
1425
+ * For example if you search for the "V" TV show, you want to find it with the "V" query and avoid to have all popular TV
1426
+ * show starting by the v letter before it.
1427
+ * - if the user query contains multiple words: sort according to the number of words that matched exactly (and not as a prefix).
1428
+ * - custom: sort according to a user defined formula set in **customRanking** attribute.
1429
+ * The standard order is ["typo", "geo", "proximity", "attribute", "exact", "custom"]
1430
+ * - customRanking: (array of strings) lets you specify part of the ranking.
1431
+ * The syntax of this condition is an array of strings containing attributes prefixed by asc (ascending order) or desc (descending order) operator.
1432
+ * For example `"customRanking" => ["desc(population)", "asc(name)"]`
1433
+ * - queryType: Select how the query words are interpreted, it can be one of the following value:
1434
+ * - prefixAll: all query words are interpreted as prefixes,
1435
+ * - prefixLast: only the last word is interpreted as a prefix (default behavior),
1436
+ * - prefixNone: no query word is interpreted as a prefix. This option is not recommended.
1437
+ * - highlightPreTag: (string) Specify the string that is inserted before the highlighted parts in the query result (default to "<em>").
1438
+ * - highlightPostTag: (string) Specify the string that is inserted after the highlighted parts in the query result (default to "</em>").
1439
+ * - optionalWords: (array of strings) Specify a list of words that should be considered as optional when found in the query.
1440
+ * @param callback (optional) the result callback with two arguments
1441
+ * success: boolean set to true if the request was successfull
1442
+ * content: the server answer or the error message if a failure occured
1443
+ */
1444
+ setSettings: function(settings, callback) {
1445
+ var indexObj = this;
1446
+ return this.as._jsonRequest({ method: 'PUT',
1447
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/settings',
1448
+ body: settings,
1449
+ callback: callback });
1450
+ },
1451
+ /*
1452
+ * List all existing user keys associated to this index
1453
+ *
1454
+ * @param callback the result callback with two arguments
1455
+ * success: boolean set to true if the request was successfull
1456
+ * content: the server answer with user keys list or error description if success is false.
1457
+ */
1458
+ listUserKeys: function(callback) {
1459
+ var indexObj = this;
1460
+ return this.as._jsonRequest({ method: 'GET',
1461
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys',
1462
+ callback: callback });
1463
+ },
1464
+ /*
1465
+ * Get ACL of a user key associated to this index
1466
+ *
1467
+ * @param callback the result callback with two arguments
1468
+ * success: boolean set to true if the request was successfull
1469
+ * content: the server answer with user keys list or error description if success is false.
1470
+ */
1471
+ getUserKeyACL: function(key, callback) {
1472
+ var indexObj = this;
1473
+ return this.as._jsonRequest({ method: 'GET',
1474
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys/' + key,
1475
+ callback: callback });
1476
+ },
1477
+ /*
1478
+ * Delete an existing user key associated to this index
1479
+ *
1480
+ * @param callback the result callback with two arguments
1481
+ * success: boolean set to true if the request was successfull
1482
+ * content: the server answer with user keys list or error description if success is false.
1483
+ */
1484
+ deleteUserKey: function(key, callback) {
1485
+ var indexObj = this;
1486
+ return this.as._jsonRequest({ method: 'DELETE',
1487
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys/' + key,
1488
+ callback: callback });
1489
+ },
1490
+ /*
1491
+ * Add an existing user key associated to this index
1492
+ *
1493
+ * @param acls the list of ACL for this key. Defined by an array of strings that
1494
+ * can contains the following values:
1495
+ * - search: allow to search (https and http)
1496
+ * - addObject: allows to add/update an object in the index (https only)
1497
+ * - deleteObject : allows to delete an existing object (https only)
1498
+ * - deleteIndex : allows to delete index content (https only)
1499
+ * - settings : allows to get index settings (https only)
1500
+ * - editSettings : allows to change index settings (https only)
1501
+ * @param callback the result callback with two arguments
1502
+ * success: boolean set to true if the request was successfull
1503
+ * content: the server answer with user keys list or error description if success is false.
1504
+ */
1505
+ addUserKey: function(acls, callback) {
1506
+ var indexObj = this;
1507
+ var aclsObject = {};
1508
+ aclsObject.acl = acls;
1509
+ return this.as._jsonRequest({ method: 'POST',
1510
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys',
1511
+ body: aclsObject,
1512
+ callback: callback });
1513
+ },
1514
+ /*
1515
+ * Add an existing user key associated to this index
1516
+ *
1517
+ * @param acls the list of ACL for this key. Defined by an array of strings that
1518
+ * can contains the following values:
1519
+ * - search: allow to search (https and http)
1520
+ * - addObject: allows to add/update an object in the index (https only)
1521
+ * - deleteObject : allows to delete an existing object (https only)
1522
+ * - deleteIndex : allows to delete index content (https only)
1523
+ * - settings : allows to get index settings (https only)
1524
+ * - editSettings : allows to change index settings (https only)
1525
+ * @param validity the number of seconds after which the key will be automatically removed (0 means no time limit for this key)
1526
+ * @param maxQueriesPerIPPerHour Specify the maximum number of API calls allowed from an IP address per hour.
1527
+ * @param maxHitsPerQuery Specify the maximum number of hits this API key can retrieve in one call.
1528
+ * @param callback the result callback with two arguments
1529
+ * success: boolean set to true if the request was successfull
1530
+ * content: the server answer with user keys list or error description if success is false.
1531
+ */
1532
+ addUserKeyWithValidity: function(acls, validity, maxQueriesPerIPPerHour, maxHitsPerQuery, callback) {
1533
+ var indexObj = this;
1534
+ var aclsObject = {};
1535
+ aclsObject.acl = acls;
1536
+ aclsObject.validity = validity;
1537
+ aclsObject.maxQueriesPerIPPerHour = maxQueriesPerIPPerHour;
1538
+ aclsObject.maxHitsPerQuery = maxHitsPerQuery;
1539
+ return this.as._jsonRequest({ method: 'POST',
1540
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys',
1541
+ body: aclsObject,
1542
+ callback: callback });
1543
+ },
1544
+ ///
1545
+ /// Internal methods only after this line
1546
+ ///
1547
+ _search: function(params, callback) {
1548
+ var pObj = {params: params};
1549
+ if (this.as.jsonp === null) {
1550
+ var self = this;
1551
+ return this.as._jsonRequest({ cache: this.cache,
1552
+ method: 'POST',
1553
+ url: '/1/indexes/' + encodeURIComponent(this.indexName) + '/query',
1554
+ body: pObj,
1555
+ callback: function(success, content) {
1556
+ var status = content && content.status;
1557
+ if (success || status && Math.floor(status / 100) === 4 || Math.floor(status / 100) === 1) {
1558
+ self.as.jsonp = false;
1559
+ callback && callback(success, content);
1560
+ } else {
1561
+ self.as.jsonp = true;
1562
+ self._search(params, callback);
1563
+ }
1564
+ }
1565
+ });
1566
+ } else if (this.as.jsonp) {
1567
+ return this.as._jsonRequest({ cache: this.cache,
1568
+ method: 'GET',
1569
+ url: '/1/indexes/' + encodeURIComponent(this.indexName),
1570
+ body: pObj,
1571
+ callback: callback });
1572
+ } else {
1573
+ return this.as._jsonRequest({ cache: this.cache,
1574
+ method: 'POST',
1575
+ url: '/1/indexes/' + encodeURIComponent(this.indexName) + '/query',
1576
+ body: pObj,
1577
+ callback: callback});
1578
+ }
1579
+ },
1580
+
1581
+ // internal attributes
1582
+ as: null,
1583
+ indexName: null,
1584
+ typeAheadArgs: null,
1585
+ typeAheadValueOption: null
1586
+ };
1587
+
1588
+ /*
1589
+ * Copyright (c) 2014 Algolia
1590
+ * http://www.algolia.com/
1591
+ *
1592
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
1593
+ * of this software and associated documentation files (the "Software"), to deal
1594
+ * in the Software without restriction, including without limitation the rights
1595
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1596
+ * copies of the Software, and to permit persons to whom the Software is
1597
+ * furnished to do so, subject to the following conditions:
1598
+ *
1599
+ * The above copyright notice and this permission notice shall be included in
1600
+ * all copies or substantial portions of the Software.
1601
+ *
1602
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1603
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1604
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1605
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1606
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1607
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1608
+ * THE SOFTWARE.
1609
+ */
1610
+
1611
+ (function($) {
1612
+ var extend = function(out) {
1613
+ out = out || {};
1614
+ for (var i = 1; i < arguments.length; i++) {
1615
+ if (!arguments[i]) {
1616
+ continue;
1617
+ }
1618
+ for (var key in arguments[i]) {
1619
+ if (arguments[i].hasOwnProperty(key)) {
1620
+ out[key] = arguments[i][key];
1621
+ }
1622
+ }
1623
+ }
1624
+ return out;
1625
+ };
1626
+
1627
+ /**
1628
+ * Algolia Search Helper providing faceting and disjunctive faceting
1629
+ * @param {AlgoliaSearch} client an AlgoliaSearch client
1630
+ * @param {string} index the index name to query
1631
+ * @param {hash} options an associative array defining the hitsPerPage, list of facets, the list of disjunctive facets and the default facet filters
1632
+ */
1633
+ window.AlgoliaSearchHelper = function(client, index, options) {
1634
+ /// Default options
1635
+ var defaults = {
1636
+ facets: [], // list of facets to compute
1637
+ disjunctiveFacets: [], // list of disjunctive facets to compute
1638
+ hitsPerPage: 20, // number of hits per page
1639
+ defaultFacetFilters: [] // the default list of facetFilters
1640
+ };
1641
+
1642
+ this.init(client, index, extend({}, defaults, options));
1643
+ };
1644
+
1645
+ AlgoliaSearchHelper.prototype = {
1646
+ /**
1647
+ * Initialize a new AlgoliaSearchHelper
1648
+ * @param {AlgoliaSearch} client an AlgoliaSearch client
1649
+ * @param {string} index the index name to query
1650
+ * @param {hash} options an associative array defining the hitsPerPage, list of facets and list of disjunctive facets
1651
+ * @return {AlgoliaSearchHelper}
1652
+ */
1653
+ init: function(client, index, options) {
1654
+ this.client = client;
1655
+ this.index = index;
1656
+ this.options = options;
1657
+ this.page = 0;
1658
+ this.refinements = {};
1659
+ this.excludes = {};
1660
+ this.disjunctiveRefinements = {};
1661
+ this.extraQueries = [];
1662
+ },
1663
+
1664
+ /**
1665
+ * Perform a query
1666
+ * @param {string} q the user query
1667
+ * @param {function} searchCallback the result callback called with two arguments:
1668
+ * success: boolean set to true if the request was successfull
1669
+ * content: the query answer with an extra 'disjunctiveFacets' attribute
1670
+ */
1671
+ search: function(q, searchCallback, searchParams) {
1672
+ this.q = q;
1673
+ this.searchCallback = searchCallback;
1674
+ this.searchParams = searchParams || {};
1675
+ this.page = this.page || 0;
1676
+ this.refinements = this.refinements || {};
1677
+ this.disjunctiveRefinements = this.disjunctiveRefinements || {};
1678
+ this._search();
1679
+ },
1680
+
1681
+ /**
1682
+ * Remove all refinements (disjunctive + conjunctive)
1683
+ */
1684
+ clearRefinements: function() {
1685
+ this.disjunctiveRefinements = {};
1686
+ this.refinements = {};
1687
+ },
1688
+
1689
+ /**
1690
+ * Ensure a facet refinement exists
1691
+ * @param {string} facet the facet to refine
1692
+ * @param {string} value the associated value
1693
+ */
1694
+ addDisjunctiveRefine: function(facet, value) {
1695
+ this.disjunctiveRefinements = this.disjunctiveRefinements || {};
1696
+ this.disjunctiveRefinements[facet] = this.disjunctiveRefinements[facet] || {};
1697
+ this.disjunctiveRefinements[facet][value] = true;
1698
+ },
1699
+
1700
+ /**
1701
+ * Ensure a facet refinement does not exist
1702
+ * @param {string} facet the facet to refine
1703
+ * @param {string} value the associated value
1704
+ */
1705
+ removeDisjunctiveRefine: function(facet, value) {
1706
+ this.disjunctiveRefinements = this.disjunctiveRefinements || {};
1707
+ this.disjunctiveRefinements[facet] = this.disjunctiveRefinements[facet] || {};
1708
+ try {
1709
+ delete this.disjunctiveRefinements[facet][value];
1710
+ } catch (e) {
1711
+ this.disjunctiveRefinements[facet][value] = undefined; // IE compat
1712
+ }
1713
+ },
1714
+
1715
+ /**
1716
+ * Ensure a facet refinement exists
1717
+ * @param {string} facet the facet to refine
1718
+ * @param {string} value the associated value
1719
+ */
1720
+ addRefine: function(facet, value) {
1721
+ var refinement = facet + ':' + value;
1722
+ this.refinements = this.refinements || {};
1723
+ this.refinements[refinement] = true;
1724
+ },
1725
+
1726
+ /**
1727
+ * Ensure a facet refinement does not exist
1728
+ * @param {string} facet the facet to refine
1729
+ * @param {string} value the associated value
1730
+ */
1731
+ removeRefine: function(facet, value) {
1732
+ var refinement = facet + ':' + value;
1733
+ this.refinements = this.refinements || {};
1734
+ this.refinements[refinement] = false;
1735
+ },
1736
+
1737
+ /**
1738
+ * Ensure a facet exclude exists
1739
+ * @param {string} facet the facet to refine
1740
+ * @param {string} value the associated value
1741
+ */
1742
+ addExclude: function(facet, value) {
1743
+ var refinement = facet + ':-' + value;
1744
+ this.excludes = this.excludes || {};
1745
+ this.excludes[refinement] = true;
1746
+ },
1747
+
1748
+ /**
1749
+ * Ensure a facet exclude does not exist
1750
+ * @param {string} facet the facet to refine
1751
+ * @param {string} value the associated value
1752
+ */
1753
+ removeExclude: function(facet, value) {
1754
+ var refinement = facet + ':-' + value;
1755
+ this.excludes = this.excludes || {};
1756
+ this.excludes[refinement] = false;
1757
+ },
1758
+
1759
+ /**
1760
+ * Toggle refinement state of an exclude
1761
+ * @param {string} facet the facet to refine
1762
+ * @param {string} value the associated value
1763
+ * @return {boolean} true if the facet has been found
1764
+ */
1765
+ toggleExclude: function(facet, value) {
1766
+ for (var i = 0; i < this.options.facets.length; ++i) {
1767
+ if (this.options.facets[i] == facet) {
1768
+ var refinement = facet + ':-' + value;
1769
+ this.excludes[refinement] = !this.excludes[refinement];
1770
+ this.page = 0;
1771
+ this._search();
1772
+ return true;
1773
+ }
1774
+ }
1775
+ return false;
1776
+ },
1777
+
1778
+ /**
1779
+ * Toggle refinement state of a facet
1780
+ * @param {string} facet the facet to refine
1781
+ * @param {string} value the associated value
1782
+ * @return {boolean} true if the facet has been found
1783
+ */
1784
+ toggleRefine: function(facet, value) {
1785
+ for (var i = 0; i < this.options.facets.length; ++i) {
1786
+ if (this.options.facets[i] == facet) {
1787
+ var refinement = facet + ':' + value;
1788
+ this.refinements[refinement] = !this.refinements[refinement];
1789
+ this.page = 0;
1790
+ this._search();
1791
+ return true;
1792
+ }
1793
+ }
1794
+ this.disjunctiveRefinements[facet] = this.disjunctiveRefinements[facet] || {};
1795
+ for (var j = 0; j < this.options.disjunctiveFacets.length; ++j) {
1796
+ if (this.options.disjunctiveFacets[j] == facet) {
1797
+ this.disjunctiveRefinements[facet][value] = !this.disjunctiveRefinements[facet][value];
1798
+ this.page = 0;
1799
+ this._search();
1800
+ return true;
1801
+ }
1802
+ }
1803
+ return false;
1804
+ },
1805
+
1806
+ /**
1807
+ * Check the refinement state of a facet
1808
+ * @param {string} facet the facet
1809
+ * @param {string} value the associated value
1810
+ * @return {boolean} true if refined
1811
+ */
1812
+ isRefined: function(facet, value) {
1813
+ var refinement = facet + ':' + value;
1814
+ if (this.refinements[refinement]) {
1815
+ return true;
1816
+ }
1817
+ if (this.disjunctiveRefinements[facet] && this.disjunctiveRefinements[facet][value]) {
1818
+ return true;
1819
+ }
1820
+ return false;
1821
+ },
1822
+
1823
+ /**
1824
+ * Check the exclude state of a facet
1825
+ * @param {string} facet the facet
1826
+ * @param {string} value the associated value
1827
+ * @return {boolean} true if refined
1828
+ */
1829
+ isExcluded: function(facet, value) {
1830
+ var refinement = facet + ':-' + value;
1831
+ if (this.excludes[refinement]) {
1832
+ return true;
1833
+ }
1834
+ return false;
1835
+ },
1836
+
1837
+ /**
1838
+ * Go to next page
1839
+ */
1840
+ nextPage: function() {
1841
+ this._gotoPage(this.page + 1);
1842
+ },
1843
+
1844
+ /**
1845
+ * Go to previous page
1846
+ */
1847
+ previousPage: function() {
1848
+ if (this.page > 0) {
1849
+ this._gotoPage(this.page - 1);
1850
+ }
1851
+ },
1852
+
1853
+ /**
1854
+ * Goto a page
1855
+ * @param {integer} page The page number
1856
+ */
1857
+ gotoPage: function(page) {
1858
+ this._gotoPage(page);
1859
+ },
1860
+
1861
+ /**
1862
+ * Configure the page but do not trigger a reload
1863
+ * @param {integer} page The page number
1864
+ */
1865
+ setPage: function(page) {
1866
+ this.page = page;
1867
+ },
1868
+
1869
+ /**
1870
+ * Configure the underlying index name
1871
+ * @param {string} name the index name
1872
+ */
1873
+ setIndex: function(name) {
1874
+ this.index = name;
1875
+ },
1876
+
1877
+ /**
1878
+ * Get the underlying configured index name
1879
+ */
1880
+ getIndex: function() {
1881
+ return this.index;
1882
+ },
1883
+
1884
+ /**
1885
+ * Clear the extra queries added to the underlying batch of queries
1886
+ */
1887
+ clearExtraQueries: function() {
1888
+ this.extraQueries = [];
1889
+ },
1890
+
1891
+ /**
1892
+ * Add an extra query to the underlying batch of queries. Once you add queries
1893
+ * to the batch, the 2nd parameter of the searchCallback will be an object with a `results`
1894
+ * attribute listing all search results.
1895
+ */
1896
+ addExtraQuery: function(index, query, params) {
1897
+ this.extraQueries.push({ index: index, query: query, params: (params || {}) });
1898
+ },
1899
+
1900
+ ///////////// PRIVATE
1901
+
1902
+ /**
1903
+ * Goto a page
1904
+ * @param {integer} page The page number
1905
+ */
1906
+ _gotoPage: function(page) {
1907
+ this.page = page;
1908
+ this._search();
1909
+ },
1910
+
1911
+ /**
1912
+ * Perform the underlying queries
1913
+ */
1914
+ _search: function() {
1915
+ this.client.startQueriesBatch();
1916
+ this.client.addQueryInBatch(this.index, this.q, this._getHitsSearchParams());
1917
+ var disjunctiveFacets = [];
1918
+ var unusedDisjunctiveFacets = {};
1919
+ var i = 0;
1920
+ for (i = 0; i < this.options.disjunctiveFacets.length; ++i) {
1921
+ var facet = this.options.disjunctiveFacets[i];
1922
+ if (this._hasDisjunctiveRefinements(facet)) {
1923
+ disjunctiveFacets.push(facet);
1924
+ } else {
1925
+ unusedDisjunctiveFacets[facet] = true;
1926
+ }
1927
+ }
1928
+ for (i = 0; i < disjunctiveFacets.length; ++i) {
1929
+ this.client.addQueryInBatch(this.index, this.q, this._getDisjunctiveFacetSearchParams(disjunctiveFacets[i]));
1930
+ }
1931
+ for (i = 0; i < this.extraQueries.length; ++i) {
1932
+ this.client.addQueryInBatch(this.extraQueries[i].index, this.extraQueries[i].query, this.extraQueries[i].params);
1933
+ }
1934
+ var self = this;
1935
+ this.client.sendQueriesBatch(function(success, content) {
1936
+ if (!success) {
1937
+ self.searchCallback(false, content);
1938
+ return;
1939
+ }
1940
+ var aggregatedAnswer = content.results[0];
1941
+ aggregatedAnswer.disjunctiveFacets = aggregatedAnswer.disjunctiveFacets || {};
1942
+ aggregatedAnswer.facets_stats = aggregatedAnswer.facets_stats || {};
1943
+ // create disjunctive facets from facets (disjunctive facets without refinements)
1944
+ for (var facet in unusedDisjunctiveFacets) {
1945
+ if (aggregatedAnswer.facets[facet] && !aggregatedAnswer.disjunctiveFacets[facet]) {
1946
+ aggregatedAnswer.disjunctiveFacets[facet] = aggregatedAnswer.facets[facet];
1947
+ try {
1948
+ delete aggregatedAnswer.facets[facet];
1949
+ } catch (e) {
1950
+ aggregatedAnswer.facets[facet] = undefined; // IE compat
1951
+ }
1952
+ }
1953
+ }
1954
+ // aggregate the disjunctive facets
1955
+ for (i = 0; i < disjunctiveFacets.length; ++i) {
1956
+ for (var dfacet in content.results[i + 1].facets) {
1957
+ aggregatedAnswer.disjunctiveFacets[dfacet] = content.results[i + 1].facets[dfacet];
1958
+ if (self.disjunctiveRefinements[dfacet]) {
1959
+ for (var value in self.disjunctiveRefinements[dfacet]) {
1960
+ // add the disjunctive reginements if it is no more retrieved
1961
+ if (!aggregatedAnswer.disjunctiveFacets[dfacet][value] && self.disjunctiveRefinements[dfacet][value]) {
1962
+ aggregatedAnswer.disjunctiveFacets[dfacet][value] = 0;
1963
+ }
1964
+ }
1965
+ }
1966
+ }
1967
+ // aggregate the disjunctive facets stats
1968
+ for (var stats in content.results[i + 1].facets_stats) {
1969
+ aggregatedAnswer.facets_stats[stats] = content.results[i + 1].facets_stats[stats];
1970
+ }
1971
+ }
1972
+
1973
+ // Backward compatibility
1974
+ aggregatedAnswer.facetStats = aggregatedAnswer.facets_stats;
1975
+
1976
+ // add the excludes
1977
+ for (var exclude in self.excludes) {
1978
+ if (self.excludes[exclude]) {
1979
+ var e = exclude.indexOf(':-');
1980
+ var facet = exclude.slice(0, e);
1981
+ var value = exclude.slice(e + 2);
1982
+ aggregatedAnswer.facets[facet] = aggregatedAnswer.facets[facet] || {};
1983
+ if (!aggregatedAnswer.facets[facet][value]) {
1984
+ aggregatedAnswer.facets[facet][value] = 0;
1985
+ }
1986
+ }
1987
+ }
1988
+ // call the actual callback
1989
+ if (self.extraQueries.length === 0) {
1990
+ self.searchCallback(true, aggregatedAnswer);
1991
+ } else {
1992
+ // append the extra queries
1993
+ var c = { results: [ aggregatedAnswer ] };
1994
+ for (i = 0; i < self.extraQueries.length; ++i) {
1995
+ c.results.push(content.results[1 + disjunctiveFacets.length + i]);
1996
+ }
1997
+ self.searchCallback(true, c);
1998
+ }
1999
+ });
2000
+ },
2001
+
2002
+ /**
2003
+ * Build search parameters used to fetch hits
2004
+ * @return {hash}
2005
+ */
2006
+ _getHitsSearchParams: function() {
2007
+ var facets = [];
2008
+ var i = 0;
2009
+ for (i = 0; i < this.options.facets.length; ++i) {
2010
+ facets.push(this.options.facets[i]);
2011
+ }
2012
+ for (i = 0; i < this.options.disjunctiveFacets.length; ++i) {
2013
+ var facet = this.options.disjunctiveFacets[i];
2014
+ if (!this._hasDisjunctiveRefinements(facet)) {
2015
+ facets.push(facet);
2016
+ }
2017
+ }
2018
+ return extend({}, {
2019
+ hitsPerPage: this.options.hitsPerPage,
2020
+ page: this.page,
2021
+ facets: facets,
2022
+ facetFilters: this._getFacetFilters()
2023
+ }, this.searchParams);
2024
+ },
2025
+
2026
+ /**
2027
+ * Build search parameters used to fetch a disjunctive facet
2028
+ * @param {string} facet the associated facet name
2029
+ * @return {hash}
2030
+ */
2031
+ _getDisjunctiveFacetSearchParams: function(facet) {
2032
+ return extend({}, this.searchParams, {
2033
+ hitsPerPage: 1,
2034
+ page: 0,
2035
+ attributesToRetrieve: [],
2036
+ attributesToHighlight: [],
2037
+ attributesToSnippet: [],
2038
+ facets: facet,
2039
+ facetFilters: this._getFacetFilters(facet),
2040
+ analytics: false
2041
+ });
2042
+ },
2043
+
2044
+ /**
2045
+ * Test if there are some disjunctive refinements on the facet
2046
+ */
2047
+ _hasDisjunctiveRefinements: function(facet) {
2048
+ for (var value in this.disjunctiveRefinements[facet]) {
2049
+ if (this.disjunctiveRefinements[facet][value]) {
2050
+ return true;
2051
+ }
2052
+ }
2053
+ return false;
2054
+ },
2055
+
2056
+ /**
2057
+ * Build facetFilters parameter based on current refinements
2058
+ * @param {string} facet if set, the current disjunctive facet
2059
+ * @return {hash}
2060
+ */
2061
+ _getFacetFilters: function(facet) {
2062
+ var facetFilters = [];
2063
+ if (this.options.defaultFacetFilters) {
2064
+ for (var i = 0; i < this.options.defaultFacetFilters.length; ++i) {
2065
+ facetFilters.push(this.options.defaultFacetFilters[i]);
2066
+ }
2067
+ }
2068
+ for (var refinement in this.refinements) {
2069
+ if (this.refinements[refinement]) {
2070
+ facetFilters.push(refinement);
2071
+ }
2072
+ }
2073
+ for (var refinement in this.excludes) {
2074
+ if (this.excludes[refinement]) {
2075
+ facetFilters.push(refinement);
2076
+ }
2077
+ }
2078
+ for (var disjunctiveRefinement in this.disjunctiveRefinements) {
2079
+ if (disjunctiveRefinement != facet) {
2080
+ var refinements = [];
2081
+ for (var value in this.disjunctiveRefinements[disjunctiveRefinement]) {
2082
+ if (this.disjunctiveRefinements[disjunctiveRefinement][value]) {
2083
+ refinements.push(disjunctiveRefinement + ':' + value);
2084
+ }
2085
+ }
2086
+ if (refinements.length > 0) {
2087
+ facetFilters.push(refinements);
2088
+ }
2089
+ }
2090
+ }
2091
+ return facetFilters;
2092
+ }
2093
+ };
2094
+ })();
2095
+
2096
+ /*
2097
+ * Copyright (c) 2014 Algolia
2098
+ * http://www.algolia.com/
2099
+ *
2100
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
2101
+ * of this software and associated documentation files (the "Software"), to deal
2102
+ * in the Software without restriction, including without limitation the rights
2103
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
2104
+ * copies of the Software, and to permit persons to whom the Software is
2105
+ * furnished to do so, subject to the following conditions:
2106
+ *
2107
+ * The above copyright notice and this permission notice shall be included in
2108
+ * all copies or substantial portions of the Software.
2109
+ *
2110
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2111
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2112
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2113
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2114
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2115
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2116
+ * THE SOFTWARE.
2117
+ */
2118
+
2119
+ (function($) {
2120
+
2121
+ /**
2122
+ * Algolia Places API
2123
+ * @param {string} Your application ID
2124
+ * @param {string} Your API Key
2125
+ */
2126
+ window.AlgoliaPlaces = function(applicationID, apiKey) {
2127
+ this.init(applicationID, apiKey);
2128
+ };
2129
+
2130
+ AlgoliaPlaces.prototype = {
2131
+ /**
2132
+ * @param {string} Your application ID
2133
+ * @param {string} Your API Key
2134
+ */
2135
+ init: function(applicationID, apiKey) {
2136
+ this.client = new AlgoliaSearch(applicationID, apiKey, 'http', true, ['places-1.algolia.io', 'places-2.algolia.io', 'places-3.algolia.io']);
2137
+ this.cache = {};
2138
+ },
2139
+
2140
+ /**
2141
+ * Perform a query
2142
+ * @param {string} q the user query
2143
+ * @param {function} searchCallback the result callback called with two arguments:
2144
+ * success: boolean set to true if the request was successfull
2145
+ * content: the query answer with an extra 'disjunctiveFacets' attribute
2146
+ * @param {hash} the list of search parameters
2147
+ */
2148
+ search: function(q, searchCallback, searchParams) {
2149
+ var indexObj = this;
2150
+ var params = 'query=' + encodeURIComponent(q);
2151
+ if (!this.client._isUndefined(searchParams) && searchParams != null) {
2152
+ params = this.client._getSearchParams(searchParams, params);
2153
+ }
2154
+ var pObj = {params: params, apiKey: this.client.apiKey, appID: this.client.applicationID};
2155
+ this.client._jsonRequest({ cache: this.cache,
2156
+ method: 'POST',
2157
+ url: '/1/places/query',
2158
+ body: pObj,
2159
+ callback: searchCallback,
2160
+ removeCustomHTTPHeaders: true });
2161
+ }
2162
+ };
2163
+ })();
2164
+
2165
+ /*
2166
+ json2.js
2167
+ 2014-02-04
2168
+
2169
+ Public Domain.
2170
+
2171
+ NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
2172
+
2173
+ See http://www.JSON.org/js.html
2174
+
2175
+
2176
+ This code should be minified before deployment.
2177
+ See http://javascript.crockford.com/jsmin.html
2178
+
2179
+ USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
2180
+ NOT CONTROL.
2181
+
2182
+
2183
+ This file creates a global JSON object containing two methods: stringify
2184
+ and parse.
2185
+
2186
+ JSON.stringify(value, replacer, space)
2187
+ value any JavaScript value, usually an object or array.
2188
+
2189
+ replacer an optional parameter that determines how object
2190
+ values are stringified for objects. It can be a
2191
+ function or an array of strings.
2192
+
2193
+ space an optional parameter that specifies the indentation
2194
+ of nested structures. If it is omitted, the text will
2195
+ be packed without extra whitespace. If it is a number,
2196
+ it will specify the number of spaces to indent at each
2197
+ level. If it is a string (such as '\t' or '&nbsp;'),
2198
+ it contains the characters used to indent at each level.
2199
+
2200
+ This method produces a JSON text from a JavaScript value.
2201
+
2202
+ When an object value is found, if the object contains a toJSON
2203
+ method, its toJSON method will be called and the result will be
2204
+ stringified. A toJSON method does not serialize: it returns the
2205
+ value represented by the name/value pair that should be serialized,
2206
+ or undefined if nothing should be serialized. The toJSON method
2207
+ will be passed the key associated with the value, and this will be
2208
+ bound to the value
2209
+
2210
+ For example, this would serialize Dates as ISO strings.
2211
+
2212
+ Date.prototype.toJSON = function (key) {
2213
+ function f(n) {
2214
+ // Format integers to have at least two digits.
2215
+ return n < 10 ? '0' + n : n;
2216
+ }
2217
+
2218
+ return this.getUTCFullYear() + '-' +
2219
+ f(this.getUTCMonth() + 1) + '-' +
2220
+ f(this.getUTCDate()) + 'T' +
2221
+ f(this.getUTCHours()) + ':' +
2222
+ f(this.getUTCMinutes()) + ':' +
2223
+ f(this.getUTCSeconds()) + 'Z';
2224
+ };
2225
+
2226
+ You can provide an optional replacer method. It will be passed the
2227
+ key and value of each member, with this bound to the containing
2228
+ object. The value that is returned from your method will be
2229
+ serialized. If your method returns undefined, then the member will
2230
+ be excluded from the serialization.
2231
+
2232
+ If the replacer parameter is an array of strings, then it will be
2233
+ used to select the members to be serialized. It filters the results
2234
+ such that only members with keys listed in the replacer array are
2235
+ stringified.
2236
+
2237
+ Values that do not have JSON representations, such as undefined or
2238
+ functions, will not be serialized. Such values in objects will be
2239
+ dropped; in arrays they will be replaced with null. You can use
2240
+ a replacer function to replace those with JSON values.
2241
+ JSON.stringify(undefined) returns undefined.
2242
+
2243
+ The optional space parameter produces a stringification of the
2244
+ value that is filled with line breaks and indentation to make it
2245
+ easier to read.
2246
+
2247
+ If the space parameter is a non-empty string, then that string will
2248
+ be used for indentation. If the space parameter is a number, then
2249
+ the indentation will be that many spaces.
2250
+
2251
+ Example:
2252
+
2253
+ text = JSON.stringify(['e', {pluribus: 'unum'}]);
2254
+ // text is '["e",{"pluribus":"unum"}]'
2255
+
2256
+
2257
+ text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
2258
+ // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
2259
+
2260
+ text = JSON.stringify([new Date()], function (key, value) {
2261
+ return this[key] instanceof Date ?
2262
+ 'Date(' + this[key] + ')' : value;
2263
+ });
2264
+ // text is '["Date(---current time---)"]'
2265
+
2266
+
2267
+ JSON.parse(text, reviver)
2268
+ This method parses a JSON text to produce an object or array.
2269
+ It can throw a SyntaxError exception.
2270
+
2271
+ The optional reviver parameter is a function that can filter and
2272
+ transform the results. It receives each of the keys and values,
2273
+ and its return value is used instead of the original value.
2274
+ If it returns what it received, then the structure is not modified.
2275
+ If it returns undefined then the member is deleted.
2276
+
2277
+ Example:
2278
+
2279
+ // Parse the text. Values that look like ISO date strings will
2280
+ // be converted to Date objects.
2281
+
2282
+ myData = JSON.parse(text, function (key, value) {
2283
+ var a;
2284
+ if (typeof value === 'string') {
2285
+ a =
2286
+ /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
2287
+ if (a) {
2288
+ return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
2289
+ +a[5], +a[6]));
2290
+ }
2291
+ }
2292
+ return value;
2293
+ });
2294
+
2295
+ myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
2296
+ var d;
2297
+ if (typeof value === 'string' &&
2298
+ value.slice(0, 5) === 'Date(' &&
2299
+ value.slice(-1) === ')') {
2300
+ d = new Date(value.slice(5, -1));
2301
+ if (d) {
2302
+ return d;
2303
+ }
2304
+ }
2305
+ return value;
2306
+ });
2307
+
2308
+
2309
+ This is a reference implementation. You are free to copy, modify, or
2310
+ redistribute.
2311
+ */
2312
+
2313
+ /*jslint evil: true, regexp: true */
2314
+
2315
+ /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
2316
+ call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
2317
+ getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
2318
+ lastIndex, length, parse, prototype, push, replace, slice, stringify,
2319
+ test, toJSON, toString, valueOf
2320
+ */
2321
+
2322
+
2323
+ // Create a JSON object only if one does not already exist. We create the
2324
+ // methods in a closure to avoid creating global variables.
2325
+
2326
+ if (typeof JSON !== 'object') {
2327
+ JSON = {};
2328
+ }
2329
+
2330
+ (function () {
2331
+ 'use strict';
2332
+
2333
+ function f(n) {
2334
+ // Format integers to have at least two digits.
2335
+ return n < 10 ? '0' + n : n;
2336
+ }
2337
+
2338
+ if (typeof Date.prototype.toJSON !== 'function') {
2339
+
2340
+ Date.prototype.toJSON = function () {
2341
+
2342
+ return isFinite(this.valueOf())
2343
+ ? this.getUTCFullYear() + '-' +
2344
+ f(this.getUTCMonth() + 1) + '-' +
2345
+ f(this.getUTCDate()) + 'T' +
2346
+ f(this.getUTCHours()) + ':' +
2347
+ f(this.getUTCMinutes()) + ':' +
2348
+ f(this.getUTCSeconds()) + 'Z'
2349
+ : null;
2350
+ };
2351
+
2352
+ String.prototype.toJSON =
2353
+ Number.prototype.toJSON =
2354
+ Boolean.prototype.toJSON = function () {
2355
+ return this.valueOf();
2356
+ };
2357
+ }
2358
+
2359
+ var cx,
2360
+ escapable,
2361
+ gap,
2362
+ indent,
2363
+ meta,
2364
+ rep;
2365
+
2366
+
2367
+ function quote(string) {
2368
+
2369
+ // If the string contains no control characters, no quote characters, and no
2370
+ // backslash characters, then we can safely slap some quotes around it.
2371
+ // Otherwise we must also replace the offending characters with safe escape
2372
+ // sequences.
2373
+
2374
+ escapable.lastIndex = 0;
2375
+ return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
2376
+ var c = meta[a];
2377
+ return typeof c === 'string'
2378
+ ? c
2379
+ : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
2380
+ }) + '"' : '"' + string + '"';
2381
+ }
2382
+
2383
+
2384
+ function str(key, holder) {
2385
+
2386
+ // Produce a string from holder[key].
2387
+
2388
+ var i, // The loop counter.
2389
+ k, // The member key.
2390
+ v, // The member value.
2391
+ length,
2392
+ mind = gap,
2393
+ partial,
2394
+ value = holder[key];
2395
+
2396
+ // If the value has a toJSON method, call it to obtain a replacement value.
2397
+
2398
+ if (value && typeof value === 'object' &&
2399
+ typeof value.toJSON === 'function') {
2400
+ value = value.toJSON(key);
2401
+ }
2402
+
2403
+ // If we were called with a replacer function, then call the replacer to
2404
+ // obtain a replacement value.
2405
+
2406
+ if (typeof rep === 'function') {
2407
+ value = rep.call(holder, key, value);
2408
+ }
2409
+
2410
+ // What happens next depends on the value's type.
2411
+
2412
+ switch (typeof value) {
2413
+ case 'string':
2414
+ return quote(value);
2415
+
2416
+ case 'number':
2417
+
2418
+ // JSON numbers must be finite. Encode non-finite numbers as null.
2419
+
2420
+ return isFinite(value) ? String(value) : 'null';
2421
+
2422
+ case 'boolean':
2423
+ case 'null':
2424
+
2425
+ // If the value is a boolean or null, convert it to a string. Note:
2426
+ // typeof null does not produce 'null'. The case is included here in
2427
+ // the remote chance that this gets fixed someday.
2428
+
2429
+ return String(value);
2430
+
2431
+ // If the type is 'object', we might be dealing with an object or an array or
2432
+ // null.
2433
+
2434
+ case 'object':
2435
+
2436
+ // Due to a specification blunder in ECMAScript, typeof null is 'object',
2437
+ // so watch out for that case.
2438
+
2439
+ if (!value) {
2440
+ return 'null';
2441
+ }
2442
+
2443
+ // Make an array to hold the partial results of stringifying this object value.
2444
+
2445
+ gap += indent;
2446
+ partial = [];
2447
+
2448
+ // Is the value an array?
2449
+
2450
+ if (Object.prototype.toString.apply(value) === '[object Array]') {
2451
+
2452
+ // The value is an array. Stringify every element. Use null as a placeholder
2453
+ // for non-JSON values.
2454
+
2455
+ length = value.length;
2456
+ for (i = 0; i < length; i += 1) {
2457
+ partial[i] = str(i, value) || 'null';
2458
+ }
2459
+
2460
+ // Join all of the elements together, separated with commas, and wrap them in
2461
+ // brackets.
2462
+
2463
+ v = partial.length === 0
2464
+ ? '[]'
2465
+ : gap
2466
+ ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
2467
+ : '[' + partial.join(',') + ']';
2468
+ gap = mind;
2469
+ return v;
2470
+ }
2471
+
2472
+ // If the replacer is an array, use it to select the members to be stringified.
2473
+
2474
+ if (rep && typeof rep === 'object') {
2475
+ length = rep.length;
2476
+ for (i = 0; i < length; i += 1) {
2477
+ if (typeof rep[i] === 'string') {
2478
+ k = rep[i];
2479
+ v = str(k, value);
2480
+ if (v) {
2481
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
2482
+ }
2483
+ }
2484
+ }
2485
+ } else {
2486
+
2487
+ // Otherwise, iterate through all of the keys in the object.
2488
+
2489
+ for (k in value) {
2490
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
2491
+ v = str(k, value);
2492
+ if (v) {
2493
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
2494
+ }
2495
+ }
2496
+ }
2497
+ }
2498
+
2499
+ // Join all of the member texts together, separated with commas,
2500
+ // and wrap them in braces.
2501
+
2502
+ v = partial.length === 0
2503
+ ? '{}'
2504
+ : gap
2505
+ ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
2506
+ : '{' + partial.join(',') + '}';
2507
+ gap = mind;
2508
+ return v;
2509
+ }
2510
+ }
2511
+
2512
+ // If the JSON object does not yet have a stringify method, give it one.
2513
+
2514
+ if (typeof JSON.stringify !== 'function') {
2515
+ escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
2516
+ meta = { // table of character substitutions
2517
+ '\b': '\\b',
2518
+ '\t': '\\t',
2519
+ '\n': '\\n',
2520
+ '\f': '\\f',
2521
+ '\r': '\\r',
2522
+ '"' : '\\"',
2523
+ '\\': '\\\\'
2524
+ };
2525
+ JSON.stringify = function (value, replacer, space) {
2526
+
2527
+ // The stringify method takes a value and an optional replacer, and an optional
2528
+ // space parameter, and returns a JSON text. The replacer can be a function
2529
+ // that can replace values, or an array of strings that will select the keys.
2530
+ // A default replacer method can be provided. Use of the space parameter can
2531
+ // produce text that is more easily readable.
2532
+
2533
+ var i;
2534
+ gap = '';
2535
+ indent = '';
2536
+
2537
+ // If the space parameter is a number, make an indent string containing that
2538
+ // many spaces.
2539
+
2540
+ if (typeof space === 'number') {
2541
+ for (i = 0; i < space; i += 1) {
2542
+ indent += ' ';
2543
+ }
2544
+
2545
+ // If the space parameter is a string, it will be used as the indent string.
2546
+
2547
+ } else if (typeof space === 'string') {
2548
+ indent = space;
2549
+ }
2550
+
2551
+ // If there is a replacer, it must be a function or an array.
2552
+ // Otherwise, throw an error.
2553
+
2554
+ rep = replacer;
2555
+ if (replacer && typeof replacer !== 'function' &&
2556
+ (typeof replacer !== 'object' ||
2557
+ typeof replacer.length !== 'number')) {
2558
+ throw new Error('JSON.stringify');
2559
+ }
2560
+
2561
+ // Make a fake root object containing our value under the key of ''.
2562
+ // Return the result of stringifying the value.
2563
+
2564
+ return str('', {'': value});
2565
+ };
2566
+ }
2567
+
2568
+
2569
+ // If the JSON object does not yet have a parse method, give it one.
2570
+
2571
+ if (typeof JSON.parse !== 'function') {
2572
+ cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
2573
+ JSON.parse = function (text, reviver) {
2574
+
2575
+ // The parse method takes a text and an optional reviver function, and returns
2576
+ // a JavaScript value if the text is a valid JSON text.
2577
+
2578
+ var j;
2579
+
2580
+ function walk(holder, key) {
2581
+
2582
+ // The walk method is used to recursively walk the resulting structure so
2583
+ // that modifications can be made.
2584
+
2585
+ var k, v, value = holder[key];
2586
+ if (value && typeof value === 'object') {
2587
+ for (k in value) {
2588
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
2589
+ v = walk(value, k);
2590
+ if (v !== undefined) {
2591
+ value[k] = v;
2592
+ } else {
2593
+ delete value[k];
2594
+ }
2595
+ }
2596
+ }
2597
+ }
2598
+ return reviver.call(holder, key, value);
2599
+ }
2600
+
2601
+
2602
+ // Parsing happens in four stages. In the first stage, we replace certain
2603
+ // Unicode characters with escape sequences. JavaScript handles many characters
2604
+ // incorrectly, either silently deleting them, or treating them as line endings.
2605
+
2606
+ text = String(text);
2607
+ cx.lastIndex = 0;
2608
+ if (cx.test(text)) {
2609
+ text = text.replace(cx, function (a) {
2610
+ return '\\u' +
2611
+ ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
2612
+ });
2613
+ }
2614
+
2615
+ // In the second stage, we run the text against regular expressions that look
2616
+ // for non-JSON patterns. We are especially concerned with '()' and 'new'
2617
+ // because they can cause invocation, and '=' because it can cause mutation.
2618
+ // But just to be safe, we want to reject all unexpected forms.
2619
+
2620
+ // We split the second stage into 4 regexp operations in order to work around
2621
+ // crippling inefficiencies in IE's and Safari's regexp engines. First we
2622
+ // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
2623
+ // replace all simple value tokens with ']' characters. Third, we delete all
2624
+ // open brackets that follow a colon or comma or that begin the text. Finally,
2625
+ // we look to see that the remaining characters are only whitespace or ']' or
2626
+ // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
2627
+
2628
+ if (/^[\],:{}\s]*$/
2629
+ .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
2630
+ .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
2631
+ .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
2632
+
2633
+ // In the third stage we use the eval function to compile the text into a
2634
+ // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
2635
+ // in JavaScript: it can begin a block or an object literal. We wrap the text
2636
+ // in parens to eliminate the ambiguity.
2637
+
2638
+ j = eval('(' + text + ')');
2639
+
2640
+ // In the optional fourth stage, we recursively walk the new structure, passing
2641
+ // each name/value pair to a reviver function for possible transformation.
2642
+
2643
+ return typeof reviver === 'function'
2644
+ ? walk({'': j}, '')
2645
+ : j;
2646
+ }
2647
+
2648
+ // If the text is not JSON parseable, then a SyntaxError is thrown.
2649
+
2650
+ throw new SyntaxError('JSON.parse');
2651
+ };
2652
+ }
2653
+ }());
2654
+
2655
+ /* global angular */
2656
+ angular.module('algoliasearch', [])
2657
+ .service('algolia', ['$injector', function ($injector) {
2658
+ return {
2659
+ Client: function(applicationID, apiKey, options) {
2660
+ options = options || {};
2661
+ options.angular = {
2662
+ '$injector': $injector
2663
+ };
2664
+ return new AlgoliaSearch(applicationID, apiKey, options);
2665
+ }
2666
+ };
2667
+ }]);