| Index: third_party/polymer/components/iron-form/iron-form.html
 | 
| diff --git a/third_party/polymer/components/iron-form/iron-form.html b/third_party/polymer/components/iron-form/iron-form.html
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..4e39f670fadb300e4c7cfe4619c7ae07ad730082
 | 
| --- /dev/null
 | 
| +++ b/third_party/polymer/components/iron-form/iron-form.html
 | 
| @@ -0,0 +1,513 @@
 | 
| +<!--
 | 
| +@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-ajax/iron-ajax.html">
 | 
| +
 | 
| +<script>
 | 
| +/*
 | 
| +`<iron-form>` is an HTML `<form>` element that can validate and submit any custom
 | 
| +elements that implement `Polymer.IronFormElementBehavior`, as well as any
 | 
| +native HTML elements. For more information on which attributes are
 | 
| +available on the native form element, see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form
 | 
| +
 | 
| +It supports both `get` and `post` methods, and uses an `iron-ajax` element to
 | 
| +submit the form data to the action URL.
 | 
| +
 | 
| +  Example:
 | 
| +
 | 
| +    <form is="iron-form" id="form" method="post" action="/form/handler">
 | 
| +      <paper-input name="name" label="name"></paper-input>
 | 
| +      <input name="address">
 | 
| +      ...
 | 
| +    </form>
 | 
| +
 | 
| +By default, a native `<button>` element will submit this form. However, if you
 | 
| +want to submit it from a custom element's click handler, you need to explicitly
 | 
| +call the form's `submit` method.
 | 
| +
 | 
| +  Example:
 | 
| +
 | 
| +    <paper-button raised onclick="submitForm()">Submit</paper-button>
 | 
| +
 | 
| +    function submitForm() {
 | 
| +      document.getElementById('form').submit();
 | 
| +    }
 | 
| +
 | 
| +To customize the request sent to the server, you can listen to the `iron-form-presubmit`
 | 
| +event, and modify the form's[`iron-ajax`](https://elements.polymer-project.org/elements/iron-ajax)
 | 
| +object. However, If you want to not use `iron-ajax` at all, you can cancel the
 | 
| +event and do your own custom submission:
 | 
| +
 | 
| +  Example of modifying the request, but still using the build-in form submission:
 | 
| +
 | 
| +    form.addEventListener('iron-form-presubmit', function() {
 | 
| +      this.request.method = 'put';
 | 
| +      this.request.params = someCustomParams;
 | 
| +    });
 | 
| +
 | 
| +  Example of bypassing the build-in form submission:
 | 
| +
 | 
| +    form.addEventListener('iron-form-presubmit', function(event) {
 | 
| +      event.preventDefault();
 | 
| +      var firebase = new Firebase(form.getAttribute('action'));
 | 
| +      firebase.set(form.serialize());
 | 
| +    });
 | 
| +
 | 
| +@demo demo/index.html
 | 
| +*/
 | 
| +  Polymer({
 | 
| +
 | 
| +    is: 'iron-form',
 | 
| +
 | 
| +    extends: 'form',
 | 
| +
 | 
| +    properties: {
 | 
| +      /**
 | 
| +       * By default, the form will display the browser's native validation
 | 
| +       * UI (i.e. popup bubbles and invalid styles on invalid fields). You can
 | 
| +       * manually disable this; however, if you do, note that you will have to
 | 
| +       * manually style invalid *native* HTML fields yourself, as you are
 | 
| +       * explicitly preventing the native form from doing so.
 | 
| +       */
 | 
| +      disableNativeValidationUi: {
 | 
| +        type: Boolean,
 | 
| +        value: false
 | 
| +      },
 | 
| +
 | 
| +      /**
 | 
| +      * Set the withCredentials flag when sending data.
 | 
| +      */
 | 
| +      withCredentials: {
 | 
| +        type: Boolean,
 | 
| +        value: false
 | 
| +      },
 | 
| +
 | 
| +      /**
 | 
| +       * 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.
 | 
| +       * If Content-Type is set to a value listed below, then
 | 
| +       * the `body` (typically used with POST requests) 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`
 | 
| +       */
 | 
| +      contentType: {
 | 
| +        type: String,
 | 
| +        value: "application/x-www-form-urlencoded"
 | 
| +      },
 | 
| +
 | 
| +      /**
 | 
| +      * HTTP request headers to send.
 | 
| +      *
 | 
| +      * 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 {};
 | 
| +        }
 | 
| +      },
 | 
| +
 | 
| +      /**
 | 
| +      * iron-ajax request object used to submit the form.
 | 
| +      */
 | 
| +      request: {
 | 
| +        type: Object,
 | 
| +      }
 | 
| +    },
 | 
| +
 | 
| +    /**
 | 
| +     * Fired if the form cannot be submitted because it's invalid.
 | 
| +     *
 | 
| +     * @event iron-form-invalid
 | 
| +     */
 | 
| +
 | 
| +    /**
 | 
| +     * Fired before the form is submitted.
 | 
| +     *
 | 
| +     * @event iron-form-presubmit
 | 
| +     */
 | 
| +
 | 
| +    /**
 | 
| +     * Fired after the form is submitted.
 | 
| +     *
 | 
| +     * @event iron-form-submit
 | 
| +     */
 | 
| +
 | 
| +     /**
 | 
| +      * Fired after the form is reset.
 | 
| +      *
 | 
| +      * @event iron-form-reset
 | 
| +      */
 | 
| +
 | 
| +    /**
 | 
| +    * Fired after the form is submitted and a response is received. An
 | 
| +    * IronRequestElement is included as the event.detail object.
 | 
| +    *
 | 
| +    * @event iron-form-response
 | 
| +    */
 | 
| +
 | 
| +    /**
 | 
| +     * Fired after the form is submitted and an error is received. An
 | 
| +     * IronRequestElement is included as the event.detail object.
 | 
| +     *
 | 
| +     * @event iron-form-error
 | 
| +     */
 | 
| +    listeners: {
 | 
| +      'iron-form-element-register': '_registerElement',
 | 
| +      'iron-form-element-unregister': '_unregisterElement',
 | 
| +      'submit': '_onSubmit',
 | 
| +      'reset': '_onReset'
 | 
| +    },
 | 
| +
 | 
| +    registered: function() {
 | 
| +      // Dear reader: I apologize for what you're about to experience. You see,
 | 
| +      // Safari does not respect `required` on input elements, so it never
 | 
| +      // has any browser validation bubbles to show. And we have to feature
 | 
| +      // detect that, since we rely on the form submission to do the right thing.
 | 
| +      // See http://caniuse.com/#search=required.
 | 
| +
 | 
| +      // Create a fake form, with an invalid input. If it gets submitted, it's Safari.
 | 
| +      var form = document.createElement('form');
 | 
| +      var input = document.createElement('input');
 | 
| +      input.setAttribute('required', 'true');
 | 
| +      form.appendChild(input);
 | 
| +
 | 
| +      // If you call submit(), the form doesn't actually fire a submit event,
 | 
| +      // so you can't intercept it and cancel it. The event is only fired
 | 
| +      // from the magical button click submission.
 | 
| +      // See http://wayback.archive.org/web/20090323062817/http://blogs.vertigosoftware.com/snyholm/archive/2006/09/27/3788.aspx.
 | 
| +      var button = document.createElement('input');
 | 
| +      button.setAttribute('type', 'submit');
 | 
| +      form.appendChild(button);
 | 
| +
 | 
| +      Polymer.clientSupportsFormValidationUI = true;
 | 
| +      form.addEventListener('submit', function(event) {
 | 
| +        // Oh good! We don't handle `required` correctly.
 | 
| +        Polymer.clientSupportsFormValidationUI = false;
 | 
| +        event.preventDefault();
 | 
| +      });
 | 
| +      button.click();
 | 
| +    },
 | 
| +
 | 
| +    ready: function() {
 | 
| +      // Object that handles the ajax form submission request.
 | 
| +      this.request = document.createElement('iron-ajax');
 | 
| +      this.request.addEventListener('response', this._handleFormResponse.bind(this));
 | 
| +      this.request.addEventListener('error', this._handleFormError.bind(this));
 | 
| +
 | 
| +      // Holds all the custom elements registered with this form.
 | 
| +      this._customElements = [];
 | 
| +      // Holds the initial values of the custom elements registered with this form.
 | 
| +      this._customElementsInitialValues = [];
 | 
| +    },
 | 
| +
 | 
| +    /**
 | 
| +     * Submits the form.
 | 
| +     */
 | 
| +    submit: function() {
 | 
| +      if (!this.noValidate && !this.validate()) {
 | 
| +        // In order to trigger the native browser invalid-form UI, we need
 | 
| +        // to do perform a fake form submit.
 | 
| +        if (Polymer.clientSupportsFormValidationUI && !this.disableNativeValidationUi) {
 | 
| +          this._doFakeSubmitForValidation();
 | 
| +        }
 | 
| +        this.fire('iron-form-invalid');
 | 
| +        return;
 | 
| +      }
 | 
| +
 | 
| +      var json = this.serialize();
 | 
| +
 | 
| +      // Native forms can also index elements magically by their name (can't make
 | 
| +      // this up if I tried) so we need to get the correct attributes, not the
 | 
| +      // elements with those names.
 | 
| +      this.request.url = this.getAttribute('action');
 | 
| +      this.request.method = this.getAttribute('method');
 | 
| +      this.request.contentType = this.contentType;
 | 
| +      this.request.withCredentials = this.withCredentials;
 | 
| +      this.request.headers = this.headers;
 | 
| +
 | 
| +      if (this.request.method.toUpperCase() === 'POST') {
 | 
| +        this.request.body = json;
 | 
| +      } else {
 | 
| +        this.request.params = json;
 | 
| +      }
 | 
| +
 | 
| +      // Allow for a presubmit hook
 | 
| +      var event = this.fire('iron-form-presubmit', {}, {cancelable: true});
 | 
| +      if(!event.defaultPrevented) {
 | 
| +        this.request.generateRequest();
 | 
| +        this.fire('iron-form-submit', json);
 | 
| +      }
 | 
| +    },
 | 
| +
 | 
| +    /**
 | 
| +     * Handler that is called when the native form fires a `submit` event
 | 
| +     *
 | 
| +     * @param {Event} event A `submit` event.
 | 
| +     */
 | 
| +    _onSubmit: function(event) {
 | 
| +      this.submit();
 | 
| +
 | 
| +      // Don't perform a page refresh.
 | 
| +      if (event) {
 | 
| +        event.preventDefault();
 | 
| +      }
 | 
| +
 | 
| +      return false;
 | 
| +    },
 | 
| +
 | 
| +    /**
 | 
| +     * Handler that is called when the native form fires a `reset` event
 | 
| +     *
 | 
| +     * @param {Event} event A `reset` event.
 | 
| +     */
 | 
| +    _onReset: function(event) {
 | 
| +      this._resetCustomElements();
 | 
| +    },
 | 
| +
 | 
| +    /**
 | 
| +     * Returns a json object containing name/value pairs for all the registered
 | 
| +     * custom components and native elements of the form. If there are elements
 | 
| +     * with duplicate names, then their values will get aggregated into an
 | 
| +     * array of values.
 | 
| +     *
 | 
| +     * @return {!Object}
 | 
| +     */
 | 
| +    serialize: function() {
 | 
| +      var json = {};
 | 
| +
 | 
| +      function addSerializedElement(name, value) {
 | 
| +        // If the name doesn't exist, add it. Otherwise, serialize it to
 | 
| +        // an array,
 | 
| +        if (!json[name]) {
 | 
| +          json[name] = value;
 | 
| +        } else {
 | 
| +          if (!Array.isArray(json[name])) {
 | 
| +            json[name] = [json[name]];
 | 
| +          }
 | 
| +          json[name].push(value);
 | 
| +        }
 | 
| +      }
 | 
| +
 | 
| +      // Go through all of the registered custom components.
 | 
| +      for (var el, i = 0; el = this._customElements[i], i < this._customElements.length; i++) {
 | 
| +        // If this custom element is inside a custom element that has already
 | 
| +        // registered to this form, skip it.
 | 
| +        if (!this._isChildOfRegisteredParent(el, true) && this._useValue(el)) {
 | 
| +          addSerializedElement(el.name, el.value);
 | 
| +        }
 | 
| +      }
 | 
| +
 | 
| +      // Also go through the form's native elements.
 | 
| +      for (var el, i = 0; el = this.elements[i], i < this.elements.length; i++) {
 | 
| +        // If this native element is inside a custom element that has already
 | 
| +        // registered to this form, skip it.
 | 
| +        if (this._isChildOfRegisteredParent(el, true) || !this._useValue(el)) {
 | 
| +          continue;
 | 
| +        }
 | 
| +
 | 
| +        // A <select multiple> has an array of values.
 | 
| +        if (el.tagName.toLowerCase() === 'select' && el.multiple) {
 | 
| +          for (var o = 0; o < el.options.length; o++) {
 | 
| +            if (el.options[o].selected) {
 | 
| +              addSerializedElement(el.name, el.options[o].value);
 | 
| +            }
 | 
| +          }
 | 
| +        } else {
 | 
| +          addSerializedElement(el.name, el.value);
 | 
| +        }
 | 
| +      }
 | 
| +
 | 
| +      return json;
 | 
| +    },
 | 
| +
 | 
| +    _handleFormResponse: function (event) {
 | 
| +      this.fire('iron-form-response', event.detail);
 | 
| +    },
 | 
| +
 | 
| +    _handleFormError: function (event) {
 | 
| +      this.fire('iron-form-error', event.detail);
 | 
| +    },
 | 
| +
 | 
| +    _registerElement: function(e) {
 | 
| +      // Get the actual element that fired the event
 | 
| +      var element = Polymer.dom(e).rootTarget;
 | 
| +
 | 
| +      element._parentForm = this;
 | 
| +      this._customElements.push(element);
 | 
| +
 | 
| +      // Save the original value of this input.
 | 
| +      this._customElementsInitialValues.push(
 | 
| +          this._usesCheckedInsteadOfValue(element) ? element.checked : element.value);
 | 
| +    },
 | 
| +
 | 
| +    _unregisterElement: function(e) {
 | 
| +      var target = e.detail.target;
 | 
| +      if (target) {
 | 
| +        var index = this._customElements.indexOf(target);
 | 
| +        if (index > -1) {
 | 
| +          this._customElements.splice(index, 1);
 | 
| +          this._customElementsInitialValues.splice(index, 1);
 | 
| +        }
 | 
| +      }
 | 
| +    },
 | 
| +
 | 
| +    /**
 | 
| +     * Validates all the required elements (custom and native) in the form.
 | 
| +     * @return {boolean} True if all the elements are valid.
 | 
| +     */
 | 
| +    validate: function() {
 | 
| +      var valid = true;
 | 
| +
 | 
| +      // Validate all the custom elements.
 | 
| +      var validatable;
 | 
| +      for (var el, i = 0; el = this._customElements[i], i < this._customElements.length; i++) {
 | 
| +        if (!this._isChildOfRegisteredParent(el, false) && !el.disabled) {
 | 
| +          validatable = /** @type {{validate: (function() : boolean)}} */ (el);
 | 
| +          // Some elements may not have correctly defined a validate method.
 | 
| +          if (validatable.validate)
 | 
| +            valid = !!validatable.validate() && valid;
 | 
| +        }
 | 
| +      }
 | 
| +
 | 
| +      // Validate the form's native elements.
 | 
| +      for (var el, i = 0; el = this.elements[i], i < this.elements.length; i++) {
 | 
| +        // If this native element is inside a custom element that has already
 | 
| +        // registered to this form, skip it.
 | 
| +        if (this._isChildOfRegisteredParent(el, false)) {
 | 
| +          continue;
 | 
| +        }
 | 
| +
 | 
| +        // Custom elements that extend a native element will also appear in
 | 
| +        // this list, but they've already been validated.
 | 
| +        if (!el.hasAttribute('is') && el.willValidate && el.checkValidity) {
 | 
| +          valid = el.checkValidity() && valid;
 | 
| +        }
 | 
| +      }
 | 
| +
 | 
| +      return valid;
 | 
| +    },
 | 
| +
 | 
| +    /**
 | 
| +     * Returns whether the given element is a radio-button or a checkbox.
 | 
| +     * @return {boolean} True if the element has a `checked` property.
 | 
| +     */
 | 
| +    _usesCheckedInsteadOfValue: function(el) {
 | 
| +      if (el.type == 'checkbox' ||
 | 
| +          el.type == 'radio' ||
 | 
| +          el.getAttribute('role') == 'checkbox' ||
 | 
| +          el.getAttribute('role') == 'radio' ||
 | 
| +          el['_hasIronCheckedElementBehavior']) {
 | 
| +        return true;
 | 
| +      }
 | 
| +      return false;
 | 
| +    },
 | 
| +
 | 
| +    _useValue: function(el) {
 | 
| +      // Skip disabled elements or elements that don't have a `name` attribute.
 | 
| +      if (el.disabled || !el.name) {
 | 
| +        return false;
 | 
| +      }
 | 
| +
 | 
| +      // Checkboxes and radio buttons should only use their value if they're
 | 
| +      // checked. Custom paper-checkbox and paper-radio-button elements
 | 
| +      // don't have a type, but they have the correct role set.
 | 
| +      if (this._usesCheckedInsteadOfValue(el))
 | 
| +        return el.checked;
 | 
| +      return true;
 | 
| +    },
 | 
| +
 | 
| +    _doFakeSubmitForValidation: function() {
 | 
| +      var fakeSubmit = document.createElement('input');
 | 
| +      fakeSubmit.setAttribute('type', 'submit');
 | 
| +      fakeSubmit.style.display = 'none';
 | 
| +      this.appendChild(fakeSubmit);
 | 
| +
 | 
| +      fakeSubmit.click();
 | 
| +
 | 
| +      this.removeChild(fakeSubmit);
 | 
| +    },
 | 
| +
 | 
| +    /**
 | 
| +     * Resets all non-disabled form custom elements to their initial values.
 | 
| +     */
 | 
| +    _resetCustomElements: function() {
 | 
| +      // Reset all the registered custom components. We need to do this after
 | 
| +      // the native reset, since programmatically changing the `value` of some
 | 
| +      // native elements (iron-input in particular) does not notify its
 | 
| +      // parent `paper-input`, which will now display the wrong value.
 | 
| +      this.async(function() {
 | 
| +        for (var el, i = 0; el = this._customElements[i], i < this._customElements.length; i++) {
 | 
| +          if (el.disabled)
 | 
| +            continue;
 | 
| +
 | 
| +          if (this._usesCheckedInsteadOfValue(el)) {
 | 
| +            el.checked = this._customElementsInitialValues[i];
 | 
| +          } else {
 | 
| +            // The native input/textarea displays literal "undefined" when its
 | 
| +            // its value is set to undefined, so default to null instead.
 | 
| +            var value = this._customElementsInitialValues[i];
 | 
| +            if (value === undefined) {
 | 
| +              value = null;
 | 
| +            }
 | 
| +            el.value = value;
 | 
| +
 | 
| +            // In the shady DOM, the native form is all-seeing, and will
 | 
| +            // reset the nested inputs inside <paper-input> and <paper-textarea>.
 | 
| +            // In particular, it resets them to what it thinks the default value
 | 
| +            // is (i.e. "", before the bindings have ran), and since this is
 | 
| +            // a programmatic update, it also doesn't fire any events.
 | 
| +            // Which means we need to manually update the native element's value.
 | 
| +            if (el.inputElement) {
 | 
| +              el.inputElement.value = el.value;
 | 
| +            } else if (el.textarea) {
 | 
| +              el.textarea.value = el.value;
 | 
| +            }
 | 
| +          }
 | 
| +          el.invalid = false;
 | 
| +        }
 | 
| +
 | 
| +        this.fire('iron-form-reset');
 | 
| +      }, 1);
 | 
| +    },
 | 
| +
 | 
| +    /**
 | 
| +     * Returns true if `node` is in the shadow DOM of a different element,
 | 
| +     * that has also implemented IronFormElementBehavior and is registered
 | 
| +     * to this form. The second parameter specifies if the parent must have a
 | 
| +     * name to be considered.
 | 
| +     */
 | 
| +    _isChildOfRegisteredParent: function(node, checkHasName) {
 | 
| +      var parent = node;
 | 
| +
 | 
| +      // At some point going up the tree we'll find either this form or the document.
 | 
| +      while (parent && parent !== document && parent != this) {
 | 
| +        // Use logical parentnode, or native ShadowRoot host.
 | 
| +        parent = Polymer.dom(parent).parentNode || parent.host;
 | 
| +
 | 
| +        // Check if the parent was registered and submittable.
 | 
| +        if (parent &&
 | 
| +            (!checkHasName || parent.name) &&
 | 
| +            parent._parentForm === this) {
 | 
| +          return true;
 | 
| +        }
 | 
| +      }
 | 
| +      return false;
 | 
| +    }
 | 
| +
 | 
| +  });
 | 
| +
 | 
| +</script>
 | 
| 
 |