Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / polymer / components / paper-input / paper-input.html
1 <!--
2 Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
3 This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE
4 The complete set of authors may be found at http://polymer.github.io/AUTHORS
5 The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS
6 Code distributed by Google as part of the polymer project is also
7 subject to an additional IP rights grant found at http://polymer.github.io/PATENTS
8 -->
9
10 <!--
11 `paper-input` is a single- or multi-line text field where user can enter input.
12 It can optionally have a label.
13
14 Example:
15
16     <paper-input label="Your Name"></paper-input>
17     <paper-input multiline label="Enter multiple lines here"></paper-input>
18
19 Theming
20 --------
21
22 Set `CoreStyle.g.paperInput.focusedColor` and `CoreStyle.g.paperInput.invalidColor` to theme
23 the focused and invalid states.
24
25 @group Paper Elements
26 @element paper-input
27 @extends core-input
28 @homepage github.io
29 -->
30 <link href="../polymer/polymer.html" rel="import">
31 <link href="../core-input/core-input.html" rel="import">
32 <link href="../core-style/core-style.html" rel="import">
33
34 <core-style id="paper-input">
35
36 #label.focused,
37 #floatedLabel.focused {
38   color: {{g.paperInput.focusedColor}};
39 }
40
41 #underlineHighlight.focused,
42 #caretInner {
43   background-color: {{g.paperInput.focusedColor}};
44 }
45
46 #error,
47 :host(.invalid) #label.focused,
48 :host(.invalid) #floatedLabel.focused {
49   color: {{g.paperInput.invalidColor}};
50 }
51 :host(.invalid) #underlineHighlight.focused,
52 :host(.invalid) #caretInner {
53   background-color: {{g.paperInput.invalidColor}};
54 }
55
56 </core-style>
57
58 <polymer-element name="paper-input" extends="core-input" attributes="label floatingLabel maxRows error" on-down="{{downAction}}" on-up="{{upAction}}">
59
60   <template>
61
62     <link href="paper-input.css" rel="stylesheet">
63
64     <core-style ref="paper-input"></core-style>
65
66     <div id="floatedLabel" class="hidden" hidden?="{{!floatingLabel}}"><span id="floatedLabelSpan">{{label}}</span></div>
67
68     <div id="container" on-transitionend="{{transitionEndAction}}" on-webkitTransitionEnd="{{transitionEndAction}}">
69
70       <div id="label"><span id="labelSpan">{{label}}</span></div>
71
72       <div id="inputContainer">
73
74         <div id="inputClone">
75           <span id="inputCloneSpan" aria-hidden="true">&nbsp;</span>
76         </div>
77
78         <template if="{{multiline}}">
79           <div id="minInputHeight"></div>
80           <div id="maxInputHeight"></div>
81         </template>
82
83         <shadow></shadow>
84
85       </div>
86
87       <div id="underlineContainer">
88         <div id="underline"></div>
89         <div id="underlineHighlight" class="focusedColor"></div>
90       </div>
91
92       <div id="caret">
93         <div id="caretInner" class="focusedColor"></div>
94       </div>
95
96     </div>
97
98     <div id="errorContainer">
99       <div id="error" role="alert" aria-hidden="{{!invalid}}">{{error || validationMessage}}</div>
100       <div id="errorIcon"></div>
101     </div>
102
103   </template>
104
105   <script>
106
107   (function() {
108
109     var paperInput = CoreStyle.g.paperInput = CoreStyle.g.paperInput || {};
110     paperInput.focusedColor = '#4059a9';
111     paperInput.invalidColor = '#d34336';
112
113     Polymer('paper-input', {
114
115       /**
116        * The label for this input. It normally appears as grey text inside
117        * the text input and disappears once the user enters text.
118        *
119        * @attribute label
120        * @type string
121        * @default ''
122        */
123       label: '',
124
125       /**
126        * If true, the label will "float" above the text input once the
127        * user enters text instead of disappearing.
128        *
129        * @attribute floatingLabel
130        * @type boolean
131        * @default false
132        */
133       floatingLabel: false,
134
135       /**
136        * (multiline only) If set to a non-zero value, the height of this
137        * text input will grow with the value changes until it is maxRows
138        * rows tall. If the maximum size does not fit the value, the text
139        * input will scroll internally.
140        *
141        * @attribute maxRows
142        * @type number
143        * @default 0
144        */
145       maxRows: 0,
146
147       /**
148        * The message to display if the input value fails validation. If this
149        * is unset or the empty string, a default message is displayed depending
150        * on the type of validation error.
151        *
152        * @attribute error
153        * @type string
154        */
155       error: '',
156
157       focused: false,
158       pressed: false,
159
160       attached: function() {
161         if (this.multiline) {
162           this.resizeInput();
163           window.requestAnimationFrame(function() {
164             this.$.underlineContainer.classList.add('animating');
165           }.bind(this));
166         }
167       },
168
169       resizeInput: function() {
170         var height = this.$.inputClone.getBoundingClientRect().height;
171         var bounded = this.maxRows > 0 || this.rows > 0;
172         if (bounded) {
173           var minHeight = this.$.minInputHeight.getBoundingClientRect().height;
174           var maxHeight = this.$.maxInputHeight.getBoundingClientRect().height;
175           height = Math.max(minHeight, Math.min(height, maxHeight));
176         }
177         this.$.inputContainer.style.height = height + 'px';
178         this.$.underlineContainer.style.top = height + 'px';
179       },
180
181       prepareLabelTransform: function() {
182         var toRect = this.$.floatedLabelSpan.getBoundingClientRect();
183         var fromRect = this.$.labelSpan.getBoundingClientRect();
184         if (toRect.width !== 0) {
185           this.$.label.cachedTransform = 'scale(' + (toRect.width / fromRect.width) + ') ' +
186             'translateY(' + (toRect.bottom - fromRect.bottom) + 'px)';
187         }
188       },
189
190       toggleLabel: function(force) {
191         var v = force !== undefined ? force : this.inputValue;
192
193         if (!this.floatingLabel) {
194           this.$.label.classList.toggle('hidden', v);
195         }
196
197         if (this.floatingLabel && !this.focused) {
198           this.$.label.classList.toggle('hidden', v);
199           this.$.floatedLabel.classList.toggle('hidden', !v);
200         }
201       },
202
203       rowsChanged: function() {
204         if (this.multiline && !isNaN(parseInt(this.rows))) {
205           this.$.minInputHeight.innerHTML = '';
206           for (var i = 0; i < this.rows; i++) {
207             this.$.minInputHeight.appendChild(document.createElement('br'));
208           }
209           this.resizeInput();
210         }
211       },
212
213       maxRowsChanged: function() {
214         if (this.multiline && !isNaN(parseInt(this.maxRows))) {
215           this.$.maxInputHeight.innerHTML = '';
216           for (var i = 0; i < this.maxRows; i++) {
217             this.$.maxInputHeight.appendChild(document.createElement('br'));
218           }
219           this.resizeInput();
220         }
221       },
222
223       inputValueChanged: function() {
224         this.super();
225
226         if (this.multiline) {
227           var escaped = this.inputValue.replace(/\n/gm, '<br>');
228           if (!escaped || escaped.lastIndexOf('<br>') === escaped.length - 4) {
229             escaped += '&nbsp';
230           }
231           this.$.inputCloneSpan.innerHTML = escaped;
232           this.resizeInput();
233         }
234
235         this.toggleLabel();
236       },
237
238       labelChanged: function() {
239         if (this.floatingLabel && this.$.floatedLabel && this.$.label) {
240           // If the element is created programmatically, labelChanged is called before
241           // binding. Run the measuring code in async so the DOM is ready.
242           this.async(function() {
243             this.prepareLabelTransform();
244           });
245         }
246       },
247
248       placeholderChanged: function() {
249         this.label = this.placeholder;
250       },
251
252       inputFocusAction: function() {
253         if (!this.pressed) {
254           if (this.floatingLabel) {
255             this.$.floatedLabel.classList.remove('hidden');
256             this.$.floatedLabel.classList.add('focused');
257             this.$.floatedLabel.classList.add('focusedColor');
258           }
259           this.$.label.classList.add('hidden');
260           this.$.underlineHighlight.classList.add('focused');
261           this.$.caret.classList.add('focused');
262
263           this.super(arguments);
264         }
265         this.focused = true;
266       },
267
268       shouldFloatLabel: function() {
269         // if type = number, the input value is the empty string until a valid number
270         // is entered so we must do some hacks here
271         return this.inputValue || (this.type === 'number' && !this.validity.valid);
272       },
273
274       inputBlurAction: function() {
275         this.super(arguments);
276
277         this.$.underlineHighlight.classList.remove('focused');
278         this.$.caret.classList.remove('focused');
279
280         if (this.floatingLabel) {
281           this.$.floatedLabel.classList.remove('focused');
282           this.$.floatedLabel.classList.remove('focusedColor');
283           if (!this.shouldFloatLabel()) {
284             this.$.floatedLabel.classList.add('hidden');
285           }
286         }
287
288         // type = number hack. see core-input for more info
289         if (!this.shouldFloatLabel()) {
290           this.$.label.classList.remove('hidden');
291           this.$.label.classList.add('animating');
292           this.async(function() {
293             this.$.label.style.webkitTransform = 'none';
294             this.$.label.style.transform = 'none';
295           });
296         }
297
298         this.focused = false;
299       },
300
301       downAction: function(e) {
302         if (this.disabled) {
303           return;
304         }
305
306         if (this.focused) {
307           return;
308         }
309
310         this.pressed = true;
311         var rect = this.$.underline.getBoundingClientRect();
312         var right = e.x - rect.left;
313         this.$.underlineHighlight.style.webkitTransformOriginX = right + 'px';
314         this.$.underlineHighlight.style.transformOriginX = right + 'px';
315         this.$.underlineHighlight.classList.remove('focused');
316         this.underlineAsync = this.async(function() {
317           this.$.underlineHighlight.classList.add('pressed');
318         }, null, 200);
319
320         // No caret animation if there is text in the input.
321         if (!this.inputValue) {
322           this.$.caret.classList.remove('focused');
323         }
324       },
325
326       upAction: function(e) {
327         if (this.disabled) {
328           return;
329         }
330
331         if (!this.pressed) {
332           return;
333         }
334
335         // if a touchevent caused the up, the synthentic mouseevents will blur
336         // the input, make sure to prevent those from being generated.
337         if (e._source === 'touch') {
338           e.preventDefault();
339         }
340
341         if (this.underlineAsync) {
342           clearTimeout(this.underlineAsync);
343           this.underlineAsync = null;
344         }
345
346         // Focus the input here to bring up the virtual keyboard.
347         this.$.input.focus();
348         this.pressed = false;
349         this.animating = true;
350
351         this.$.underlineHighlight.classList.remove('pressed');
352         this.$.underlineHighlight.classList.add('animating');
353         this.async(function() {
354           this.$.underlineHighlight.classList.add('focused');
355         });
356
357         // No caret animation if there is text in the input.
358         if (!this.inputValue) {
359           this.$.caret.classList.add('animating');
360           this.async(function() {
361             this.$.caret.classList.add('focused');
362           }, null, 100);
363         }
364
365         if (this.floatingLabel) {
366           this.$.label.classList.add('focusedColor');
367           this.$.label.classList.add('animating');
368           if (!this.$.label.cachedTransform) {
369             this.prepareLabelTransform();
370           }
371           this.$.label.style.webkitTransform = this.$.label.cachedTransform;
372           this.$.label.style.transform = this.$.label.cachedTransform;
373         }
374       },
375
376       keydownAction: function() {
377         this.super();
378
379         // more type = number hacks. see core-input for more info
380         if (this.type === 'number') {
381           this.async(function() {
382             if (!this.inputValue) {
383               this.toggleLabel(!this.validity.valid);
384             }
385           });
386         }
387       },
388
389       keypressAction: function() {
390         if (this.animating) {
391           this.transitionEndAction();
392         }
393       },
394
395       transitionEndAction: function(e) {
396         this.animating = false;
397         if (this.pressed) {
398           return;
399         }
400
401         if (this.focused) {
402
403           if (this.floatingLabel || this.inputValue) {
404             this.$.label.classList.add('hidden');
405           }
406
407           if (this.floatingLabel) {
408             this.$.label.classList.remove('focusedColor');
409             this.$.label.classList.remove('animating');
410             this.$.floatedLabel.classList.remove('hidden');
411             this.$.floatedLabel.classList.add('focused');
412             this.$.floatedLabel.classList.add('focusedColor');
413           }
414
415           this.async(function() {
416             this.$.underlineHighlight.classList.remove('animating');
417             this.$.caret.classList.remove('animating');
418           }, null, 100);
419
420         } else {
421
422           this.$.label.classList.remove('animating');
423
424         }
425       }
426
427     });
428
429   }());
430
431   </script>
432
433 </polymer-element>