1. #1
    Sencha User
    Join Date
    Apr 2010
    Posts
    2
    Vote Rating
    0
    Mejari is on a distinguished road

      0  

    Default Ext.ux.form.field.PasswordField

    Ext.ux.form.field.PasswordField


    Hey all! I've been playing around with this for a little while and thought it would be good to share. This is a password field that has two text fields for entering and repeating a password, a password strength meter that checks the strength of the entered password, and a notification of whether or not your repeated password matches your entered password.

    This is my first attempt at a full extension for public use, so please be gentle but please give feedback. I'm not sure I've done everything I need to to make this drop-n-go'able for most forms, so any help cleaning that up would be very much appreciated. And of course feel free to use/abuse/whatever you like.

    Also, the current way I implemented the field labels is very hacky and I'd love another option. Right now, in order to have the labels line up correctly, I'm force-overriding any user-defined fieldLabel and setting it to: 'Password:<br/><br/>Confirm Password'. This lets the field labels line up with the rest of the form's labels and to have labels for both password boxes.

    CSS is in the header comment and required images (X and checkmark) are attached.

    Thanks for checking it out!
    -Dylan Gulick
    Jama Software

    Here are some screenshots:
    passwordField1.png


    passwordField2.png


    And here's the code:

    Code:
    Ext.namespace('Ext.ux.form.field');
    /**
     * Password Field.
     *
     * @class Ext.ux.form.field.PasswordField
     * @extends Ext.form.Field
     * 
     * Associated CSS:
     .j-password-field h4 {margin-bottom: 3px;}
    .j-password-field .containerBar {height: 6px; width: 170px; margin-bottom: 15px; background-color: #E6E6E6;}
    .j-password-field .percentage {height: 6px;}
    .j-password-field .strong .percentage {background-color: green;}
    .j-password-field .strong span {color: green;}
    .j-password-field .good .percentage {background-color: #6699FF;}
    .j-password-field .good span {color: #6699FF;}
    .j-password-field .fair .percentage {background-color: #FFCC00;}
    .j-password-field .fair span {color: #FFCC00;}
    
    
    .j-password-field .matches {color:green;background-image:url(../../../img/16/green_check_mark.png)  !important; background-repeat: no-repeat; padding-left:22px; line-height:16px; display:block; font-size:10pt;}
    .j-password-field .doesnt-match {color:red; background-image:url(../../../img/16/cross.png) !important; background-repeat: no-repeat; padding-left:22px; line-height:16px; display:block; font-size:10pt;}
     */
    Ext.ux.form.field.PasswordField = Ext.extend(Ext.form.Field, {
        width: 500,
        validateOnBlur: true,
        fieldLabel: 'Password:<br/><br/>Confirm Password',//Very Hacky
        
        constructor: function(config) {
            if(config.name) {
                this.passwordFieldName = config.name;
                config.name = null;
            }
            Ext.apply(this, config);
            Ext.ux.form.field.PasswordField.superclass.constructor.apply(this, config);
        },
        
        initComponent: function() {
            Ext.applyIf(this, {
                minLength: 6,
                maxLength: 64,
                regexText: "Invalid Password"
            });
            
            Ext.apply(this, {
                regex: new RegExp('^[A-Za-z0-9!@#$%^&\*_]{'+this.minLength+','+this.maxLength+'}$','i')
            });
            
            Ext.ux.form.field.PasswordField.superclass.initComponent.apply(this);
        },
        
        getName: function() {
    	 return this.passwordFieldName;
        },
    
    
        onPasswordKeyUp: function(event, el) {
            var pwd = this.passwordField.getValue();
            var strength = this.getPasswordStrength(pwd);
            var labelConfig;
            switch(strength)
            {
                case 1:
                    labelConfig = {
                        pClass: "fair",
                        pLabel: 'Fair',
                        width: 50,
                    };
                    break;
                case 2:
                    labelConfig = {
                        pClass: "good",
                        pLabel: 'Good',
                        width: 75,
                    };
                    break;
                case 3:
                    labelConfig = {
                        pClass: "strong",
                        pLabel: 'Strong',
                        width: 100,
                    };
                    break;
                case 0:
                default:
                    labelConfig = {
                        pClass: "",
                        pLabel: 'Too short',
                        width: 0,
                    };
                    break;
            }
            this.passwordStrengthLabel.update(labelConfig);
            
            if(this.repeatPasswordField.getValue()) {
                this.updateRepeatMatch(pwd);
            }
        },
        
        onRepeatKeyUp: function(event, el) {
            var pwd = this.passwordField.getValue();
            this.updateRepeatMatch(pwd);
        },
        
        updateRepeatMatch: function(pwd) {
            var repeatedPwd = this.repeatPasswordField.getValue();
            this.repeatPasswordCorrectLabel.removeClass('matches');
            this.repeatPasswordCorrectLabel.removeClass('doesnt-match');
            
            if(!repeatedPwd || repeatedPwd.length == 0) {
                this.repeatPasswordCorrectLabel.update('');
            } else if (repeatedPwd == pwd) {
                this.repeatPasswordCorrectLabel.addClass('matches');
                this.repeatPasswordCorrectLabel.update('&nbsp;');
            } else {
                this.repeatPasswordCorrectLabel.addClass('doesnt-match');
                this.repeatPasswordCorrectLabel.update("Doesn't match");
            }
        },
        
        getPasswordStrength: function(password) {
            //RegExes from http://www.marketingtechblog.com/javascript-password-strength/
            var strongRegex = new RegExp('^(?=.{'+(this.minLength+4)+',})(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*\\W).*$', 'g');
            var mediumRegex = new RegExp('^(?=.{'+(this.minLength+2)+',})(((?=.*[A-Z])(?=.*[a-z]))|((?=.*[A-Z])(?=.*[0-9]))|((?=.*[a-z])(?=.*[0-9]))).*$', 'g');
            var strength;
            if (password.length < this.minLength) {
                strength=0;
            } else if (strongRegex.test(password)) {
                strength=3;
            } else if (mediumRegex.test(password)) {
                strength=2;
            } else {
                strength = 1;
            }
            return strength;
        },
        
        getValue : function() {
            if(this.repeatPasswordField.getValue() == this.passwordField.getValue()) {
                return this.passwordField.getValue();
            }
            
            return '';
        },
        
        setValue : function(v) {
            this.passwordField.setValue(v);
            return this;
        },
        
        isDirty: function() {
            if (this.rendered && !this.disabled) {
                if(this.passwordField.isDirty() || this.repeatPasswordFIeld.isDirty()) {
                    return true;
                }
            }
            return false;
        },
        
        getErrors: function() {
            var errors = [];
            var pwd = this.passwordField.getValue();
            if(!pwd || pwd.length < this.minLength) {
                errors.push('Password must be at least ' + this.minLength + ' charaters');
            }
            if(this.repeatPasswordField.getValue() != pwd) {
                errors.push('Password confirmation incorrect');
            }
            return errors;
        },
        
        onRender: function(ct, position){
            if(!this.el) {
                this.panel = new Ext.Container({
                    layout: 'form',
                    width: 400,
                    renderTo: ct,
                    border: false,
                    autoWidth: true,
                    items: [
                        {
                            xtype: 'container',
                            layout:'column',
                            border: false,
                            cls: 'j-password-field',
                            width: 400,
                            items:[
                            {
                                xtype: 'container',
                                columnWidth: .5,
                                layout: {
                                    type: 'form',
                                    fieldTpl: new Ext.Template(
                                            '<div class="x-form-item {itemCls}" tabIndex="-1">',//Overriding the default fieldTpl to remove the label
                                                '<div class="x-form-element" id="x-form-el-{id}" style="{elementStyle};padding-left:0px;">',
                                                '</div><div class="{clearCls}"></div>',
                                            '</div>'
                                        )
                                },
                                border: false,
                                style: 'margin-right:10px;',
                                items: [
                                       this.passwordField = new Ext.form.TextField({
                                           fieldLabel: i18n.g('j.l.password'),
                                           regex: this.regex,
                                           regexText: this.regexText,
                                           name: this.passwordFieldName,
                                        allowBlank: false,
                                        width: 115,
                                        enableKeyEvents: true,
                                        inputType: 'password',
                                        listeners: {
                                            afterrender: function(pwdField) {
                                                pwdField.getEl().on('keyup', this.onPasswordKeyUp, this);
                                            },
                                            scope: this
                                        }
                                    }),
                                    this.repeatPasswordField = new Ext.form.TextField({
                                        fieldLabel: i18n.g('j.l.confirmPassword'),
                                           regex: this.regex,
                                           regexText: this.regexText,
                                           submitValue: false,
                                        allowBlank: false,
                                        inputType: 'password',
                                        width: 115,
                                        enableKeyEvents: true,
                                        listeners: {
                                            afterrender: function() {
                                                this.repeatPasswordField.getEl().on('keyup', this.onRepeatKeyUp, this);
                                            },
                                            scope: this
                                        }
                                    })
                                ]
                            },{ 
                                xtype: 'container',
                                columnWidth: .5,
                                layout: 'form',
                                border: false,
                                items: [
                                     this.passwordStrengthLabel = new Ext.form.Label({
                                        width: 200,
                                        tpl: new Ext.XTemplate(
                                            '<div ext:qtip="{[this.getQtipText()]}" class="{pClass}">' +
                                                '<h4>Password Strength: <span>{pLabel}</span></h4>' +
                                                '<div class="containerBar">' +
                                                    '<div class="percentage" style="width: {width}%"></div>' +
                                                '</div>' +
                                            '</div>', {
                                                getQtipText: function() {
                                                    return 'Strong passwords contain at least '+this.minLength+ ' characters, ' +
                                                    'do not include common words or names, and combine uppercase letters, ' +
                                                    'lowercase letters, numbers and symbols (!@#$%^&*_).';
                                                }.createDelegate(this)
                                            }),
                                        listeners: {
                                            afterrender: function(pwdStrengthLabel) {
                                                pwdStrengthLabel.update({
                                                    pClass: "",
                                                    pLabel: 'Too short',
                                                    width: 0,
                                                });
                                            },
                                            scope: this
                                        }
                                    }),
                                    this.repeatPasswordCorrectLabel = new Ext.form.Label({
                                        text: ''
                                    })
                                ]
                            }]
                        }
                    ]
                });
                this.el = this.panel.getEl();
            }
            Ext.ux.form.field.PasswordField.superclass.onRender.call(this, ct, position);
        },
        
        onDisable: function() {
            this.delegateFn('disable');
        },
    
    
        onEnable: function() {
            this.delegateFn('enable');
        },
    
    
        reset: function() {
            this.delegateFn('reset');
        },
    
    
        delegateFn: function(fn) {
            this.passwordField[fn]();
            this.repeatPasswordField[fn]();
        },
        
        beforeDestroy: function() {
            Ext.destroy(this.panel);
            Ext.ux.form.field.PasswordField.superclass.beforeDestroy.call(this);
        },
        
        getRawValue: Ext.emptyFn,
        setRawValue: Ext.emptyFn
        
    });
    Attached Images
    Last edited by Mejari; 10 Sep 2012 at 9:52 PM. Reason: Added 'getName' method, set some widths

  2. #2
    Sencha - Support Team scottmartin's Avatar
    Join Date
    Jul 2010
    Location
    Houston, Tx
    Posts
    8,990
    Vote Rating
    455
    scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future

      0  

    Default


    Thank you for sharing.

    Scott.

Thread Participants: 1