diff options
Diffstat (limited to 'catapult/third_party/polymer/components/iron-ajax/iron-ajax.html')
-rw-r--r-- | catapult/third_party/polymer/components/iron-ajax/iron-ajax.html | 580 |
1 files changed, 580 insertions, 0 deletions
diff --git a/catapult/third_party/polymer/components/iron-ajax/iron-ajax.html b/catapult/third_party/polymer/components/iron-ajax/iron-ajax.html new file mode 100644 index 00000000..1a9dddf6 --- /dev/null +++ b/catapult/third_party/polymer/components/iron-ajax/iron-ajax.html @@ -0,0 +1,580 @@ +<!-- +@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 before a request is sent. + * + * @event iron-ajax-presend + */ + + /** + * 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 + }, + + /** + * Changes the [`completes`](iron-request#property-completes) promise chain + * from `generateRequest` to reject with an object + * containing the original request, as well an error message. + * If false (default), the promise rejects with an error message only. + */ + rejectWithRequest: { + 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 (typeof this.headers === '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, + rejectWithRequest: this.rejectWithRequest, + }; + }, + + /** + * 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) + ); + + var evt = this.fire('iron-ajax-presend', { + request: request, + options: requestOptions + }, {bubbles: this.bubbles, cancelable: true}); + + if (evt.defaultPrevented) { + request.abort(); + request.rejectCompletes(request); + return request; + } + + request.send(requestOptions); + + this._setLastRequest(request); + this._setLoading(true); + + this.fire('request', { + request: request, + options: requestOptions + }, { + bubbles: this.bubbles, + composed: true + }); + + this.fire('iron-ajax-request', { + request: request, + options: requestOptions + }, { + bubbles: this.bubbles, + composed: true + }); + + 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, + composed: true + }); + this.fire('iron-ajax-response', request, { + bubbles: this.bubbles, + composed: true + }); + }, + + _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, + composed: true + }); + + this.fire('error', { + request: request, + error: error + }, { + bubbles: this.bubbles, + composed: true + }); + }, + + _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> |