UNPKG

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