| <!-- |
| @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-a11y-announcer/iron-a11y-announcer.html"> |
| <link rel="import" href="../iron-validatable-behavior/iron-validatable-behavior.html"> |
| |
| <script> |
| |
| /* |
| `<iron-input>` adds two-way binding and custom validators using `Polymer.IronValidatorBehavior` |
| to `<input>`. |
| |
| ### Two-way binding |
| |
| By default you can only get notified of changes to an `input`'s `value` due to user input: |
| |
| <input value="{{myValue::input}}"> |
| |
| `iron-input` adds the `bind-value` property that mirrors the `value` property, and can be used |
| for two-way data binding. `bind-value` will notify if it is changed either by user input or by script. |
| |
| <input is="iron-input" bind-value="{{myValue}}"> |
| |
| ### Custom validators |
| |
| You can use custom validators that implement `Polymer.IronValidatorBehavior` with `<iron-input>`. |
| |
| <input is="iron-input" validator="my-custom-validator"> |
| |
| ### Stopping invalid input |
| |
| It may be desirable to only allow users to enter certain characters. You can use the |
| `prevent-invalid-input` and `allowed-pattern` attributes together to accomplish this. This feature |
| is separate from validation, and `allowed-pattern` does not affect how the input is validated. |
| |
| <!-- only allow characters that match [0-9] --> |
| <input is="iron-input" prevent-invalid-input allowed-pattern="[0-9]"> |
| |
| @hero hero.svg |
| @demo demo/index.html |
| */ |
| |
| Polymer({ |
| |
| is: 'iron-input', |
| |
| extends: 'input', |
| |
| behaviors: [ |
| Polymer.IronValidatableBehavior |
| ], |
| |
| properties: { |
| |
| /** |
| * Use this property instead of `value` for two-way data binding. |
| */ |
| bindValue: { |
| observer: '_bindValueChanged', |
| type: String |
| }, |
| |
| /** |
| * Set to true to prevent the user from entering invalid input. If `allowedPattern` is set, |
| * any character typed by the user will be matched against that pattern, and rejected if it's not a match. |
| * Pasted input will have each character checked individually; if any character |
| * doesn't match `allowedPattern`, the entire pasted string will be rejected. |
| * If `allowedPattern` is not set, it will use the `type` attribute (only supported for `type=number`). |
| */ |
| preventInvalidInput: { |
| type: Boolean |
| }, |
| |
| /** |
| * Regular expression that list the characters allowed as input. |
| * This pattern represents the allowed characters for the field; as the user inputs text, |
| * each individual character will be checked against the pattern (rather than checking |
| * the entire value as a whole). The recommended format should be a list of allowed characters; |
| * for example, `[a-zA-Z0-9.+-!;:]` |
| */ |
| allowedPattern: { |
| type: String, |
| observer: "_allowedPatternChanged" |
| }, |
| |
| _previousValidInput: { |
| type: String, |
| value: '' |
| }, |
| |
| _patternAlreadyChecked: { |
| type: Boolean, |
| value: false |
| } |
| |
| }, |
| |
| listeners: { |
| 'input': '_onInput', |
| 'keypress': '_onKeypress' |
| }, |
| |
| /** @suppress {checkTypes} */ |
| registered: function() { |
| // Feature detect whether we need to patch dispatchEvent (i.e. on FF and IE). |
| if (!this._canDispatchEventOnDisabled()) { |
| this._origDispatchEvent = this.dispatchEvent; |
| this.dispatchEvent = this._dispatchEventFirefoxIE; |
| } |
| }, |
| |
| created: function() { |
| Polymer.IronA11yAnnouncer.requestAvailability(); |
| }, |
| |
| _canDispatchEventOnDisabled: function() { |
| var input = document.createElement('input'); |
| var canDispatch = false; |
| input.disabled = true; |
| |
| input.addEventListener('feature-check-dispatch-event', function() { |
| canDispatch = true; |
| }); |
| |
| try { |
| input.dispatchEvent(new Event('feature-check-dispatch-event')); |
| } catch(e) {} |
| |
| return canDispatch; |
| }, |
| |
| _dispatchEventFirefoxIE: function() { |
| // Due to Firefox bug, events fired on disabled form controls can throw |
| // errors; furthermore, neither IE nor Firefox will actually dispatch |
| // events from disabled form controls; as such, we toggle disable around |
| // the dispatch to allow notifying properties to notify |
| // See issue #47 for details |
| var disabled = this.disabled; |
| this.disabled = false; |
| this._origDispatchEvent.apply(this, arguments); |
| this.disabled = disabled; |
| }, |
| |
| get _patternRegExp() { |
| var pattern; |
| if (this.allowedPattern) { |
| pattern = new RegExp(this.allowedPattern); |
| } else { |
| switch (this.type) { |
| case 'number': |
| pattern = /[0-9.,e-]/; |
| break; |
| } |
| } |
| return pattern; |
| }, |
| |
| ready: function() { |
| this.bindValue = this.value; |
| }, |
| |
| /** |
| * @suppress {checkTypes} |
| */ |
| _bindValueChanged: function() { |
| if (this.value !== this.bindValue) { |
| this.value = !(this.bindValue || this.bindValue === 0 || this.bindValue === false) ? '' : this.bindValue; |
| } |
| // manually notify because we don't want to notify until after setting value |
| this.fire('bind-value-changed', {value: this.bindValue}); |
| }, |
| |
| _allowedPatternChanged: function() { |
| // Force to prevent invalid input when an `allowed-pattern` is set |
| this.preventInvalidInput = this.allowedPattern ? true : false; |
| }, |
| |
| _onInput: function() { |
| // Need to validate each of the characters pasted if they haven't |
| // been validated inside `_onKeypress` already. |
| if (this.preventInvalidInput && !this._patternAlreadyChecked) { |
| var valid = this._checkPatternValidity(); |
| if (!valid) { |
| this._announceInvalidCharacter('Invalid string of characters not entered.'); |
| this.value = this._previousValidInput; |
| } |
| } |
| |
| this.bindValue = this.value; |
| this._previousValidInput = this.value; |
| this._patternAlreadyChecked = false; |
| }, |
| |
| _isPrintable: function(event) { |
| // What a control/printable character is varies wildly based on the browser. |
| // - most control characters (arrows, backspace) do not send a `keypress` event |
| // in Chrome, but the *do* on Firefox |
| // - in Firefox, when they do send a `keypress` event, control chars have |
| // a charCode = 0, keyCode = xx (for ex. 40 for down arrow) |
| // - printable characters always send a keypress event. |
| // - in Firefox, printable chars always have a keyCode = 0. In Chrome, the keyCode |
| // always matches the charCode. |
| // None of this makes any sense. |
| |
| // For these keys, ASCII code == browser keycode. |
| var anyNonPrintable = |
| (event.keyCode == 8) || // backspace |
| (event.keyCode == 9) || // tab |
| (event.keyCode == 13) || // enter |
| (event.keyCode == 27); // escape |
| |
| // For these keys, make sure it's a browser keycode and not an ASCII code. |
| var mozNonPrintable = |
| (event.keyCode == 19) || // pause |
| (event.keyCode == 20) || // caps lock |
| (event.keyCode == 45) || // insert |
| (event.keyCode == 46) || // delete |
| (event.keyCode == 144) || // num lock |
| (event.keyCode == 145) || // scroll lock |
| (event.keyCode > 32 && event.keyCode < 41) || // page up/down, end, home, arrows |
| (event.keyCode > 111 && event.keyCode < 124); // fn keys |
| |
| return !anyNonPrintable && !(event.charCode == 0 && mozNonPrintable); |
| }, |
| |
| _onKeypress: function(event) { |
| if (!this.preventInvalidInput && this.type !== 'number') { |
| return; |
| } |
| var regexp = this._patternRegExp; |
| if (!regexp) { |
| return; |
| } |
| |
| // Handle special keys and backspace |
| if (event.metaKey || event.ctrlKey || event.altKey) |
| return; |
| |
| // Check the pattern either here or in `_onInput`, but not in both. |
| this._patternAlreadyChecked = true; |
| |
| var thisChar = String.fromCharCode(event.charCode); |
| if (this._isPrintable(event) && !regexp.test(thisChar)) { |
| event.preventDefault(); |
| this._announceInvalidCharacter('Invalid character ' + thisChar + ' not entered.'); |
| } |
| }, |
| |
| _checkPatternValidity: function() { |
| var regexp = this._patternRegExp; |
| if (!regexp) { |
| return true; |
| } |
| for (var i = 0; i < this.value.length; i++) { |
| if (!regexp.test(this.value[i])) { |
| return false; |
| } |
| } |
| return true; |
| }, |
| |
| /** |
| * Returns true if `value` is valid. The validator provided in `validator` will be used first, |
| * then any constraints. |
| * @return {boolean} True if the value is valid. |
| */ |
| validate: function() { |
| // First, check what the browser thinks. Some inputs (like type=number) |
| // behave weirdly and will set the value to "" if something invalid is |
| // entered, but will set the validity correctly. |
| var valid = this.checkValidity(); |
| |
| // Only do extra checking if the browser thought this was valid. |
| if (valid) { |
| // Empty, required input is invalid |
| if (this.required && this.value === '') { |
| valid = false; |
| } else if (this.hasValidator()) { |
| valid = Polymer.IronValidatableBehavior.validate.call(this, this.value); |
| } |
| } |
| |
| this.invalid = !valid; |
| this.fire('iron-input-validate'); |
| return valid; |
| }, |
| |
| _announceInvalidCharacter: function(message) { |
| this.fire('iron-announce', { text: message }); |
| } |
| }); |
| |
| /* |
| The `iron-input-validate` event is fired whenever `validate()` is called. |
| @event iron-input-validate |
| */ |
| |
| </script> |