Index: third_party/polymer/components/iron-ajax/iron-ajax.html |
diff --git a/third_party/polymer/components/iron-ajax/iron-ajax.html b/third_party/polymer/components/iron-ajax/iron-ajax.html |
new file mode 100644 |
index 0000000000000000000000000000000000000000..fa2fcc68fcde9e6de6f7eb2243b245d9787e16c9 |
--- /dev/null |
+++ b/third_party/polymer/components/iron-ajax/iron-ajax.html |
@@ -0,0 +1,533 @@ |
+<!-- |
+@license |
+Copyright (c) 2015 The Polymer Project Authors. All rights reserved. |
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt |
+The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt |
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt |
+Code distributed by Google as part of the polymer project is also |
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt |
+--> |
+ |
+<link rel="import" href="../polymer/polymer.html"> |
+<link rel="import" href="iron-request.html"> |
+ |
+<!-- |
+The `iron-ajax` element exposes network request functionality. |
+ |
+ <iron-ajax |
+ auto |
+ url="https://www.googleapis.com/youtube/v3/search" |
+ params='{"part":"snippet", "q":"polymer", "key": "YOUTUBE_API_KEY", "type": "video"}' |
+ handle-as="json" |
+ on-response="handleResponse" |
+ debounce-duration="300"></iron-ajax> |
+ |
+With `auto` set to `true`, the element performs a request whenever |
+its `url`, `params` or `body` properties are changed. Automatically generated |
+requests will be debounced in the case that multiple attributes are changed |
+sequentially. |
+ |
+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({ |
+ |
+ is: 'iron-ajax', |
+ |
+ /** |
+ * Fired when a request is sent. |
+ * |
+ * @event request |
+ * @event iron-ajax-request |
+ */ |
+ |
+ /** |
+ * Fired when a response is received. |
+ * |
+ * @event response |
+ * @event iron-ajax-response |
+ */ |
+ |
+ /** |
+ * Fired when an error is received. |
+ * |
+ * @event error |
+ * @event iron-ajax-error |
+ */ |
+ |
+ hostAttributes: { |
+ hidden: true |
+ }, |
+ |
+ properties: { |
+ /** |
+ * The URL target of the request. |
+ */ |
+ url: { |
+ type: String |
+ }, |
+ |
+ /** |
+ * An object that contains query parameters to be appended to the |
+ * 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, |
+ value: function() { |
+ return {}; |
+ } |
+ }, |
+ |
+ /** |
+ * The HTTP method to use such as 'GET', 'POST', 'PUT', or 'DELETE'. |
+ * Default is 'GET'. |
+ */ |
+ method: { |
+ type: String, |
+ value: 'GET' |
+ }, |
+ |
+ /** |
+ * HTTP request headers to send. |
+ * |
+ * Example: |
+ * |
+ * <iron-ajax |
+ * auto |
+ * url="http://somesite.com" |
+ * headers='{"X-Requested-With": "XMLHttpRequest"}' |
+ * 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, |
+ value: function() { |
+ return {}; |
+ } |
+ }, |
+ |
+ /** |
+ * 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. |
+ * |
+ * Varies the handling of the `body` param. |
+ */ |
+ contentType: { |
+ type: String, |
+ value: null |
+ }, |
+ |
+ /** |
+ * Body content to send with the request, typically used with "POST" |
+ * requests. |
+ * |
+ * 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` |
+ * |
+ * 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: Object, |
+ value: null |
+ }, |
+ |
+ /** |
+ * Toggle whether XHR is synchronous or asynchronous. Don't change this |
+ * to true unless You Know What You Are Doing™. |
+ */ |
+ sync: { |
+ type: Boolean, |
+ value: false |
+ }, |
+ |
+ /** |
+ * Specifies what data to store in the `response` property, and |
+ * to deliver as `event.detail.response` in `response` events. |
+ * |
+ * One of: |
+ * |
+ * `text`: uses `XHR.responseText`. |
+ * |
+ * `xml`: uses `XHR.responseXML`. |
+ * |
+ * `json`: uses `XHR.responseText` parsed as JSON. |
+ * |
+ * `arraybuffer`: uses `XHR.response`. |
+ * |
+ * `blob`: uses `XHR.response`. |
+ * |
+ * `document`: uses `XHR.response`. |
+ */ |
+ handleAs: { |
+ type: String, |
+ value: 'json' |
+ }, |
+ |
+ /** |
+ * Set the withCredentials flag on the request. |
+ */ |
+ withCredentials: { |
+ type: Boolean, |
+ value: false |
+ }, |
+ |
+ /** |
+ * 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. |
+ */ |
+ auto: { |
+ type: Boolean, |
+ value: false |
+ }, |
+ |
+ /** |
+ * If true, error messages will automatically be logged to the console. |
+ */ |
+ verbose: { |
+ type: Boolean, |
+ value: false |
+ }, |
+ |
+ /** |
+ * The most recent request made by this iron-ajax element. |
+ */ |
+ lastRequest: { |
+ type: Object, |
+ notify: true, |
+ readOnly: true |
+ }, |
+ |
+ /** |
+ * True while lastRequest is in flight. |
+ */ |
+ loading: { |
+ type: Boolean, |
+ notify: true, |
+ readOnly: true |
+ }, |
+ |
+ /** |
+ * 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, |
+ notify: true, |
+ readOnly: true |
+ }, |
+ |
+ /** |
+ * lastRequest's error, if any. |
+ * |
+ * @type {Object} |
+ */ |
+ lastError: { |
+ type: Object, |
+ notify: true, |
+ readOnly: true |
+ }, |
+ |
+ /** |
+ * An Array of all in-flight requests originating from this iron-ajax |
+ * element. |
+ */ |
+ activeRequests: { |
+ type: Array, |
+ notify: true, |
+ readOnly: true, |
+ value: function() { |
+ return []; |
+ } |
+ }, |
+ |
+ /** |
+ * Length of time in milliseconds to debounce multiple automatically generated requests. |
+ */ |
+ debounceDuration: { |
+ type: Number, |
+ value: 0, |
+ notify: true |
+ }, |
+ |
+ /** |
+ * 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: '' |
+ }, |
+ |
+ /** |
+ * By default, iron-ajax's events do not bubble. Setting this attribute will cause its |
+ * request and response events as well as its iron-ajax-request, -response, and -error |
+ * events to bubble to the window object. The vanilla error event never bubbles when |
+ * using shadow dom even if this.bubbles is true because a scoped flag is not passed with |
+ * it (first link) and because the shadow dom spec did not used to allow certain events, |
+ * including events named error, to leak outside of shadow trees (second link). |
+ * https://www.w3.org/TR/shadow-dom/#scoped-flag |
+ * https://www.w3.org/TR/2015/WD-shadow-dom-20151215/#events-that-are-not-leaked-into-ancestor-trees |
+ */ |
+ bubbles: { |
+ type: Boolean, |
+ value: false |
+ }, |
+ |
+ _boundHandleResponse: { |
+ type: Function, |
+ value: function() { |
+ return this._handleResponse.bind(this); |
+ } |
+ } |
+ }, |
+ |
+ observers: [ |
+ '_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; |
+ var value; |
+ |
+ for (param in this.params) { |
+ value = this.params[param]; |
+ param = window.encodeURIComponent(param); |
+ |
+ 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); |
+ } |
+ } |
+ |
+ 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; |
+ var url = this.url || ''; |
+ |
+ if (queryString) { |
+ var bindingChar = url.indexOf('?') >= 0 ? '&' : '?'; |
+ return url + bindingChar + queryString; |
+ } |
+ |
+ return 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 = {}; |
+ 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) { |
+ for (header in this.headers) { |
+ headers[header] = this.headers[header].toString(); |
+ } |
+ } |
+ |
+ 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 || '', |
+ method: this.method, |
+ headers: this.requestHeaders, |
+ body: this.body, |
+ async: !this.sync, |
+ handleAs: this.handleAs, |
+ jsonPrefix: this.jsonPrefix, |
+ withCredentials: this.withCredentials, |
+ timeout: this.timeout |
+ }; |
+ }, |
+ |
+ /** |
+ * Performs an AJAX request to the specified URL. |
+ * |
+ * @return {!IronRequestElement} |
+ */ |
+ generateRequest: function() { |
+ var request = /** @type {!IronRequestElement} */ (document.createElement('iron-request')); |
+ var requestOptions = this.toRequestOptions(); |
+ |
+ this.push('activeRequests', request); |
+ |
+ request.completes.then( |
+ this._boundHandleResponse |
+ ).catch( |
+ this._handleError.bind(this, request) |
+ ).then( |
+ this._discardRequest.bind(this, request) |
+ ); |
+ |
+ request.send(requestOptions); |
+ |
+ this._setLastRequest(request); |
+ this._setLoading(true); |
+ |
+ this.fire('request', { |
+ request: request, |
+ options: requestOptions |
+ }, {bubbles: this.bubbles}); |
+ |
+ this.fire('iron-ajax-request', { |
+ request: request, |
+ options: requestOptions |
+ }, {bubbles: this.bubbles}); |
+ |
+ return request; |
+ }, |
+ |
+ _handleResponse: function(request) { |
+ if (request === this.lastRequest) { |
+ this._setLastResponse(request.response); |
+ this._setLastError(null); |
+ this._setLoading(false); |
+ } |
+ this.fire('response', request, {bubbles: this.bubbles}); |
+ this.fire('iron-ajax-response', request, {bubbles: this.bubbles}); |
+ }, |
+ |
+ _handleError: function(request, error) { |
+ if (this.verbose) { |
+ Polymer.Base._error(error); |
+ } |
+ |
+ if (request === this.lastRequest) { |
+ this._setLastError({ |
+ request: request, |
+ error: error, |
+ status: request.xhr.status, |
+ statusText: request.xhr.statusText, |
+ response: request.xhr.response |
+ }); |
+ this._setLastResponse(null); |
+ this._setLoading(false); |
+ } |
+ |
+ // Tests fail if this goes after the normal this.fire('error', ...) |
+ this.fire('iron-ajax-error', { |
+ request: request, |
+ error: error |
+ }, {bubbles: this.bubbles}); |
+ |
+ this.fire('error', { |
+ request: request, |
+ error: error |
+ }, {bubbles: this.bubbles}); |
+ }, |
+ |
+ _discardRequest: function(request) { |
+ var requestIndex = this.activeRequests.indexOf(request); |
+ |
+ if (requestIndex > -1) { |
+ this.splice('activeRequests', requestIndex, 1); |
+ } |
+ }, |
+ |
+ _requestOptionsChanged: function() { |
+ this.debounce('generate-request', function() { |
+ if (this.url == null) { |
+ return; |
+ } |
+ |
+ if (this.auto) { |
+ this.generateRequest(); |
+ } |
+ }, this.debounceDuration); |
+ }, |
+ |
+ }); |
+</script> |