|
1 (function ($) { |
|
2 |
|
3 /** |
|
4 * Attach handlers to evaluate the strength of any password fields and to check |
|
5 * that its confirmation is correct. |
|
6 */ |
|
7 Drupal.behaviors.password = { |
|
8 attach: function (context, settings) { |
|
9 var translate = settings.password; |
|
10 $('input.password-field', context).once('password', function () { |
|
11 var passwordInput = $(this); |
|
12 var innerWrapper = $(this).parent(); |
|
13 var outerWrapper = $(this).parent().parent(); |
|
14 |
|
15 // Add identifying class to password element parent. |
|
16 innerWrapper.addClass('password-parent'); |
|
17 |
|
18 // Add the password confirmation layer. |
|
19 $('input.password-confirm', outerWrapper).parent().prepend('<div class="password-confirm">' + translate['confirmTitle'] + ' <span></span></div>').addClass('confirm-parent'); |
|
20 var confirmInput = $('input.password-confirm', outerWrapper); |
|
21 var confirmResult = $('div.password-confirm', outerWrapper); |
|
22 var confirmChild = $('span', confirmResult); |
|
23 |
|
24 // Add the description box. |
|
25 var passwordMeter = '<div class="password-strength"><div class="password-strength-text" aria-live="assertive"></div><div class="password-strength-title">' + translate['strengthTitle'] + '</div><div class="password-indicator"><div class="indicator"></div></div></div>'; |
|
26 $(confirmInput).parent().after('<div class="password-suggestions description"></div>'); |
|
27 $(innerWrapper).prepend(passwordMeter); |
|
28 var passwordDescription = $('div.password-suggestions', outerWrapper).hide(); |
|
29 |
|
30 // Check the password strength. |
|
31 var passwordCheck = function () { |
|
32 |
|
33 // Evaluate the password strength. |
|
34 var result = Drupal.evaluatePasswordStrength(passwordInput.val(), settings.password); |
|
35 |
|
36 // Update the suggestions for how to improve the password. |
|
37 if (passwordDescription.html() != result.message) { |
|
38 passwordDescription.html(result.message); |
|
39 } |
|
40 |
|
41 // Only show the description box if there is a weakness in the password. |
|
42 if (result.strength == 100) { |
|
43 passwordDescription.hide(); |
|
44 } |
|
45 else { |
|
46 passwordDescription.show(); |
|
47 } |
|
48 |
|
49 // Adjust the length of the strength indicator. |
|
50 $(innerWrapper).find('.indicator').css('width', result.strength + '%'); |
|
51 |
|
52 // Update the strength indication text. |
|
53 $(innerWrapper).find('.password-strength-text').html(result.indicatorText); |
|
54 |
|
55 passwordCheckMatch(); |
|
56 }; |
|
57 |
|
58 // Check that password and confirmation inputs match. |
|
59 var passwordCheckMatch = function () { |
|
60 |
|
61 if (confirmInput.val()) { |
|
62 var success = passwordInput.val() === confirmInput.val(); |
|
63 |
|
64 // Show the confirm result. |
|
65 confirmResult.css({ visibility: 'visible' }); |
|
66 |
|
67 // Remove the previous styling if any exists. |
|
68 if (this.confirmClass) { |
|
69 confirmChild.removeClass(this.confirmClass); |
|
70 } |
|
71 |
|
72 // Fill in the success message and set the class accordingly. |
|
73 var confirmClass = success ? 'ok' : 'error'; |
|
74 confirmChild.html(translate['confirm' + (success ? 'Success' : 'Failure')]).addClass(confirmClass); |
|
75 this.confirmClass = confirmClass; |
|
76 } |
|
77 else { |
|
78 confirmResult.css({ visibility: 'hidden' }); |
|
79 } |
|
80 }; |
|
81 |
|
82 // Monitor keyup and blur events. |
|
83 // Blur must be used because a mouse paste does not trigger keyup. |
|
84 passwordInput.keyup(passwordCheck).focus(passwordCheck).blur(passwordCheck); |
|
85 confirmInput.keyup(passwordCheckMatch).blur(passwordCheckMatch); |
|
86 }); |
|
87 } |
|
88 }; |
|
89 |
|
90 /** |
|
91 * Evaluate the strength of a user's password. |
|
92 * |
|
93 * Returns the estimated strength and the relevant output message. |
|
94 */ |
|
95 Drupal.evaluatePasswordStrength = function (password, translate) { |
|
96 password = $.trim(password); |
|
97 |
|
98 var weaknesses = 0, strength = 100, msg = []; |
|
99 |
|
100 var hasLowercase = /[a-z]+/.test(password); |
|
101 var hasUppercase = /[A-Z]+/.test(password); |
|
102 var hasNumbers = /[0-9]+/.test(password); |
|
103 var hasPunctuation = /[^a-zA-Z0-9]+/.test(password); |
|
104 |
|
105 // If there is a username edit box on the page, compare password to that, otherwise |
|
106 // use value from the database. |
|
107 var usernameBox = $('input.username'); |
|
108 var username = (usernameBox.length > 0) ? usernameBox.val() : translate.username; |
|
109 |
|
110 // Lose 5 points for every character less than 6, plus a 30 point penalty. |
|
111 if (password.length < 6) { |
|
112 msg.push(translate.tooShort); |
|
113 strength -= ((6 - password.length) * 5) + 30; |
|
114 } |
|
115 |
|
116 // Count weaknesses. |
|
117 if (!hasLowercase) { |
|
118 msg.push(translate.addLowerCase); |
|
119 weaknesses++; |
|
120 } |
|
121 if (!hasUppercase) { |
|
122 msg.push(translate.addUpperCase); |
|
123 weaknesses++; |
|
124 } |
|
125 if (!hasNumbers) { |
|
126 msg.push(translate.addNumbers); |
|
127 weaknesses++; |
|
128 } |
|
129 if (!hasPunctuation) { |
|
130 msg.push(translate.addPunctuation); |
|
131 weaknesses++; |
|
132 } |
|
133 |
|
134 // Apply penalty for each weakness (balanced against length penalty). |
|
135 switch (weaknesses) { |
|
136 case 1: |
|
137 strength -= 12.5; |
|
138 break; |
|
139 |
|
140 case 2: |
|
141 strength -= 25; |
|
142 break; |
|
143 |
|
144 case 3: |
|
145 strength -= 40; |
|
146 break; |
|
147 |
|
148 case 4: |
|
149 strength -= 40; |
|
150 break; |
|
151 } |
|
152 |
|
153 // Check if password is the same as the username. |
|
154 if (password !== '' && password.toLowerCase() === username.toLowerCase()) { |
|
155 msg.push(translate.sameAsUsername); |
|
156 // Passwords the same as username are always very weak. |
|
157 strength = 5; |
|
158 } |
|
159 |
|
160 // Based on the strength, work out what text should be shown by the password strength meter. |
|
161 if (strength < 60) { |
|
162 indicatorText = translate.weak; |
|
163 } else if (strength < 70) { |
|
164 indicatorText = translate.fair; |
|
165 } else if (strength < 80) { |
|
166 indicatorText = translate.good; |
|
167 } else if (strength <= 100) { |
|
168 indicatorText = translate.strong; |
|
169 } |
|
170 |
|
171 // Assemble the final message. |
|
172 msg = translate.hasWeaknesses + '<ul><li>' + msg.join('</li><li>') + '</li></ul>'; |
|
173 return { strength: strength, message: msg, indicatorText: indicatorText }; |
|
174 |
|
175 }; |
|
176 |
|
177 /** |
|
178 * Field instance settings screen: force the 'Display on registration form' |
|
179 * checkbox checked whenever 'Required' is checked. |
|
180 */ |
|
181 Drupal.behaviors.fieldUserRegistration = { |
|
182 attach: function (context, settings) { |
|
183 var $checkbox = $('form#field-ui-field-edit-form input#edit-instance-settings-user-register-form'); |
|
184 |
|
185 if ($checkbox.length) { |
|
186 $('input#edit-instance-required', context).once('user-register-form-checkbox', function () { |
|
187 $(this).bind('change', function (e) { |
|
188 if ($(this).attr('checked')) { |
|
189 $checkbox.attr('checked', true); |
|
190 } |
|
191 }); |
|
192 }); |
|
193 |
|
194 } |
|
195 } |
|
196 }; |
|
197 |
|
198 })(jQuery); |