Index: polymer_1.2.3/iron-ajax/iron-ajax.html |
diff --git a/polymer_1.0.4/bower_components/iron-ajax/iron-ajax.html b/polymer_1.2.3/iron-ajax/iron-ajax.html |
similarity index 52% |
copy from polymer_1.0.4/bower_components/iron-ajax/iron-ajax.html |
copy to polymer_1.2.3/iron-ajax/iron-ajax.html |
index 47d202b4151a946307021c0295f9a38eda192972..c6a189e4358fbe59ab54d97813728c33eb6fdd86 100644 |
--- a/polymer_1.0.4/bower_components/iron-ajax/iron-ajax.html |
+++ b/polymer_1.2.3/iron-ajax/iron-ajax.html |
@@ -30,9 +30,13 @@ Note: The `params` attribute must be double quoted JSON. |
You can trigger a request explicitly by calling `generateRequest` on the |
element. |
+ |
+@demo demo/index.html |
+@hero hero.svg |
--> |
<script> |
+ 'use strict'; |
Polymer({ |
@@ -56,18 +60,23 @@ element. |
* @event error |
*/ |
+ hostAttributes: { |
+ hidden: true |
+ }, |
+ |
properties: { |
/** |
* The URL target of the request. |
*/ |
url: { |
- type: String, |
- value: '' |
+ type: String |
}, |
/** |
* An object that contains query parameters to be appended to the |
- * specified `url` when generating a request. |
+ * specified `url` when generating a request. If you wish to set the body |
+ * content when making a POST request, you should use the `body` property |
+ * instead. |
*/ |
params: { |
type: Object, |
@@ -94,8 +103,10 @@ element. |
* auto |
* url="http://somesite.com" |
* headers='{"X-Requested-With": "XMLHttpRequest"}' |
- * handle-as="json" |
- * last-response-changed="{{handleResponse}}"></iron-ajax> |
+ * handle-as="json"></iron-ajax> |
+ * |
+ * Note: setting a `Content-Type` header here will override the value |
+ * specified by the `contentType` property of this element. |
*/ |
headers: { |
type: Object, |
@@ -105,27 +116,37 @@ element. |
}, |
/** |
- * Content type to use when sending data. If the contenttype is set |
- * and a `Content-Type` header is specified in the `headers` attribute, |
- * the `headers` attribute value will take precedence. |
+ * Content type to use when sending data. If the `contentType` property |
+ * is set and a `Content-Type` header is specified in the `headers` |
+ * property, the `headers` property value will take precedence. |
*/ |
contentType: { |
type: String, |
- value: 'application/x-www-form-urlencoded' |
+ value: null |
}, |
/** |
- * Optional raw body content to send when method === "POST". |
+ * Body content to send with the request, typically used with "POST" |
+ * requests. |
* |
- * Example: |
+ * If body is a string it will be sent unmodified. |
+ * |
+ * If Content-Type is set to a value listed below, then |
+ * the body will be encoded accordingly. |
+ * |
+ * * `content-type="application/json"` |
+ * * body is encoded like `{"foo":"bar baz","x":1}` |
+ * * `content-type="application/x-www-form-urlencoded"` |
+ * * body is encoded like `foo=bar+baz&x=1` |
* |
- * <iron-ajax method="POST" auto url="http://somesite.com" |
- * body='{"foo":1, "bar":2}'> |
- * </iron-ajax> |
+ * Otherwise the body will be passed to the browser unmodified, and it |
+ * will handle any encoding (e.g. for FormData, Blob, ArrayBuffer). |
+ * |
+ * @type (ArrayBuffer|ArrayBufferView|Blob|Document|FormData|null|string|undefined|Object) |
*/ |
body: { |
- type: String, |
- value: '' |
+ type: Object, |
+ value: null |
}, |
/** |
@@ -139,7 +160,7 @@ element. |
/** |
* Specifies what data to store in the `response` property, and |
- * to deliver as `event.response` in `response` events. |
+ * to deliver as `event.detail.response` in `response` events. |
* |
* One of: |
* |
@@ -169,6 +190,14 @@ element. |
}, |
/** |
+ * Set the timeout flag on the request. |
+ */ |
+ timeout: { |
+ type: Number, |
+ value: 0 |
+ }, |
+ |
+ /** |
* If true, automatically performs an Ajax request when either `url` or |
* `params` changes. |
*/ |
@@ -186,29 +215,34 @@ element. |
}, |
/** |
- * Will be set to true if there is at least one in-flight request |
- * associated with this iron-ajax element. |
+ * The most recent request made by this iron-ajax element. |
*/ |
- loading: { |
- type: Boolean, |
+ lastRequest: { |
+ type: Object, |
notify: true, |
readOnly: true |
}, |
/** |
- * Will be set to the most recent request made by this iron-ajax element. |
+ * True while lastRequest is in flight. |
*/ |
- lastRequest: { |
- type: Object, |
+ loading: { |
+ type: Boolean, |
notify: true, |
readOnly: true |
}, |
/** |
- * Will be set to the most recent response received by a request |
- * that originated from this iron-ajax element. The type of the response |
- * is determined by the value of `handleas` at the time that the request |
- * was generated. |
+ * lastRequest's response. |
+ * |
+ * Note that lastResponse and lastError are set when lastRequest finishes, |
+ * so if loading is true, then lastResponse and lastError will correspond |
+ * to the result of the previous request. |
+ * |
+ * The type of the response is determined by the value of `handleAs` at |
+ * the time that the request was generated. |
+ * |
+ * @type {Object} |
*/ |
lastResponse: { |
type: Object, |
@@ -217,8 +251,9 @@ element. |
}, |
/** |
- * Will be set to the most recent error that resulted from a request |
- * that originated from this iron-ajax element. |
+ * lastRequest's error, if any. |
+ * |
+ * @type {Object} |
*/ |
lastError: { |
type: Object, |
@@ -235,12 +270,12 @@ element. |
notify: true, |
readOnly: true, |
value: function() { |
- this._setActiveRequests([]); |
+ return []; |
} |
}, |
/** |
- * Length of time in milliseconds to debounce multiple requests. |
+ * Length of time in milliseconds to debounce multiple automatically generated requests. |
*/ |
debounceDuration: { |
type: Number, |
@@ -248,26 +283,39 @@ element. |
notify: true |
}, |
- _boundHandleResponse: { |
- type: Function, |
- value: function() { |
- return this.handleResponse.bind(this); |
- } |
+ /** |
+ * Prefix to be stripped from a JSON response before parsing it. |
+ * |
+ * In order to prevent an attack using CSRF with Array responses |
+ * (http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx/) |
+ * many backends will mitigate this by prefixing all JSON response bodies |
+ * with a string that would be nonsensical to a JavaScript parser. |
+ * |
+ */ |
+ jsonPrefix: { |
+ type: String, |
+ value: '' |
}, |
- _boundDiscardRequest: { |
+ _boundHandleResponse: { |
type: Function, |
value: function() { |
- return this.discardRequest.bind(this); |
+ return this._handleResponse.bind(this); |
} |
} |
}, |
observers: [ |
- 'requestOptionsChanged(url, method, params, headers,' + |
- 'contentType, body, sync, handleAs, withCredentials, auto)' |
+ '_requestOptionsChanged(url, method, params.*, headers, contentType, ' + |
+ 'body, sync, handleAs, jsonPrefix, withCredentials, timeout, auto)' |
], |
+ /** |
+ * The query string that should be appended to the `url`, serialized from |
+ * the current value of `params`. |
+ * |
+ * @return {string} |
+ */ |
get queryString () { |
var queryParts = []; |
var param; |
@@ -277,30 +325,53 @@ element. |
value = this.params[param]; |
param = window.encodeURIComponent(param); |
- if (value !== null) { |
- param += '=' + window.encodeURIComponent(value); |
+ if (Array.isArray(value)) { |
+ for (var i = 0; i < value.length; i++) { |
+ queryParts.push(param + '=' + window.encodeURIComponent(value[i])); |
+ } |
+ } else if (value !== null) { |
+ queryParts.push(param + '=' + window.encodeURIComponent(value)); |
+ } else { |
+ queryParts.push(param); |
} |
- |
- queryParts.push(param); |
} |
return queryParts.join('&'); |
}, |
+ /** |
+ * The `url` with query string (if `params` are specified), suitable for |
+ * providing to an `iron-request` instance. |
+ * |
+ * @return {string} |
+ */ |
get requestUrl() { |
var queryString = this.queryString; |
if (queryString) { |
- return this.url + '?' + queryString; |
+ var bindingChar = this.url.indexOf('?') >= 0 ? '&' : '?'; |
+ return this.url + bindingChar + queryString; |
} |
return this.url; |
}, |
+ /** |
+ * An object that maps header names to header values, first applying the |
+ * the value of `Content-Type` and then overlaying the headers specified |
+ * in the `headers` property. |
+ * |
+ * @return {Object} |
+ */ |
get requestHeaders() { |
- var headers = { |
- 'content-type': this.contentType |
- }; |
+ var headers = {}; |
+ var contentType = this.contentType; |
+ if (contentType == null && (typeof this.body === 'string')) { |
+ contentType = 'application/x-www-form-urlencoded'; |
+ } |
+ if (contentType) { |
+ headers['content-type'] = contentType; |
+ } |
var header; |
if (this.headers instanceof Object) { |
@@ -312,37 +383,41 @@ element. |
return headers; |
}, |
+ /** |
+ * Request options suitable for generating an `iron-request` instance based |
+ * on the current state of the `iron-ajax` instance's properties. |
+ * |
+ * @return {{ |
+ * url: string, |
+ * method: (string|undefined), |
+ * async: (boolean|undefined), |
+ * body: (ArrayBuffer|ArrayBufferView|Blob|Document|FormData|null|string|undefined|Object), |
+ * headers: (Object|undefined), |
+ * handleAs: (string|undefined), |
+ * jsonPrefix: (string|undefined), |
+ * withCredentials: (boolean|undefined)}} |
+ */ |
toRequestOptions: function() { |
return { |
- url: this.requestUrl, |
+ url: this.requestUrl || '', |
method: this.method, |
headers: this.requestHeaders, |
body: this.body, |
async: !this.sync, |
handleAs: this.handleAs, |
- withCredentials: this.withCredentials |
+ jsonPrefix: this.jsonPrefix, |
+ withCredentials: this.withCredentials, |
+ timeout: this.timeout |
}; |
}, |
- requestOptionsChanged: function() { |
- this.debounce('generate-request', function() { |
- if (!this.url && this.url !== '') { |
- return; |
- } |
- |
- if (this.auto) { |
- this.generateRequest(); |
- } |
- }, this.debounceDuration); |
- }, |
- |
/** |
* Performs an AJAX request to the specified URL. |
* |
- * @method generateRequest |
+ * @return {!IronRequestElement} |
*/ |
generateRequest: function() { |
- var request = document.createElement('iron-request'); |
+ var request = /** @type {!IronRequestElement} */ (document.createElement('iron-request')); |
var requestOptions = this.toRequestOptions(); |
this.activeRequests.push(request); |
@@ -350,49 +425,71 @@ element. |
request.completes.then( |
this._boundHandleResponse |
).catch( |
- this.handleError.bind(this, request) |
+ this._handleError.bind(this, request) |
).then( |
- this._boundDiscardRequest |
+ this._discardRequest.bind(this, request) |
); |
request.send(requestOptions); |
this._setLastRequest(request); |
+ this._setLoading(true); |
this.fire('request', { |
request: request, |
options: requestOptions |
- }); |
+ }, {bubbles: false}); |
return request; |
}, |
- handleResponse: function(request) { |
- this._setLastResponse(request.response); |
- this.fire('response', request); |
+ _handleResponse: function(request) { |
+ if (request === this.lastRequest) { |
+ this._setLastResponse(request.response); |
+ this._setLastError(null); |
+ this._setLoading(false); |
+ } |
+ this.fire('response', request, {bubbles: false}); |
}, |
- handleError: function(request, error) { |
+ _handleError: function(request, error) { |
if (this.verbose) { |
console.error(error); |
} |
- this._setLastError({ |
- request: request, |
- error: error |
- }); |
+ if (request === this.lastRequest) { |
+ this._setLastError({ |
+ request: request, |
+ error: error |
+ }); |
+ this._setLastResponse(null); |
+ this._setLoading(false); |
+ } |
this.fire('error', { |
request: request, |
error: error |
- }); |
+ }, {bubbles: false}); |
}, |
- discardRequest: function(request) { |
+ _discardRequest: function(request) { |
var requestIndex = this.activeRequests.indexOf(request); |
- if (requestIndex > 0) { |
+ if (requestIndex > -1) { |
this.activeRequests.splice(requestIndex, 1); |
} |
- } |
+ }, |
+ |
+ _requestOptionsChanged: function() { |
+ this.debounce('generate-request', function() { |
+ if (this.url == null) { |
+ return; |
+ } |
+ |
+ if (this.auto) { |
+ this.generateRequest(); |
+ } |
+ }, this.debounceDuration); |
+ }, |
+ |
}); |
</script> |