Sunday, September 18, 2011

Ext JS 4 password strength meter

One of my friends wants me to help him on putting a password strength meter on his registration page. Inspired by a post on Ext JS 1.x forum, I created a Ext JS 4 compatible one as shown below,


To do this, what you need is 1) the following Ext JS widget extends from Ext.form.field.Text. 2) do not forget the CSS file and images used in CSS. To change the appearance of password meter, you can simply change images used in CSS.

JavaScript code:
Ext.define('yiyu.util.PasswordMeter',
    {
     extend : 'Ext.form.field.Text',
     alias : 'widget.passwordMeter',
     inputType : 'password',

     reset : function() {
      this.callParent();
      this.updateMeter(this);
     },
     
     //private
     onRender : function(container, position) {
      var me = this;
      me.callParent(arguments);
      this.objMeter = me.el.createChild({
       tag : "div",
       'class' : "strengthMeter"
      });
      me.objMeter.setWidth(me.el.getWidth(true) - 17);
      me.scoreBar = me.objMeter.createChild({
       tag : "div",
       'class' : "scoreBar"
      });
      me.scoreBar.setWidth(me.objMeter.getWidth(true));

      if (Ext.isIE6) { // Fix style for IE6
       this.objMeter.setStyle('margin-left', '3px');
      }
     },

     // private
     initEvents : function() {
      var me = this, el = me.inputEl;
      me.callParent();
      me.mon(el, {
       scope : me,
       keyup : me.updateMeter
      });
     },
     /**
      * Sets the width of the meter, based on the score
      * 
      * @param {Object} e
      * Private function 
      */
     updateMeter : function() {
      var score, p, maxWidth, nScore, scoreWidth;
      score = 0;
      p = this.getValue();
      
      maxWidth = this.objMeter.getWidth() - 2;

      nScore = this.calcStrength(p);

      scoreWidth = maxWidth - (maxWidth / 100) * nScore;
      
      this.scoreBar.setWidth(scoreWidth, true);
     },

     /**
      * Calculates the strength of a password
      * 
      * @param {Object} p
      *   The password that needs to be calculated
      * @return {int} intScore The strength score of the password
      */
     calcStrength : function(p) {
      var intScore = 0;

      // PASSWORD LENGTH
      intScore += p.length;

      if (p.length > 0 && p.length <= 4) { // length 4 or
                // less
       intScore += p.length;
      } else if (p.length >= 5 && p.length <= 7) { 
       // length between 5 and 7
       intScore += 6;
      } else if (p.length >= 8 && p.length <= 15) { 
       // length between 8 and 15
       intScore += 12;       
      } else if (p.length >= 16) { // length 16 or more
       intScore += 18;       
      }

      // LETTERS (Not exactly implemented as dictacted above
      // because of my limited understanding of Regex)
      if (p.match(/[a-z]/)) { 
       // [verified] at least one lower case letter
       intScore += 1;
      }
      if (p.match(/[A-Z]/)) { // [verified] at least one upper
            // case letter
       intScore += 5;
      }
      // NUMBERS
      if (p.match(/\d/)) { // [verified] at least one
            // number
       intScore += 5;
      }
      if (p.match(new RegExp(".*\\d.*\\d.*\\d"))) {
       // [verified] at least three numbers
       intScore += 5;
      }

      // SPECIAL CHAR
      if (p.match(new RegExp("[!,@,#,$,%,^,&,*,?,_,~]"))) {
       // [verified] at least one special character
       intScore += 5;
      }
      // [verified] at least two special characters
      if (p.match(new RegExp(
          ".*[!,@,#,$,%,^,&,*,?,_,~].*[!,@,#,$,%,^,&,*,?,_,~]"
        ))) {
       intScore += 5;
      }

      // COMBOS
      if (p.match(new RegExp("(?=.*[a-z])(?=.*[A-Z])"))) {
       // [verified] both upper and lower case
       intScore += 2;
      }
      if (p.match(new RegExp(
        "(?=.*\\d)(?=.*[a-z])(?=.*[A-Z])"))) {
       // [verified] both letters and numbers
       intScore += 2;
      }
      // [verified] letters, numbers, and special characters
      if (p
        .match(new RegExp(
          "(?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!,@,#,$,%,^,&,*,?,_,~])"
          ))) {
       intScore += 2;
      }

      var nRound = Math.round(intScore * 2);

      if (nRound > 100) {
       nRound = 100;
      }

      return nRound;
     }
    })


CSS code:
.strengthMeter {
 border: 1px solid #B5B8C8;
 margin: 3px 0 3px 0;
 background-image: url(images/meter.gif);
 height: 10px;
 background-size: 100%; 
}

.scoreBar {
 background-image: url(images/meter_background.gif);
 height: 10px;
 background-size: 100%; 
 line-height: 1px;
 font-size: 1px;
 float: right;
}


Edit: Here is a better implementation crated by osnoek.

1 comment: