UNPKG

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