@fr0st/ui-passwordstrength 2.0.8 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,341 +1,428 @@
1
- (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@fr0st/ui'), require('@fr0st/query')) :
3
- typeof define === 'function' && define.amd ? define(['exports', '@fr0st/ui', '@fr0st/query'], factory) :
4
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.UI = global.UI || {}, global.UI, global.fQuery));
5
- })(this, (function (exports, ui, $) { 'use strict';
6
-
7
- /**
8
- * PasswordStrength Class
9
- * @class
10
- */
11
- class PasswordStrength extends ui.BaseComponent {
12
- /**
13
- * New PasswordStrength constructor.
14
- * @param {HTMLElement} node The input node.
15
- * @param {object} [options] The options to create the PasswordStrength with.
16
- */
17
- constructor(node, options) {
18
- super(node, options);
19
-
20
- if (this._options.container) {
21
- this._container = $.findOne(this._options.container);
22
- } else {
23
- this._container = $.closest(this._node, ':not(.form-input):not(.input-group)');
24
- }
25
-
26
- this._render();
27
- this._refresh();
28
- this._events();
29
- }
30
-
31
- /**
32
- * Dispose the PasswordStrength.
33
- */
34
- dispose() {
35
- $.remove(this._progress);
36
- $.removeEvent(this._node, 'input.ui.passwordstrength');
37
- $.removeAttribute(this._node, 'aria-describedby');
38
-
39
- this._container = null;
40
- this._progress = null;
41
- this._progressBar = null;
42
-
43
- super.dispose();
44
- }
45
-
46
- /**
47
- * Get the password strength.
48
- * @return {number} The password strength. (0, 100)
49
- */
50
- getStrength() {
51
- const value = $.getValue(this._node);
52
-
53
- return this.constructor.getStrength(value);
54
- }
55
- }
56
-
57
- /**
58
- * Attach events for the PasswordStrength.
59
- */
60
- function _events() {
61
- $.addEvent(this._node, 'input.ui.passwordstrength', (_) => {
62
- this._refresh();
63
- });
64
- }
65
-
66
- /**
67
- * Refresh the password strength.
68
- */
69
- function _refresh() {
70
- const strength = this.getStrength();
71
-
72
- let nextLevel;
73
- for (const level of this._options.levels) {
74
- if (strength < level.score) {
75
- break;
76
- }
77
-
78
- nextLevel = level;
79
- }
80
-
81
- $.setStyle(this._progressBar, { width: `${strength}%` });
82
- $.setAttribute(this._progressBar, {
83
- 'class': this.constructor.classes.progressBar,
84
- 'aria-valuenow': strength,
85
- });
86
-
87
- $.addClass(this._progressBar, nextLevel.class);
88
-
89
- if (this._options.striped) {
90
- $.addClass(this._progressBar, this.constructor.classes.progressBarStriped);
91
- }
92
-
93
- if (nextLevel.text) {
94
- $.setText(this._progressBar, nextLevel.text);
95
- }
96
- }
97
-
98
- /**
99
- * Render the password strength element.
100
- */
101
- function _render() {
102
- this._progress = $.create('div', {
103
- class: this.constructor.classes.progress,
104
- });
105
-
106
- const id = ui.generateId('password-strength');
107
-
108
- this._progressBar = $.create('div', {
109
- attributes: {
110
- 'id': id,
111
- 'role': 'progressbar',
112
- 'aria-valuemin': 0,
113
- 'aria-valuemax': 100,
114
- },
115
- });
116
-
117
- $.append(this._progress, this._progressBar);
118
- $.append(this._container, this._progress);
119
-
120
- $.setAttribute(this._node, {
121
- 'aria-describedby': id,
122
- });
123
- }
124
-
125
- /**
126
- * Find character sequences in a string.
127
- * @param {string} string The input string.
128
- * @param {array} locations The character locations.
129
- * @return {array} The character sequences.
130
- */
131
- function findSequences(string, locations) {
132
- const sequences = [];
133
- let sequence = [];
134
-
135
- for (let i = 0; i < locations.length - 1; i++) {
136
- const current = locations[i];
137
- const next = locations[i + 1];
138
- const distance = next - current;
139
-
140
- const char = string[current];
141
- const nextChar = string[next];
142
- const charDistance = nextChar.charCodeAt(0) - char.charCodeAt(0);
143
-
144
- if (distance === 1 && charDistance === 1) {
145
- if (!sequence.length) {
146
- sequence.push(char);
147
- }
148
- sequence.push(nextChar);
149
- } else if (sequence.length) {
150
- sequences.push(sequence);
151
- sequence = [];
152
- }
153
- }
154
-
155
- if (sequence.length) {
156
- sequences.push(sequence);
157
- }
158
-
159
- return sequences;
160
- }
161
- /**
162
- * Get the strength of a password.
163
- * @param {string} password The password.
164
- * @return {number} The password strength.
165
- */
166
- function getStrength(password) {
167
- if (password.match(/^password/i)) {
168
- password = password.substring(8);
169
- }
170
-
171
- const length = password.length;
172
-
173
- let score = 0;
174
-
175
- if (length) {
176
- score += length * 6;
177
- }
178
-
179
- const upper = [];
180
- const lower = [];
181
- const numbers = [];
182
- const symbols = [];
183
- const dictionary = {};
184
-
185
- for (let i = 0; i < length; i++) {
186
- const char = password[i];
187
- const code = char.charCodeAt(0);
188
-
189
- if (code >= 48 && code <= 57) {
190
- numbers.push(i);
191
- } else if (code >= 65 && code <= 90) {
192
- upper.push(i);
193
- } else if (code >= 97 && code <= 122) {
194
- lower.push(i);
195
- } else {
196
- symbols.push(i);
197
- }
198
-
199
- if (!(char in dictionary)) {
200
- dictionary[char] = 0;
201
- }
202
-
203
- dictionary[char]++;
204
- }
205
-
206
- // reward upper/lower case
207
- if (upper.length !== length && lower.length !== length) {
208
- if (upper.length) {
209
- score += length - upper.length;
210
- }
211
-
212
- if (lower.length) {
213
- score += length - lower.length;
214
- }
215
- }
216
-
217
- // reward numbers
218
- if (numbers.length !== length) {
219
- score += numbers.length * 3;
220
- }
221
-
222
- // reward symbols
223
- score += symbols.length * 6;
224
-
225
- // reward inner numbers/symbols
226
- for (const list of [numbers, symbols]) {
227
- const reward = list.filter((v) => v && v !== length - 1).length;
228
- score += reward * 3;
229
- }
230
-
231
- // punish only letters
232
- if (!numbers.length && !symbols.length) {
233
- score -= length;
234
- }
235
-
236
- // punish only numbers
237
- if (!upper.length && !lower.length && !symbols.length) {
238
- score -= length;
239
- }
240
-
241
- // punish no symbols
242
- if (!symbols.length) {
243
- score -= length;
244
- }
245
-
246
- // repeating characters
247
- const repeats = Object.values(dictionary)
248
- .reduce((acc, count) => acc + count - 1, 0);
249
-
250
- if (repeats > 0) {
251
- score -= repeats;
252
- }
253
-
254
- if (length >= 2) {
255
- // consecutive characters
256
- const matches = password.matchAll(/(.)\1+/g);
257
-
258
- for (const match of matches) {
259
- score -= Math.min(match[0].length, 12) * 3;
260
- }
261
-
262
- const lowerPassword = password.toLowerCase();
263
-
264
- // sequential letters
265
- const letters = upper.concat(lower);
266
- const letterSequences = findSequences(lowerPassword, letters);
267
- for (const sequence of letterSequences) {
268
- if (sequence.length > 2) {
269
- score -= (Math.min(sequence.length - 2, 16)) * 3;
270
- }
271
- }
272
-
273
- // sequential numbers
274
- const numberSequences = findSequences(lowerPassword, numbers);
275
- for (const sequence of numberSequences) {
276
- if (sequence.length > 2) {
277
- score -= (Math.min(sequence.length - 2, 16)) * 3;
278
- }
279
- }
280
- }
281
-
282
- return $._clamp(score, 0, 100);
283
- }
284
-
285
- // PasswordStrength default options
286
- PasswordStrength.defaults = {
287
- levels: [
288
- {
289
- score: 0,
290
- class: 'text-bg-danger',
291
- text: 'Very Weak',
292
- },
293
- {
294
- score: 20,
295
- class: 'text-bg-danger',
296
- text: 'Weak',
297
- },
298
- {
299
- score: 40,
300
- class: 'text-bg-warning',
301
- text: 'Normal',
302
- },
303
- {
304
- score: 60,
305
- class: 'text-bg-success',
306
- text: 'Strong',
307
- },
308
- {
309
- score: 80,
310
- class: 'text-bg-success',
311
- text: 'Very Strong',
312
- },
313
- ],
314
- container: null,
315
- striped: false,
316
- };
317
-
318
- // PasswordStrength classes
319
- PasswordStrength.classes = {
320
- progress: 'progress mt-2',
321
- progressBar: 'progress-bar',
322
- progressBarStriped: 'progress-bar-striped',
323
- };
324
-
325
- // PasswordStrength static
326
- PasswordStrength.getStrength = getStrength;
327
-
328
- // PasswordStrength prototype
329
- const proto = PasswordStrength.prototype;
330
-
331
- proto._events = _events;
332
- proto._refresh = _refresh;
333
- proto._render = _render;
334
-
335
- // PasswordStrength init
336
- ui.initComponent('passwordstrength', PasswordStrength);
337
-
338
- exports.PasswordStrength = PasswordStrength;
339
-
340
- }));
341
- //# sourceMappingURL=frost-ui-passwordstrength.js.map
1
+ (function(global, factory) {
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@fr0st/ui'), require('@fr0st/query')) :
3
+ typeof define === 'function' && define.amd ? define(['exports', '@fr0st/ui', '@fr0st/query'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.UI = global.UI || {}), global.UI,global.fQuery));
5
+ })(this, function(exports, _fr0st_ui, _fr0st_query) {
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
7
+ //#region \0rolldown/runtime.js
8
+ var __create = Object.create;
9
+ var __defProp = Object.defineProperty;
10
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
11
+ var __getOwnPropNames = Object.getOwnPropertyNames;
12
+ var __getProtoOf = Object.getPrototypeOf;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __copyProps = (to, from, except, desc) => {
15
+ if (from && typeof from === "object" || typeof from === "function") {
16
+ for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
17
+ key = keys[i];
18
+ if (!__hasOwnProp.call(to, key) && key !== except) {
19
+ __defProp(to, key, {
20
+ get: ((k) => from[k]).bind(null, key),
21
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
22
+ });
23
+ }
24
+ }
25
+ }
26
+ return to;
27
+ };
28
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
29
+ value: mod,
30
+ enumerable: true
31
+ }) : target, mod));
32
+
33
+ //#endregion
34
+ _fr0st_query = __toESM(_fr0st_query, 1);
35
+
36
+ //#region src/strength.js
37
+ var characterTypes = [
38
+ {
39
+ pattern: /\p{Ll}/u,
40
+ type: "lower"
41
+ },
42
+ {
43
+ pattern: /\p{Lu}/u,
44
+ type: "upper"
45
+ },
46
+ {
47
+ pattern: /\p{N}/u,
48
+ type: "number"
49
+ }
50
+ ];
51
+ var keyboardSequences = [
52
+ "1234567890",
53
+ "qwertyuiop",
54
+ "asdfghjkl",
55
+ "zxcvbnm"
56
+ ].flatMap((row) => [row, Array.from(row).reverse().join("")]);
57
+ var leetCharacters = {
58
+ "$": "s",
59
+ "0": "o",
60
+ "3": "e",
61
+ "4": "a",
62
+ "5": "s",
63
+ "7": "t",
64
+ "@": "a"
65
+ };
66
+ var lengthBands = [
67
+ {
68
+ end: 5,
69
+ max: 19,
70
+ min: 1,
71
+ start: 1
72
+ },
73
+ {
74
+ end: 8,
75
+ max: 39,
76
+ min: 20,
77
+ start: 6
78
+ },
79
+ {
80
+ end: 11,
81
+ max: 59,
82
+ min: 40,
83
+ start: 9
84
+ },
85
+ {
86
+ end: 15,
87
+ max: 79,
88
+ min: 60,
89
+ start: 12
90
+ },
91
+ {
92
+ end: 24,
93
+ max: 100,
94
+ min: 80,
95
+ start: 16
96
+ }
97
+ ];
98
+ /**
99
+ * Gets the type of a password character.
100
+ * @param {string} character The character.
101
+ * @returns {string} The character type.
102
+ */
103
+ var getCharacterType = (character) => characterTypes.find(({ pattern }) => pattern.test(character))?.type || "symbol";
104
+ /**
105
+ * Gets a length-based password strength.
106
+ * @param {string[]} characters The password characters.
107
+ * @returns {number} The password strength.
108
+ */
109
+ var getLengthStrength = (characters) => {
110
+ const band = lengthBands.find(({ end }) => characters.length <= end) || lengthBands.at(-1);
111
+ const score = Math.round(_fr0st_query.default._map(Math.min(characters.length, band.end), band.start, band.end, band.min, band.max));
112
+ const diversity = new Set(characters.map(getCharacterType)).size;
113
+ return Math.min(score + (diversity - 1) * 2, band.max);
114
+ };
115
+ /**
116
+ * Gets the smallest repeated pattern.
117
+ * @param {string[]} characters The password characters.
118
+ * @returns {string[]|null} The repeated pattern.
119
+ */
120
+ var getRepeatedPattern = (characters) => {
121
+ const maxLength = Math.floor(characters.length / 2);
122
+ for (let length = 1; length <= maxLength; length++) if (characters.length % length === 0 && characters.every((character, index) => character === characters[index % length])) return characters.slice(0, length);
123
+ return null;
124
+ };
125
+ /**
126
+ * Normalizes common password matching.
127
+ * @param {string} password The password.
128
+ * @returns {string} The normalized password.
129
+ */
130
+ var normalizePassword = (password) => String(password).normalize("NFKC").toLowerCase();
131
+ /**
132
+ * Normalizes common leet substitutions.
133
+ * @param {string} password The password.
134
+ * @returns {string} The normalized password.
135
+ */
136
+ var normalizeLeet = (password) => Array.from(password, (character) => leetCharacters[character] || character).join("");
137
+ /**
138
+ * Gets normalized variants used to match common passwords.
139
+ * @param {string} password The normalized password.
140
+ * @returns {Set<string>} The password variants.
141
+ */
142
+ var getPasswordVariants = (password) => {
143
+ const variants = [password, password.replace(/[^\p{L}\p{N}]/gu, normalizeLeet)].flatMap((value) => {
144
+ const undecorated = value.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu, "");
145
+ return [undecorated, undecorated.replace(/^\d+|\d+$/g, "")];
146
+ });
147
+ return /* @__PURE__ */ new Set([
148
+ normalizeLeet(password),
149
+ ...variants,
150
+ ...variants.map(normalizeLeet)
151
+ ]);
152
+ };
153
+ /**
154
+ * Gets the score for a common password.
155
+ * @param {string} password The normalized password.
156
+ * @param {string[]} commonPasswords The common passwords.
157
+ * @returns {number|null} The common-password score.
158
+ */
159
+ var getCommonPasswordStrength = (password, commonPasswords) => {
160
+ const common = new Set(commonPasswords.map(normalizePassword));
161
+ if (common.has(password)) return 0;
162
+ return common.isDisjointFrom(getPasswordVariants(password)) ? null : 5;
163
+ };
164
+ /**
165
+ * Checks whether characters form an ascending or descending sequence.
166
+ * @param {string[]} characters The password characters.
167
+ * @returns {boolean} Whether the characters form a sequence.
168
+ */
169
+ var isSequence = (characters) => {
170
+ const [first, second, third] = characters.map((character) => character.toLowerCase());
171
+ const type = getCharacterType(first);
172
+ const direction = second.codePointAt(0) - first.codePointAt(0);
173
+ return ["lower", "number"].includes(type) && getCharacterType(second) === type && getCharacterType(third) === type && Math.abs(direction) === 1 && third.codePointAt(0) - second.codePointAt(0) === direction;
174
+ };
175
+ /**
176
+ * Checks whether characters form a keyboard sequence.
177
+ * @param {string[]} characters The password characters.
178
+ * @returns {boolean} Whether the characters form a keyboard sequence.
179
+ */
180
+ var isKeyboardSequence = (characters) => {
181
+ const sequence = characters.join("").toLowerCase();
182
+ return keyboardSequences.some((keyboardSequence) => keyboardSequence.includes(sequence));
183
+ };
184
+ /**
185
+ * Checks whether characters form a predictable pattern.
186
+ * @param {string[]} characters The password characters.
187
+ * @returns {boolean} Whether the characters form a predictable pattern.
188
+ */
189
+ var isPredictable = (characters) => new Set(characters).size === 1 || isSequence(characters) || isKeyboardSequence(characters);
190
+ /**
191
+ * Gets the number of characters in predictable patterns.
192
+ * @param {string[]} characters The password characters.
193
+ * @returns {number} The number of predictable characters.
194
+ */
195
+ var getPredictableCount = (characters) => {
196
+ const indices = /* @__PURE__ */ new Set();
197
+ for (let i = 0; i + 2 < characters.length; i++) if (isPredictable(characters.slice(i, i + 3))) {
198
+ indices.add(i);
199
+ indices.add(i + 1);
200
+ indices.add(i + 2);
201
+ }
202
+ return indices.size;
203
+ };
204
+ /**
205
+ * Calculates the strength of a password.
206
+ * @param {string} password The password.
207
+ * @param {string[]} [commonPasswords=[]] Passwords to score as very weak.
208
+ * @returns {number} The password strength, from 0 to 100.
209
+ */
210
+ var getStrength = (password, commonPasswords = []) => {
211
+ const normalizedPassword = password.normalize("NFKC");
212
+ const characters = Array.from(normalizedPassword);
213
+ if (!characters.length) return 0;
214
+ const commonStrength = getCommonPasswordStrength(normalizedPassword.toLowerCase(), commonPasswords);
215
+ if (commonStrength !== null) return commonStrength;
216
+ const score = getLengthStrength(characters);
217
+ const repeatedPattern = getRepeatedPattern(characters);
218
+ if (repeatedPattern?.length === 1) return 0;
219
+ if (repeatedPattern) return Math.min(score, getLengthStrength(repeatedPattern), 15);
220
+ const predictableCount = getPredictableCount(characters);
221
+ return _fr0st_query.default._clamp(score - predictableCount, 0, predictableCount === characters.length ? 15 : 100);
222
+ };
223
+
224
+ //#endregion
225
+ //#region src/password-strength.js
226
+ /**
227
+ * @typedef {object} PasswordStrengthLevel
228
+ * @property {string} class The CSS class applied to the progress bar.
229
+ * @property {number} score The minimum score for the level.
230
+ * @property {string} [text] The label displayed in the progress bar.
231
+ */
232
+ /**
233
+ * @callback PasswordStrengthScorer
234
+ * @param {string} password The password.
235
+ * @param {string[]} commonPasswords The common passwords.
236
+ * @returns {number} The password strength, from 0 to 100.
237
+ */
238
+ /**
239
+ * @typedef {object} PasswordStrengthOptions
240
+ * @property {string[]} [commonPasswords] Passwords to score as very weak.
241
+ * @property {string|null} [container=null] The selector for the progress container.
242
+ * @property {PasswordStrengthLevel[]} [levels] The ordered password-strength levels.
243
+ * @property {PasswordStrengthScorer} [scorer] Calculates the password strength.
244
+ * @property {boolean} [striped=false] Whether to display a striped progress bar.
245
+ */
246
+ /**
247
+ * Displays the strength of a password input as a progress bar.
248
+ * @augments {BaseComponent<PasswordStrengthOptions>}
249
+ */
250
+ var PasswordStrength = class extends _fr0st_ui.BaseComponent {
251
+ static classes = {
252
+ progress: "progress mt-2",
253
+ progressBar: "progress-bar",
254
+ progressBarStriped: "progress-bar-striped"
255
+ };
256
+ /** @type {PasswordStrengthOptions} */
257
+ static defaults = {
258
+ commonPasswords: [
259
+ "123456",
260
+ "12345678",
261
+ "123456789",
262
+ "1234567890",
263
+ "000000",
264
+ "111111",
265
+ "abc123",
266
+ "admin",
267
+ "dragon",
268
+ "iloveyou",
269
+ "letmein",
270
+ "monkey",
271
+ "password",
272
+ "password1",
273
+ "password123",
274
+ "qwerty",
275
+ "qwerty123",
276
+ "welcome"
277
+ ],
278
+ levels: [
279
+ {
280
+ score: 0,
281
+ class: "text-bg-danger",
282
+ text: "Very Weak"
283
+ },
284
+ {
285
+ score: 20,
286
+ class: "text-bg-danger",
287
+ text: "Weak"
288
+ },
289
+ {
290
+ score: 40,
291
+ class: "text-bg-warning",
292
+ text: "Normal"
293
+ },
294
+ {
295
+ score: 60,
296
+ class: "text-bg-success",
297
+ text: "Strong"
298
+ },
299
+ {
300
+ score: 80,
301
+ class: "text-bg-success",
302
+ text: "Very Strong"
303
+ }
304
+ ],
305
+ container: null,
306
+ scorer: getStrength,
307
+ striped: false
308
+ };
309
+ #container;
310
+ #describedBy;
311
+ #form;
312
+ #progress;
313
+ #progressBar;
314
+ #resetHandler;
315
+ /**
316
+ * Calculates the strength of a password.
317
+ * @param {string} password The password.
318
+ * @param {string[]} [commonPasswords=this.defaults.commonPasswords] Passwords to score as very weak.
319
+ * @returns {number} The password strength, from 0 to 100.
320
+ */
321
+ static getStrength(password, commonPasswords = this.defaults.commonPasswords) {
322
+ return getStrength(password, commonPasswords);
323
+ }
324
+ /**
325
+ * Creates a PasswordStrength.
326
+ * @param {HTMLElement} node The input node.
327
+ * @param {PasswordStrengthOptions} [options] The PasswordStrength options.
328
+ */
329
+ constructor(node, options) {
330
+ super(node, options);
331
+ const overrides = {
332
+ ...(0, _fr0st_ui.getDataset)(node),
333
+ ...options
334
+ };
335
+ for (const key of ["levels", "commonPasswords"]) if (Array.isArray(overrides[key])) {
336
+ this.options[key].length = 0;
337
+ _fr0st_query.default._extend(this.options[key], overrides[key]);
338
+ }
339
+ this.#form = this.node.form;
340
+ if (this.options.container) this.#container = _fr0st_query.default.findOne(this.options.container);
341
+ else this.#container = _fr0st_query.default.closest(this.node, ":not(.form-input):not(.input-group)").shift();
342
+ this.#render();
343
+ this.#refresh();
344
+ this.#events();
345
+ }
346
+ /** @inheritdoc */
347
+ dispose() {
348
+ _fr0st_query.default.remove(this.#progress);
349
+ _fr0st_query.default.removeEvent(this.node, "input.ui.passwordstrength");
350
+ if (this.#form) _fr0st_query.default.removeEvent(this.#form, "reset.ui.passwordstrength", this.#resetHandler);
351
+ if (this.#describedBy === null) _fr0st_query.default.removeAttribute(this.node, "aria-describedby");
352
+ else _fr0st_query.default.setAttribute(this.node, { "aria-describedby": this.#describedBy });
353
+ this.#container = null;
354
+ this.#form = null;
355
+ this.#progress = null;
356
+ this.#progressBar = null;
357
+ this.#resetHandler = null;
358
+ super.dispose();
359
+ }
360
+ /**
361
+ * Gets the password strength.
362
+ * @returns {number} The password strength, from 0 to 100.
363
+ */
364
+ getStrength() {
365
+ const value = _fr0st_query.default.getValue(this.node);
366
+ const strength = Number(this.options.scorer(value, this.options.commonPasswords));
367
+ return Number.isFinite(strength) ? _fr0st_query.default._clamp(strength, 0, 100) : 0;
368
+ }
369
+ /**
370
+ * Attaches events for the PasswordStrength.
371
+ */
372
+ #events() {
373
+ if (this.#form) {
374
+ this.#resetHandler = (event) => {
375
+ setTimeout(() => {
376
+ if (this.node && !event.defaultPrevented) this.#refresh();
377
+ }, 0);
378
+ };
379
+ _fr0st_query.default.addEvent(this.#form, "reset.ui.passwordstrength", this.#resetHandler);
380
+ }
381
+ _fr0st_query.default.addEvent(this.node, "input.ui.passwordstrength", (_) => {
382
+ this.#refresh();
383
+ });
384
+ }
385
+ /**
386
+ * Refreshes the password strength.
387
+ */
388
+ #refresh() {
389
+ const strength = this.getStrength();
390
+ const nextLevel = this.options.levels.findLast((level) => strength >= level.score);
391
+ _fr0st_query.default.setStyle(this.#progressBar, { width: `${strength}%` });
392
+ _fr0st_query.default.setAttribute(this.#progressBar, {
393
+ "class": this.constructor.classes.progressBar,
394
+ "aria-valuenow": strength
395
+ });
396
+ _fr0st_query.default.addClass(this.#progressBar, nextLevel.class);
397
+ if (this.options.striped) _fr0st_query.default.addClass(this.#progressBar, this.constructor.classes.progressBarStriped);
398
+ _fr0st_query.default.setText(this.#progressBar, nextLevel.text ?? "");
399
+ }
400
+ /**
401
+ * Renders the password strength element.
402
+ */
403
+ #render() {
404
+ this.#describedBy = _fr0st_query.default.getAttribute(this.node, "aria-describedby");
405
+ this.#progress = _fr0st_query.default.create("div", { class: this.constructor.classes.progress });
406
+ const id = (0, _fr0st_ui.generateId)("password-strength");
407
+ this.#progressBar = _fr0st_query.default.create("div", { attributes: {
408
+ "id": id,
409
+ "role": "progressbar",
410
+ "aria-valuemin": 0,
411
+ "aria-valuemax": 100
412
+ } });
413
+ _fr0st_query.default.append(this.#progress, this.#progressBar);
414
+ _fr0st_query.default.append(this.#container, this.#progress);
415
+ const describedBy = [this.#describedBy, id].filter(Boolean).join(" ");
416
+ _fr0st_query.default.setAttribute(this.node, { "aria-describedby": describedBy });
417
+ }
418
+ };
419
+
420
+ //#endregion
421
+ //#region src/index.js
422
+ (0, _fr0st_ui.initComponent)("passwordstrength", PasswordStrength);
423
+ var src_default = PasswordStrength;
424
+
425
+ //#endregion
426
+ exports.PasswordStrength = src_default;
427
+ });
428
+ //# sourceMappingURL=frost-ui-passwordstrength.js.map