luci-base: ui.js: rework password input handling
authorJo-Philipp Wich <jo@mein.io>
Tue, 28 Apr 2020 21:34:33 +0000 (23:34 +0200)
committerJo-Philipp Wich <jo@mein.io>
Sat, 2 May 2020 21:53:02 +0000 (23:53 +0200)
 - Get rid of dummy element
 - Render password field as text input initially and later transform
   it to a password field
 - Use .control-group markup to allow better styling

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
modules/luci-base/htdocs/luci-static/resources/ui.js

index 612f74776e7ff1b62da18f30d837d61e9b18fac0..73f5b90bc5e34fbe7d9624a41a58dbcfe9495493 100644 (file)
@@ -310,48 +310,45 @@ var UITextfield = UIElement.extend(/** @lends LuCI.ui.Textfield.prototype */ {
        /** @override */
        render: function() {
                var frameEl = E('div', { 'id': this.options.id });
-
-               if (this.options.password) {
-                       frameEl.classList.add('nowrap');
-                       frameEl.appendChild(E('input', {
-                               'type': 'password',
-                               'style': 'position:absolute; left:-100000px',
-                               'aria-hidden': true,
-                               'tabindex': -1,
-                               'name': this.options.name ? 'password.%s'.format(this.options.name) : null
-                       }));
-               }
-
-               frameEl.appendChild(E('input', {
+               var inputEl = E('input', {
                        'id': this.options.id ? 'widget.' + this.options.id : null,
                        'name': this.options.name,
-                       'type': this.options.password ? 'password' : 'text',
+                       'type': 'text',
                        'class': this.options.password ? 'cbi-input-password' : 'cbi-input-text',
                        'readonly': this.options.readonly ? '' : null,
                        'disabled': this.options.disabled ? '' : null,
                        'maxlength': this.options.maxlength,
                        'placeholder': this.options.placeholder,
                        'value': this.value,
-               }));
+               });
 
-               if (this.options.password)
-                       frameEl.appendChild(E('button', {
-                               'class': 'cbi-button cbi-button-neutral',
-                               'title': _('Reveal/hide password'),
-                               'aria-label': _('Reveal/hide password'),
-                               'click': function(ev) {
-                                       var e = this.previousElementSibling;
-                                       e.type = (e.type === 'password') ? 'text' : 'password';
-                                       ev.preventDefault();
-                               }
-                       }, '∗'));
+               if (this.options.password) {
+                       frameEl.appendChild(E('div', { 'class': 'control-group' }, [
+                               inputEl,
+                               E('button', {
+                                       'class': 'cbi-button cbi-button-neutral',
+                                       'title': _('Reveal/hide password'),
+                                       'aria-label': _('Reveal/hide password'),
+                                       'click': function(ev) {
+                                               var e = this.previousElementSibling;
+                                               e.type = (e.type === 'password') ? 'text' : 'password';
+                                               ev.preventDefault();
+                                       }
+                               }, '∗')
+                       ]));
+
+                       window.requestAnimationFrame(function() { inputEl.type = 'password' });
+               }
+               else {
+                       frameEl.appendChild(inputEl);
+               }
 
                return this.bind(frameEl);
        },
 
        /** @private */
        bind: function(frameEl) {
-               var inputEl = frameEl.childNodes[+!!this.options.password];
+               var inputEl = frameEl.querySelector('input');
 
                this.node = frameEl;
 
@@ -365,7 +362,7 @@ var UITextfield = UIElement.extend(/** @lends LuCI.ui.Textfield.prototype */ {
 
        /** @override */
        getValue: function() {
-               var inputEl = this.node.childNodes[+!!this.options.password];
+               var inputEl = this.node.querySelector('input');
                return inputEl.value;
        },