algoliasearch-rails 1.11.17 → 1.11.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
@@ -0,0 +1,37 @@
1
+ /*! algoliasearch 3.0.3 | © 2014, 2015 Algolia SAS | github.com/algolia/algoliasearch-client-js */
2
+ (function(l){var m;"undefined"!==typeof window?m=window:"undefined"!==typeof self&&(m=self);m.ALGOLIA_MIGRATION_LAYER=l()})(function(){return function m(n,k,e){function h(a,c){if(!k[a]){if(!n[a]){var d="function"==typeof require&&require;if(!c&&d)return d(a,!0);if(g)return g(a,!0);d=Error("Cannot find module '"+a+"'");throw d.code="MODULE_NOT_FOUND",d;}d=k[a]={exports:{}};n[a][0].call(d.exports,function(c){var d=n[a][1][c];return h(d?d:c)},d,d.exports,m,n,k,e)}return k[a].exports}for(var g="function"==
3
+ typeof require&&require,a=0;a<e.length;a++)h(e[a]);return h}({1:[function(m,n,k){function e(a,b){for(var c in b)a.setAttribute(c,b[c])}function h(a,b){a.onload=function(){this.onerror=this.onload=null;b(null,a)};a.onerror=function(){this.onerror=this.onload=null;b(Error("Failed to load "+this.src),a)}}function g(a,b){a.onreadystatechange=function(){if("complete"==this.readyState||"loaded"==this.readyState)this.onreadystatechange=null,b(null,a)}}n.exports=function(a,b,c){var d=document.head||document.getElementsByTagName("head")[0],
4
+ f=document.createElement("script");"function"===typeof b&&(c=b,b={});b=b||{};c=c||function(){};f.type=b.type||"text/javascript";f.charset=b.charset||"utf8";f.async="async"in b?!!b.async:!0;f.src=a;b.attrs&&e(f,b.attrs);b.text&&(f.text=""+b.text);("onload"in f?h:g)(f,c);f.onload||h(f,c);d.appendChild(f)}},{}],2:[function(m,n,k){n.exports=function(e){e=new RegExp("cdn\\.jsdelivr\\.net/algoliasearch/latest/"+e.replace(".","\\.")+"(?:\\.min)?\\.js$");for(var h=document.getElementsByTagName("script"),
5
+ g=!1,a=0,b=h.length;a<b;a++)if(h[a].src&&e.test(h[a].src)){g=!0;break}return g}},{}],3:[function(m,n,k){(function(e){function h(h){return function(){var a="AlgoliaSearch: loaded V2 script using "+h;e.console&&e.console.log&&e.console.log(a)}}n.exports=function(g){var a=m(1);g="//cdn.jsdelivr.net/algoliasearch/2/"+g+".min.js";e.console&&(e.console.warn?e.console.warn("-- AlgoliaSearch `latest` warning --\nWarning, you are using the `latest` version string from jsDelivr to load the AlgoliaSearch library.\nUsing `latest` is no more recommended, you should load //cdn.jsdelivr.net/algoliasearch/2/algoliasearch.min.js\n\nAlso, we updated the AlgoliaSearch JavaScript client to V3. If you want to upgrade,\nplease read our migration guide at https://github.com/algolia/algoliasearch-client-js/wiki/Migration-guide-from-2.x.x-to-3.x.x\n-- /AlgoliaSearch `latest` warning --"):
6
+ e.console.log&&e.console.log("-- AlgoliaSearch `latest` warning --\nWarning, you are using the `latest` version string from jsDelivr to load the AlgoliaSearch library.\nUsing `latest` is no more recommended, you should load //cdn.jsdelivr.net/algoliasearch/2/algoliasearch.min.js\n\nAlso, we updated the AlgoliaSearch JavaScript client to V3. If you want to upgrade,\nplease read our migration guide at https://github.com/algolia/algoliasearch-client-js/wiki/Migration-guide-from-2.x.x-to-3.x.x\n-- /AlgoliaSearch `latest` warning --"));
7
+ try{document.write("<script>window.ALGOLIA_SUPPORTS_DOCWRITE = true\x3c/script>"),!0===e.ALGOLIA_SUPPORTS_DOCWRITE?(document.write('<script src="'+g+'">\x3c/script>'),h("document.write")()):a(g,h("DOMElement"))}catch(b){a(g,h("DOMElement"))}}}).call(this,"undefined"!==typeof global?global:"undefined"!==typeof self?self:"undefined"!==typeof window?window:{})},{1:1}],4:[function(m,n,k){(function(e){n.exports=function(){e.AlgoliaSearch=function(){throw Error("-- AlgoliaSearch V2 => V3 error --\nYou are trying to use a new version of the AlgoliaSearch JavaScript client with an old notation.\nPlease read our migration guide at https://github.com/algolia/algoliasearch-client-js/wiki/Migration-guide-from-2.x.x-to-3.x.x\n-- /AlgoliaSearch V2 => V3 error --");
8
+ };e.AlgoliaSearchHelper=function(){throw Error("-- AlgoliaSearch V2 => V3 error --\nYou are trying to use a new version of the AlgoliaSearch JavaScript client with an old notation.\nPlease read our migration guide at https://github.com/algolia/algoliasearch-client-js/wiki/Migration-guide-from-2.x.x-to-3.x.x\n-- /AlgoliaSearch V2 => V3 error --");};AlgoliaExplainResults=function(){throw Error("-- AlgoliaSearch V2 => V3 error --\nYou are trying to use a new version of the AlgoliaSearch JavaScript client with an old notation.\nPlease read our migration guide at https://github.com/algolia/algoliasearch-client-js/wiki/Migration-guide-from-2.x.x-to-3.x.x\n-- /AlgoliaSearch V2 => V3 error --");
9
+ }}}).call(this,"undefined"!==typeof global?global:"undefined"!==typeof self?self:"undefined"!==typeof window?window:{})},{}],5:[function(m,n,k){n=m(2);k=m(3);m=m(4);n("algoliasearch.angular")?k("algoliasearch.angular"):m()},{2:2,3:3,4:4}]},{},[5])(5)});
10
+ (function e$$0(m,n,k){function e(a,b){if(!n[a]){if(!m[a]){var c="function"==typeof require&&require;if(!b&&c)return c(a,!0);if(h)return h(a,!0);c=Error("Cannot find module '"+a+"'");throw c.code="MODULE_NOT_FOUND",c;}c=n[a]={exports:{}};m[a][0].call(c.exports,function(b){var c=m[a][1][b];return e(c?c:b)},c,c.exports,e$$0,m,n,k)}return n[a].exports}for(var h="function"==typeof require&&require,g=0;g<k.length;g++)e(k[g]);return e})({1:[function(l,m,n){function k(){if(!g){g=!0;for(var a,b=h.length;b;){a=
11
+ h;h=[];for(var c=-1;++c<b;)a[c]();b=h.length}g=!1}}function e(){}l=m.exports={};var h=[],g=!1;l.nextTick=function(a){h.push(a);g||setTimeout(k,0)};l.title="browser";l.browser=!0;l.env={};l.argv=[];l.version="";l.versions={};l.on=e;l.addListener=e;l.once=e;l.off=e;l.removeListener=e;l.removeAllListeners=e;l.emit=e;l.binding=function(a){throw Error("process.binding is not supported");};l.cwd=function(){return"/"};l.chdir=function(a){throw Error("process.chdir is not supported");};l.umask=function(){return 0}},
12
+ {}],2:[function(l,m,n){(function(k){function e(a,b,c,d){if(!a)throw Error("Please provide an application ID. Usage: algoliasearch(applicationID, apiKey, opts)");if(!b)throw Error("Please provide an API key. Usage: algoliasearch(applicationID, apiKey, opts)");c=c||{};void 0===c.timeout&&(c.timeout=2E3);void 0===c.protocol&&(c.protocol=document&&document.location.protocol||"http:");void 0===c.hosts&&(c.hosts=[]);void 0===c.tld&&(c.tld="net");/:$/.test(c.protocol)||(c.protocol+=":");0===c.hosts.length&&
13
+ (c.hosts=g([a+"-1.algolia."+c.tld,a+"-2.algolia."+c.tld,a+"-3.algolia."+c.tld]),c.hosts.unshift(a+"-dsn.algolia."+c.tld));c.hosts=h(c.hosts,function(a){return c.protocol+"//"+a});this.applicationID=a;this.apiKey=b;this.hosts=c.hosts;this.currentHostIndex=0;this.requestTimeout=c.timeout;this.extraHeaders=[];this.cache={};this._request=d}function h(a,b){for(var c=[],d=0;d<a.length;++d)c.push(b(a[d],d));return c}function g(a){for(var b=a.length,c,d;0!==b;)d=Math.floor(Math.random()*b),--b,c=a[b],a[b]=
14
+ a[d],a[d]=c;return a}m.exports=e;e.prototype={deleteIndex:function(a,b){return this._jsonRequest({method:"DELETE",url:"/1/indexes/"+encodeURIComponent(a),callback:b})},moveIndex:function(a,b,c){b={operation:"move",destination:b};return this._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(a)+"/operation",body:b,callback:c})},copyIndex:function(a,b,c){b={operation:"copy",destination:b};return this._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(a)+"/operation",body:b,
15
+ callback:c})},getLogs:function(a,b,c){if(0===arguments.length||"function"===typeof a)c=a,a=0,b=10;else if(1===arguments.length||"function"===typeof b)c=b,b=10;return this._jsonRequest({method:"GET",url:"/1/logs?offset="+a+"&length="+b,callback:c})},listIndexes:function(a,b){var c="";void 0===a||"function"===typeof a?b=a:c="?page="+a;return this._jsonRequest({method:"GET",url:"/1/indexes"+c,callback:b})},initIndex:function(a){return new this.Index(this,a)},listUserKeys:function(a){return this._jsonRequest({method:"GET",
16
+ 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,{validity:0,maxQueriesPerIPPerHour:0,maxHitsPerQuery:0},b)},addUserKeyWithValidity:function(a,b,c){var d={};d.acl=a;d.validity=b.validity;d.maxQueriesPerIPPerHour=b.maxQueriesPerIPPerHour;d.maxHitsPerQuery=b.maxHitsPerQuery;
17
+ return this._jsonRequest({method:"POST",url:"/1/keys",body:d,callback:c})},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=[],f=0;f<a[c].length;++f)d.push(a[c][f]);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){b=
18
+ "query="+encodeURIComponent(b);this._isUndefined(c)||null===c||(b=this._getSearchParams(c,b));this.batch.push({indexName:a,params:b})},clearCache:function(){this.cache={}},sendQueriesBatch:function(a){for(var b={requests:[]},c=0;c<this.batch.length;++c)b.requests.push(this.batch[c]);return this._sendQueriesBatch(b,a)},setRequestTimeout:function(a){a&&(this.requestTimeout=parseInt(a,10))},Index:function(a,b){this.indexName=b;this.as=a;this.typeAheadValueOption=this.typeAheadArgs=null;this.cache={}},
19
+ setExtraHeader:function(a,b){this.extraHeaders.push({key:a,value:b})},_sendQueriesBatch:function(a,b){return this._jsonRequest({cache:this.cache,method:"POST",url:"/1/indexes/*/queries",body:a,fallback:{method:"GET",url:"/1/indexes/*",body:{params:function(){for(var b="",d=0;d<a.requests.length;++d)var f="/1/indexes/"+encodeURIComponent(a.requests[d].indexName)+"?"+a.requests[d].params,b=b+(d+"="+encodeURIComponent(f)+"&");return b}()}},callback:b})},_jsonRequest:function(a){function b(h,g){function m(){f.currentHostIndex=
20
+ ++f.currentHostIndex%f.hosts.length;e+=1;g.timeout=f.requestTimeout*(e+1);return b(h,g)}if(c&&void 0!==c[d])return f._request.resolve(c[d]);if(e>=f.hosts.length){if(!a.fallback||h===f._request.fallback)return f._request.reject(Error("Cannot connect to the AlgoliaSearch API. Send an email to support@algolia.com to report and resolve the issue."));e=0;g.method=a.fallback.method;g.url=a.fallback.url;g.body=a.fallback.body;g.timeout=f.requestTimeout*(e+1);f.currentHostIndex=0;f.forceFallback=!0;return b(f._request.fallback,
21
+ g)}var k=g.url,k=k+((-1===k.indexOf("?")?"?":"&")+"X-Algolia-API-Key="+f.apiKey),k=k+("&X-Algolia-Application-Id="+f.applicationID);f.userToken&&(k+="&X-Algolia-UserToken="+encodeURIComponent(f.userToken));f.tagFilters&&(k+="&X-Algolia-TagFilters="+encodeURIComponent(f.tagFilters));for(var l=0;l<f.extraHeaders.length;++l)k+="&"+f.extraHeaders[l].key+"="+f.extraHeaders[l].value;return h(f.hosts[f.currentHostIndex]+k,{body:g.body,method:g.method,timeout:g.timeout}).then(function(a){if(a instanceof Error)return m();
22
+ var b=a&&a.body&&a.body.message&&a.body.status||a.statusCode||a&&a.body&&200,e=200===b||201===b,b=!e&&4!==Math.floor(b/100)&&1!==Math.floor(b/100);e&&c&&(c[d]=a.body);return e?a.body:b?m():f._request.reject(Error(a.body&&a.body.message||"Unknown error"))},function(){f.forceFallback?(f.currentHostIndex=++f.currentHostIndex%f.hosts.length,e+=1):e=f.hosts.length;return b(h,g)})}var c=a.cache,d=a.url,f=this,e=0;void 0!==a.body&&(d+="_body_"+JSON.stringify(a.body));var h=f.forceFallback&&a.fallback,g=
23
+ h?a.fallback:a,h=b(h?f._request.fallback:f._request,{url:g.url,method:g.method,body:g.body,timeout:f.requestTimeout*(e+1)});if(a.callback)h.then(function(b){k.nextTick(function(){a.callback(null,b)})},function(b){k.nextTick(function(){a.callback(b)})});else return h},_getSearchParams:function(a,b){if(this._isUndefined(a)||null===a)return b;for(var c in a)null!==c&&a.hasOwnProperty(c)&&(b+=""===b?"":"&",b+=c+"="+encodeURIComponent("[object Array]"===Object.prototype.toString.call(a[c])?JSON.stringify(a[c]):
24
+ a[c]));return b},_isUndefined:function(a){return void 0===a}};e.prototype.Index.prototype={clearCache:function(){this.cache={}},addObject:function(a,b,c){if(1===arguments.length||"function"===typeof b)c=b,b=void 0;return this.as._jsonRequest({method:void 0!==b?"PUT":"POST",url:"/1/indexes/"+encodeURIComponent(this.indexName)+(void 0!==b?"/"+encodeURIComponent(b):""),body:a,callback:c})},addObjects:function(a,b){for(var c={requests:[]},d=0;d<a.length;++d)c.requests.push({action:"addObject",body:a[d]});
25
+ return this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/batch",body:c,callback:b})},getObject:function(a,b,c){if(1===arguments.length||"function"===typeof b)c=b,b=void 0;var d="";if(void 0!==b)for(var d="?attributes=",f=0;f<b.length;++f)0!==f&&(d+=","),d+=b[f];return this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/"+encodeURIComponent(a)+d,callback:c})},partialUpdateObject:function(a,b){return this.as._jsonRequest({method:"POST",
26
+ url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/"+encodeURIComponent(a.objectID)+"/partial",body:a,callback:b})},partialUpdateObjects:function(a,b){for(var c={requests:[]},d=0;d<a.length;++d)c.requests.push({action:"partialUpdateObject",objectID:a[d].objectID,body:a[d]});return this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/batch",body:c,callback:b})},saveObject:function(a,b){return this.as._jsonRequest({method:"PUT",url:"/1/indexes/"+encodeURIComponent(this.indexName)+
27
+ "/"+encodeURIComponent(a.objectID),body:a,callback:b})},saveObjects:function(a,b){for(var c={requests:[]},d=0;d<a.length;++d)c.requests.push({action:"updateObject",objectID:a[d].objectID,body:a[d]});return this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/batch",body:c,callback:b})},deleteObject:function(a,b){if("function"===typeof a||"string"!==typeof a&&"number"!==typeof a){var c=Error("Cannot delete an object without an objectID");b=a;return"function"===
28
+ typeof b?b(c):this.as._request.reject(c)}return this.as._jsonRequest({method:"DELETE",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/"+encodeURIComponent(a),callback:b})},search:function(a,b,c){if(0===arguments.length||"function"===typeof a)c=a,a="";else if(1===arguments.length||"function"===typeof b)c=b,b=void 0;if("object"===typeof a&&null!==a)b=a,a=void 0;else if(void 0===a||null===a)a="";var d="";void 0!==a&&(d+="query="+encodeURIComponent(a));void 0!==b&&(d=this.as._getSearchParams(b,
29
+ d));return this._search(d,c)},browse:function(a,b,c){if(1===arguments.length||"function"===typeof b)c=b,b=void 0;var d="?page="+a;this.as._isUndefined(b)||(d+="&hitsPerPage="+b);return this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/browse"+d,callback:c})},ttAdapter:function(a){var b=this;return function(c,d){b.search(c,a,function(a,b){a?d(a):d(b.hits)})}},waitTask:function(a,b){var c=this,d=this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(c.indexName)+
30
+ "/task/"+a}).then(function(d){if("published"!==d.status)return(new c.as._request.delay(100)).then(function(){return c.waitTask(a,b)});if(b)k.nextTick(function(){b(null,d)});else return d},function(a){if(b)k.nextTick(function(){b(a)});else return a});if(!b)return d},clearIndex:function(a){return this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/clear",callback:a})},getSettings:function(a){return this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(this.indexName)+
31
+ "/settings",callback:a})},setSettings:function(a,b){return this.as._jsonRequest({method:"PUT",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/settings",body:a,callback:b})},listUserKeys:function(a){return this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/keys",callback:a})},getUserKeyACL:function(a,b){return this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/keys/"+a,callback:b})},deleteUserKey:function(a,b){return this.as._jsonRequest({method:"DELETE",
32
+ url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/keys/"+a,callback:b})},addUserKey:function(a,b){var c={};c.acl=a;return this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/keys",body:c,callback:b})},addUserKeyWithValidity:function(a,b,c){var d={};d.acl=a;d.validity=b.validity;d.maxQueriesPerIPPerHour=b.maxQueriesPerIPPerHour;d.maxHitsPerQuery=b.maxHitsPerQuery;return this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(this.indexName)+
33
+ "/keys",body:d,callback:c})},_search:function(a,b){return this.as._jsonRequest({cache:this.cache,method:"POST",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/query",body:{params:a},fallback:{method:"GET",url:"/1/indexes/"+encodeURIComponent(this.indexName),body:{params:a}},callback:b})},as:null,indexName:null,typeAheadArgs:null,typeAheadValueOption:null}}).call(this,l(1))},{1:1}],3:[function(l,m,n){(function(k){var e=l(5),h=l(4);k.angular.module("algoliasearch",[]).service("algolia",["$http",
34
+ "$q","$timeout",function(g,a,b){function c(c,d){return a(function(e,h){var k,l=null;void 0!==d.body&&(l=JSON.stringify(d.body));var m=a(function(a){b(function(){k=!0;a("test");e(Error("Timeout - Could not connect to endpoint "+c))},d.timeout)});g({url:c,method:d.method,data:l,cache:!1,timeout:m}).then(function(a){e({statusCode:a.status,body:a.data})},function(a){k||(0===a.status?h(Error("Network error")):e({body:a.data,statusCode:a.status}))})})}c.fallback=function(b,c){return a(function(a,d){h(b,
35
+ c,function(b,c){b?d(b):a(c)})})};c.reject=function(b){return a.reject(b)};c.resolve=function(b){return a.when(b)};c.delay=function(c){return a(function(a){b(a,c)})};var d=e(c);return{Client:function(a,b,c){return d(a,b,c)}}}])}).call(this,"undefined"!==typeof global?global:"undefined"!==typeof self?self:"undefined"!==typeof window?window:{})},{4:4,5:5}],4:[function(l,m,n){m.exports=function(e,h,g){function a(){r||m||(r=!0,l||(c(),g(Error("Failed to load JSONP script"))))}function b(){"loaded"!==this.readyState&&
36
+ "complete"!==this.readyState||a()}function c(){clearTimeout(t);p.onload=null;p.onreadystatechange=null;p.onerror=null;n.removeChild(p);try{delete window[q],delete window[q+"_loaded"]}catch(a){window[q]=null,window[q+"_loaded"]=null}}function d(){m=!0;c();g(Error("Timeout - Could not connect to endpoint "+e))}function f(){r||m||(c(),g(Error("Failed to load JSONP script")))}if("GET"!==h.method)g(Error("Method "+h.method+" "+e+" is not supported by JSONP."));else{var l=!1,m=!1;k+=1;var n=document.getElementsByTagName("head")[0],
37
+ p=document.createElement("script"),q="algoliaJSONP_"+k,r=!1;window[q]=function(a){try{delete window[q]}catch(b){window[q]=void 0}m||(l=!0,c(),g(null,{body:a}))};e+="&callback="+q;h.body&&h.body.params&&(e+="&"+h.body.params);var t=setTimeout(d,h.timeout);p.onreadystatechange=b;p.onload=a;p.onerror=f;p.async=!0;p.defer=!0;p.src=e;n.appendChild(p)}};var k=0},{}],5:[function(l,m,n){m.exports=function(k){function e(e,g,a){return new (l(2))(e,g,a,k)}e.version="3.0.3";return e}},{2:2}]},{},[3]);
@@ -0,0 +1,1702 @@
1
+ /*! algoliasearch 3.0.3 | © 2014, 2015 Algolia SAS | github.com/algolia/algoliasearch-client-js */
2
+ (function(f){var g;if(typeof window!=='undefined'){g=window}else if(typeof self!=='undefined'){g=self}g.ALGOLIA_MIGRATION_LAYER=f()})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
3
+
4
+ module.exports = function load (src, opts, cb) {
5
+ var head = document.head || document.getElementsByTagName('head')[0]
6
+ var script = document.createElement('script')
7
+
8
+ if (typeof opts === 'function') {
9
+ cb = opts
10
+ opts = {}
11
+ }
12
+
13
+ opts = opts || {}
14
+ cb = cb || function() {}
15
+
16
+ script.type = opts.type || 'text/javascript'
17
+ script.charset = opts.charset || 'utf8';
18
+ script.async = 'async' in opts ? !!opts.async : true
19
+ script.src = src
20
+
21
+ if (opts.attrs) {
22
+ setAttributes(script, opts.attrs)
23
+ }
24
+
25
+ if (opts.text) {
26
+ script.text = '' + opts.text
27
+ }
28
+
29
+ var onend = 'onload' in script ? stdOnEnd : ieOnEnd
30
+ onend(script, cb)
31
+
32
+ // some good legacy browsers (firefox) fail the 'in' detection above
33
+ // so as a fallback we always set onload
34
+ // old IE will ignore this and new IE will set onload
35
+ if (!script.onload) {
36
+ stdOnEnd(script, cb);
37
+ }
38
+
39
+ head.appendChild(script)
40
+ }
41
+
42
+ function setAttributes(script, attrs) {
43
+ for (var attr in attrs) {
44
+ script.setAttribute(attr, attrs[attr]);
45
+ }
46
+ }
47
+
48
+ function stdOnEnd (script, cb) {
49
+ script.onload = function () {
50
+ this.onerror = this.onload = null
51
+ cb(null, script)
52
+ }
53
+ script.onerror = function () {
54
+ // this.onload = null here is necessary
55
+ // because even IE9 works not like others
56
+ this.onerror = this.onload = null
57
+ cb(new Error('Failed to load ' + this.src), script)
58
+ }
59
+ }
60
+
61
+ function ieOnEnd (script, cb) {
62
+ script.onreadystatechange = function () {
63
+ if (this.readyState != 'complete' && this.readyState != 'loaded') return
64
+ this.onreadystatechange = null
65
+ cb(null, script) // there is no way to catch loading errors in IE8
66
+ }
67
+ }
68
+
69
+ },{}],2:[function(require,module,exports){
70
+ // this module helps finding if the current page is using
71
+ // the cdn.jsdelivr.net/algoliasearch/latest/$BUILDNAME.min.js version
72
+
73
+ module.exports = isUsingLatest;
74
+
75
+ function isUsingLatest(buildName) {
76
+ var toFind = new RegExp('cdn\\.jsdelivr\\.net/algoliasearch/latest/' +
77
+ buildName.replace('.', '\\.') + // algoliasearch, algoliasearch.angular
78
+ '(?:\\.min)?\\.js$'); // [.min].js
79
+
80
+ var scripts = document.getElementsByTagName('script');
81
+ var found = false;
82
+ for (var currentScript = 0, nbScripts = scripts.length;
83
+ currentScript < nbScripts;
84
+ currentScript++) {
85
+ if (scripts[currentScript].src && toFind.test(scripts[currentScript].src)) {
86
+ found = true;
87
+ break;
88
+ }
89
+ }
90
+
91
+ return found;
92
+ }
93
+
94
+ },{}],3:[function(require,module,exports){
95
+ (function (global){
96
+ module.exports = loadV2;
97
+
98
+ function loadV2(buildName) {
99
+ var loadScript = require(1);
100
+ var v2ScriptUrl = '//cdn.jsdelivr.net/algoliasearch/2/' + buildName + '.min.js';
101
+
102
+ var message =
103
+ '-- AlgoliaSearch `latest` warning --\n' +
104
+ 'Warning, you are using the `latest` version string from jsDelivr to load the AlgoliaSearch library.\n' +
105
+ 'Using `latest` is no more recommended, you should load //cdn.jsdelivr.net/algoliasearch/2/algoliasearch.min.js\n\n' +
106
+ 'Also, we updated the AlgoliaSearch JavaScript client to V3. If you want to upgrade,\n' +
107
+ 'please read our migration guide at https://github.com/algolia/algoliasearch-client-js/wiki/Migration-guide-from-2.x.x-to-3.x.x\n' +
108
+ '-- /AlgoliaSearch `latest` warning --';
109
+
110
+ if (global.console) {
111
+ if (global.console.warn) {
112
+ global.console.warn(message);
113
+ } else if (global.console.log) {
114
+ global.console.log(message);
115
+ }
116
+ }
117
+
118
+ // If current script loaded asynchronously,
119
+ // it will load the script with DOMElement
120
+ // otherwise, it will load the script with document.write
121
+ try {
122
+ // why \x3c? http://stackoverflow.com/a/236106/147079
123
+ document.write('\x3Cscript>window.ALGOLIA_SUPPORTS_DOCWRITE = true\x3C/script>');
124
+
125
+ if (global.ALGOLIA_SUPPORTS_DOCWRITE === true) {
126
+ document.write('\x3Cscript src="' + v2ScriptUrl + '">\x3C/script>');
127
+ scriptLoaded('document.write')();
128
+ } else {
129
+ loadScript(v2ScriptUrl, scriptLoaded('DOMElement'));
130
+ }
131
+ } catch(e) {
132
+ loadScript(v2ScriptUrl, scriptLoaded('DOMElement'));
133
+ }
134
+ }
135
+
136
+ function scriptLoaded(method) {
137
+ return function log() {
138
+ var message = 'AlgoliaSearch: loaded V2 script using ' + method;
139
+
140
+ global.console && global.console.log && global.console.log(message);
141
+ };
142
+ }
143
+
144
+ }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
145
+ },{"1":1}],4:[function(require,module,exports){
146
+ (function (global){
147
+ /*global AlgoliaExplainResults:true*/
148
+ /*eslint no-unused-vars: [2, {"vars": "local"}]*/
149
+
150
+ module.exports = oldGlobals;
151
+
152
+ // put old window.AlgoliaSearch.. into window. again so that
153
+ // users upgrading to V3 without changing their code, will be warned
154
+ function oldGlobals() {
155
+ var message =
156
+ '-- AlgoliaSearch V2 => V3 error --\n' +
157
+ 'You are trying to use a new version of the AlgoliaSearch JavaScript client with an old notation.\n' +
158
+ 'Please read our migration guide at https://github.com/algolia/algoliasearch-client-js/wiki/Migration-guide-from-2.x.x-to-3.x.x\n' +
159
+ '-- /AlgoliaSearch V2 => V3 error --';
160
+
161
+ global.AlgoliaSearch = function() {
162
+ throw new Error(message);
163
+ };
164
+
165
+ global.AlgoliaSearchHelper = function() {
166
+ throw new Error(message);
167
+ };
168
+
169
+ // cannot use window.AlgoliaExplainResults on old IEs, dunno why
170
+ AlgoliaExplainResults = function() {
171
+ throw new Error(message);
172
+ };
173
+ }
174
+
175
+ }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
176
+ },{}],5:[function(require,module,exports){
177
+ // This script will be browserified and prepended to the normal build
178
+ // directly in window, not wrapped in any module definition
179
+ // To avoid cases where we are loaded with /latest/ along with
180
+ migrationLayer("algoliasearch.jquery");
181
+
182
+ // Now onto the V2 related code:
183
+ // If the client is using /latest/$BUILDNAME.min.js, load V2 of the library
184
+ //
185
+ // Otherwise, setup a migration layer that will throw on old constructors like
186
+ // new AlgoliaSearch().
187
+ // So that users upgrading from v2 to v3 will have a clear information
188
+ // message on what to do if they did not read the migration guide
189
+ function migrationLayer(buildName) {
190
+ var isUsingLatest = require(2);
191
+ var loadV2 = require(3);
192
+ var oldGlobals = require(4);
193
+
194
+ if (isUsingLatest(buildName)) {
195
+ loadV2(buildName);
196
+ } else {
197
+ oldGlobals();
198
+ }
199
+ }
200
+
201
+ },{"2":2,"3":3,"4":4}]},{},[5])(5)
202
+ });(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
203
+ // shim for using process in browser
204
+
205
+ var process = module.exports = {};
206
+ var queue = [];
207
+ var draining = false;
208
+
209
+ function drainQueue() {
210
+ if (draining) {
211
+ return;
212
+ }
213
+ draining = true;
214
+ var currentQueue;
215
+ var len = queue.length;
216
+ while(len) {
217
+ currentQueue = queue;
218
+ queue = [];
219
+ var i = -1;
220
+ while (++i < len) {
221
+ currentQueue[i]();
222
+ }
223
+ len = queue.length;
224
+ }
225
+ draining = false;
226
+ }
227
+ process.nextTick = function (fun) {
228
+ queue.push(fun);
229
+ if (!draining) {
230
+ setTimeout(drainQueue, 0);
231
+ }
232
+ };
233
+
234
+ process.title = 'browser';
235
+ process.browser = true;
236
+ process.env = {};
237
+ process.argv = [];
238
+ process.version = ''; // empty string to avoid regexp issues
239
+ process.versions = {};
240
+
241
+ function noop() {}
242
+
243
+ process.on = noop;
244
+ process.addListener = noop;
245
+ process.once = noop;
246
+ process.off = noop;
247
+ process.removeListener = noop;
248
+ process.removeAllListeners = noop;
249
+ process.emit = noop;
250
+
251
+ process.binding = function (name) {
252
+ throw new Error('process.binding is not supported');
253
+ };
254
+
255
+ // TODO(shtylman)
256
+ process.cwd = function () { return '/' };
257
+ process.chdir = function (dir) {
258
+ throw new Error('process.chdir is not supported');
259
+ };
260
+ process.umask = function() { return 0; };
261
+
262
+ },{}],2:[function(require,module,exports){
263
+ (function (process){
264
+ module.exports = AlgoliaSearch;
265
+
266
+ /*
267
+ * Algolia Search library initialization
268
+ * https://www.algolia.com/
269
+ *
270
+ * @param {string} applicationID - Your applicationID, found in your dashboard
271
+ * @param {string} apiKey - Your API key, found in your dashboard
272
+ * @param {Object} [opts]
273
+ * @param {number} [opts.timeout=2000] - The request timeout set in milliseconds, another request will be issued after this timeout
274
+ * @param {string} [opts.protocol='http:'] - The protocol used to query Algolia Search API.
275
+ * Set to 'https:' to force using https. Default to document.location.protocol in browsers
276
+ * @param {string[]} [opts.hosts=[
277
+ * this.applicationID + '-1.algolia.' + opts.tld,
278
+ * this.applicationID + '-2.algolia.' + opts.tld,
279
+ * this.applicationID + '-3.algolia.' + opts.tld]
280
+ * ] - The hosts to use for Algolia Search API. It this your responsibility to shuffle the hosts and add a DSN host in it
281
+ * @param {string} [opts.tld='net'] - The tld to use when computing hosts default list
282
+ */
283
+ function AlgoliaSearch(applicationID, apiKey, opts, _request) {
284
+ var usage = 'Usage: algoliasearch(applicationID, apiKey, opts)';
285
+
286
+ if (!applicationID) {
287
+ throw new Error('Please provide an application ID. ' + usage);
288
+ }
289
+
290
+ if (!apiKey) {
291
+ throw new Error('Please provide an API key. ' + usage);
292
+ }
293
+
294
+ opts = opts || {};
295
+
296
+ // now setting default options
297
+ // could not find a tiny module to do that, let's go manual
298
+ if (opts.timeout === undefined) {
299
+ opts.timeout = 2000;
300
+ }
301
+
302
+ if (opts.protocol === undefined) {
303
+ opts.protocol = document && document.location.protocol || 'http:';
304
+ }
305
+
306
+ if (opts.hosts === undefined) {
307
+ opts.hosts = []; // filled later on, has dependencies
308
+ }
309
+
310
+ if (opts.tld === undefined) {
311
+ opts.tld = 'net';
312
+ }
313
+
314
+ // while we advocate for colon-at-the-end values: 'http:' for `opts.protocol`
315
+ // we also accept `http` and `https`. It's a common error.
316
+ if (!/:$/.test(opts.protocol)) {
317
+ opts.protocol = opts.protocol + ':';
318
+ }
319
+
320
+ // no hosts given, add defaults
321
+ if (opts.hosts.length === 0) {
322
+ opts.hosts = shuffle([
323
+ applicationID + '-1.algolia.' + opts.tld,
324
+ applicationID + '-2.algolia.' + opts.tld,
325
+ applicationID + '-3.algolia.' + opts.tld
326
+ ]);
327
+
328
+ // add default dsn host
329
+ opts.hosts.unshift(applicationID + '-dsn.algolia.' + opts.tld);
330
+ }
331
+
332
+ opts.hosts = map(opts.hosts, function prependProtocol(host) {
333
+ return opts.protocol + '//' + host;
334
+ });
335
+
336
+ this.applicationID = applicationID;
337
+ this.apiKey = apiKey;
338
+ this.hosts = opts.hosts;
339
+
340
+ this.currentHostIndex = 0;
341
+ this.requestTimeout = opts.timeout;
342
+ this.extraHeaders = [];
343
+ this.cache = {};
344
+ this._request = _request;
345
+ }
346
+
347
+ AlgoliaSearch.prototype = {
348
+ /*
349
+ * Delete an index
350
+ *
351
+ * @param indexName the name of index to delete
352
+ * @param callback the result callback with two arguments
353
+ * error: null or Error('message')
354
+ * content: the server answer that contains the task ID
355
+ */
356
+ deleteIndex: function(indexName, callback) {
357
+ return this._jsonRequest({ method: 'DELETE',
358
+ url: '/1/indexes/' + encodeURIComponent(indexName),
359
+ callback: callback });
360
+ },
361
+ /**
362
+ * Move an existing index.
363
+ * @param srcIndexName the name of index to copy.
364
+ * @param dstIndexName the new index name that will contains a copy of srcIndexName (destination will be overriten if it already exist).
365
+ * @param callback the result callback with two arguments
366
+ * error: null or Error('message')
367
+ * content: the server answer that contains the task ID
368
+ */
369
+ moveIndex: function(srcIndexName, dstIndexName, callback) {
370
+ var postObj = {operation: 'move', destination: dstIndexName};
371
+ return this._jsonRequest({ method: 'POST',
372
+ url: '/1/indexes/' + encodeURIComponent(srcIndexName) + '/operation',
373
+ body: postObj,
374
+ callback: callback });
375
+
376
+ },
377
+ /**
378
+ * Copy an existing index.
379
+ * @param srcIndexName the name of index to copy.
380
+ * @param dstIndexName the new index name that will contains a copy of srcIndexName (destination will be overriten if it already exist).
381
+ * @param callback the result callback with two arguments
382
+ * error: null or Error('message')
383
+ * content: the server answer that contains the task ID
384
+ */
385
+ copyIndex: function(srcIndexName, dstIndexName, callback) {
386
+ var postObj = {operation: 'copy', destination: dstIndexName};
387
+ return this._jsonRequest({ method: 'POST',
388
+ url: '/1/indexes/' + encodeURIComponent(srcIndexName) + '/operation',
389
+ body: postObj,
390
+ callback: callback });
391
+ },
392
+ /**
393
+ * Return last log entries.
394
+ * @param offset Specify the first entry to retrieve (0-based, 0 is the most recent log entry).
395
+ * @param length Specify the maximum number of entries to retrieve starting at offset. Maximum allowed value: 1000.
396
+ * @param callback the result callback with two arguments
397
+ * error: null or Error('message')
398
+ * content: the server answer that contains the task ID
399
+ */
400
+ getLogs: function(offset, length, callback) {
401
+ if (arguments.length === 0 || typeof offset === 'function') {
402
+ // getLogs([cb])
403
+ callback = offset;
404
+ offset = 0;
405
+ length = 10;
406
+ } else if (arguments.length === 1 || typeof length === 'function') {
407
+ // getLogs(1, [cb)]
408
+ callback = length;
409
+ length = 10;
410
+ }
411
+
412
+ return this._jsonRequest({ method: 'GET',
413
+ url: '/1/logs?offset=' + offset + '&length=' + length,
414
+ callback: callback });
415
+ },
416
+ /*
417
+ * List all existing indexes (paginated)
418
+ *
419
+ * @param page The page to retrieve, starting at 0.
420
+ * @param callback the result callback with two arguments
421
+ * error: null or Error('message')
422
+ * content: the server answer with index list
423
+ */
424
+ listIndexes: function(page, callback) {
425
+ var params = '';
426
+
427
+ if (page === undefined || typeof page === 'function') {
428
+ callback = page;
429
+ } else {
430
+ params = '?page=' + page;
431
+ }
432
+
433
+ return this._jsonRequest({ method: 'GET',
434
+ url: '/1/indexes' + params,
435
+ callback: callback });
436
+ },
437
+
438
+ /*
439
+ * Get the index object initialized
440
+ *
441
+ * @param indexName the name of index
442
+ * @param callback the result callback with one argument (the Index instance)
443
+ */
444
+ initIndex: function(indexName) {
445
+ return new this.Index(this, indexName);
446
+ },
447
+ /*
448
+ * List all existing user keys with their associated ACLs
449
+ *
450
+ * @param callback the result callback with two arguments
451
+ * error: null or Error('message')
452
+ * content: the server answer with user keys list
453
+ */
454
+ listUserKeys: function(callback) {
455
+ return this._jsonRequest({ method: 'GET',
456
+ url: '/1/keys',
457
+ callback: callback });
458
+ },
459
+ /*
460
+ * Get ACL of a user key
461
+ *
462
+ * @param key
463
+ * @param callback the result callback with two arguments
464
+ * error: null or Error('message')
465
+ * content: the server answer with user keys list
466
+ */
467
+ getUserKeyACL: function(key, callback) {
468
+ return this._jsonRequest({ method: 'GET',
469
+ url: '/1/keys/' + key,
470
+ callback: callback });
471
+ },
472
+ /*
473
+ * Delete an existing user key
474
+ * @param key
475
+ * @param callback the result callback with two arguments
476
+ * error: null or Error('message')
477
+ * content: the server answer with user keys list
478
+ */
479
+ deleteUserKey: function(key, callback) {
480
+ return this._jsonRequest({ method: 'DELETE',
481
+ url: '/1/keys/' + key,
482
+ callback: callback });
483
+ },
484
+ /*
485
+ * Add an existing user key
486
+ *
487
+ * @param acls the list of ACL for this key. Defined by an array of strings that
488
+ * can contains the following values:
489
+ * - search: allow to search (https and http)
490
+ * - addObject: allows to add/update an object in the index (https only)
491
+ * - deleteObject : allows to delete an existing object (https only)
492
+ * - deleteIndex : allows to delete index content (https only)
493
+ * - settings : allows to get index settings (https only)
494
+ * - editSettings : allows to change index settings (https only)
495
+ * @param callback the result callback with two arguments
496
+ * error: null or Error('message')
497
+ * content: the server answer with user keys list
498
+ */
499
+ addUserKey: function(acls, callback) {
500
+ return this.addUserKeyWithValidity(acls, {
501
+ validity: 0,
502
+ maxQueriesPerIPPerHour: 0,
503
+ maxHitsPerQuery: 0
504
+ }, callback);
505
+ },
506
+ /*
507
+ * Add an existing user key
508
+ *
509
+ * @param acls the list of ACL for this key. Defined by an array of strings that
510
+ * can contains the following values:
511
+ * - search: allow to search (https and http)
512
+ * - addObject: allows to add/update an object in the index (https only)
513
+ * - deleteObject : allows to delete an existing object (https only)
514
+ * - deleteIndex : allows to delete index content (https only)
515
+ * - settings : allows to get index settings (https only)
516
+ * - editSettings : allows to change index settings (https only)
517
+ * @param params.validity the number of seconds after which the key will be automatically removed (0 means no time limit for this key)
518
+ * @param params.maxQueriesPerIPPerHour Specify the maximum number of API calls allowed from an IP address per hour.
519
+ * @param params.maxHitsPerQuery Specify the maximum number of hits this API key can retrieve in one call.
520
+ * @param callback the result callback with two arguments
521
+ * error: null or Error('message')
522
+ * content: the server answer with user keys list
523
+ */
524
+ addUserKeyWithValidity: function(acls, params, callback) {
525
+ var aclsObject = {};
526
+ aclsObject.acl = acls;
527
+ aclsObject.validity = params.validity;
528
+ aclsObject.maxQueriesPerIPPerHour = params.maxQueriesPerIPPerHour;
529
+ aclsObject.maxHitsPerQuery = params.maxHitsPerQuery;
530
+ return this._jsonRequest({ method: 'POST',
531
+ url: '/1/keys',
532
+ body: aclsObject,
533
+ callback: callback });
534
+ },
535
+
536
+ /**
537
+ * Set the extra security tagFilters header
538
+ * @param {string|array} tags The list of tags defining the current security filters
539
+ */
540
+ setSecurityTags: function(tags) {
541
+ if (Object.prototype.toString.call(tags) === '[object Array]') {
542
+ var strTags = [];
543
+ for (var i = 0; i < tags.length; ++i) {
544
+ if (Object.prototype.toString.call(tags[i]) === '[object Array]') {
545
+ var oredTags = [];
546
+ for (var j = 0; j < tags[i].length; ++j) {
547
+ oredTags.push(tags[i][j]);
548
+ }
549
+ strTags.push('(' + oredTags.join(',') + ')');
550
+ } else {
551
+ strTags.push(tags[i]);
552
+ }
553
+ }
554
+ tags = strTags.join(',');
555
+ }
556
+ this.tagFilters = tags;
557
+ },
558
+
559
+ /**
560
+ * Set the extra user token header
561
+ * @param {string} userToken The token identifying a uniq user (used to apply rate limits)
562
+ */
563
+ setUserToken: function(userToken) {
564
+ this.userToken = userToken;
565
+ },
566
+
567
+ /*
568
+ * Initialize a new batch of search queries
569
+ */
570
+ startQueriesBatch: function() {
571
+ this.batch = [];
572
+ },
573
+ /*
574
+ * Add a search query in the batch
575
+ *
576
+ * @param query the full text query
577
+ * @param args (optional) if set, contains an object with query parameters:
578
+ * - attributes: an array of object attribute names to retrieve
579
+ * (if not set all attributes are retrieve)
580
+ * - attributesToHighlight: an array of object attribute names to highlight
581
+ * (if not set indexed attributes are highlighted)
582
+ * - minWordSizefor1Typo: the minimum number of characters to accept one typo.
583
+ * Defaults to 3.
584
+ * - minWordSizefor2Typos: the minimum number of characters to accept two typos.
585
+ * Defaults to 7.
586
+ * - getRankingInfo: if set, the result hits will contain ranking information in
587
+ * _rankingInfo attribute
588
+ * - page: (pagination parameter) page to retrieve (zero base). Defaults to 0.
589
+ * - hitsPerPage: (pagination parameter) number of hits per page. Defaults to 10.
590
+ */
591
+ addQueryInBatch: function(indexName, query, args) {
592
+ var params = 'query=' + encodeURIComponent(query);
593
+ if (!this._isUndefined(args) && args !== null) {
594
+ params = this._getSearchParams(args, params);
595
+ }
596
+ this.batch.push({ indexName: indexName, params: params });
597
+ },
598
+ /*
599
+ * Clear all queries in cache
600
+ */
601
+ clearCache: function() {
602
+ this.cache = {};
603
+ },
604
+ /*
605
+ * Launch the batch of queries using XMLHttpRequest.
606
+ * (Optimized for browser using a POST query to minimize number of OPTIONS queries)
607
+ *
608
+ * @param callback the function that will receive results
609
+ */
610
+ sendQueriesBatch: function(callback) {
611
+ var as = this;
612
+ var params = {requests: []};
613
+
614
+ for (var i = 0; i < as.batch.length; ++i) {
615
+ params.requests.push(as.batch[i]);
616
+ }
617
+
618
+ return this._sendQueriesBatch(params, callback);
619
+ },
620
+
621
+ /**
622
+ * Set the number of milliseconds a request can take before automatically being terminated.
623
+ *
624
+ * @param {Number} milliseconds
625
+ */
626
+ setRequestTimeout: function(milliseconds) {
627
+ if (milliseconds) {
628
+ this.requestTimeout = parseInt(milliseconds, 10);
629
+ }
630
+ },
631
+
632
+ /*
633
+ * Index class constructor.
634
+ * You should not use this method directly but use initIndex() function
635
+ */
636
+ Index: function(algoliasearch, indexName) {
637
+ this.indexName = indexName;
638
+ this.as = algoliasearch;
639
+ this.typeAheadArgs = null;
640
+ this.typeAheadValueOption = null;
641
+
642
+ // make sure every index instance has it's own cache
643
+ this.cache = {};
644
+ },
645
+ /**
646
+ * Add an extra field to the HTTP request
647
+ *
648
+ * @param key the header field name
649
+ * @param value the header field value
650
+ */
651
+ setExtraHeader: function(key, value) {
652
+ this.extraHeaders.push({ key: key, value: value});
653
+ },
654
+
655
+ _sendQueriesBatch: function(params, callback) {
656
+ return this._jsonRequest({ cache: this.cache,
657
+ method: 'POST',
658
+ url: '/1/indexes/*/queries',
659
+ body: params,
660
+ fallback: {
661
+ method: 'GET',
662
+ url: '/1/indexes/*',
663
+ body: {params: (function() {
664
+ var reqParams = '';
665
+ for (var i = 0; i < params.requests.length; ++i) {
666
+ var q = '/1/indexes/' + encodeURIComponent(params.requests[i].indexName) + '?' + params.requests[i].params;
667
+ reqParams += i + '=' + encodeURIComponent(q) + '&';
668
+ }
669
+ return reqParams;
670
+ }())}
671
+ },
672
+ callback: callback
673
+ });
674
+ },
675
+ /*
676
+ * Wrapper that try all hosts to maximize the quality of service
677
+ */
678
+ _jsonRequest: function(opts) {
679
+ // handle opts.fallback, automatically use fallback (JSONP in browser plugins, wrapped with $plugin-promises)
680
+ // so if an error occurs and max tries => use fallback
681
+ // set tries to 0 again
682
+ // if fallback used and no more tries, return error
683
+ // fallback parameters are in opts.fallback
684
+ // call request.fallback or request accordingly, same promise chain otherwise
685
+ // put callback& params in front if problem
686
+ var cache = opts.cache;
687
+ var cacheID = opts.url;
688
+ var client = this;
689
+ var tries = 0;
690
+
691
+ // as we use POST requests to pass parameters (like query='aa'),
692
+ // the cacheID must be different between calls
693
+ if (opts.body !== undefined) {
694
+ cacheID += '_body_' + JSON.stringify(opts.body);
695
+ }
696
+
697
+ function doRequest(requester, reqOpts) {
698
+ // handle cache existence
699
+ if (cache && cache[cacheID] !== undefined) {
700
+ return client._request.resolve(cache[cacheID]);
701
+ }
702
+
703
+ if (tries >= client.hosts.length) {
704
+ if (!opts.fallback || requester === client._request.fallback) {
705
+ // could not get a response even using the fallback if one was available
706
+ return client._request.reject(new Error(
707
+ 'Cannot connect to the AlgoliaSearch API.' +
708
+ ' Send an email to support@algolia.com to report and resolve the issue.'
709
+ ));
710
+ }
711
+
712
+ tries = 0;
713
+ reqOpts.method = opts.fallback.method;
714
+ reqOpts.url = opts.fallback.url;
715
+ reqOpts.body = opts.fallback.body;
716
+ reqOpts.timeout = client.requestTimeout * (tries + 1);
717
+ client.currentHostIndex = 0;
718
+ client.forceFallback = true;
719
+ return doRequest(client._request.fallback, reqOpts);
720
+ }
721
+
722
+ var url = reqOpts.url;
723
+
724
+ url += (url.indexOf('?') === -1 ? '?' : '&') + 'X-Algolia-API-Key=' + client.apiKey;
725
+ url += '&X-Algolia-Application-Id=' + client.applicationID;
726
+
727
+ if (client.userToken) {
728
+ url += '&X-Algolia-UserToken=' + encodeURIComponent(client.userToken);
729
+ }
730
+
731
+ if (client.tagFilters) {
732
+ url += '&X-Algolia-TagFilters=' + encodeURIComponent(client.tagFilters);
733
+ }
734
+
735
+ for (var i = 0; i < client.extraHeaders.length; ++i) {
736
+ url += '&' + client.extraHeaders[i].key + '=' + client.extraHeaders[i].value;
737
+ }
738
+
739
+ return requester(client.hosts[client.currentHostIndex] + url, {
740
+ body: reqOpts.body,
741
+ method: reqOpts.method,
742
+ timeout: reqOpts.timeout
743
+ })
744
+ .then(function success(httpResponse) {
745
+ // timeout case, retry immediately
746
+ if (httpResponse instanceof Error) {
747
+ return retryRequest();
748
+ }
749
+
750
+ var status =
751
+ // When in browser mode, using XDR or JSONP
752
+ // We rely on our own API response `status`, only
753
+ // provided when an error occurs, we also expect a .message along
754
+ // Otherwise, it could be a `waitTask` status, that's the only
755
+ // case where we have a response.status that's not the http statusCode
756
+ httpResponse && httpResponse.body && httpResponse.body.message && httpResponse.body.status ||
757
+
758
+ // this is important to check the request statusCode AFTER the body eventual
759
+ // statusCode because some implementations (jQuery XDomainRequest transport) may
760
+ // send statusCode 200 while we had an error
761
+ httpResponse.statusCode ||
762
+
763
+ // When in browser mode, using XDR or JSONP
764
+ // we default to success when no error (no response.status && response.message)
765
+ // If there was a JSON.parse() error then body is null and it fails
766
+ httpResponse && httpResponse.body && 200;
767
+
768
+ var ok = status === 200 || status === 201;
769
+ var retry = !ok && Math.floor(status / 100) !== 4 && Math.floor(status / 100) !== 1;
770
+
771
+ if (ok && cache) {
772
+ cache[cacheID] = httpResponse.body;
773
+ }
774
+
775
+ if (ok) {
776
+ return httpResponse.body;
777
+ }
778
+
779
+ if (retry) {
780
+ return retryRequest();
781
+ }
782
+
783
+ var unrecoverableError = new Error(
784
+ httpResponse.body && httpResponse.body.message || 'Unknown error'
785
+ );
786
+
787
+ return client._request.reject(unrecoverableError);
788
+ }, tryFallback);
789
+
790
+ function retryRequest() {
791
+ client.currentHostIndex = ++client.currentHostIndex % client.hosts.length;
792
+ tries += 1;
793
+ reqOpts.timeout = client.requestTimeout * (tries + 1);
794
+ return doRequest(requester, reqOpts);
795
+ }
796
+
797
+ function tryFallback() {
798
+ // if we are switching to fallback right now, set tries to maximum
799
+ if (!client.forceFallback) {
800
+ // next time doRequest is called, simulate we tried all hosts
801
+ tries = client.hosts.length;
802
+ } else {
803
+ // we were already using the fallback, but something went wrong (script error)
804
+ client.currentHostIndex = ++client.currentHostIndex % client.hosts.length;
805
+ tries += 1;
806
+ }
807
+
808
+ return doRequest(requester, reqOpts);
809
+ }
810
+ }
811
+
812
+ // we can use a fallback if forced AND fallback parameters are available
813
+ var useFallback = client.forceFallback && opts.fallback;
814
+ var requestOptions = useFallback ? opts.fallback : opts;
815
+
816
+ var promise = doRequest(
817
+ useFallback ? client._request.fallback : client._request, {
818
+ url: requestOptions.url,
819
+ method: requestOptions.method,
820
+ body: requestOptions.body,
821
+ timeout: client.requestTimeout * (tries + 1)
822
+ }
823
+ );
824
+
825
+ // either we have a callback
826
+ // either we are using promises
827
+ if (opts.callback) {
828
+ promise.then(function okCb(content) {
829
+ process.nextTick(function() {
830
+ opts.callback(null, content);
831
+ });
832
+ }, function nookCb(err) {
833
+ process.nextTick(function() {
834
+ opts.callback(err);
835
+ });
836
+ });
837
+ } else {
838
+ return promise;
839
+ }
840
+ },
841
+
842
+ /*
843
+ * Transform search param object in query string
844
+ */
845
+ _getSearchParams: function(args, params) {
846
+ if (this._isUndefined(args) || args === null) {
847
+ return params;
848
+ }
849
+ for (var key in args) {
850
+ if (key !== null && args.hasOwnProperty(key)) {
851
+ params += params === '' ? '' : '&';
852
+ params += key + '=' + encodeURIComponent(Object.prototype.toString.call(args[key]) === '[object Array]' ? JSON.stringify(args[key]) : args[key]);
853
+ }
854
+ }
855
+ return params;
856
+ },
857
+ _isUndefined: function(obj) {
858
+ return obj === void 0;
859
+ }
860
+ };
861
+
862
+ /*
863
+ * Contains all the functions related to one index
864
+ * You should use AlgoliaSearch.initIndex(indexName) to retrieve this object
865
+ */
866
+ AlgoliaSearch.prototype.Index.prototype = {
867
+ /*
868
+ * Clear all queries in cache
869
+ */
870
+ clearCache: function() {
871
+ this.cache = {};
872
+ },
873
+ /*
874
+ * Add an object in this index
875
+ *
876
+ * @param content contains the javascript object to add inside the index
877
+ * @param objectID (optional) an objectID you want to attribute to this object
878
+ * (if the attribute already exist the old object will be overwrite)
879
+ * @param callback (optional) the result callback with two arguments:
880
+ * error: null or Error('message')
881
+ * content: the server answer that contains 3 elements: createAt, taskId and objectID
882
+ */
883
+ addObject: function(content, objectID, callback) {
884
+ var indexObj = this;
885
+
886
+ if (arguments.length === 1 || typeof objectID === 'function') {
887
+ callback = objectID;
888
+ objectID = undefined;
889
+ }
890
+
891
+ return this.as._jsonRequest({
892
+ method: objectID !== undefined ?
893
+ 'PUT' : // update or create
894
+ 'POST', // create (API generates an objectID)
895
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + // create
896
+ (objectID !== undefined ? '/' + encodeURIComponent(objectID) : ''), // update or create
897
+ body: content,
898
+ callback: callback
899
+ });
900
+ },
901
+ /*
902
+ * Add several objects
903
+ *
904
+ * @param objects contains an array of objects to add
905
+ * @param callback (optional) the result callback with two arguments:
906
+ * error: null or Error('message')
907
+ * content: the server answer that updateAt and taskID
908
+ */
909
+ addObjects: function(objects, callback) {
910
+ var indexObj = this;
911
+ var postObj = {requests: []};
912
+ for (var i = 0; i < objects.length; ++i) {
913
+ var request = { action: 'addObject',
914
+ body: objects[i] };
915
+ postObj.requests.push(request);
916
+ }
917
+ return this.as._jsonRequest({ method: 'POST',
918
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
919
+ body: postObj,
920
+ callback: callback });
921
+ },
922
+ /*
923
+ * Get an object from this index
924
+ *
925
+ * @param objectID the unique identifier of the object to retrieve
926
+ * @param attrs (optional) if set, contains the array of attribute names to retrieve
927
+ * @param callback (optional) the result callback with two arguments
928
+ * error: null or Error('message')
929
+ * content: the object to retrieve or the error message if a failure occured
930
+ */
931
+ getObject: function(objectID, attrs, callback) {
932
+ var indexObj = this;
933
+
934
+ if (arguments.length === 1 || typeof attrs === 'function') {
935
+ callback = attrs;
936
+ attrs = undefined;
937
+ }
938
+
939
+ var params = '';
940
+ if (attrs !== undefined) {
941
+ params = '?attributes=';
942
+ for (var i = 0; i < attrs.length; ++i) {
943
+ if (i !== 0) {
944
+ params += ',';
945
+ }
946
+ params += attrs[i];
947
+ }
948
+ }
949
+
950
+ return this.as._jsonRequest({
951
+ method: 'GET',
952
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(objectID) + params,
953
+ callback: callback
954
+ });
955
+ },
956
+
957
+ /*
958
+ * Update partially an object (only update attributes passed in argument)
959
+ *
960
+ * @param partialObject contains the javascript attributes to override, the
961
+ * object must contains an objectID attribute
962
+ * @param callback (optional) the result callback with two arguments:
963
+ * error: null or Error('message')
964
+ * content: the server answer that contains 3 elements: createAt, taskId and objectID
965
+ */
966
+ partialUpdateObject: function(partialObject, callback) {
967
+ var indexObj = this;
968
+ return this.as._jsonRequest({ method: 'POST',
969
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(partialObject.objectID) + '/partial',
970
+ body: partialObject,
971
+ callback: callback });
972
+ },
973
+ /*
974
+ * Partially Override the content of several objects
975
+ *
976
+ * @param objects contains an array of objects to update (each object must contains a objectID attribute)
977
+ * @param callback (optional) the result callback with two arguments:
978
+ * error: null or Error('message')
979
+ * content: the server answer that updateAt and taskID
980
+ */
981
+ partialUpdateObjects: function(objects, callback) {
982
+ var indexObj = this;
983
+ var postObj = {requests: []};
984
+ for (var i = 0; i < objects.length; ++i) {
985
+ var request = { action: 'partialUpdateObject',
986
+ objectID: objects[i].objectID,
987
+ body: objects[i] };
988
+ postObj.requests.push(request);
989
+ }
990
+ return this.as._jsonRequest({ method: 'POST',
991
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
992
+ body: postObj,
993
+ callback: callback });
994
+ },
995
+ /*
996
+ * Override the content of object
997
+ *
998
+ * @param object contains the javascript object to save, the object must contains an objectID attribute
999
+ * @param callback (optional) the result callback with two arguments:
1000
+ * error: null or Error('message')
1001
+ * content: the server answer that updateAt and taskID
1002
+ */
1003
+ saveObject: function(object, callback) {
1004
+ var indexObj = this;
1005
+ return this.as._jsonRequest({ method: 'PUT',
1006
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(object.objectID),
1007
+ body: object,
1008
+ callback: callback });
1009
+ },
1010
+ /*
1011
+ * Override the content of several objects
1012
+ *
1013
+ * @param objects contains an array of objects to update (each object must contains a objectID attribute)
1014
+ * @param callback (optional) the result callback with two arguments:
1015
+ * error: null or Error('message')
1016
+ * content: the server answer that updateAt and taskID
1017
+ */
1018
+ saveObjects: function(objects, callback) {
1019
+ var indexObj = this;
1020
+ var postObj = {requests: []};
1021
+ for (var i = 0; i < objects.length; ++i) {
1022
+ var request = { action: 'updateObject',
1023
+ objectID: objects[i].objectID,
1024
+ body: objects[i] };
1025
+ postObj.requests.push(request);
1026
+ }
1027
+ return this.as._jsonRequest({ method: 'POST',
1028
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
1029
+ body: postObj,
1030
+ callback: callback });
1031
+ },
1032
+ /*
1033
+ * Delete an object from the index
1034
+ *
1035
+ * @param objectID the unique identifier of object to delete
1036
+ * @param callback (optional) the result callback with two arguments:
1037
+ * error: null or Error('message')
1038
+ * content: the server answer that contains 3 elements: createAt, taskId and objectID
1039
+ */
1040
+ deleteObject: function(objectID, callback) {
1041
+ if (typeof objectID === 'function' || typeof objectID !== 'string' && typeof objectID !== 'number') {
1042
+ var err = new Error('Cannot delete an object without an objectID');
1043
+ callback = objectID;
1044
+ if (typeof callback === 'function') {
1045
+ return callback(err);
1046
+ }
1047
+
1048
+ return this.as._request.reject(err);
1049
+ }
1050
+
1051
+ var indexObj = this;
1052
+ return this.as._jsonRequest({ method: 'DELETE',
1053
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(objectID),
1054
+ callback: callback });
1055
+ },
1056
+ /*
1057
+ * Search inside the index using XMLHttpRequest request (Using a POST query to
1058
+ * minimize number of OPTIONS queries: Cross-Origin Resource Sharing).
1059
+ *
1060
+ * @param query the full text query
1061
+ * @param args (optional) if set, contains an object with query parameters:
1062
+ * - page: (integer) Pagination parameter used to select the page to retrieve.
1063
+ * Page is zero-based and defaults to 0. Thus, to retrieve the 10th page you need to set page=9
1064
+ * - hitsPerPage: (integer) Pagination parameter used to select the number of hits per page. Defaults to 20.
1065
+ * - attributesToRetrieve: a string that contains the list of object attributes you want to retrieve (let you minimize the answer size).
1066
+ * Attributes are separated with a comma (for example "name,address").
1067
+ * You can also use an array (for example ["name","address"]).
1068
+ * By default, all attributes are retrieved. You can also use '*' to retrieve all values when an attributesToRetrieve setting is specified for your index.
1069
+ * - attributesToHighlight: a string that contains the list of attributes you want to highlight according to the query.
1070
+ * Attributes are separated by a comma. You can also use an array (for example ["name","address"]).
1071
+ * If an attribute has no match for the query, the raw value is returned. By default all indexed text attributes are highlighted.
1072
+ * You can use `*` if you want to highlight all textual attributes. Numerical attributes are not highlighted.
1073
+ * A matchLevel is returned for each highlighted attribute and can contain:
1074
+ * - full: if all the query terms were found in the attribute,
1075
+ * - partial: if only some of the query terms were found,
1076
+ * - none: if none of the query terms were found.
1077
+ * - attributesToSnippet: a string that contains the list of attributes to snippet alongside the number of words to return (syntax is `attributeName:nbWords`).
1078
+ * Attributes are separated by a comma (Example: attributesToSnippet=name:10,content:10).
1079
+ * You can also use an array (Example: attributesToSnippet: ['name:10','content:10']). By default no snippet is computed.
1080
+ * - minWordSizefor1Typo: the minimum number of characters in a query word to accept one typo in this word. Defaults to 3.
1081
+ * - minWordSizefor2Typos: the minimum number of characters in a query word to accept two typos in this word. Defaults to 7.
1082
+ * - getRankingInfo: if set to 1, the result hits will contain ranking information in _rankingInfo attribute.
1083
+ * - aroundLatLng: search for entries around a given latitude/longitude (specified as two floats separated by a comma).
1084
+ * For example aroundLatLng=47.316669,5.016670).
1085
+ * You can specify the maximum distance in meters with the aroundRadius parameter (in meters) and the precision for ranking with aroundPrecision
1086
+ * (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).
1087
+ * At indexing, you should specify geoloc of an object with the _geoloc attribute (in the form {"_geoloc":{"lat":48.853409, "lng":2.348800}})
1088
+ * - insideBoundingBox: search entries inside a given area defined by the two extreme points of a rectangle (defined by 4 floats: p1Lat,p1Lng,p2Lat,p2Lng).
1089
+ * For example insideBoundingBox=47.3165,4.9665,47.3424,5.0201).
1090
+ * At indexing, you should specify geoloc of an object with the _geoloc attribute (in the form {"_geoloc":{"lat":48.853409, "lng":2.348800}})
1091
+ * - numericFilters: a string that contains the list of numeric filters you want to apply separated by a comma.
1092
+ * The syntax of one filter is `attributeName` followed by `operand` followed by `value`. Supported operands are `<`, `<=`, `=`, `>` and `>=`.
1093
+ * You can have multiple conditions on one attribute like for example numericFilters=price>100,price<1000.
1094
+ * You can also use an array (for example numericFilters: ["price>100","price<1000"]).
1095
+ * - tagFilters: filter the query by a set of tags. You can AND tags by separating them by commas.
1096
+ * To OR tags, you must add parentheses. For example, tags=tag1,(tag2,tag3) means tag1 AND (tag2 OR tag3).
1097
+ * You can also use an array, for example tagFilters: ["tag1",["tag2","tag3"]] means tag1 AND (tag2 OR tag3).
1098
+ * At indexing, tags should be added in the _tags** attribute of objects (for example {"_tags":["tag1","tag2"]}).
1099
+ * - facetFilters: filter the query by a list of facets.
1100
+ * Facets are separated by commas and each facet is encoded as `attributeName:value`.
1101
+ * For example: `facetFilters=category:Book,author:John%20Doe`.
1102
+ * You can also use an array (for example `["category:Book","author:John%20Doe"]`).
1103
+ * - facets: List of object attributes that you want to use for faceting.
1104
+ * Comma separated list: `"category,author"` or array `['category','author']`
1105
+ * Only attributes that have been added in **attributesForFaceting** index setting can be used in this parameter.
1106
+ * You can also use `*` to perform faceting on all attributes specified in **attributesForFaceting**.
1107
+ * - queryType: select how the query words are interpreted, it can be one of the following value:
1108
+ * - prefixAll: all query words are interpreted as prefixes,
1109
+ * - prefixLast: only the last word is interpreted as a prefix (default behavior),
1110
+ * - prefixNone: no query word is interpreted as a prefix. This option is not recommended.
1111
+ * - optionalWords: a string that contains the list of words that should be considered as optional when found in the query.
1112
+ * Comma separated and array are accepted.
1113
+ * - distinct: If set to 1, enable the distinct feature (disabled by default) if the attributeForDistinct index setting is set.
1114
+ * This feature is similar to the SQL "distinct" keyword: when enabled in a query with the distinct=1 parameter,
1115
+ * all hits containing a duplicate value for the attributeForDistinct attribute are removed from results.
1116
+ * For example, if the chosen attribute is show_name and several hits have the same value for show_name, then only the best
1117
+ * one is kept and others are removed.
1118
+ * - restrictSearchableAttributes: List of attributes you want to use for textual search (must be a subset of the attributesToIndex index setting)
1119
+ * either comma separated or as an array
1120
+ * @param callback the result callback with two arguments:
1121
+ * error: null or Error('message'). If false, the content contains the error.
1122
+ * content: the server answer that contains the list of results.
1123
+ */
1124
+ search: function(query, args, callback) {
1125
+ if (arguments.length === 0 || typeof query === 'function') {
1126
+ // .search(), .search(cb)
1127
+ callback = query;
1128
+ query = '';
1129
+ } else if (arguments.length === 1 || typeof args === 'function') {
1130
+ // .search(query/args), .search(query, cb)
1131
+ callback = args;
1132
+ args = undefined;
1133
+ }
1134
+
1135
+ // .search(args), careful: typeof null === 'object'
1136
+ if (typeof query === 'object' && query !== null) {
1137
+ args = query;
1138
+ query = undefined;
1139
+ } else if (query === undefined || query === null) { // .search(undefined/null)
1140
+ query = '';
1141
+ }
1142
+
1143
+ var params = '';
1144
+
1145
+ if (query !== undefined) {
1146
+ params += 'query=' + encodeURIComponent(query);
1147
+ }
1148
+
1149
+ if (args !== undefined) {
1150
+ params = this.as._getSearchParams(args, params);
1151
+ }
1152
+
1153
+ return this._search(params, callback);
1154
+ },
1155
+
1156
+ /*
1157
+ * Browse all index content
1158
+ *
1159
+ * @param page Pagination parameter used to select the page to retrieve.
1160
+ * Page is zero-based and defaults to 0. Thus, to retrieve the 10th page you need to set page=9
1161
+ * @param hitsPerPage: Pagination parameter used to select the number of hits per page. Defaults to 1000.
1162
+ * @param callback the result callback with two arguments:
1163
+ * error: null or Error('message'). If false, the content contains the error.
1164
+ * content: the server answer that contains the list of results.
1165
+ */
1166
+ browse: function(page, hitsPerPage, callback) {
1167
+ var indexObj = this;
1168
+
1169
+ if (arguments.length === 1 || typeof hitsPerPage === 'function') {
1170
+ callback = hitsPerPage;
1171
+ hitsPerPage = undefined;
1172
+ }
1173
+
1174
+ var params = '?page=' + page;
1175
+ if (!this.as._isUndefined(hitsPerPage)) {
1176
+ params += '&hitsPerPage=' + hitsPerPage;
1177
+ }
1178
+ return this.as._jsonRequest({ method: 'GET',
1179
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/browse' + params,
1180
+ callback: callback });
1181
+ },
1182
+
1183
+ /*
1184
+ * Get a Typeahead.js adapter
1185
+ * @param searchParams contains an object with query parameters (see search for details)
1186
+ */
1187
+ ttAdapter: function(params) {
1188
+ var self = this;
1189
+ return function(query, cb) {
1190
+ self.search(query, params, function(err, content) {
1191
+ if (err) {
1192
+ cb(err);
1193
+ return;
1194
+ }
1195
+
1196
+ cb(content.hits);
1197
+ });
1198
+ };
1199
+ },
1200
+
1201
+ /*
1202
+ * Wait the publication of a task on the server.
1203
+ * All server task are asynchronous and you can check with this method that the task is published.
1204
+ *
1205
+ * @param taskID the id of the task returned by server
1206
+ * @param callback the result callback with with two arguments:
1207
+ * error: null or Error('message')
1208
+ * content: the server answer that contains the list of results
1209
+ */
1210
+ waitTask: function(taskID, callback) {
1211
+ // waitTask() must be handled differently from other methods,
1212
+ // it's a recursive method using a timeout
1213
+ var indexObj = this;
1214
+
1215
+ var promise = this.as._jsonRequest({
1216
+ method: 'GET',
1217
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/task/' + taskID
1218
+ }).then(function success(content) {
1219
+ if (content.status !== 'published') {
1220
+ return new indexObj.as._request.delay(100).then(function() {
1221
+ return indexObj.waitTask(taskID, callback);
1222
+ });
1223
+ }
1224
+
1225
+ if (callback) {
1226
+ process.nextTick(function() {
1227
+ callback(null, content);
1228
+ });
1229
+ } else {
1230
+ return content;
1231
+ }
1232
+ }, function failure(err) {
1233
+ if (callback) {
1234
+ process.nextTick(function() {
1235
+ callback(err);
1236
+ });
1237
+ } else {
1238
+ return err;
1239
+ }
1240
+ });
1241
+
1242
+ if (!callback) {
1243
+ return promise;
1244
+ }
1245
+ },
1246
+
1247
+ /*
1248
+ * This function deletes the index content. Settings and index specific API keys are kept untouched.
1249
+ *
1250
+ * @param callback (optional) the result callback with two arguments
1251
+ * error: null or Error('message')
1252
+ * content: the settings object or the error message if a failure occured
1253
+ */
1254
+ clearIndex: function(callback) {
1255
+ var indexObj = this;
1256
+ return this.as._jsonRequest({ method: 'POST',
1257
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/clear',
1258
+ callback: callback });
1259
+ },
1260
+ /*
1261
+ * Get settings of this index
1262
+ *
1263
+ * @param callback (optional) the result callback with two arguments
1264
+ * error: null or Error('message')
1265
+ * content: the settings object or the error message if a failure occured
1266
+ */
1267
+ getSettings: function(callback) {
1268
+ var indexObj = this;
1269
+ return this.as._jsonRequest({ method: 'GET',
1270
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/settings',
1271
+ callback: callback });
1272
+ },
1273
+
1274
+ /*
1275
+ * Set settings for this index
1276
+ *
1277
+ * @param settigns the settings object that can contains :
1278
+ * - minWordSizefor1Typo: (integer) the minimum number of characters to accept one typo (default = 3).
1279
+ * - minWordSizefor2Typos: (integer) the minimum number of characters to accept two typos (default = 7).
1280
+ * - hitsPerPage: (integer) the number of hits per page (default = 10).
1281
+ * - attributesToRetrieve: (array of strings) default list of attributes to retrieve in objects.
1282
+ * If set to null, all attributes are retrieved.
1283
+ * - attributesToHighlight: (array of strings) default list of attributes to highlight.
1284
+ * If set to null, all indexed attributes are highlighted.
1285
+ * - attributesToSnippet**: (array of strings) default list of attributes to snippet alongside the number of words to return (syntax is attributeName:nbWords).
1286
+ * By default no snippet is computed. If set to null, no snippet is computed.
1287
+ * - attributesToIndex: (array of strings) the list of fields you want to index.
1288
+ * If set to null, all textual and numerical attributes of your objects are indexed, but you should update it to get optimal results.
1289
+ * This parameter has two important uses:
1290
+ * - Limit the attributes to index: For example if you store a binary image in base64, you want to store it and be able to
1291
+ * retrieve it but you don't want to search in the base64 string.
1292
+ * - Control part of the ranking*: (see the ranking parameter for full explanation) Matches in attributes at the beginning of
1293
+ * the list will be considered more important than matches in attributes further down the list.
1294
+ * In one attribute, matching text at the beginning of the attribute will be considered more important than text after, you can disable
1295
+ * this behavior if you add your attribute inside `unordered(AttributeName)`, for example attributesToIndex: ["title", "unordered(text)"].
1296
+ * - attributesForFaceting: (array of strings) The list of fields you want to use for faceting.
1297
+ * 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.
1298
+ * - attributeForDistinct: (string) The attribute name used for the Distinct feature. This feature is similar to the SQL "distinct" keyword: when enabled
1299
+ * in query with the distinct=1 parameter, all hits containing a duplicate value for this attribute are removed from results.
1300
+ * 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.
1301
+ * - ranking: (array of strings) controls the way results are sorted.
1302
+ * We have six available criteria:
1303
+ * - typo: sort according to number of typos,
1304
+ * - geo: sort according to decreassing distance when performing a geo-location based search,
1305
+ * - proximity: sort according to the proximity of query words in hits,
1306
+ * - attribute: sort according to the order of attributes defined by attributesToIndex,
1307
+ * - exact:
1308
+ * - if the user query contains one word: sort objects having an attribute that is exactly the query word before others.
1309
+ * 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
1310
+ * show starting by the v letter before it.
1311
+ * - if the user query contains multiple words: sort according to the number of words that matched exactly (and not as a prefix).
1312
+ * - custom: sort according to a user defined formula set in **customRanking** attribute.
1313
+ * The standard order is ["typo", "geo", "proximity", "attribute", "exact", "custom"]
1314
+ * - customRanking: (array of strings) lets you specify part of the ranking.
1315
+ * The syntax of this condition is an array of strings containing attributes prefixed by asc (ascending order) or desc (descending order) operator.
1316
+ * For example `"customRanking" => ["desc(population)", "asc(name)"]`
1317
+ * - queryType: Select how the query words are interpreted, it can be one of the following value:
1318
+ * - prefixAll: all query words are interpreted as prefixes,
1319
+ * - prefixLast: only the last word is interpreted as a prefix (default behavior),
1320
+ * - prefixNone: no query word is interpreted as a prefix. This option is not recommended.
1321
+ * - highlightPreTag: (string) Specify the string that is inserted before the highlighted parts in the query result (default to "<em>").
1322
+ * - highlightPostTag: (string) Specify the string that is inserted after the highlighted parts in the query result (default to "</em>").
1323
+ * - optionalWords: (array of strings) Specify a list of words that should be considered as optional when found in the query.
1324
+ * @param callback (optional) the result callback with two arguments
1325
+ * error: null or Error('message')
1326
+ * content: the server answer or the error message if a failure occured
1327
+ */
1328
+ setSettings: function(settings, callback) {
1329
+ var indexObj = this;
1330
+ return this.as._jsonRequest({ method: 'PUT',
1331
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/settings',
1332
+ body: settings,
1333
+ callback: callback });
1334
+ },
1335
+ /*
1336
+ * List all existing user keys associated to this index
1337
+ *
1338
+ * @param callback the result callback with two arguments
1339
+ * error: null or Error('message')
1340
+ * content: the server answer with user keys list
1341
+ */
1342
+ listUserKeys: function(callback) {
1343
+ var indexObj = this;
1344
+ return this.as._jsonRequest({ method: 'GET',
1345
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys',
1346
+ callback: callback });
1347
+ },
1348
+ /*
1349
+ * Get ACL of a user key associated to this index
1350
+ *
1351
+ * @param key
1352
+ * @param callback the result callback with two arguments
1353
+ * error: null or Error('message')
1354
+ * content: the server answer with user keys list
1355
+ */
1356
+ getUserKeyACL: function(key, callback) {
1357
+ var indexObj = this;
1358
+ return this.as._jsonRequest({ method: 'GET',
1359
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys/' + key,
1360
+ callback: callback });
1361
+ },
1362
+ /*
1363
+ * Delete an existing user key associated to this index
1364
+ *
1365
+ * @param key
1366
+ * @param callback the result callback with two arguments
1367
+ * error: null or Error('message')
1368
+ * content: the server answer with user keys list
1369
+ */
1370
+ deleteUserKey: function(key, callback) {
1371
+ var indexObj = this;
1372
+ return this.as._jsonRequest({ method: 'DELETE',
1373
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys/' + key,
1374
+ callback: callback });
1375
+ },
1376
+ /*
1377
+ * Add an existing user key associated to this index
1378
+ *
1379
+ * @param acls the list of ACL for this key. Defined by an array of strings that
1380
+ * can contains the following values:
1381
+ * - search: allow to search (https and http)
1382
+ * - addObject: allows to add/update an object in the index (https only)
1383
+ * - deleteObject : allows to delete an existing object (https only)
1384
+ * - deleteIndex : allows to delete index content (https only)
1385
+ * - settings : allows to get index settings (https only)
1386
+ * - editSettings : allows to change index settings (https only)
1387
+ * @param callback the result callback with two arguments
1388
+ * error: null or Error('message')
1389
+ * content: the server answer with user keys list
1390
+ */
1391
+ addUserKey: function(acls, callback) {
1392
+ var indexObj = this;
1393
+ var aclsObject = {};
1394
+ aclsObject.acl = acls;
1395
+ return this.as._jsonRequest({ method: 'POST',
1396
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys',
1397
+ body: aclsObject,
1398
+ callback: callback });
1399
+ },
1400
+ /*
1401
+ * Add an existing user key associated to this index
1402
+ *
1403
+ * @param acls the list of ACL for this key. Defined by an array of strings that
1404
+ * can contains the following values:
1405
+ * - search: allow to search (https and http)
1406
+ * - addObject: allows to add/update an object in the index (https only)
1407
+ * - deleteObject : allows to delete an existing object (https only)
1408
+ * - deleteIndex : allows to delete index content (https only)
1409
+ * - settings : allows to get index settings (https only)
1410
+ * - editSettings : allows to change index settings (https only)
1411
+ * @param params.validity the number of seconds after which the key will be automatically removed (0 means no time limit for this key)
1412
+ * @param params.maxQueriesPerIPPerHour Specify the maximum number of API calls allowed from an IP address per hour.
1413
+ * @param params.maxHitsPerQuery Specify the maximum number of hits this API key can retrieve in one call.
1414
+ * @param callback the result callback with two arguments
1415
+ * error: null or Error('message')
1416
+ * content: the server answer with user keys list
1417
+ */
1418
+ addUserKeyWithValidity: function(acls, params, callback) {
1419
+ var indexObj = this;
1420
+ var aclsObject = {};
1421
+ aclsObject.acl = acls;
1422
+ aclsObject.validity = params.validity;
1423
+ aclsObject.maxQueriesPerIPPerHour = params.maxQueriesPerIPPerHour;
1424
+ aclsObject.maxHitsPerQuery = params.maxHitsPerQuery;
1425
+ return this.as._jsonRequest({ method: 'POST',
1426
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys',
1427
+ body: aclsObject,
1428
+ callback: callback });
1429
+ },
1430
+ ///
1431
+ /// Internal methods only after this line
1432
+ ///
1433
+ _search: function(params, callback) {
1434
+ return this.as._jsonRequest({ cache: this.cache,
1435
+ method: 'POST',
1436
+ url: '/1/indexes/' + encodeURIComponent(this.indexName) + '/query',
1437
+ body: {params: params},
1438
+ fallback: {
1439
+ method: 'GET',
1440
+ url: '/1/indexes/' + encodeURIComponent(this.indexName),
1441
+ body: {params: params}
1442
+ },
1443
+ callback: callback
1444
+ });
1445
+ },
1446
+
1447
+ // internal attributes
1448
+ as: null,
1449
+ indexName: null,
1450
+ typeAheadArgs: null,
1451
+ typeAheadValueOption: null
1452
+ };
1453
+
1454
+ // extracted from https://github.com/component/map/blob/master/index.js
1455
+ // without the crazy toFunction thing
1456
+ function map(arr, fn){
1457
+ var ret = [];
1458
+ for (var i = 0; i < arr.length; ++i) {
1459
+ ret.push(fn(arr[i], i));
1460
+ }
1461
+ return ret;
1462
+ }
1463
+
1464
+ // extracted from https://github.com/coolaj86/knuth-shuffle
1465
+ // not compatible with browserify
1466
+ function shuffle(array) {
1467
+ /*eslint-disable*/
1468
+ var currentIndex = array.length
1469
+ , temporaryValue
1470
+ , randomIndex
1471
+ ;
1472
+
1473
+ // While there remain elements to shuffle...
1474
+ while (0 !== currentIndex) {
1475
+
1476
+ // Pick a remaining element...
1477
+ randomIndex = Math.floor(Math.random() * currentIndex);
1478
+ currentIndex -= 1;
1479
+
1480
+ // And swap it with the current element.
1481
+ temporaryValue = array[currentIndex];
1482
+ array[currentIndex] = array[randomIndex];
1483
+ array[randomIndex] = temporaryValue;
1484
+ }
1485
+
1486
+ return array;
1487
+ }
1488
+
1489
+ }).call(this,require(1))
1490
+ },{"1":1}],3:[function(require,module,exports){
1491
+ (function (global){
1492
+ var createAlgoliasearch = require(5);
1493
+ var JSONPRequest = require(4);
1494
+
1495
+ var algoliasearch = createAlgoliasearch(request);
1496
+ var $ = global.jQuery;
1497
+
1498
+ $.algolia = {Client: algoliasearch};
1499
+
1500
+ function request(url, opts) {
1501
+ return $.Deferred(function(deferred) {
1502
+ var body = null;
1503
+
1504
+ if (opts.body !== undefined) {
1505
+ body = JSON.stringify(opts.body);
1506
+ }
1507
+
1508
+ $.ajax(url, {
1509
+ type: opts.method,
1510
+ timeout: opts.timeout,
1511
+ dataType: 'json',
1512
+ data: body,
1513
+ complete: function(jqXHR, textStatus/* , error*/) {
1514
+ if (textStatus === 'timeout') {
1515
+ deferred.resolve(new Error('Timeout - Could not connect to endpoint ' + url));
1516
+ return;
1517
+ }
1518
+
1519
+ if (jqXHR.status === 0) {
1520
+ deferred.reject(new Error('Network error'));
1521
+ return;
1522
+ }
1523
+
1524
+ deferred.resolve({
1525
+ statusCode: jqXHR.status,
1526
+ body: jqXHR.responseJSON
1527
+ });
1528
+ }
1529
+ });
1530
+ }).promise();
1531
+ }
1532
+
1533
+ request.fallback = function(url, opts) {
1534
+ return $.Deferred(function(deferred) {
1535
+ JSONPRequest(url, opts, function JSONPRequestDone(err, content) {
1536
+ if (err) {
1537
+ deferred.reject(err);
1538
+ return;
1539
+ }
1540
+
1541
+ deferred.resolve(content);
1542
+ });
1543
+ }).promise();
1544
+ };
1545
+
1546
+ request.reject = function(val) {
1547
+ return $.Deferred(function(deferred) {
1548
+ deferred.reject(val);
1549
+ }).promise();
1550
+ };
1551
+
1552
+ request.resolve = function(val) {
1553
+ return $.Deferred(function(deferred) {
1554
+ deferred.resolve(val);
1555
+ }).promise();
1556
+ };
1557
+
1558
+ request.delay = function(ms) {
1559
+ return $.Deferred(function(deferred) {
1560
+ setTimeout(function() {
1561
+ deferred.resolve();
1562
+ }, ms);
1563
+ }).promise();
1564
+ };
1565
+
1566
+ }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
1567
+ },{"4":4,"5":5}],4:[function(require,module,exports){
1568
+ module.exports = JSONPRequest;
1569
+
1570
+ var JSONPCounter = 0;
1571
+
1572
+ function JSONPRequest(url, opts, cb) {
1573
+ if (opts.method !== 'GET') {
1574
+ cb(new Error('Method ' + opts.method + ' ' + url + ' is not supported by JSONP.'));
1575
+ return;
1576
+ }
1577
+
1578
+ var cbCalled = false;
1579
+ var timedOut = false;
1580
+
1581
+ JSONPCounter += 1;
1582
+ var head = document.getElementsByTagName('head')[0];
1583
+ var script = document.createElement('script');
1584
+ var cbName = 'algoliaJSONP_' + JSONPCounter;
1585
+ var done = false;
1586
+
1587
+ window[cbName] = function(data) {
1588
+ try {
1589
+ delete window[cbName];
1590
+ } catch (e) {
1591
+ window[cbName] = undefined;
1592
+ }
1593
+
1594
+ if (timedOut) {
1595
+ return;
1596
+ }
1597
+
1598
+ cbCalled = true;
1599
+
1600
+ clean();
1601
+
1602
+ cb(null, {
1603
+ body: data/*,
1604
+ // We do not send the statusCode, there's no statusCode in JSONP, it will be
1605
+ // computed using data.status && data.message like with XDR
1606
+ statusCode*/
1607
+ });
1608
+ };
1609
+
1610
+ // add callback by hand
1611
+ url += '&callback=' + cbName;
1612
+
1613
+ // add body params by hand
1614
+ if (opts.body && opts.body.params) {
1615
+ url += '&' + opts.body.params;
1616
+ }
1617
+
1618
+ var ontimeout = setTimeout(timeout, opts.timeout);
1619
+
1620
+ // script onreadystatechange needed only for
1621
+ // <= IE8
1622
+ // https://github.com/angular/angular.js/issues/4523
1623
+ script.onreadystatechange = readystatechange;
1624
+ script.onload = success;
1625
+ script.onerror = error;
1626
+
1627
+ script.async = true;
1628
+ script.defer = true;
1629
+ script.src = url;
1630
+ head.appendChild(script);
1631
+
1632
+ function success() {
1633
+ if (done || timedOut) {
1634
+ return;
1635
+ }
1636
+
1637
+ done = true;
1638
+
1639
+ // script loaded but did not call the fn => script loading error
1640
+ if (!cbCalled) {
1641
+ clean();
1642
+ cb(new Error('Failed to load JSONP script'));
1643
+ }
1644
+ }
1645
+
1646
+ function readystatechange() {
1647
+ if (this.readyState === 'loaded' || this.readyState === 'complete') {
1648
+ success();
1649
+ }
1650
+ }
1651
+
1652
+ function clean() {
1653
+ clearTimeout(ontimeout);
1654
+ script.onload = null;
1655
+ script.onreadystatechange = null;
1656
+ script.onerror = null;
1657
+ head.removeChild(script);
1658
+
1659
+ try {
1660
+ delete window[cbName];
1661
+ delete window[cbName + '_loaded'];
1662
+ } catch (e) {
1663
+ window[cbName] = null;
1664
+ window[cbName + '_loaded'] = null;
1665
+ }
1666
+ }
1667
+
1668
+ function timeout() {
1669
+ timedOut = true;
1670
+ clean();
1671
+ cb(new Error('Timeout - Could not connect to endpoint ' + url));
1672
+ }
1673
+
1674
+ function error() {
1675
+ if (done || timedOut) {
1676
+ return;
1677
+ }
1678
+
1679
+ clean();
1680
+ cb(new Error('Failed to load JSONP script'));
1681
+ }
1682
+ }
1683
+
1684
+ },{}],5:[function(require,module,exports){
1685
+ // this file is a `factory of algoliasearch()`
1686
+ // Given a `request` param, it will provide you an AlgoliaSearch client
1687
+ // using this particular request
1688
+ module.exports = createAlgoliasearch;
1689
+
1690
+ function createAlgoliasearch(request) {
1691
+ function algoliasearch(applicationID, apiKey, opts) {
1692
+ var AlgoliaSearch = require(2);
1693
+
1694
+ return new AlgoliaSearch(applicationID, apiKey, opts, request);
1695
+ }
1696
+
1697
+ algoliasearch.version = "3.0.3";
1698
+
1699
+ return algoliasearch;
1700
+ }
1701
+
1702
+ },{"2":2}]},{},[3]);