algoliasearch-rails 1.12.0 → 1.12.1

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.
@@ -1,37 +1,75 @@
1
- /*! algoliasearch 3.0.3 | © 2014, 2015 Algolia SAS | github.com/algolia/algoliasearch-client-js */
2
- (function(m){var l;"undefined"!==typeof window?l=window:"undefined"!==typeof self&&(l=self);l.ALGOLIA_MIGRATION_LAYER=m()})(function(){return function l(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,l,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(l,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(l,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(l,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=l(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(l,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(l,n,k){n=l(2);k=l(3);l=l(4);n("algoliasearch.jquery")?k("algoliasearch.jquery"):l()},{2:2,3:3,4:4}]},{},[5])(5)});
10
- (function e$$0(l,n,k){function e(a,b){if(!n[a]){if(!l[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:{}};l[a][0].call(c.exports,function(b){var c=l[a][1][b];return e(c?c:b)},c,c.exports,e$$0,l,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(m,l,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(){}m=l.exports={};var h=[],g=!1;m.nextTick=function(a){h.push(a);g||setTimeout(k,0)};m.title="browser";m.browser=!0;m.env={};m.argv=[];m.version="";m.versions={};m.on=e;m.addListener=e;m.once=e;m.off=e;m.removeListener=e;m.removeAllListeners=e;m.emit=e;m.binding=function(a){throw Error("process.binding is not supported");};m.cwd=function(){return"/"};m.chdir=function(a){throw Error("process.chdir is not supported");};m.umask=function(){return 0}},
12
- {}],2:[function(m,l,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}l.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 l(){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 m=0;m<f.extraHeaders.length;++m)k+="&"+f.extraHeaders[m].key+"="+f.extraHeaders[m].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 l();
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?l():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,m(1))},{1:1}],3:[function(m,l,n){(function(k){function e(b,c){return a.Deferred(function(d){var e=null;void 0!==c.body&&(e=JSON.stringify(c.body));
34
- a.ajax(b,{type:c.method,timeout:c.timeout,dataType:"json",data:e,complete:function(a,c){"timeout"===c?d.resolve(Error("Timeout - Could not connect to endpoint "+b)):0===a.status?d.reject(Error("Network error")):d.resolve({statusCode:a.status,body:a.responseJSON})}})}).promise()}var h=m(5),g=m(4),h=h(e),a=k.jQuery;a.algolia={Client:h};e.fallback=function(b,c){return a.Deferred(function(a){g(b,c,function(b,c){b?a.reject(b):a.resolve(c)})}).promise()};e.reject=function(b){return a.Deferred(function(a){a.reject(b)}).promise()};
35
- e.resolve=function(b){return a.Deferred(function(a){a.resolve(b)}).promise()};e.delay=function(b){return a.Deferred(function(a){setTimeout(function(){a.resolve()},b)}).promise()}}).call(this,"undefined"!==typeof global?global:"undefined"!==typeof self?self:"undefined"!==typeof window?window:{})},{4:4,5:5}],4:[function(m,l,n){l.exports=function(e,h,g){function a(){r||l||(r=!0,m||(c(),g(Error("Failed to load JSONP script"))))}function b(){"loaded"!==this.readyState&&"complete"!==this.readyState||a()}
36
- 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(){l=!0;c();g(Error("Timeout - Could not connect to endpoint "+e))}function f(){r||l||(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 m=!1,l=!1;k+=1;var n=document.getElementsByTagName("head")[0],p=document.createElement("script"),
37
- q="algoliaJSONP_"+k,r=!1;window[q]=function(a){try{delete window[q]}catch(b){window[q]=void 0}l||(m=!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(m,l,n){l.exports=function(k){function e(e,g,a){return new (m(2))(e,g,a,k)}e.version="3.0.3";return e}},{2:2}]},{},[3]);
1
+ /*! algoliasearch 3.3.0 | © 2014, 2015 Algolia SAS | github.com/algolia/algoliasearch-client-js */
2
+ (function(g){var h;"undefined"!==typeof window?h=window:"undefined"!==typeof self&&(h=self);h.ALGOLIA_MIGRATION_LAYER=g()})(function(){return function h(d,n,c){function b(f,p){if(!n[f]){if(!d[f]){var l="function"==typeof require&&require;if(!p&&l)return l(f,!0);if(k)return k(f,!0);l=Error("Cannot find module '"+f+"'");throw l.code="MODULE_NOT_FOUND",l;}l=n[f]={exports:{}};d[f][0].call(l.exports,function(c){var k=d[f][1][c];return b(k?k:c)},l,l.exports,h,d,n,c)}return n[f].exports}for(var k="function"==
3
+ typeof require&&require,f=0;f<c.length;f++)b(c[f]);return b}({1:[function(h,d,n){function c(f,b){for(var c in b)f.setAttribute(c,b[c])}function b(f,b){f.onload=function(){this.onerror=this.onload=null;b(null,f)};f.onerror=function(){this.onerror=this.onload=null;b(Error("Failed to load "+this.src),f)}}function k(f,b){f.onreadystatechange=function(){if("complete"==this.readyState||"loaded"==this.readyState)this.onreadystatechange=null,b(null,f)}}d.exports=function(f,d,p){var l=document.head||document.getElementsByTagName("head")[0],
4
+ q=document.createElement("script");"function"===typeof d&&(p=d,d={});d=d||{};p=p||function(){};q.type=d.type||"text/javascript";q.charset=d.charset||"utf8";q.async="async"in d?!!d.async:!0;q.src=f;d.attrs&&c(q,d.attrs);d.text&&(q.text=""+d.text);("onload"in q?b:k)(q,p);q.onload||b(q,p);l.appendChild(q)}},{}],2:[function(h,d,n){d.exports=function(c){c=new RegExp("cdn\\.jsdelivr\\.net/algoliasearch/latest/"+c.replace(".","\\.")+"(?:\\.min)?\\.js$");for(var b=document.getElementsByTagName("script"),
5
+ k=!1,f=0,d=b.length;f<d;f++)if(b[f].src&&c.test(b[f].src)){k=!0;break}return k}},{}],3:[function(h,d,n){(function(c){function b(b){return function(){var f="AlgoliaSearch: loaded V2 script using "+b;c.console&&c.console.log&&c.console.log(f)}}d.exports=function(k){var f=h(1);k="//cdn.jsdelivr.net/algoliasearch/2/"+k+".min.js";c.console&&(c.console.warn?c.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
+ c.console.log&&c.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===c.ALGOLIA_SUPPORTS_DOCWRITE?(document.write('<script src="'+k+'">\x3c/script>'),b("document.write")()):f(k,b("DOMElement"))}catch(d){f(k,b("DOMElement"))}}}).call(this,"undefined"!==typeof global?global:"undefined"!==typeof self?self:"undefined"!==typeof window?window:{})},{1:1}],4:[function(h,d,n){(function(c){d.exports=function(){c.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
+ };c.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(h,d,n){d=h(2);n=h(3);h=h(4);d("algoliasearch.jquery")?n("algoliasearch.jquery"):h()},{2:2,3:3,4:4}]},{},[5])(5)});
10
+ (function e$$0(h,d,n){function c(f,k){if(!d[f]){if(!h[f]){var p="function"==typeof require&&require;if(!k&&p)return p(f,!0);if(b)return b(f,!0);p=Error("Cannot find module '"+f+"'");throw p.code="MODULE_NOT_FOUND",p;}p=d[f]={exports:{}};h[f][0].call(p.exports,function(b){var k=h[f][1][b];return c(k?k:b)},p,p.exports,e$$0,h,d,n)}return d[f].exports}for(var b="function"==typeof require&&require,k=0;k<n.length;k++)c(n[k]);return c})({1:[function(g,h,d){function n(){if(!k){k=!0;for(var f,c=b.length;c;){f=
11
+ b;b=[];for(var d=-1;++d<c;)f[d]();c=b.length}k=!1}}function c(){}g=h.exports={};var b=[],k=!1;g.nextTick=function(f){b.push(f);k||setTimeout(n,0)};g.title="browser";g.browser=!0;g.env={};g.argv=[];g.version="";g.versions={};g.on=c;g.addListener=c;g.once=c;g.off=c;g.removeListener=c;g.removeAllListeners=c;g.emit=c;g.binding=function(f){throw Error("process.binding is not supported");};g.cwd=function(){return"/"};g.chdir=function(f){throw Error("process.chdir is not supported");};g.umask=function(){return 0}},
12
+ {}],2:[function(g,h,d){h.exports=function(c,b,k,f){k=k||"=";var d={};if("string"!==typeof c||0===c.length)return d;var p=/\+/g;c=c.split(b||"&");b=1E3;f&&"number"===typeof f.maxKeys&&(b=f.maxKeys);f=c.length;0<b&&f>b&&(f=b);for(b=0;b<f;++b){var l=c[b].replace(p,"%20"),g=l.indexOf(k),h;0<=g?(h=l.substr(0,g),l=l.substr(g+1)):(h=l,l="");h=decodeURIComponent(h);l=decodeURIComponent(l);Object.prototype.hasOwnProperty.call(d,h)?n(d[h])?d[h].push(l):d[h]=[d[h],l]:d[h]=l}return d};var n=Array.isArray||function(c){return"[object Array]"===
13
+ Object.prototype.toString.call(c)}},{}],3:[function(g,h,d){function n(f,b){if(f.map)return f.map(b);for(var c=[],d=0;d<f.length;d++)c.push(b(f[d],d));return c}var c=function(f){switch(typeof f){case "string":return f;case "boolean":return f?"true":"false";case "number":return isFinite(f)?f:"";default:return""}};h.exports=function(f,d,g,l){d=d||"&";g=g||"=";null===f&&(f=void 0);return"object"===typeof f?n(k(f),function(k){var l=encodeURIComponent(c(k))+g;return b(f[k])?n(f[k],function(a){return l+
14
+ encodeURIComponent(c(a))}).join(d):l+encodeURIComponent(c(f[k]))}).join(d):l?encodeURIComponent(c(l))+g+encodeURIComponent(c(f)):""};var b=Array.isArray||function(f){return"[object Array]"===Object.prototype.toString.call(f)},k=Object.keys||function(f){var b=[],c;for(c in f)Object.prototype.hasOwnProperty.call(f,c)&&b.push(c);return b}},{}],4:[function(g,h,d){d.decode=d.parse=g(2);d.encode=d.stringify=g(3)},{2:2,3:3}],5:[function(g,h,d){function n(){var b;try{b=c.debug}catch(f){}return b}d=h.exports=
15
+ g(6);d.log=function(){return"object"===typeof console&&console.log&&Function.prototype.apply.call(console.log,console,arguments)};d.formatArgs=function(){var b=arguments,f=this.useColors;b[0]=(f?"%c":"")+this.namespace+(f?" %c":" ")+b[0]+(f?"%c ":" ")+"+"+d.humanize(this.diff);if(!f)return b;var f="color: "+this.color,b=[b[0],f,"color: inherit"].concat(Array.prototype.slice.call(b,1)),c=0,g=0;b[0].replace(/%[a-z%]/g,function(b){"%%"!==b&&(c++,"%c"===b&&(g=c))});b.splice(g,0,f);return b};d.save=function(b){try{null==
16
+ b?c.removeItem("debug"):c.debug=b}catch(f){}};d.load=n;d.useColors=function(){return"WebkitAppearance"in document.documentElement.style||window.console&&(console.firebug||console.exception&&console.table)||navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&31<=parseInt(RegExp.$1,10)};var c;if("undefined"!==typeof chrome&&"undefined"!==typeof chrome.storage)c=chrome.storage.local;else a:{try{c=window.localStorage;break a}catch(b){}c=void 0}d.colors="lightseagreen forestgreen goldenrod dodgerblue darkorchid crimson".split(" ");
17
+ d.formatters.j=function(b){return JSON.stringify(b)};d.enable(n())},{6:6}],6:[function(g,h,d){d=h.exports=function(b){function k(){}function f(){var b=+new Date;f.diff=b-(c||b);f.prev=c;c=f.curr=b;null==f.useColors&&(f.useColors=d.useColors());null==f.color&&f.useColors&&(f.color=d.colors[n++%d.colors.length]);var k=Array.prototype.slice.call(arguments);k[0]=d.coerce(k[0]);"string"!==typeof k[0]&&(k=["%o"].concat(k));var g=0;k[0]=k[0].replace(/%([a-z%])/g,function(b,a){if("%%"===b)return b;g++;var e=
18
+ d.formatters[a];"function"===typeof e&&(b=e.call(f,k[g]),k.splice(g,1),g--);return b});"function"===typeof d.formatArgs&&(k=d.formatArgs.apply(f,k));(f.log||d.log||console.log.bind(console)).apply(f,k)}k.enabled=!1;f.enabled=!0;var g=d.enabled(b)?f:k;g.namespace=b;return g};d.coerce=function(b){return b instanceof Error?b.stack||b.message:b};d.disable=function(){d.enable("")};d.enable=function(b){d.save(b);for(var c=(b||"").split(/[\s,]+/),f=c.length,g=0;g<f;g++)c[g]&&(b=c[g].replace(/\*/g,".*?"),
19
+ "-"===b[0]?d.skips.push(new RegExp("^"+b.substr(1)+"$")):d.names.push(new RegExp("^"+b+"$")))};d.enabled=function(b){var c,f;c=0;for(f=d.skips.length;c<f;c++)if(d.skips[c].test(b))return!1;c=0;for(f=d.names.length;c<f;c++)if(d.names[c].test(b))return!0;return!1};d.humanize=g(7);d.names=[];d.skips=[];d.formatters={};var n=0,c},{7:7}],7:[function(g,h,d){function n(b){if(b=/^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(b)){var c=
20
+ parseFloat(b[1]);switch((b[2]||"ms").toLowerCase()){case "years":case "year":case "yrs":case "yr":case "y":return 315576E5*c;case "days":case "day":case "d":return 864E5*c;case "hours":case "hour":case "hrs":case "hr":case "h":return 36E5*c;case "minutes":case "minute":case "mins":case "min":case "m":return 6E4*c;case "seconds":case "second":case "secs":case "sec":case "s":return 1E3*c;case "milliseconds":case "millisecond":case "msecs":case "msec":case "ms":return c}}}function c(b,c,f){if(!(b<c))return b<
21
+ 1.5*c?Math.floor(b/c)+" "+f:Math.ceil(b/c)+" "+f+"s"}h.exports=function(b,d){d=d||{};return"string"==typeof b?n(b):d.long?c(b,864E5,"day")||c(b,36E5,"hour")||c(b,6E4,"minute")||c(b,1E3,"second")||b+" ms":864E5<=b?Math.round(b/864E5)+"d":36E5<=b?Math.round(b/36E5)+"h":6E4<=b?Math.round(b/6E4)+"m":1E3<=b?Math.round(b/1E3)+"s":b+"ms"}},{}],8:[function(g,h,d){(function(d,c){(function(){function b(a){return"function"===typeof a}function g(){return function(){d.nextTick(l)}}function f(){var a=0,e=new I(l),
22
+ b=document.createTextNode("");e.observe(b,{characterData:!0});return function(){b.data=a=++a%2}}function t(){var a=new MessageChannel;a.port1.onmessage=l;return function(){a.port2.postMessage(0)}}function p(){return function(){setTimeout(l,1)}}function l(){for(var a=0;a<z;a+=2)(0,A[a])(A[a+1]),A[a]=void 0,A[a+1]=void 0;z=0}function q(){}function x(a,e,b,m){try{a.call(e,b,m)}catch(c){return c}}function a(a,e,b){B(function(a){var c=!1,f=x(b,e,function(b){c||(c=!0,e!==b?m(a,b):v(a,b))},function(e){c||
23
+ (c=!0,u(a,e))},"Settle: "+(a._label||" unknown promise"));!c&&f&&(c=!0,u(a,f))},a)}function e(a,e){1===e._state?v(a,e._result):2===a._state?u(a,e._result):C(e,void 0,function(e){m(a,e)},function(e){u(a,e)})}function m(m,c){if(m===c)u(m,new TypeError("You cannot resolve a promise with itself"));else if("function"===typeof c||"object"===typeof c&&null!==c)if(c.constructor===m.constructor)e(m,c);else{var f;try{f=c.then}catch(d){D.error=d,f=D}f===D?u(m,D.error):void 0===f?v(m,c):b(f)?a(m,c,f):v(m,c)}else v(m,
24
+ c)}function r(a){a._onerror&&a._onerror(a._result);F(a)}function v(a,e){void 0===a._state&&(a._result=e,a._state=1,0!==a._subscribers.length&&B(F,a))}function u(a,e){void 0===a._state&&(a._state=2,a._result=e,B(r,a))}function C(a,e,b,m){var c=a._subscribers,f=c.length;a._onerror=null;c[f]=e;c[f+1]=b;c[f+2]=m;0===f&&a._state&&B(F,a)}function F(a){var e=a._subscribers,b=a._state;if(0!==e.length){for(var m,c,f=a._result,d=0;d<e.length;d+=3)m=e[d],c=e[d+b],m?J(b,m,c,f):c(f);a._subscribers.length=0}}function K(){this.error=
25
+ null}function J(a,e,c,f){var d=b(c),r,g,k,h;if(d){try{r=c(f)}catch(l){G.error=l,r=G}r===G?(h=!0,g=r.error,r=null):k=!0;if(e===r){u(e,new TypeError("A promises callback cannot return that same promise."));return}}else r=f,k=!0;void 0===e._state&&(d&&k?m(e,r):h?u(e,g):1===a?v(e,r):2===a&&u(e,r))}function N(a,e){try{e(function(e){m(a,e)},function(e){u(a,e)})}catch(b){u(a,b)}}function w(a,e,b,m){this._instanceConstructor=a;this.promise=new a(q,m);this._abortOnReject=b;this._validateInput(e)?(this._input=
26
+ e,this._remaining=this.length=e.length,this._init(),0===this.length?v(this.promise,this._result):(this.length=this.length||0,this._enumerate(),0===this._remaining&&v(this.promise,this._result))):u(this.promise,this._validationError())}function y(a){this._id=O++;this._result=this._state=void 0;this._subscribers=[];if(q!==a){if(!b(a))throw new TypeError("You must pass a resolver function as the first argument to the promise constructor");if(!(this instanceof y))throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");
27
+ N(this,a)}}var L=Array.isArray?Array.isArray:function(a){return"[object Array]"===Object.prototype.toString.call(a)},z=0,B=function(a,e){A[z]=a;A[z+1]=e;z+=2;2===z&&M()},E="undefined"!==typeof window?window:{},I=E.MutationObserver||E.WebKitMutationObserver,E="undefined"!==typeof Uint8ClampedArray&&"undefined"!==typeof importScripts&&"undefined"!==typeof MessageChannel,A=Array(1E3),M;M="undefined"!==typeof d&&"[object process]"==={}.toString.call(d)?g():I?f():E?t():p();var D=new K,G=new K;w.prototype._validateInput=
28
+ function(a){return L(a)};w.prototype._validationError=function(){return Error("Array Methods must be provided an Array")};w.prototype._init=function(){this._result=Array(this.length)};w.prototype._enumerate=function(){for(var a=this.length,e=this.promise,b=this._input,m=0;void 0===e._state&&m<a;m++)this._eachEntry(b[m],m)};w.prototype._eachEntry=function(a,e){var b=this._instanceConstructor;"object"===typeof a&&null!==a?a.constructor===b&&void 0!==a._state?(a._onerror=null,this._settledAt(a._state,
29
+ e,a._result)):this._willSettleAt(b.resolve(a),e):(this._remaining--,this._result[e]=this._makeResult(1,e,a))};w.prototype._settledAt=function(a,e,b){var m=this.promise;void 0===m._state&&(this._remaining--,this._abortOnReject&&2===a?u(m,b):this._result[e]=this._makeResult(a,e,b));0===this._remaining&&v(m,this._result)};w.prototype._makeResult=function(a,e,b){return b};w.prototype._willSettleAt=function(a,e){var b=this;C(a,void 0,function(a){b._settledAt(1,e,a)},function(a){b._settledAt(2,e,a)})};
30
+ var O=0;y.all=function(a,e){return(new w(this,a,!0,e)).promise};y.race=function(a,e){function b(a){m(f,a)}function c(a){u(f,a)}var f=new this(q,e);if(!L(a))return u(f,new TypeError("You must pass an array to race.")),f;for(var d=a.length,r=0;void 0===f._state&&r<d;r++)C(this.resolve(a[r]),void 0,b,c);return f};y.resolve=function(a,e){if(a&&"object"===typeof a&&a.constructor===this)return a;var b=new this(q,e);m(b,a);return b};y.reject=function(a,e){var b=new this(q,e);u(b,a);return b};y.prototype=
31
+ {constructor:y,then:function(a,e){var b=this._state;if(1===b&&!a||2===b&&!e)return this;var m=new this.constructor(q),c=this._result;if(b){var f=arguments[b-1];B(function(){J(b,m,f,c)})}else C(this,m,a,e);return m},"catch":function(a){return this.then(null,a)}};var H={Promise:y,polyfill:function(){var a;a="undefined"!==typeof c?c:"undefined"!==typeof window&&window.document?window:self;"Promise"in a&&"resolve"in a.Promise&&"reject"in a.Promise&&"all"in a.Promise&&"race"in a.Promise&&function(){var e;
32
+ new a.Promise(function(a){e=a});return b(e)}()||(a.Promise=y)}};"function"===typeof define&&define.amd?define(function(){return H}):"undefined"!==typeof h&&h.exports?h.exports=H:"undefined"!==typeof this&&(this.ES6Promise=H)}).call(this)}).call(this,g(1),"undefined"!==typeof global?global:"undefined"!==typeof self?self:"undefined"!==typeof window?window:{})},{1:1}],9:[function(g,h,d){var n=Object.prototype.hasOwnProperty,c=Object.prototype.toString,b=g(10),k=function(b){if(!b||"[object Object]"!==
33
+ c.call(b))return!1;var d=n.call(b,"constructor"),g=b.constructor&&b.constructor.prototype&&n.call(b.constructor.prototype,"isPrototypeOf");if(b.constructor&&!d&&!g)return!1;for(var k in b);return void 0===k||n.call(b,k)};h.exports=function t(){var c,d,g,h,a,e=arguments[0],m=1,r=arguments.length,v=!1;if("boolean"===typeof e)v=e,e=arguments[1]||{},m=2;else if("object"!==typeof e&&"function"!==typeof e||null==e)e={};for(;m<r;++m)if(c=arguments[m],null!=c)for(d in c)g=e[d],h=c[d],e!==h&&(v&&h&&(k(h)||
34
+ (a=b(h)))?(a?(a=!1,g=g&&b(g)?g:[]):g=g&&k(g)?g:{},e[d]=t(v,g,h)):void 0!==h&&(e[d]=h));return e}},{10:10}],10:[function(g,h,d){h.exports=Array.isArray||function(d){return"[object Array]"==Object.prototype.toString.call(d)}},{}],11:[function(g,h,d){var n=Object.prototype.hasOwnProperty,c=Object.prototype.toString;h.exports=function(b,d,f){if("[object Function]"!==c.call(d))throw new TypeError("iterator must be a function");var g=b.length;if(g===+g)for(var h=0;h<g;h++)d.call(f,b[h],h,b);else for(h in b)n.call(b,
35
+ h)&&d.call(f,b[h],h,b)}},{}],12:[function(g,h,d){h.exports="function"===typeof Object.create?function(d,c){d.super_=c;d.prototype=Object.create(c.prototype,{constructor:{value:d,enumerable:!1,writable:!0,configurable:!0}})}:function(d,c){d.super_=c;var b=function(){};b.prototype=c.prototype;d.prototype=new b;d.prototype.constructor=d}},{}],13:[function(g,h,d){(function(d){function c(a,e,m){var c=g(9);if(!a)throw Error("algoliasearch: Please provide an application ID. Usage: algoliasearch(applicationID, apiKey, opts)");
36
+ if(!e)throw Error("algoliasearch: Please provide an API key. Usage: algoliasearch(applicationID, apiKey, opts)");this.applicationID=a;this.apiKey=e;a=[this.applicationID+"-1.algolianet.com",this.applicationID+"-2.algolianet.com",this.applicationID+"-3.algolianet.com"];this.hosts={read:[],write:[]};this.hostIndex={read:0,write:0};m=m||{};e=m.protocol||"https:";var f=void 0===m.timeout?2E3:m.timeout;/:$/.test(e)||(e+=":");if("http:"!==m.protocol&&"https:"!==m.protocol)throw Error("algoliasearch: protocol must be `http:` or `https:` (was `"+
37
+ m.protocol+"`)");m.hosts?(this.hosts.read=c([],m.hosts),this.hosts.write=c([],m.hosts)):(this.hosts.read=[this.applicationID+"-dsn.algolia.net"].concat(a),this.hosts.write=[this.applicationID+".algolia.net"].concat(a));this.hosts.read=b(this.hosts.read,k(e));this.hosts.write=b(this.hosts.write,k(e));this.requestTimeout=f;this.extraHeaders=[];this.cache={};this._ua=m._ua;this._useCache=void 0===m._useCache?!0:m._useCache;this._setTimeout=m._setTimeout;q("init done, %j",this)}function b(a,e){for(var b=
38
+ [],c=0;c<a.length;++c)b.push(e(a[c],c));return b}function k(a){return function(e){return a+"//"+e.toLowerCase()}}function f(){throw Error("algoliasearch: Not implemented in this environment.\nIf you feel this is a mistake, write to support@algolia.com");}function t(a,e){var b=a.toLowerCase().replace(".","").replace("()","");return"algoliasearch: `"+a+"` was replaced by `"+e+"`. Please see https://github.com/algolia/algoliasearch-client-js/wiki/Deprecated#"+b}function p(a,e){e(a,0)}function l(a,e){var b=
39
+ !1;return function(){b||(console.log(e),b=!0);return a.apply(this,arguments)}}h.exports=c;"development"===d.env.NODE_ENV&&g(5).enable("algoliasearch*");var q=g(5)("algoliasearch"),x=g(11);c.prototype={deleteIndex:function(a,e){return this._jsonRequest({method:"DELETE",url:"/1/indexes/"+encodeURIComponent(a),hostType:"write",callback:e})},moveIndex:function(a,e,b){e={operation:"move",destination:e};return this._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(a)+"/operation",body:e,
40
+ hostType:"write",callback:b})},copyIndex:function(a,e,b){e={operation:"copy",destination:e};return this._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(a)+"/operation",body:e,hostType:"write",callback:b})},getLogs:function(a,e,b){if(0===arguments.length||"function"===typeof a)b=a,a=0,e=10;else if(1===arguments.length||"function"===typeof e)b=e,e=10;return this._jsonRequest({method:"GET",url:"/1/logs?offset="+a+"&length="+e,hostType:"read",callback:b})},listIndexes:function(a,e){var b=
41
+ "";void 0===a||"function"===typeof a?e=a:b="?page="+a;return this._jsonRequest({method:"GET",url:"/1/indexes"+b,hostType:"read",callback:e})},initIndex:function(a){return new this.Index(this,a)},listUserKeys:function(a){return this._jsonRequest({method:"GET",url:"/1/keys",hostType:"read",callback:a})},getUserKeyACL:function(a,e){return this._jsonRequest({method:"GET",url:"/1/keys/"+a,hostType:"read",callback:e})},deleteUserKey:function(a,e){return this._jsonRequest({method:"DELETE",url:"/1/keys/"+
42
+ a,hostType:"write",callback:e})},addUserKey:function(a,e,b){if(1===arguments.length||"function"===typeof e)b=e,e=null;var c={acl:a};e&&(c.validity=e.validity,c.maxQueriesPerIPPerHour=e.maxQueriesPerIPPerHour,c.maxHitsPerQuery=e.maxHitsPerQuery,c.indexes=e.indexes);return this._jsonRequest({method:"POST",url:"/1/keys",body:c,hostType:"write",callback:b})},addUserKeyWithValidity:l(function(a,e,b){return this.addUserKey(a,e,b)},t("client.addUserKeyWithValidity()","client.addUserKey()")),updateUserKey:function(a,
43
+ e,b,c){if(2===arguments.length||"function"===typeof b)c=b,b=null;var f={acl:e};b&&(f.validity=b.validity,f.maxQueriesPerIPPerHour=b.maxQueriesPerIPPerHour,f.maxHitsPerQuery=b.maxHitsPerQuery,f.indexes=b.indexes);return this._jsonRequest({method:"PUT",url:"/1/keys/"+a,body:f,hostType:"write",callback:c})},setSecurityTags:function(a){if("[object Array]"===Object.prototype.toString.call(a)){for(var e=[],b=0;b<a.length;++b)if("[object Array]"===Object.prototype.toString.call(a[b])){for(var c=[],f=0;f<
44
+ a[b].length;++f)c.push(a[b][f]);e.push("("+c.join(",")+")")}else e.push(a[b]);a=e.join(",")}this.securityTags=a},setUserToken:function(a){this.userToken=a},startQueriesBatch:l(function(){this._batch=[]},t("client.startQueriesBatch()","client.search()")),addQueryInBatch:l(function(a,e,b){this._batch.push({indexName:a,query:e,params:b})},t("client.addQueryInBatch()","client.search()")),clearCache:function(){this.cache={}},sendQueriesBatch:l(function(a){return this.search(this._batch,a)},t("client.sendQueriesBatch()",
45
+ "client.search()")),setRequestTimeout:function(a){a&&(this.requestTimeout=parseInt(a,10))},search:function(a,e){var c=this,f={requests:b(a,function(a){var e="";void 0!==a.query&&(e+="query="+encodeURIComponent(a.query));return{indexName:a.indexName,params:c._getSearchParams(a.params,e)}})};return this._jsonRequest({cache:this.cache,method:"POST",url:"/1/indexes/*/queries",body:f,hostType:"read",callback:e})},destroy:f,enableRateLimitForward:f,disableRateLimitForward:f,useSecuredAPIKey:f,disableSecuredAPIKey:f,
46
+ generateSecuredApiKey:f,Index:function(a,e){this.indexName=e;this.as=a;this.typeAheadValueOption=this.typeAheadArgs=null;this.cache={}},setExtraHeader:function(a,e){this.extraHeaders.push({name:a.toLowerCase(),value:e})},_sendQueriesBatch:function(a,e){return this._jsonRequest({cache:this.cache,method:"POST",url:"/1/indexes/*/queries",body:a,hostType:"read",fallback:{method:"GET",url:"/1/indexes/*",body:{params:function(){for(var e="",b=0;b<a.requests.length;++b)var c="/1/indexes/"+encodeURIComponent(a.requests[b].indexName)+
47
+ "?"+a.requests[b].params,e=e+(b+"="+encodeURIComponent(c)+"&");return e}()}},callback:e})},_jsonRequest:function(a){function e(g,l){function n(){d.hostIndex[a.hostType]=++d.hostIndex[a.hostType]%d.hosts[a.hostType].length;h+=1;l.timeout=d.requestTimeout*(h+1);return e(g,l)}var q;d._useCache&&(q=a.url);d._useCache&&c&&(q+="_body_"+l.body);if(d._useCache&&f&&void 0!==f[q])return b("serving response from cache"),d._promise.resolve(f[q]);if(h>=d.hosts[a.hostType].length){if(!a.fallback||!d._request.fallback||
48
+ k)return d._promise.reject(Error("Cannot connect to the AlgoliaSearch API. Send an email to support@algolia.com to report and resolve the issue."));h=0;l.method=a.fallback.method;l.url=a.fallback.url;l.jsonBody=a.fallback.body;l.jsonBody&&(l.body=JSON.stringify(a.fallback.body));l.timeout=d.requestTimeout*(h+1);d.hostIndex[a.hostType]=0;k=d.useFallback=!0;return e(d._request.fallback,l)}return g.call(d,d.hosts[a.hostType][d.hostIndex[a.hostType]]+l.url,{body:c,jsonBody:a.body,method:l.method,headers:d._computeRequestHeaders(),
49
+ timeout:l.timeout,debug:b}).then(function(a){if(a instanceof Error)return b("error: %s",a.message),n();b("received response: %j",a);var e=a&&a.body&&a.body.message&&a.body.status||a.statusCode||a&&a.body&&200,c=200===e||201===e,e=!c&&4!==Math.floor(e/100)&&1!==Math.floor(e/100);d._useCache&&c&&f&&(f[q]=a.body);return c?a.body:e?n():d._promise.reject(Error(a.body&&a.body.message||"Unknown error"))},function(c){b("error: %s, stack: %s",c.message,c.stack);d.useFallback?(d.hostIndex[a.hostType]=++d.hostIndex[a.hostType]%
50
+ d.hosts[a.hostType].length,h+=1):h=d.hosts[a.hostType].length;return e(g,l)})}var b=g(5)("algoliasearch:"+a.url),c,f=a.cache,d=this,h=0,k=!1;void 0!==a.body&&(c=JSON.stringify(a.body));b("request start");var l=d.useFallback&&a.fallback,n=l?a.fallback:a,l=e(l?d._request.fallback:d._request,{url:n.url,method:n.method,body:c,jsonBody:a.body,timeout:d.requestTimeout*(h+1)});if(a.callback)l.then(function(e){p(function(){a.callback(null,e)},d._setTimeout||setTimeout)},function(e){p(function(){a.callback(e)},
51
+ d._setTimeout||setTimeout)});else return l},_getSearchParams:function(a,e){if(this._isUndefined(a)||null===a)return e;for(var b in a)null!==b&&a.hasOwnProperty(b)&&(e+=""===e?"":"&",e+=b+"="+encodeURIComponent("[object Array]"===Object.prototype.toString.call(a[b])?JSON.stringify(a[b]):a[b]));return e},_isUndefined:function(a){return void 0===a},_computeRequestHeaders:function(){var a={"x-algolia-api-key":this.apiKey,"x-algolia-application-id":this.applicationID,"x-user-agent":this._ua};this.userToken&&
52
+ (a["x-algolia-usertoken"]=this.userToken);this.securityTags&&(a["x-algolia-tagfilters"]=this.securityTags);this.extraHeaders&&x(this.extraHeaders,function(e){a[e.name]=e.value});return a}};c.prototype.Index.prototype={clearCache:function(){this.cache={}},addObject:function(a,e,b){if(1===arguments.length||"function"===typeof e)b=e,e=void 0;return this.as._jsonRequest({method:void 0!==e?"PUT":"POST",url:"/1/indexes/"+encodeURIComponent(this.indexName)+(void 0!==e?"/"+encodeURIComponent(e):""),body:a,
53
+ hostType:"write",callback:b})},addObjects:function(a,e){for(var b={requests:[]},c=0;c<a.length;++c)b.requests.push({action:"addObject",body:a[c]});return this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/batch",body:b,hostType:"write",callback:e})},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",
54
+ url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/"+encodeURIComponent(a)+d,hostType:"read",callback:c})},getObjects:function(a,e){var c=this,d={requests:b(a,function(a){return{indexName:c.indexName,objectID:a}})};return this.as._jsonRequest({method:"POST",url:"/1/indexes/*/objects",hostType:"read",body:d,callback:e})},partialUpdateObject:function(a,b){return this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/"+encodeURIComponent(a.objectID)+"/partial",
55
+ body:a,hostType:"write",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,hostType:"write",callback:b})},saveObject:function(a,b){return this.as._jsonRequest({method:"PUT",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/"+encodeURIComponent(a.objectID),body:a,
56
+ hostType:"write",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,hostType:"write",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"===typeof b?
57
+ b(c):this.as._promise.reject(c)}return this.as._jsonRequest({method:"DELETE",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/"+encodeURIComponent(a),hostType:"write",callback:b})},deleteObjects:function(a,e){var c={requests:b(a,function(a){return{action:"deleteObject",objectID:a,body:{objectID:a}}})};return this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/batch",body:c,hostType:"write",callback:e})},deleteByQuery:function(a,e,c){function d(a){return g.waitTask(a.taskID)}
58
+ function f(){return g.deleteByQuery(a,e)}var g=this,h=g.as;if(1===arguments.length||"function"===typeof e)c=e,e={};e.attributesToRetrieve="objectID";e.hitsPerPage=1E3;this.clearCache();var k=this.search(a,e).then(function(a){if(0===a.nbHits)return a;a=b(a.hits,function(a){return a.objectID});return g.deleteObjects(a).then(d).then(f)});if(!c)return k;k.then(function(){p(function(){c(null)},h._setTimeout||setTimeout)},function(a){p(function(){c(a)},h._setTimeout||setTimeout)})},search:function(a,b,
59
+ c){if("function"===typeof a&&"object"===typeof b||"object"===typeof c)throw Error("algoliasearch: index.search usage is index.search(query, params, cb)");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,d));return this._search(d,c)},browse:function(a,
60
+ 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,hostType:"read",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=c.as,f=this.as._jsonRequest({method:"GET",hostType:"read",url:"/1/indexes/"+encodeURIComponent(c.indexName)+
61
+ "/task/"+a}).then(function(b){return"published"!==b.status?c.as._promise.delay(100).then(function(){return c.waitTask(a)}):b});if(!b)return f;f.then(function(a){p(function(){b(null,a)},d._setTimeout||setTimeout)},function(a){p(function(){b(a)},d._setTimeout||setTimeout)})},clearIndex:function(a){return this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/clear",hostType:"write",callback:a})},getSettings:function(a){return this.as._jsonRequest({method:"GET",url:"/1/indexes/"+
62
+ encodeURIComponent(this.indexName)+"/settings",hostType:"read",callback:a})},setSettings:function(a,b){return this.as._jsonRequest({method:"PUT",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/settings",hostType:"write",body:a,callback:b})},listUserKeys:function(a){return this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/keys",hostType:"read",callback:a})},getUserKeyACL:function(a,b){return this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(this.indexName)+
63
+ "/keys/"+a,hostType:"read",callback:b})},deleteUserKey:function(a,b){return this.as._jsonRequest({method:"DELETE",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/keys/"+a,hostType:"write",callback:b})},addUserKey:function(a,b,c){if(1===arguments.length||"function"===typeof b)c=b,b=null;var d={acl:a};b&&(d.validity=b.validity,d.maxQueriesPerIPPerHour=b.maxQueriesPerIPPerHour,d.maxHitsPerQuery=b.maxHitsPerQuery);return this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(this.indexName)+
64
+ "/keys",body:d,hostType:"write",callback:c})},addUserKeyWithValidity:l(function(a,b,c){return this.addUserKey(a,b,c)},t("index.addUserKeyWithValidity()","index.addUserKey()")),updateUserKey:function(a,b,c,d){if(2===arguments.length||"function"===typeof c)d=c,c=null;var f={acl:b};c&&(f.validity=c.validity,f.maxQueriesPerIPPerHour=c.maxQueriesPerIPPerHour,f.maxHitsPerQuery=c.maxHitsPerQuery);return this.as._jsonRequest({method:"PUT",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/keys/"+a,body:f,
65
+ hostType:"write",callback:d})},_search:function(a,b){return this.as._jsonRequest({cache:this.cache,method:"POST",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/query",body:{params:a},hostType:"read",fallback:{method:"GET",url:"/1/indexes/"+encodeURIComponent(this.indexName),body:{params:a}},callback:b})},as:null,indexName:null,typeAheadArgs:null,typeAheadValueOption:null}}).call(this,g(1))},{1:1,11:11,5:5,9:9}],14:[function(g,h,d){h.exports=function(c,b,d){function f(){b.debug("JSONP: success");
66
+ v||a||(v=!0,x||(b.debug("JSONP: Fail. Script loaded but did not call the callback"),h(),d(Error("Failed to load JSONP script"))))}function g(){"loaded"!==this.readyState&&"complete"!==this.readyState||f()}function h(){clearTimeout(u);m.onload=null;m.onreadystatechange=null;m.onerror=null;e.removeChild(m);try{delete window[r],delete window[r+"_loaded"]}catch(a){window[r]=null,window[r+"_loaded"]=null}}function l(){b.debug("JSONP: Script timeout");a=!0;h();d(Error("Timeout - Could not connect to endpoint "+
67
+ c))}function q(){b.debug("JSONP: Script error");v||a||(h(),d(Error("Failed to load JSONP script")))}if("GET"!==b.method)d(Error("Method "+b.method+" "+c+" is not supported by JSONP."));else{b.debug("JSONP: start");var x=!1,a=!1;n+=1;var e=document.getElementsByTagName("head")[0],m=document.createElement("script"),r="algoliaJSONP_"+n,v=!1;window[r]=function(b){try{delete window[r]}catch(c){window[r]=void 0}a||(x=!0,h(),d(null,{body:b}))};c+="&callback="+r;b.jsonBody&&b.jsonBody.params&&(c+="&"+b.jsonBody.params);
68
+ var u=setTimeout(l,b.timeout);m.onreadystatechange=g;m.onload=f;m.onerror=q;m.async=!0;m.defer=!0;m.src=c;e.appendChild(m)}};var n=0},{}],15:[function(g,h,d){(function(d){function c(d,f,a){var e=g(9),h=g(17);a=e(!0,{},a)||{};void 0===a.protocol&&(a.protocol=h());a._ua=c.ua;return new b(d,f,a)}function b(){f.apply(this,arguments)}var h=g(12),f=g(13),t=g(18),p=g(14);window.algoliasearch=g(16);c.version=g(19);c.ua="Algolia for jQuery "+c.version;var l=d.jQuery;l.algolia={Client:c,ua:c.ua,version:c.version};
69
+ h(b,f);b.prototype._request=function(b,c){return l.Deferred(function(a){var d=c.body;b=t(b,c.headers);l.ajax(b,{type:c.method,timeout:c.timeout,dataType:"json",data:d,complete:function(c,d){"timeout"===d?a.resolve(Error("Timeout - Could not connect to endpoint "+b)):0===c.status?a.reject(Error("Network error")):a.resolve({statusCode:c.status,body:c.responseJSON})}})}).promise()};b.prototype._request.fallback=function(b,c){b=t(b,c.headers);return l.Deferred(function(a){p(b,c,function(b,c){b?a.reject(b):
70
+ a.resolve(c)})}).promise()};b.prototype._promise={reject:function(b){return l.Deferred(function(c){c.reject(b)}).promise()},resolve:function(b){return l.Deferred(function(c){c.resolve(b)}).promise()},delay:function(b){return l.Deferred(function(c){setTimeout(function(){c.resolve()},b)}).promise()}}}).call(this,"undefined"!==typeof global?global:"undefined"!==typeof self?self:"undefined"!==typeof window?window:{})},{12:12,13:13,14:14,16:16,17:17,18:18,19:19,9:9}],16:[function(g,h,d){(function(d){function c(a,
71
+ d,f){var h=g(9),k=g(17);f=h(!0,{},f)||{};void 0===f.protocol&&(f.protocol=k());f._ua=c.ua;return new b(a,d,f)}function b(){t.apply(this,arguments)}h.exports=c;var k=g(12),f=d.Promise||g(8).Promise,t=g(13),p=g(18),l=g(14);c.version=g(19);c.ua="Algolia for vanilla JavaScript "+c.version;var q="XDomainRequest"in window,x="withCredentials"in new XMLHttpRequest,a="timeout"in new XMLHttpRequest;k(b,t);b.prototype._request=function(b,c){return new f(function(d,f){function g(){if(!w){a||clearTimeout(t);var b=
72
+ null;try{b=JSON.parse(n.responseText)}catch(c){}d({body:b,statusCode:n.status})}}function h(b){w||(a||clearTimeout(t),f(Error("Could not connect to host, error was:"+b)))}function k(){a||(w=!0,n.abort());d(Error("Timeout - Could not connect to endpoint "+b))}if(x||q){b=p(b,c.headers);var l=c.body,n=x?new XMLHttpRequest:new XDomainRequest,t,w;n instanceof XMLHttpRequest?n.open(c.method,b,!0):n.open(c.method,b);x&&l&&"GET"!==c.method&&n.setRequestHeader("content-type","application/x-www-form-urlencoded");
73
+ n.onprogress=function(){};n.onload=g;n.onerror=h;a?(n.timeout=c.timeout,n.ontimeout=k):t=setTimeout(k,c.timeout);n.send(l)}else f(Error("CORS not supported"))})};b.prototype._request.fallback=function(a,b){a=p(a,b.headers);return new f(function(c,d){l(a,b,function(a,b){a?d(a):c(b)})})};b.prototype._promise={reject:function(a){return f.reject(a)},resolve:function(a){return f.resolve(a)},delay:function(a){return new f(function(b){setTimeout(b,a)})}}}).call(this,"undefined"!==typeof global?global:"undefined"!==
74
+ typeof self?self:"undefined"!==typeof window?window:{})},{12:12,13:13,14:14,17:17,18:18,19:19,8:8,9:9}],17:[function(g,h,d){(function(d){h.exports=function(){var c=d.document.location.protocol;"http:"!==c&&"https:"!==c&&(c="http:");return c}}).call(this,"undefined"!==typeof global?global:"undefined"!==typeof self?self:"undefined"!==typeof window?window:{})},{}],18:[function(g,h,d){h.exports=function(c,b){c=/\?/.test(c)?c+"&":c+"?";return c+n.encode(b)};var n=g(4)},{4:4}],19:[function(g,h,d){h.exports=
75
+ "3.3.0"},{}]},{},[15]);
@@ -1,4 +1,4 @@
1
- /*! algoliasearch 3.0.3 | © 2014, 2015 Algolia SAS | github.com/algolia/algoliasearch-client-js */
1
+ /*! algoliasearch 3.3.0 | © 2014, 2015 Algolia SAS | github.com/algolia/algoliasearch-client-js */
2
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
3
 
4
4
  module.exports = function load (src, opts, cb) {
@@ -200,209 +200,746 @@ function migrationLayer(buildName) {
200
200
 
201
201
  },{"2":2,"3":3,"4":4}]},{},[5])(5)
202
202
  });(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.algoliasearch = 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){
203
- (function (global){
204
- // this is the standalone build entry of AlgoliaSearch
205
- var createAlgoliasearch = require(6);
203
+ // shim for using process in browser
206
204
 
207
- module.exports = createAlgoliasearch(request);
205
+ var process = module.exports = {};
206
+ var queue = [];
207
+ var draining = false;
208
208
 
209
- var Promise = global.Promise || require(3).Promise;
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
+ };
210
233
 
211
- var JSONPRequest = require(5);
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 = {};
212
240
 
213
- var support = {
214
- hasXMLHttpRequest: 'XMLHttpRequest' in window,
215
- hasXDomainRequest: 'XDomainRequest' in window,
216
- cors: 'withCredentials' in new XMLHttpRequest(),
217
- timeout: 'timeout' in new XMLHttpRequest()
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');
218
253
  };
219
254
 
220
- function request(url, opts) {
221
- return new Promise(function(resolve, reject) {
222
- // no cors or XDomainRequest, no request
223
- if (!support.cors && !support.hasXDomainRequest) {
224
- // very old browser, not supported
225
- reject(new Error('CORS not supported'));
226
- return;
227
- }
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; };
228
261
 
229
- var body = null;
230
- var req = support.cors ? new XMLHttpRequest() : new XDomainRequest();
231
- var ontimeout;
232
- var timedOut;
262
+ },{}],2:[function(require,module,exports){
263
+ // Copyright Joyent, Inc. and other Node contributors.
264
+ //
265
+ // Permission is hereby granted, free of charge, to any person obtaining a
266
+ // copy of this software and associated documentation files (the
267
+ // "Software"), to deal in the Software without restriction, including
268
+ // without limitation the rights to use, copy, modify, merge, publish,
269
+ // distribute, sublicense, and/or sell copies of the Software, and to permit
270
+ // persons to whom the Software is furnished to do so, subject to the
271
+ // following conditions:
272
+ //
273
+ // The above copyright notice and this permission notice shall be included
274
+ // in all copies or substantial portions of the Software.
275
+ //
276
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
277
+ // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
278
+ // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
279
+ // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
280
+ // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
281
+ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
282
+ // USE OR OTHER DEALINGS IN THE SOFTWARE.
283
+
284
+ 'use strict';
285
+
286
+ // If obj.hasOwnProperty has been overridden, then calling
287
+ // obj.hasOwnProperty(prop) will break.
288
+ // See: https://github.com/joyent/node/issues/1707
289
+ function hasOwnProperty(obj, prop) {
290
+ return Object.prototype.hasOwnProperty.call(obj, prop);
291
+ }
233
292
 
234
- if (opts.body !== undefined) {
235
- body = JSON.stringify(opts.body);
293
+ module.exports = function(qs, sep, eq, options) {
294
+ sep = sep || '&';
295
+ eq = eq || '=';
296
+ var obj = {};
297
+
298
+ if (typeof qs !== 'string' || qs.length === 0) {
299
+ return obj;
300
+ }
301
+
302
+ var regexp = /\+/g;
303
+ qs = qs.split(sep);
304
+
305
+ var maxKeys = 1000;
306
+ if (options && typeof options.maxKeys === 'number') {
307
+ maxKeys = options.maxKeys;
308
+ }
309
+
310
+ var len = qs.length;
311
+ // maxKeys <= 0 means that we should not limit keys count
312
+ if (maxKeys > 0 && len > maxKeys) {
313
+ len = maxKeys;
314
+ }
315
+
316
+ for (var i = 0; i < len; ++i) {
317
+ var x = qs[i].replace(regexp, '%20'),
318
+ idx = x.indexOf(eq),
319
+ kstr, vstr, k, v;
320
+
321
+ if (idx >= 0) {
322
+ kstr = x.substr(0, idx);
323
+ vstr = x.substr(idx + 1);
324
+ } else {
325
+ kstr = x;
326
+ vstr = '';
236
327
  }
237
328
 
238
- // do not rely on default XHR async flag, as some analytics code like hotjar
239
- // breaks it and set it to false by default
240
- if (req instanceof XMLHttpRequest) {
241
- req.open(opts.method, url, true);
329
+ k = decodeURIComponent(kstr);
330
+ v = decodeURIComponent(vstr);
331
+
332
+ if (!hasOwnProperty(obj, k)) {
333
+ obj[k] = v;
334
+ } else if (isArray(obj[k])) {
335
+ obj[k].push(v);
242
336
  } else {
243
- req.open(opts.method, url);
337
+ obj[k] = [obj[k], v];
244
338
  }
339
+ }
245
340
 
246
- if (support.cors && body && opts.method !== 'GET') {
247
- req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
341
+ return obj;
342
+ };
343
+
344
+ var isArray = Array.isArray || function (xs) {
345
+ return Object.prototype.toString.call(xs) === '[object Array]';
346
+ };
347
+
348
+ },{}],3:[function(require,module,exports){
349
+ // Copyright Joyent, Inc. and other Node contributors.
350
+ //
351
+ // Permission is hereby granted, free of charge, to any person obtaining a
352
+ // copy of this software and associated documentation files (the
353
+ // "Software"), to deal in the Software without restriction, including
354
+ // without limitation the rights to use, copy, modify, merge, publish,
355
+ // distribute, sublicense, and/or sell copies of the Software, and to permit
356
+ // persons to whom the Software is furnished to do so, subject to the
357
+ // following conditions:
358
+ //
359
+ // The above copyright notice and this permission notice shall be included
360
+ // in all copies or substantial portions of the Software.
361
+ //
362
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
363
+ // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
364
+ // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
365
+ // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
366
+ // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
367
+ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
368
+ // USE OR OTHER DEALINGS IN THE SOFTWARE.
369
+
370
+ 'use strict';
371
+
372
+ var stringifyPrimitive = function(v) {
373
+ switch (typeof v) {
374
+ case 'string':
375
+ return v;
376
+
377
+ case 'boolean':
378
+ return v ? 'true' : 'false';
379
+
380
+ case 'number':
381
+ return isFinite(v) ? v : '';
382
+
383
+ default:
384
+ return '';
385
+ }
386
+ };
387
+
388
+ module.exports = function(obj, sep, eq, name) {
389
+ sep = sep || '&';
390
+ eq = eq || '=';
391
+ if (obj === null) {
392
+ obj = undefined;
393
+ }
394
+
395
+ if (typeof obj === 'object') {
396
+ return map(objectKeys(obj), function(k) {
397
+ var ks = encodeURIComponent(stringifyPrimitive(k)) + eq;
398
+ if (isArray(obj[k])) {
399
+ return map(obj[k], function(v) {
400
+ return ks + encodeURIComponent(stringifyPrimitive(v));
401
+ }).join(sep);
402
+ } else {
403
+ return ks + encodeURIComponent(stringifyPrimitive(obj[k]));
404
+ }
405
+ }).join(sep);
406
+
407
+ }
408
+
409
+ if (!name) return '';
410
+ return encodeURIComponent(stringifyPrimitive(name)) + eq +
411
+ encodeURIComponent(stringifyPrimitive(obj));
412
+ };
413
+
414
+ var isArray = Array.isArray || function (xs) {
415
+ return Object.prototype.toString.call(xs) === '[object Array]';
416
+ };
417
+
418
+ function map (xs, f) {
419
+ if (xs.map) return xs.map(f);
420
+ var res = [];
421
+ for (var i = 0; i < xs.length; i++) {
422
+ res.push(f(xs[i], i));
423
+ }
424
+ return res;
425
+ }
426
+
427
+ var objectKeys = Object.keys || function (obj) {
428
+ var res = [];
429
+ for (var key in obj) {
430
+ if (Object.prototype.hasOwnProperty.call(obj, key)) res.push(key);
431
+ }
432
+ return res;
433
+ };
434
+
435
+ },{}],4:[function(require,module,exports){
436
+ 'use strict';
437
+
438
+ exports.decode = exports.parse = require(2);
439
+ exports.encode = exports.stringify = require(3);
440
+
441
+ },{"2":2,"3":3}],5:[function(require,module,exports){
442
+
443
+ /**
444
+ * This is the web browser implementation of `debug()`.
445
+ *
446
+ * Expose `debug()` as the module.
447
+ */
448
+
449
+ exports = module.exports = require(6);
450
+ exports.log = log;
451
+ exports.formatArgs = formatArgs;
452
+ exports.save = save;
453
+ exports.load = load;
454
+ exports.useColors = useColors;
455
+
456
+ /**
457
+ * Use chrome.storage.local if we are in an app
458
+ */
459
+
460
+ var storage;
461
+
462
+ if (typeof chrome !== 'undefined' && typeof chrome.storage !== 'undefined')
463
+ storage = chrome.storage.local;
464
+ else
465
+ storage = localstorage();
466
+
467
+ /**
468
+ * Colors.
469
+ */
470
+
471
+ exports.colors = [
472
+ 'lightseagreen',
473
+ 'forestgreen',
474
+ 'goldenrod',
475
+ 'dodgerblue',
476
+ 'darkorchid',
477
+ 'crimson'
478
+ ];
479
+
480
+ /**
481
+ * Currently only WebKit-based Web Inspectors, Firefox >= v31,
482
+ * and the Firebug extension (any Firefox version) are known
483
+ * to support "%c" CSS customizations.
484
+ *
485
+ * TODO: add a `localStorage` variable to explicitly enable/disable colors
486
+ */
487
+
488
+ function useColors() {
489
+ // is webkit? http://stackoverflow.com/a/16459606/376773
490
+ return ('WebkitAppearance' in document.documentElement.style) ||
491
+ // is firebug? http://stackoverflow.com/a/398120/376773
492
+ (window.console && (console.firebug || (console.exception && console.table))) ||
493
+ // is firefox >= v31?
494
+ // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
495
+ (navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31);
496
+ }
497
+
498
+ /**
499
+ * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
500
+ */
501
+
502
+ exports.formatters.j = function(v) {
503
+ return JSON.stringify(v);
504
+ };
505
+
506
+
507
+ /**
508
+ * Colorize log arguments if enabled.
509
+ *
510
+ * @api public
511
+ */
512
+
513
+ function formatArgs() {
514
+ var args = arguments;
515
+ var useColors = this.useColors;
516
+
517
+ args[0] = (useColors ? '%c' : '')
518
+ + this.namespace
519
+ + (useColors ? ' %c' : ' ')
520
+ + args[0]
521
+ + (useColors ? '%c ' : ' ')
522
+ + '+' + exports.humanize(this.diff);
523
+
524
+ if (!useColors) return args;
525
+
526
+ var c = 'color: ' + this.color;
527
+ args = [args[0], c, 'color: inherit'].concat(Array.prototype.slice.call(args, 1));
528
+
529
+ // the final "%c" is somewhat tricky, because there could be other
530
+ // arguments passed either before or after the %c, so we need to
531
+ // figure out the correct index to insert the CSS into
532
+ var index = 0;
533
+ var lastC = 0;
534
+ args[0].replace(/%[a-z%]/g, function(match) {
535
+ if ('%%' === match) return;
536
+ index++;
537
+ if ('%c' === match) {
538
+ // we only are interested in the *last* %c
539
+ // (the user may have provided their own)
540
+ lastC = index;
248
541
  }
542
+ });
249
543
 
250
- req.onload = load;
251
- req.onerror = error;
544
+ args.splice(lastC, 0, c);
545
+ return args;
546
+ }
252
547
 
253
- if (support.timeout) {
254
- // .timeout supported by both XHR and XDR,
255
- // we do receive timeout event, tested
256
- req.timeout = opts.timeout;
548
+ /**
549
+ * Invokes `console.log()` when available.
550
+ * No-op when `console.log` is not a "function".
551
+ *
552
+ * @api public
553
+ */
257
554
 
258
- req.ontimeout = timeout;
555
+ function log() {
556
+ // this hackery is required for IE8/9, where
557
+ // the `console.log` function doesn't have 'apply'
558
+ return 'object' === typeof console
559
+ && console.log
560
+ && Function.prototype.apply.call(console.log, console, arguments);
561
+ }
562
+
563
+ /**
564
+ * Save `namespaces`.
565
+ *
566
+ * @param {String} namespaces
567
+ * @api private
568
+ */
569
+
570
+ function save(namespaces) {
571
+ try {
572
+ if (null == namespaces) {
573
+ storage.removeItem('debug');
259
574
  } else {
260
- ontimeout = setTimeout(timeout, opts.timeout);
575
+ storage.debug = namespaces;
261
576
  }
577
+ } catch(e) {}
578
+ }
262
579
 
263
- req.send(body);
580
+ /**
581
+ * Load `namespaces`.
582
+ *
583
+ * @return {String} returns the previously persisted debug modes
584
+ * @api private
585
+ */
264
586
 
265
- // event object not received in IE8, at least
266
- // but we do not use it, still important to note
267
- function load(/*event*/) {
268
- // When browser does not supports req.timeout, we can
269
- // have both a load and timeout event, since handled by a dumb setTimeout
270
- if (timedOut) {
271
- return;
272
- }
587
+ function load() {
588
+ var r;
589
+ try {
590
+ r = storage.debug;
591
+ } catch(e) {}
592
+ return r;
593
+ }
273
594
 
274
- if (!support.timeout) {
275
- clearTimeout(ontimeout);
276
- }
595
+ /**
596
+ * Enable namespaces listed in `localStorage.debug` initially.
597
+ */
277
598
 
278
- var response = null;
599
+ exports.enable(load());
279
600
 
280
- try {
281
- response = JSON.parse(req.responseText);
282
- } catch(e) {}
601
+ /**
602
+ * Localstorage attempts to return the localstorage.
603
+ *
604
+ * This is necessary because safari throws
605
+ * when a user disables cookies/localstorage
606
+ * and you attempt to access it.
607
+ *
608
+ * @return {LocalStorage}
609
+ * @api private
610
+ */
283
611
 
284
- resolve({
285
- body: response,
286
- statusCode: req.status
287
- });
612
+ function localstorage(){
613
+ try {
614
+ return window.localStorage;
615
+ } catch (e) {}
616
+ }
617
+
618
+ },{"6":6}],6:[function(require,module,exports){
619
+
620
+ /**
621
+ * This is the common logic for both the Node.js and web browser
622
+ * implementations of `debug()`.
623
+ *
624
+ * Expose `debug()` as the module.
625
+ */
626
+
627
+ exports = module.exports = debug;
628
+ exports.coerce = coerce;
629
+ exports.disable = disable;
630
+ exports.enable = enable;
631
+ exports.enabled = enabled;
632
+ exports.humanize = require(7);
633
+
634
+ /**
635
+ * The currently active debug mode names, and names to skip.
636
+ */
637
+
638
+ exports.names = [];
639
+ exports.skips = [];
640
+
641
+ /**
642
+ * Map of special "%n" handling functions, for the debug "format" argument.
643
+ *
644
+ * Valid key names are a single, lowercased letter, i.e. "n".
645
+ */
646
+
647
+ exports.formatters = {};
648
+
649
+ /**
650
+ * Previously assigned color.
651
+ */
652
+
653
+ var prevColor = 0;
654
+
655
+ /**
656
+ * Previous log timestamp.
657
+ */
658
+
659
+ var prevTime;
660
+
661
+ /**
662
+ * Select a color.
663
+ *
664
+ * @return {Number}
665
+ * @api private
666
+ */
667
+
668
+ function selectColor() {
669
+ return exports.colors[prevColor++ % exports.colors.length];
670
+ }
671
+
672
+ /**
673
+ * Create a debugger with the given `namespace`.
674
+ *
675
+ * @param {String} namespace
676
+ * @return {Function}
677
+ * @api public
678
+ */
679
+
680
+ function debug(namespace) {
681
+
682
+ // define the `disabled` version
683
+ function disabled() {
684
+ }
685
+ disabled.enabled = false;
686
+
687
+ // define the `enabled` version
688
+ function enabled() {
689
+
690
+ var self = enabled;
691
+
692
+ // set `diff` timestamp
693
+ var curr = +new Date();
694
+ var ms = curr - (prevTime || curr);
695
+ self.diff = ms;
696
+ self.prev = prevTime;
697
+ self.curr = curr;
698
+ prevTime = curr;
699
+
700
+ // add the `color` if not set
701
+ if (null == self.useColors) self.useColors = exports.useColors();
702
+ if (null == self.color && self.useColors) self.color = selectColor();
703
+
704
+ var args = Array.prototype.slice.call(arguments);
705
+
706
+ args[0] = exports.coerce(args[0]);
707
+
708
+ if ('string' !== typeof args[0]) {
709
+ // anything else let's inspect with %o
710
+ args = ['%o'].concat(args);
288
711
  }
289
712
 
290
- function error(event) {
291
- if (timedOut) {
292
- return;
293
- }
713
+ // apply any `formatters` transformations
714
+ var index = 0;
715
+ args[0] = args[0].replace(/%([a-z%])/g, function(match, format) {
716
+ // if we encounter an escaped % then don't increase the array index
717
+ if (match === '%%') return match;
718
+ index++;
719
+ var formatter = exports.formatters[format];
720
+ if ('function' === typeof formatter) {
721
+ var val = args[index];
722
+ match = formatter.call(self, val);
294
723
 
295
- if (!support.timeout) {
296
- clearTimeout(ontimeout);
724
+ // now we need to remove `args[index]` since it's inlined in the `format`
725
+ args.splice(index, 1);
726
+ index--;
297
727
  }
728
+ return match;
729
+ });
298
730
 
299
- // error event is trigerred both with XDR/XHR on:
300
- // - DNS error
301
- // - unallowed cross domain request
302
- reject(new Error('Could not connect to host, error was:' + event));
731
+ if ('function' === typeof exports.formatArgs) {
732
+ args = exports.formatArgs.apply(self, args);
303
733
  }
734
+ var logFn = enabled.log || exports.log || console.log.bind(console);
735
+ logFn.apply(self, args);
736
+ }
737
+ enabled.enabled = true;
304
738
 
305
- function timeout() {
306
- if (!support.timeout) {
307
- timedOut = true;
308
- req.abort();
309
- }
739
+ var fn = exports.enabled(namespace) ? enabled : disabled;
310
740
 
311
- resolve(new Error('Timeout - Could not connect to endpoint ' + url));
741
+ fn.namespace = namespace;
742
+
743
+ return fn;
744
+ }
745
+
746
+ /**
747
+ * Enables a debug mode by namespaces. This can include modes
748
+ * separated by a colon and wildcards.
749
+ *
750
+ * @param {String} namespaces
751
+ * @api public
752
+ */
753
+
754
+ function enable(namespaces) {
755
+ exports.save(namespaces);
756
+
757
+ var split = (namespaces || '').split(/[\s,]+/);
758
+ var len = split.length;
759
+
760
+ for (var i = 0; i < len; i++) {
761
+ if (!split[i]) continue; // ignore empty strings
762
+ namespaces = split[i].replace(/\*/g, '.*?');
763
+ if (namespaces[0] === '-') {
764
+ exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$'));
765
+ } else {
766
+ exports.names.push(new RegExp('^' + namespaces + '$'));
312
767
  }
768
+ }
769
+ }
313
770
 
314
- });
771
+ /**
772
+ * Disable debug output.
773
+ *
774
+ * @api public
775
+ */
776
+
777
+ function disable() {
778
+ exports.enable('');
315
779
  }
316
780
 
317
- request.fallback = function(url, opts) {
318
- return new Promise(function(resolve, reject) {
319
- JSONPRequest(url, opts, function JSONPRequestDone(err, content) {
320
- if (err) {
321
- reject(err);
322
- return;
323
- }
781
+ /**
782
+ * Returns true if the given mode name is enabled, false otherwise.
783
+ *
784
+ * @param {String} name
785
+ * @return {Boolean}
786
+ * @api public
787
+ */
324
788
 
325
- resolve(content);
326
- });
327
- });
328
- };
789
+ function enabled(name) {
790
+ var i, len;
791
+ for (i = 0, len = exports.skips.length; i < len; i++) {
792
+ if (exports.skips[i].test(name)) {
793
+ return false;
794
+ }
795
+ }
796
+ for (i = 0, len = exports.names.length; i < len; i++) {
797
+ if (exports.names[i].test(name)) {
798
+ return true;
799
+ }
800
+ }
801
+ return false;
802
+ }
329
803
 
330
- request.reject = function(val) {
331
- return Promise.reject(val);
332
- };
804
+ /**
805
+ * Coerce `val`.
806
+ *
807
+ * @param {Mixed} val
808
+ * @return {Mixed}
809
+ * @api private
810
+ */
811
+
812
+ function coerce(val) {
813
+ if (val instanceof Error) return val.stack || val.message;
814
+ return val;
815
+ }
816
+
817
+ },{"7":7}],7:[function(require,module,exports){
818
+ /**
819
+ * Helpers.
820
+ */
821
+
822
+ var s = 1000;
823
+ var m = s * 60;
824
+ var h = m * 60;
825
+ var d = h * 24;
826
+ var y = d * 365.25;
827
+
828
+ /**
829
+ * Parse or format the given `val`.
830
+ *
831
+ * Options:
832
+ *
833
+ * - `long` verbose formatting [false]
834
+ *
835
+ * @param {String|Number} val
836
+ * @param {Object} options
837
+ * @return {String|Number}
838
+ * @api public
839
+ */
333
840
 
334
- request.resolve = function(val) {
335
- return Promise.resolve(val);
841
+ module.exports = function(val, options){
842
+ options = options || {};
843
+ if ('string' == typeof val) return parse(val);
844
+ return options.long
845
+ ? long(val)
846
+ : short(val);
336
847
  };
337
848
 
338
- request.delay = function(ms) {
339
- return new Promise(function(resolve/*, reject*/) {
340
- setTimeout(resolve, ms);
341
- });
342
- };
849
+ /**
850
+ * Parse the given `str` and return milliseconds.
851
+ *
852
+ * @param {String} str
853
+ * @return {Number}
854
+ * @api private
855
+ */
343
856
 
344
- }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
345
- },{"3":3,"5":5,"6":6}],2:[function(require,module,exports){
346
- // shim for using process in browser
857
+ function parse(str) {
858
+ var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(str);
859
+ if (!match) return;
860
+ var n = parseFloat(match[1]);
861
+ var type = (match[2] || 'ms').toLowerCase();
862
+ switch (type) {
863
+ case 'years':
864
+ case 'year':
865
+ case 'yrs':
866
+ case 'yr':
867
+ case 'y':
868
+ return n * y;
869
+ case 'days':
870
+ case 'day':
871
+ case 'd':
872
+ return n * d;
873
+ case 'hours':
874
+ case 'hour':
875
+ case 'hrs':
876
+ case 'hr':
877
+ case 'h':
878
+ return n * h;
879
+ case 'minutes':
880
+ case 'minute':
881
+ case 'mins':
882
+ case 'min':
883
+ case 'm':
884
+ return n * m;
885
+ case 'seconds':
886
+ case 'second':
887
+ case 'secs':
888
+ case 'sec':
889
+ case 's':
890
+ return n * s;
891
+ case 'milliseconds':
892
+ case 'millisecond':
893
+ case 'msecs':
894
+ case 'msec':
895
+ case 'ms':
896
+ return n;
897
+ }
898
+ }
347
899
 
348
- var process = module.exports = {};
349
- var queue = [];
350
- var draining = false;
900
+ /**
901
+ * Short format for `ms`.
902
+ *
903
+ * @param {Number} ms
904
+ * @return {String}
905
+ * @api private
906
+ */
351
907
 
352
- function drainQueue() {
353
- if (draining) {
354
- return;
355
- }
356
- draining = true;
357
- var currentQueue;
358
- var len = queue.length;
359
- while(len) {
360
- currentQueue = queue;
361
- queue = [];
362
- var i = -1;
363
- while (++i < len) {
364
- currentQueue[i]();
365
- }
366
- len = queue.length;
367
- }
368
- draining = false;
908
+ function short(ms) {
909
+ if (ms >= d) return Math.round(ms / d) + 'd';
910
+ if (ms >= h) return Math.round(ms / h) + 'h';
911
+ if (ms >= m) return Math.round(ms / m) + 'm';
912
+ if (ms >= s) return Math.round(ms / s) + 's';
913
+ return ms + 'ms';
369
914
  }
370
- process.nextTick = function (fun) {
371
- queue.push(fun);
372
- if (!draining) {
373
- setTimeout(drainQueue, 0);
374
- }
375
- };
376
-
377
- process.title = 'browser';
378
- process.browser = true;
379
- process.env = {};
380
- process.argv = [];
381
- process.version = ''; // empty string to avoid regexp issues
382
- process.versions = {};
383
915
 
384
- function noop() {}
916
+ /**
917
+ * Long format for `ms`.
918
+ *
919
+ * @param {Number} ms
920
+ * @return {String}
921
+ * @api private
922
+ */
385
923
 
386
- process.on = noop;
387
- process.addListener = noop;
388
- process.once = noop;
389
- process.off = noop;
390
- process.removeListener = noop;
391
- process.removeAllListeners = noop;
392
- process.emit = noop;
924
+ function long(ms) {
925
+ return plural(ms, d, 'day')
926
+ || plural(ms, h, 'hour')
927
+ || plural(ms, m, 'minute')
928
+ || plural(ms, s, 'second')
929
+ || ms + ' ms';
930
+ }
393
931
 
394
- process.binding = function (name) {
395
- throw new Error('process.binding is not supported');
396
- };
932
+ /**
933
+ * Pluralization helper.
934
+ */
397
935
 
398
- // TODO(shtylman)
399
- process.cwd = function () { return '/' };
400
- process.chdir = function (dir) {
401
- throw new Error('process.chdir is not supported');
402
- };
403
- process.umask = function() { return 0; };
936
+ function plural(ms, n, name) {
937
+ if (ms < n) return;
938
+ if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name;
939
+ return Math.ceil(ms / n) + ' ' + name + 's';
940
+ }
404
941
 
405
- },{}],3:[function(require,module,exports){
942
+ },{}],8:[function(require,module,exports){
406
943
  (function (process,global){
407
944
  /*!
408
945
  * @overview es6-promise - a tiny implementation of Promises/A+.
@@ -1364,11 +1901,159 @@ process.umask = function() { return 0; };
1364
1901
  this['ES6Promise'] = es6$promise$umd$$ES6Promise;
1365
1902
  }
1366
1903
  }).call(this);
1367
- }).call(this,require(2),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
1368
- },{"2":2}],4:[function(require,module,exports){
1904
+ }).call(this,require(1),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
1905
+ },{"1":1}],9:[function(require,module,exports){
1906
+ var hasOwn = Object.prototype.hasOwnProperty;
1907
+ var toString = Object.prototype.toString;
1908
+ var undefined;
1909
+
1910
+ var isArray = require(10);
1911
+
1912
+ var isPlainObject = function isPlainObject(obj) {
1913
+ 'use strict';
1914
+ if (!obj || toString.call(obj) !== '[object Object]') {
1915
+ return false;
1916
+ }
1917
+
1918
+ var has_own_constructor = hasOwn.call(obj, 'constructor');
1919
+ var has_is_property_of_method = obj.constructor && obj.constructor.prototype && hasOwn.call(obj.constructor.prototype, 'isPrototypeOf');
1920
+ // Not own constructor property must be Object
1921
+ if (obj.constructor && !has_own_constructor && !has_is_property_of_method) {
1922
+ return false;
1923
+ }
1924
+
1925
+ // Own properties are enumerated firstly, so to speed up,
1926
+ // if last one is own, then all properties are own.
1927
+ var key;
1928
+ for (key in obj) {}
1929
+
1930
+ return key === undefined || hasOwn.call(obj, key);
1931
+ };
1932
+
1933
+ module.exports = function extend() {
1934
+ 'use strict';
1935
+ var options, name, src, copy, copyIsArray, clone,
1936
+ target = arguments[0],
1937
+ i = 1,
1938
+ length = arguments.length,
1939
+ deep = false;
1940
+
1941
+ // Handle a deep copy situation
1942
+ if (typeof target === 'boolean') {
1943
+ deep = target;
1944
+ target = arguments[1] || {};
1945
+ // skip the boolean and the target
1946
+ i = 2;
1947
+ } else if ((typeof target !== 'object' && typeof target !== 'function') || target == null) {
1948
+ target = {};
1949
+ }
1950
+
1951
+ for (; i < length; ++i) {
1952
+ options = arguments[i];
1953
+ // Only deal with non-null/undefined values
1954
+ if (options != null) {
1955
+ // Extend the base object
1956
+ for (name in options) {
1957
+ src = target[name];
1958
+ copy = options[name];
1959
+
1960
+ // Prevent never-ending loop
1961
+ if (target === copy) {
1962
+ continue;
1963
+ }
1964
+
1965
+ // Recurse if we're merging plain objects or arrays
1966
+ if (deep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) {
1967
+ if (copyIsArray) {
1968
+ copyIsArray = false;
1969
+ clone = src && isArray(src) ? src : [];
1970
+ } else {
1971
+ clone = src && isPlainObject(src) ? src : {};
1972
+ }
1973
+
1974
+ // Never move original objects, clone them
1975
+ target[name] = extend(deep, clone, copy);
1976
+
1977
+ // Don't bring in undefined values
1978
+ } else if (copy !== undefined) {
1979
+ target[name] = copy;
1980
+ }
1981
+ }
1982
+ }
1983
+ }
1984
+
1985
+ // Return the modified object
1986
+ return target;
1987
+ };
1988
+
1989
+
1990
+ },{"10":10}],10:[function(require,module,exports){
1991
+ module.exports = Array.isArray || function (arr) {
1992
+ return Object.prototype.toString.call(arr) == '[object Array]';
1993
+ };
1994
+
1995
+ },{}],11:[function(require,module,exports){
1996
+
1997
+ var hasOwn = Object.prototype.hasOwnProperty;
1998
+ var toString = Object.prototype.toString;
1999
+
2000
+ module.exports = function forEach (obj, fn, ctx) {
2001
+ if (toString.call(fn) !== '[object Function]') {
2002
+ throw new TypeError('iterator must be a function');
2003
+ }
2004
+ var l = obj.length;
2005
+ if (l === +l) {
2006
+ for (var i = 0; i < l; i++) {
2007
+ fn.call(ctx, obj[i], i, obj);
2008
+ }
2009
+ } else {
2010
+ for (var k in obj) {
2011
+ if (hasOwn.call(obj, k)) {
2012
+ fn.call(ctx, obj[k], k, obj);
2013
+ }
2014
+ }
2015
+ }
2016
+ };
2017
+
2018
+
2019
+ },{}],12:[function(require,module,exports){
2020
+ if (typeof Object.create === 'function') {
2021
+ // implementation from standard node.js 'util' module
2022
+ module.exports = function inherits(ctor, superCtor) {
2023
+ ctor.super_ = superCtor
2024
+ ctor.prototype = Object.create(superCtor.prototype, {
2025
+ constructor: {
2026
+ value: ctor,
2027
+ enumerable: false,
2028
+ writable: true,
2029
+ configurable: true
2030
+ }
2031
+ });
2032
+ };
2033
+ } else {
2034
+ // old school shim for old browsers
2035
+ module.exports = function inherits(ctor, superCtor) {
2036
+ ctor.super_ = superCtor
2037
+ var TempCtor = function () {}
2038
+ TempCtor.prototype = superCtor.prototype
2039
+ ctor.prototype = new TempCtor()
2040
+ ctor.prototype.constructor = ctor
2041
+ }
2042
+ }
2043
+
2044
+ },{}],13:[function(require,module,exports){
1369
2045
  (function (process){
1370
2046
  module.exports = AlgoliaSearch;
1371
2047
 
2048
+ // default debug activated in dev environments
2049
+ // this is triggered in package.json, using the envify transform
2050
+ if (process.env.NODE_ENV === 'development') {
2051
+ require(5).enable('algoliasearch*');
2052
+ }
2053
+
2054
+ var debug = require(5)('algoliasearch');
2055
+ var foreach = require(11);
2056
+
1372
2057
  /*
1373
2058
  * Algolia Search library initialization
1374
2059
  * https://www.algolia.com/
@@ -1380,74 +2065,80 @@ module.exports = AlgoliaSearch;
1380
2065
  * @param {string} [opts.protocol='http:'] - The protocol used to query Algolia Search API.
1381
2066
  * Set to 'https:' to force using https. Default to document.location.protocol in browsers
1382
2067
  * @param {string[]} [opts.hosts=[
1383
- * this.applicationID + '-1.algolia.' + opts.tld,
1384
- * this.applicationID + '-2.algolia.' + opts.tld,
1385
- * this.applicationID + '-3.algolia.' + opts.tld]
1386
- * ] - The hosts to use for Algolia Search API. It this your responsibility to shuffle the hosts and add a DSN host in it
1387
- * @param {string} [opts.tld='net'] - The tld to use when computing hosts default list
2068
+ * this.applicationID + '-1.algolianet.com',
2069
+ * this.applicationID + '-2.algolianet.com',
2070
+ * this.applicationID + '-3.algolianet.com']
2071
+ * ] - The hosts to use for Algolia Search API. If you provide them, you will no more benefit from our HA implementation
1388
2072
  */
1389
- function AlgoliaSearch(applicationID, apiKey, opts, _request) {
2073
+ function AlgoliaSearch(applicationID, apiKey, opts) {
2074
+ var extend = require(9);
2075
+
1390
2076
  var usage = 'Usage: algoliasearch(applicationID, apiKey, opts)';
1391
2077
 
1392
2078
  if (!applicationID) {
1393
- throw new Error('Please provide an application ID. ' + usage);
2079
+ throw new Error('algoliasearch: Please provide an application ID. ' + usage);
1394
2080
  }
1395
2081
 
1396
2082
  if (!apiKey) {
1397
- throw new Error('Please provide an API key. ' + usage);
2083
+ throw new Error('algoliasearch: Please provide an API key. ' + usage);
1398
2084
  }
1399
2085
 
1400
- opts = opts || {};
2086
+ this.applicationID = applicationID;
2087
+ this.apiKey = apiKey;
1401
2088
 
1402
- // now setting default options
1403
- // could not find a tiny module to do that, let's go manual
1404
- if (opts.timeout === undefined) {
1405
- opts.timeout = 2000;
1406
- }
2089
+ var defaultHosts = [
2090
+ this.applicationID + '-1.algolianet.com',
2091
+ this.applicationID + '-2.algolianet.com',
2092
+ this.applicationID + '-3.algolianet.com'
2093
+ ];
2094
+ this.hosts = {
2095
+ read: [],
2096
+ write: []
2097
+ };
1407
2098
 
1408
- if (opts.protocol === undefined) {
1409
- opts.protocol = document && document.location.protocol || 'http:';
1410
- }
2099
+ this.hostIndex = {
2100
+ read: 0,
2101
+ write: 0
2102
+ };
1411
2103
 
1412
- if (opts.hosts === undefined) {
1413
- opts.hosts = []; // filled later on, has dependencies
1414
- }
2104
+ opts = opts || {};
1415
2105
 
1416
- if (opts.tld === undefined) {
1417
- opts.tld = 'net';
1418
- }
2106
+ var protocol = opts.protocol || 'https:';
2107
+ var timeout = opts.timeout === undefined ? 2000 : opts.timeout;
1419
2108
 
1420
2109
  // while we advocate for colon-at-the-end values: 'http:' for `opts.protocol`
1421
2110
  // we also accept `http` and `https`. It's a common error.
1422
- if (!/:$/.test(opts.protocol)) {
1423
- opts.protocol = opts.protocol + ':';
2111
+ if (!/:$/.test(protocol)) {
2112
+ protocol = protocol + ':';
1424
2113
  }
1425
2114
 
1426
- // no hosts given, add defaults
1427
- if (opts.hosts.length === 0) {
1428
- opts.hosts = shuffle([
1429
- applicationID + '-1.algolia.' + opts.tld,
1430
- applicationID + '-2.algolia.' + opts.tld,
1431
- applicationID + '-3.algolia.' + opts.tld
1432
- ]);
1433
-
1434
- // add default dsn host
1435
- opts.hosts.unshift(applicationID + '-dsn.algolia.' + opts.tld);
2115
+ if (opts.protocol !== 'http:' && opts.protocol !== 'https:') {
2116
+ throw new Error('algoliasearch: protocol must be `http:` or `https:` (was `' + opts.protocol + '`)');
1436
2117
  }
1437
2118
 
1438
- opts.hosts = map(opts.hosts, function prependProtocol(host) {
1439
- return opts.protocol + '//' + host;
1440
- });
2119
+ // no hosts given, add defaults
2120
+ if (!opts.hosts) {
2121
+ this.hosts.read = [this.applicationID + '-dsn.algolia.net'].concat(defaultHosts);
2122
+ this.hosts.write = [this.applicationID + '.algolia.net'].concat(defaultHosts);
2123
+ } else {
2124
+ this.hosts.read = extend([], opts.hosts);
2125
+ this.hosts.write = extend([], opts.hosts);
2126
+ }
1441
2127
 
1442
- this.applicationID = applicationID;
1443
- this.apiKey = apiKey;
1444
- this.hosts = opts.hosts;
2128
+ // add protocol and lowercase hosts
2129
+ this.hosts.read = map(this.hosts.read, prepareHost(protocol));
2130
+ this.hosts.write = map(this.hosts.write, prepareHost(protocol));
2131
+ this.requestTimeout = timeout;
1445
2132
 
1446
- this.currentHostIndex = 0;
1447
- this.requestTimeout = opts.timeout;
1448
2133
  this.extraHeaders = [];
1449
2134
  this.cache = {};
1450
- this._request = _request;
2135
+
2136
+ this._ua = opts._ua;
2137
+ this._useCache = opts._useCache === undefined ? true : opts._useCache;
2138
+
2139
+ this._setTimeout = opts._setTimeout;
2140
+
2141
+ debug('init done, %j', this);
1451
2142
  }
1452
2143
 
1453
2144
  AlgoliaSearch.prototype = {
@@ -1455,20 +2146,21 @@ AlgoliaSearch.prototype = {
1455
2146
  * Delete an index
1456
2147
  *
1457
2148
  * @param indexName the name of index to delete
1458
- * @param callback the result callback with two arguments
2149
+ * @param callback the result callback called with two arguments
1459
2150
  * error: null or Error('message')
1460
2151
  * content: the server answer that contains the task ID
1461
2152
  */
1462
2153
  deleteIndex: function(indexName, callback) {
1463
2154
  return this._jsonRequest({ method: 'DELETE',
1464
2155
  url: '/1/indexes/' + encodeURIComponent(indexName),
2156
+ hostType: 'write',
1465
2157
  callback: callback });
1466
2158
  },
1467
2159
  /**
1468
2160
  * Move an existing index.
1469
2161
  * @param srcIndexName the name of index to copy.
1470
2162
  * @param dstIndexName the new index name that will contains a copy of srcIndexName (destination will be overriten if it already exist).
1471
- * @param callback the result callback with two arguments
2163
+ * @param callback the result callback called with two arguments
1472
2164
  * error: null or Error('message')
1473
2165
  * content: the server answer that contains the task ID
1474
2166
  */
@@ -1477,6 +2169,7 @@ AlgoliaSearch.prototype = {
1477
2169
  return this._jsonRequest({ method: 'POST',
1478
2170
  url: '/1/indexes/' + encodeURIComponent(srcIndexName) + '/operation',
1479
2171
  body: postObj,
2172
+ hostType: 'write',
1480
2173
  callback: callback });
1481
2174
 
1482
2175
  },
@@ -1484,7 +2177,7 @@ AlgoliaSearch.prototype = {
1484
2177
  * Copy an existing index.
1485
2178
  * @param srcIndexName the name of index to copy.
1486
2179
  * @param dstIndexName the new index name that will contains a copy of srcIndexName (destination will be overriten if it already exist).
1487
- * @param callback the result callback with two arguments
2180
+ * @param callback the result callback called with two arguments
1488
2181
  * error: null or Error('message')
1489
2182
  * content: the server answer that contains the task ID
1490
2183
  */
@@ -1493,13 +2186,14 @@ AlgoliaSearch.prototype = {
1493
2186
  return this._jsonRequest({ method: 'POST',
1494
2187
  url: '/1/indexes/' + encodeURIComponent(srcIndexName) + '/operation',
1495
2188
  body: postObj,
2189
+ hostType: 'write',
1496
2190
  callback: callback });
1497
2191
  },
1498
2192
  /**
1499
2193
  * Return last log entries.
1500
2194
  * @param offset Specify the first entry to retrieve (0-based, 0 is the most recent log entry).
1501
2195
  * @param length Specify the maximum number of entries to retrieve starting at offset. Maximum allowed value: 1000.
1502
- * @param callback the result callback with two arguments
2196
+ * @param callback the result callback called with two arguments
1503
2197
  * error: null or Error('message')
1504
2198
  * content: the server answer that contains the task ID
1505
2199
  */
@@ -1517,13 +2211,14 @@ AlgoliaSearch.prototype = {
1517
2211
 
1518
2212
  return this._jsonRequest({ method: 'GET',
1519
2213
  url: '/1/logs?offset=' + offset + '&length=' + length,
2214
+ hostType: 'read',
1520
2215
  callback: callback });
1521
2216
  },
1522
2217
  /*
1523
2218
  * List all existing indexes (paginated)
1524
2219
  *
1525
2220
  * @param page The page to retrieve, starting at 0.
1526
- * @param callback the result callback with two arguments
2221
+ * @param callback the result callback called with two arguments
1527
2222
  * error: null or Error('message')
1528
2223
  * content: the server answer with index list
1529
2224
  */
@@ -1538,6 +2233,7 @@ AlgoliaSearch.prototype = {
1538
2233
 
1539
2234
  return this._jsonRequest({ method: 'GET',
1540
2235
  url: '/1/indexes' + params,
2236
+ hostType: 'read',
1541
2237
  callback: callback });
1542
2238
  },
1543
2239
 
@@ -1553,90 +2249,142 @@ AlgoliaSearch.prototype = {
1553
2249
  /*
1554
2250
  * List all existing user keys with their associated ACLs
1555
2251
  *
1556
- * @param callback the result callback with two arguments
2252
+ * @param callback the result callback called with two arguments
1557
2253
  * error: null or Error('message')
1558
2254
  * content: the server answer with user keys list
1559
2255
  */
1560
2256
  listUserKeys: function(callback) {
1561
2257
  return this._jsonRequest({ method: 'GET',
1562
2258
  url: '/1/keys',
2259
+ hostType: 'read',
1563
2260
  callback: callback });
1564
2261
  },
1565
2262
  /*
1566
2263
  * Get ACL of a user key
1567
2264
  *
1568
2265
  * @param key
1569
- * @param callback the result callback with two arguments
2266
+ * @param callback the result callback called with two arguments
1570
2267
  * error: null or Error('message')
1571
2268
  * content: the server answer with user keys list
1572
2269
  */
1573
2270
  getUserKeyACL: function(key, callback) {
1574
2271
  return this._jsonRequest({ method: 'GET',
1575
2272
  url: '/1/keys/' + key,
2273
+ hostType: 'read',
1576
2274
  callback: callback });
1577
2275
  },
1578
2276
  /*
1579
2277
  * Delete an existing user key
1580
2278
  * @param key
1581
- * @param callback the result callback with two arguments
2279
+ * @param callback the result callback called with two arguments
1582
2280
  * error: null or Error('message')
1583
2281
  * content: the server answer with user keys list
1584
2282
  */
1585
2283
  deleteUserKey: function(key, callback) {
1586
2284
  return this._jsonRequest({ method: 'DELETE',
1587
2285
  url: '/1/keys/' + key,
2286
+ hostType: 'write',
1588
2287
  callback: callback });
1589
2288
  },
1590
2289
  /*
1591
2290
  * Add an existing user key
1592
2291
  *
1593
- * @param acls the list of ACL for this key. Defined by an array of strings that
1594
- * can contains the following values:
1595
- * - search: allow to search (https and http)
1596
- * - addObject: allows to add/update an object in the index (https only)
1597
- * - deleteObject : allows to delete an existing object (https only)
1598
- * - deleteIndex : allows to delete index content (https only)
1599
- * - settings : allows to get index settings (https only)
1600
- * - editSettings : allows to change index settings (https only)
1601
- * @param callback the result callback with two arguments
1602
- * error: null or Error('message')
1603
- * content: the server answer with user keys list
2292
+ * @param {string[]} acls - The list of ACL for this key. Defined by an array of strings that
2293
+ * can contains the following values:
2294
+ * - search: allow to search (https and http)
2295
+ * - addObject: allows to add/update an object in the index (https only)
2296
+ * - deleteObject : allows to delete an existing object (https only)
2297
+ * - deleteIndex : allows to delete index content (https only)
2298
+ * - settings : allows to get index settings (https only)
2299
+ * - editSettings : allows to change index settings (https only)
2300
+ * @param {Object} [params] - Optionnal parameters to set for the key
2301
+ * @param {number} params.validity - Number of seconds after which the key will be automatically removed (0 means no time limit for this key)
2302
+ * @param {number} params.maxQueriesPerIPPerHour - Number of API calls allowed from an IP address per hour
2303
+ * @param {number} params.maxHitsPerQuery - Number of hits this API key can retrieve in one call
2304
+ * @param {string[]} params.indexes - Allowed targeted indexes for this key
2305
+ * @param {Function} callback - The result callback called with two arguments
2306
+ * error: null or Error('message')
2307
+ * content: the server answer with user keys list
2308
+ * @return {Promise|undefined} Returns a promise if no callback given
1604
2309
  */
1605
- addUserKey: function(acls, callback) {
1606
- return this.addUserKeyWithValidity(acls, {
1607
- validity: 0,
1608
- maxQueriesPerIPPerHour: 0,
1609
- maxHitsPerQuery: 0
1610
- }, callback);
2310
+ addUserKey: function(acls, params, callback) {
2311
+ if (arguments.length === 1 || typeof params === 'function') {
2312
+ callback = params;
2313
+ params = null;
2314
+ }
2315
+
2316
+ var postObj = {
2317
+ acl: acls
2318
+ };
2319
+
2320
+ if (params) {
2321
+ postObj.validity = params.validity;
2322
+ postObj.maxQueriesPerIPPerHour = params.maxQueriesPerIPPerHour;
2323
+ postObj.maxHitsPerQuery = params.maxHitsPerQuery;
2324
+ postObj.indexes = params.indexes;
2325
+ }
2326
+
2327
+ return this._jsonRequest({
2328
+ method: 'POST',
2329
+ url: '/1/keys',
2330
+ body: postObj,
2331
+ hostType: 'write',
2332
+ callback: callback
2333
+ });
1611
2334
  },
1612
- /*
2335
+ /**
1613
2336
  * Add an existing user key
1614
- *
1615
- * @param acls the list of ACL for this key. Defined by an array of strings that
1616
- * can contains the following values:
1617
- * - search: allow to search (https and http)
1618
- * - addObject: allows to add/update an object in the index (https only)
1619
- * - deleteObject : allows to delete an existing object (https only)
1620
- * - deleteIndex : allows to delete index content (https only)
1621
- * - settings : allows to get index settings (https only)
1622
- * - editSettings : allows to change index settings (https only)
1623
- * @param params.validity the number of seconds after which the key will be automatically removed (0 means no time limit for this key)
1624
- * @param params.maxQueriesPerIPPerHour Specify the maximum number of API calls allowed from an IP address per hour.
1625
- * @param params.maxHitsPerQuery Specify the maximum number of hits this API key can retrieve in one call.
1626
- * @param callback the result callback with two arguments
1627
- * error: null or Error('message')
1628
- * content: the server answer with user keys list
2337
+ * @deprecated Please use client.addUserKey()
1629
2338
  */
1630
- addUserKeyWithValidity: function(acls, params, callback) {
1631
- var aclsObject = {};
1632
- aclsObject.acl = acls;
1633
- aclsObject.validity = params.validity;
1634
- aclsObject.maxQueriesPerIPPerHour = params.maxQueriesPerIPPerHour;
1635
- aclsObject.maxHitsPerQuery = params.maxHitsPerQuery;
1636
- return this._jsonRequest({ method: 'POST',
1637
- url: '/1/keys',
1638
- body: aclsObject,
1639
- callback: callback });
2339
+ addUserKeyWithValidity: deprecate(function(acls, params, callback) {
2340
+ return this.addUserKey(acls, params, callback);
2341
+ }, deprecatedMessage('client.addUserKeyWithValidity()', 'client.addUserKey()')),
2342
+
2343
+ /**
2344
+ * Update an existing user key
2345
+ * @param {string} key - The key to update
2346
+ * @param {string[]} acls - The list of ACL for this key. Defined by an array of strings that
2347
+ * can contains the following values:
2348
+ * - search: allow to search (https and http)
2349
+ * - addObject: allows to add/update an object in the index (https only)
2350
+ * - deleteObject : allows to delete an existing object (https only)
2351
+ * - deleteIndex : allows to delete index content (https only)
2352
+ * - settings : allows to get index settings (https only)
2353
+ * - editSettings : allows to change index settings (https only)
2354
+ * @param {Object} [params] - Optionnal parameters to set for the key
2355
+ * @param {number} params.validity - Number of seconds after which the key will be automatically removed (0 means no time limit for this key)
2356
+ * @param {number} params.maxQueriesPerIPPerHour - Number of API calls allowed from an IP address per hour
2357
+ * @param {number} params.maxHitsPerQuery - Number of hits this API key can retrieve in one call
2358
+ * @param {string[]} params.indexes - Allowed targeted indexes for this key
2359
+ * @param {Function} callback - The result callback called with two arguments
2360
+ * error: null or Error('message')
2361
+ * content: the server answer with user keys list
2362
+ * @return {Promise|undefined} Returns a promise if no callback given
2363
+ */
2364
+ updateUserKey: function(key, acls, params, callback) {
2365
+ if (arguments.length === 2 || typeof params === 'function') {
2366
+ callback = params;
2367
+ params = null;
2368
+ }
2369
+
2370
+ var putObj = {
2371
+ acl: acls
2372
+ };
2373
+
2374
+ if (params) {
2375
+ putObj.validity = params.validity;
2376
+ putObj.maxQueriesPerIPPerHour = params.maxQueriesPerIPPerHour;
2377
+ putObj.maxHitsPerQuery = params.maxHitsPerQuery;
2378
+ putObj.indexes = params.indexes;
2379
+ }
2380
+
2381
+ return this._jsonRequest({
2382
+ method: 'PUT',
2383
+ url: '/1/keys/' + key,
2384
+ body: putObj,
2385
+ hostType: 'write',
2386
+ callback: callback
2387
+ });
1640
2388
  },
1641
2389
 
1642
2390
  /**
@@ -1659,7 +2407,8 @@ AlgoliaSearch.prototype = {
1659
2407
  }
1660
2408
  tags = strTags.join(',');
1661
2409
  }
1662
- this.tagFilters = tags;
2410
+
2411
+ this.securityTags = tags;
1663
2412
  },
1664
2413
 
1665
2414
  /**
@@ -1670,59 +2419,41 @@ AlgoliaSearch.prototype = {
1670
2419
  this.userToken = userToken;
1671
2420
  },
1672
2421
 
1673
- /*
2422
+ /**
1674
2423
  * Initialize a new batch of search queries
2424
+ * @deprecated use client.search()
1675
2425
  */
1676
- startQueriesBatch: function() {
1677
- this.batch = [];
1678
- },
1679
- /*
2426
+ startQueriesBatch: deprecate(function() {
2427
+ this._batch = [];
2428
+ }, deprecatedMessage('client.startQueriesBatch()', 'client.search()')),
2429
+
2430
+ /**
1680
2431
  * Add a search query in the batch
1681
- *
1682
- * @param query the full text query
1683
- * @param args (optional) if set, contains an object with query parameters:
1684
- * - attributes: an array of object attribute names to retrieve
1685
- * (if not set all attributes are retrieve)
1686
- * - attributesToHighlight: an array of object attribute names to highlight
1687
- * (if not set indexed attributes are highlighted)
1688
- * - minWordSizefor1Typo: the minimum number of characters to accept one typo.
1689
- * Defaults to 3.
1690
- * - minWordSizefor2Typos: the minimum number of characters to accept two typos.
1691
- * Defaults to 7.
1692
- * - getRankingInfo: if set, the result hits will contain ranking information in
1693
- * _rankingInfo attribute
1694
- * - page: (pagination parameter) page to retrieve (zero base). Defaults to 0.
1695
- * - hitsPerPage: (pagination parameter) number of hits per page. Defaults to 10.
2432
+ * @deprecated use client.search()
1696
2433
  */
1697
- addQueryInBatch: function(indexName, query, args) {
1698
- var params = 'query=' + encodeURIComponent(query);
1699
- if (!this._isUndefined(args) && args !== null) {
1700
- params = this._getSearchParams(args, params);
1701
- }
1702
- this.batch.push({ indexName: indexName, params: params });
1703
- },
1704
- /*
1705
- * Clear all queries in cache
2434
+ addQueryInBatch: deprecate(function(indexName, query, args) {
2435
+ this._batch.push({
2436
+ indexName: indexName,
2437
+ query: query,
2438
+ params: args
2439
+ });
2440
+ }, deprecatedMessage('client.addQueryInBatch()', 'client.search()')),
2441
+
2442
+ /**
2443
+ * Clear all queries in client's cache
2444
+ * @return undefined
1706
2445
  */
1707
2446
  clearCache: function() {
1708
2447
  this.cache = {};
1709
2448
  },
1710
- /*
2449
+
2450
+ /**
1711
2451
  * Launch the batch of queries using XMLHttpRequest.
1712
- * (Optimized for browser using a POST query to minimize number of OPTIONS queries)
1713
- *
1714
- * @param callback the function that will receive results
2452
+ * @deprecated use client.search()
1715
2453
  */
1716
- sendQueriesBatch: function(callback) {
1717
- var as = this;
1718
- var params = {requests: []};
1719
-
1720
- for (var i = 0; i < as.batch.length; ++i) {
1721
- params.requests.push(as.batch[i]);
1722
- }
1723
-
1724
- return this._sendQueriesBatch(params, callback);
1725
- },
2454
+ sendQueriesBatch: deprecate(function(callback) {
2455
+ return this.search(this._batch, callback);
2456
+ }, deprecatedMessage('client.sendQueriesBatch()', 'client.search()')),
1726
2457
 
1727
2458
  /**
1728
2459
  * Set the number of milliseconds a request can take before automatically being terminated.
@@ -1735,6 +2466,53 @@ AlgoliaSearch.prototype = {
1735
2466
  }
1736
2467
  },
1737
2468
 
2469
+ /**
2470
+ * Search through multiple indices at the same time
2471
+ * @param {Object[]} queries An array of queries you want to run.
2472
+ * @param {string} queries[].indexName The index name you want to target
2473
+ * @param {string} [queries[].query] The query to issue on this index. Can also be passed into `params`
2474
+ * @param {Object} queries[].params Any search param like hitsPerPage, ..
2475
+ * @param {Function} callback Callback to be called
2476
+ * @return {Promise|undefined} Returns a promise if no callback given
2477
+ */
2478
+ search: function(queries, callback) {
2479
+ var client = this;
2480
+
2481
+ var postObj = {
2482
+ requests: map(queries, function prepareRequest(query) {
2483
+ var params = '';
2484
+
2485
+ // allow query.query
2486
+ // so we are mimicing the index.search(query, params) method
2487
+ // {indexName:, query:, params:}
2488
+ if (query.query !== undefined) {
2489
+ params += 'query=' + encodeURIComponent(query.query)
2490
+ }
2491
+
2492
+ return {
2493
+ indexName: query.indexName,
2494
+ params: client._getSearchParams(query.params, params)
2495
+ };
2496
+ })
2497
+ };
2498
+
2499
+ return this._jsonRequest({
2500
+ cache: this.cache,
2501
+ method: 'POST',
2502
+ url: '/1/indexes/*/queries',
2503
+ body: postObj,
2504
+ hostType: 'read',
2505
+ callback: callback
2506
+ });
2507
+ },
2508
+
2509
+ // environment specific methods
2510
+ destroy: notImplemented,
2511
+ enableRateLimitForward: notImplemented,
2512
+ disableRateLimitForward: notImplemented,
2513
+ useSecuredAPIKey: notImplemented,
2514
+ disableSecuredAPIKey: notImplemented,
2515
+ generateSecuredApiKey: notImplemented,
1738
2516
  /*
1739
2517
  * Index class constructor.
1740
2518
  * You should not use this method directly but use initIndex() function
@@ -1751,11 +2529,11 @@ AlgoliaSearch.prototype = {
1751
2529
  /**
1752
2530
  * Add an extra field to the HTTP request
1753
2531
  *
1754
- * @param key the header field name
2532
+ * @param name the header field name
1755
2533
  * @param value the header field value
1756
2534
  */
1757
- setExtraHeader: function(key, value) {
1758
- this.extraHeaders.push({ key: key, value: value});
2535
+ setExtraHeader: function(name, value) {
2536
+ this.extraHeaders.push({ name: name.toLowerCase(), value: value});
1759
2537
  },
1760
2538
 
1761
2539
  _sendQueriesBatch: function(params, callback) {
@@ -1763,6 +2541,7 @@ AlgoliaSearch.prototype = {
1763
2541
  method: 'POST',
1764
2542
  url: '/1/indexes/*/queries',
1765
2543
  body: params,
2544
+ hostType: 'read',
1766
2545
  fallback: {
1767
2546
  method: 'GET',
1768
2547
  url: '/1/indexes/*',
@@ -1782,77 +2561,88 @@ AlgoliaSearch.prototype = {
1782
2561
  * Wrapper that try all hosts to maximize the quality of service
1783
2562
  */
1784
2563
  _jsonRequest: function(opts) {
1785
- // handle opts.fallback, automatically use fallback (JSONP in browser plugins, wrapped with $plugin-promises)
1786
- // so if an error occurs and max tries => use fallback
1787
- // set tries to 0 again
1788
- // if fallback used and no more tries, return error
1789
- // fallback parameters are in opts.fallback
1790
- // call request.fallback or request accordingly, same promise chain otherwise
1791
- // put callback& params in front if problem
2564
+ var requestDebug = require(5)('algoliasearch:' + opts.url);
2565
+
2566
+ var body;
1792
2567
  var cache = opts.cache;
1793
- var cacheID = opts.url;
1794
2568
  var client = this;
1795
2569
  var tries = 0;
2570
+ var usingFallback = false;
1796
2571
 
1797
- // as we use POST requests to pass parameters (like query='aa'),
1798
- // the cacheID must be different between calls
1799
2572
  if (opts.body !== undefined) {
1800
- cacheID += '_body_' + JSON.stringify(opts.body);
2573
+ body = JSON.stringify(opts.body);
1801
2574
  }
1802
2575
 
2576
+ requestDebug('request start');
2577
+
1803
2578
  function doRequest(requester, reqOpts) {
2579
+ var cacheID;
2580
+
2581
+ if (client._useCache) {
2582
+ cacheID = opts.url;
2583
+ }
2584
+
2585
+ // as we sometime use POST requests to pass parameters (like query='aa'),
2586
+ // the cacheID must also include the body to be different between calls
2587
+ if (client._useCache && body) {
2588
+ cacheID += '_body_' + reqOpts.body;
2589
+ }
2590
+
1804
2591
  // handle cache existence
1805
- if (cache && cache[cacheID] !== undefined) {
1806
- return client._request.resolve(cache[cacheID]);
2592
+ if (client._useCache && cache && cache[cacheID] !== undefined) {
2593
+ requestDebug('serving response from cache');
2594
+ return client._promise.resolve(cache[cacheID]);
1807
2595
  }
1808
2596
 
1809
- if (tries >= client.hosts.length) {
1810
- if (!opts.fallback || requester === client._request.fallback) {
2597
+ if (tries >= client.hosts[opts.hostType].length) {
2598
+ if (!opts.fallback || !client._request.fallback || usingFallback) {
1811
2599
  // could not get a response even using the fallback if one was available
1812
- return client._request.reject(new Error(
2600
+ return client._promise.reject(new Error(
1813
2601
  'Cannot connect to the AlgoliaSearch API.' +
1814
2602
  ' Send an email to support@algolia.com to report and resolve the issue.'
1815
2603
  ));
1816
2604
  }
1817
2605
 
2606
+ // let's try the fallback starting from here
1818
2607
  tries = 0;
2608
+
2609
+ // method, url and body are fallback dependent
1819
2610
  reqOpts.method = opts.fallback.method;
1820
2611
  reqOpts.url = opts.fallback.url;
1821
- reqOpts.body = opts.fallback.body;
2612
+ reqOpts.jsonBody = opts.fallback.body;
2613
+ if (reqOpts.jsonBody) {
2614
+ reqOpts.body = JSON.stringify(opts.fallback.body);
2615
+ }
2616
+
1822
2617
  reqOpts.timeout = client.requestTimeout * (tries + 1);
1823
- client.currentHostIndex = 0;
1824
- client.forceFallback = true;
2618
+ client.hostIndex[opts.hostType] = 0;
2619
+ client.useFallback = true; // now we will only use JSONP, even on future requests
2620
+ usingFallback = true; // the current request is now using fallback
1825
2621
  return doRequest(client._request.fallback, reqOpts);
1826
2622
  }
1827
2623
 
1828
- var url = reqOpts.url;
1829
-
1830
- url += (url.indexOf('?') === -1 ? '?' : '&') + 'X-Algolia-API-Key=' + client.apiKey;
1831
- url += '&X-Algolia-Application-Id=' + client.applicationID;
1832
-
1833
- if (client.userToken) {
1834
- url += '&X-Algolia-UserToken=' + encodeURIComponent(client.userToken);
1835
- }
1836
-
1837
- if (client.tagFilters) {
1838
- url += '&X-Algolia-TagFilters=' + encodeURIComponent(client.tagFilters);
1839
- }
1840
-
1841
- for (var i = 0; i < client.extraHeaders.length; ++i) {
1842
- url += '&' + client.extraHeaders[i].key + '=' + client.extraHeaders[i].value;
1843
- }
1844
-
1845
- return requester(client.hosts[client.currentHostIndex] + url, {
1846
- body: reqOpts.body,
1847
- method: reqOpts.method,
1848
- timeout: reqOpts.timeout
1849
- })
2624
+ // `requester` is any of this._request or this._request.fallback
2625
+ // thus it needs to be called using the client as context
2626
+ return requester.call(client,
2627
+ // http(s)://currenthost/url(?qs)
2628
+ client.hosts[opts.hostType][client.hostIndex[opts.hostType]] + reqOpts.url, {
2629
+ body: body,
2630
+ jsonBody: opts.body,
2631
+ method: reqOpts.method,
2632
+ headers: client._computeRequestHeaders(),
2633
+ timeout: reqOpts.timeout,
2634
+ debug: requestDebug
2635
+ }
2636
+ )
1850
2637
  .then(function success(httpResponse) {
1851
2638
  // timeout case, retry immediately
1852
2639
  if (httpResponse instanceof Error) {
2640
+ requestDebug('error: %s', httpResponse.message);
1853
2641
  return retryRequest();
1854
2642
  }
1855
2643
 
2644
+ requestDebug('received response: %j', httpResponse);
2645
+
1856
2646
  var status =
1857
2647
  // When in browser mode, using XDR or JSONP
1858
2648
  // We rely on our own API response `status`, only
@@ -1874,7 +2664,7 @@ AlgoliaSearch.prototype = {
1874
2664
  var ok = status === 200 || status === 201;
1875
2665
  var retry = !ok && Math.floor(status / 100) !== 4 && Math.floor(status / 100) !== 1;
1876
2666
 
1877
- if (ok && cache) {
2667
+ if (client._useCache && ok && cache) {
1878
2668
  cache[cacheID] = httpResponse.body;
1879
2669
  }
1880
2670
 
@@ -1890,24 +2680,38 @@ AlgoliaSearch.prototype = {
1890
2680
  httpResponse.body && httpResponse.body.message || 'Unknown error'
1891
2681
  );
1892
2682
 
1893
- return client._request.reject(unrecoverableError);
2683
+ return client._promise.reject(unrecoverableError);
1894
2684
  }, tryFallback);
1895
2685
 
1896
2686
  function retryRequest() {
1897
- client.currentHostIndex = ++client.currentHostIndex % client.hosts.length;
2687
+ client.hostIndex[opts.hostType] = ++client.hostIndex[opts.hostType] % client.hosts[opts.hostType].length;
1898
2688
  tries += 1;
1899
2689
  reqOpts.timeout = client.requestTimeout * (tries + 1);
1900
2690
  return doRequest(requester, reqOpts);
1901
2691
  }
1902
2692
 
1903
- function tryFallback() {
2693
+ function tryFallback(err) {
2694
+ // error cases:
2695
+ // While not in fallback mode:
2696
+ // - CORS not supported
2697
+ // - network error
2698
+ // While in fallback mode:
2699
+ // - timeout
2700
+ // - network error
2701
+ // - badly formatted JSONP (script loaded, did not call our callback)
2702
+ // In both cases:
2703
+ // - uncaught exception occurs (TypeError)
2704
+ requestDebug('error: %s, stack: %s', err.message, err.stack);
2705
+
2706
+ // we were not using the fallback, try now
1904
2707
  // if we are switching to fallback right now, set tries to maximum
1905
- if (!client.forceFallback) {
1906
- // next time doRequest is called, simulate we tried all hosts
1907
- tries = client.hosts.length;
2708
+ if (!client.useFallback) {
2709
+ // next time doRequest is called, simulate we tried all hosts,
2710
+ // this will force to use the fallback
2711
+ tries = client.hosts[opts.hostType].length;
1908
2712
  } else {
1909
2713
  // we were already using the fallback, but something went wrong (script error)
1910
- client.currentHostIndex = ++client.currentHostIndex % client.hosts.length;
2714
+ client.hostIndex[opts.hostType] = ++client.hostIndex[opts.hostType] % client.hosts[opts.hostType].length;
1911
2715
  tries += 1;
1912
2716
  }
1913
2717
 
@@ -1916,14 +2720,16 @@ AlgoliaSearch.prototype = {
1916
2720
  }
1917
2721
 
1918
2722
  // we can use a fallback if forced AND fallback parameters are available
1919
- var useFallback = client.forceFallback && opts.fallback;
2723
+ var useFallback = client.useFallback && opts.fallback;
1920
2724
  var requestOptions = useFallback ? opts.fallback : opts;
1921
2725
 
1922
2726
  var promise = doRequest(
2727
+ // set the requester
1923
2728
  useFallback ? client._request.fallback : client._request, {
1924
2729
  url: requestOptions.url,
1925
2730
  method: requestOptions.method,
1926
- body: requestOptions.body,
2731
+ body: body,
2732
+ jsonBody: opts.body,
1927
2733
  timeout: client.requestTimeout * (tries + 1)
1928
2734
  }
1929
2735
  );
@@ -1932,13 +2738,13 @@ AlgoliaSearch.prototype = {
1932
2738
  // either we are using promises
1933
2739
  if (opts.callback) {
1934
2740
  promise.then(function okCb(content) {
1935
- process.nextTick(function() {
2741
+ exitPromise(function() {
1936
2742
  opts.callback(null, content);
1937
- });
2743
+ }, client._setTimeout || setTimeout);
1938
2744
  }, function nookCb(err) {
1939
- process.nextTick(function() {
2745
+ exitPromise(function() {
1940
2746
  opts.callback(err);
1941
- });
2747
+ }, client._setTimeout || setTimeout);
1942
2748
  });
1943
2749
  } else {
1944
2750
  return promise;
@@ -1960,8 +2766,33 @@ AlgoliaSearch.prototype = {
1960
2766
  }
1961
2767
  return params;
1962
2768
  },
2769
+
1963
2770
  _isUndefined: function(obj) {
1964
2771
  return obj === void 0;
2772
+ },
2773
+
2774
+ _computeRequestHeaders: function() {
2775
+ var requestHeaders = {
2776
+ 'x-algolia-api-key': this.apiKey,
2777
+ 'x-algolia-application-id': this.applicationID,
2778
+ 'x-user-agent': this._ua
2779
+ };
2780
+
2781
+ if (this.userToken) {
2782
+ requestHeaders['x-algolia-usertoken'] = this.userToken;
2783
+ }
2784
+
2785
+ if (this.securityTags) {
2786
+ requestHeaders['x-algolia-tagfilters'] = this.securityTags;
2787
+ }
2788
+
2789
+ if (this.extraHeaders) {
2790
+ foreach(this.extraHeaders, function addToRequestHeaders(header) {
2791
+ requestHeaders[header.name] = header.value;
2792
+ });
2793
+ }
2794
+
2795
+ return requestHeaders;
1965
2796
  }
1966
2797
  };
1967
2798
 
@@ -1982,7 +2813,7 @@ AlgoliaSearch.prototype.Index.prototype = {
1982
2813
  * @param content contains the javascript object to add inside the index
1983
2814
  * @param objectID (optional) an objectID you want to attribute to this object
1984
2815
  * (if the attribute already exist the old object will be overwrite)
1985
- * @param callback (optional) the result callback with two arguments:
2816
+ * @param callback (optional) the result callback called with two arguments:
1986
2817
  * error: null or Error('message')
1987
2818
  * content: the server answer that contains 3 elements: createAt, taskId and objectID
1988
2819
  */
@@ -2001,6 +2832,7 @@ AlgoliaSearch.prototype.Index.prototype = {
2001
2832
  url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + // create
2002
2833
  (objectID !== undefined ? '/' + encodeURIComponent(objectID) : ''), // update or create
2003
2834
  body: content,
2835
+ hostType: 'write',
2004
2836
  callback: callback
2005
2837
  });
2006
2838
  },
@@ -2008,7 +2840,7 @@ AlgoliaSearch.prototype.Index.prototype = {
2008
2840
  * Add several objects
2009
2841
  *
2010
2842
  * @param objects contains an array of objects to add
2011
- * @param callback (optional) the result callback with two arguments:
2843
+ * @param callback (optional) the result callback called with two arguments:
2012
2844
  * error: null or Error('message')
2013
2845
  * content: the server answer that updateAt and taskID
2014
2846
  */
@@ -2021,16 +2853,17 @@ AlgoliaSearch.prototype.Index.prototype = {
2021
2853
  postObj.requests.push(request);
2022
2854
  }
2023
2855
  return this.as._jsonRequest({ method: 'POST',
2024
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
2025
- body: postObj,
2026
- callback: callback });
2856
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
2857
+ body: postObj,
2858
+ hostType: 'write',
2859
+ callback: callback });
2027
2860
  },
2028
2861
  /*
2029
2862
  * Get an object from this index
2030
2863
  *
2031
2864
  * @param objectID the unique identifier of the object to retrieve
2032
2865
  * @param attrs (optional) if set, contains the array of attribute names to retrieve
2033
- * @param callback (optional) the result callback with two arguments
2866
+ * @param callback (optional) the result callback called with two arguments
2034
2867
  * error: null or Error('message')
2035
2868
  * content: the object to retrieve or the error message if a failure occured
2036
2869
  */
@@ -2056,6 +2889,33 @@ AlgoliaSearch.prototype.Index.prototype = {
2056
2889
  return this.as._jsonRequest({
2057
2890
  method: 'GET',
2058
2891
  url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(objectID) + params,
2892
+ hostType: 'read',
2893
+ callback: callback
2894
+ });
2895
+ },
2896
+
2897
+ /*
2898
+ * Get several objects from this index
2899
+ *
2900
+ * @param objectIDs the array of unique identifier of objects to retrieve
2901
+ */
2902
+ getObjects: function(objectIDs, callback) {
2903
+ var indexObj = this;
2904
+
2905
+ var body = {
2906
+ requests: map(objectIDs, function prepareRequest(objectID) {
2907
+ return {
2908
+ 'indexName': indexObj.indexName,
2909
+ 'objectID': objectID
2910
+ };
2911
+ })
2912
+ };
2913
+
2914
+ return this.as._jsonRequest({
2915
+ method: 'POST',
2916
+ url: '/1/indexes/*/objects',
2917
+ hostType: 'read',
2918
+ body: body,
2059
2919
  callback: callback
2060
2920
  });
2061
2921
  },
@@ -2065,22 +2925,23 @@ AlgoliaSearch.prototype.Index.prototype = {
2065
2925
  *
2066
2926
  * @param partialObject contains the javascript attributes to override, the
2067
2927
  * object must contains an objectID attribute
2068
- * @param callback (optional) the result callback with two arguments:
2928
+ * @param callback (optional) the result callback called with two arguments:
2069
2929
  * error: null or Error('message')
2070
2930
  * content: the server answer that contains 3 elements: createAt, taskId and objectID
2071
2931
  */
2072
2932
  partialUpdateObject: function(partialObject, callback) {
2073
2933
  var indexObj = this;
2074
2934
  return this.as._jsonRequest({ method: 'POST',
2075
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(partialObject.objectID) + '/partial',
2076
- body: partialObject,
2077
- callback: callback });
2935
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(partialObject.objectID) + '/partial',
2936
+ body: partialObject,
2937
+ hostType: 'write',
2938
+ callback: callback });
2078
2939
  },
2079
2940
  /*
2080
2941
  * Partially Override the content of several objects
2081
2942
  *
2082
2943
  * @param objects contains an array of objects to update (each object must contains a objectID attribute)
2083
- * @param callback (optional) the result callback with two arguments:
2944
+ * @param callback (optional) the result callback called with two arguments:
2084
2945
  * error: null or Error('message')
2085
2946
  * content: the server answer that updateAt and taskID
2086
2947
  */
@@ -2094,30 +2955,32 @@ AlgoliaSearch.prototype.Index.prototype = {
2094
2955
  postObj.requests.push(request);
2095
2956
  }
2096
2957
  return this.as._jsonRequest({ method: 'POST',
2097
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
2098
- body: postObj,
2099
- callback: callback });
2958
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
2959
+ body: postObj,
2960
+ hostType: 'write',
2961
+ callback: callback });
2100
2962
  },
2101
2963
  /*
2102
2964
  * Override the content of object
2103
2965
  *
2104
2966
  * @param object contains the javascript object to save, the object must contains an objectID attribute
2105
- * @param callback (optional) the result callback with two arguments:
2967
+ * @param callback (optional) the result callback called with two arguments:
2106
2968
  * error: null or Error('message')
2107
2969
  * content: the server answer that updateAt and taskID
2108
2970
  */
2109
2971
  saveObject: function(object, callback) {
2110
2972
  var indexObj = this;
2111
2973
  return this.as._jsonRequest({ method: 'PUT',
2112
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(object.objectID),
2113
- body: object,
2114
- callback: callback });
2974
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(object.objectID),
2975
+ body: object,
2976
+ hostType: 'write',
2977
+ callback: callback });
2115
2978
  },
2116
2979
  /*
2117
2980
  * Override the content of several objects
2118
2981
  *
2119
2982
  * @param objects contains an array of objects to update (each object must contains a objectID attribute)
2120
- * @param callback (optional) the result callback with two arguments:
2983
+ * @param callback (optional) the result callback called with two arguments:
2121
2984
  * error: null or Error('message')
2122
2985
  * content: the server answer that updateAt and taskID
2123
2986
  */
@@ -2131,15 +2994,16 @@ AlgoliaSearch.prototype.Index.prototype = {
2131
2994
  postObj.requests.push(request);
2132
2995
  }
2133
2996
  return this.as._jsonRequest({ method: 'POST',
2134
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
2135
- body: postObj,
2136
- callback: callback });
2997
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
2998
+ body: postObj,
2999
+ hostType: 'write',
3000
+ callback: callback });
2137
3001
  },
2138
3002
  /*
2139
3003
  * Delete an object from the index
2140
3004
  *
2141
3005
  * @param objectID the unique identifier of object to delete
2142
- * @param callback (optional) the result callback with two arguments:
3006
+ * @param callback (optional) the result callback called with two arguments:
2143
3007
  * error: null or Error('message')
2144
3008
  * content: the server answer that contains 3 elements: createAt, taskId and objectID
2145
3009
  */
@@ -2151,13 +3015,118 @@ AlgoliaSearch.prototype.Index.prototype = {
2151
3015
  return callback(err);
2152
3016
  }
2153
3017
 
2154
- return this.as._request.reject(err);
3018
+ return this.as._promise.reject(err);
2155
3019
  }
2156
3020
 
2157
3021
  var indexObj = this;
2158
3022
  return this.as._jsonRequest({ method: 'DELETE',
2159
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(objectID),
2160
- callback: callback });
3023
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(objectID),
3024
+ hostType: 'write',
3025
+ callback: callback });
3026
+ },
3027
+ /*
3028
+ * Delete several objects from an index
3029
+ *
3030
+ * @param objectIDs contains an array of objectID to delete
3031
+ * @param callback (optional) the result callback called with two arguments:
3032
+ * error: null or Error('message')
3033
+ * content: the server answer that contains 3 elements: createAt, taskId and objectID
3034
+ */
3035
+ deleteObjects: function(objectIDs, callback) {
3036
+ var indexObj = this;
3037
+ var postObj = {
3038
+ requests: map(objectIDs, function prepareRequest(objectID) {
3039
+ return {
3040
+ action: 'deleteObject',
3041
+ objectID: objectID,
3042
+ body: {
3043
+ objectID: objectID
3044
+ }
3045
+ };
3046
+ })
3047
+ };
3048
+
3049
+ return this.as._jsonRequest({
3050
+ method: 'POST',
3051
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
3052
+ body: postObj,
3053
+ hostType: 'write',
3054
+ callback: callback
3055
+ });
3056
+ },
3057
+ /*
3058
+ * Delete all objects matching a query
3059
+ *
3060
+ * @param query the query string
3061
+ * @param params the optional query parameters
3062
+ * @param callback (optional) the result callback called with one argument
3063
+ * error: null or Error('message')
3064
+ */
3065
+ deleteByQuery: function(query, params, callback) {
3066
+ var indexObj = this;
3067
+ var client = indexObj.as;
3068
+
3069
+ if (arguments.length === 1 || typeof params === 'function') {
3070
+ callback = params;
3071
+ params = {};
3072
+ }
3073
+
3074
+ params.attributesToRetrieve = 'objectID';
3075
+ params.hitsPerPage = 1000;
3076
+
3077
+ // when deleting, we should never use cache to get the
3078
+ // search results
3079
+ this.clearCache();
3080
+
3081
+ // there's a problem in how we use the promise chain,
3082
+ // see how waitTask is done
3083
+ var promise = this
3084
+ .search(query, params)
3085
+ .then(stopOrDelete);
3086
+
3087
+ function stopOrDelete(searchContent) {
3088
+ // stop here
3089
+ if (searchContent.nbHits === 0) {
3090
+ // return indexObj.as._request.resolve();
3091
+ return searchContent;
3092
+ }
3093
+
3094
+ // continue and do a recursive call
3095
+ var objectIDs = map(searchContent.hits, function getObjectID(object) {
3096
+ return object.objectID;
3097
+ });
3098
+
3099
+ return indexObj
3100
+ .deleteObjects(objectIDs)
3101
+ .then(waitTask)
3102
+ .then(deleteByQuery);
3103
+ }
3104
+
3105
+ function waitTask(deleteObjectsContent) {
3106
+ return indexObj.waitTask(deleteObjectsContent.taskID);
3107
+ }
3108
+
3109
+ function deleteByQuery() {
3110
+ return indexObj.deleteByQuery(query, params);
3111
+ }
3112
+
3113
+ if (!callback) {
3114
+ return promise;
3115
+ }
3116
+
3117
+ promise.then(success, failure);
3118
+
3119
+ function success() {
3120
+ exitPromise(function() {
3121
+ callback(null);
3122
+ }, client._setTimeout || setTimeout);
3123
+ }
3124
+
3125
+ function failure(err) {
3126
+ exitPromise(function() {
3127
+ callback(err);
3128
+ }, client._setTimeout || setTimeout);
3129
+ }
2161
3130
  },
2162
3131
  /*
2163
3132
  * Search inside the index using XMLHttpRequest request (Using a POST query to
@@ -2223,11 +3192,19 @@ AlgoliaSearch.prototype.Index.prototype = {
2223
3192
  * one is kept and others are removed.
2224
3193
  * - restrictSearchableAttributes: List of attributes you want to use for textual search (must be a subset of the attributesToIndex index setting)
2225
3194
  * either comma separated or as an array
2226
- * @param callback the result callback with two arguments:
3195
+ * @param callback the result callback called with two arguments:
2227
3196
  * error: null or Error('message'). If false, the content contains the error.
2228
3197
  * content: the server answer that contains the list of results.
2229
3198
  */
2230
3199
  search: function(query, args, callback) {
3200
+ // warn V2 users on how to search
3201
+ if (typeof query === 'function' && typeof args === 'object' ||
3202
+ typeof callback === 'object') {
3203
+ // .search(query, params, cb)
3204
+ // .search(cb, params)
3205
+ throw new Error('algoliasearch: index.search usage is index.search(query, params, cb)');
3206
+ }
3207
+
2231
3208
  if (arguments.length === 0 || typeof query === 'function') {
2232
3209
  // .search(), .search(cb)
2233
3210
  callback = query;
@@ -2253,6 +3230,7 @@ AlgoliaSearch.prototype.Index.prototype = {
2253
3230
  }
2254
3231
 
2255
3232
  if (args !== undefined) {
3233
+ // `_getSearchParams` will augment params, do not be fooled by the = versus += from previous if
2256
3234
  params = this.as._getSearchParams(args, params);
2257
3235
  }
2258
3236
 
@@ -2265,7 +3243,7 @@ AlgoliaSearch.prototype.Index.prototype = {
2265
3243
  * @param page Pagination parameter used to select the page to retrieve.
2266
3244
  * Page is zero-based and defaults to 0. Thus, to retrieve the 10th page you need to set page=9
2267
3245
  * @param hitsPerPage: Pagination parameter used to select the number of hits per page. Defaults to 1000.
2268
- * @param callback the result callback with two arguments:
3246
+ * @param callback the result callback called with two arguments:
2269
3247
  * error: null or Error('message'). If false, the content contains the error.
2270
3248
  * content: the server answer that contains the list of results.
2271
3249
  */
@@ -2282,8 +3260,9 @@ AlgoliaSearch.prototype.Index.prototype = {
2282
3260
  params += '&hitsPerPage=' + hitsPerPage;
2283
3261
  }
2284
3262
  return this.as._jsonRequest({ method: 'GET',
2285
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/browse' + params,
2286
- callback: callback });
3263
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/browse' + params,
3264
+ hostType: 'read',
3265
+ callback: callback });
2287
3266
  },
2288
3267
 
2289
3268
  /*
@@ -2317,64 +3296,70 @@ AlgoliaSearch.prototype.Index.prototype = {
2317
3296
  // waitTask() must be handled differently from other methods,
2318
3297
  // it's a recursive method using a timeout
2319
3298
  var indexObj = this;
3299
+ var client = indexObj.as;
2320
3300
 
2321
3301
  var promise = this.as._jsonRequest({
2322
3302
  method: 'GET',
3303
+ hostType: 'read',
2323
3304
  url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/task/' + taskID
2324
3305
  }).then(function success(content) {
2325
3306
  if (content.status !== 'published') {
2326
- return new indexObj.as._request.delay(100).then(function() {
2327
- return indexObj.waitTask(taskID, callback);
3307
+ return indexObj.as._promise.delay(100).then(function() {
3308
+ // do not forward the callback, we want the promise
3309
+ // on next iteration
3310
+ return indexObj.waitTask(taskID);
2328
3311
  });
2329
3312
  }
2330
3313
 
2331
- if (callback) {
2332
- process.nextTick(function() {
2333
- callback(null, content);
2334
- });
2335
- } else {
2336
- return content;
2337
- }
2338
- }, function failure(err) {
2339
- if (callback) {
2340
- process.nextTick(function() {
2341
- callback(err);
2342
- });
2343
- } else {
2344
- return err;
2345
- }
3314
+ return content;
2346
3315
  });
2347
3316
 
2348
3317
  if (!callback) {
2349
3318
  return promise;
2350
3319
  }
3320
+
3321
+ promise.then(successCb, failureCb);
3322
+
3323
+ function successCb(content) {
3324
+ exitPromise(function() {
3325
+ callback(null, content);
3326
+ }, client._setTimeout || setTimeout);
3327
+ }
3328
+
3329
+ function failureCb(err) {
3330
+ exitPromise(function() {
3331
+ callback(err);
3332
+ }, client._setTimeout || setTimeout);
3333
+ }
2351
3334
  },
2352
3335
 
2353
3336
  /*
2354
3337
  * This function deletes the index content. Settings and index specific API keys are kept untouched.
2355
3338
  *
2356
- * @param callback (optional) the result callback with two arguments
3339
+ * @param callback (optional) the result callback called with two arguments
2357
3340
  * error: null or Error('message')
2358
3341
  * content: the settings object or the error message if a failure occured
2359
3342
  */
2360
3343
  clearIndex: function(callback) {
2361
3344
  var indexObj = this;
2362
3345
  return this.as._jsonRequest({ method: 'POST',
2363
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/clear',
2364
- callback: callback });
3346
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/clear',
3347
+ hostType: 'write',
3348
+ callback: callback });
2365
3349
  },
2366
3350
  /*
2367
3351
  * Get settings of this index
2368
3352
  *
2369
- * @param callback (optional) the result callback with two arguments
3353
+ * @param callback (optional) the result callback called with two arguments
2370
3354
  * error: null or Error('message')
2371
3355
  * content: the settings object or the error message if a failure occured
2372
3356
  */
2373
3357
  getSettings: function(callback) {
2374
3358
  var indexObj = this;
2375
3359
  return this.as._jsonRequest({ method: 'GET',
2376
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/settings',
2377
- callback: callback });
3360
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/settings',
3361
+ hostType: 'read',
3362
+ callback: callback });
2378
3363
  },
2379
3364
 
2380
3365
  /*
@@ -2427,57 +3412,61 @@ AlgoliaSearch.prototype.Index.prototype = {
2427
3412
  * - highlightPreTag: (string) Specify the string that is inserted before the highlighted parts in the query result (default to "<em>").
2428
3413
  * - highlightPostTag: (string) Specify the string that is inserted after the highlighted parts in the query result (default to "</em>").
2429
3414
  * - optionalWords: (array of strings) Specify a list of words that should be considered as optional when found in the query.
2430
- * @param callback (optional) the result callback with two arguments
3415
+ * @param callback (optional) the result callback called with two arguments
2431
3416
  * error: null or Error('message')
2432
3417
  * content: the server answer or the error message if a failure occured
2433
3418
  */
2434
3419
  setSettings: function(settings, callback) {
2435
3420
  var indexObj = this;
2436
3421
  return this.as._jsonRequest({ method: 'PUT',
2437
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/settings',
2438
- body: settings,
2439
- callback: callback });
3422
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/settings',
3423
+ hostType: 'write',
3424
+ body: settings,
3425
+ callback: callback });
2440
3426
  },
2441
3427
  /*
2442
3428
  * List all existing user keys associated to this index
2443
3429
  *
2444
- * @param callback the result callback with two arguments
3430
+ * @param callback the result callback called with two arguments
2445
3431
  * error: null or Error('message')
2446
3432
  * content: the server answer with user keys list
2447
3433
  */
2448
3434
  listUserKeys: function(callback) {
2449
3435
  var indexObj = this;
2450
3436
  return this.as._jsonRequest({ method: 'GET',
2451
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys',
2452
- callback: callback });
3437
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys',
3438
+ hostType: 'read',
3439
+ callback: callback });
2453
3440
  },
2454
3441
  /*
2455
3442
  * Get ACL of a user key associated to this index
2456
3443
  *
2457
3444
  * @param key
2458
- * @param callback the result callback with two arguments
3445
+ * @param callback the result callback called with two arguments
2459
3446
  * error: null or Error('message')
2460
3447
  * content: the server answer with user keys list
2461
3448
  */
2462
3449
  getUserKeyACL: function(key, callback) {
2463
3450
  var indexObj = this;
2464
3451
  return this.as._jsonRequest({ method: 'GET',
2465
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys/' + key,
2466
- callback: callback });
3452
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys/' + key,
3453
+ hostType: 'read',
3454
+ callback: callback });
2467
3455
  },
2468
3456
  /*
2469
3457
  * Delete an existing user key associated to this index
2470
3458
  *
2471
3459
  * @param key
2472
- * @param callback the result callback with two arguments
3460
+ * @param callback the result callback called with two arguments
2473
3461
  * error: null or Error('message')
2474
3462
  * content: the server answer with user keys list
2475
3463
  */
2476
3464
  deleteUserKey: function(key, callback) {
2477
3465
  var indexObj = this;
2478
3466
  return this.as._jsonRequest({ method: 'DELETE',
2479
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys/' + key,
2480
- callback: callback });
3467
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys/' + key,
3468
+ hostType: 'write',
3469
+ callback: callback });
2481
3470
  },
2482
3471
  /*
2483
3472
  * Add an existing user key associated to this index
@@ -2490,57 +3479,94 @@ AlgoliaSearch.prototype.Index.prototype = {
2490
3479
  * - deleteIndex : allows to delete index content (https only)
2491
3480
  * - settings : allows to get index settings (https only)
2492
3481
  * - editSettings : allows to change index settings (https only)
2493
- * @param callback the result callback with two arguments
3482
+ * @param callback the result callback called with two arguments
2494
3483
  * error: null or Error('message')
2495
3484
  * content: the server answer with user keys list
2496
3485
  */
2497
- addUserKey: function(acls, callback) {
2498
- var indexObj = this;
2499
- var aclsObject = {};
2500
- aclsObject.acl = acls;
2501
- return this.as._jsonRequest({ method: 'POST',
2502
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys',
2503
- body: aclsObject,
2504
- callback: callback });
3486
+ addUserKey: function(acls, params, callback) {
3487
+ if (arguments.length === 1 || typeof params === 'function') {
3488
+ callback = params;
3489
+ params = null;
3490
+ }
3491
+
3492
+ var postObj = {
3493
+ acl: acls
3494
+ };
3495
+
3496
+ if (params) {
3497
+ postObj.validity = params.validity;
3498
+ postObj.maxQueriesPerIPPerHour = params.maxQueriesPerIPPerHour;
3499
+ postObj.maxHitsPerQuery = params.maxHitsPerQuery;
3500
+ }
3501
+
3502
+ return this.as._jsonRequest({
3503
+ method: 'POST',
3504
+ url: '/1/indexes/' + encodeURIComponent(this.indexName) + '/keys',
3505
+ body: postObj,
3506
+ hostType: 'write',
3507
+ callback: callback
3508
+ });
2505
3509
  },
2506
- /*
3510
+
3511
+ /**
2507
3512
  * Add an existing user key associated to this index
2508
- *
2509
- * @param acls the list of ACL for this key. Defined by an array of strings that
2510
- * can contains the following values:
2511
- * - search: allow to search (https and http)
2512
- * - addObject: allows to add/update an object in the index (https only)
2513
- * - deleteObject : allows to delete an existing object (https only)
2514
- * - deleteIndex : allows to delete index content (https only)
2515
- * - settings : allows to get index settings (https only)
2516
- * - editSettings : allows to change index settings (https only)
2517
- * @param params.validity the number of seconds after which the key will be automatically removed (0 means no time limit for this key)
2518
- * @param params.maxQueriesPerIPPerHour Specify the maximum number of API calls allowed from an IP address per hour.
2519
- * @param params.maxHitsPerQuery Specify the maximum number of hits this API key can retrieve in one call.
2520
- * @param callback the result callback with two arguments
2521
- * error: null or Error('message')
2522
- * content: the server answer with user keys list
3513
+ * @deprecated use index.addUserKey()
2523
3514
  */
2524
- addUserKeyWithValidity: function(acls, params, callback) {
2525
- var indexObj = this;
2526
- var aclsObject = {};
2527
- aclsObject.acl = acls;
2528
- aclsObject.validity = params.validity;
2529
- aclsObject.maxQueriesPerIPPerHour = params.maxQueriesPerIPPerHour;
2530
- aclsObject.maxHitsPerQuery = params.maxHitsPerQuery;
2531
- return this.as._jsonRequest({ method: 'POST',
2532
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys',
2533
- body: aclsObject,
2534
- callback: callback });
2535
- },
2536
- ///
2537
- /// Internal methods only after this line
2538
- ///
3515
+ addUserKeyWithValidity: deprecate(function(acls, params, callback) {
3516
+ return this.addUserKey(acls, params, callback);
3517
+ }, deprecatedMessage('index.addUserKeyWithValidity()', 'index.addUserKey()')),
3518
+
3519
+ /**
3520
+ * Update an existing user key associated to this index
3521
+ * @param {string} key - The key to update
3522
+ * @param {string[]} acls - The list of ACL for this key. Defined by an array of strings that
3523
+ * can contains the following values:
3524
+ * - search: allow to search (https and http)
3525
+ * - addObject: allows to add/update an object in the index (https only)
3526
+ * - deleteObject : allows to delete an existing object (https only)
3527
+ * - deleteIndex : allows to delete index content (https only)
3528
+ * - settings : allows to get index settings (https only)
3529
+ * - editSettings : allows to change index settings (https only)
3530
+ * @param {Object} [params] - Optionnal parameters to set for the key
3531
+ * @param {number} params.validity - Number of seconds after which the key will be automatically removed (0 means no time limit for this key)
3532
+ * @param {number} params.maxQueriesPerIPPerHour - Number of API calls allowed from an IP address per hour
3533
+ * @param {number} params.maxHitsPerQuery - Number of hits this API key can retrieve in one call
3534
+ * @param {Function} callback - The result callback called with two arguments
3535
+ * error: null or Error('message')
3536
+ * content: the server answer with user keys list
3537
+ * @return {Promise|undefined} Returns a promise if no callback given
3538
+ */
3539
+ updateUserKey: function(key, acls, params, callback) {
3540
+ if (arguments.length === 2 || typeof params === 'function') {
3541
+ callback = params;
3542
+ params = null;
3543
+ }
3544
+
3545
+ var putObj = {
3546
+ acl: acls
3547
+ };
3548
+
3549
+ if (params) {
3550
+ putObj.validity = params.validity;
3551
+ putObj.maxQueriesPerIPPerHour = params.maxQueriesPerIPPerHour;
3552
+ putObj.maxHitsPerQuery = params.maxHitsPerQuery;
3553
+ }
3554
+
3555
+ return this.as._jsonRequest({
3556
+ method: 'PUT',
3557
+ url: '/1/indexes/' + encodeURIComponent(this.indexName) + '/keys/' + key,
3558
+ body: putObj,
3559
+ hostType: 'write',
3560
+ callback: callback
3561
+ });
3562
+ },
3563
+
2539
3564
  _search: function(params, callback) {
2540
3565
  return this.as._jsonRequest({ cache: this.cache,
2541
3566
  method: 'POST',
2542
3567
  url: '/1/indexes/' + encodeURIComponent(this.indexName) + '/query',
2543
3568
  body: {params: params},
3569
+ hostType: 'read',
2544
3570
  fallback: {
2545
3571
  method: 'GET',
2546
3572
  url: '/1/indexes/' + encodeURIComponent(this.indexName),
@@ -2550,7 +3576,6 @@ AlgoliaSearch.prototype.Index.prototype = {
2550
3576
  });
2551
3577
  },
2552
3578
 
2553
- // internal attributes
2554
3579
  as: null,
2555
3580
  indexName: null,
2556
3581
  typeAheadArgs: null,
@@ -2567,33 +3592,53 @@ function map(arr, fn){
2567
3592
  return ret;
2568
3593
  }
2569
3594
 
2570
- // extracted from https://github.com/coolaj86/knuth-shuffle
2571
- // not compatible with browserify
2572
- function shuffle(array) {
2573
- /*eslint-disable*/
2574
- var currentIndex = array.length
2575
- , temporaryValue
2576
- , randomIndex
2577
- ;
2578
-
2579
- // While there remain elements to shuffle...
2580
- while (0 !== currentIndex) {
2581
-
2582
- // Pick a remaining element...
2583
- randomIndex = Math.floor(Math.random() * currentIndex);
2584
- currentIndex -= 1;
2585
-
2586
- // And swap it with the current element.
2587
- temporaryValue = array[currentIndex];
2588
- array[currentIndex] = array[randomIndex];
2589
- array[randomIndex] = temporaryValue;
3595
+ function prepareHost(protocol) {
3596
+ return function prepare(host) {
3597
+ return protocol + '//' + host.toLowerCase();
3598
+ };
3599
+ }
3600
+
3601
+ function notImplemented() {
3602
+ var message = 'algoliasearch: Not implemented in this environment.\n' +
3603
+ 'If you feel this is a mistake, write to support@algolia.com';
3604
+
3605
+ throw new Error(message);
3606
+ }
3607
+
3608
+ function deprecatedMessage(previousUsage, newUsage) {
3609
+ var githubAnchorLink = previousUsage.toLowerCase()
3610
+ .replace('.', '')
3611
+ .replace('()', '');
3612
+
3613
+ return 'algoliasearch: `' + previousUsage + '` was replaced by `' +
3614
+ newUsage + '`. Please see https://github.com/algolia/algoliasearch-client-js/wiki/Deprecated#' + githubAnchorLink
3615
+ }
3616
+
3617
+ // Parse cloud does not supports setTimeout
3618
+ // We do not store a setTimeout reference in the client everytime
3619
+ // We only fallback to a fake setTimeout when not available
3620
+ // setTimeout cannot be override globally sadly
3621
+ function exitPromise(fn, _setTimeout) {
3622
+ _setTimeout(fn, 0);
3623
+ }
3624
+
3625
+ function deprecate(fn, message) {
3626
+ var warned = false;
3627
+
3628
+ function deprecated() {
3629
+ if (!warned) {
3630
+ console.log(message);
3631
+ warned = true;
3632
+ }
3633
+
3634
+ return fn.apply(this, arguments);
2590
3635
  }
2591
3636
 
2592
- return array;
3637
+ return deprecated;
2593
3638
  }
2594
3639
 
2595
- }).call(this,require(2))
2596
- },{"2":2}],5:[function(require,module,exports){
3640
+ }).call(this,require(1))
3641
+ },{"1":1,"11":11,"5":5,"9":9}],14:[function(require,module,exports){
2597
3642
  module.exports = JSONPRequest;
2598
3643
 
2599
3644
  var JSONPCounter = 0;
@@ -2604,6 +3649,8 @@ function JSONPRequest(url, opts, cb) {
2604
3649
  return;
2605
3650
  }
2606
3651
 
3652
+ opts.debug('JSONP: start');
3653
+
2607
3654
  var cbCalled = false;
2608
3655
  var timedOut = false;
2609
3656
 
@@ -2639,9 +3686,9 @@ function JSONPRequest(url, opts, cb) {
2639
3686
  // add callback by hand
2640
3687
  url += '&callback=' + cbName;
2641
3688
 
2642
- // add body params by hand
2643
- if (opts.body && opts.body.params) {
2644
- url += '&' + opts.body.params;
3689
+ // add body params manually
3690
+ if (opts.jsonBody && opts.jsonBody.params) {
3691
+ url += '&' + opts.jsonBody.params;
2645
3692
  }
2646
3693
 
2647
3694
  var ontimeout = setTimeout(timeout, opts.timeout);
@@ -2659,6 +3706,8 @@ function JSONPRequest(url, opts, cb) {
2659
3706
  head.appendChild(script);
2660
3707
 
2661
3708
  function success() {
3709
+ opts.debug('JSONP: success');
3710
+
2662
3711
  if (done || timedOut) {
2663
3712
  return;
2664
3713
  }
@@ -2667,6 +3716,7 @@ function JSONPRequest(url, opts, cb) {
2667
3716
 
2668
3717
  // script loaded but did not call the fn => script loading error
2669
3718
  if (!cbCalled) {
3719
+ opts.debug('JSONP: Fail. Script loaded but did not call the callback');
2670
3720
  clean();
2671
3721
  cb(new Error('Failed to load JSONP script'));
2672
3722
  }
@@ -2695,12 +3745,16 @@ function JSONPRequest(url, opts, cb) {
2695
3745
  }
2696
3746
 
2697
3747
  function timeout() {
3748
+ opts.debug('JSONP: Script timeout');
3749
+
2698
3750
  timedOut = true;
2699
3751
  clean();
2700
3752
  cb(new Error('Timeout - Could not connect to endpoint ' + url));
2701
3753
  }
2702
3754
 
2703
3755
  function error() {
3756
+ opts.debug('JSONP: Script error');
3757
+
2704
3758
  if (done || timedOut) {
2705
3759
  return;
2706
3760
  }
@@ -2710,23 +3764,217 @@ function JSONPRequest(url, opts, cb) {
2710
3764
  }
2711
3765
  }
2712
3766
 
2713
- },{}],6:[function(require,module,exports){
2714
- // this file is a `factory of algoliasearch()`
2715
- // Given a `request` param, it will provide you an AlgoliaSearch client
2716
- // using this particular request
2717
- module.exports = createAlgoliasearch;
3767
+ },{}],15:[function(require,module,exports){
3768
+ (function (global){
3769
+ // This is the standalone browser build entry point
3770
+ // Browser implementation of the Algolia Search JavaScript client,
3771
+ // using XMLHttpRequest, XDomainRequest and JSONP as fallback
3772
+ module.exports = algoliasearch;
3773
+
3774
+ var inherits = require(12);
3775
+ var Promise = global.Promise || require(8).Promise;
3776
+
3777
+ var AlgoliaSearch = require(13);
3778
+ var inlineHeaders = require(17);
3779
+ var JSONPRequest = require(14);
3780
+
3781
+ function algoliasearch(applicationID, apiKey, opts) {
3782
+ var extend = require(9);
3783
+
3784
+ var getDocumentProtocol = require(16);
3785
+
3786
+ opts = extend(true, {}, opts) || {};
3787
+
3788
+ if (opts.protocol === undefined) {
3789
+ opts.protocol = getDocumentProtocol();
3790
+ }
3791
+
3792
+ opts._ua = algoliasearch.ua;
3793
+
3794
+ return new AlgoliaSearchBrowser(applicationID, apiKey, opts);
3795
+ }
3796
+
3797
+ algoliasearch.version = require(18);
3798
+ algoliasearch.ua = 'Algolia for vanilla JavaScript ' + algoliasearch.version;
3799
+
3800
+ var support = {
3801
+ hasXMLHttpRequest: 'XMLHttpRequest' in window,
3802
+ hasXDomainRequest: 'XDomainRequest' in window,
3803
+ cors: 'withCredentials' in new XMLHttpRequest(),
3804
+ timeout: 'timeout' in new XMLHttpRequest()
3805
+ };
3806
+
3807
+ function AlgoliaSearchBrowser() {
3808
+ // call AlgoliaSearch constructor
3809
+ AlgoliaSearch.apply(this, arguments);
3810
+ }
3811
+
3812
+ inherits(AlgoliaSearchBrowser, AlgoliaSearch);
3813
+
3814
+ AlgoliaSearchBrowser.prototype._request = function(url, opts) {
3815
+ return new Promise(function(resolve, reject) {
3816
+ // no cors or XDomainRequest, no request
3817
+ if (!support.cors && !support.hasXDomainRequest) {
3818
+ // very old browser, not supported
3819
+ reject(new Error('CORS not supported'));
3820
+ return;
3821
+ }
3822
+
3823
+ url = inlineHeaders(url, opts.headers);
3824
+
3825
+ var body = opts.body;
3826
+ var req = support.cors ? new XMLHttpRequest() : new XDomainRequest();
3827
+ var ontimeout;
3828
+ var timedOut;
3829
+
3830
+ // do not rely on default XHR async flag, as some analytics code like hotjar
3831
+ // breaks it and set it to false by default
3832
+ if (req instanceof XMLHttpRequest) {
3833
+ req.open(opts.method, url, true);
3834
+ } else {
3835
+ req.open(opts.method, url);
3836
+ }
3837
+
3838
+ if (support.cors && body && opts.method !== 'GET') {
3839
+ req.setRequestHeader('content-type', 'application/x-www-form-urlencoded');
3840
+ }
3841
+
3842
+ // we set an empty onprogress listener
3843
+ // so that XDomainRequest on IE9 is not aborted
3844
+ // refs:
3845
+ // - https://github.com/algolia/algoliasearch-client-js/issues/76
3846
+ // - https://social.msdn.microsoft.com/Forums/ie/en-US/30ef3add-767c-4436-b8a9-f1ca19b4812e/ie9-rtm-xdomainrequest-issued-requests-may-abort-if-all-event-handlers-not-specified?forum=iewebdevelopment
3847
+ req.onprogress = function noop() {};
3848
+
3849
+ req.onload = load;
3850
+ req.onerror = error;
3851
+
3852
+ if (support.timeout) {
3853
+ // .timeout supported by both XHR and XDR,
3854
+ // we do receive timeout event, tested
3855
+ req.timeout = opts.timeout;
3856
+
3857
+ req.ontimeout = timeout;
3858
+ } else {
3859
+ ontimeout = setTimeout(timeout, opts.timeout);
3860
+ }
3861
+
3862
+ req.send(body);
3863
+
3864
+ // event object not received in IE8, at least
3865
+ // but we do not use it, still important to note
3866
+ function load(/*event*/) {
3867
+ // When browser does not supports req.timeout, we can
3868
+ // have both a load and timeout event, since handled by a dumb setTimeout
3869
+ if (timedOut) {
3870
+ return;
3871
+ }
3872
+
3873
+ if (!support.timeout) {
3874
+ clearTimeout(ontimeout);
3875
+ }
3876
+
3877
+ var response = null;
3878
+
3879
+ try {
3880
+ response = JSON.parse(req.responseText);
3881
+ } catch(e) {}
3882
+
3883
+ resolve({
3884
+ body: response,
3885
+ statusCode: req.status
3886
+ });
3887
+ }
3888
+
3889
+ function error(event) {
3890
+ if (timedOut) {
3891
+ return;
3892
+ }
3893
+
3894
+ if (!support.timeout) {
3895
+ clearTimeout(ontimeout);
3896
+ }
3897
+
3898
+ // error event is trigerred both with XDR/XHR on:
3899
+ // - DNS error
3900
+ // - unallowed cross domain request
3901
+ reject(new Error('Could not connect to host, error was:' + event));
3902
+ }
3903
+
3904
+ function timeout() {
3905
+ if (!support.timeout) {
3906
+ timedOut = true;
3907
+ req.abort();
3908
+ }
3909
+
3910
+ resolve(new Error('Timeout - Could not connect to endpoint ' + url));
3911
+ }
3912
+
3913
+ });
3914
+ };
3915
+
3916
+ AlgoliaSearchBrowser.prototype._request.fallback = function(url, opts) {
3917
+ url = inlineHeaders(url, opts.headers);
3918
+
3919
+ return new Promise(function(resolve, reject) {
3920
+ JSONPRequest(url, opts, function JSONPRequestDone(err, content) {
3921
+ if (err) {
3922
+ reject(err);
3923
+ return;
3924
+ }
3925
+
3926
+ resolve(content);
3927
+ });
3928
+ });
3929
+ };
3930
+
3931
+ AlgoliaSearchBrowser.prototype._promise = {
3932
+ reject: function(val) {
3933
+ return Promise.reject(val);
3934
+ },
3935
+ resolve: function(val) {
3936
+ return Promise.resolve(val);
3937
+ },
3938
+ delay: function(ms) {
3939
+ return new Promise(function(resolve/*, reject*/) {
3940
+ setTimeout(resolve, ms);
3941
+ });
3942
+ }
3943
+ };
3944
+
3945
+ }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
3946
+ },{"12":12,"13":13,"14":14,"16":16,"17":17,"18":18,"8":8,"9":9}],16:[function(require,module,exports){
3947
+ (function (global){
3948
+ module.exports = getDocumentProtocol;
2718
3949
 
2719
- function createAlgoliasearch(request) {
2720
- function algoliasearch(applicationID, apiKey, opts) {
2721
- var AlgoliaSearch = require(4);
3950
+ function getDocumentProtocol() {
3951
+ var protocol = global.document.location.protocol;
2722
3952
 
2723
- return new AlgoliaSearch(applicationID, apiKey, opts, request);
3953
+ // when in `file:` mode (local html file), default to `http:`
3954
+ if (protocol !== 'http:' && protocol !== 'https:') {
3955
+ protocol = 'http:';
2724
3956
  }
2725
3957
 
2726
- algoliasearch.version = "3.0.3";
3958
+ return protocol;
3959
+ }
3960
+
3961
+ }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
3962
+ },{}],17:[function(require,module,exports){
3963
+ module.exports = inlineHeaders;
3964
+
3965
+ var querystring = require(4);
3966
+
3967
+ function inlineHeaders(url, headers) {
3968
+ if (/\?/.test(url)) {
3969
+ url += '&';
3970
+ } else {
3971
+ url += '?';
3972
+ }
2727
3973
 
2728
- return algoliasearch;
3974
+ return url + querystring.encode(headers);
2729
3975
  }
2730
3976
 
2731
- },{"4":4}]},{},[1])(1)
3977
+ },{"4":4}],18:[function(require,module,exports){
3978
+ module.exports="3.3.0"
3979
+ },{}]},{},[15])(15)
2732
3980
  });