UNPKG

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