2015-08-17 17:00:26 -07:00
/ * *
* @ file
* User behaviors .
* /
( function ( $ , Drupal , drupalSettings ) {
2015-10-21 21:44:50 -07:00
'use strict' ;
2015-08-17 17:00:26 -07:00
/ * *
* Attach handlers to evaluate the strength of any password fields and to
* check that its confirmation is correct .
*
* @ type { Drupal ~ behavior }
2015-09-04 13:20:09 -07:00
*
* @ prop { Drupal ~ behaviorAttach } attach
* Attaches password strength indicator and other relevant validation to
* password fields .
2015-08-17 17:00:26 -07:00
* /
Drupal . behaviors . password = {
attach : function ( context , settings ) {
var $passwordInput = $ ( context ) . find ( 'input.js-password-field' ) . once ( 'password' ) ;
if ( $passwordInput . length ) {
var translate = settings . password ;
var $passwordInputParent = $passwordInput . parent ( ) ;
var $passwordInputParentWrapper = $passwordInputParent . parent ( ) ;
var $passwordSuggestions ;
// Add identifying class to password element parent.
$passwordInputParent . addClass ( 'password-parent' ) ;
// Add the password confirmation layer.
$passwordInputParentWrapper
. find ( 'input.js-password-confirm' )
. parent ( )
2015-09-04 13:20:09 -07:00
. append ( '<div aria-live="polite" aria-atomic="true" class="password-confirm js-password-confirm">' + translate . confirmTitle + ' <span></span></div>' )
2015-08-17 17:00:26 -07:00
. addClass ( 'confirm-parent' ) ;
var $confirmInput = $passwordInputParentWrapper . find ( 'input.js-password-confirm' ) ;
var $confirmResult = $passwordInputParentWrapper . find ( 'div.js-password-confirm' ) ;
var $confirmChild = $confirmResult . find ( 'span' ) ;
// If the password strength indicator is enabled, add its markup.
if ( settings . password . showStrengthIndicator ) {
2015-09-04 13:20:09 -07:00
var passwordMeter = '<div class="password-strength"><div class="password-strength__meter"><div class="password-strength__indicator js-password-strength__indicator"></div></div><div aria-live="polite" aria-atomic="true" class="password-strength__title">' + translate . strengthTitle + ' <span class="password-strength__text js-password-strength__text"></span></div></div>' ;
2015-08-17 17:00:26 -07:00
$confirmInput . parent ( ) . after ( '<div class="password-suggestions description"></div>' ) ;
$passwordInputParent . append ( passwordMeter ) ;
$passwordSuggestions = $passwordInputParentWrapper . find ( 'div.password-suggestions' ) . hide ( ) ;
}
// Check that password and confirmation inputs match.
var passwordCheckMatch = function ( confirmInputVal ) {
var success = $passwordInput . val ( ) === confirmInputVal ;
var confirmClass = success ? 'ok' : 'error' ;
// Fill in the success message and set the class accordingly.
$confirmChild . html ( translate [ 'confirm' + ( success ? 'Success' : 'Failure' ) ] )
. removeClass ( 'ok error' ) . addClass ( confirmClass ) ;
} ;
// Check the password strength.
var passwordCheck = function ( ) {
if ( settings . password . showStrengthIndicator ) {
// Evaluate the password strength.
var result = Drupal . evaluatePasswordStrength ( $passwordInput . val ( ) , settings . password ) ;
// Update the suggestions for how to improve the password.
if ( $passwordSuggestions . html ( ) !== result . message ) {
$passwordSuggestions . html ( result . message ) ;
}
// Only show the description box if a weakness exists in the
// password.
$passwordSuggestions . toggle ( result . strength !== 100 ) ;
// Adjust the length of the strength indicator.
$passwordInputParent . find ( '.js-password-strength__indicator' )
. css ( 'width' , result . strength + '%' )
. removeClass ( 'is-weak is-fair is-good is-strong' )
. addClass ( result . indicatorClass ) ;
// Update the strength indication text.
$passwordInputParent . find ( '.js-password-strength__text' ) . html ( result . indicatorText ) ;
}
// Check the value in the confirm input and show results.
if ( $confirmInput . val ( ) ) {
passwordCheckMatch ( $confirmInput . val ( ) ) ;
$confirmResult . css ( { visibility : 'visible' } ) ;
}
else {
$confirmResult . css ( { visibility : 'hidden' } ) ;
}
} ;
// Monitor input events.
$passwordInput . on ( 'input' , passwordCheck ) ;
$confirmInput . on ( 'input' , passwordCheck ) ;
}
}
} ;
/ * *
* Evaluate the strength of a user ' s password .
*
* Returns the estimated strength and the relevant output message .
*
* @ param { string } password
2015-09-04 13:20:09 -07:00
* The password to evaluate .
2015-08-17 17:00:26 -07:00
* @ param { object } translate
2015-09-04 13:20:09 -07:00
* An object containing the text to display for each strength level .
2015-08-17 17:00:26 -07:00
*
* @ return { object }
2015-09-04 13:20:09 -07:00
* An object containing strength , message , indicatorText and indicatorClass .
2015-08-17 17:00:26 -07:00
* /
Drupal . evaluatePasswordStrength = function ( password , translate ) {
password = password . trim ( ) ;
var indicatorText ;
var indicatorClass ;
var weaknesses = 0 ;
var strength = 100 ;
var msg = [ ] ;
var hasLowercase = /[a-z]/ . test ( password ) ;
var hasUppercase = /[A-Z]/ . test ( password ) ;
var hasNumbers = /[0-9]/ . test ( password ) ;
var hasPunctuation = /[^a-zA-Z0-9]/ . test ( password ) ;
// If there is a username edit box on the page, compare password to that,
// otherwise use value from the database.
var $usernameBox = $ ( 'input.username' ) ;
var username = ( $usernameBox . length > 0 ) ? $usernameBox . val ( ) : translate . username ;
2015-10-08 11:40:12 -07:00
// Lose 5 points for every character less than 12, plus a 30 point penalty.
if ( password . length < 12 ) {
2015-08-17 17:00:26 -07:00
msg . push ( translate . tooShort ) ;
2015-10-08 11:40:12 -07:00
strength -= ( ( 12 - password . length ) * 5 ) + 30 ;
2015-08-17 17:00:26 -07:00
}
// Count weaknesses.
if ( ! hasLowercase ) {
msg . push ( translate . addLowerCase ) ;
weaknesses ++ ;
}
if ( ! hasUppercase ) {
msg . push ( translate . addUpperCase ) ;
weaknesses ++ ;
}
if ( ! hasNumbers ) {
msg . push ( translate . addNumbers ) ;
weaknesses ++ ;
}
if ( ! hasPunctuation ) {
msg . push ( translate . addPunctuation ) ;
weaknesses ++ ;
}
// Apply penalty for each weakness (balanced against length penalty).
switch ( weaknesses ) {
case 1 :
strength -= 12.5 ;
break ;
case 2 :
strength -= 25 ;
break ;
case 3 :
strength -= 40 ;
break ;
case 4 :
strength -= 40 ;
break ;
}
// Check if password is the same as the username.
if ( password !== '' && password . toLowerCase ( ) === username . toLowerCase ( ) ) {
msg . push ( translate . sameAsUsername ) ;
// Passwords the same as username are always very weak.
strength = 5 ;
}
// Based on the strength, work out what text should be shown by the
// password strength meter.
if ( strength < 60 ) {
indicatorText = translate . weak ;
indicatorClass = 'is-weak' ;
}
else if ( strength < 70 ) {
indicatorText = translate . fair ;
indicatorClass = 'is-fair' ;
}
else if ( strength < 80 ) {
indicatorText = translate . good ;
indicatorClass = 'is-good' ;
}
else if ( strength <= 100 ) {
indicatorText = translate . strong ;
indicatorClass = 'is-strong' ;
}
// Assemble the final message.
msg = translate . hasWeaknesses + '<ul><li>' + msg . join ( '</li><li>' ) + '</li></ul>' ;
return {
strength : strength ,
message : msg ,
indicatorText : indicatorText ,
indicatorClass : indicatorClass
} ;
} ;
} ) ( jQuery , Drupal , drupalSettings ) ;