2015-06-26 20:27:38 -07:00
|
|
|
<!--
|
|
|
|
@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">
|
2016-03-22 10:46:57 -07:00
|
|
|
<link rel="import" href="../iron-a11y-announcer/iron-a11y-announcer.html">
|
2015-06-26 20:27:38 -07:00
|
|
|
<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] -->
|
2015-07-16 16:55:16 -07:00
|
|
|
<input is="iron-input" prevent-invalid-input allowed-pattern="[0-9]">
|
2015-06-26 20:27:38 -07:00
|
|
|
|
|
|
|
@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
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2016-03-22 10:46:57 -07:00
|
|
|
* 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`).
|
2015-06-26 20:27:38 -07:00
|
|
|
*/
|
|
|
|
preventInvalidInput: {
|
|
|
|
type: Boolean
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2016-03-22 10:46:57 -07:00
|
|
|
* 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.+-!;:]`
|
2015-06-26 20:27:38 -07:00
|
|
|
*/
|
|
|
|
allowedPattern: {
|
2015-12-14 08:43:03 -07:00
|
|
|
type: String,
|
|
|
|
observer: "_allowedPatternChanged"
|
2015-06-26 20:27:38 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
_previousValidInput: {
|
|
|
|
type: String,
|
|
|
|
value: ''
|
|
|
|
},
|
|
|
|
|
|
|
|
_patternAlreadyChecked: {
|
|
|
|
type: Boolean,
|
|
|
|
value: false
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
listeners: {
|
|
|
|
'input': '_onInput',
|
|
|
|
'keypress': '_onKeypress'
|
|
|
|
},
|
|
|
|
|
2016-04-14 09:30:37 -07:00
|
|
|
/** @suppress {checkTypes} */
|
2016-03-22 10:46:57 -07:00
|
|
|
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;
|
|
|
|
},
|
|
|
|
|
2015-06-26 20:27:38 -07:00
|
|
|
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;
|
|
|
|
},
|
|
|
|
|
2015-08-31 12:00:39 -07:00
|
|
|
/**
|
|
|
|
* @suppress {checkTypes}
|
|
|
|
*/
|
2015-06-26 20:27:38 -07:00
|
|
|
_bindValueChanged: function() {
|
|
|
|
if (this.value !== this.bindValue) {
|
2016-01-06 13:16:16 -07:00
|
|
|
this.value = !(this.bindValue || this.bindValue === 0 || this.bindValue === false) ? '' : this.bindValue;
|
2015-06-26 20:27:38 -07:00
|
|
|
}
|
|
|
|
// manually notify because we don't want to notify until after setting value
|
|
|
|
this.fire('bind-value-changed', {value: this.bindValue});
|
|
|
|
},
|
|
|
|
|
2015-12-14 08:43:03 -07:00
|
|
|
_allowedPatternChanged: function() {
|
|
|
|
// Force to prevent invalid input when an `allowed-pattern` is set
|
|
|
|
this.preventInvalidInput = this.allowedPattern ? true : false;
|
|
|
|
},
|
|
|
|
|
2015-06-26 20:27:38 -07:00
|
|
|
_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) {
|
2016-03-22 10:46:57 -07:00
|
|
|
this._announceInvalidCharacter('Invalid string of characters not entered.');
|
2015-06-26 20:27:38 -07:00
|
|
|
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.
|
|
|
|
|
2015-07-16 16:55:16 -07:00
|
|
|
// For these keys, ASCII code == browser keycode.
|
|
|
|
var anyNonPrintable =
|
2015-06-26 20:27:38 -07:00
|
|
|
(event.keyCode == 8) || // backspace
|
2015-08-11 11:50:27 -07:00
|
|
|
(event.keyCode == 9) || // tab
|
2015-07-16 16:55:16 -07:00
|
|
|
(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 =
|
2015-06-26 20:27:38 -07:00
|
|
|
(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
|
|
|
|
|
2015-07-16 16:55:16 -07:00
|
|
|
return !anyNonPrintable && !(event.charCode == 0 && mozNonPrintable);
|
2015-06-26 20:27:38 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
_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();
|
2016-03-22 10:46:57 -07:00
|
|
|
this._announceInvalidCharacter('Invalid character ' + thisChar + ' not entered.');
|
2015-06-26 20:27:38 -07:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_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() {
|
2016-03-22 10:46:57 -07:00
|
|
|
// 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);
|
|
|
|
}
|
2015-06-26 20:27:38 -07:00
|
|
|
}
|
|
|
|
|
2016-03-22 10:46:57 -07:00
|
|
|
this.invalid = !valid;
|
2015-06-26 20:27:38 -07:00
|
|
|
this.fire('iron-input-validate');
|
|
|
|
return valid;
|
2016-03-22 10:46:57 -07:00
|
|
|
},
|
2015-06-26 20:27:38 -07:00
|
|
|
|
2016-03-22 10:46:57 -07:00
|
|
|
_announceInvalidCharacter: function(message) {
|
|
|
|
this.fire('iron-announce', { text: message });
|
|
|
|
}
|
2015-06-26 20:27:38 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
/*
|
|
|
|
The `iron-input-validate` event is fired whenever `validate()` is called.
|
|
|
|
@event iron-input-validate
|
|
|
|
*/
|
|
|
|
|
|
|
|
</script>
|