UNPKG

3.93 kBJavaScriptView Raw
1var _ = require('underscore');
2var xhr = require('xhr');
3var qs = require('qs');
4
5
6// Throw an error when a URL is needed, and none is supplied.
7var urlError = function () {
8 throw new Error('A "url" property or function must be specified');
9};
10
11
12module.exports = function (method, model, options) {
13 var type = methodMap[method];
14 var headers = {};
15
16 // Default options, unless specified.
17 _.defaults(options || (options = {}), {
18 emulateHTTP: false,
19 emulateJSON: false
20 });
21
22 // Default request options.
23 var params = {type: type};
24
25 // Ensure that we have a URL.
26 if (!options.url) {
27 params.url = _.result(model, 'url') || urlError();
28 }
29
30 // Ensure that we have the appropriate request data.
31 if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
32 params.json = options.attrs || model.toJSON(options);
33 }
34
35 // If passed a data param, we add it to the URL or body depending on request type
36 if (options.data && type === 'GET') {
37 // make sure we've got a '?'
38 params.url += _.contains(params.url, '?') ? '&' : '?';
39 params.url += qs.stringify(options.data);
40 }
41
42 // For older servers, emulate JSON by encoding the request into an HTML-form.
43 if (options.emulateJSON) {
44 headers['Content-Type'] = 'application/x-www-form-urlencoded';
45 params.body = params.json ? {model: params.json} : {};
46 delete params.json;
47 }
48
49 // For older servers, emulate HTTP by mimicking the HTTP method with `_method`
50 // And an `X-HTTP-Method-Override` header.
51 if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) {
52 params.type = 'POST';
53 if (options.emulateJSON) params.body._method = type;
54 headers['X-HTTP-Method-Override'] = type;
55 }
56
57 // When emulating JSON, we turn the body into a querystring.
58 // We do this later to let the emulateHTTP run its course.
59 if (options.emulateJSON) {
60 params.body = qs.stringify(params.body);
61 }
62
63 // Start setting ajaxConfig options (headers, xhrFields).
64 var ajaxConfig = (_.result(model, 'ajaxConfig') || {});
65
66 // Combine generated headers with user's headers.
67 if (ajaxConfig.headers) {
68 _.extend(headers, ajaxConfig.headers);
69 }
70 params.headers = headers;
71
72 //Set XDR for cross domain in IE8/9
73 if (ajaxConfig.useXDR) {
74 params.useXDR = true;
75 }
76
77 // Set raw xhr options.
78 if (ajaxConfig.xhrFields) {
79 var beforeSend = ajaxConfig.beforeSend;
80 params.beforeSend = function (req) {
81 for (var key in ajaxConfig.xhrFields) {
82 req[key] = ajaxConfig.xhrFields[key];
83 }
84 if (beforeSend) return beforeSend.apply(this, arguments);
85 };
86 params.xhrFields = ajaxConfig.xhrFields;
87 }
88
89 // Turn a jQuery.ajax formatted request into xhr compatible
90 params.method = params.type;
91
92 var ajaxSettings = _.extend(params, options);
93
94 // Make the request. The callback executes functions that are compatible
95 // With jQuery.ajax's syntax.
96 var request = options.xhr = xhr(ajaxSettings, function (err, resp, body) {
97 if (err && options.error) return options.error(resp, 'error', err.message);
98
99 // Parse body as JSON if a string.
100 if (body && typeof body === 'string') {
101 try {
102 body = JSON.parse(body);
103 } catch (e) {}
104 }
105 if (options.success) return options.success(body, 'success', resp);
106 });
107 model.trigger('request', model, request, options, ajaxSettings);
108 request.ajaxSettings = ajaxSettings;
109 return request;
110};
111
112// Map from CRUD to HTTP for our default `Backbone.sync` implementation.
113var methodMap = {
114 'create': 'POST',
115 'update': 'PUT',
116 'patch': 'PATCH',
117 'delete': 'DELETE',
118 'read': 'GET'
119};