algoliasearch-rails 1.12.0 → 1.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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]);