algoliasearch-rails 1.12.0 → 1.12.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0d74f56ff72c3c4e345e66f172b6be8d29378bee
4
- data.tar.gz: 0363a42c7a6c17e842c561807b151095ef7e23f7
3
+ metadata.gz: d0687fa4b4aca95ec66bd4510bad437cbc246232
4
+ data.tar.gz: 1e37ff5b68c78600c40518ff1567b2a7678a009f
5
5
  SHA512:
6
- metadata.gz: 4196b33784fc23a9b8dbd3d66318a3c32c9bca6dfb3c132c0b6679c0182a6e6857261e6db9164c86d2133b3899b048bbedc1dde8a9fc6e99126a54e77a0fbe48
7
- data.tar.gz: 92be73f9302e3081eff020bee02410491d35523488554f6f511cf00d5c9a99f6c7712006de9a2bb287655a616b22f9d5a7d3f378d08ced3b746f78b1b58eb6ea
6
+ metadata.gz: 460f63d74615acde329a19f174527b60f4eed599ddff0eba4fd703ffb9ff751dce3cf7c8e09128452eff49ada7ee70ad8199bbf5eec3ae54168e6aed89df158a
7
+ data.tar.gz: a4c6fa6eaccc32d945c1f79d843d4f092e0f085cdd6cc38a9d447bd1c9ff533ed9dd068564afa05bf4e10ecc42fcf05114eaccfbbb4b5eff3d736a6273d58765
data/ChangeLog CHANGED
@@ -1,5 +1,9 @@
1
1
  CHANGELOG
2
2
 
3
+ 2015-05-12 1.12.1
4
+
5
+ * Upgrade to algoliasearch-client-js 3.3.0
6
+
3
7
  2015-04-29 1.12.0
4
8
 
5
9
  * Add Sequel support.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.12.0
1
+ 1.12.1
@@ -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) {
@@ -260,9 +260,1800 @@ process.chdir = function (dir) {
260
260
  process.umask = function() { return 0; };
261
261
 
262
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
+ }
292
+
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 = '';
327
+ }
328
+
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);
336
+ } else {
337
+ obj[k] = [obj[k], v];
338
+ }
339
+ }
340
+
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;
541
+ }
542
+ });
543
+
544
+ args.splice(lastC, 0, c);
545
+ return args;
546
+ }
547
+
548
+ /**
549
+ * Invokes `console.log()` when available.
550
+ * No-op when `console.log` is not a "function".
551
+ *
552
+ * @api public
553
+ */
554
+
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');
574
+ } else {
575
+ storage.debug = namespaces;
576
+ }
577
+ } catch(e) {}
578
+ }
579
+
580
+ /**
581
+ * Load `namespaces`.
582
+ *
583
+ * @return {String} returns the previously persisted debug modes
584
+ * @api private
585
+ */
586
+
587
+ function load() {
588
+ var r;
589
+ try {
590
+ r = storage.debug;
591
+ } catch(e) {}
592
+ return r;
593
+ }
594
+
595
+ /**
596
+ * Enable namespaces listed in `localStorage.debug` initially.
597
+ */
598
+
599
+ exports.enable(load());
600
+
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
+ */
611
+
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);
711
+ }
712
+
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);
723
+
724
+ // now we need to remove `args[index]` since it's inlined in the `format`
725
+ args.splice(index, 1);
726
+ index--;
727
+ }
728
+ return match;
729
+ });
730
+
731
+ if ('function' === typeof exports.formatArgs) {
732
+ args = exports.formatArgs.apply(self, args);
733
+ }
734
+ var logFn = enabled.log || exports.log || console.log.bind(console);
735
+ logFn.apply(self, args);
736
+ }
737
+ enabled.enabled = true;
738
+
739
+ var fn = exports.enabled(namespace) ? enabled : disabled;
740
+
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 + '$'));
767
+ }
768
+ }
769
+ }
770
+
771
+ /**
772
+ * Disable debug output.
773
+ *
774
+ * @api public
775
+ */
776
+
777
+ function disable() {
778
+ exports.enable('');
779
+ }
780
+
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
+ */
788
+
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
+ }
803
+
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
+ */
840
+
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);
847
+ };
848
+
849
+ /**
850
+ * Parse the given `str` and return milliseconds.
851
+ *
852
+ * @param {String} str
853
+ * @return {Number}
854
+ * @api private
855
+ */
856
+
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
+ }
899
+
900
+ /**
901
+ * Short format for `ms`.
902
+ *
903
+ * @param {Number} ms
904
+ * @return {String}
905
+ * @api private
906
+ */
907
+
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';
914
+ }
915
+
916
+ /**
917
+ * Long format for `ms`.
918
+ *
919
+ * @param {Number} ms
920
+ * @return {String}
921
+ * @api private
922
+ */
923
+
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
+ }
931
+
932
+ /**
933
+ * Pluralization helper.
934
+ */
935
+
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
+ }
941
+
942
+ },{}],8:[function(require,module,exports){
943
+ (function (process,global){
944
+ /*!
945
+ * @overview es6-promise - a tiny implementation of Promises/A+.
946
+ * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald)
947
+ * @license Licensed under MIT license
948
+ * See https://raw.githubusercontent.com/jakearchibald/es6-promise/master/LICENSE
949
+ * @version 2.0.1
950
+ */
951
+
952
+ (function() {
953
+ "use strict";
954
+
955
+ function $$utils$$objectOrFunction(x) {
956
+ return typeof x === 'function' || (typeof x === 'object' && x !== null);
957
+ }
958
+
959
+ function $$utils$$isFunction(x) {
960
+ return typeof x === 'function';
961
+ }
962
+
963
+ function $$utils$$isMaybeThenable(x) {
964
+ return typeof x === 'object' && x !== null;
965
+ }
966
+
967
+ var $$utils$$_isArray;
968
+
969
+ if (!Array.isArray) {
970
+ $$utils$$_isArray = function (x) {
971
+ return Object.prototype.toString.call(x) === '[object Array]';
972
+ };
973
+ } else {
974
+ $$utils$$_isArray = Array.isArray;
975
+ }
976
+
977
+ var $$utils$$isArray = $$utils$$_isArray;
978
+ var $$utils$$now = Date.now || function() { return new Date().getTime(); };
979
+ function $$utils$$F() { }
980
+
981
+ var $$utils$$o_create = (Object.create || function (o) {
982
+ if (arguments.length > 1) {
983
+ throw new Error('Second argument not supported');
984
+ }
985
+ if (typeof o !== 'object') {
986
+ throw new TypeError('Argument must be an object');
987
+ }
988
+ $$utils$$F.prototype = o;
989
+ return new $$utils$$F();
990
+ });
991
+
992
+ var $$asap$$len = 0;
993
+
994
+ var $$asap$$default = function asap(callback, arg) {
995
+ $$asap$$queue[$$asap$$len] = callback;
996
+ $$asap$$queue[$$asap$$len + 1] = arg;
997
+ $$asap$$len += 2;
998
+ if ($$asap$$len === 2) {
999
+ // If len is 1, that means that we need to schedule an async flush.
1000
+ // If additional callbacks are queued before the queue is flushed, they
1001
+ // will be processed by this flush that we are scheduling.
1002
+ $$asap$$scheduleFlush();
1003
+ }
1004
+ };
1005
+
1006
+ var $$asap$$browserGlobal = (typeof window !== 'undefined') ? window : {};
1007
+ var $$asap$$BrowserMutationObserver = $$asap$$browserGlobal.MutationObserver || $$asap$$browserGlobal.WebKitMutationObserver;
1008
+
1009
+ // test for web worker but not in IE10
1010
+ var $$asap$$isWorker = typeof Uint8ClampedArray !== 'undefined' &&
1011
+ typeof importScripts !== 'undefined' &&
1012
+ typeof MessageChannel !== 'undefined';
1013
+
1014
+ // node
1015
+ function $$asap$$useNextTick() {
1016
+ return function() {
1017
+ process.nextTick($$asap$$flush);
1018
+ };
1019
+ }
1020
+
1021
+ function $$asap$$useMutationObserver() {
1022
+ var iterations = 0;
1023
+ var observer = new $$asap$$BrowserMutationObserver($$asap$$flush);
1024
+ var node = document.createTextNode('');
1025
+ observer.observe(node, { characterData: true });
1026
+
1027
+ return function() {
1028
+ node.data = (iterations = ++iterations % 2);
1029
+ };
1030
+ }
1031
+
1032
+ // web worker
1033
+ function $$asap$$useMessageChannel() {
1034
+ var channel = new MessageChannel();
1035
+ channel.port1.onmessage = $$asap$$flush;
1036
+ return function () {
1037
+ channel.port2.postMessage(0);
1038
+ };
1039
+ }
1040
+
1041
+ function $$asap$$useSetTimeout() {
1042
+ return function() {
1043
+ setTimeout($$asap$$flush, 1);
1044
+ };
1045
+ }
1046
+
1047
+ var $$asap$$queue = new Array(1000);
1048
+
1049
+ function $$asap$$flush() {
1050
+ for (var i = 0; i < $$asap$$len; i+=2) {
1051
+ var callback = $$asap$$queue[i];
1052
+ var arg = $$asap$$queue[i+1];
1053
+
1054
+ callback(arg);
1055
+
1056
+ $$asap$$queue[i] = undefined;
1057
+ $$asap$$queue[i+1] = undefined;
1058
+ }
1059
+
1060
+ $$asap$$len = 0;
1061
+ }
1062
+
1063
+ var $$asap$$scheduleFlush;
1064
+
1065
+ // Decide what async method to use to triggering processing of queued callbacks:
1066
+ if (typeof process !== 'undefined' && {}.toString.call(process) === '[object process]') {
1067
+ $$asap$$scheduleFlush = $$asap$$useNextTick();
1068
+ } else if ($$asap$$BrowserMutationObserver) {
1069
+ $$asap$$scheduleFlush = $$asap$$useMutationObserver();
1070
+ } else if ($$asap$$isWorker) {
1071
+ $$asap$$scheduleFlush = $$asap$$useMessageChannel();
1072
+ } else {
1073
+ $$asap$$scheduleFlush = $$asap$$useSetTimeout();
1074
+ }
1075
+
1076
+ function $$$internal$$noop() {}
1077
+ var $$$internal$$PENDING = void 0;
1078
+ var $$$internal$$FULFILLED = 1;
1079
+ var $$$internal$$REJECTED = 2;
1080
+ var $$$internal$$GET_THEN_ERROR = new $$$internal$$ErrorObject();
1081
+
1082
+ function $$$internal$$selfFullfillment() {
1083
+ return new TypeError("You cannot resolve a promise with itself");
1084
+ }
1085
+
1086
+ function $$$internal$$cannotReturnOwn() {
1087
+ return new TypeError('A promises callback cannot return that same promise.')
1088
+ }
1089
+
1090
+ function $$$internal$$getThen(promise) {
1091
+ try {
1092
+ return promise.then;
1093
+ } catch(error) {
1094
+ $$$internal$$GET_THEN_ERROR.error = error;
1095
+ return $$$internal$$GET_THEN_ERROR;
1096
+ }
1097
+ }
1098
+
1099
+ function $$$internal$$tryThen(then, value, fulfillmentHandler, rejectionHandler) {
1100
+ try {
1101
+ then.call(value, fulfillmentHandler, rejectionHandler);
1102
+ } catch(e) {
1103
+ return e;
1104
+ }
1105
+ }
1106
+
1107
+ function $$$internal$$handleForeignThenable(promise, thenable, then) {
1108
+ $$asap$$default(function(promise) {
1109
+ var sealed = false;
1110
+ var error = $$$internal$$tryThen(then, thenable, function(value) {
1111
+ if (sealed) { return; }
1112
+ sealed = true;
1113
+ if (thenable !== value) {
1114
+ $$$internal$$resolve(promise, value);
1115
+ } else {
1116
+ $$$internal$$fulfill(promise, value);
1117
+ }
1118
+ }, function(reason) {
1119
+ if (sealed) { return; }
1120
+ sealed = true;
1121
+
1122
+ $$$internal$$reject(promise, reason);
1123
+ }, 'Settle: ' + (promise._label || ' unknown promise'));
1124
+
1125
+ if (!sealed && error) {
1126
+ sealed = true;
1127
+ $$$internal$$reject(promise, error);
1128
+ }
1129
+ }, promise);
1130
+ }
1131
+
1132
+ function $$$internal$$handleOwnThenable(promise, thenable) {
1133
+ if (thenable._state === $$$internal$$FULFILLED) {
1134
+ $$$internal$$fulfill(promise, thenable._result);
1135
+ } else if (promise._state === $$$internal$$REJECTED) {
1136
+ $$$internal$$reject(promise, thenable._result);
1137
+ } else {
1138
+ $$$internal$$subscribe(thenable, undefined, function(value) {
1139
+ $$$internal$$resolve(promise, value);
1140
+ }, function(reason) {
1141
+ $$$internal$$reject(promise, reason);
1142
+ });
1143
+ }
1144
+ }
1145
+
1146
+ function $$$internal$$handleMaybeThenable(promise, maybeThenable) {
1147
+ if (maybeThenable.constructor === promise.constructor) {
1148
+ $$$internal$$handleOwnThenable(promise, maybeThenable);
1149
+ } else {
1150
+ var then = $$$internal$$getThen(maybeThenable);
1151
+
1152
+ if (then === $$$internal$$GET_THEN_ERROR) {
1153
+ $$$internal$$reject(promise, $$$internal$$GET_THEN_ERROR.error);
1154
+ } else if (then === undefined) {
1155
+ $$$internal$$fulfill(promise, maybeThenable);
1156
+ } else if ($$utils$$isFunction(then)) {
1157
+ $$$internal$$handleForeignThenable(promise, maybeThenable, then);
1158
+ } else {
1159
+ $$$internal$$fulfill(promise, maybeThenable);
1160
+ }
1161
+ }
1162
+ }
1163
+
1164
+ function $$$internal$$resolve(promise, value) {
1165
+ if (promise === value) {
1166
+ $$$internal$$reject(promise, $$$internal$$selfFullfillment());
1167
+ } else if ($$utils$$objectOrFunction(value)) {
1168
+ $$$internal$$handleMaybeThenable(promise, value);
1169
+ } else {
1170
+ $$$internal$$fulfill(promise, value);
1171
+ }
1172
+ }
1173
+
1174
+ function $$$internal$$publishRejection(promise) {
1175
+ if (promise._onerror) {
1176
+ promise._onerror(promise._result);
1177
+ }
1178
+
1179
+ $$$internal$$publish(promise);
1180
+ }
1181
+
1182
+ function $$$internal$$fulfill(promise, value) {
1183
+ if (promise._state !== $$$internal$$PENDING) { return; }
1184
+
1185
+ promise._result = value;
1186
+ promise._state = $$$internal$$FULFILLED;
1187
+
1188
+ if (promise._subscribers.length === 0) {
1189
+ } else {
1190
+ $$asap$$default($$$internal$$publish, promise);
1191
+ }
1192
+ }
1193
+
1194
+ function $$$internal$$reject(promise, reason) {
1195
+ if (promise._state !== $$$internal$$PENDING) { return; }
1196
+ promise._state = $$$internal$$REJECTED;
1197
+ promise._result = reason;
1198
+
1199
+ $$asap$$default($$$internal$$publishRejection, promise);
1200
+ }
1201
+
1202
+ function $$$internal$$subscribe(parent, child, onFulfillment, onRejection) {
1203
+ var subscribers = parent._subscribers;
1204
+ var length = subscribers.length;
1205
+
1206
+ parent._onerror = null;
1207
+
1208
+ subscribers[length] = child;
1209
+ subscribers[length + $$$internal$$FULFILLED] = onFulfillment;
1210
+ subscribers[length + $$$internal$$REJECTED] = onRejection;
1211
+
1212
+ if (length === 0 && parent._state) {
1213
+ $$asap$$default($$$internal$$publish, parent);
1214
+ }
1215
+ }
1216
+
1217
+ function $$$internal$$publish(promise) {
1218
+ var subscribers = promise._subscribers;
1219
+ var settled = promise._state;
1220
+
1221
+ if (subscribers.length === 0) { return; }
1222
+
1223
+ var child, callback, detail = promise._result;
1224
+
1225
+ for (var i = 0; i < subscribers.length; i += 3) {
1226
+ child = subscribers[i];
1227
+ callback = subscribers[i + settled];
1228
+
1229
+ if (child) {
1230
+ $$$internal$$invokeCallback(settled, child, callback, detail);
1231
+ } else {
1232
+ callback(detail);
1233
+ }
1234
+ }
1235
+
1236
+ promise._subscribers.length = 0;
1237
+ }
1238
+
1239
+ function $$$internal$$ErrorObject() {
1240
+ this.error = null;
1241
+ }
1242
+
1243
+ var $$$internal$$TRY_CATCH_ERROR = new $$$internal$$ErrorObject();
1244
+
1245
+ function $$$internal$$tryCatch(callback, detail) {
1246
+ try {
1247
+ return callback(detail);
1248
+ } catch(e) {
1249
+ $$$internal$$TRY_CATCH_ERROR.error = e;
1250
+ return $$$internal$$TRY_CATCH_ERROR;
1251
+ }
1252
+ }
1253
+
1254
+ function $$$internal$$invokeCallback(settled, promise, callback, detail) {
1255
+ var hasCallback = $$utils$$isFunction(callback),
1256
+ value, error, succeeded, failed;
1257
+
1258
+ if (hasCallback) {
1259
+ value = $$$internal$$tryCatch(callback, detail);
1260
+
1261
+ if (value === $$$internal$$TRY_CATCH_ERROR) {
1262
+ failed = true;
1263
+ error = value.error;
1264
+ value = null;
1265
+ } else {
1266
+ succeeded = true;
1267
+ }
1268
+
1269
+ if (promise === value) {
1270
+ $$$internal$$reject(promise, $$$internal$$cannotReturnOwn());
1271
+ return;
1272
+ }
1273
+
1274
+ } else {
1275
+ value = detail;
1276
+ succeeded = true;
1277
+ }
1278
+
1279
+ if (promise._state !== $$$internal$$PENDING) {
1280
+ // noop
1281
+ } else if (hasCallback && succeeded) {
1282
+ $$$internal$$resolve(promise, value);
1283
+ } else if (failed) {
1284
+ $$$internal$$reject(promise, error);
1285
+ } else if (settled === $$$internal$$FULFILLED) {
1286
+ $$$internal$$fulfill(promise, value);
1287
+ } else if (settled === $$$internal$$REJECTED) {
1288
+ $$$internal$$reject(promise, value);
1289
+ }
1290
+ }
1291
+
1292
+ function $$$internal$$initializePromise(promise, resolver) {
1293
+ try {
1294
+ resolver(function resolvePromise(value){
1295
+ $$$internal$$resolve(promise, value);
1296
+ }, function rejectPromise(reason) {
1297
+ $$$internal$$reject(promise, reason);
1298
+ });
1299
+ } catch(e) {
1300
+ $$$internal$$reject(promise, e);
1301
+ }
1302
+ }
1303
+
1304
+ function $$$enumerator$$makeSettledResult(state, position, value) {
1305
+ if (state === $$$internal$$FULFILLED) {
1306
+ return {
1307
+ state: 'fulfilled',
1308
+ value: value
1309
+ };
1310
+ } else {
1311
+ return {
1312
+ state: 'rejected',
1313
+ reason: value
1314
+ };
1315
+ }
1316
+ }
1317
+
1318
+ function $$$enumerator$$Enumerator(Constructor, input, abortOnReject, label) {
1319
+ this._instanceConstructor = Constructor;
1320
+ this.promise = new Constructor($$$internal$$noop, label);
1321
+ this._abortOnReject = abortOnReject;
1322
+
1323
+ if (this._validateInput(input)) {
1324
+ this._input = input;
1325
+ this.length = input.length;
1326
+ this._remaining = input.length;
1327
+
1328
+ this._init();
1329
+
1330
+ if (this.length === 0) {
1331
+ $$$internal$$fulfill(this.promise, this._result);
1332
+ } else {
1333
+ this.length = this.length || 0;
1334
+ this._enumerate();
1335
+ if (this._remaining === 0) {
1336
+ $$$internal$$fulfill(this.promise, this._result);
1337
+ }
1338
+ }
1339
+ } else {
1340
+ $$$internal$$reject(this.promise, this._validationError());
1341
+ }
1342
+ }
1343
+
1344
+ $$$enumerator$$Enumerator.prototype._validateInput = function(input) {
1345
+ return $$utils$$isArray(input);
1346
+ };
1347
+
1348
+ $$$enumerator$$Enumerator.prototype._validationError = function() {
1349
+ return new Error('Array Methods must be provided an Array');
1350
+ };
1351
+
1352
+ $$$enumerator$$Enumerator.prototype._init = function() {
1353
+ this._result = new Array(this.length);
1354
+ };
1355
+
1356
+ var $$$enumerator$$default = $$$enumerator$$Enumerator;
1357
+
1358
+ $$$enumerator$$Enumerator.prototype._enumerate = function() {
1359
+ var length = this.length;
1360
+ var promise = this.promise;
1361
+ var input = this._input;
1362
+
1363
+ for (var i = 0; promise._state === $$$internal$$PENDING && i < length; i++) {
1364
+ this._eachEntry(input[i], i);
1365
+ }
1366
+ };
1367
+
1368
+ $$$enumerator$$Enumerator.prototype._eachEntry = function(entry, i) {
1369
+ var c = this._instanceConstructor;
1370
+ if ($$utils$$isMaybeThenable(entry)) {
1371
+ if (entry.constructor === c && entry._state !== $$$internal$$PENDING) {
1372
+ entry._onerror = null;
1373
+ this._settledAt(entry._state, i, entry._result);
1374
+ } else {
1375
+ this._willSettleAt(c.resolve(entry), i);
1376
+ }
1377
+ } else {
1378
+ this._remaining--;
1379
+ this._result[i] = this._makeResult($$$internal$$FULFILLED, i, entry);
1380
+ }
1381
+ };
1382
+
1383
+ $$$enumerator$$Enumerator.prototype._settledAt = function(state, i, value) {
1384
+ var promise = this.promise;
1385
+
1386
+ if (promise._state === $$$internal$$PENDING) {
1387
+ this._remaining--;
1388
+
1389
+ if (this._abortOnReject && state === $$$internal$$REJECTED) {
1390
+ $$$internal$$reject(promise, value);
1391
+ } else {
1392
+ this._result[i] = this._makeResult(state, i, value);
1393
+ }
1394
+ }
1395
+
1396
+ if (this._remaining === 0) {
1397
+ $$$internal$$fulfill(promise, this._result);
1398
+ }
1399
+ };
1400
+
1401
+ $$$enumerator$$Enumerator.prototype._makeResult = function(state, i, value) {
1402
+ return value;
1403
+ };
1404
+
1405
+ $$$enumerator$$Enumerator.prototype._willSettleAt = function(promise, i) {
1406
+ var enumerator = this;
1407
+
1408
+ $$$internal$$subscribe(promise, undefined, function(value) {
1409
+ enumerator._settledAt($$$internal$$FULFILLED, i, value);
1410
+ }, function(reason) {
1411
+ enumerator._settledAt($$$internal$$REJECTED, i, reason);
1412
+ });
1413
+ };
1414
+
1415
+ var $$promise$all$$default = function all(entries, label) {
1416
+ return new $$$enumerator$$default(this, entries, true /* abort on reject */, label).promise;
1417
+ };
1418
+
1419
+ var $$promise$race$$default = function race(entries, label) {
1420
+ /*jshint validthis:true */
1421
+ var Constructor = this;
1422
+
1423
+ var promise = new Constructor($$$internal$$noop, label);
1424
+
1425
+ if (!$$utils$$isArray(entries)) {
1426
+ $$$internal$$reject(promise, new TypeError('You must pass an array to race.'));
1427
+ return promise;
1428
+ }
1429
+
1430
+ var length = entries.length;
1431
+
1432
+ function onFulfillment(value) {
1433
+ $$$internal$$resolve(promise, value);
1434
+ }
1435
+
1436
+ function onRejection(reason) {
1437
+ $$$internal$$reject(promise, reason);
1438
+ }
1439
+
1440
+ for (var i = 0; promise._state === $$$internal$$PENDING && i < length; i++) {
1441
+ $$$internal$$subscribe(Constructor.resolve(entries[i]), undefined, onFulfillment, onRejection);
1442
+ }
1443
+
1444
+ return promise;
1445
+ };
1446
+
1447
+ var $$promise$resolve$$default = function resolve(object, label) {
1448
+ /*jshint validthis:true */
1449
+ var Constructor = this;
1450
+
1451
+ if (object && typeof object === 'object' && object.constructor === Constructor) {
1452
+ return object;
1453
+ }
1454
+
1455
+ var promise = new Constructor($$$internal$$noop, label);
1456
+ $$$internal$$resolve(promise, object);
1457
+ return promise;
1458
+ };
1459
+
1460
+ var $$promise$reject$$default = function reject(reason, label) {
1461
+ /*jshint validthis:true */
1462
+ var Constructor = this;
1463
+ var promise = new Constructor($$$internal$$noop, label);
1464
+ $$$internal$$reject(promise, reason);
1465
+ return promise;
1466
+ };
1467
+
1468
+ var $$es6$promise$promise$$counter = 0;
1469
+
1470
+ function $$es6$promise$promise$$needsResolver() {
1471
+ throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
1472
+ }
1473
+
1474
+ function $$es6$promise$promise$$needsNew() {
1475
+ throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");
1476
+ }
1477
+
1478
+ var $$es6$promise$promise$$default = $$es6$promise$promise$$Promise;
1479
+
1480
+ /**
1481
+ Promise objects represent the eventual result of an asynchronous operation. The
1482
+ primary way of interacting with a promise is through its `then` method, which
1483
+ registers callbacks to receive either a promise’s eventual value or the reason
1484
+ why the promise cannot be fulfilled.
1485
+
1486
+ Terminology
1487
+ -----------
1488
+
1489
+ - `promise` is an object or function with a `then` method whose behavior conforms to this specification.
1490
+ - `thenable` is an object or function that defines a `then` method.
1491
+ - `value` is any legal JavaScript value (including undefined, a thenable, or a promise).
1492
+ - `exception` is a value that is thrown using the throw statement.
1493
+ - `reason` is a value that indicates why a promise was rejected.
1494
+ - `settled` the final resting state of a promise, fulfilled or rejected.
1495
+
1496
+ A promise can be in one of three states: pending, fulfilled, or rejected.
1497
+
1498
+ Promises that are fulfilled have a fulfillment value and are in the fulfilled
1499
+ state. Promises that are rejected have a rejection reason and are in the
1500
+ rejected state. A fulfillment value is never a thenable.
1501
+
1502
+ Promises can also be said to *resolve* a value. If this value is also a
1503
+ promise, then the original promise's settled state will match the value's
1504
+ settled state. So a promise that *resolves* a promise that rejects will
1505
+ itself reject, and a promise that *resolves* a promise that fulfills will
1506
+ itself fulfill.
1507
+
1508
+
1509
+ Basic Usage:
1510
+ ------------
1511
+
1512
+ ```js
1513
+ var promise = new Promise(function(resolve, reject) {
1514
+ // on success
1515
+ resolve(value);
1516
+
1517
+ // on failure
1518
+ reject(reason);
1519
+ });
1520
+
1521
+ promise.then(function(value) {
1522
+ // on fulfillment
1523
+ }, function(reason) {
1524
+ // on rejection
1525
+ });
1526
+ ```
1527
+
1528
+ Advanced Usage:
1529
+ ---------------
1530
+
1531
+ Promises shine when abstracting away asynchronous interactions such as
1532
+ `XMLHttpRequest`s.
1533
+
1534
+ ```js
1535
+ function getJSON(url) {
1536
+ return new Promise(function(resolve, reject){
1537
+ var xhr = new XMLHttpRequest();
1538
+
1539
+ xhr.open('GET', url);
1540
+ xhr.onreadystatechange = handler;
1541
+ xhr.responseType = 'json';
1542
+ xhr.setRequestHeader('Accept', 'application/json');
1543
+ xhr.send();
1544
+
1545
+ function handler() {
1546
+ if (this.readyState === this.DONE) {
1547
+ if (this.status === 200) {
1548
+ resolve(this.response);
1549
+ } else {
1550
+ reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']'));
1551
+ }
1552
+ }
1553
+ };
1554
+ });
1555
+ }
1556
+
1557
+ getJSON('/posts.json').then(function(json) {
1558
+ // on fulfillment
1559
+ }, function(reason) {
1560
+ // on rejection
1561
+ });
1562
+ ```
1563
+
1564
+ Unlike callbacks, promises are great composable primitives.
1565
+
1566
+ ```js
1567
+ Promise.all([
1568
+ getJSON('/posts'),
1569
+ getJSON('/comments')
1570
+ ]).then(function(values){
1571
+ values[0] // => postsJSON
1572
+ values[1] // => commentsJSON
1573
+
1574
+ return values;
1575
+ });
1576
+ ```
1577
+
1578
+ @class Promise
1579
+ @param {function} resolver
1580
+ Useful for tooling.
1581
+ @constructor
1582
+ */
1583
+ function $$es6$promise$promise$$Promise(resolver) {
1584
+ this._id = $$es6$promise$promise$$counter++;
1585
+ this._state = undefined;
1586
+ this._result = undefined;
1587
+ this._subscribers = [];
1588
+
1589
+ if ($$$internal$$noop !== resolver) {
1590
+ if (!$$utils$$isFunction(resolver)) {
1591
+ $$es6$promise$promise$$needsResolver();
1592
+ }
1593
+
1594
+ if (!(this instanceof $$es6$promise$promise$$Promise)) {
1595
+ $$es6$promise$promise$$needsNew();
1596
+ }
1597
+
1598
+ $$$internal$$initializePromise(this, resolver);
1599
+ }
1600
+ }
1601
+
1602
+ $$es6$promise$promise$$Promise.all = $$promise$all$$default;
1603
+ $$es6$promise$promise$$Promise.race = $$promise$race$$default;
1604
+ $$es6$promise$promise$$Promise.resolve = $$promise$resolve$$default;
1605
+ $$es6$promise$promise$$Promise.reject = $$promise$reject$$default;
1606
+
1607
+ $$es6$promise$promise$$Promise.prototype = {
1608
+ constructor: $$es6$promise$promise$$Promise,
1609
+
1610
+ /**
1611
+ The primary way of interacting with a promise is through its `then` method,
1612
+ which registers callbacks to receive either a promise's eventual value or the
1613
+ reason why the promise cannot be fulfilled.
1614
+
1615
+ ```js
1616
+ findUser().then(function(user){
1617
+ // user is available
1618
+ }, function(reason){
1619
+ // user is unavailable, and you are given the reason why
1620
+ });
1621
+ ```
1622
+
1623
+ Chaining
1624
+ --------
1625
+
1626
+ The return value of `then` is itself a promise. This second, 'downstream'
1627
+ promise is resolved with the return value of the first promise's fulfillment
1628
+ or rejection handler, or rejected if the handler throws an exception.
1629
+
1630
+ ```js
1631
+ findUser().then(function (user) {
1632
+ return user.name;
1633
+ }, function (reason) {
1634
+ return 'default name';
1635
+ }).then(function (userName) {
1636
+ // If `findUser` fulfilled, `userName` will be the user's name, otherwise it
1637
+ // will be `'default name'`
1638
+ });
1639
+
1640
+ findUser().then(function (user) {
1641
+ throw new Error('Found user, but still unhappy');
1642
+ }, function (reason) {
1643
+ throw new Error('`findUser` rejected and we're unhappy');
1644
+ }).then(function (value) {
1645
+ // never reached
1646
+ }, function (reason) {
1647
+ // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'.
1648
+ // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'.
1649
+ });
1650
+ ```
1651
+ If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream.
1652
+
1653
+ ```js
1654
+ findUser().then(function (user) {
1655
+ throw new PedagogicalException('Upstream error');
1656
+ }).then(function (value) {
1657
+ // never reached
1658
+ }).then(function (value) {
1659
+ // never reached
1660
+ }, function (reason) {
1661
+ // The `PedgagocialException` is propagated all the way down to here
1662
+ });
1663
+ ```
1664
+
1665
+ Assimilation
1666
+ ------------
1667
+
1668
+ Sometimes the value you want to propagate to a downstream promise can only be
1669
+ retrieved asynchronously. This can be achieved by returning a promise in the
1670
+ fulfillment or rejection handler. The downstream promise will then be pending
1671
+ until the returned promise is settled. This is called *assimilation*.
1672
+
1673
+ ```js
1674
+ findUser().then(function (user) {
1675
+ return findCommentsByAuthor(user);
1676
+ }).then(function (comments) {
1677
+ // The user's comments are now available
1678
+ });
1679
+ ```
1680
+
1681
+ If the assimliated promise rejects, then the downstream promise will also reject.
1682
+
1683
+ ```js
1684
+ findUser().then(function (user) {
1685
+ return findCommentsByAuthor(user);
1686
+ }).then(function (comments) {
1687
+ // If `findCommentsByAuthor` fulfills, we'll have the value here
1688
+ }, function (reason) {
1689
+ // If `findCommentsByAuthor` rejects, we'll have the reason here
1690
+ });
1691
+ ```
1692
+
1693
+ Simple Example
1694
+ --------------
1695
+
1696
+ Synchronous Example
1697
+
1698
+ ```javascript
1699
+ var result;
1700
+
1701
+ try {
1702
+ result = findResult();
1703
+ // success
1704
+ } catch(reason) {
1705
+ // failure
1706
+ }
1707
+ ```
1708
+
1709
+ Errback Example
1710
+
1711
+ ```js
1712
+ findResult(function(result, err){
1713
+ if (err) {
1714
+ // failure
1715
+ } else {
1716
+ // success
1717
+ }
1718
+ });
1719
+ ```
1720
+
1721
+ Promise Example;
1722
+
1723
+ ```javascript
1724
+ findResult().then(function(result){
1725
+ // success
1726
+ }, function(reason){
1727
+ // failure
1728
+ });
1729
+ ```
1730
+
1731
+ Advanced Example
1732
+ --------------
1733
+
1734
+ Synchronous Example
1735
+
1736
+ ```javascript
1737
+ var author, books;
1738
+
1739
+ try {
1740
+ author = findAuthor();
1741
+ books = findBooksByAuthor(author);
1742
+ // success
1743
+ } catch(reason) {
1744
+ // failure
1745
+ }
1746
+ ```
1747
+
1748
+ Errback Example
1749
+
1750
+ ```js
1751
+
1752
+ function foundBooks(books) {
1753
+
1754
+ }
1755
+
1756
+ function failure(reason) {
1757
+
1758
+ }
1759
+
1760
+ findAuthor(function(author, err){
1761
+ if (err) {
1762
+ failure(err);
1763
+ // failure
1764
+ } else {
1765
+ try {
1766
+ findBoooksByAuthor(author, function(books, err) {
1767
+ if (err) {
1768
+ failure(err);
1769
+ } else {
1770
+ try {
1771
+ foundBooks(books);
1772
+ } catch(reason) {
1773
+ failure(reason);
1774
+ }
1775
+ }
1776
+ });
1777
+ } catch(error) {
1778
+ failure(err);
1779
+ }
1780
+ // success
1781
+ }
1782
+ });
1783
+ ```
1784
+
1785
+ Promise Example;
1786
+
1787
+ ```javascript
1788
+ findAuthor().
1789
+ then(findBooksByAuthor).
1790
+ then(function(books){
1791
+ // found books
1792
+ }).catch(function(reason){
1793
+ // something went wrong
1794
+ });
1795
+ ```
1796
+
1797
+ @method then
1798
+ @param {Function} onFulfilled
1799
+ @param {Function} onRejected
1800
+ Useful for tooling.
1801
+ @return {Promise}
1802
+ */
1803
+ then: function(onFulfillment, onRejection) {
1804
+ var parent = this;
1805
+ var state = parent._state;
1806
+
1807
+ if (state === $$$internal$$FULFILLED && !onFulfillment || state === $$$internal$$REJECTED && !onRejection) {
1808
+ return this;
1809
+ }
1810
+
1811
+ var child = new this.constructor($$$internal$$noop);
1812
+ var result = parent._result;
1813
+
1814
+ if (state) {
1815
+ var callback = arguments[state - 1];
1816
+ $$asap$$default(function(){
1817
+ $$$internal$$invokeCallback(state, child, callback, result);
1818
+ });
1819
+ } else {
1820
+ $$$internal$$subscribe(parent, child, onFulfillment, onRejection);
1821
+ }
1822
+
1823
+ return child;
1824
+ },
1825
+
1826
+ /**
1827
+ `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same
1828
+ as the catch block of a try/catch statement.
1829
+
1830
+ ```js
1831
+ function findAuthor(){
1832
+ throw new Error('couldn't find that author');
1833
+ }
1834
+
1835
+ // synchronous
1836
+ try {
1837
+ findAuthor();
1838
+ } catch(reason) {
1839
+ // something went wrong
1840
+ }
1841
+
1842
+ // async with promises
1843
+ findAuthor().catch(function(reason){
1844
+ // something went wrong
1845
+ });
1846
+ ```
1847
+
1848
+ @method catch
1849
+ @param {Function} onRejection
1850
+ Useful for tooling.
1851
+ @return {Promise}
1852
+ */
1853
+ 'catch': function(onRejection) {
1854
+ return this.then(null, onRejection);
1855
+ }
1856
+ };
1857
+
1858
+ var $$es6$promise$polyfill$$default = function polyfill() {
1859
+ var local;
1860
+
1861
+ if (typeof global !== 'undefined') {
1862
+ local = global;
1863
+ } else if (typeof window !== 'undefined' && window.document) {
1864
+ local = window;
1865
+ } else {
1866
+ local = self;
1867
+ }
1868
+
1869
+ var es6PromiseSupport =
1870
+ "Promise" in local &&
1871
+ // Some of these methods are missing from
1872
+ // Firefox/Chrome experimental implementations
1873
+ "resolve" in local.Promise &&
1874
+ "reject" in local.Promise &&
1875
+ "all" in local.Promise &&
1876
+ "race" in local.Promise &&
1877
+ // Older version of the spec had a resolver object
1878
+ // as the arg rather than a function
1879
+ (function() {
1880
+ var resolve;
1881
+ new local.Promise(function(r) { resolve = r; });
1882
+ return $$utils$$isFunction(resolve);
1883
+ }());
1884
+
1885
+ if (!es6PromiseSupport) {
1886
+ local.Promise = $$es6$promise$promise$$default;
1887
+ }
1888
+ };
1889
+
1890
+ var es6$promise$umd$$ES6Promise = {
1891
+ 'Promise': $$es6$promise$promise$$default,
1892
+ 'polyfill': $$es6$promise$polyfill$$default
1893
+ };
1894
+
1895
+ /* global define:true module:true window: true */
1896
+ if (typeof define === 'function' && define['amd']) {
1897
+ define(function() { return es6$promise$umd$$ES6Promise; });
1898
+ } else if (typeof module !== 'undefined' && module['exports']) {
1899
+ module['exports'] = es6$promise$umd$$ES6Promise;
1900
+ } else if (typeof this !== 'undefined') {
1901
+ this['ES6Promise'] = es6$promise$umd$$ES6Promise;
1902
+ }
1903
+ }).call(this);
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){
263
2045
  (function (process){
264
2046
  module.exports = AlgoliaSearch;
265
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
+
266
2057
  /*
267
2058
  * Algolia Search library initialization
268
2059
  * https://www.algolia.com/
@@ -274,74 +2065,80 @@ module.exports = AlgoliaSearch;
274
2065
  * @param {string} [opts.protocol='http:'] - The protocol used to query Algolia Search API.
275
2066
  * Set to 'https:' to force using https. Default to document.location.protocol in browsers
276
2067
  * @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
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
282
2072
  */
283
- function AlgoliaSearch(applicationID, apiKey, opts, _request) {
2073
+ function AlgoliaSearch(applicationID, apiKey, opts) {
2074
+ var extend = require(9);
2075
+
284
2076
  var usage = 'Usage: algoliasearch(applicationID, apiKey, opts)';
285
2077
 
286
2078
  if (!applicationID) {
287
- throw new Error('Please provide an application ID. ' + usage);
2079
+ throw new Error('algoliasearch: Please provide an application ID. ' + usage);
288
2080
  }
289
2081
 
290
2082
  if (!apiKey) {
291
- throw new Error('Please provide an API key. ' + usage);
2083
+ throw new Error('algoliasearch: Please provide an API key. ' + usage);
292
2084
  }
293
2085
 
294
- opts = opts || {};
2086
+ this.applicationID = applicationID;
2087
+ this.apiKey = apiKey;
295
2088
 
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
- }
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
+ };
301
2098
 
302
- if (opts.protocol === undefined) {
303
- opts.protocol = document && document.location.protocol || 'http:';
304
- }
2099
+ this.hostIndex = {
2100
+ read: 0,
2101
+ write: 0
2102
+ };
305
2103
 
306
- if (opts.hosts === undefined) {
307
- opts.hosts = []; // filled later on, has dependencies
308
- }
2104
+ opts = opts || {};
309
2105
 
310
- if (opts.tld === undefined) {
311
- opts.tld = 'net';
312
- }
2106
+ var protocol = opts.protocol || 'https:';
2107
+ var timeout = opts.timeout === undefined ? 2000 : opts.timeout;
313
2108
 
314
2109
  // while we advocate for colon-at-the-end values: 'http:' for `opts.protocol`
315
2110
  // we also accept `http` and `https`. It's a common error.
316
- if (!/:$/.test(opts.protocol)) {
317
- opts.protocol = opts.protocol + ':';
2111
+ if (!/:$/.test(protocol)) {
2112
+ protocol = protocol + ':';
318
2113
  }
319
2114
 
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);
2115
+ if (opts.protocol !== 'http:' && opts.protocol !== 'https:') {
2116
+ throw new Error('algoliasearch: protocol must be `http:` or `https:` (was `' + opts.protocol + '`)');
330
2117
  }
331
2118
 
332
- opts.hosts = map(opts.hosts, function prependProtocol(host) {
333
- return opts.protocol + '//' + host;
334
- });
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
+ }
335
2127
 
336
- this.applicationID = applicationID;
337
- this.apiKey = apiKey;
338
- 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;
339
2132
 
340
- this.currentHostIndex = 0;
341
- this.requestTimeout = opts.timeout;
342
2133
  this.extraHeaders = [];
343
2134
  this.cache = {};
344
- 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);
345
2142
  }
346
2143
 
347
2144
  AlgoliaSearch.prototype = {
@@ -349,20 +2146,21 @@ AlgoliaSearch.prototype = {
349
2146
  * Delete an index
350
2147
  *
351
2148
  * @param indexName the name of index to delete
352
- * @param callback the result callback with two arguments
2149
+ * @param callback the result callback called with two arguments
353
2150
  * error: null or Error('message')
354
2151
  * content: the server answer that contains the task ID
355
2152
  */
356
2153
  deleteIndex: function(indexName, callback) {
357
2154
  return this._jsonRequest({ method: 'DELETE',
358
2155
  url: '/1/indexes/' + encodeURIComponent(indexName),
2156
+ hostType: 'write',
359
2157
  callback: callback });
360
2158
  },
361
2159
  /**
362
2160
  * Move an existing index.
363
2161
  * @param srcIndexName the name of index to copy.
364
2162
  * @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
2163
+ * @param callback the result callback called with two arguments
366
2164
  * error: null or Error('message')
367
2165
  * content: the server answer that contains the task ID
368
2166
  */
@@ -371,6 +2169,7 @@ AlgoliaSearch.prototype = {
371
2169
  return this._jsonRequest({ method: 'POST',
372
2170
  url: '/1/indexes/' + encodeURIComponent(srcIndexName) + '/operation',
373
2171
  body: postObj,
2172
+ hostType: 'write',
374
2173
  callback: callback });
375
2174
 
376
2175
  },
@@ -378,7 +2177,7 @@ AlgoliaSearch.prototype = {
378
2177
  * Copy an existing index.
379
2178
  * @param srcIndexName the name of index to copy.
380
2179
  * @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
2180
+ * @param callback the result callback called with two arguments
382
2181
  * error: null or Error('message')
383
2182
  * content: the server answer that contains the task ID
384
2183
  */
@@ -387,13 +2186,14 @@ AlgoliaSearch.prototype = {
387
2186
  return this._jsonRequest({ method: 'POST',
388
2187
  url: '/1/indexes/' + encodeURIComponent(srcIndexName) + '/operation',
389
2188
  body: postObj,
2189
+ hostType: 'write',
390
2190
  callback: callback });
391
2191
  },
392
2192
  /**
393
2193
  * Return last log entries.
394
2194
  * @param offset Specify the first entry to retrieve (0-based, 0 is the most recent log entry).
395
2195
  * @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
2196
+ * @param callback the result callback called with two arguments
397
2197
  * error: null or Error('message')
398
2198
  * content: the server answer that contains the task ID
399
2199
  */
@@ -411,13 +2211,14 @@ AlgoliaSearch.prototype = {
411
2211
 
412
2212
  return this._jsonRequest({ method: 'GET',
413
2213
  url: '/1/logs?offset=' + offset + '&length=' + length,
2214
+ hostType: 'read',
414
2215
  callback: callback });
415
2216
  },
416
2217
  /*
417
2218
  * List all existing indexes (paginated)
418
2219
  *
419
2220
  * @param page The page to retrieve, starting at 0.
420
- * @param callback the result callback with two arguments
2221
+ * @param callback the result callback called with two arguments
421
2222
  * error: null or Error('message')
422
2223
  * content: the server answer with index list
423
2224
  */
@@ -432,6 +2233,7 @@ AlgoliaSearch.prototype = {
432
2233
 
433
2234
  return this._jsonRequest({ method: 'GET',
434
2235
  url: '/1/indexes' + params,
2236
+ hostType: 'read',
435
2237
  callback: callback });
436
2238
  },
437
2239
 
@@ -447,90 +2249,142 @@ AlgoliaSearch.prototype = {
447
2249
  /*
448
2250
  * List all existing user keys with their associated ACLs
449
2251
  *
450
- * @param callback the result callback with two arguments
2252
+ * @param callback the result callback called with two arguments
451
2253
  * error: null or Error('message')
452
2254
  * content: the server answer with user keys list
453
2255
  */
454
2256
  listUserKeys: function(callback) {
455
2257
  return this._jsonRequest({ method: 'GET',
456
2258
  url: '/1/keys',
2259
+ hostType: 'read',
457
2260
  callback: callback });
458
2261
  },
459
2262
  /*
460
2263
  * Get ACL of a user key
461
2264
  *
462
2265
  * @param key
463
- * @param callback the result callback with two arguments
2266
+ * @param callback the result callback called with two arguments
464
2267
  * error: null or Error('message')
465
2268
  * content: the server answer with user keys list
466
2269
  */
467
2270
  getUserKeyACL: function(key, callback) {
468
2271
  return this._jsonRequest({ method: 'GET',
469
2272
  url: '/1/keys/' + key,
2273
+ hostType: 'read',
470
2274
  callback: callback });
471
2275
  },
472
2276
  /*
473
2277
  * Delete an existing user key
474
2278
  * @param key
475
- * @param callback the result callback with two arguments
2279
+ * @param callback the result callback called with two arguments
476
2280
  * error: null or Error('message')
477
2281
  * content: the server answer with user keys list
478
2282
  */
479
2283
  deleteUserKey: function(key, callback) {
480
2284
  return this._jsonRequest({ method: 'DELETE',
481
2285
  url: '/1/keys/' + key,
2286
+ hostType: 'write',
482
2287
  callback: callback });
483
2288
  },
484
2289
  /*
485
2290
  * Add an existing user key
486
2291
  *
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
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
498
2309
  */
499
- addUserKey: function(acls, callback) {
500
- return this.addUserKeyWithValidity(acls, {
501
- validity: 0,
502
- maxQueriesPerIPPerHour: 0,
503
- maxHitsPerQuery: 0
504
- }, 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
+ });
505
2334
  },
506
- /*
2335
+ /**
507
2336
  * 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
2337
+ * @deprecated Please use client.addUserKey()
523
2338
  */
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 });
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
+ });
534
2388
  },
535
2389
 
536
2390
  /**
@@ -553,7 +2407,8 @@ AlgoliaSearch.prototype = {
553
2407
  }
554
2408
  tags = strTags.join(',');
555
2409
  }
556
- this.tagFilters = tags;
2410
+
2411
+ this.securityTags = tags;
557
2412
  },
558
2413
 
559
2414
  /**
@@ -564,59 +2419,41 @@ AlgoliaSearch.prototype = {
564
2419
  this.userToken = userToken;
565
2420
  },
566
2421
 
567
- /*
2422
+ /**
568
2423
  * Initialize a new batch of search queries
2424
+ * @deprecated use client.search()
569
2425
  */
570
- startQueriesBatch: function() {
571
- this.batch = [];
572
- },
573
- /*
2426
+ startQueriesBatch: deprecate(function() {
2427
+ this._batch = [];
2428
+ }, deprecatedMessage('client.startQueriesBatch()', 'client.search()')),
2429
+
2430
+ /**
574
2431
  * 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.
2432
+ * @deprecated use client.search()
590
2433
  */
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
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
600
2445
  */
601
2446
  clearCache: function() {
602
2447
  this.cache = {};
603
2448
  },
604
- /*
2449
+
2450
+ /**
605
2451
  * 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
2452
+ * @deprecated use client.search()
609
2453
  */
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
- },
2454
+ sendQueriesBatch: deprecate(function(callback) {
2455
+ return this.search(this._batch, callback);
2456
+ }, deprecatedMessage('client.sendQueriesBatch()', 'client.search()')),
620
2457
 
621
2458
  /**
622
2459
  * Set the number of milliseconds a request can take before automatically being terminated.
@@ -629,6 +2466,53 @@ AlgoliaSearch.prototype = {
629
2466
  }
630
2467
  },
631
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,
632
2516
  /*
633
2517
  * Index class constructor.
634
2518
  * You should not use this method directly but use initIndex() function
@@ -645,11 +2529,11 @@ AlgoliaSearch.prototype = {
645
2529
  /**
646
2530
  * Add an extra field to the HTTP request
647
2531
  *
648
- * @param key the header field name
2532
+ * @param name the header field name
649
2533
  * @param value the header field value
650
2534
  */
651
- setExtraHeader: function(key, value) {
652
- this.extraHeaders.push({ key: key, value: value});
2535
+ setExtraHeader: function(name, value) {
2536
+ this.extraHeaders.push({ name: name.toLowerCase(), value: value});
653
2537
  },
654
2538
 
655
2539
  _sendQueriesBatch: function(params, callback) {
@@ -657,6 +2541,7 @@ AlgoliaSearch.prototype = {
657
2541
  method: 'POST',
658
2542
  url: '/1/indexes/*/queries',
659
2543
  body: params,
2544
+ hostType: 'read',
660
2545
  fallback: {
661
2546
  method: 'GET',
662
2547
  url: '/1/indexes/*',
@@ -676,77 +2561,88 @@ AlgoliaSearch.prototype = {
676
2561
  * Wrapper that try all hosts to maximize the quality of service
677
2562
  */
678
2563
  _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
2564
+ var requestDebug = require(5)('algoliasearch:' + opts.url);
2565
+
2566
+ var body;
686
2567
  var cache = opts.cache;
687
- var cacheID = opts.url;
688
2568
  var client = this;
689
2569
  var tries = 0;
2570
+ var usingFallback = false;
690
2571
 
691
- // as we use POST requests to pass parameters (like query='aa'),
692
- // the cacheID must be different between calls
693
2572
  if (opts.body !== undefined) {
694
- cacheID += '_body_' + JSON.stringify(opts.body);
2573
+ body = JSON.stringify(opts.body);
695
2574
  }
696
2575
 
2576
+ requestDebug('request start');
2577
+
697
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
+
698
2591
  // handle cache existence
699
- if (cache && cache[cacheID] !== undefined) {
700
- 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]);
701
2595
  }
702
2596
 
703
- if (tries >= client.hosts.length) {
704
- if (!opts.fallback || requester === client._request.fallback) {
2597
+ if (tries >= client.hosts[opts.hostType].length) {
2598
+ if (!opts.fallback || !client._request.fallback || usingFallback) {
705
2599
  // could not get a response even using the fallback if one was available
706
- return client._request.reject(new Error(
2600
+ return client._promise.reject(new Error(
707
2601
  'Cannot connect to the AlgoliaSearch API.' +
708
2602
  ' Send an email to support@algolia.com to report and resolve the issue.'
709
2603
  ));
710
2604
  }
711
2605
 
2606
+ // let's try the fallback starting from here
712
2607
  tries = 0;
2608
+
2609
+ // method, url and body are fallback dependent
713
2610
  reqOpts.method = opts.fallback.method;
714
2611
  reqOpts.url = opts.fallback.url;
715
- 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
+
716
2617
  reqOpts.timeout = client.requestTimeout * (tries + 1);
717
- client.currentHostIndex = 0;
718
- 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
719
2621
  return doRequest(client._request.fallback, reqOpts);
720
2622
  }
721
2623
 
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
- })
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
+ )
744
2637
  .then(function success(httpResponse) {
745
2638
  // timeout case, retry immediately
746
2639
  if (httpResponse instanceof Error) {
2640
+ requestDebug('error: %s', httpResponse.message);
747
2641
  return retryRequest();
748
2642
  }
749
2643
 
2644
+ requestDebug('received response: %j', httpResponse);
2645
+
750
2646
  var status =
751
2647
  // When in browser mode, using XDR or JSONP
752
2648
  // We rely on our own API response `status`, only
@@ -768,7 +2664,7 @@ AlgoliaSearch.prototype = {
768
2664
  var ok = status === 200 || status === 201;
769
2665
  var retry = !ok && Math.floor(status / 100) !== 4 && Math.floor(status / 100) !== 1;
770
2666
 
771
- if (ok && cache) {
2667
+ if (client._useCache && ok && cache) {
772
2668
  cache[cacheID] = httpResponse.body;
773
2669
  }
774
2670
 
@@ -784,24 +2680,38 @@ AlgoliaSearch.prototype = {
784
2680
  httpResponse.body && httpResponse.body.message || 'Unknown error'
785
2681
  );
786
2682
 
787
- return client._request.reject(unrecoverableError);
2683
+ return client._promise.reject(unrecoverableError);
788
2684
  }, tryFallback);
789
2685
 
790
2686
  function retryRequest() {
791
- client.currentHostIndex = ++client.currentHostIndex % client.hosts.length;
2687
+ client.hostIndex[opts.hostType] = ++client.hostIndex[opts.hostType] % client.hosts[opts.hostType].length;
792
2688
  tries += 1;
793
2689
  reqOpts.timeout = client.requestTimeout * (tries + 1);
794
2690
  return doRequest(requester, reqOpts);
795
2691
  }
796
2692
 
797
- 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
798
2707
  // 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;
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;
802
2712
  } else {
803
2713
  // we were already using the fallback, but something went wrong (script error)
804
- client.currentHostIndex = ++client.currentHostIndex % client.hosts.length;
2714
+ client.hostIndex[opts.hostType] = ++client.hostIndex[opts.hostType] % client.hosts[opts.hostType].length;
805
2715
  tries += 1;
806
2716
  }
807
2717
 
@@ -810,14 +2720,16 @@ AlgoliaSearch.prototype = {
810
2720
  }
811
2721
 
812
2722
  // we can use a fallback if forced AND fallback parameters are available
813
- var useFallback = client.forceFallback && opts.fallback;
2723
+ var useFallback = client.useFallback && opts.fallback;
814
2724
  var requestOptions = useFallback ? opts.fallback : opts;
815
2725
 
816
2726
  var promise = doRequest(
2727
+ // set the requester
817
2728
  useFallback ? client._request.fallback : client._request, {
818
2729
  url: requestOptions.url,
819
2730
  method: requestOptions.method,
820
- body: requestOptions.body,
2731
+ body: body,
2732
+ jsonBody: opts.body,
821
2733
  timeout: client.requestTimeout * (tries + 1)
822
2734
  }
823
2735
  );
@@ -826,13 +2738,13 @@ AlgoliaSearch.prototype = {
826
2738
  // either we are using promises
827
2739
  if (opts.callback) {
828
2740
  promise.then(function okCb(content) {
829
- process.nextTick(function() {
2741
+ exitPromise(function() {
830
2742
  opts.callback(null, content);
831
- });
2743
+ }, client._setTimeout || setTimeout);
832
2744
  }, function nookCb(err) {
833
- process.nextTick(function() {
2745
+ exitPromise(function() {
834
2746
  opts.callback(err);
835
- });
2747
+ }, client._setTimeout || setTimeout);
836
2748
  });
837
2749
  } else {
838
2750
  return promise;
@@ -854,8 +2766,33 @@ AlgoliaSearch.prototype = {
854
2766
  }
855
2767
  return params;
856
2768
  },
2769
+
857
2770
  _isUndefined: function(obj) {
858
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;
859
2796
  }
860
2797
  };
861
2798
 
@@ -876,7 +2813,7 @@ AlgoliaSearch.prototype.Index.prototype = {
876
2813
  * @param content contains the javascript object to add inside the index
877
2814
  * @param objectID (optional) an objectID you want to attribute to this object
878
2815
  * (if the attribute already exist the old object will be overwrite)
879
- * @param callback (optional) the result callback with two arguments:
2816
+ * @param callback (optional) the result callback called with two arguments:
880
2817
  * error: null or Error('message')
881
2818
  * content: the server answer that contains 3 elements: createAt, taskId and objectID
882
2819
  */
@@ -895,6 +2832,7 @@ AlgoliaSearch.prototype.Index.prototype = {
895
2832
  url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + // create
896
2833
  (objectID !== undefined ? '/' + encodeURIComponent(objectID) : ''), // update or create
897
2834
  body: content,
2835
+ hostType: 'write',
898
2836
  callback: callback
899
2837
  });
900
2838
  },
@@ -902,7 +2840,7 @@ AlgoliaSearch.prototype.Index.prototype = {
902
2840
  * Add several objects
903
2841
  *
904
2842
  * @param objects contains an array of objects to add
905
- * @param callback (optional) the result callback with two arguments:
2843
+ * @param callback (optional) the result callback called with two arguments:
906
2844
  * error: null or Error('message')
907
2845
  * content: the server answer that updateAt and taskID
908
2846
  */
@@ -915,16 +2853,17 @@ AlgoliaSearch.prototype.Index.prototype = {
915
2853
  postObj.requests.push(request);
916
2854
  }
917
2855
  return this.as._jsonRequest({ method: 'POST',
918
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
919
- body: postObj,
920
- callback: callback });
2856
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
2857
+ body: postObj,
2858
+ hostType: 'write',
2859
+ callback: callback });
921
2860
  },
922
2861
  /*
923
2862
  * Get an object from this index
924
2863
  *
925
2864
  * @param objectID the unique identifier of the object to retrieve
926
2865
  * @param attrs (optional) if set, contains the array of attribute names to retrieve
927
- * @param callback (optional) the result callback with two arguments
2866
+ * @param callback (optional) the result callback called with two arguments
928
2867
  * error: null or Error('message')
929
2868
  * content: the object to retrieve or the error message if a failure occured
930
2869
  */
@@ -950,6 +2889,33 @@ AlgoliaSearch.prototype.Index.prototype = {
950
2889
  return this.as._jsonRequest({
951
2890
  method: 'GET',
952
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,
953
2919
  callback: callback
954
2920
  });
955
2921
  },
@@ -959,22 +2925,23 @@ AlgoliaSearch.prototype.Index.prototype = {
959
2925
  *
960
2926
  * @param partialObject contains the javascript attributes to override, the
961
2927
  * object must contains an objectID attribute
962
- * @param callback (optional) the result callback with two arguments:
2928
+ * @param callback (optional) the result callback called with two arguments:
963
2929
  * error: null or Error('message')
964
2930
  * content: the server answer that contains 3 elements: createAt, taskId and objectID
965
2931
  */
966
2932
  partialUpdateObject: function(partialObject, callback) {
967
2933
  var indexObj = this;
968
2934
  return this.as._jsonRequest({ method: 'POST',
969
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(partialObject.objectID) + '/partial',
970
- body: partialObject,
971
- callback: callback });
2935
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(partialObject.objectID) + '/partial',
2936
+ body: partialObject,
2937
+ hostType: 'write',
2938
+ callback: callback });
972
2939
  },
973
2940
  /*
974
2941
  * Partially Override the content of several objects
975
2942
  *
976
2943
  * @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:
2944
+ * @param callback (optional) the result callback called with two arguments:
978
2945
  * error: null or Error('message')
979
2946
  * content: the server answer that updateAt and taskID
980
2947
  */
@@ -988,30 +2955,32 @@ AlgoliaSearch.prototype.Index.prototype = {
988
2955
  postObj.requests.push(request);
989
2956
  }
990
2957
  return this.as._jsonRequest({ method: 'POST',
991
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
992
- body: postObj,
993
- callback: callback });
2958
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
2959
+ body: postObj,
2960
+ hostType: 'write',
2961
+ callback: callback });
994
2962
  },
995
2963
  /*
996
2964
  * Override the content of object
997
2965
  *
998
2966
  * @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:
2967
+ * @param callback (optional) the result callback called with two arguments:
1000
2968
  * error: null or Error('message')
1001
2969
  * content: the server answer that updateAt and taskID
1002
2970
  */
1003
2971
  saveObject: function(object, callback) {
1004
2972
  var indexObj = this;
1005
2973
  return this.as._jsonRequest({ method: 'PUT',
1006
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(object.objectID),
1007
- body: object,
1008
- callback: callback });
2974
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(object.objectID),
2975
+ body: object,
2976
+ hostType: 'write',
2977
+ callback: callback });
1009
2978
  },
1010
2979
  /*
1011
2980
  * Override the content of several objects
1012
2981
  *
1013
2982
  * @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:
2983
+ * @param callback (optional) the result callback called with two arguments:
1015
2984
  * error: null or Error('message')
1016
2985
  * content: the server answer that updateAt and taskID
1017
2986
  */
@@ -1025,15 +2994,16 @@ AlgoliaSearch.prototype.Index.prototype = {
1025
2994
  postObj.requests.push(request);
1026
2995
  }
1027
2996
  return this.as._jsonRequest({ method: 'POST',
1028
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
1029
- body: postObj,
1030
- callback: callback });
2997
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
2998
+ body: postObj,
2999
+ hostType: 'write',
3000
+ callback: callback });
1031
3001
  },
1032
3002
  /*
1033
3003
  * Delete an object from the index
1034
3004
  *
1035
3005
  * @param objectID the unique identifier of object to delete
1036
- * @param callback (optional) the result callback with two arguments:
3006
+ * @param callback (optional) the result callback called with two arguments:
1037
3007
  * error: null or Error('message')
1038
3008
  * content: the server answer that contains 3 elements: createAt, taskId and objectID
1039
3009
  */
@@ -1045,13 +3015,118 @@ AlgoliaSearch.prototype.Index.prototype = {
1045
3015
  return callback(err);
1046
3016
  }
1047
3017
 
1048
- return this.as._request.reject(err);
3018
+ return this.as._promise.reject(err);
1049
3019
  }
1050
3020
 
1051
3021
  var indexObj = this;
1052
3022
  return this.as._jsonRequest({ method: 'DELETE',
1053
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(objectID),
1054
- 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
+ }
1055
3130
  },
1056
3131
  /*
1057
3132
  * Search inside the index using XMLHttpRequest request (Using a POST query to
@@ -1117,11 +3192,19 @@ AlgoliaSearch.prototype.Index.prototype = {
1117
3192
  * one is kept and others are removed.
1118
3193
  * - restrictSearchableAttributes: List of attributes you want to use for textual search (must be a subset of the attributesToIndex index setting)
1119
3194
  * either comma separated or as an array
1120
- * @param callback the result callback with two arguments:
3195
+ * @param callback the result callback called with two arguments:
1121
3196
  * error: null or Error('message'). If false, the content contains the error.
1122
3197
  * content: the server answer that contains the list of results.
1123
3198
  */
1124
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
+
1125
3208
  if (arguments.length === 0 || typeof query === 'function') {
1126
3209
  // .search(), .search(cb)
1127
3210
  callback = query;
@@ -1147,6 +3230,7 @@ AlgoliaSearch.prototype.Index.prototype = {
1147
3230
  }
1148
3231
 
1149
3232
  if (args !== undefined) {
3233
+ // `_getSearchParams` will augment params, do not be fooled by the = versus += from previous if
1150
3234
  params = this.as._getSearchParams(args, params);
1151
3235
  }
1152
3236
 
@@ -1159,7 +3243,7 @@ AlgoliaSearch.prototype.Index.prototype = {
1159
3243
  * @param page Pagination parameter used to select the page to retrieve.
1160
3244
  * Page is zero-based and defaults to 0. Thus, to retrieve the 10th page you need to set page=9
1161
3245
  * @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:
3246
+ * @param callback the result callback called with two arguments:
1163
3247
  * error: null or Error('message'). If false, the content contains the error.
1164
3248
  * content: the server answer that contains the list of results.
1165
3249
  */
@@ -1176,8 +3260,9 @@ AlgoliaSearch.prototype.Index.prototype = {
1176
3260
  params += '&hitsPerPage=' + hitsPerPage;
1177
3261
  }
1178
3262
  return this.as._jsonRequest({ method: 'GET',
1179
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/browse' + params,
1180
- callback: callback });
3263
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/browse' + params,
3264
+ hostType: 'read',
3265
+ callback: callback });
1181
3266
  },
1182
3267
 
1183
3268
  /*
@@ -1211,64 +3296,70 @@ AlgoliaSearch.prototype.Index.prototype = {
1211
3296
  // waitTask() must be handled differently from other methods,
1212
3297
  // it's a recursive method using a timeout
1213
3298
  var indexObj = this;
3299
+ var client = indexObj.as;
1214
3300
 
1215
3301
  var promise = this.as._jsonRequest({
1216
3302
  method: 'GET',
3303
+ hostType: 'read',
1217
3304
  url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/task/' + taskID
1218
3305
  }).then(function success(content) {
1219
3306
  if (content.status !== 'published') {
1220
- return new indexObj.as._request.delay(100).then(function() {
1221
- 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);
1222
3311
  });
1223
3312
  }
1224
3313
 
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
- }
3314
+ return content;
1240
3315
  });
1241
3316
 
1242
3317
  if (!callback) {
1243
3318
  return promise;
1244
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
+ }
1245
3334
  },
1246
3335
 
1247
3336
  /*
1248
3337
  * This function deletes the index content. Settings and index specific API keys are kept untouched.
1249
3338
  *
1250
- * @param callback (optional) the result callback with two arguments
3339
+ * @param callback (optional) the result callback called with two arguments
1251
3340
  * error: null or Error('message')
1252
3341
  * content: the settings object or the error message if a failure occured
1253
3342
  */
1254
3343
  clearIndex: function(callback) {
1255
3344
  var indexObj = this;
1256
3345
  return this.as._jsonRequest({ method: 'POST',
1257
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/clear',
1258
- callback: callback });
3346
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/clear',
3347
+ hostType: 'write',
3348
+ callback: callback });
1259
3349
  },
1260
3350
  /*
1261
3351
  * Get settings of this index
1262
3352
  *
1263
- * @param callback (optional) the result callback with two arguments
3353
+ * @param callback (optional) the result callback called with two arguments
1264
3354
  * error: null or Error('message')
1265
3355
  * content: the settings object or the error message if a failure occured
1266
3356
  */
1267
3357
  getSettings: function(callback) {
1268
3358
  var indexObj = this;
1269
3359
  return this.as._jsonRequest({ method: 'GET',
1270
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/settings',
1271
- callback: callback });
3360
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/settings',
3361
+ hostType: 'read',
3362
+ callback: callback });
1272
3363
  },
1273
3364
 
1274
3365
  /*
@@ -1321,81 +3412,61 @@ AlgoliaSearch.prototype.Index.prototype = {
1321
3412
  * - highlightPreTag: (string) Specify the string that is inserted before the highlighted parts in the query result (default to "<em>").
1322
3413
  * - highlightPostTag: (string) Specify the string that is inserted after the highlighted parts in the query result (default to "</em>").
1323
3414
  * - 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
3415
+ * @param callback (optional) the result callback called with two arguments
1325
3416
  * error: null or Error('message')
1326
3417
  * content: the server answer or the error message if a failure occured
1327
3418
  */
1328
3419
  setSettings: function(settings, callback) {
1329
3420
  var indexObj = this;
1330
3421
  return this.as._jsonRequest({ method: 'PUT',
1331
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/settings',
1332
- body: settings,
1333
- callback: callback });
3422
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/settings',
3423
+ hostType: 'write',
3424
+ body: settings,
3425
+ callback: callback });
1334
3426
  },
1335
3427
  /*
1336
3428
  * List all existing user keys associated to this index
1337
3429
  *
1338
- * @param callback the result callback with two arguments
3430
+ * @param callback the result callback called with two arguments
1339
3431
  * error: null or Error('message')
1340
3432
  * content: the server answer with user keys list
1341
3433
  */
1342
3434
  listUserKeys: function(callback) {
1343
3435
  var indexObj = this;
1344
3436
  return this.as._jsonRequest({ method: 'GET',
1345
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys',
1346
- callback: callback });
3437
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys',
3438
+ hostType: 'read',
3439
+ callback: callback });
1347
3440
  },
1348
3441
  /*
1349
3442
  * Get ACL of a user key associated to this index
1350
3443
  *
1351
3444
  * @param key
1352
- * @param callback the result callback with two arguments
3445
+ * @param callback the result callback called with two arguments
1353
3446
  * error: null or Error('message')
1354
3447
  * content: the server answer with user keys list
1355
3448
  */
1356
3449
  getUserKeyACL: function(key, callback) {
1357
3450
  var indexObj = this;
1358
3451
  return this.as._jsonRequest({ method: 'GET',
1359
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys/' + key,
1360
- callback: callback });
3452
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys/' + key,
3453
+ hostType: 'read',
3454
+ callback: callback });
1361
3455
  },
1362
3456
  /*
1363
3457
  * Delete an existing user key associated to this index
1364
3458
  *
1365
3459
  * @param key
1366
- * @param callback the result callback with two arguments
3460
+ * @param callback the result callback called with two arguments
1367
3461
  * error: null or Error('message')
1368
3462
  * content: the server answer with user keys list
1369
3463
  */
1370
3464
  deleteUserKey: function(key, callback) {
1371
3465
  var indexObj = this;
1372
3466
  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 });
3467
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys/' + key,
3468
+ hostType: 'write',
3469
+ callback: callback });
1399
3470
  },
1400
3471
  /*
1401
3472
  * Add an existing user key associated to this index
@@ -1408,33 +3479,94 @@ AlgoliaSearch.prototype.Index.prototype = {
1408
3479
  * - deleteIndex : allows to delete index content (https only)
1409
3480
  * - settings : allows to get index settings (https only)
1410
3481
  * - 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
3482
+ * @param callback the result callback called with two arguments
1415
3483
  * error: null or Error('message')
1416
3484
  * content: the server answer with user keys list
1417
3485
  */
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 });
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
+ });
1429
3509
  },
1430
- ///
1431
- /// Internal methods only after this line
1432
- ///
3510
+
3511
+ /**
3512
+ * Add an existing user key associated to this index
3513
+ * @deprecated use index.addUserKey()
3514
+ */
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
+
1433
3564
  _search: function(params, callback) {
1434
3565
  return this.as._jsonRequest({ cache: this.cache,
1435
3566
  method: 'POST',
1436
3567
  url: '/1/indexes/' + encodeURIComponent(this.indexName) + '/query',
1437
3568
  body: {params: params},
3569
+ hostType: 'read',
1438
3570
  fallback: {
1439
3571
  method: 'GET',
1440
3572
  url: '/1/indexes/' + encodeURIComponent(this.indexName),
@@ -1444,7 +3576,6 @@ AlgoliaSearch.prototype.Index.prototype = {
1444
3576
  });
1445
3577
  },
1446
3578
 
1447
- // internal attributes
1448
3579
  as: null,
1449
3580
  indexName: null,
1450
3581
  typeAheadArgs: null,
@@ -1461,125 +3592,53 @@ function map(arr, fn){
1461
3592
  return ret;
1462
3593
  }
1463
3594
 
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;
3595
+ function prepareHost(protocol) {
3596
+ return function prepare(host) {
3597
+ return protocol + '//' + host.toLowerCase();
3598
+ };
1487
3599
  }
1488
3600
 
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);
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';
1494
3604
 
1495
- global.angular.module('algoliasearch', [])
1496
- .service('algolia', ['$http', '$q', '$timeout', function ($http, $q, $timeout) {
1497
- function request(url, opts) {
1498
- return $q(function(resolve, reject) {
1499
- var timedOut;
1500
- var body = null;
3605
+ throw new Error(message);
3606
+ }
1501
3607
 
1502
- if (opts.body !== undefined) {
1503
- body = JSON.stringify(opts.body);
1504
- }
3608
+ function deprecatedMessage(previousUsage, newUsage) {
3609
+ var githubAnchorLink = previousUsage.toLowerCase()
3610
+ .replace('.', '')
3611
+ .replace('()', '');
1505
3612
 
1506
- var timeout = $q(function(resolveTimeout) {
1507
- $timeout(function() {
1508
- timedOut = true;
1509
- // will cancel the xhr
1510
- resolveTimeout('test');
1511
- resolve(new Error('Timeout - Could not connect to endpoint ' + url));
1512
- }, opts.timeout);
1513
- });
3613
+ return 'algoliasearch: `' + previousUsage + '` was replaced by `' +
3614
+ newUsage + '`. Please see https://github.com/algolia/algoliasearch-client-js/wiki/Deprecated#' + githubAnchorLink
3615
+ }
1514
3616
 
1515
- $http({
1516
- url: url,
1517
- method: opts.method,
1518
- data: body,
1519
- cache: false,
1520
- timeout: timeout
1521
- }).then(function success(response) {
1522
- resolve({
1523
- statusCode: response.status,
1524
- body: response.data
1525
- });
1526
- }, function error(response) {
1527
- if (timedOut) {
1528
- return;
1529
- }
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
+ }
1530
3624
 
1531
- // network error
1532
- if (response.status === 0) {
1533
- reject(new Error('Network error'));
1534
- return;
1535
- }
3625
+ function deprecate(fn, message) {
3626
+ var warned = false;
1536
3627
 
1537
- resolve({
1538
- body: response.data,
1539
- statusCode: response.status
1540
- });
1541
- });
1542
- });
3628
+ function deprecated() {
3629
+ if (!warned) {
3630
+ console.log(message);
3631
+ warned = true;
1543
3632
  }
1544
3633
 
1545
- request.fallback = function(url, opts) {
1546
- return $q(function(resolve, reject) {
1547
- JSONPRequest(url, opts, function JSONPRequestDone(err, content) {
1548
- if (err) {
1549
- reject(err);
1550
- return;
1551
- }
1552
-
1553
- resolve(content);
1554
- });
1555
- });
1556
- };
1557
-
1558
- request.reject = function(val) {
1559
- return $q.reject(val);
1560
- };
1561
-
1562
- request.resolve = function(val) {
1563
- // http://www.bennadel.com/blog/2735-q-when-is-the-missing-q-resolve-method-in-angularjs.htm
1564
- return $q.when(val);
1565
- };
1566
-
1567
- request.delay = function(ms) {
1568
- return $q(function(resolve/*, reject*/) {
1569
- $timeout(resolve, ms);
1570
- });
1571
- };
3634
+ return fn.apply(this, arguments);
3635
+ }
1572
3636
 
1573
- var algoliasearch = createAlgoliasearch(request);
1574
- return {
1575
- Client: function(applicationID, apiKey, options) {
1576
- return algoliasearch(applicationID, apiKey, options);
1577
- }
1578
- };
1579
- }]);
3637
+ return deprecated;
3638
+ }
1580
3639
 
1581
- }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
1582
- },{"4":4,"5":5}],4:[function(require,module,exports){
3640
+ }).call(this,require(1))
3641
+ },{"1":1,"11":11,"5":5,"9":9}],14:[function(require,module,exports){
1583
3642
  module.exports = JSONPRequest;
1584
3643
 
1585
3644
  var JSONPCounter = 0;
@@ -1590,6 +3649,8 @@ function JSONPRequest(url, opts, cb) {
1590
3649
  return;
1591
3650
  }
1592
3651
 
3652
+ opts.debug('JSONP: start');
3653
+
1593
3654
  var cbCalled = false;
1594
3655
  var timedOut = false;
1595
3656
 
@@ -1625,9 +3686,9 @@ function JSONPRequest(url, opts, cb) {
1625
3686
  // add callback by hand
1626
3687
  url += '&callback=' + cbName;
1627
3688
 
1628
- // add body params by hand
1629
- if (opts.body && opts.body.params) {
1630
- url += '&' + opts.body.params;
3689
+ // add body params manually
3690
+ if (opts.jsonBody && opts.jsonBody.params) {
3691
+ url += '&' + opts.jsonBody.params;
1631
3692
  }
1632
3693
 
1633
3694
  var ontimeout = setTimeout(timeout, opts.timeout);
@@ -1645,6 +3706,8 @@ function JSONPRequest(url, opts, cb) {
1645
3706
  head.appendChild(script);
1646
3707
 
1647
3708
  function success() {
3709
+ opts.debug('JSONP: success');
3710
+
1648
3711
  if (done || timedOut) {
1649
3712
  return;
1650
3713
  }
@@ -1653,6 +3716,7 @@ function JSONPRequest(url, opts, cb) {
1653
3716
 
1654
3717
  // script loaded but did not call the fn => script loading error
1655
3718
  if (!cbCalled) {
3719
+ opts.debug('JSONP: Fail. Script loaded but did not call the callback');
1656
3720
  clean();
1657
3721
  cb(new Error('Failed to load JSONP script'));
1658
3722
  }
@@ -1681,12 +3745,16 @@ function JSONPRequest(url, opts, cb) {
1681
3745
  }
1682
3746
 
1683
3747
  function timeout() {
3748
+ opts.debug('JSONP: Script timeout');
3749
+
1684
3750
  timedOut = true;
1685
3751
  clean();
1686
3752
  cb(new Error('Timeout - Could not connect to endpoint ' + url));
1687
3753
  }
1688
3754
 
1689
3755
  function error() {
3756
+ opts.debug('JSONP: Script error');
3757
+
1690
3758
  if (done || timedOut) {
1691
3759
  return;
1692
3760
  }
@@ -1696,22 +3764,346 @@ function JSONPRequest(url, opts, cb) {
1696
3764
  }
1697
3765
  }
1698
3766
 
1699
- },{}],5:[function(require,module,exports){
1700
- // this file is a `factory of algoliasearch()`
1701
- // Given a `request` param, it will provide you an AlgoliaSearch client
1702
- // using this particular request
1703
- module.exports = createAlgoliasearch;
3767
+ },{}],15:[function(require,module,exports){
3768
+ (function (global){
3769
+ // This is the AngularJS Algolia Search module
3770
+ // It's using $http to do requests with a JSONP fallback
3771
+ // $q promises are returned
3772
+
3773
+ var inherits = require(12);
3774
+
3775
+ var AlgoliaSearch = require(13);
3776
+ var inlineHeaders = require(18);
3777
+ var JSONPRequest = require(14);
3778
+
3779
+ // expose original algoliasearch fn in window
3780
+ window.algoliasearch = require(16);
3781
+
3782
+ global.angular.module('algoliasearch', [])
3783
+ .service('algolia', ['$http', '$q', '$timeout', function ($http, $q, $timeout) {
3784
+
3785
+ function algoliasearch(applicationID, apiKey, opts) {
3786
+ var extend = require(9);
3787
+
3788
+ var getDocumentProtocol = require(17);
3789
+
3790
+ opts = extend(true, {}, opts) || {};
3791
+
3792
+ if (opts.protocol === undefined) {
3793
+ opts.protocol = getDocumentProtocol();
3794
+ }
3795
+
3796
+ opts._ua = algoliasearch.ua;
3797
+
3798
+ return new AlgoliaSearchAngular(applicationID, apiKey, opts);
3799
+ }
3800
+
3801
+ algoliasearch.version = require(19);
3802
+ algoliasearch.ua = 'Algolia for AngularJS ' + algoliasearch.version;
3803
+
3804
+ function AlgoliaSearchAngular() {
3805
+ // call AlgoliaSearch constructor
3806
+ AlgoliaSearch.apply(this, arguments);
3807
+ }
3808
+
3809
+ inherits(AlgoliaSearchAngular, AlgoliaSearch);
3810
+
3811
+ AlgoliaSearchAngular.prototype._request = function(url, opts) {
3812
+ return $q(function(resolve, reject) {
3813
+ var timedOut;
3814
+ var body = opts.body;
3815
+
3816
+ url = inlineHeaders(url, opts.headers);
3817
+
3818
+ var timeout = $q(function(resolveTimeout) {
3819
+ $timeout(function() {
3820
+ timedOut = true;
3821
+ // will cancel the xhr
3822
+ resolveTimeout('test');
3823
+ resolve(new Error('Timeout - Could not connect to endpoint ' + url));
3824
+ }, opts.timeout);
3825
+ });
3826
+
3827
+ $http({
3828
+ url: url,
3829
+ method: opts.method,
3830
+ data: body,
3831
+ cache: false,
3832
+ timeout: timeout
3833
+ }).then(function success(response) {
3834
+ resolve({
3835
+ statusCode: response.status,
3836
+ body: response.data
3837
+ });
3838
+ }, function error(response) {
3839
+ if (timedOut) {
3840
+ return;
3841
+ }
3842
+
3843
+ // network error
3844
+ if (response.status === 0) {
3845
+ reject(new Error('Network error'));
3846
+ return;
3847
+ }
3848
+
3849
+ resolve({
3850
+ body: response.data,
3851
+ statusCode: response.status
3852
+ });
3853
+ });
3854
+ });
3855
+ };
3856
+
3857
+ AlgoliaSearchAngular.prototype._request.fallback = function(url, opts) {
3858
+ url = inlineHeaders(url, opts.headers);
3859
+
3860
+ return $q(function(resolve, reject) {
3861
+ JSONPRequest(url, opts, function JSONPRequestDone(err, content) {
3862
+ if (err) {
3863
+ reject(err);
3864
+ return;
3865
+ }
3866
+
3867
+ resolve(content);
3868
+ });
3869
+ });
3870
+ };
3871
+
3872
+ AlgoliaSearchAngular.prototype._promise = {
3873
+ reject: function(val) {
3874
+ return $q.reject(val);
3875
+ },
3876
+ resolve: function(val) {
3877
+ // http://www.bennadel.com/blog/2735-q-when-is-the-missing-q-resolve-method-in-angularjs.htm
3878
+ return $q.when(val);
3879
+ },
3880
+ delay: function(ms) {
3881
+ return $q(function(resolve/*, reject*/) {
3882
+ $timeout(resolve, ms);
3883
+ });
3884
+ }
3885
+ };
3886
+
3887
+ return {
3888
+ Client: function (applicationID, apiKey, options) {
3889
+ return algoliasearch(applicationID, apiKey, options);
3890
+ },
3891
+ ua: algoliasearch.ua,
3892
+ version: algoliasearch.version
3893
+ };
3894
+ }]);
3895
+
3896
+ }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
3897
+ },{"12":12,"13":13,"14":14,"16":16,"17":17,"18":18,"19":19,"9":9}],16:[function(require,module,exports){
3898
+ (function (global){
3899
+ // This is the standalone browser build entry point
3900
+ // Browser implementation of the Algolia Search JavaScript client,
3901
+ // using XMLHttpRequest, XDomainRequest and JSONP as fallback
3902
+ module.exports = algoliasearch;
3903
+
3904
+ var inherits = require(12);
3905
+ var Promise = global.Promise || require(8).Promise;
3906
+
3907
+ var AlgoliaSearch = require(13);
3908
+ var inlineHeaders = require(18);
3909
+ var JSONPRequest = require(14);
3910
+
3911
+ function algoliasearch(applicationID, apiKey, opts) {
3912
+ var extend = require(9);
3913
+
3914
+ var getDocumentProtocol = require(17);
3915
+
3916
+ opts = extend(true, {}, opts) || {};
3917
+
3918
+ if (opts.protocol === undefined) {
3919
+ opts.protocol = getDocumentProtocol();
3920
+ }
3921
+
3922
+ opts._ua = algoliasearch.ua;
3923
+
3924
+ return new AlgoliaSearchBrowser(applicationID, apiKey, opts);
3925
+ }
3926
+
3927
+ algoliasearch.version = require(19);
3928
+ algoliasearch.ua = 'Algolia for vanilla JavaScript ' + algoliasearch.version;
3929
+
3930
+ var support = {
3931
+ hasXMLHttpRequest: 'XMLHttpRequest' in window,
3932
+ hasXDomainRequest: 'XDomainRequest' in window,
3933
+ cors: 'withCredentials' in new XMLHttpRequest(),
3934
+ timeout: 'timeout' in new XMLHttpRequest()
3935
+ };
3936
+
3937
+ function AlgoliaSearchBrowser() {
3938
+ // call AlgoliaSearch constructor
3939
+ AlgoliaSearch.apply(this, arguments);
3940
+ }
3941
+
3942
+ inherits(AlgoliaSearchBrowser, AlgoliaSearch);
3943
+
3944
+ AlgoliaSearchBrowser.prototype._request = function(url, opts) {
3945
+ return new Promise(function(resolve, reject) {
3946
+ // no cors or XDomainRequest, no request
3947
+ if (!support.cors && !support.hasXDomainRequest) {
3948
+ // very old browser, not supported
3949
+ reject(new Error('CORS not supported'));
3950
+ return;
3951
+ }
3952
+
3953
+ url = inlineHeaders(url, opts.headers);
3954
+
3955
+ var body = opts.body;
3956
+ var req = support.cors ? new XMLHttpRequest() : new XDomainRequest();
3957
+ var ontimeout;
3958
+ var timedOut;
3959
+
3960
+ // do not rely on default XHR async flag, as some analytics code like hotjar
3961
+ // breaks it and set it to false by default
3962
+ if (req instanceof XMLHttpRequest) {
3963
+ req.open(opts.method, url, true);
3964
+ } else {
3965
+ req.open(opts.method, url);
3966
+ }
3967
+
3968
+ if (support.cors && body && opts.method !== 'GET') {
3969
+ req.setRequestHeader('content-type', 'application/x-www-form-urlencoded');
3970
+ }
3971
+
3972
+ // we set an empty onprogress listener
3973
+ // so that XDomainRequest on IE9 is not aborted
3974
+ // refs:
3975
+ // - https://github.com/algolia/algoliasearch-client-js/issues/76
3976
+ // - 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
3977
+ req.onprogress = function noop() {};
3978
+
3979
+ req.onload = load;
3980
+ req.onerror = error;
3981
+
3982
+ if (support.timeout) {
3983
+ // .timeout supported by both XHR and XDR,
3984
+ // we do receive timeout event, tested
3985
+ req.timeout = opts.timeout;
3986
+
3987
+ req.ontimeout = timeout;
3988
+ } else {
3989
+ ontimeout = setTimeout(timeout, opts.timeout);
3990
+ }
3991
+
3992
+ req.send(body);
3993
+
3994
+ // event object not received in IE8, at least
3995
+ // but we do not use it, still important to note
3996
+ function load(/*event*/) {
3997
+ // When browser does not supports req.timeout, we can
3998
+ // have both a load and timeout event, since handled by a dumb setTimeout
3999
+ if (timedOut) {
4000
+ return;
4001
+ }
4002
+
4003
+ if (!support.timeout) {
4004
+ clearTimeout(ontimeout);
4005
+ }
4006
+
4007
+ var response = null;
4008
+
4009
+ try {
4010
+ response = JSON.parse(req.responseText);
4011
+ } catch(e) {}
4012
+
4013
+ resolve({
4014
+ body: response,
4015
+ statusCode: req.status
4016
+ });
4017
+ }
4018
+
4019
+ function error(event) {
4020
+ if (timedOut) {
4021
+ return;
4022
+ }
4023
+
4024
+ if (!support.timeout) {
4025
+ clearTimeout(ontimeout);
4026
+ }
4027
+
4028
+ // error event is trigerred both with XDR/XHR on:
4029
+ // - DNS error
4030
+ // - unallowed cross domain request
4031
+ reject(new Error('Could not connect to host, error was:' + event));
4032
+ }
4033
+
4034
+ function timeout() {
4035
+ if (!support.timeout) {
4036
+ timedOut = true;
4037
+ req.abort();
4038
+ }
4039
+
4040
+ resolve(new Error('Timeout - Could not connect to endpoint ' + url));
4041
+ }
4042
+
4043
+ });
4044
+ };
4045
+
4046
+ AlgoliaSearchBrowser.prototype._request.fallback = function(url, opts) {
4047
+ url = inlineHeaders(url, opts.headers);
4048
+
4049
+ return new Promise(function(resolve, reject) {
4050
+ JSONPRequest(url, opts, function JSONPRequestDone(err, content) {
4051
+ if (err) {
4052
+ reject(err);
4053
+ return;
4054
+ }
4055
+
4056
+ resolve(content);
4057
+ });
4058
+ });
4059
+ };
1704
4060
 
1705
- function createAlgoliasearch(request) {
1706
- function algoliasearch(applicationID, apiKey, opts) {
1707
- var AlgoliaSearch = require(2);
4061
+ AlgoliaSearchBrowser.prototype._promise = {
4062
+ reject: function(val) {
4063
+ return Promise.reject(val);
4064
+ },
4065
+ resolve: function(val) {
4066
+ return Promise.resolve(val);
4067
+ },
4068
+ delay: function(ms) {
4069
+ return new Promise(function(resolve/*, reject*/) {
4070
+ setTimeout(resolve, ms);
4071
+ });
4072
+ }
4073
+ };
4074
+
4075
+ }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
4076
+ },{"12":12,"13":13,"14":14,"17":17,"18":18,"19":19,"8":8,"9":9}],17:[function(require,module,exports){
4077
+ (function (global){
4078
+ module.exports = getDocumentProtocol;
1708
4079
 
1709
- return new AlgoliaSearch(applicationID, apiKey, opts, request);
4080
+ function getDocumentProtocol() {
4081
+ var protocol = global.document.location.protocol;
4082
+
4083
+ // when in `file:` mode (local html file), default to `http:`
4084
+ if (protocol !== 'http:' && protocol !== 'https:') {
4085
+ protocol = 'http:';
1710
4086
  }
1711
4087
 
1712
- algoliasearch.version = "3.0.3";
4088
+ return protocol;
4089
+ }
4090
+
4091
+ }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
4092
+ },{}],18:[function(require,module,exports){
4093
+ module.exports = inlineHeaders;
4094
+
4095
+ var querystring = require(4);
4096
+
4097
+ function inlineHeaders(url, headers) {
4098
+ if (/\?/.test(url)) {
4099
+ url += '&';
4100
+ } else {
4101
+ url += '?';
4102
+ }
1713
4103
 
1714
- return algoliasearch;
4104
+ return url + querystring.encode(headers);
1715
4105
  }
1716
4106
 
1717
- },{"2":2}]},{},[3]);
4107
+ },{"4":4}],19:[function(require,module,exports){
4108
+ module.exports="3.3.0"
4109
+ },{}]},{},[15]);