algoliasearch-rails 1.11.17 → 1.11.18

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