1/** 2 * @requires Prototype.js 3 * @param A string with the input field ID 4 * @return Public API, init() which initializes the password-strength component 5 * @description PasswordStrength component to check the strength and valid passwords entered by the user. 6 */ 7 8 9window.PbLib = window.PbLib || {}; 10PbLib.UI = PbLib.UI || {}; 11 12PbLib.UI.formElementPasswordChecker = function (domId, showStrength) { 13 this.changeDelay = false; 14 15 // Make sure to behave as a constructor function 16 if (PbLib.UI === this) { 17 return new PbLib.UI.formElementPasswordChecker(domId, showStrength); 18 } 19 20 this.showStrength = !!showStrength; 21 22 // Collect al the password strenth checker elments 23 this.passwordField = document.getElementById(domId); 24 this.checkboxElem = document.getElementById(domId + '-psc-checkbox'); 25 this.checkboxElem.parentElement.removeAttribute('hidden'); 26 27 document.addEventListener('click', this.handleOnClick.bind(this)); 28 29 if (!showStrength) { 30 return; 31 } 32 33 var passwordFieldWidth = this.passwordField.offsetWidth; 34 this.passwordStrengthElem = document.createElement('span'); 35 this.passwordStrengthElem.classList.add('psc-pi-strength'); 36 this.passwordField.parentElement.insertBefore(this.passwordStrengthElem, this.passwordField); 37 this.passwordStrengthElem.appendChild(this.passwordField); 38 39 this.passwordStrengthContainerElem = this.passwordStrengthElem.parentElement.parentElement; 40 this.confirmPasswordField = this.passwordStrengthContainerElem.querySelector('.psc-password-repeat input'); 41 42 this.passwordField.style.width = passwordFieldWidth + 'px'; 43 44 // Add an element to show the strength text, weak, mediocre, strong, very strong. 45 if (!this.passwordStrengthTextElem) { 46 this.passwordStrengthTextElem = document.createElement('span'); 47 this.passwordStrengthTextElem.classList.add('psc-ps-text'); 48 this.passwordField.parentElement.appendChild(this.passwordStrengthTextElem); 49 } 50 51 // Set the observers on the input element and the document for handeling keystrokes and click. 52 this.passwordField.addEventListener('keyup', this.handleOnChange.bind(this)); 53 this.passwordField.addEventListener('change', this.handleOnChange.bind(this)); 54 55 if (this.confirmPasswordField) { 56 this.confirmPasswordField.style.width = passwordFieldWidth + 'px'; 57 this.confirmPasswordField.addEventListener('keyup', this.handleOnChange.bind(this)); 58 this.confirmPasswordField.addEventListener('change', this.handleOnChange.bind(this)); 59 } 60}; 61 62PbLib.UI.formElementPasswordChecker.prototype.getEntropy = function (password) { 63 if (!PbLib.UI.hasOwnProperty('entropyData')) { 64 PbLib.UI.entropyData = { 65 'sets': [ 66 ['0'.charCodeAt(0), '9'.charCodeAt(0)], 67 ['A'.charCodeAt(0), 'Z'.charCodeAt(0)], 68 ['a'.charCodeAt(0), 'z'.charCodeAt(0)], 69 ], 70 }; 71 PbLib.UI.entropyData.lastSet = PbLib.UI.entropyData.sets.length; 72 let lastCharCode = PbLib.UI.entropyData.sets.at(-1)[1]; 73 PbLib.UI.entropyData.sets.push([lastCharCode + 1, lastCharCode + 1]); 74 } 75 76 const pLen = password.length; 77 if (pLen <= 0) { 78 return 0; 79 } 80 81 const usedRanges = {}; 82 for (let i = 0; i < pLen; i++) { 83 let charCode = password.charCodeAt(i); 84 for (let s = 0; s <= PbLib.UI.entropyData.lastSet; s++) { 85 if (charCode <= PbLib.UI.entropyData.sets[s][1] || s === PbLib.UI.entropyData.lastSet) { 86 if (!usedRanges.hasOwnProperty(s)) { 87 usedRanges[s] = [charCode, charCode]; 88 } else { 89 usedRanges[s][0] = Math.min(usedRanges[s][0], charCode); 90 usedRanges[s][1] = Math.max(usedRanges[s][1], charCode); 91 } 92 break; 93 } 94 } 95 } 96 const totalPasswordSpace = Object.keys(usedRanges).reduce( 97 (sum, key) => sum + usedRanges[key][1] - usedRanges[key][0] + 1, 98 0 99 ); 100 return Math.floor(pLen * Math.log(totalPasswordSpace) / Math.log(2)); 101}; 102 103PbLib.UI.formElementPasswordChecker.prototype.handleOnChange = function (event) { 104 105 var element = event.target; 106 var pw = this.passwordField.value; 107 var ent; 108 109 // Get the password strength 110 if (pw) { 111 ent = this.getEntropy(pw); 112 } 113 114 // Set the correct class and text according to the password strength score. 115 if (!pw && (element === this.passwordField || element === this.confirmPasswordField)) { 116 this.passwordStrengthElem.className = 'psc-pi-strength'; 117 } else if (ent < 40) {
118 this.passwordStrengthTextElem.innerHTML = 'Weak'; 119 this.passwordStrengthElem.className = 'psc-pi-strength psc-password-strength-weak'; 120 } else if (ent < 49) { 121 this.passwordStrengthTextElem.innerHTML = 'Fair'; 122 this.passwordStrengthElem.className = 'psc-pi-strength psc-password-strength-fair'; 123 } else if (ent < 65) { 124 this.passwordStrengthTextElem.innerHTML = 'Strong'; 125 this.passwordStrengthElem.className = 'psc-pi-strength psc-password-strength-strong'; 126 } else { 127 this.passwordStrengthTextElem.innerHTML = 'Very strong'; 128 this.passwordStrengthElem.className = 'psc-pi-strength psc-password-strength-verystrong'; 129 } 130 131 // Trigger password validator on input field changes. 132 if (this.confirmPasswordField && 133 (element === this.passwordField || element === this.confirmPasswordField) 134 ) { 135 if (this.changeDelay) { 136 window.clearTimeout(this.changeDelay); 137 this.changeDelay = false; 138 } 139 140 var boundMatchPasswords = (function () { 141 this.matchPasswords(); 142 }).bind(this); 143 144 this.changeDelay = setTimeout(function () { 145 boundMatchPasswords(); 146 }, 1000); 147 } 148}; 149 150PbLib.UI.formElementPasswordChecker.prototype.toggleInputType = function () { 151 // Change the password input type to text or password 152 var inputType = this.checkboxElem.checked === true ? 'text' : 'password'; 153 154 this.passwordField.setAttribute('type', inputType); 155 if (this.confirmPasswordField) { 156 this.confirmPasswordField.setAttribute('type', inputType); 157 } 158}; 159 160PbLib.UI.formElementPasswordChecker.prototype.handleOnClick = function (event) { 161 var element = event.target; 162 163 if (this.showStrength) { 164 // Check if the element equals one of the password textfields. 165 if (element === this.passwordStrengthElem || element === this.passwordStrengthTextElem) { 166 if (!this.passwordStrengBubble) { 167 // Load the Bubble CSS file when needed. 168 PbLib.loadCss( 169 PbLib.getNewURI('ui/uibase/components/content-bubble/pbuic-content-bubble.css'), 170 this.toggleBubble.bind(this) 171 ); 172 } else if (this.passwordStrengBubble.style.display === 'none') { 173 // If Bubble already exists and is hidden, show it. 174 this.passwordStrengBubble.style.removeProperty('display'); 175 } else { 176 this.passwordStrengBubble.style.display = 'none'; 177 } 178 } else if (this.passwordStrengBubble) { 179 this.passwordStrengBubble.style.display = 'none'; 180 } 181 } 182 183 // Check if checkbox is changed to change the input type of the password field. 184 if (element === this.checkboxElem) { 185 this.toggleInputType(); 186 } 187}; 188 189PbLib.UI.formElementPasswordChecker.prototype.toggleBubble = function () { 190 if (!this.passwordStrengBubble) { 191 // Add new bubble to the DOM. 192 this.passwordStrengBubble = document.createElement('span'); 193 this.passwordStrengBubble.classList.add('pbuic-content-bubble'); 194 this.passwordStrengBubble.innerHTML = 195 '<p>' + 196 'A longer password containing different characters (@#$, a-z, A-Z, and 0-9) is stronger.' + 197 '</p><span class="pbuic-cb-arrow"></span>'; 198 this.passwordStrengBubble.style.top = this.passwordStrengthElem.offsetHeight + 10 + 'px'; 199 this.passwordStrengthElem.appendChild(this.passwordStrengBubble); 200 } else { 201 this.passwordStrengBubble.style.removeProperty('display'); 202 } 203}; 204 205PbLib.UI.formElementPasswordChecker.prototype.matchPasswords = function () { 206 var pwOrigin = this.passwordField.value; 207 var pwMatch = this.confirmPasswordField.value; 208 209 // Add an extra class to the container element when passwords do not match. 210 if ((pwOrigin.length > 0 && pwMatch.length > 0) && (pwOrigin !== pwMatch)) { 211 this.passwordField.parentElement.parentElement.classList.add('psc-invalid'); 212 213 if (!this.passwordNoMatch) { 214 // Creat an element for the password do not match text. 215 this.passwordNoMatch = document.createElement('span'); 216 this.passwordNoMatch.classList.add('pbuic-pr-no-match'); 217 this.passwordNoMatch.innerHTML = 'The passwords don\'t match.'; 218 this.confirmPasswordField.parentElement.appendChild(this.passwordNoMatch); 219 } else { 220 this.passwordNoMatch.style.removeProperty('display'); 221 } 222 223 } else if (pwOrigin === pwMatch || (pwOrigin.length === 0 || pwMatch.length === 0)) { 224 this.passwordField.parentElement.parentElement.classList.remove('psc-invalid'); 225 if (this.passwordNoMatch) { 226 this.passwordNoMatch.style.display = 'none'; 227 } 228 } 229};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.