OLD | NEW |
| (Empty) |
1 | |
2 Polymer({ | |
3 is: 'iron-request', | |
4 | |
5 properties: { | |
6 | |
7 /** | |
8 * A reference to the XMLHttpRequest instance used to generate the | |
9 * network request. | |
10 * | |
11 * @attribute xhr | |
12 * @type XMLHttpRequest | |
13 * @default `new XMLHttpRequest` | |
14 */ | |
15 xhr: { | |
16 type: Object, | |
17 notify: true, | |
18 readOnly: true, | |
19 value: function() { | |
20 return new XMLHttpRequest(); | |
21 } | |
22 }, | |
23 | |
24 /** | |
25 * A reference to the parsed response body, if the `xhr` has completely | |
26 * resolved. | |
27 * | |
28 * @attribute response | |
29 * @type {*} | |
30 * @default null | |
31 */ | |
32 response: { | |
33 type: Object, | |
34 notify: true, | |
35 readOnly: true, | |
36 value: function() { | |
37 return null; | |
38 } | |
39 }, | |
40 | |
41 /** | |
42 * A promise that resolves when the `xhr` response comes back, or rejects | |
43 * if there is an error before the `xhr` completes. | |
44 * | |
45 * @attribute completes | |
46 * @type Promise | |
47 * @default `new Promise` | |
48 */ | |
49 completes: { | |
50 type: Object, | |
51 readOnly: true, | |
52 notify: true, | |
53 value: function() { | |
54 return new Promise(function (resolve, reject) { | |
55 this.resolveCompletes = resolve; | |
56 this.rejectCompletes = reject; | |
57 }.bind(this)); | |
58 } | |
59 }, | |
60 | |
61 /** | |
62 * An object that contains progress information emitted by the XHR if | |
63 * available. | |
64 * | |
65 * @attribute progress | |
66 * @type Object | |
67 * @default {} | |
68 */ | |
69 progress: { | |
70 type: Object, | |
71 notify: true, | |
72 readOnly: true, | |
73 value: function() { | |
74 return {}; | |
75 } | |
76 }, | |
77 | |
78 /** | |
79 * Aborted will be true if an abort of the request is attempted. | |
80 * | |
81 * @attribute aborted | |
82 * @type boolean | |
83 * @default false | |
84 */ | |
85 aborted: { | |
86 type: Boolean, | |
87 notify: true, | |
88 readOnly: true, | |
89 value: false, | |
90 } | |
91 }, | |
92 | |
93 /** | |
94 * Succeeded is true if the request succeeded. The request succeeded if the | |
95 * status code is greater-than-or-equal-to 200, and less-than 300. Also, | |
96 * the status code 0 is accepted as a success even though the outcome may | |
97 * be ambiguous. | |
98 * | |
99 * @return {boolean} | |
100 */ | |
101 get succeeded() { | |
102 var status = this.xhr.status || 0; | |
103 | |
104 // Note: if we are using the file:// protocol, the status code will be 0 | |
105 // for all outcomes (successful or otherwise). | |
106 return status === 0 || | |
107 (status >= 200 && status < 300); | |
108 }, | |
109 | |
110 /** | |
111 * Sends an HTTP request to the server and returns the XHR object. | |
112 * | |
113 * @param {{ | |
114 * url: string, | |
115 * method: (string|undefined), | |
116 * async: (boolean|undefined), | |
117 * body: (ArrayBuffer|ArrayBufferView|Blob|Document|FormData|null|string|u
ndefined), | |
118 * headers: (Object|undefined), | |
119 * handleAs: (string|undefined), | |
120 * withCredentials: (boolean|undefined)}} options - | |
121 * url The url to which the request is sent. | |
122 * method The HTTP method to use, default is GET. | |
123 * async By default, all requests are sent asynchronously. To send synch
ronous requests, | |
124 * set to true. | |
125 * body The content for the request body for POST method. | |
126 * headers HTTP request headers. | |
127 * handleAs The response type. Default is 'text'. | |
128 * withCredentials Whether or not to send credentials on the request. De
fault is false. | |
129 * @return {Promise} | |
130 */ | |
131 send: function (options) { | |
132 var xhr = this.xhr; | |
133 | |
134 if (xhr.readyState > 0) { | |
135 return null; | |
136 } | |
137 | |
138 xhr.addEventListener('readystatechange', function () { | |
139 if (xhr.readyState === 4 && !this.aborted) { | |
140 | |
141 if (!this.succeeded) { | |
142 this.rejectCompletes(new Error('The request failed with status code:
' + this.xhr.status)); | |
143 return; | |
144 } | |
145 | |
146 this._setResponse(this.parseResponse()); | |
147 this.resolveCompletes(this); | |
148 } | |
149 }.bind(this)); | |
150 | |
151 xhr.addEventListener('progress', function (progress) { | |
152 this._setProgress({ | |
153 lengthComputable: progress.lengthComputable, | |
154 loaded: progress.loaded, | |
155 total: progress.total | |
156 }); | |
157 }.bind(this)) | |
158 | |
159 xhr.addEventListener('error', function (error) { | |
160 this.rejectCompletes(error); | |
161 }.bind(this)); | |
162 | |
163 xhr.addEventListener('abort', function () { | |
164 this.rejectCompletes(new Error('Request aborted.')); | |
165 }.bind(this)); | |
166 | |
167 xhr.open( | |
168 options.method || 'GET', | |
169 options.url, | |
170 options.async !== false | |
171 ); | |
172 | |
173 if (options.headers) { | |
174 Object.keys(options.headers).forEach(function (requestHeader) { | |
175 xhr.setRequestHeader( | |
176 requestHeader, | |
177 options.headers[requestHeader] | |
178 ); | |
179 }, this); | |
180 } | |
181 | |
182 // In IE, `xhr.responseType` is an empty string when the response | |
183 // returns. Hence, caching it as `xhr._responseType`. | |
184 xhr.responseType = xhr._responseType = (options.handleAs || 'text'); | |
185 xhr.withCredentials = !!options.withCredentials; | |
186 | |
187 xhr.send(options.body); | |
188 | |
189 return this.completes; | |
190 }, | |
191 | |
192 /** | |
193 * Attempts to parse the response body of the XHR. If parsing succeeds, | |
194 * the value returned will be deserialized based on the `responseType` | |
195 * set on the XHR. | |
196 * | |
197 * @return {*} The parsed response, | |
198 * or undefined if there was an empty response or parsing failed. | |
199 */ | |
200 parseResponse: function () { | |
201 var xhr = this.xhr; | |
202 var responseType = this.xhr.responseType || | |
203 this.xhr._responseType; | |
204 // If we don't have a natural `xhr.responseType`, we prefer parsing | |
205 // `xhr.responseText` over returning `xhr.response`.. | |
206 var preferResponseText = !this.xhr.responseType; | |
207 | |
208 try { | |
209 switch (responseType) { | |
210 case 'json': | |
211 // If xhr.response is undefined, responseType `json` may | |
212 // not be supported. | |
213 if (preferResponseText || xhr.response === undefined) { | |
214 // If accessing `xhr.responseText` throws, responseType `json` | |
215 // is supported and the result is rightly `undefined`. | |
216 try { | |
217 xhr.responseText; | |
218 } catch (e) { | |
219 return xhr.response; | |
220 } | |
221 | |
222 // Otherwise, attempt to parse `xhr.responseText` as JSON. | |
223 if (xhr.responseText) { | |
224 return JSON.parse(xhr.responseText); | |
225 } | |
226 } | |
227 | |
228 return xhr.response; | |
229 case 'xml': | |
230 return xhr.responseXML; | |
231 case 'blob': | |
232 case 'document': | |
233 case 'arraybuffer': | |
234 return xhr.response; | |
235 case 'text': | |
236 default: | |
237 return xhr.responseText; | |
238 } | |
239 } catch (e) { | |
240 this.rejectCompletes(new Error('Could not parse response. ' + e.message)
); | |
241 } | |
242 }, | |
243 | |
244 /** | |
245 * Aborts the request. | |
246 */ | |
247 abort: function () { | |
248 this._setAborted(true); | |
249 this.xhr.abort(); | |
250 } | |
251 }); | |
OLD | NEW |