Update Polymer and pull in iron-list
[chromium-blink-merge.git] / third_party / polymer / v1_0 / components-chromium / paper-input / paper-input-container-extracted.js
blob35205cb682c3624ad6e015d97c417510dbba7ff0
2   Polymer({
3     is: 'paper-input-container',
5     properties: {
6       /**
7        * Set to true to disable the floating label. The label disappears when the input value is
8        * not null.
9        */
10       noLabelFloat: {
11         type: Boolean,
12         value: false
13       },
15       /**
16        * Set to true to always float the floating label.
17        */
18       alwaysFloatLabel: {
19         type: Boolean,
20         value: false
21       },
23       /**
24        * The attribute to listen for value changes on.
25        */
26       attrForValue: {
27         type: String,
28         value: 'bind-value'
29       },
31       /**
32        * Set to true to auto-validate the input value when it changes.
33        */
34       autoValidate: {
35         type: Boolean,
36         value: false
37       },
39       /**
40        * True if the input is invalid. This property is set automatically when the input value
41        * changes if auto-validating, or when the `iron-input-valid` event is heard from a child.
42        */
43       invalid: {
44         observer: '_invalidChanged',
45         type: Boolean,
46         value: false
47       },
49       /**
50        * True if the input has focus.
51        */
52       focused: {
53         readOnly: true,
54         type: Boolean,
55         value: false,
56         notify: true
57       },
59       _addons: {
60         type: Array
61         // do not set a default value here intentionally - it will be initialized lazily when a
62         // distributed child is attached, which may occur before configuration for this element
63         // in polyfill.
64       },
66       _inputHasContent: {
67         type: Boolean,
68         value: false
69       },
71       _inputSelector: {
72         type: String,
73         value: 'input,textarea,.paper-input-input'
74       },
76       _boundOnFocus: {
77         type: Function,
78         value: function() {
79           return this._onFocus.bind(this);
80         }
81       },
83       _boundOnBlur: {
84         type: Function,
85         value: function() {
86           return this._onBlur.bind(this);
87         }
88       },
90       _boundOnInput: {
91         type: Function,
92         value: function() {
93           return this._onInput.bind(this);
94         }
95       },
97       _boundValueChanged: {
98         type: Function,
99         value: function() {
100           return this._onValueChanged.bind(this);
101         }
102       }
103     },
105     listeners: {
106       'addon-attached': '_onAddonAttached',
107       'iron-input-validate': '_onIronInputValidate'
108     },
110     get _valueChangedEvent() {
111       return this.attrForValue + '-changed';
112     },
114     get _propertyForValue() {
115       return Polymer.CaseMap.dashToCamelCase(this.attrForValue);
116     },
118     get _inputElement() {
119       return Polymer.dom(this).querySelector(this._inputSelector);
120     },
122     get _inputElementValue() {
123       return this._inputElement[this._propertyForValue] || this._inputElement.value;
124     },
126     ready: function() {
127       if (!this._addons) {
128         this._addons = [];
129       }
130       this.addEventListener('focus', this._boundOnFocus, true);
131       this.addEventListener('blur', this._boundOnBlur, true);
132       if (this.attrForValue) {
133         this._inputElement.addEventListener(this._valueChangedEvent, this._boundValueChanged);
134       } else {
135         this.addEventListener('input', this._onInput);
136       }
137     },
139     attached: function() {
140       // Only validate when attached if the input already has a value.
141       if (this._inputElementValue != '') {
142         this._handleValueAndAutoValidate(this._inputElement);
143       } else {
144         this._handleValue(this._inputElement);
145       }
146     },
148     _onAddonAttached: function(event) {
149       if (!this._addons) {
150         this._addons = [];
151       }
152       var target = event.target;
153       if (this._addons.indexOf(target) === -1) {
154         this._addons.push(target);
155         if (this.isAttached) {
156           this._handleValue(this._inputElement);
157         }
158       }
159     },
161     _onFocus: function() {
162       this._setFocused(true);
163     },
165     _onBlur: function() {
166       this._setFocused(false);
167       this._handleValueAndAutoValidate(this._inputElement);
168     },
170     _onInput: function(event) {
171       this._handleValueAndAutoValidate(event.target);
172     },
174     _onValueChanged: function(event) {
175       this._handleValueAndAutoValidate(event.target);
176     },
178     _handleValue: function(inputElement) {
179       var value = this._inputElementValue;
181       // type="number" hack needed because this.value is empty until it's valid
182       if (value || value === 0 || (inputElement.type === 'number' && !inputElement.checkValidity())) {
183         this._inputHasContent = true;
184       } else {
185         this._inputHasContent = false;
186       }
188       this.updateAddons({
189         inputElement: inputElement,
190         value: value,
191         invalid: this.invalid
192       });
193     },
195     _handleValueAndAutoValidate: function(inputElement) {
196       if (this.autoValidate) {
197         var valid;
198         if (inputElement.validate) {
199           valid = inputElement.validate(this._inputElementValue);
200         } else {
201           valid = inputElement.checkValidity();
202         }
203         this.invalid = !valid;
204       }
206       // Call this last to notify the add-ons.
207       this._handleValue(inputElement);
208     },
210     _onIronInputValidate: function(event) {
211       this.invalid = this._inputElement.invalid;
212     },
214     _invalidChanged: function() {
215       if (this._addons) {
216         this.updateAddons({invalid: this.invalid});
217       }
218     },
220     /**
221      * Call this to update the state of add-ons.
222      * @param {Object} state Add-on state.
223      */
224     updateAddons: function(state) {
225       for (var addon, index = 0; addon = this._addons[index]; index++) {
226         addon.update(state);
227       }
228     },
230     _computeInputContentClass: function(noLabelFloat, alwaysFloatLabel, focused, invalid, _inputHasContent) {
231       var cls = 'input-content';
232       if (!noLabelFloat) {
233         var label = this.querySelector('label');
235         if (alwaysFloatLabel || _inputHasContent) {
236           cls += ' label-is-floating';
237           if (invalid) {
238             cls += ' is-invalid';
239           } else if (focused) {
240             cls += " label-is-highlighted";
241           }
242           // The label might have a horizontal offset if a prefix element exists
243           // which needs to be undone when displayed as a floating label.
244           if (this.$.prefix && label && label.offsetParent &&
245               Polymer.dom(this.$.prefix).getDistributedNodes().length > 0) {
246            label.style.left = -label.offsetParent.offsetLeft + 'px';
247           }
248         } else {
249           // When the label is not floating, it should overlap the input element.
250           if (label) {
251             label.style.left = 0;
252           }
253         }
254       } else {
255         if (_inputHasContent) {
256           cls += ' label-is-hidden';
257         }
258       }
259       return cls;
260     },
262     _computeUnderlineClass: function(focused, invalid) {
263       var cls = 'underline';
264       if (invalid) {
265         cls += ' is-invalid';
266       } else if (focused) {
267         cls += ' is-highlighted'
268       }
269       return cls;
270     },
272     _computeAddOnContentClass: function(focused, invalid) {
273       var cls = 'add-on-content';
274       if (invalid) {
275         cls += ' is-invalid';
276       } else if (focused) {
277         cls += ' is-highlighted'
278       }
279       return cls;
280     }
281   });