1
2
3  (function() {
4
5    var paperInput = CoreStyle.g.paperInput = CoreStyle.g.paperInput || {};
6    paperInput.focusedColor = '#4059a9';
7    paperInput.invalidColor = '#d34336';
8
9    Polymer('paper-input', {
10
11      /**
12       * The label for this input. It normally appears as grey text inside
13       * the text input and disappears once the user enters text.
14       *
15       * @attribute label
16       * @type string
17       * @default ''
18       */
19      label: '',
20
21      /**
22       * If true, the label will "float" above the text input once the
23       * user enters text instead of disappearing.
24       *
25       * @attribute floatingLabel
26       * @type boolean
27       * @default false
28       */
29      floatingLabel: false,
30
31      /**
32       * (multiline only) If set to a non-zero value, the height of this
33       * text input will grow with the value changes until it is maxRows
34       * rows tall. If the maximum size does not fit the value, the text
35       * input will scroll internally.
36       *
37       * @attribute maxRows
38       * @type number
39       * @default 0
40       */
41      maxRows: 0,
42
43      /**
44       * The message to display if the input value fails validation. If this
45       * is unset or the empty string, a default message is displayed depending
46       * on the type of validation error.
47       *
48       * @attribute error
49       * @type string
50       */
51      error: '',
52
53      focused: false,
54      pressed: false,
55
56      attached: function() {
57        if (this.multiline) {
58          this.resizeInput();
59          window.requestAnimationFrame(function() {
60            this.$.underlineContainer.classList.add('animating');
61          }.bind(this));
62        }
63      },
64
65      resizeInput: function() {
66        var height = this.$.inputClone.getBoundingClientRect().height;
67        var bounded = this.maxRows > 0 || this.rows > 0;
68        if (bounded) {
69          var minHeight = this.$.minInputHeight.getBoundingClientRect().height;
70          var maxHeight = this.$.maxInputHeight.getBoundingClientRect().height;
71          height = Math.max(minHeight, Math.min(height, maxHeight));
72        }
73        this.$.inputContainer.style.height = height + 'px';
74        this.$.underlineContainer.style.top = height + 'px';
75      },
76
77      prepareLabelTransform: function() {
78        var toRect = this.$.floatedLabelSpan.getBoundingClientRect();
79        var fromRect = this.$.labelSpan.getBoundingClientRect();
80        if (toRect.width !== 0) {
81          this.$.label.cachedTransform = 'scale(' + (toRect.width / fromRect.width) + ') ' +
82            'translateY(' + (toRect.bottom - fromRect.bottom) + 'px)';
83        }
84      },
85
86      toggleLabel: function(force) {
87        var v = force !== undefined ? force : this.inputValue;
88
89        if (!this.floatingLabel) {
90          this.$.label.classList.toggle('hidden', v);
91        }
92
93        if (this.floatingLabel && !this.focused) {
94          this.$.label.classList.toggle('hidden', v);
95          this.$.floatedLabel.classList.toggle('hidden', !v);
96        }
97      },
98
99      rowsChanged: function() {
100        if (this.multiline && !isNaN(parseInt(this.rows))) {
101          this.$.minInputHeight.innerHTML = '';
102          for (var i = 0; i < this.rows; i++) {
103            this.$.minInputHeight.appendChild(document.createElement('br'));
104          }
105          this.resizeInput();
106        }
107      },
108
109      maxRowsChanged: function() {
110        if (this.multiline && !isNaN(parseInt(this.maxRows))) {
111          this.$.maxInputHeight.innerHTML = '';
112          for (var i = 0; i < this.maxRows; i++) {
113            this.$.maxInputHeight.appendChild(document.createElement('br'));
114          }
115          this.resizeInput();
116        }
117      },
118
119      inputValueChanged: function() {
120        this.super();
121
122        if (this.multiline) {
123          var escaped = this.inputValue.replace(/\n/gm, '<br>');
124          if (!escaped || escaped.lastIndexOf('<br>') === escaped.length - 4) {
125            escaped += '&nbsp';
126          }
127          this.$.inputCloneSpan.innerHTML = escaped;
128          this.resizeInput();
129        }
130
131        this.toggleLabel();
132      },
133
134      labelChanged: function() {
135        if (this.floatingLabel && this.$.floatedLabel && this.$.label) {
136          // If the element is created programmatically, labelChanged is called before
137          // binding. Run the measuring code in async so the DOM is ready.
138          this.async(function() {
139            this.prepareLabelTransform();
140          });
141        }
142      },
143
144      placeholderChanged: function() {
145        this.label = this.placeholder;
146      },
147
148      inputFocusAction: function() {
149        if (!this.pressed) {
150          if (this.floatingLabel) {
151            this.$.floatedLabel.classList.remove('hidden');
152            this.$.floatedLabel.classList.add('focused');
153            this.$.floatedLabel.classList.add('focusedColor');
154          }
155          this.$.label.classList.add('hidden');
156          this.$.underlineHighlight.classList.add('focused');
157          this.$.caret.classList.add('focused');
158
159          this.super(arguments);
160        }
161        this.focused = true;
162      },
163
164      shouldFloatLabel: function() {
165        // if type = number, the input value is the empty string until a valid number
166        // is entered so we must do some hacks here
167        return this.inputValue || (this.type === 'number' && !this.validity.valid);
168      },
169
170      inputBlurAction: function() {
171        this.super(arguments);
172
173        this.$.underlineHighlight.classList.remove('focused');
174        this.$.caret.classList.remove('focused');
175
176        if (this.floatingLabel) {
177          this.$.floatedLabel.classList.remove('focused');
178          this.$.floatedLabel.classList.remove('focusedColor');
179          if (!this.shouldFloatLabel()) {
180            this.$.floatedLabel.classList.add('hidden');
181          }
182        }
183
184        // type = number hack. see core-input for more info
185        if (!this.shouldFloatLabel()) {
186          this.$.label.classList.remove('hidden');
187          this.$.label.classList.add('animating');
188          this.async(function() {
189            this.$.label.style.webkitTransform = 'none';
190            this.$.label.style.transform = 'none';
191          });
192        }
193
194        this.focused = false;
195      },
196
197      downAction: function(e) {
198        if (this.disabled) {
199          return;
200        }
201
202        if (this.focused) {
203          return;
204        }
205
206        this.pressed = true;
207        var rect = this.$.underline.getBoundingClientRect();
208        var right = e.x - rect.left;
209        this.$.underlineHighlight.style.webkitTransformOriginX = right + 'px';
210        this.$.underlineHighlight.style.transformOriginX = right + 'px';
211        this.$.underlineHighlight.classList.remove('focused');
212        this.underlineAsync = this.async(function() {
213          this.$.underlineHighlight.classList.add('pressed');
214        }, null, 200);
215
216        // No caret animation if there is text in the input.
217        if (!this.inputValue) {
218          this.$.caret.classList.remove('focused');
219        }
220      },
221
222      upAction: function(e) {
223        if (this.disabled) {
224          return;
225        }
226
227        if (!this.pressed) {
228          return;
229        }
230
231        // if a touchevent caused the up, the synthentic mouseevents will blur
232        // the input, make sure to prevent those from being generated.
233        if (e._source === 'touch') {
234          e.preventDefault();
235        }
236
237        if (this.underlineAsync) {
238          clearTimeout(this.underlineAsync);
239          this.underlineAsync = null;
240        }
241
242        // Focus the input here to bring up the virtual keyboard.
243        this.$.input.focus();
244        this.pressed = false;
245        this.animating = true;
246
247        this.$.underlineHighlight.classList.remove('pressed');
248        this.$.underlineHighlight.classList.add('animating');
249        this.async(function() {
250          this.$.underlineHighlight.classList.add('focused');
251        });
252
253        // No caret animation if there is text in the input.
254        if (!this.inputValue) {
255          this.$.caret.classList.add('animating');
256          this.async(function() {
257            this.$.caret.classList.add('focused');
258          }, null, 100);
259        }
260
261        if (this.floatingLabel) {
262          this.$.label.classList.add('focusedColor');
263          this.$.label.classList.add('animating');
264          if (!this.$.label.cachedTransform) {
265            this.prepareLabelTransform();
266          }
267          this.$.label.style.webkitTransform = this.$.label.cachedTransform;
268          this.$.label.style.transform = this.$.label.cachedTransform;
269        }
270      },
271
272      keydownAction: function() {
273        this.super();
274
275        // more type = number hacks. see core-input for more info
276        if (this.type === 'number') {
277          this.async(function() {
278            if (!this.inputValue) {
279              this.toggleLabel(!this.validity.valid);
280            }
281          });
282        }
283      },
284
285      keypressAction: function() {
286        if (this.animating) {
287          this.transitionEndAction();
288        }
289      },
290
291      transitionEndAction: function(e) {
292        this.animating = false;
293        if (this.pressed) {
294          return;
295        }
296
297        if (this.focused) {
298
299          if (this.floatingLabel || this.inputValue) {
300            this.$.label.classList.add('hidden');
301          }
302
303          if (this.floatingLabel) {
304            this.$.label.classList.remove('focusedColor');
305            this.$.label.classList.remove('animating');
306            this.$.floatedLabel.classList.remove('hidden');
307            this.$.floatedLabel.classList.add('focused');
308            this.$.floatedLabel.classList.add('focusedColor');
309          }
310
311          this.async(function() {
312            this.$.underlineHighlight.classList.remove('animating');
313            this.$.caret.classList.remove('animating');
314          }, null, 100);
315
316        } else {
317
318          this.$.label.classList.remove('animating');
319
320        }
321      }
322
323    });
324
325  }());
326
327