5 * Fired when a response is received.
11 * Fired when an error is received.
17 * Fired whenever a response or an error is received.
19 * @event core-complete
23 * The URL target of the request.
32 * Specifies what data to store in the `response` property, and
33 * to deliver as `event.response` in `response` events.
37 * `text`: uses `XHR.responseText`.
39 * `xml`: uses `XHR.responseXML`.
41 * `json`: uses `XHR.responseText` parsed as JSON.
43 * `arraybuffer`: uses `XHR.response`.
45 * `blob`: uses `XHR.response`.
47 * `document`: uses `XHR.response`.
56 * If true, automatically performs an Ajax request when either `url` or `params` changes.
65 * Parameters to send to the specified URL, as JSON.
74 * The response for the current request, or null if it hasn't
75 * completed yet or the request resulted in error.
84 * The error for the current request, or null if it hasn't
85 * completed yet or the request resulted in success.
94 * Whether the current request is currently loading.
103 * The progress of the current request.
105 * @attribute progress
106 * @type {loaded: number, total: number, lengthComputable: boolean}
112 * The HTTP method to use such as 'GET', 'POST', 'PUT', or 'DELETE'.
122 * HTTP request headers to send.
128 * url="http://somesite.com"
129 * headers='{"X-Requested-With": "XMLHttpRequest"}'
131 * on-core-response="{{handleResponse}}"></core-ajax>
140 * Optional raw body content to send when method === "POST".
144 * <core-ajax method="POST" auto url="http://somesite.com"
145 * body='{"foo":1, "bar":2}'>
155 * Content type to use when sending data.
157 * @attribute contentType
159 * @default 'application/x-www-form-urlencoded'
161 contentType: 'application/x-www-form-urlencoded',
164 * Set the withCredentials flag on the request.
166 * @attribute withCredentials
170 withCredentials: false,
173 * Additional properties to send to core-xhr.
175 * Can be set to an object containing default properties
176 * to send as arguments to the `core-xhr.request()` method
177 * which implements the low-level communication.
185 created: function() {
190 this.xhr = document.createElement('core-xhr');
193 receive: function(response, xhr) {
194 if (this.isSuccess(xhr)) {
195 this.processResponse(xhr);
197 this.processError(xhr);
202 isSuccess: function(xhr) {
203 var status = xhr.status || 0;
204 return !status || (status >= 200 && status < 300);
207 processResponse: function(xhr) {
208 var response = this.evalResponse(xhr);
209 if (xhr === this.activeRequest) {
210 this.response = response;
212 this.fire('core-response', {response: response, xhr: xhr});
215 processError: function(xhr) {
216 var response = xhr.status + ': ' + xhr.responseText;
217 if (xhr === this.activeRequest) {
218 this.error = response;
220 this.fire('core-error', {response: response, xhr: xhr});
223 processProgress: function(progress, xhr) {
224 if (xhr !== this.activeRequest) {
227 // We create a proxy object here because these fields
228 // on the progress event are readonly properties, which
229 // causes problems in common use cases (e.g. binding to
230 // <paper-progress> attributes).
231 var progressProxy = {
232 lengthComputable: progress.lengthComputable,
233 loaded: progress.loaded,
234 total: progress.total
236 this.progress = progressProxy;
239 complete: function(xhr) {
240 if (xhr === this.activeRequest) {
241 this.loading = false;
243 this.fire('core-complete', {response: xhr.status, xhr: xhr});
246 evalResponse: function(xhr) {
247 return this[(this.handleAs || 'text') + 'Handler'](xhr);
250 xmlHandler: function(xhr) {
251 return xhr.responseXML;
254 textHandler: function(xhr) {
255 return xhr.responseText;
258 jsonHandler: function(xhr) {
259 var r = xhr.responseText;
261 return JSON.parse(r);
263 console.warn('core-ajax caught an exception trying to parse response as JSON:');
264 console.warn('url:', this.url);
270 documentHandler: function(xhr) {
274 blobHandler: function(xhr) {
278 arraybufferHandler: function(xhr) {
282 urlChanged: function() {
283 if (!this.handleAs) {
284 var ext = String(this.url).split('.').pop();
287 this.handleAs = 'json';
294 paramsChanged: function() {
298 bodyChanged: function() {
302 autoChanged: function() {
306 // TODO(sorvell): multiple side-effects could call autoGo
307 // during one micro-task, use a job to have only one action
311 this.goJob = this.job(this.goJob, this.go, 0);
316 * Performs an Ajax request to the specified URL.
321 var args = this.xhrArgs || {};
322 // TODO(sjmiles): we may want XHR to default to POST if body is set
323 args.body = this.body || args.body;
324 args.params = this.params || args.params;
325 if (args.params && typeof(args.params) == 'string') {
326 args.params = JSON.parse(args.params);
328 args.headers = this.headers || args.headers || {};
329 if (args.headers && typeof(args.headers) == 'string') {
330 args.headers = JSON.parse(args.headers);
332 var hasContentType = Object.keys(args.headers).some(function (header) {
333 return header.toLowerCase() === 'content-type';
335 // No Content-Type should be specified if sending `FormData`.
336 // The UA must set the Content-Type w/ a calculated multipart boundary ID.
337 if (args.body instanceof FormData) {
338 delete args.headers['Content-Type'];
340 else if (!hasContentType && this.contentType) {
341 args.headers['Content-Type'] = this.contentType;
343 if (this.handleAs === 'arraybuffer' || this.handleAs === 'blob' ||
344 this.handleAs === 'document') {
345 args.responseType = this.handleAs;
347 args.withCredentials = this.withCredentials;
348 args.callback = this.receive.bind(this);
350 args.method = this.method;
352 this.response = this.error = this.progress = null;
353 this.activeRequest = args.url && this.xhr.request(args);
354 if (this.activeRequest) {
356 var activeRequest = this.activeRequest;
357 // IE < 10 doesn't support progress events.
358 if ('onprogress' in activeRequest) {
359 this.activeRequest.addEventListener(
362 this.processProgress(progress, activeRequest);
363 }.bind(this), false);
366 lengthComputable: false,
370 return this.activeRequest;