UNPKG

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