1class PasswordStrengthCalculator { 2 /** 3 * password length: 4 * level 0 (0 point): less than 4 characters 5 * level 1 (6 points): between 5 and 7 characters 6 * level 2 (12 points): between 8 and 15 characters 7 * level 3 (18 points): 16 or more characters 8 */ 9 verdictLength(password) { 10 const length = password.length; 11 let score, log; 12 switch (true) { 13 case length > 0 && length < 5: 14 log = `3 points for length (${length})`; 15 score = 3; 16 break; 17 case length > 4 && length < 8: 18 log = `6 points for length (${length})`; 19 score = 6; 20 break; 21 case length > 7 && length < 16: 22 log = `12 points for length (${length})`; 23 score = 12; 24 break; 25 default: 26 log = `18 points for length (${length})`; 27 score = 18; 28 break; 29 } 30 return { score: score, log: log }; 31 } 32 /** 33 * letters: 34 * level 0 (0 points): no letters 35 * level 1 (5 points): all letters are lower case 36 * level 1 (5 points): all letters are upper case
37 * level 2 (7 points): letters are mixed case 38 */ 39 verdictLetter(password) { 40 const matchLower = password.match(/[a-z]/), matchUpper = password.match(/[A-Z]/); 41 let score = 0, log = ''; 42 if (matchLower) { 43 if (matchUpper) { 44 score = 7; 45 log = '7 points for letters are mixed'; 46 } 47 else { 48 score = 5; 49 log = '5 point for at least one lower case char'; 50 } 51 } 52 else if (matchUpper) { 53 score = 5; 54 log = '5 points for at least one upper case char'; 55 } 56 return { score: score, log: log }; 57 } 58 /** 59 * numbers: 60 * level 0 (0 points): no numbers exist 61 * level 1 (5 points): one number exists 62 * level 1 (7 points): 3 or more numbers exists 63 */ 64 verdictNumbers(password) { 65 const numbers = password.replace(/\D/gi, ''); 66 let score = 0, log = ''; 67 if (numbers.length > 1) { 68 score = 7; 69 log = '7 points for at least three numbers'; 70 } 71 else if (numbers.length > 0) { 72 score = 5; 73 log = '5 points for at least one number'; 74 } 75 return { score: score, log: log }; 76 } 77 /** 78 * special characters: 79 * level 0 (0 points): no special characters 80 * level 1 (5 points): one special character exists 81 * level 2 (10 points): more than one special character exists 82 */ 83 verdictSpecialChars(password) { 84 const specialCharacters = password.replace(/[\w\s]/gi, ''); 85 let score = 0, log = ''; 86 if (specialCharacters.length > 1) { 87 score = 10; 88 log = '10 points for at least two special chars'; 89 } 90 else if (specialCharacters.length > 0) { 91 score = 5; 92 log = '5 points for at least one special char'; 93 } 94 return { score: score, log: log }; 95 } 96 /** 97 * combinations: 98 * level 0 (1 points): mixed case letters 99 * level 0 (1 points): letters and numbers 100 * level 1 (2 points): mixed case letters and numbers 101 * level 3 (4 points): letters, numbers and special characters 102 * level 4 (6 points): mixed case letters, numbers and special characters 103 */ 104 verdictCombos(letter, number, special) { 105 let score = 0, log = ''; 106 if (letter === 7 && number > 0 && special > 0) { 107 score = 6; 108 log = '6 combo points for letters, numbers and special characters'; 109 } 110 else if (letter > 0 && number > 0 && special > 0) { 111 score = 4; 112 log = '4 combo points for letters, numbers and special characters'; 113 } 114 else if (letter === 7 && number > 0) { 115 score = 2; 116 log = '2 combo points for mixed case letters and numbers'; 117 } 118 else if (letter > 0 && number > 0) { 119 score = 1; 120 log = '1 combo points for letters and numbers'; 121 } 122 else if (letter === 7) { 123 score = 1; 124 log = '1 combo points for mixed case letters'; 125 } 126 return { score: score, log: log }; 127 } 128 /** 129 * final verdict base on final score 130 */ 131 finalVerdict(finalScore) { 132 let strVerdict; 133 if (finalScore < 16) { 134 strVerdict = 'very weak'; 135 } 136 else if (finalScore > 15 && finalScore < 25) { 137 strVerdict = 'weak'; 138 } 139 else if (finalScore > 24 && finalScore < 35) { 140 strVerdict = 'mediocre'; 141 } 142 else if (finalScore > 34 && finalScore < 45) { 143 strVerdict = 'strong'; 144 } 145 else { 146 strVerdict = 'stronger'; 147 } 148 return strVerdict; 149 } 150 calculate(password) { 151 const lengthVerdict = this.verdictLength(password), letterVerdict = this.verdictLetter(password), numberVerdict = this.verdictNumbers(password), specialVerdict = this.verdictSpecialChars(password), combosVerdict = this.verdictCombos(letterVerdict.score, numberVerdict.score, specialVerdict.score); 152 const score = lengthVerdict.score 153 + letterVerdict.score 154 + numberVerdict.score 155 + specialVerdict.score 156 + combosVerdict.score, log = [ 157 lengthVerdict.log, 158 letterVerdict.log, 159 numberVerdict.log, 160 specialVerdict.log, 161 combosVerdict.log, 162 score + ' points final score' 163 ].join('\n'); 164 return { score: score, log: log, verdict: this.finalVerdict(score) }; 165 } 166} 167 168const document = window.document; 169class SfRegister { 170 constructor() { 171 this.loading = false;
172 this.ajaxEndpoint = '/index.php?ajax=sf_register'; 173 this.ajaxRequest = null; 174 this.passwordStrengthCalculator = null; 175 this.form = null; 176 this.barGraph = null; 177 this.zone = null; 178 this.zoneEmpty = null; 179 this.zoneLoading = null; 180 this.fileInformation = null; 181 this.removeImage = null; 182 if (document.readyState === 'loading') { 183 // Attach content loaded element with callback to document 184 document.addEventListener('DOMContentLoaded', () => this.initialize()); 185 } 186 else { 187 this.initialize(); 188 } 189 } 190 /** 191 * Callback after content was loaded 192 */ 193 initialize() { 194 this.initializeElements(); 195 this.initializePasswordStrengthCalculator(); 196 this.initializeEvents(); 197 } 198 initializeElements() { 199 this.form = document.getElementById('sfrForm'); 200 this.zone = document.getElementById('sfrZone'); 201 this.zoneEmpty = document.getElementById('sfrZone_empty'); 202 this.zoneLoading = document.getElementById('sfrZone_loading'); 203 this.barGraph = document.getElementById('bargraph'); 204 this.fileInformation = document.getElementById('uploadFile'); 205 this.removeImage = document.getElementById('removeImage'); 206 } 207 initializePasswordStrengthCalculator() { 208 if (this.barGraph !== null) { 209 this.barGraph.classList.add('show'); 210 this.passwordStrengthCalculator = new PasswordStrengthCalculator(); 211 } 212 } 213 initializeEvents() { 214 this.attachToElementById('sfrCountry', 'change', (event) => this.countryChanged(event)); 215 this.attachToElementById('sfrCountry', 'keyup', (event) => this.countryChanged(event)); 216 this.attachToElementById('uploadButton', 'change', (event) => this.uploadFile(event)); 217 this.attachToElementById('removeImageButton', 'click', () => this.removeFile()); 218 this.attachToElementById('sfrPassword', 'keyup', (event) => this.checkPasswordOnChange(event)); 219 } 220 /** 221 * Add class d-block remove class d-none 222 */ 223 showElement(element) { 224 element.classList.remove('d-none'); 225 element.classList.add('d-block'); 226 } 227 /** 228 * Add class d-none remove class d-block 229 */ 230 hideElement(element) { 231 element.classList.remove('d-block'); 232 element.classList.add('d-none'); 233 } 234 attachToElementById(id, eventName, callback) { 235 const element = document.getElementById(id); 236 if (element !== null) { 237 this.attachToElement(element, eventName, callback); 238 } 239 } 240 attachToElement(element, eventName, callback) { 241 if (element) { 242 element.addEventListener(eventName, callback); 243 } 244 } 245 /** 246 * Gets password meter element and sets the value with 247 * the result of the calculate password strength function 248 */ 249 checkPasswordOnChange(event) { 250 const element = event.target, meterResult = this.passwordStrengthCalculator.calculate(element.value); 251 this.barGraph.value = meterResult.score; 252 } 253 loadCountryZonesByCountry(countrySelectedValue) { 254 if (this.zone !== null) { 255 this.loading = true; 256 this.zone.disabled = true; 257 this.hideElement(this.zoneEmpty); 258 this.showElement(this.zoneLoading); 259 this.ajaxRequest = new XMLHttpRequest(); 260 this.ajaxRequest.onload = (event) => this.xhrReadyOnLoad(event); 261 this.ajaxRequest.open('POST', this.ajaxEndpoint); 262 this.ajaxRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); 263 this.ajaxRequest.send('tx_sfregister[action]=zones&tx_sfregister[parent]=' + countrySelectedValue); 264 } 265 } 266 /** 267 * Change value of zone select box 268 */ 269 countryChanged(event) { 270 if (this.loading !== true 271 && ((event instanceof KeyboardEvent && (event.key === 'ArrowDown' || event.key === 'ArrowUp')) 272 || event.type === 'change')) { 273 if (this.zone) { 274 const target = event.target, countrySelectedValue = target.options[target.selectedIndex].value; 275 this.loadCountryZonesByCountry(countrySelectedValue); 276 } 277 } 278 } 279 /** 280 * Process ajax response and display error message or 281 * hand data received to add zone option function 282 */ 283 xhrReadyOnLoad(stateChanged) { 284 const xhrResponse = stateChanged.target; 285 if (xhrResponse.readyState === 4 && xhrResponse.status === 200) { 286 const xhrResponseData = JSON.parse(xhrResponse.responseText); 287 this.hideElement(this.zoneLoading); 288 if (xhrResponseData.status === 'error' || xhrResponseData.data.length === 0) { 289 this.showElement(this.zoneEmpty); 290 } 291 else { 292 this.addZoneOptions(xhrResponseData.data); 293 } 294 } 295 this.loading = false;
296 } 297 /** 298 * Process data received with xhr response 299 */ 300 addZoneOptions(options) { 301 while (this.zone.length) { 302 this.zone.removeChild(this.zone[0]); 303 } 304 options.forEach((option, index) => { 305 this.zone.options[index] = new Option(option.label, option.value); 306 }); 307 this.zone.disabled = false; 308 } 309 /** 310 * Adds a preview information about file to upload in a label 311 */ 312 uploadFile(event) { 313 const upload = event.target; 314 if (this.fileInformation !== null) { 315 this.fileInformation.value = upload.value; 316 } 317 } 318 /** 319 * Handle remove image button clicked 320 */ 321 removeFile() { 322 if (this.removeImage !== null) { 323 this.removeImage.value = '1'; 324 this.form.submit(); 325 } 326 } 327} 328 329new SfRegister(); 330//# sourceMappingURL=sf_register.js.map
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.