<!-- @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-behaviors/iron-control-state.html"> <script> /** * Use `Polymer.PaperInputBehavior` to implement inputs with `<paper-input-container>`. This * behavior is implemented by `<paper-input>`. It exposes a number of properties from * `<paper-input-container>` and `<input is="iron-input">` and they should be bound in your * template. * * The input element can be accessed by the `inputElement` property if you need to access * properties or methods that are not exposed. * @polymerBehavior Polymer.PaperInputBehavior */ Polymer.PaperInputBehaviorImpl = { properties: { /** * Fired when the input changes due to user interaction. * * @event change */ /** * The label for this input. Bind this to `<paper-input-container>`'s `label` property. */ label: { type: String }, /** * The value for this input. Bind this to the `<input is="iron-input">`'s `bindValue` * property, or the value property of your input that is `notify:true`. */ value: { notify: true, type: String }, /** * Set to true to disable this input. Bind this to both the `<paper-input-container>`'s * and the input's `disabled` property. */ disabled: { type: Boolean, value: false }, /** * Returns true if the value is invalid. Bind this to both the `<paper-input-container>`'s * and the input's `invalid` property. */ invalid: { type: Boolean, value: false, notify: true }, /** * Set to true to prevent the user from entering invalid input. Bind this to the * `<input is="iron-input">`'s `preventInvalidInput` property. */ preventInvalidInput: { type: Boolean }, /** * Set this to specify the pattern allowed by `preventInvalidInput`. Bind this to the * `<input is="iron-input">`'s `allowedPattern` property. */ allowedPattern: { type: String }, /** * The type of the input. The supported types are `text`, `number` and `password`. Bind this * to the `<input is="iron-input">`'s `type` property. */ type: { type: String }, /** * The datalist of the input (if any). This should match the id of an existing <datalist>. Bind this * to the `<input is="iron-input">`'s `list` property. */ list: { type: String }, /** * A pattern to validate the `input` with. Bind this to the `<input is="iron-input">`'s * `pattern` property. */ pattern: { type: String }, /** * Set to true to mark the input as required. Bind this to the `<input is="iron-input">`'s * `required` property. */ required: { type: Boolean, value: false }, /** * The error message to display when the input is invalid. Bind this to the * `<paper-input-error>`'s content, if using. */ errorMessage: { type: String }, /** * Set to true to show a character counter. */ charCounter: { type: Boolean, value: false }, /** * Set to true to disable the floating label. Bind this to the `<paper-input-container>`'s * `noLabelFloat` property. */ noLabelFloat: { type: Boolean, value: false }, /** * Set to true to always float the label. Bind this to the `<paper-input-container>`'s * `alwaysFloatLabel` property. */ alwaysFloatLabel: { type: Boolean, value: false }, /** * Set to true to auto-validate the input value. Bind this to the `<paper-input-container>`'s * `autoValidate` property. */ autoValidate: { type: Boolean, value: false }, /** * Name of the validator to use. Bind this to the `<input is="iron-input">`'s `validator` * property. */ validator: { type: String }, // HTMLInputElement attributes for binding if needed /** * Bind this to the `<input is="iron-input">`'s `autocomplete` property. */ autocomplete: { type: String, value: 'off' }, /** * Bind this to the `<input is="iron-input">`'s `autofocus` property. */ autofocus: { type: Boolean }, /** * Bind this to the `<input is="iron-input">`'s `inputmode` property. */ inputmode: { type: String }, /** * Bind this to the `<input is="iron-input">`'s `minlength` property. */ minlength: { type: Number }, /** * The maximum length of the input value. Bind this to the `<input is="iron-input">`'s * `maxlength` property. */ maxlength: { type: Number }, /** * The minimum (numeric or date-time) input value. * Bind this to the `<input is="iron-input">`'s `min` property. */ min: { type: String }, /** * The maximum (numeric or date-time) input value. * Can be a String (e.g. `"2000-1-1"`) or a Number (e.g. `2`). * Bind this to the `<input is="iron-input">`'s `max` property. */ max: { type: String }, /** * Limits the numeric or date-time increments. * Bind this to the `<input is="iron-input">`'s `step` property. */ step: { type: String }, /** * Bind this to the `<input is="iron-input">`'s `name` property. */ name: { type: String }, /** * A placeholder string in addition to the label. If this is set, the label will always float. */ placeholder: { type: String, // need to set a default so _computeAlwaysFloatLabel is run value: '' }, /** * Bind this to the `<input is="iron-input">`'s `readonly` property. */ readonly: { type: Boolean, value: false }, /** * Bind this to the `<input is="iron-input">`'s `size` property. */ size: { type: Number }, // Nonstandard attributes for binding if needed /** * Bind this to the `<input is="iron-input">`'s `autocapitalize` property. */ autocapitalize: { type: String, value: 'none' }, /** * Bind this to the `<input is="iron-input">`'s `autocorrect` property. */ autocorrect: { type: String, value: 'off' }, /** * Bind this to the `<input is="iron-input">`'s `autosave` property, used with type=search. */ autosave: { type: String }, /** * Bind this to the `<input is="iron-input">`'s `results` property, , used with type=search. */ results: { type: Number }, _ariaDescribedBy: { type: String, value: '' } }, listeners: { 'addon-attached': '_onAddonAttached' }, observers: [ '_focusedControlStateChanged(focused)' ], /** * Returns a reference to the input element. */ get inputElement() { return this.$.input; }, attached: function() { this._updateAriaLabelledBy(); }, _appendStringWithSpace: function(str, more) { if (str) { str = str + ' ' + more; } else { str = more; } return str; }, _onAddonAttached: function(event) { var target = event.path ? event.path[0] : event.target; if (target.id) { this._ariaDescribedBy = this._appendStringWithSpace(this._ariaDescribedBy, target.id); } else { var id = 'paper-input-add-on-' + Math.floor((Math.random() * 100000)); target.id = id; this._ariaDescribedBy = this._appendStringWithSpace(this._ariaDescribedBy, id); } }, /** * Validates the input element and sets an error style if needed. * * @return {boolean} */ validate: function() { return this.inputElement.validate(); }, /** * If `autoValidate` is true, then validates the element. */ _handleAutoValidate: function() { if (this.autoValidate) this.validate(); }, /** * Restores the cursor to its original position after updating the value. * @param {string} newValue The value that should be saved. */ updateValueAndPreserveCaret: function(newValue) { // Not all elements might have selection, and even if they have the // right properties, accessing them might throw an exception (like for // <input type=number>) try { var start = this.inputElement.selectionStart; this.value = newValue; // The cursor automatically jumps to the end after re-setting the value, // so restore it to its original position. this.inputElement.selectionStart = start; this.inputElement.selectionEnd = start; } catch (e) { // Just set the value and give up on the caret. this.value = newValue; } }, _computeAlwaysFloatLabel: function(alwaysFloatLabel, placeholder) { return placeholder || alwaysFloatLabel; }, _focusedControlStateChanged: function(focused) { // IronControlState stops the focus and blur events in order to redispatch them on the host // element, but paper-input-container listens to those events. Since there are more // pending work on focus/blur in IronControlState, I'm putting in this hack to get the // input focus state working for now. if (!this.$.container) { this.$.container = Polymer.dom(this.root).querySelector('paper-input-container'); if (!this.$.container) { return; } } if (focused) { this.$.container._onFocus(); } else { this.$.container._onBlur(); } }, _updateAriaLabelledBy: function() { var label = Polymer.dom(this.root).querySelector('label'); if (!label) { this._ariaLabelledBy = ''; return; } var labelledBy; if (label.id) { labelledBy = label.id; } else { labelledBy = 'paper-input-label-' + new Date().getUTCMilliseconds(); label.id = labelledBy; } this._ariaLabelledBy = labelledBy; }, _onChange:function(event) { // In the Shadow DOM, the `change` event is not leaked into the // ancestor tree, so we must do this manually. // See https://w3c.github.io/webcomponents/spec/shadow/#events-that-are-not-leaked-into-ancestor-trees. if (this.shadowRoot) { this.fire(event.type, {sourceEvent: event}, { node: this, bubbles: event.bubbles, cancelable: event.cancelable }); } } }; /** @polymerBehavior */ Polymer.PaperInputBehavior = [Polymer.IronControlState, Polymer.PaperInputBehaviorImpl]; </script>