UNPKG

35 kBJavaScriptView Raw
1(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.MDB = 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){
2
3module.exports = require('./lib/moviedb');
4
5},{"./lib/moviedb":3}],2:[function(require,module,exports){
6module.exports={
7 "base_url": "https://api.themoviedb.org/3/"
8 , "authentication" : {
9 "requestToken" : "authentication/token/new"
10 , "sessionId" : "authentication/session/new"
11 }
12 , "methods" : {
13 "configuration" : {
14 "": { "resource":"configuration", "method": "get" }
15 }
16 , "find" : {
17 "" : { "resource": "find/:id", "method": "get" }
18 }
19 , "search" : {
20 "Movie" : { "resource": "search/movie", "method": "get" }
21 , "Tv" : { "resource": "search/tv", "method": "get" }
22 , "Multi" : { "resource": "search/multi", "method": "get" }
23 , "Collection" : { "resource": "search/collection", "method": "get" }
24 , "Person" : { "resource": "search/person", "method": "get" }
25 , "List" : { "resource": "search/list", "method": "get" }
26 , "Company" : { "resource": "search/company", "method": "get" }
27 , "Keyword" : { "resource": "search/keyword", "method": "get" }
28 }
29 , "collection" : {
30 "Info" : { "resource": "collection/:id", "method": "get" }
31 , "Images" : { "resource": "collection/:id/images", "method": "get" }
32 }
33 , "discover" : {
34 "Movie" : { "resource": "discover/movie", "method": "get" }
35 , "Tv" : { "resource": "discover/tv", "method": "get" }
36 }
37 , "movie" : {
38 "Info" : { "resource": "movie/:id", "method": "get" }
39 , "AlternativeTitles" : { "resource": "movie/:id/alternative_titles", "method": "get" }
40 , "Credits" : { "resource": "movie/:id/credits", "method": "get" }
41 , "Images" : { "resource": "movie/:id/images", "method": "get" }
42 , "Videos" : { "resource": "movie/:id/videos", "method": "get" }
43 , "Keywords" : { "resource": "movie/:id/keywords", "method": "get" }
44 , "Releases" : { "resource":"movie/:id/releases", "method": "get" }
45 , "Trailers" : { "resource": "movie/:id/trailers", "method": "get" }
46 , "Translations" : { "resource": "movie/:id/translations", "method": "get" }
47 , "Similar" : { "resource": "movie/:id/similar_movies", "method": "get" }
48 , "Reviews" : { "resource": "movie/:id/reviews", "method": "get" }
49 , "Lists" : { "resource": "movie/:id/lists", "method": "get" }
50 , "Changes" : { "resource": "movie/:id/changes", "method": "get" }
51 , "RatingUpdate" : { "resource": "movie/:id/rating", "method": "post" }
52 }
53 , "tv" : {
54 "Info" : { "resource": "tv/:id", "method": "get" }
55 , "Credits" : { "resource": "tv/:id/credits", "method": "get" }
56 , "ExternalIds" : { "resource": "tv/:id/external_ids", "method": "get" }
57 , "Images" : { "resource": "tv/:id/images", "method": "get" }
58 , "Translations" : { "resource": "tv/:id/translations", "method": "get" }
59 , "SeasonInfo" : { "resource": "tv/:id/season/:season_number", "method": "get" }
60 , "SeasonCredits" : { "resource": "tv/:id/season/:season_number/credits", "method": "get" }
61 , "SeasonExternalIds" : { "resource": "tv/:id/season/:season_number/external_ids", "method": "get" }
62 , "SeasonImages" : { "resource": "tv/:id/season/:season_number/images", "method": "get" }
63 , "EpisodeInfo" : { "resource": "tv/:id/season/:season_number/episode/:episode_number", "method": "get" }
64 , "EpisodeCredits" : { "resource": "tv/:id/season/:season_number/episode/:episode_number/credits", "method": "get" }
65 , "EpisodeExternalIds" : { "resource": "tv/:id/season/:season_number/episode/:episode_number/external_ids", "method": "get" }
66 , "EpisodeImages" : { "resource": "tv/:id/season/:season_number/episode/:episode_number/images", "method": "get" }
67 }
68 , "person" : {
69 "Info" : { "resource": "person/:id", "method": "get" }
70 , "Credits" : { "resource": "person/:id/credits", "method": "get" }
71 , "Images" : { "resource": "person/:id/images", "method": "get" }
72 , "Changes" : { "resource": "person/:id/changes", "method": "get" }
73 , "Latest" : { "resource": "person/latest", "method": "get" }
74 , "Popular" : { "resource": "person/popular", "method": "get" }
75 }
76 , "list" : {
77 "Info" : { "resource": "list/:id", "method": "get" }
78 }
79 , "genre" : {
80 "List" : { "resource": "genre/list", "method": "get" }
81 , "Movies" : { "resource": "genre/:id/movies", "method": "get" }
82 }
83 , "keyword" : {
84 "Info" : { "resource": "keyword/:id", "method": "get" }
85 , "Movies" : { "resource": "keyword/:id/movies", "method": "get" }
86 }
87 , "company" : {
88 "Info" : { "resource": "company/:id", "method": "get" }
89 , "Movies" : { "resource": "company/:id/movies", "method": "get" }
90 }
91 , "account" : {
92 "Info" : { "resource": "account", "method": "get" }
93 , "Lists" : { "resource": "account/:id/lists", "method": "get" }
94 , "FavoriteMovies" : { "resource": "account/:id/favorite_movies", "method": "get" }
95 , "FavoriteUpdate" : { "resource": "account/:id/favorite", "method": "post" }
96 , "RatedMovies" : { "resource": "account/:id/rated_movies", "method": "get" }
97 , "MovieWatchlist" : { "resource": "account/:id/movie_watchlist", "method": "get" }
98 , "MovieWatchlistUpdate" : { "resource": "account/:id/movie_watchlist", "method": "post" }
99 }
100 , "misc" : {
101 "LatestMovies" : { "resource": "movie/latest", "method": "get" }
102 , "UpcomingMovies" : { "resource": "movie/upcoming", "method": "get" }
103 , "NowPlayingMovies" : { "resource": "movie/now_playing", "method": "get" }
104 , "PopularMovies" : { "resource": "movie/popular", "method": "get" }
105 , "TopRatedMovies" : { "resource": "movie/top_rated", "method": "get" }
106 , "ChangedMovies" : { "resource": "movie/changes", "method": "get" }
107 , "ChangedPeople" : { "resource": "person/changes", "method": "get" }
108 , "TopRatedTvs" : { "resource": "tv/top_rated", "method": "get" }
109 , "PopularTvs" : { "resource": "tv/popular", "method": "get" }
110 }
111 }
112}
113
114},{}],3:[function(require,module,exports){
115
116/*
117 * Module dependencies
118 */
119
120var request = require('superagent')
121 , endpoints = require('./endpoints.json');
122
123/*
124 * Exports the constructor
125 */
126
127module.exports = function(api_key, base_url){
128 if(api_key) return new MovieDB(api_key, base_url);
129 else throw new Error('Bad api key');
130};
131
132/*
133 * Constructor
134 */
135
136function MovieDB(api_key, base_url) {
137 this.api_key = api_key;
138 if(base_url) endpoints.base_url = base_url;
139 return this;
140}
141
142/*
143 * API auth
144 */
145
146MovieDB.prototype.requestToken = function(fn){
147 var self = this;
148
149 request
150 .get(endpoints.base_url + endpoints.authentication.requestToken)
151 .query({'api_key': self.api_key})
152 .set('Accept', 'application/json')
153 .end(function(res){
154 if(res.ok) self.token = res.body;
155 else throw res.error;
156 fn();
157 }).on('error', function(error){
158 throw error;
159 });
160
161 return this;
162};
163
164/*
165 * Generate API methods
166 */
167
168Object.keys(endpoints.methods).forEach(function(method){
169 var met = endpoints.methods[method];
170 Object.keys(met).forEach(function(m){
171 MovieDB.prototype[method + m] = function(params, fn){
172 var self = this;
173
174 if("function" == typeof params) {
175 fn = params;
176 params = {};
177 }
178
179 if(!this.token || Date.now() > +new Date(this.token.expires_at)) {
180 this.requestToken(function(){
181 execMethod.call(self, met[m].method, params, met[m].resource, fn);
182 });
183 } else {
184 execMethod.call(this, met[m].method, params, met[m].resource, fn);
185 }
186
187 return this;
188 };
189 });
190});
191
192var execMethod = function(type, params, endpoint, fn){
193 params = params || {};
194 endpoint = endpoint.replace(':id', params.id).replace(':season_number', params.season_number).replace(':episode_number', params.episode_number);
195 type = type.toUpperCase();
196
197 var req = request(type, endpoints.base_url + endpoint)
198 .query({api_key : this.api_key})
199 .set('Accept', 'application/json');
200
201 if(type === 'GET')
202 req.query(params);
203 else
204 req.send(params);
205
206 req.end(function(res){
207 if(res.ok) fn(null, res.body);
208 else fn(res.error, null);
209 });
210
211 req.on('error', fn);
212};
213
214},{"./endpoints.json":2,"superagent":4}],4:[function(require,module,exports){
215/**
216 * Module dependencies.
217 */
218
219var Emitter = require('emitter');
220var reduce = require('reduce');
221
222/**
223 * Root reference for iframes.
224 */
225
226var root = 'undefined' == typeof window
227 ? this
228 : window;
229
230/**
231 * Noop.
232 */
233
234function noop(){};
235
236/**
237 * Check if `obj` is a host object,
238 * we don't want to serialize these :)
239 *
240 * TODO: future proof, move to compoent land
241 *
242 * @param {Object} obj
243 * @return {Boolean}
244 * @api private
245 */
246
247function isHost(obj) {
248 var str = {}.toString.call(obj);
249
250 switch (str) {
251 case '[object File]':
252 case '[object Blob]':
253 case '[object FormData]':
254 return true;
255 default:
256 return false;
257 }
258}
259
260/**
261 * Determine XHR.
262 */
263
264function getXHR() {
265 if (root.XMLHttpRequest
266 && ('file:' != root.location.protocol || !root.ActiveXObject)) {
267 return new XMLHttpRequest;
268 } else {
269 try { return new ActiveXObject('Microsoft.XMLHTTP'); } catch(e) {}
270 try { return new ActiveXObject('Msxml2.XMLHTTP.6.0'); } catch(e) {}
271 try { return new ActiveXObject('Msxml2.XMLHTTP.3.0'); } catch(e) {}
272 try { return new ActiveXObject('Msxml2.XMLHTTP'); } catch(e) {}
273 }
274 return false;
275}
276
277/**
278 * Removes leading and trailing whitespace, added to support IE.
279 *
280 * @param {String} s
281 * @return {String}
282 * @api private
283 */
284
285var trim = ''.trim
286 ? function(s) { return s.trim(); }
287 : function(s) { return s.replace(/(^\s*|\s*$)/g, ''); };
288
289/**
290 * Check if `obj` is an object.
291 *
292 * @param {Object} obj
293 * @return {Boolean}
294 * @api private
295 */
296
297function isObject(obj) {
298 return obj === Object(obj);
299}
300
301/**
302 * Serialize the given `obj`.
303 *
304 * @param {Object} obj
305 * @return {String}
306 * @api private
307 */
308
309function serialize(obj) {
310 if (!isObject(obj)) return obj;
311 var pairs = [];
312 for (var key in obj) {
313 if (null != obj[key]) {
314 pairs.push(encodeURIComponent(key)
315 + '=' + encodeURIComponent(obj[key]));
316 }
317 }
318 return pairs.join('&');
319}
320
321/**
322 * Expose serialization method.
323 */
324
325 request.serializeObject = serialize;
326
327 /**
328 * Parse the given x-www-form-urlencoded `str`.
329 *
330 * @param {String} str
331 * @return {Object}
332 * @api private
333 */
334
335function parseString(str) {
336 var obj = {};
337 var pairs = str.split('&');
338 var parts;
339 var pair;
340
341 for (var i = 0, len = pairs.length; i < len; ++i) {
342 pair = pairs[i];
343 parts = pair.split('=');
344 obj[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1]);
345 }
346
347 return obj;
348}
349
350/**
351 * Expose parser.
352 */
353
354request.parseString = parseString;
355
356/**
357 * Default MIME type map.
358 *
359 * superagent.types.xml = 'application/xml';
360 *
361 */
362
363request.types = {
364 html: 'text/html',
365 json: 'application/json',
366 xml: 'application/xml',
367 urlencoded: 'application/x-www-form-urlencoded',
368 'form': 'application/x-www-form-urlencoded',
369 'form-data': 'application/x-www-form-urlencoded'
370};
371
372/**
373 * Default serialization map.
374 *
375 * superagent.serialize['application/xml'] = function(obj){
376 * return 'generated xml here';
377 * };
378 *
379 */
380
381 request.serialize = {
382 'application/x-www-form-urlencoded': serialize,
383 'application/json': JSON.stringify
384 };
385
386 /**
387 * Default parsers.
388 *
389 * superagent.parse['application/xml'] = function(str){
390 * return { object parsed from str };
391 * };
392 *
393 */
394
395request.parse = {
396 'application/x-www-form-urlencoded': parseString,
397 'application/json': JSON.parse
398};
399
400/**
401 * Parse the given header `str` into
402 * an object containing the mapped fields.
403 *
404 * @param {String} str
405 * @return {Object}
406 * @api private
407 */
408
409function parseHeader(str) {
410 var lines = str.split(/\r?\n/);
411 var fields = {};
412 var index;
413 var line;
414 var field;
415 var val;
416
417 lines.pop(); // trailing CRLF
418
419 for (var i = 0, len = lines.length; i < len; ++i) {
420 line = lines[i];
421 index = line.indexOf(':');
422 field = line.slice(0, index).toLowerCase();
423 val = trim(line.slice(index + 1));
424 fields[field] = val;
425 }
426
427 return fields;
428}
429
430/**
431 * Return the mime type for the given `str`.
432 *
433 * @param {String} str
434 * @return {String}
435 * @api private
436 */
437
438function type(str){
439 return str.split(/ *; */).shift();
440};
441
442/**
443 * Return header field parameters.
444 *
445 * @param {String} str
446 * @return {Object}
447 * @api private
448 */
449
450function params(str){
451 return reduce(str.split(/ *; */), function(obj, str){
452 var parts = str.split(/ *= */)
453 , key = parts.shift()
454 , val = parts.shift();
455
456 if (key && val) obj[key] = val;
457 return obj;
458 }, {});
459};
460
461/**
462 * Initialize a new `Response` with the given `xhr`.
463 *
464 * - set flags (.ok, .error, etc)
465 * - parse header
466 *
467 * Examples:
468 *
469 * Aliasing `superagent` as `request` is nice:
470 *
471 * request = superagent;
472 *
473 * We can use the promise-like API, or pass callbacks:
474 *
475 * request.get('/').end(function(res){});
476 * request.get('/', function(res){});
477 *
478 * Sending data can be chained:
479 *
480 * request
481 * .post('/user')
482 * .send({ name: 'tj' })
483 * .end(function(res){});
484 *
485 * Or passed to `.send()`:
486 *
487 * request
488 * .post('/user')
489 * .send({ name: 'tj' }, function(res){});
490 *
491 * Or passed to `.post()`:
492 *
493 * request
494 * .post('/user', { name: 'tj' })
495 * .end(function(res){});
496 *
497 * Or further reduced to a single call for simple cases:
498 *
499 * request
500 * .post('/user', { name: 'tj' }, function(res){});
501 *
502 * @param {XMLHTTPRequest} xhr
503 * @param {Object} options
504 * @api private
505 */
506
507function Response(req, options) {
508 options = options || {};
509 this.req = req;
510 this.xhr = this.req.xhr;
511 this.text = this.xhr.responseText;
512 this.setStatusProperties(this.xhr.status);
513 this.header = this.headers = parseHeader(this.xhr.getAllResponseHeaders());
514 // getAllResponseHeaders sometimes falsely returns "" for CORS requests, but
515 // getResponseHeader still works. so we get content-type even if getting
516 // other headers fails.
517 this.header['content-type'] = this.xhr.getResponseHeader('content-type');
518 this.setHeaderProperties(this.header);
519 this.body = this.req.method != 'HEAD'
520 ? this.parseBody(this.text)
521 : null;
522}
523
524/**
525 * Get case-insensitive `field` value.
526 *
527 * @param {String} field
528 * @return {String}
529 * @api public
530 */
531
532Response.prototype.get = function(field){
533 return this.header[field.toLowerCase()];
534};
535
536/**
537 * Set header related properties:
538 *
539 * - `.type` the content type without params
540 *
541 * A response of "Content-Type: text/plain; charset=utf-8"
542 * will provide you with a `.type` of "text/plain".
543 *
544 * @param {Object} header
545 * @api private
546 */
547
548Response.prototype.setHeaderProperties = function(header){
549 // content-type
550 var ct = this.header['content-type'] || '';
551 this.type = type(ct);
552
553 // params
554 var obj = params(ct);
555 for (var key in obj) this[key] = obj[key];
556};
557
558/**
559 * Parse the given body `str`.
560 *
561 * Used for auto-parsing of bodies. Parsers
562 * are defined on the `superagent.parse` object.
563 *
564 * @param {String} str
565 * @return {Mixed}
566 * @api private
567 */
568
569Response.prototype.parseBody = function(str){
570 var parse = request.parse[this.type];
571 return parse && str && str.length
572 ? parse(str)
573 : null;
574};
575
576/**
577 * Set flags such as `.ok` based on `status`.
578 *
579 * For example a 2xx response will give you a `.ok` of __true__
580 * whereas 5xx will be __false__ and `.error` will be __true__. The
581 * `.clientError` and `.serverError` are also available to be more
582 * specific, and `.statusType` is the class of error ranging from 1..5
583 * sometimes useful for mapping respond colors etc.
584 *
585 * "sugar" properties are also defined for common cases. Currently providing:
586 *
587 * - .noContent
588 * - .badRequest
589 * - .unauthorized
590 * - .notAcceptable
591 * - .notFound
592 *
593 * @param {Number} status
594 * @api private
595 */
596
597Response.prototype.setStatusProperties = function(status){
598 var type = status / 100 | 0;
599
600 // status / class
601 this.status = status;
602 this.statusType = type;
603
604 // basics
605 this.info = 1 == type;
606 this.ok = 2 == type;
607 this.clientError = 4 == type;
608 this.serverError = 5 == type;
609 this.error = (4 == type || 5 == type)
610 ? this.toError()
611 : false;
612
613 // sugar
614 this.accepted = 202 == status;
615 this.noContent = 204 == status || 1223 == status;
616 this.badRequest = 400 == status;
617 this.unauthorized = 401 == status;
618 this.notAcceptable = 406 == status;
619 this.notFound = 404 == status;
620 this.forbidden = 403 == status;
621};
622
623/**
624 * Return an `Error` representative of this response.
625 *
626 * @return {Error}
627 * @api public
628 */
629
630Response.prototype.toError = function(){
631 var req = this.req;
632 var method = req.method;
633 var url = req.url;
634
635 var msg = 'cannot ' + method + ' ' + url + ' (' + this.status + ')';
636 var err = new Error(msg);
637 err.status = this.status;
638 err.method = method;
639 err.url = url;
640
641 return err;
642};
643
644/**
645 * Expose `Response`.
646 */
647
648request.Response = Response;
649
650/**
651 * Initialize a new `Request` with the given `method` and `url`.
652 *
653 * @param {String} method
654 * @param {String} url
655 * @api public
656 */
657
658function Request(method, url) {
659 var self = this;
660 Emitter.call(this);
661 this._query = this._query || [];
662 this.method = method;
663 this.url = url;
664 this.header = {};
665 this._header = {};
666 this.on('end', function(){
667 try {
668 var res = new Response(self);
669 if ('HEAD' == method) res.text = null;
670 self.callback(null, res);
671 } catch(e) {
672 var err = new Error('Parser is unable to parse the response');
673 err.parse = true;
674 err.original = e;
675 self.callback(err);
676 }
677 });
678}
679
680/**
681 * Mixin `Emitter`.
682 */
683
684Emitter(Request.prototype);
685
686/**
687 * Allow for extension
688 */
689
690Request.prototype.use = function(fn) {
691 fn(this);
692 return this;
693}
694
695/**
696 * Set timeout to `ms`.
697 *
698 * @param {Number} ms
699 * @return {Request} for chaining
700 * @api public
701 */
702
703Request.prototype.timeout = function(ms){
704 this._timeout = ms;
705 return this;
706};
707
708/**
709 * Clear previous timeout.
710 *
711 * @return {Request} for chaining
712 * @api public
713 */
714
715Request.prototype.clearTimeout = function(){
716 this._timeout = 0;
717 clearTimeout(this._timer);
718 return this;
719};
720
721/**
722 * Abort the request, and clear potential timeout.
723 *
724 * @return {Request}
725 * @api public
726 */
727
728Request.prototype.abort = function(){
729 if (this.aborted) return;
730 this.aborted = true;
731 this.xhr.abort();
732 this.clearTimeout();
733 this.emit('abort');
734 return this;
735};
736
737/**
738 * Set header `field` to `val`, or multiple fields with one object.
739 *
740 * Examples:
741 *
742 * req.get('/')
743 * .set('Accept', 'application/json')
744 * .set('X-API-Key', 'foobar')
745 * .end(callback);
746 *
747 * req.get('/')
748 * .set({ Accept: 'application/json', 'X-API-Key': 'foobar' })
749 * .end(callback);
750 *
751 * @param {String|Object} field
752 * @param {String} val
753 * @return {Request} for chaining
754 * @api public
755 */
756
757Request.prototype.set = function(field, val){
758 if (isObject(field)) {
759 for (var key in field) {
760 this.set(key, field[key]);
761 }
762 return this;
763 }
764 this._header[field.toLowerCase()] = val;
765 this.header[field] = val;
766 return this;
767};
768
769/**
770 * Remove header `field`.
771 *
772 * Example:
773 *
774 * req.get('/')
775 * .unset('User-Agent')
776 * .end(callback);
777 *
778 * @param {String} field
779 * @return {Request} for chaining
780 * @api public
781 */
782
783Request.prototype.unset = function(field){
784 delete this._header[field.toLowerCase()];
785 delete this.header[field];
786 return this;
787};
788
789/**
790 * Get case-insensitive header `field` value.
791 *
792 * @param {String} field
793 * @return {String}
794 * @api private
795 */
796
797Request.prototype.getHeader = function(field){
798 return this._header[field.toLowerCase()];
799};
800
801/**
802 * Set Content-Type to `type`, mapping values from `request.types`.
803 *
804 * Examples:
805 *
806 * superagent.types.xml = 'application/xml';
807 *
808 * request.post('/')
809 * .type('xml')
810 * .send(xmlstring)
811 * .end(callback);
812 *
813 * request.post('/')
814 * .type('application/xml')
815 * .send(xmlstring)
816 * .end(callback);
817 *
818 * @param {String} type
819 * @return {Request} for chaining
820 * @api public
821 */
822
823Request.prototype.type = function(type){
824 this.set('Content-Type', request.types[type] || type);
825 return this;
826};
827
828/**
829 * Set Accept to `type`, mapping values from `request.types`.
830 *
831 * Examples:
832 *
833 * superagent.types.json = 'application/json';
834 *
835 * request.get('/agent')
836 * .accept('json')
837 * .end(callback);
838 *
839 * request.get('/agent')
840 * .accept('application/json')
841 * .end(callback);
842 *
843 * @param {String} accept
844 * @return {Request} for chaining
845 * @api public
846 */
847
848Request.prototype.accept = function(type){
849 this.set('Accept', request.types[type] || type);
850 return this;
851};
852
853/**
854 * Set Authorization field value with `user` and `pass`.
855 *
856 * @param {String} user
857 * @param {String} pass
858 * @return {Request} for chaining
859 * @api public
860 */
861
862Request.prototype.auth = function(user, pass){
863 var str = btoa(user + ':' + pass);
864 this.set('Authorization', 'Basic ' + str);
865 return this;
866};
867
868/**
869* Add query-string `val`.
870*
871* Examples:
872*
873* request.get('/shoes')
874* .query('size=10')
875* .query({ color: 'blue' })
876*
877* @param {Object|String} val
878* @return {Request} for chaining
879* @api public
880*/
881
882Request.prototype.query = function(val){
883 if ('string' != typeof val) val = serialize(val);
884 if (val) this._query.push(val);
885 return this;
886};
887
888/**
889 * Write the field `name` and `val` for "multipart/form-data"
890 * request bodies.
891 *
892 * ``` js
893 * request.post('/upload')
894 * .field('foo', 'bar')
895 * .end(callback);
896 * ```
897 *
898 * @param {String} name
899 * @param {String|Blob|File} val
900 * @return {Request} for chaining
901 * @api public
902 */
903
904Request.prototype.field = function(name, val){
905 if (!this._formData) this._formData = new FormData();
906 this._formData.append(name, val);
907 return this;
908};
909
910/**
911 * Queue the given `file` as an attachment to the specified `field`,
912 * with optional `filename`.
913 *
914 * ``` js
915 * request.post('/upload')
916 * .attach(new Blob(['<a id="a"><b id="b">hey!</b></a>'], { type: "text/html"}))
917 * .end(callback);
918 * ```
919 *
920 * @param {String} field
921 * @param {Blob|File} file
922 * @param {String} filename
923 * @return {Request} for chaining
924 * @api public
925 */
926
927Request.prototype.attach = function(field, file, filename){
928 if (!this._formData) this._formData = new FormData();
929 this._formData.append(field, file, filename);
930 return this;
931};
932
933/**
934 * Send `data`, defaulting the `.type()` to "json" when
935 * an object is given.
936 *
937 * Examples:
938 *
939 * // querystring
940 * request.get('/search')
941 * .end(callback)
942 *
943 * // multiple data "writes"
944 * request.get('/search')
945 * .send({ search: 'query' })
946 * .send({ range: '1..5' })
947 * .send({ order: 'desc' })
948 * .end(callback)
949 *
950 * // manual json
951 * request.post('/user')
952 * .type('json')
953 * .send('{"name":"tj"})
954 * .end(callback)
955 *
956 * // auto json
957 * request.post('/user')
958 * .send({ name: 'tj' })
959 * .end(callback)
960 *
961 * // manual x-www-form-urlencoded
962 * request.post('/user')
963 * .type('form')
964 * .send('name=tj')
965 * .end(callback)
966 *
967 * // auto x-www-form-urlencoded
968 * request.post('/user')
969 * .type('form')
970 * .send({ name: 'tj' })
971 * .end(callback)
972 *
973 * // defaults to x-www-form-urlencoded
974 * request.post('/user')
975 * .send('name=tobi')
976 * .send('species=ferret')
977 * .end(callback)
978 *
979 * @param {String|Object} data
980 * @return {Request} for chaining
981 * @api public
982 */
983
984Request.prototype.send = function(data){
985 var obj = isObject(data);
986 var type = this.getHeader('Content-Type');
987
988 // merge
989 if (obj && isObject(this._data)) {
990 for (var key in data) {
991 this._data[key] = data[key];
992 }
993 } else if ('string' == typeof data) {
994 if (!type) this.type('form');
995 type = this.getHeader('Content-Type');
996 if ('application/x-www-form-urlencoded' == type) {
997 this._data = this._data
998 ? this._data + '&' + data
999 : data;
1000 } else {
1001 this._data = (this._data || '') + data;
1002 }
1003 } else {
1004 this._data = data;
1005 }
1006
1007 if (!obj) return this;
1008 if (!type) this.type('json');
1009 return this;
1010};
1011
1012/**
1013 * Invoke the callback with `err` and `res`
1014 * and handle arity check.
1015 *
1016 * @param {Error} err
1017 * @param {Response} res
1018 * @api private
1019 */
1020
1021Request.prototype.callback = function(err, res){
1022 var fn = this._callback;
1023 if (2 == fn.length) return fn(err, res);
1024 if (err) return this.emit('error', err);
1025 fn(res);
1026};
1027
1028/**
1029 * Invoke callback with x-domain error.
1030 *
1031 * @api private
1032 */
1033
1034Request.prototype.crossDomainError = function(){
1035 var err = new Error('Origin is not allowed by Access-Control-Allow-Origin');
1036 err.crossDomain = true;
1037 this.callback(err);
1038};
1039
1040/**
1041 * Invoke callback with timeout error.
1042 *
1043 * @api private
1044 */
1045
1046Request.prototype.timeoutError = function(){
1047 var timeout = this._timeout;
1048 var err = new Error('timeout of ' + timeout + 'ms exceeded');
1049 err.timeout = timeout;
1050 this.callback(err);
1051};
1052
1053/**
1054 * Enable transmission of cookies with x-domain requests.
1055 *
1056 * Note that for this to work the origin must not be
1057 * using "Access-Control-Allow-Origin" with a wildcard,
1058 * and also must set "Access-Control-Allow-Credentials"
1059 * to "true".
1060 *
1061 * @api public
1062 */
1063
1064Request.prototype.withCredentials = function(){
1065 this._withCredentials = true;
1066 return this;
1067};
1068
1069/**
1070 * Initiate request, invoking callback `fn(res)`
1071 * with an instanceof `Response`.
1072 *
1073 * @param {Function} fn
1074 * @return {Request} for chaining
1075 * @api public
1076 */
1077
1078Request.prototype.end = function(fn){
1079 var self = this;
1080 var xhr = this.xhr = getXHR();
1081 var query = this._query.join('&');
1082 var timeout = this._timeout;
1083 var data = this._formData || this._data;
1084
1085 // store callback
1086 this._callback = fn || noop;
1087
1088 // state change
1089 xhr.onreadystatechange = function(){
1090 if (4 != xhr.readyState) return;
1091 if (0 == xhr.status) {
1092 if (self.aborted) return self.timeoutError();
1093 return self.crossDomainError();
1094 }
1095 self.emit('end');
1096 };
1097
1098 // progress
1099 if (xhr.upload) {
1100 xhr.upload.onprogress = function(e){
1101 e.percent = e.loaded / e.total * 100;
1102 self.emit('progress', e);
1103 };
1104 }
1105
1106 // timeout
1107 if (timeout && !this._timer) {
1108 this._timer = setTimeout(function(){
1109 self.abort();
1110 }, timeout);
1111 }
1112
1113 // querystring
1114 if (query) {
1115 query = request.serializeObject(query);
1116 this.url += ~this.url.indexOf('?')
1117 ? '&' + query
1118 : '?' + query;
1119 }
1120
1121 // initiate request
1122 xhr.open(this.method, this.url, true);
1123
1124 // CORS
1125 if (this._withCredentials) xhr.withCredentials = true;
1126
1127 // body
1128 if ('GET' != this.method && 'HEAD' != this.method && 'string' != typeof data && !isHost(data)) {
1129 // serialize stuff
1130 var serialize = request.serialize[this.getHeader('Content-Type')];
1131 if (serialize) data = serialize(data);
1132 }
1133
1134 // set header fields
1135 for (var field in this.header) {
1136 if (null == this.header[field]) continue;
1137 xhr.setRequestHeader(field, this.header[field]);
1138 }
1139
1140 // send stuff
1141 this.emit('request', this);
1142 xhr.send(data);
1143 return this;
1144};
1145
1146/**
1147 * Expose `Request`.
1148 */
1149
1150request.Request = Request;
1151
1152/**
1153 * Issue a request:
1154 *
1155 * Examples:
1156 *
1157 * request('GET', '/users').end(callback)
1158 * request('/users').end(callback)
1159 * request('/users', callback)
1160 *
1161 * @param {String} method
1162 * @param {String|Function} url or callback
1163 * @return {Request}
1164 * @api public
1165 */
1166
1167function request(method, url) {
1168 // callback
1169 if ('function' == typeof url) {
1170 return new Request('GET', method).end(url);
1171 }
1172
1173 // url first
1174 if (1 == arguments.length) {
1175 return new Request('GET', method);
1176 }
1177
1178 return new Request(method, url);
1179}
1180
1181/**
1182 * GET `url` with optional callback `fn(res)`.
1183 *
1184 * @param {String} url
1185 * @param {Mixed|Function} data or fn
1186 * @param {Function} fn
1187 * @return {Request}
1188 * @api public
1189 */
1190
1191request.get = function(url, data, fn){
1192 var req = request('GET', url);
1193 if ('function' == typeof data) fn = data, data = null;
1194 if (data) req.query(data);
1195 if (fn) req.end(fn);
1196 return req;
1197};
1198
1199/**
1200 * HEAD `url` with optional callback `fn(res)`.
1201 *
1202 * @param {String} url
1203 * @param {Mixed|Function} data or fn
1204 * @param {Function} fn
1205 * @return {Request}
1206 * @api public
1207 */
1208
1209request.head = function(url, data, fn){
1210 var req = request('HEAD', url);
1211 if ('function' == typeof data) fn = data, data = null;
1212 if (data) req.send(data);
1213 if (fn) req.end(fn);
1214 return req;
1215};
1216
1217/**
1218 * DELETE `url` with optional callback `fn(res)`.
1219 *
1220 * @param {String} url
1221 * @param {Function} fn
1222 * @return {Request}
1223 * @api public
1224 */
1225
1226request.del = function(url, fn){
1227 var req = request('DELETE', url);
1228 if (fn) req.end(fn);
1229 return req;
1230};
1231
1232/**
1233 * PATCH `url` with optional `data` and callback `fn(res)`.
1234 *
1235 * @param {String} url
1236 * @param {Mixed} data
1237 * @param {Function} fn
1238 * @return {Request}
1239 * @api public
1240 */
1241
1242request.patch = function(url, data, fn){
1243 var req = request('PATCH', url);
1244 if ('function' == typeof data) fn = data, data = null;
1245 if (data) req.send(data);
1246 if (fn) req.end(fn);
1247 return req;
1248};
1249
1250/**
1251 * POST `url` with optional `data` and callback `fn(res)`.
1252 *
1253 * @param {String} url
1254 * @param {Mixed} data
1255 * @param {Function} fn
1256 * @return {Request}
1257 * @api public
1258 */
1259
1260request.post = function(url, data, fn){
1261 var req = request('POST', url);
1262 if ('function' == typeof data) fn = data, data = null;
1263 if (data) req.send(data);
1264 if (fn) req.end(fn);
1265 return req;
1266};
1267
1268/**
1269 * PUT `url` with optional `data` and callback `fn(res)`.
1270 *
1271 * @param {String} url
1272 * @param {Mixed|Function} data or fn
1273 * @param {Function} fn
1274 * @return {Request}
1275 * @api public
1276 */
1277
1278request.put = function(url, data, fn){
1279 var req = request('PUT', url);
1280 if ('function' == typeof data) fn = data, data = null;
1281 if (data) req.send(data);
1282 if (fn) req.end(fn);
1283 return req;
1284};
1285
1286/**
1287 * Expose `request`.
1288 */
1289
1290module.exports = request;
1291
1292},{"emitter":5,"reduce":6}],5:[function(require,module,exports){
1293
1294/**
1295 * Expose `Emitter`.
1296 */
1297
1298module.exports = Emitter;
1299
1300/**
1301 * Initialize a new `Emitter`.
1302 *
1303 * @api public
1304 */
1305
1306function Emitter(obj) {
1307 if (obj) return mixin(obj);
1308};
1309
1310/**
1311 * Mixin the emitter properties.
1312 *
1313 * @param {Object} obj
1314 * @return {Object}
1315 * @api private
1316 */
1317
1318function mixin(obj) {
1319 for (var key in Emitter.prototype) {
1320 obj[key] = Emitter.prototype[key];
1321 }
1322 return obj;
1323}
1324
1325/**
1326 * Listen on the given `event` with `fn`.
1327 *
1328 * @param {String} event
1329 * @param {Function} fn
1330 * @return {Emitter}
1331 * @api public
1332 */
1333
1334Emitter.prototype.on =
1335Emitter.prototype.addEventListener = function(event, fn){
1336 this._callbacks = this._callbacks || {};
1337 (this._callbacks[event] = this._callbacks[event] || [])
1338 .push(fn);
1339 return this;
1340};
1341
1342/**
1343 * Adds an `event` listener that will be invoked a single
1344 * time then automatically removed.
1345 *
1346 * @param {String} event
1347 * @param {Function} fn
1348 * @return {Emitter}
1349 * @api public
1350 */
1351
1352Emitter.prototype.once = function(event, fn){
1353 var self = this;
1354 this._callbacks = this._callbacks || {};
1355
1356 function on() {
1357 self.off(event, on);
1358 fn.apply(this, arguments);
1359 }
1360
1361 on.fn = fn;
1362 this.on(event, on);
1363 return this;
1364};
1365
1366/**
1367 * Remove the given callback for `event` or all
1368 * registered callbacks.
1369 *
1370 * @param {String} event
1371 * @param {Function} fn
1372 * @return {Emitter}
1373 * @api public
1374 */
1375
1376Emitter.prototype.off =
1377Emitter.prototype.removeListener =
1378Emitter.prototype.removeAllListeners =
1379Emitter.prototype.removeEventListener = function(event, fn){
1380 this._callbacks = this._callbacks || {};
1381
1382 // all
1383 if (0 == arguments.length) {
1384 this._callbacks = {};
1385 return this;
1386 }
1387
1388 // specific event
1389 var callbacks = this._callbacks[event];
1390 if (!callbacks) return this;
1391
1392 // remove all handlers
1393 if (1 == arguments.length) {
1394 delete this._callbacks[event];
1395 return this;
1396 }
1397
1398 // remove specific handler
1399 var cb;
1400 for (var i = 0; i < callbacks.length; i++) {
1401 cb = callbacks[i];
1402 if (cb === fn || cb.fn === fn) {
1403 callbacks.splice(i, 1);
1404 break;
1405 }
1406 }
1407 return this;
1408};
1409
1410/**
1411 * Emit `event` with the given args.
1412 *
1413 * @param {String} event
1414 * @param {Mixed} ...
1415 * @return {Emitter}
1416 */
1417
1418Emitter.prototype.emit = function(event){
1419 this._callbacks = this._callbacks || {};
1420 var args = [].slice.call(arguments, 1)
1421 , callbacks = this._callbacks[event];
1422
1423 if (callbacks) {
1424 callbacks = callbacks.slice(0);
1425 for (var i = 0, len = callbacks.length; i < len; ++i) {
1426 callbacks[i].apply(this, args);
1427 }
1428 }
1429
1430 return this;
1431};
1432
1433/**
1434 * Return array of callbacks for `event`.
1435 *
1436 * @param {String} event
1437 * @return {Array}
1438 * @api public
1439 */
1440
1441Emitter.prototype.listeners = function(event){
1442 this._callbacks = this._callbacks || {};
1443 return this._callbacks[event] || [];
1444};
1445
1446/**
1447 * Check if this emitter has `event` handlers.
1448 *
1449 * @param {String} event
1450 * @return {Boolean}
1451 * @api public
1452 */
1453
1454Emitter.prototype.hasListeners = function(event){
1455 return !! this.listeners(event).length;
1456};
1457
1458},{}],6:[function(require,module,exports){
1459
1460/**
1461 * Reduce `arr` with `fn`.
1462 *
1463 * @param {Array} arr
1464 * @param {Function} fn
1465 * @param {Mixed} initial
1466 *
1467 * TODO: combatible error handling?
1468 */
1469
1470module.exports = function(arr, fn, initial){
1471 var idx = 0;
1472 var len = arr.length;
1473 var curr = arguments.length == 3
1474 ? initial
1475 : arr[idx++];
1476
1477 while (idx < len) {
1478 curr = fn.call(null, curr, arr[idx], ++idx, arr);
1479 }
1480
1481 return curr;
1482};
1483},{}]},{},[1])(1)
1484});