├── .editorconfig ├── .eslintrc.json ├── .gitignore ├── .vscode └── settings.json ├── AUTHORS.txt ├── CHANGES.md ├── GPL-LICENSE.txt ├── Gruntfile.js ├── MIT-LICENSE.txt ├── OPTIONS.md ├── README.md ├── bower.json ├── dist ├── pwstrength-bootstrap.js ├── pwstrength-bootstrap.min.js └── pwstrength-bootstrap.min.map ├── examples ├── bootstrap2 │ ├── bootstrap2.css │ ├── bootstrap2.js │ ├── index.html │ ├── multi.html │ ├── popover.html │ └── rules.html ├── bootstrap3 │ ├── barCssClasses.html │ ├── bootstrap3.css │ ├── bootstrap3.js │ ├── i18n.html │ ├── index.html │ ├── popover.html │ ├── status.html │ ├── username.html │ └── zxcvbn.html ├── bootstrap4 │ ├── barCssClasses.html │ ├── i18n.html │ ├── index.html │ ├── popover.html │ ├── status.html │ ├── username.html │ └── zxcvbn.html ├── bootstrap5 │ ├── index.html │ ├── popover.html │ └── rule_options.html ├── i18next.js ├── index.html ├── pwstrength.js └── zxcvbn.js ├── locales ├── ar.json ├── cs.json ├── de.json ├── el.json ├── en.json ├── eo.json ├── es.json ├── fr.json ├── it.json ├── nl.json ├── no.json ├── pl.json ├── pt.json ├── ru.json ├── sk.json ├── th.json ├── tr.json └── zh-TW.json ├── package-lock.json ├── package.json ├── spec └── rules.spec.js └── src ├── i18n.js ├── methods.js ├── options.js ├── rules.js ├── ui.js ├── ui.popover.js └── ui.progressbar.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | indent_style = space 13 | indent_size = 4 14 | 15 | [*.json] 16 | insert_final_newline = false 17 | 18 | [*.{diff,md}] 19 | trim_trailing_whitespace = false 20 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "ecmaVersion": 5, 4 | "sourceType": "script" 5 | }, 6 | "env": { 7 | "browser": true 8 | }, 9 | "globals": { 10 | "jQuery": false 11 | }, 12 | "extends": "eslint:recommended", 13 | "rules": { 14 | "guard-for-in": 2, 15 | "no-alert": 1, 16 | "no-caller": 2, 17 | "no-else-return": 2, 18 | "no-empty-function": 2, 19 | "no-eval": 2, 20 | "no-implicit-globals": 2, 21 | "no-implied-eval": 2, 22 | "no-new": 1, 23 | "no-return-assign": 2, 24 | "no-return-await": 2, 25 | "no-self-compare": 2, 26 | "no-sequences": 2, 27 | "vars-on-top": 2, 28 | "no-shadow": 1, 29 | "no-use-before-define": 2, 30 | "camelcase": ["error", {"properties": "always"}], 31 | "no-plusplus": 2, 32 | "no-tabs": 2, 33 | "no-nested-ternary": 2, 34 | "one-var": ["error", "always"], 35 | "quotes": ["error", "single"], 36 | "semi": ["error", "always"], 37 | "comma-dangle": ["error", "never"], 38 | "radix": ["error", "always"], 39 | "eqeqeq": ["error", "always"] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | node_modules 3 | bower_components 4 | /nbproject/private/ 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.trimTrailingWhitespace": true, 3 | "typescript.validate.enable": false, 4 | "[javascript]": { 5 | "editor.defaultFormatter": "esbenp.prettier-vscode" 6 | }, 7 | "[json]": { 8 | "editor.defaultFormatter": "esbenp.prettier-vscode" 9 | }, 10 | "[jsonc]": { 11 | "editor.defaultFormatter": "esbenp.prettier-vscode" 12 | }, 13 | "prettier.eslintIntegration": true, 14 | } -------------------------------------------------------------------------------- /AUTHORS.txt: -------------------------------------------------------------------------------- 1 | Authors ordered by first contribution. 2 | 3 | Tane Piper 4 | Alejandro Blanco 5 | Victor "Skakruk" 6 | Dameon87 7 | Manuel Saelices 8 | Samuel Levy 9 | Petros Kal. 10 | Maximilian Meister 11 | André Durão 12 | Peter Goes 13 | Brent 14 | mikemey 15 | saparicio 16 | Javier Holguera 17 | Rômulo Alves 18 | Hannah "hpinkos" 19 | Mike Stecker 20 | Aysel Afsar 21 | Pablo Alexander 22 | Meiyer 23 | toxuh 24 | Anton Zakharov 25 | Guilherme Pejon 26 | bakcay 27 | Jason DiBianco 28 | Andreas B 29 | piernik 30 | Julio Montoya 31 | mindcreations 32 | Cal West 33 | Grégory Pelletey 34 | pages-solutions 35 | fuzzfree 36 | Roland Häder 37 | Tzipora Ziegler 38 | Ishmael Doss๛ 39 | Pascal Gentemann 40 | Lukáš Kovařík 41 | Khalid Moharrum 42 | Codecamv 43 | Mauro José da Silva 44 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## 3.1.3 4 | 5 | - Add Dutch localization. 6 | 7 | ## 3.1.2 8 | 9 | - Update dependencies for security patches. 10 | 11 | ## 3.1.1 12 | 13 | - Fix wordTwoCharacterClasses rule error message not showing up. 14 | 15 | ## 3.1.0 16 | 17 | - Allow chaining after setter methods. 18 | - Bootstrap 5 support. 19 | 20 | ## 3.0.10 21 | 22 | - Update dev dependencies. 23 | - Stop using deprecated $.isFunction. 24 | 25 | ## 3.0.9 26 | 27 | - Add jsdelivr entry in package.json. 28 | - Update dev dependencies. 29 | 30 | ## 3.0.8 31 | 32 | - Start using peerDependencies instead of regular dependencies. 33 | 34 | ## 3.0.7 35 | 36 | - Fix dependencies version mess. 37 | 38 | ## 3.0.6 39 | 40 | - Update dependencies. 41 | 42 | ## 3.0.5 43 | 44 | - Apply Prettier code formatter. 45 | - Replace JSLint with ESLint. 46 | - Add new rules.specialCharClass option to personalize special chars 47 | detection. 48 | - Separate progress bar and popover widgets to their own files. 49 | 50 | ## 3.0.4 51 | 52 | - Fix ruleIsMet method, include added custom validation rules. 53 | - Fix conflict between progressBarMinWidth and progressBarEmptyPercentage 54 | options. 55 | 56 | ## 3.0.3 57 | 58 | - Add Arabic localization. 59 | - Add Norwegian localization. 60 | - Add new ui.progressBarMinWidth option to control the minimum width of the 61 | progress bar. 62 | 63 | ## 3.0.2 64 | 65 | - Add Czech localization. 66 | - Bugfix with popovers in Bootstrap 4.2.X versions. 67 | 68 | ## 3.0.1 69 | 70 | - Bugfix in progress bar colors in Bootstrap 3. 71 | 72 | ## 3.0.0 73 | 74 | - Bootstrap 4 by default. 75 | - Improved support for Bootstrap 4. 76 | 77 | ## 2.2.1 78 | 79 | - Bugfix in the common passwords rule. 80 | 81 | ## 2.2.0 82 | 83 | - Add new rule to penalize common passwords. 84 | 85 | ## 2.1.4 86 | 87 | - Thai localization. 88 | - Fix typo in German localization. 89 | - Activate by default the extra security rules. 90 | - Make the invalid chars optional rule configurable. 91 | 92 | ## 2.1.3 93 | 94 | - Bugfix, call `onScore` when zxcvbn is in use too. 95 | 96 | ## 2.1.2 97 | 98 | - Fix errors in Portuguese localization. 99 | - Fix French localization capitalization. 100 | - Fix ruleIsMet issues with wordMin and wordMax rules. 101 | - Don't allow verdict to break line when inside progress bar. 102 | 103 | ## 2.1.1 104 | 105 | - Add missing rule, needed by the `ruleIsMet` method. 106 | - Add `wordMaxLength` and `wordInvalidChar` optional rules to the engine. 107 | 108 | ## 2.1.0 109 | 110 | - Slovak translation. 111 | - Add a new `ruleIsMet` method that returns a boolean value indicating if all 112 | password inputs in the page pass a specific rule. 113 | 114 | ## 2.0.8 115 | 116 | - Fix showing the strength of the password through the status of the field. 117 | 118 | ## 2.0.7 119 | 120 | - Add new option `progressExtraCssClasses` to be able to customize the 121 | container of the progress bar. 122 | - Updated development dependencies. 123 | 124 | ## 2.0.6 125 | 126 | - Updated development dependencies. 127 | - Bootstrap 4 alpha 6 support. 128 | 129 | ## 2.0.5 130 | 131 | - Italian localization. 132 | 133 | ## 2.0.4 134 | 135 | - French localization. 136 | - Don't use Math.log2 since IE doesn't support it. 137 | 138 | ## 2.0.3 139 | 140 | - German localization. 141 | - Polish localization. 142 | 143 | ## 2.0.2 144 | 145 | - Add a `onScore` callback to allow for a final score modification. 146 | - Turkish localization. 147 | 148 | ## 2.0.1 149 | 150 | - Fix bad assignment in the plugin initialization. 151 | - Russian localization. 152 | - New option to control the events the plugin listen to. 153 | 154 | ## 2.0.0 155 | 156 | - Use six possible verdicts and six possible css classes, so they match one 157 | to one making it possible to configure each class for each verdict level. 158 | - Properly manage the paste event so the meter updates when the user pastes the 159 | password. 160 | - Add a new option to display the password score. 161 | - Translations support, ahora hablamos idiomas. 162 | - New option to set the minimum possible percentage filled in the progress bar 163 | when the password field is not empty. 164 | - New option to set the minimum possible percentage filled in the progress bar 165 | when the password field is empty. 166 | - New option for extra CSS classes to be added to the generated progress bar. 167 | 168 | ### Breaking changes 169 | 170 | - There are 6 verdicts and css classes now, instead of 5. 171 | - `verdicts` and `errorMessages` options have been removed. Now they rely on 172 | the translations system. 173 | 174 | ## 1.2.10 175 | 176 | - Replace entropy call with log2 of guesses for zxcvbn because entropy property 177 | is removed in zxcvbn v4.0.1, and it was just log2 of guesses. 178 | 179 | ## 1.2.9 180 | 181 | - No changes, I forgot to add the built files into the 1.2.8, so I'm releasing 182 | the same again. 183 | 184 | ## 1.2.8 185 | 186 | - Updated to work with Bootstrap 4. Bootstrap 3 is still the default mode. 187 | - Allow to establish the placement of the popover through an option. 188 | - Make the css classes added to the bar and verdicts customizable. 189 | - Bugfix in the progress bar percentage calculation for a score of zero. 190 | 191 | ## 1.2.7 192 | 193 | - Bugfix: escape special characters in username for regex. 194 | 195 | ## 1.2.6 196 | 197 | - More sensible default score for sequences rule. 198 | - Publish plugin in npm. 199 | 200 | ## 1.2.5 201 | 202 | - Bugfix when using zxcvbn and form inputs with empty values. 203 | - New option to specify a list of banned words for zxcvbn. 204 | 205 | ## 1.2.4 206 | 207 | - New option to add a class in verdict element. 208 | - If there is text in the password field, don't show the progress bar empty. 209 | - Empty verdict for an empty password field. 210 | - Support html in the verdicts content. 211 | 212 | ## 1.2.3 213 | 214 | - New option to customize the html of the popover with the errors. 215 | - Bugfix in special char regex. 216 | 217 | ## 1.2.2 218 | 219 | - Every rule can have associated error messages. 220 | 221 | ## 1.2.1 222 | 223 | - Improve documentation. 224 | - Fix typo in alphabetical sequence. 225 | - Use the not minified version of the library in bower as main file. 226 | 227 | ## 1.2.0 228 | 229 | - Listen also to the `change` and `onpaste` events, not only to the `onkeyup`. 230 | - Show the lowest verdict when the score is below zero. 231 | - New option to pass more input fields content to the zxcvbn library. 232 | - Don't show the verdicts inside the popover if they are being showed inside 233 | the progressbar. 234 | 235 | ## 1.1.5 236 | 237 | - Better Bower configuration. 238 | - Pass also the verdict level to the "on key up" event handler. 239 | - Add a basic usage section to the readme. 240 | 241 | ## 1.1.4 242 | 243 | - Bower support. 244 | 245 | ## 1.1.3 246 | 247 | - Pass the score and the verdict to the "on key up" event handler. 248 | 249 | ## 1.1.2 250 | 251 | - Upgrade dev dependencies: grunt plugins and jquery 252 | - Bugfix in sequences lookup 253 | - New tests for sequences lookup 254 | 255 | ## 1.1.1 256 | 257 | - Pass the username field content to the zxcvbn function, so zxcvbn takes it 258 | into consideration when scoring the password. 259 | - Add a debug option, so the score gets printed in the JS console. 260 | - Check reversed sequences too in the sequences rule. 261 | - Fix the popover flickering. 262 | 263 | ## 1.1.0 264 | 265 | - Support zxcvbn for password scoring. 266 | - Support showing the password strength as a validation status in the password 267 | field. 268 | - Support hiding the progress bar, making it optional. 269 | - Support showing the verdicts inside the progress bar. 270 | 271 | ## 1.0.2 272 | 273 | - Bugfix in UI initialization. 274 | - Fix typo in readme. 275 | 276 | ## 1.0.1 277 | 278 | - Separate source file in several smaller files. 279 | - Add Grunt support for creating a bundle and a minified version. 280 | - Add tests for the rules engine, and continuos integration with Travis. 281 | 282 | ## 1.0.0 283 | 284 | - Complete refactor of the code. This is a cleaner version, easier to extend 285 | and mantain. 286 | - Broke backwards compatibility. Bootstrap 3 is the default option now, other 287 | options default values have changed. Options structure has changed too. 288 | - Old tests have been renamed to examples, which is what they really are. Leave 289 | room for real tests. 290 | 291 | ## 0.7.0 292 | 293 | - New rule to check for sequences in the password. It penalizes finding 294 | sequences of consecutive numbers, consecutive characters in the alphabet or 295 | in the qwerty layout. Active by default. 296 | 297 | ## 0.6.0 298 | 299 | - New feature: support showing the verdicts and errors in a Bootstrap popover. 300 | - Hide the verdicts and errors when the input is empty. 301 | - Remove _showVerdictsInitially_ option, is not needed anymore. 302 | 303 | ## 0.5.0 304 | 305 | - Support to activate/deactivate rules using the _rules_ object inside the 306 | _options_ object. 307 | - Two new rules added, deactivated by default. Check for too many character 308 | repetitions, and check for number of character classes used. 309 | 310 | ## 0.4.5 311 | 312 | - Fix error message when the password contains the username. 313 | - Check if the password is an email, and mark as weak. 314 | - Add a _container_ option, it will be used to look for the viewports. 315 | 316 | ## 0.4.4 317 | 318 | - Bad version in plugin manifest. 319 | 320 | ## 0.4.3 321 | 322 | - Change jQuery plugin name to avoid conflict with an existing one. 323 | 324 | ## 0.4.2 325 | 326 | - New option to choose if the verdicts should be displayed before the user 327 | introduces a letter. New default behaviour: don't show them. 328 | - Bugfix with progress bar color and Bootstrap 2. 329 | - Improve code quality. 330 | 331 | ## 0.4.1 332 | 333 | - jQuery plugins registry support. 334 | 335 | ## 0.4.0 336 | 337 | - Bootstrap 3.0.0 support. 338 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | var license = 4 | '/*!\n' + 5 | '* jQuery Password Strength plugin for Twitter Bootstrap\n' + 6 | '* Version: <%= pkg.version %>\n' + 7 | '*\n' + 8 | '* Copyright (c) 2008-2013 Tane Piper\n' + 9 | '* Copyright (c) 2013 Alejandro Blanco\n' + 10 | '* Dual licensed under the MIT and GPL licenses.\n' + 11 | '*/\n\n' + 12 | '(function (jQuery) {\n', 13 | eslintConfig = { 14 | target: ['src/*js', 'spec/*js', 'Gruntfile.js'] 15 | }, 16 | jasmineConfig = { 17 | options: { 18 | forceExit: true, 19 | jUnit: { 20 | report: false 21 | } 22 | }, 23 | all: ['spec/'] 24 | }, 25 | concatConfig = { 26 | options: { 27 | banner: license, 28 | footer: '}(jQuery));', 29 | process: function(src, filepath) { 30 | // Remove ALL block comments, the stripBanners only removes 31 | // the first one 32 | src = src.replace(/\/\*[\s\S]*?\*\//g, ''); 33 | return '// Source: ' + filepath + src; 34 | } 35 | }, 36 | dist: { 37 | src: [ 38 | 'src/i18n.js', 39 | 'src/rules.js', 40 | 'src/options.js', 41 | 'src/ui.js', 42 | 'src/ui.progressbar.js', 43 | 'src/ui.popover.js', 44 | 'src/methods.js' 45 | ], 46 | dest: '<%= pkg.name %>.js' 47 | } 48 | }, 49 | uglifyConfig = { 50 | options: { 51 | banner: 52 | '/* <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> - GPLv3 & MIT License */\n', 53 | sourceMap: true, 54 | sourceMapName: '<%= pkg.name %>.min.map' 55 | }, 56 | dist: { 57 | files: { 58 | '<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>'] 59 | } 60 | } 61 | }, 62 | shellConfig = { 63 | copyFile: { 64 | command: 'cp <%= concat.dist.dest %> examples/pwstrength.js' 65 | }, 66 | copyZxcvbn: { 67 | command: 68 | 'cp bower_components/zxcvbn/dist/zxcvbn.js examples/zxcvbn.js' 69 | }, 70 | copyI18next: { 71 | command: 72 | 'cp bower_components/i18next/i18next.min.js examples/i18next.js' 73 | }, 74 | makeDir: { 75 | command: 'mkdir -p dist' 76 | }, 77 | moveFiles: { 78 | command: 'mv <%= pkg.name %>* dist/' 79 | } 80 | }; 81 | 82 | module.exports = function(grunt) { 83 | 'use strict'; 84 | 85 | grunt.initConfig({ 86 | pkg: grunt.file.readJSON('package.json'), 87 | eslint: eslintConfig, 88 | // eslint-disable-next-line camelcase 89 | jasmine_node: jasmineConfig, 90 | concat: concatConfig, 91 | uglify: uglifyConfig, 92 | shell: shellConfig 93 | }); 94 | 95 | // Load the plugins 96 | grunt.loadNpmTasks('grunt-eslint'); 97 | grunt.loadNpmTasks('grunt-shell'); 98 | grunt.loadNpmTasks('grunt-contrib-uglify'); 99 | grunt.loadNpmTasks('grunt-contrib-concat'); 100 | grunt.loadNpmTasks('grunt-jasmine-node'); 101 | 102 | grunt.registerTask('test', ['eslint', 'jasmine_node']); 103 | 104 | // Default task(s) 105 | grunt.registerTask('default', ['eslint', 'concat', 'uglify', 'shell']); 106 | }; 107 | -------------------------------------------------------------------------------- /MIT-LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2010 Tane Piper 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /OPTIONS.md: -------------------------------------------------------------------------------- 1 | # Options 2 | 3 | The plugin expect the options to follow this structure: 4 | 5 | ```javascript 6 | options = { 7 | common: {}, 8 | rules: {}, 9 | ui: {} 10 | }; 11 | ``` 12 | 13 | Let's see the options of each section. 14 | 15 | ## Common 16 | 17 | * __minChar__: 18 | 19 | Default: `6` (Integer) 20 | 21 | Sets the minimum required of characters for a password to not be considered 22 | too weak. 23 | 24 | * __maxChar__: 25 | 26 | Default: `20` (Integer) 27 | 28 | Sets the maximum allowed characters for a password. 29 | 30 | * __usernameField__: 31 | 32 | Default: `"#username"` (String) 33 | 34 | The username field to match a password to, to ensure the user does not use 35 | the same value for their password. 36 | 37 | * __invalidCharsRegExp__: 38 | 39 | Default: `new RegExp(/[\s,'"]/)` (Regular Expression) 40 | 41 | A regular expression object to use to test for banned characters in the 42 | password. 43 | 44 | * __userInputs__: 45 | 46 | Default: `[]` (Array) 47 | 48 | Array of CSS selectors for input fields with user input. The content of these 49 | fields will be retrieved and passed to the zxcvbn library. 50 | 51 | This option only takes effect when the zxcvbn library is being used. Check 52 | the `zxcvbn` option. 53 | 54 | * __onLoad__: 55 | 56 | Default: `undefined` (Function) 57 | 58 | A callback function, fired on load of the widget. No arguments will be 59 | passed. 60 | 61 | * __onKeyUp__: 62 | 63 | Default: `undefined` (Function) 64 | 65 | A callback function, fired on key up when the user is typing. The `keyup` 66 | event will be passed as first argument, and an object as second with the 67 | score and the verdict text and level. 68 | 69 | This handler will also be called when the `change` or the `onpaste` events 70 | happen. 71 | 72 | * __onScore__: 73 | 74 | Default: `undefined` (Function) 75 | 76 | A callback function that will be called when the score is calculted by the 77 | rules engine, allowing for a final modification before rendering the result. 78 | 79 | The options, the word and the score will be passed as arguments, in that 80 | order. 81 | 82 | * __zxcvbn__: 83 | 84 | Default: `false` (Boolean) 85 | 86 | Use the zxcvbn to calculate the password entropy and use it as the score. For 87 | more information about zxcvbn plase check this 88 | [post](https://tech.dropbox.com/2012/04/zxcvbn-realistic-password-strength-estimation/). 89 | 90 | If you activate this setting, then all the rules won't be applied, and all 91 | the options under the _Rules_ section will be ignored as well. zxcvbn will be 92 | used instead of the default rules engine. 93 | 94 | To use this option you must load the zxcvbn library file on your site. You'll 95 | find it at their [repository](https://github.com/lowe/zxcvbn). 96 | 97 | * __zxcvbnTerms__: 98 | 99 | Default: `[]` (Array) 100 | 101 | An array of strings. A list of banned words for the zxcvbn library. This 102 | option will be ignored if zxcvbn is not being used. 103 | 104 | * __events__: `["keyup", "change", "paste"]` (Array) 105 | 106 | An array of strings. A list of the event names the plugin is going to listen 107 | to. Customize this option to deal with iOS 9 keyup bug. 108 | 109 | * __debug__: 110 | 111 | Default: `false` (Boolean) 112 | 113 | If true, it prints the password strength in the javascript console, so you 114 | can test and tune your rules settings. 115 | 116 | ## Rules 117 | 118 | * __extra__: 119 | 120 | Default: `{}` (Object) 121 | 122 | Empty object where custom rules functions will be stored. 123 | 124 | * __scores__: 125 | 126 | Default: (Object) 127 | 128 | ``` 129 | { 130 | wordNotEmail: -100, 131 | wordMinLength: -50, 132 | wordMaxLength: -50, 133 | wordInvalidChar: -100, 134 | wordSimilarToUsername: -100, 135 | wordSequences: -50, 136 | wordTwoCharacterClasses: 2, 137 | wordRepetitions: -25, 138 | wordLowercase: 1, 139 | wordUppercase: 3, 140 | wordOneNumber: 3, 141 | wordThreeNumbers: 5, 142 | wordOneSpecialChar: 3, 143 | wordTwoSpecialChar: 5, 144 | wordUpperLowerCombo: 2, 145 | wordLetterNumberCombo: 2, 146 | wordLetterNumberCharCombo: 2 147 | } 148 | ``` 149 | 150 | Scores returned by the rules when they match. Negative values are for 151 | penalizations. 152 | 153 | * __activated__: 154 | 155 | Default: (Object) 156 | 157 | ``` 158 | { 159 | wordNotEmail: true, 160 | wordMinLength: true, 161 | wordMaxLength: false, 162 | wordInvalidChar: true, 163 | wordSimilarToUsername: true, 164 | wordSequences: true, 165 | wordTwoCharacterClasses: false, 166 | wordRepetitions: false, 167 | wordLowercase: true, 168 | wordUppercase: true, 169 | wordOneNumber: true, 170 | wordThreeNumbers: true, 171 | wordOneSpecialChar: true, 172 | wordTwoSpecialChar: true, 173 | wordUpperLowerCombo: true, 174 | wordLetterNumberCombo: true, 175 | wordLetterNumberCharCombo: true 176 | } 177 | ``` 178 | An object that sets wich validation rules are activated. By changing this 179 | object is possible to deactivate some validations, or to activate them for 180 | extra security. 181 | 182 | * __raisePower__: 183 | 184 | Default: `1.4` (Double) 185 | 186 | The value used to modify the final score, based on the password length, 187 | allows you to tailor your results. 188 | 189 | * __specialCharClass__: 190 | 191 | Default: `"[!,@,#,$,%,^,&,*,?,_,~]"` (String) 192 | 193 | This is the regular expression class used to match special chars whitin 194 | the rules engine. 195 | 196 | * __commonPasswords__: 197 | 198 | Default: `['123456', 'password', ...]` (Array of Strings) 199 | 200 | A list of the most common passwords. If the user inputs a password present 201 | in the list, then it gets heavily penalized. 202 | 203 | ## User Interface 204 | 205 | * __bootstrap2__: 206 | 207 | Default: `false` (Boolean) 208 | 209 | Set it to `true` to activate support for Bootstrap 2. Incompatible with `bootstrap3` option. 210 | 211 | Bootstrap 4 and 5 are the default supported version of Bootstrap. 212 | 213 | * __bootstrap3__: 214 | 215 | Default: `false` (Boolean) 216 | 217 | Set it to `true` to activate support for Bootstrap 3. Incompatible with `bootstrap2` option. 218 | 219 | Bootstrap 4 and 5 are the default supported version of Bootstrap. 220 | 221 | * __colorClasses__: 222 | 223 | Default: `["danger", "danger", "danger", "warning", "warning", "success"]` (Array) 224 | 225 | The css classes applied to the bar in the progress bar and to the verdicts, 226 | depending on the strength of the password. 227 | 228 | Keep in mind that for Boostrap 2 a `bar-` prefix will be added, that for 229 | Boostrap 3 the prefix will be `progress-bar-`, and that for Bootstrap 4 and 5 it 230 | will be `progress-`. This is the case for the progress bar, not the verdicts. 231 | For the verdicts there is no prefix whatsoever. 232 | 233 | * __showProgressBar__: 234 | 235 | Default: `true` (Boolean) 236 | 237 | Displays the password strength in a progress bar. 238 | 239 | * __progressExtraCssClasses__: (Bootstrap 3, 4 & 5 only) 240 | 241 | Default: `""` (String) 242 | 243 | CSS classes to be added to the generated progress wrapper of the progress-bar. 244 | It is meant to make use of the extra classes provided by Bootstrap. The classes 245 | will be added to the proper DOM element depending of which version of Bootstrap 246 | is being used. 247 | 248 | E.g. 249 | ```css 250 | div.progress.custom-class { 251 | height: 4px; 252 | border-radius: 0px; 253 | background-color: transparent; 254 | } 255 | div.progress.custom-class > .progress-bar { 256 | line-height: 4px; 257 | font-size: 2px; 258 | } 259 | ``` 260 | 261 | * __progressBarEmptyPercentage__: 262 | 263 | Default: `1` (Integer) 264 | 265 | Minimum percentage filled in the progress bar that depicts the strength of 266 | the password. An empty password will show the progress bar filled this much. 267 | 268 | * __progressBarMinWidth__: 269 | 270 | Default: `1` (Integer) 271 | 272 | Minimum width in pixels filled in the progress bar that depicts the 273 | strength of the password. A terrible but not empty password will show the 274 | progress bar filled this many pixels. Makes use of the `min-width` CSS 275 | property. 276 | 277 | * __progressBarMinPercentage__: 278 | 279 | Default: `1` (Integer) 280 | 281 | Minimum percentage filled in the progress bar that depicts the strength of 282 | the password. A terrible but not empty password will show the progress bar 283 | filled this much. 284 | 285 | * __progressBarExtraCssClasses__: 286 | 287 | Default: `""` (String) 288 | 289 | CSS classes to be added to the generated progress bar. It is meant to make 290 | use of the extra classes provided by Bootstrap. The classes will be added to 291 | the proper DOM element depending of which version of Bootstrap is being 292 | used. 293 | 294 | * __showPopover__: 295 | 296 | Default: `false` (Boolean) 297 | 298 | Displays the error messages and the verdicts in a Bootstrap popover, instead 299 | of below the input field. 300 | 301 | If the `showVerdictsInsideProgressBar` option is active, then the verdicts 302 | won't appear on the popover. 303 | 304 | _Note: In Bootstrap 4 & 5, the popper.js library is required. More details in 305 | [the Bootstrap 4 documentation](https://getbootstrap.com/docs/4.1/components/popovers/) 306 | or [the Bootstrap 5 documentation](https://getbootstrap.com/docs/5.0/components/popovers/). 307 | In previous versions tooltip.js and popover.js must be included._ 308 | 309 | * __popoverPlacement__: 310 | 311 | Default: `"bottom"` (String) 312 | 313 | Choose where the popover should be placed in relation with the input field. 314 | The possible options are: `"top"` `"bottom"` `"left"` `"right"` `"auto"` 315 | 316 | * __showStatus__: 317 | 318 | Default: `false` (Boolean) 319 | 320 | Displays the password strength as a validation status in the password field, 321 | for this to work, the Bootstrap form structure must be followed. 322 | 323 | * __spanError__: 324 | 325 | Default: (Function) 326 | 327 | ```javascript 328 | function (options, key) { 329 | var text = options.i18n.t(key); 330 | if (!text) { return ''; } 331 | return '' + text + ''; 332 | }; 333 | ``` 334 | 335 | Function to generate a span with the style property set to red font color, 336 | used in the errors messages. Overwrite for custom styling. 337 | 338 | * __popoverError__: 339 | 340 | Default: (Function) 341 | 342 | ```javascript 343 | function (errors) { 344 | var errors = options.instances.errors, 345 | errorsTitle = options.i18n.t("errorList"), 346 | message = "
" + errorsTitle + "
    "; 347 | 348 | jQuery.each(errors, function (idx, err) { 349 | message += "
  • " + err + "
  • "; 350 | }); 351 | message += "
"; 352 | return message; 353 | }; 354 | ``` 355 | 356 | Function to generate the errors list in the popover if `showPopover` is true. 357 | Overwrite for custom styling. 358 | 359 | * __showVerdicts__: 360 | 361 | Default: `true` (Boolean) 362 | 363 | Determines if the verdicts are displayed or not. 364 | 365 | * __showVerdictsInsideProgressBar__: 366 | 367 | Default: `false` (Boolean) 368 | 369 | Determines if the verdicts are displayed inside the progress bar or not. When 370 | this setting is active, the verdict viewport is ignored and they won't appear 371 | on the popover if it is being showed. Also this option overrides the value of 372 | the _showVerdicts_ one. 373 | 374 | * __useVerdictCssClass__: 375 | 376 | Default: `false` (Boolean) 377 | 378 | Determines if it's necessary to add a css class in the verdict element. 379 | 380 | * __showErrors__: 381 | 382 | Default: `false` (Boolean) 383 | 384 | Determines if the error list is displayed with the progress bar or not. 385 | 386 | * __showScore__: 387 | 388 | Default: `false` (Boolean) 389 | 390 | Determines if the password score is displayed with the progress bar or not. 391 | 392 | * __container__: 393 | 394 | Default: `undefined` (CSS selector, or DOM node) 395 | 396 | If defined, it will be used to locate the viewports, if undefined, the parent 397 | of the input password will be used instead. The viewports must be children of 398 | this node. 399 | 400 | * __viewports__: 401 | 402 | Default: (Object) 403 | 404 | ``` 405 | { 406 | progress: undefined, 407 | verdict: undefined, 408 | errors: undefined, 409 | score: undefined 410 | } 411 | ``` 412 | 413 | An object containing the viewports to use to show the elements of the 414 | strength meter. Each one can be a CSS selector (`"#progressbar"`) or a DOM 415 | node reference. They must be contained by the `container`. 416 | 417 | * __scores__: 418 | 419 | Default: `[0, 14, 26, 38, 50]` (Array) 420 | 421 | The scores used to determine what colorClasses and verdicts to display. It 422 | has to have 5 elements, which creates 6 categories of strength (the 6 423 | possible verdicts). 424 | 425 | ### Example of an options object 426 | 427 | ```javascript 428 | var options = {}; 429 | options.common = { 430 | minChar: 8 431 | }; 432 | options.rules = { 433 | activated: { 434 | wordTwoCharacterClasses: true, 435 | wordRepetitions: true 436 | } 437 | }; 438 | options.ui = { 439 | showErrors: true 440 | }; 441 | ``` 442 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jQuery Password Strength Meter for Twitter Bootstrap 2 | 3 | [![Code Climate](https://codeclimate.com/github/ablanco/jquery.pwstrength.bootstrap.png)](https://codeclimate.com/github/ablanco/jquery.pwstrength.bootstrap) 4 | [![JSDeliver](https://data.jsdelivr.com/v1/package/npm/pwstrength-bootstrap/badge?style=rounded)](https://www.jsdelivr.com/package/npm/pwstrength-bootstrap) 5 | 6 | The jQuery Password Strength Meter is a plugin for Twitter Bootstrap that 7 | provides rulesets for visualy displaying the quality of a users typed in 8 | password. 9 | 10 | Dual licensed under the MIT and GPL licenses. You can choose the one that 11 | suits your purposes better. 12 | 13 | [npm entry](https://www.npmjs.com/package/pwstrength-bootstrap) 14 | 15 | ## Looking for a Premium UI Kit? 16 | 17 | AdminKit is a developer friendly & highly customizable Bootstrap 5 admin template featuring hundreds of UI components, forms, tables, charts and icons. [Learn more](https://gumroad.com/a/706106483). 18 | 19 | [![AdminKit - Premium Bootstrap 5 admin template](https://assets.adminkit.io/banners/partners/adminkit-1200x628.jpg)](https://gumroad.com/a/706106483) 20 | 21 | ## Requirements 22 | 23 | - jQuery 1.7 or higher 24 | - Bootstrap 2, 3, 4 or 5 25 | 26 | ### Not using Bootstrap? 27 | 28 | This plugin currently relies heavily on Bootstrap and it is not possible to 29 | use it with another framework without making big changes in the code or 30 | forgetting completely about the UI feedback. 31 | 32 | Forks to use it with another frameworks that I know of: 33 | 34 | - [Zurb Foundation fork by edtownend](https://github.com/edtownend/jquery.pwstrength.foundation) 35 | 36 | ## How to use it 37 | 38 | Get the latest version through [Bower](http://bower.io/search/?q=pwstrength-bootstrap), 39 | [npm](https://www.npmjs.com/package/pwstrength-bootstrap), or just download it 40 | from this [repository](https://github.com/ablanco/jquery.pwstrength.bootstrap/tree/master/dist). 41 | Load it into your HTML after your original bootstrap and jQuery javascript files: 42 | 43 | ```html 44 | 45 | ``` 46 | 47 | Then just invoke the plugin on the password fields you want to attach a strength 48 | meter to. For example, to use it on all the password fields with the default 49 | examples: 50 | 51 | ```javascript 52 | $(":password").pwstrength(); 53 | ``` 54 | 55 | To apply it only to one input and change the options: 56 | 57 | ```javascript 58 | $("#passwd1").pwstrength({ 59 | ui: { showVerdictsInsideProgressBar: true }, 60 | }); 61 | ``` 62 | 63 | ## Options 64 | 65 | Click here to find [the complete list of options for the plugin](OPTIONS.md). 66 | 67 | If you are looking for options to change or add new texts, please have a look 68 | at the internationalization section. 69 | 70 | ## Methods 71 | 72 | Once the plugin has been initialized, it is possible to interact with it 73 | through the methods. 74 | 75 | ### Force an update 76 | 77 | It is possible to force an update on a password strength meter. It will force 78 | a new score calculation and an update of the UI elements, the `onKeyUp` 79 | callback will be called. 80 | 81 | ```javascript 82 | $("#passwdfield").pwstrength("forceUpdate"); 83 | ``` 84 | 85 | ### Remove the strength meter 86 | 87 | This will remove the data associated to the meter, and the UI elements. 88 | 89 | ```javascript 90 | $("#passwdfield").pwstrength("destroy"); 91 | ``` 92 | 93 | ### Adding Custom Rules 94 | 95 | The plugin comes with the functionality to easily define your own custom rules. 96 | The format is as follows: 97 | 98 | ```javascript 99 | $("#passwdfield").pwstrength( 100 | "addRule", 101 | "ruleName", 102 | function (options, word, score) {}, 103 | rule_score, 104 | rule_enabled 105 | ); 106 | ``` 107 | 108 | Example: 109 | 110 | ```javascript 111 | $("#passwdfield").pwstrength( 112 | "addRule", 113 | "testRule", 114 | function (options, word, score) { 115 | return word.match(/[a-z].[0-9]/) && score; 116 | }, 117 | 10, 118 | true 119 | ); 120 | ``` 121 | 122 | ### Change the score associated to a rule 123 | 124 | It is possible to change the score given by a rule. It works like this: 125 | 126 | ```javascript 127 | $("#passwdfield").pwstrength("changeScore", "wordSequences", -100); 128 | ``` 129 | 130 | That would penalize even more the presence of sequences in the password. 131 | 132 | ### Activate and deactivate rules 133 | 134 | It is also possible to activate or deactivate rules. It as simple as: 135 | 136 | ```javascript 137 | $("#passwdfield").pwstrength("ruleActive", "wordSequences", false); 138 | ``` 139 | 140 | That would avoid looking for sequences in the password being tested. 141 | 142 | ### Know if all password inputs pass a specific rule 143 | 144 | This method allows to make a quick check to test if all password inputs in the 145 | page pass a rule, the method returns a boolean value. Example: 146 | 147 | ```javascript 148 | $("#passwdfield").pwstrength("ruleIsMet", "wordSequences"); 149 | ``` 150 | 151 | ## Callback Functions 152 | 153 | The plugin provides three callback functions, onLoad, onKeyUp, and scoreCalculated. You can use 154 | them like this: 155 | 156 | ```javascript 157 | $(document).ready(function () { 158 | var options = {}; 159 | options.common = { 160 | onLoad: function () { 161 | $("#messages").text("Start typing password"); 162 | }, 163 | onKeyUp: function (evt, data) { 164 | $("#length-help-text").text( 165 | "Current length: " + 166 | $(evt.target).val().length + 167 | " and score: " + 168 | data.score 169 | ); 170 | }, 171 | onScore: function (options, word, totalScoreCalculated) { 172 | // If my word meets a specific scenario, I want the min score to 173 | // be the level 1 score, for example. 174 | if ( 175 | word.length === 20 && 176 | totalScoreCalculated < options.ui.scores[1] 177 | ) { 178 | // Score doesn't meet the score[1]. So we will return the min 179 | // numbers of points to get that score instead. 180 | return options.ui.score[1]; 181 | } 182 | // Fall back to the score that was calculated by the rules engine. 183 | // Must pass back the score to set the total score variable. 184 | return totalScoreCalculated; 185 | }, 186 | }; 187 | $(":password").pwstrength(options); 188 | }); 189 | ``` 190 | 191 | ## Extra restrictions 192 | 193 | The plugin comes with two validation rules deactivated by default. One checks 194 | the length of the password and penalizes it if it's too long; and the other 195 | checks if the password contains a banned char, and penalizes it if it does. 196 | 197 | You can configure the max length of the password by using the option `maxChar`. 198 | You can also configure the invalid chars by using the option 199 | `invalidCharsRegExp`. 200 | 201 | If you need these restrictions you just need to activate this two rules: 202 | 203 | ```javascript 204 | $(document).ready(function () { 205 | var options = {}; 206 | options.rules = { 207 | activated: { 208 | wordMaxLength: true, 209 | wordInvalidChar: true, 210 | }, 211 | }; 212 | $(":password").pwstrength(options); 213 | }); 214 | ``` 215 | 216 | ## Internationalization (i18n) 217 | 218 | The plugin has support for internationalization. It also comes with some 219 | example translations, you can find them in the [locales folder](locales). 220 | 221 | The plugin provides a default implementation of the translation function, but 222 | you can override it using the option `i18n.t`. 223 | 224 | The default implementation will try to make use of the popular 225 | [i18next front-end translation tool](http://i18next.com/). If you happen to 226 | use it, then you only need to add the translations into your resources and 227 | load them. The plugin will automatically make use of it. You can find more 228 | details about and how to use it i18next in their website. There is also an 229 | example in the repository that uses that library. 230 | 231 | In case the i18next library is not available, then the default behavior is 232 | to return the english texts as a fallback. 233 | 234 | ### What are the translatable texts? 235 | 236 | You can find the non-rules texts in any of the 237 | [provided translation example files](locales), and besides what you find there, 238 | every rule name is a valid key for the translation file. **You can use this to 239 | add new error messages (or remove them) for the engine rules**. 240 | 241 | ### How to customize the translation function 242 | 243 | If you want to manage translations yourself or you don't use i18next you can 244 | override the default translation function like this: 245 | 246 | ```javascript 247 | $(document).ready(function () { 248 | var options = {}; 249 | options.i18n = { 250 | t: function (key) { 251 | var result = translateThisThing(key); // Do your magic here 252 | 253 | return result === key ? "" : result; // This assumes you return the 254 | // key if no translation was found, adapt as necessary 255 | }, 256 | }; 257 | $(":password").pwstrength(options); 258 | }); 259 | ``` 260 | 261 | You can find an example of some keys and translations in the 262 | [locales folder](locales). 263 | 264 | ## Examples 265 | 266 | There are some examples in the `examples` directory. Just serve them with any 267 | webserver and check them in your browser. Make sure you serve the `examples` 268 | directory as the site root. For example: 269 | 270 | ```bash 271 | cd examples 272 | python -m SimpleHTTPServer 273 | ``` 274 | 275 | And go to [localhost:8000](http://localhost:8000). 276 | 277 | Alternatively, you can check-out the examples in a [hosted demo](https://cdn.rawgit.com/ablanco/jquery.pwstrength.bootstrap/master/examples/index.html). 278 | 279 | ## Build and Test 280 | 281 | The build and testing processes rely on [Grunt](http://gruntjs.com/). To use 282 | them you need to have [node.js](http://nodejs.org/) and grunt-cli installed on 283 | your system. Assuming you have node.js in your Linux system, you'll need to do 284 | something like this: 285 | 286 | ```bash 287 | sudo npm install -g grunt-cli 288 | ``` 289 | 290 | Now you have the grunt command line utility installed globally. 291 | 292 | ### Bundle and minified 293 | 294 | To generate the bundle and the minified file you only need to execute this in 295 | the project directory: 296 | 297 | ```bash 298 | npm install -d 299 | grunt 300 | ``` 301 | 302 | It will check the source files, and build a minified version with its 303 | corresponding source map. The generated files will be available in the `dist` 304 | directory. 305 | 306 | ### Testing 307 | 308 | To run the tests the only thing you need to do is execute this in the project 309 | directory: 310 | 311 | ```bash 312 | npm install -d 313 | grunt test 314 | ``` 315 | 316 | It will check all the source files with [ESLint](https://eslint.org/) and run the 317 | tests, which are written with [Jasmine](http://jasmine.github.io/). You'll find 318 | the tests source code in the `spec` directory. 319 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pwstrength-bootstrap", 3 | "version": "3.1.3", 4 | "homepage": "https://github.com/ablanco/jquery.pwstrength.bootstrap", 5 | "authors": ["Alejandro Blanco "], 6 | "description": "jQuery plugin for Twitter Bootstrap that provides rulesets for visualy displaying the quality of a users typed in password.", 7 | "main": "dist/pwstrength-bootstrap.js", 8 | "dependencies": { 9 | "jquery": ">=1.7.0", 10 | "bootstrap": ">=2.0.0", 11 | "zxcvbn": ">=4.2.0", 12 | "i18next": ">=2.4.0", 13 | "popper.js": ">=1.14.3" 14 | }, 15 | "moduleType": ["globals"], 16 | "keywords": ["jquery", "bootstrap", "password", "strength", "meter"], 17 | "license": "MIT", 18 | "ignore": [ 19 | "**/.*", 20 | "node_modules", 21 | "bower_components", 22 | "spec", 23 | "examples", 24 | "AUTHORS.txt", 25 | "CHANGES.md", 26 | "Gruntfile.js", 27 | "package.json" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /dist/pwstrength-bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /* pwstrength-bootstrap 2024-08-15 - GPLv3 & MIT License */ 2 | 3 | !function(s){var t={};!function(o){"use strict";t.fallback={wordMinLength:"Your password is too short",wordMaxLength:"Your password is too long",wordInvalidChar:"Your password contains an invalid character",wordNotEmail:"Do not use your email as your password",wordSimilarToUsername:"Your password cannot contain your username",wordTwoCharacterClasses:"Use different character classes",wordRepetitions:"Too many repetitions",wordSequences:"Your password contains sequences",errorList:"Errors:",veryWeak:"Very Weak",weak:"Weak",normal:"Normal",medium:"Medium",strong:"Strong",veryStrong:"Very Strong"},t.t=function(r){var e="";return(e=o?o.t(r):t.fallback[r])===r?"":e}}(window.i18next);var r,c={};try{!s&&module&&module.exports&&(s=require("jquery"),r=require("jsdom").jsdom,s=s(r().defaultView))}catch(r){}!function(i){"use strict";var r={};c.forbiddenSequences=["0123456789","abcdefghijklmnopqrstuvwxyz","qwertyuiop","asdfghjkl","zxcvbnm","!@#$%^&*()_+"],r.wordNotEmail=function(r,e,o){return e.match(/^([\w!#$%&'*+\-/=?^`{|}~]+\.)*[\w!#$%&'*+\-/=?^`{|}~]+@((((([a-z0-9]{1}[a-z0-9-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(:\d{1,5})?)$/i)?o:0},r.wordMinLength=function(r,e,o){var s=e.length,e=Math.pow(s,r.rules.raisePower);return sr.common.maxChar?o:e},r.wordInvalidChar=function(r,e,o){return r.common.invalidCharsRegExp.test(e)?o:0},r.wordMinLengthStaticScore=function(r,e,o){return e.lengthr.common.maxChar?0:o},r.wordSimilarToUsername=function(r,e,o){r=i(r.common.usernameField).val();return r&&e.toLowerCase().match(r.replace(/[-[\]/{}()*+=?:.\\^$|!,]/g,"\\$&").toLowerCase())?o:0},r.wordTwoCharacterClasses=function(r,e,o){r=new RegExp("(."+r.rules.specialCharClass+")");if(e.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/)||e.match(/([a-zA-Z])/)&&e.match(/([0-9])/)||e.match(r)&&e.match(/[a-zA-Z0-9_]/))return o},r.wordRepetitions=function(r,e,o){return e.match(/(.)\1\1/)?o:0},r.wordSequences=function(r,o,e){var s,t=!1;return 2'+e+"":""},u.ui.popoverError=function(r){"use strict";var e=r.instances.errors,o="
"+r.i18n.t("errorList")+'
    ';return s.each(e,function(r,e){o+="
  • "+e+"
  • "}),o+="
"},u.ui.showVerdicts=!0,u.ui.showVerdictsInsideProgressBar=!1,u.ui.useVerdictCssClass=!1,u.ui.showErrors=!1,u.ui.showScore=!1,u.ui.container=void 0,u.ui.viewports={progress:void 0,verdict:void 0,errors:void 0,score:void 0},u.ui.scores=[0,14,26,38,50],u.i18n={},u.i18n.t=t.t;var d={};!function(n){"use strict";var a=["error","warning","success"],o=["veryWeak","weak","normal","medium","strong","veryStrong"];d.getContainer=function(r,e){r=n(r.ui.container);return r=!r||1!==r.length?e.parent():r},d.findElement=function(r,e,o){return(e?r.find(e):r).find(o)},d.getUIElements=function(r,e){var o;return r.instances.viewports||(o=d.getContainer(r,e),(e={}).$progressbar=d.findElement(o,r.ui.viewports.progress,"div.progress"),r.ui.showVerdictsInsideProgressBar&&(e.$verdict=e.$progressbar.find("span.password-verdict")),r.ui.showPopover||(r.ui.showVerdictsInsideProgressBar||(e.$verdict=d.findElement(o,r.ui.viewports.verdict,"span.password-verdict")),e.$errors=d.findElement(o,r.ui.viewports.errors,"ul.error-list")),e.$score=d.findElement(o,r.ui.viewports.score,"span.password-score"),r.instances.viewports=e)},d.initHelper=function(r,e,o,s){r=d.getContainer(r,e);s?r.find(s).append(o):n(o).insertAfter(e)},d.initVerdict=function(r,e){d.initHelper(r,e,'',r.ui.viewports.verdict)},d.initErrorList=function(r,e){d.initHelper(r,e,'
    ',r.ui.viewports.errors)},d.initScore=function(r,e){d.initHelper(r,e,'',r.ui.viewports.score)},d.initUI=function(r,e){r.ui.showPopover?d.initPopover(r,e):(r.ui.showErrors&&d.initErrorList(r,e),r.ui.showVerdicts&&!r.ui.showVerdictsInsideProgressBar&&d.initVerdict(r,e)),r.ui.showProgressBar&&d.initProgressBar(r,e),r.ui.showScore&&d.initScore(r,e)},d.updateVerdict=function(r,e,o,s){e=d.getUIElements(r,e).$verdict;e.removeClass(r.ui.colorClasses.join(" ")),-1"+e+""}),e.html(s)},d.updateScore=function(r,e,o,s){r=d.getUIElements(r,e).$score,e="";s||(e=o.toFixed(2)),r.html(e)},d.updateFieldStatus=function(o,r,e,s){var t=r;o.ui.bootstrap2?t=r.parents(".control-group").first():o.ui.bootstrap3&&(t=r.parents(".form-group").first()),n.each(a,function(r,e){e=d.cssClassesForBS(o,e),t.removeClass(e)}),s||(e=a[Math.floor(e/2)],e=d.cssClassesForBS(o,e),t.addClass(e))},d.cssClassesForBS=function(r,e){return r.ui.bootstrap3?e="has-"+e:r.ui.bootstrap2||(e="border-"+(e="error"===e?"danger":e)),e},d.getVerdictAndCssClass=function(r,e){return void 0===e?["",0]:(e=e<=r.ui.scores[0]?0:e
    '+o+"",a=!1),r.ui.showErrors&&(0
    ',trigger:"hover focus",title:"",delay:0,html:!1,container:!1},a.fn.tooltip.noConflict=function(){return a.fn.tooltip=c,this}}(window.jQuery),!function(a){var b=function(a,b){this.init("popover",a,b)};b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype,{constructor:b,setContent:function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"html":"text"](c),a.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var a,b=this.$element,c=this.options;return a=(typeof c.content=="function"?c.content.call(b[0]):c.content)||b.attr("data-content"),a},tip:function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}});var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("popover"),f=typeof c=="object"&&c;e||d.data("popover",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.defaults=a.extend({},a.fn.tooltip.defaults,{placement:"right",trigger:"click",content:"",template:'

    '}),a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(window.jQuery) -------------------------------------------------------------------------------- /examples/bootstrap2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Bootstrap 2 Password Strength Meter Example 5 | 6 | 7 | 8 |
    9 |

    Bootstrap 2 Password Strength Meter Example

    10 |
    11 |
    12 | Please type in your password 13 | User: 14 |
    15 | Pass: 16 |
    17 |
    18 |
    19 |
    20 |
    21 |
    22 |
    jQuery(document).ready(function () {
    23 |     "use strict";
    24 |     var options = {};
    25 |     options.common = {
    26 |         onLoad: function () {
    27 |             $('#messages').text('Start typing password');
    28 |         }
    29 |     };
    30 |     options.ui = {
    31 |         bootstrap2: true,
    32 |         showErrors: true
    33 |     };
    34 |     $(':password').pwstrength(options);
    35 | });
    36 |
    37 |
    38 |

    Go back

    39 |
    40 | 41 | 42 | 43 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /examples/bootstrap2/multi.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Bootstrap 2 Password Strength Meter Example - Multiple inputs 5 | 6 | 7 | 8 |
    9 |

    Bootstrap 2 Password Strength Meter Example - Multiple inputs

    10 |
    11 | User:
    12 |
    13 | Please type in your first password 14 | Pass: 15 |
    16 | 17 |
    18 | Please type in your second password 19 | Pass: 20 |
    21 |
    22 |
    23 |
    24 |
    jQuery(document).ready(function () {
    25 |     "use strict";
    26 |     $(':password').pwstrength({
    27 |         ui: { bootstrap2: true }
    28 |     });
    29 | });
    30 |
    31 |
    32 |

    Go back

    33 |
    34 | 35 | 36 | 37 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /examples/bootstrap2/popover.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Bootstrap 2 Password Strength Meter Example - Popover 5 | 6 | 7 | 8 |
    9 |

    Bootstrap 2 Password Strength Meter Example - Popover

    10 |
    11 |
    12 | Please type in your password 13 | User:
    14 | Pass: 15 |
    16 |
    17 |
    18 |
    19 |
    jQuery(document).ready(function () {
    20 |     "use strict";
    21 |     var options = {};
    22 |     options.ui = {
    23 |         showPopover: true,
    24 |         showErrors: true,
    25 |         bootstrap2: true
    26 |     };
    27 |     $(':password').pwstrength(options);
    28 | });
    29 |
    30 |
    31 |

    Go back

    32 |
    33 | 34 | 35 | 36 | 37 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /examples/bootstrap2/rules.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Bootstrap 2 Password Strength Meter Example - Custom Rules 5 | 6 | 7 | 8 |
    9 |

    Bootstrap 2 Password Strength Meter Example - Custom Rules

    10 |
    11 |
    12 | Please type in your password 13 | User:
    14 | Pass: 15 |
    16 |
    17 |

    The words "password", "3141592" and "yeah" have been blocked by the 18 | custom rules. So if the password contains one of the blocked words, 19 | then the password will be marked as weak.

    20 |
    21 |
    22 |
    jQuery(document).ready(function () {
    23 |     "use strict";
    24 |     var options = { ui: { bootstrap2: true } },
    25 |         $password = $(':password').pwstrength(options),
    26 |         common_words = ["password", "3141592", "yeah"];
    27 | 
    28 |     $password.pwstrength("addRule", "commonWords", function (options, word, score) {
    29 |         var result = false;
    30 |         $.each(common_words, function (i, item) {
    31 |             var re = new RegExp(item, "gi");
    32 |             if (word.match(re)) {
    33 |                 result = score;
    34 |             }
    35 |         });
    36 |         return result;
    37 |     }, -100, true);
    38 | });
    39 |
    40 |
    41 |

    Go back

    42 |
    43 | 44 | 45 | 46 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /examples/bootstrap3/barCssClasses.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Bootstrap 3 Password Strength Meter Example - Extra Css Classes for the Progress Bar 5 | 6 | 7 | 8 |
    9 |

    Bootstrap 3 Password Strength Meter Example - Extra Css Classes for the Progress Bar

    10 |
    11 |
    12 |
    13 |
    14 | 15 | 16 |
    17 |
    18 |
    19 |
    20 |
    21 |
    22 |
    23 |
    24 |
    25 |
    26 |
    27 |
    28 |
    jQuery(document).ready(function () {
    29 |     "use strict";
    30 |     var options = {};
    31 |     options.ui = {
    32 |         bootstrap3: true,
    33 |         container: "#pwd-container",
    34 |         showVerdictsInsideProgressBar: true,
    35 |         viewports: {
    36 |             progress: ".pwstrength_viewport_progress"
    37 |         },
    38 |         progressBarExtraCssClasses: "progress-bar-striped active"
    39 |     };
    40 |     options.common = {
    41 |         debug: true,
    42 |         onLoad: function () {
    43 |             $('#messages').text('Start typing password');
    44 |         }
    45 |     };
    46 |     $(':password').pwstrength(options);
    47 | });
    48 |
    49 |
    50 |

    Go back

    51 |
    52 | 53 | 54 | 55 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /examples/bootstrap3/bootstrap3.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.5 (http://getbootstrap.com) 3 | * Copyright 2011-2016 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | 7 | /*! 8 | * Generated using the Bootstrap Customizer (http://getbootstrap.com/customize/?id=f74bf9a4a7b6b231e920b9877fb70144) 9 | * Config saved to config.json and https://gist.github.com/f74bf9a4a7b6b231e920b9877fb70144 10 | */ 11 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(t){"use strict";var e=t.fn.jquery.split(" ")[0].split(".");if(e[0]<2&&e[1]<9||1==e[0]&&9==e[1]&&e[2]<1||e[0]>2)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 3")}(jQuery),+function(t){"use strict";function e(e){return this.each(function(){var i=t(this),n=i.data("bs.tooltip"),r="object"==typeof e&&e;(n||!/destroy|hide/.test(e))&&(n||i.data("bs.tooltip",n=new o(this,r)),"string"==typeof e&&n[e]())})}var o=function(t,e){this.type=null,this.options=null,this.enabled=null,this.timeout=null,this.hoverState=null,this.$element=null,this.inState=null,this.init("tooltip",t,e)};o.VERSION="3.3.6",o.TRANSITION_DURATION=150,o.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},o.prototype.init=function(e,o,i){if(this.enabled=!0,this.type=e,this.$element=t(o),this.options=this.getOptions(i),this.$viewport=this.options.viewport&&t(t.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var n=this.options.trigger.split(" "),r=n.length;r--;){var s=n[r];if("click"==s)this.$element.on("click."+this.type,this.options.selector,t.proxy(this.toggle,this));else if("manual"!=s){var p="hover"==s?"mouseenter":"focusin",a="hover"==s?"mouseleave":"focusout";this.$element.on(p+"."+this.type,this.options.selector,t.proxy(this.enter,this)),this.$element.on(a+"."+this.type,this.options.selector,t.proxy(this.leave,this))}}this.options.selector?this._options=t.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},o.prototype.getDefaults=function(){return o.DEFAULTS},o.prototype.getOptions=function(e){return e=t.extend({},this.getDefaults(),this.$element.data(),e),e.delay&&"number"==typeof e.delay&&(e.delay={show:e.delay,hide:e.delay}),e},o.prototype.getDelegateOptions=function(){var e={},o=this.getDefaults();return this._options&&t.each(this._options,function(t,i){o[t]!=i&&(e[t]=i)}),e},o.prototype.enter=function(e){var o=e instanceof this.constructor?e:t(e.currentTarget).data("bs."+this.type);return o||(o=new this.constructor(e.currentTarget,this.getDelegateOptions()),t(e.currentTarget).data("bs."+this.type,o)),e instanceof t.Event&&(o.inState["focusin"==e.type?"focus":"hover"]=!0),o.tip().hasClass("in")||"in"==o.hoverState?void(o.hoverState="in"):(clearTimeout(o.timeout),o.hoverState="in",o.options.delay&&o.options.delay.show?void(o.timeout=setTimeout(function(){"in"==o.hoverState&&o.show()},o.options.delay.show)):o.show())},o.prototype.isInStateTrue=function(){for(var t in this.inState)if(this.inState[t])return!0;return!1},o.prototype.leave=function(e){var o=e instanceof this.constructor?e:t(e.currentTarget).data("bs."+this.type);return o||(o=new this.constructor(e.currentTarget,this.getDelegateOptions()),t(e.currentTarget).data("bs."+this.type,o)),e instanceof t.Event&&(o.inState["focusout"==e.type?"focus":"hover"]=!1),o.isInStateTrue()?void 0:(clearTimeout(o.timeout),o.hoverState="out",o.options.delay&&o.options.delay.hide?void(o.timeout=setTimeout(function(){"out"==o.hoverState&&o.hide()},o.options.delay.hide)):o.hide())},o.prototype.show=function(){var e=t.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(e);var i=t.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(e.isDefaultPrevented()||!i)return;var n=this,r=this.tip(),s=this.getUID(this.type);this.setContent(),r.attr("id",s),this.$element.attr("aria-describedby",s),this.options.animation&&r.addClass("fade");var p="function"==typeof this.options.placement?this.options.placement.call(this,r[0],this.$element[0]):this.options.placement,a=/\s?auto?\s?/i,l=a.test(p);l&&(p=p.replace(a,"")||"top"),r.detach().css({top:0,left:0,display:"block"}).addClass(p).data("bs."+this.type,this),this.options.container?r.appendTo(this.options.container):r.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var h=this.getPosition(),u=r[0].offsetWidth,c=r[0].offsetHeight;if(l){var f=p,d=this.getPosition(this.$viewport);p="bottom"==p&&h.bottom+c>d.bottom?"top":"top"==p&&h.top-cd.width?"left":"left"==p&&h.left-us.top+s.height&&(n.top=s.top+s.height-a)}else{var l=e.left-r,h=e.left+r+o;ls.right&&(n.left=s.left+s.width-h)}return n},o.prototype.getTitle=function(){var t,e=this.$element,o=this.options;return t=e.attr("data-original-title")||("function"==typeof o.title?o.title.call(e[0]):o.title)},o.prototype.getUID=function(t){do t+=~~(1e6*Math.random());while(document.getElementById(t));return t},o.prototype.tip=function(){if(!this.$tip&&(this.$tip=t(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},o.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},o.prototype.enable=function(){this.enabled=!0},o.prototype.disable=function(){this.enabled=!1},o.prototype.toggleEnabled=function(){this.enabled=!this.enabled},o.prototype.toggle=function(e){var o=this;e&&(o=t(e.currentTarget).data("bs."+this.type),o||(o=new this.constructor(e.currentTarget,this.getDelegateOptions()),t(e.currentTarget).data("bs."+this.type,o))),e?(o.inState.click=!o.inState.click,o.isInStateTrue()?o.enter(o):o.leave(o)):o.tip().hasClass("in")?o.leave(o):o.enter(o)},o.prototype.destroy=function(){var t=this;clearTimeout(this.timeout),this.hide(function(){t.$element.off("."+t.type).removeData("bs."+t.type),t.$tip&&t.$tip.detach(),t.$tip=null,t.$arrow=null,t.$viewport=null})};var i=t.fn.tooltip;t.fn.tooltip=e,t.fn.tooltip.Constructor=o,t.fn.tooltip.noConflict=function(){return t.fn.tooltip=i,this}}(jQuery),+function(t){"use strict";function e(e){return this.each(function(){var i=t(this),n=i.data("bs.popover"),r="object"==typeof e&&e;(n||!/destroy|hide/.test(e))&&(n||i.data("bs.popover",n=new o(this,r)),"string"==typeof e&&n[e]())})}var o=function(t,e){this.init("popover",t,e)};if(!t.fn.tooltip)throw new Error("Popover requires tooltip.js");o.VERSION="3.3.6",o.DEFAULTS=t.extend({},t.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),o.prototype=t.extend({},t.fn.tooltip.Constructor.prototype),o.prototype.constructor=o,o.prototype.getDefaults=function(){return o.DEFAULTS},o.prototype.setContent=function(){var t=this.tip(),e=this.getTitle(),o=this.getContent();t.find(".popover-title")[this.options.html?"html":"text"](e),t.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof o?"html":"append":"text"](o),t.removeClass("fade top bottom left right in"),t.find(".popover-title").html()||t.find(".popover-title").hide()},o.prototype.hasContent=function(){return this.getTitle()||this.getContent()},o.prototype.getContent=function(){var t=this.$element,e=this.options;return t.attr("data-content")||("function"==typeof e.content?e.content.call(t[0]):e.content)},o.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var i=t.fn.popover;t.fn.popover=e,t.fn.popover.Constructor=o,t.fn.popover.noConflict=function(){return t.fn.popover=i,this}}(jQuery),+function(t){"use strict";function e(){var t=document.createElement("bootstrap"),e={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var o in e)if(void 0!==t.style[o])return{end:e[o]};return!1}t.fn.emulateTransitionEnd=function(e){var o=!1,i=this;t(this).one("bsTransitionEnd",function(){o=!0});var n=function(){o||t(i).trigger(t.support.transition.end)};return setTimeout(n,e),this},t(function(){t.support.transition=e(),t.support.transition&&(t.event.special.bsTransitionEnd={bindType:t.support.transition.end,delegateType:t.support.transition.end,handle:function(e){return t(e.target).is(this)?e.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery); -------------------------------------------------------------------------------- /examples/bootstrap3/i18n.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Bootstrap 3 Password Strength Meter Translations Example 5 | 6 | 7 | 8 |
    9 |

    10 |
    11 |
    12 |
    13 |
    14 | 15 | 16 |
    17 |
    18 |
    19 |
    20 |
    21 |
    22 |
    23 |
    24 |
    25 |
    26 |
    27 |
    28 |
    jQuery(document).ready(function () {
     29 |   "use strict";
     30 |   i18next.init({
     31 |     lng: 'es',
     32 |     resources: {
     33 |       es: {
     34 |         translation: {
     35 |           "wordLength": "Tu contraseña es demasiado corta",
     36 |           "wordNotEmail": "No uses tu email como tu contraseña",
     37 |           "wordSimilarToUsername": "Tu contraseña no puede contener tu nombre de usuario",
     38 |           "wordTwoCharacterClasses": "Mezcla diferentes clases de caracteres",
     39 |           "wordRepetitions": "Demasiadas repeticiones",
     40 |           "wordSequences": "Tu contraseña contiene secuencias",
     41 |           "errorList": "Errores:",
     42 |           "veryWeak": "Muy Débil",
     43 |           "weak": "Débil",
     44 |           "normal": "Normal",
     45 |           "medium": "Media",
     46 |           "strong": "Fuerte",
     47 |           "veryStrong": "Muy Fuerte",
     48 | 
     49 |           "start": "Comience a escribir la contraseña",
     50 |           "label": "Contraseña",
     51 |           "pageTitle": "Bootstrap 3 Password Strength Meter - Ejemplo de Traducciones",
     52 |           "goBack": "Atrás"
     53 |         }
     54 |       }
     55 |     }
     56 |   }, function () {
     57 |     // Initialized and ready to go
     58 | 
     59 |     var options = {};
     60 |     options.ui = {
     61 |         bootstrap3: true,
     62 |         container: "#pwd-container",
     63 |         showVerdictsInsideProgressBar: true,
     64 |         viewports: {
     65 |             progress: ".pwstrength_viewport_progress"
     66 |         },
     67 |         showErrors: true
     68 |     };
     69 |     options.common = {
     70 |         debug: true,
     71 |         onLoad: function () {
     72 |             $('#messages').html(i18next.t('start'));
     73 |         }
     74 |     };
     75 |     $(':password').pwstrength(options);
     76 | 
     77 |     $('.container').find('label').html(i18next.t('label')).end()
     78 |                    .find('h1').html(i18next.t('pageTitle')).end()
     79 |                    .find('a').html(i18next.t('goBack'));
     80 |   });
     81 | });
    82 |
    83 |
    84 |

    85 |
    86 | 87 | 88 | 89 | 90 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /examples/bootstrap3/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Bootstrap 3 Password Strength Meter Example 5 | 6 | 7 | 8 |
    9 |

    Bootstrap 3 Password Strength Meter Example

    10 |
    11 |
    12 |
    13 |
    14 | 15 | 16 |
    17 |
    18 |
    19 |
    20 |
    21 |
    22 |
    23 |
    24 |
    25 |
    26 |
    27 |
    28 |
    jQuery(document).ready(function () {
    29 |     "use strict";
    30 |     var options = {};
    31 |     options.ui = {
    32 |         bootstrap3: true,
    33 |         container: "#pwd-container",
    34 |         showVerdictsInsideProgressBar: true,
    35 |         viewports: {
    36 |             progress: ".pwstrength_viewport_progress"
    37 |         }
    38 |     };
    39 |     options.common = {
    40 |         debug: true,
    41 |         onLoad: function () {
    42 |             $('#messages').text('Start typing password');
    43 |         }
    44 |     };
    45 |     $(':password').pwstrength(options);
    46 | });
    47 |
    48 |
    49 |

    Go back

    50 |
    51 | 52 | 53 | 54 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /examples/bootstrap3/popover.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Bootstrap 3 Password Strength Meter Example - Popover 5 | 6 | 7 | 8 |
    9 |

    Bootstrap 3 Password Strength Meter Example - Popover

    10 |
    11 |
    12 |
    13 |
    14 | 15 | 16 | 17 | 18 |
    19 |
    20 |
    21 |
    22 |
    23 |
    24 |
    jQuery(document).ready(function () {
    25 |     "use strict";
    26 |     var options = {};
    27 |     options.ui = {
    28 |         bootstrap3: true,
    29 |         showPopover: true,
    30 |         showErrors: true,
    31 |         showProgressBar: false
    32 |     };
    33 |     options.rules = {
    34 |         activated: {
    35 |             wordTwoCharacterClasses: true,
    36 |             wordRepetitions: true
    37 |         }
    38 |     };
    39 |     $(':password').pwstrength(options);
    40 | });
    41 |
    42 |
    43 |

    Go back

    44 |
    45 | 46 | 47 | 48 | 49 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /examples/bootstrap3/status.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Bootstrap 3 Password Strength Meter Example - Status 5 | 6 | 7 | 8 |
    9 |

    Bootstrap 3 Password Strength Meter Example - Status

    10 |
    11 |
    12 |
    13 |
    14 | 15 | 16 |
    17 | 18 |
    19 |
    20 |
    21 |
    22 |
    23 |
    jQuery(document).ready(function () {
    24 |     "use strict";
    25 |     var options = {};
    26 |     options.ui = {
    27 |         bootstrap3: true,
    28 |         container: "#pwd-container",
    29 |         showStatus: true,
    30 |         showProgressBar: false,
    31 |         viewports: {
    32 |             verdict: ".pwstrength_viewport_verdict"
    33 |         }
    34 |     };
    35 |     $(':password').pwstrength(options);
    36 | });
    37 |
    38 |
    39 |

    Go back

    40 |
    41 | 42 | 43 | 44 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /examples/bootstrap3/username.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Bootstrap 3 Password Strength Meter Example - Interact with Other Inputs 5 | 6 | 7 | 8 |
    9 |

    Bootstrap 3 Password Strength Meter Example - Interact with Other Inputs

    10 |
    11 |
    12 |
    13 |
    14 | 15 | 16 |
    17 |
    18 | 19 | 20 |
    21 |
    22 |
    23 |
    24 |
    25 |
    26 |
    27 |
    28 |
    29 |
    30 |
    31 |
    32 |
    jQuery(document).ready(function () {
    33 |     "use strict";
    34 |     var options = {};
    35 |     options.ui = {
    36 |         bootstrap3: true,
    37 |         container: "#pwd-container",
    38 |         showVerdictsInsideProgressBar: true,
    39 |         viewports: {
    40 |             progress: ".pwstrength_viewport_progress"
    41 |         }
    42 |     };
    43 |     options.common = {
    44 |         debug: true,
    45 |         usernameField: "#username"
    46 |     };
    47 |     $(':password').pwstrength(options);
    48 | });
    49 |
    50 |
    51 |

    Go back

    52 |
    53 | 54 | 55 | 56 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /examples/bootstrap3/zxcvbn.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Bootstrap 3 Password Strength Meter Example - zxcvbn 5 | 6 | 7 | 8 |
    9 |

    Bootstrap 3 Password Strength Meter Example - zxcvbn

    10 |
    11 |
    12 |
    13 |
    14 | 15 | 16 |
    17 |
    18 | 19 | 20 |
    21 |
    22 | 23 | 24 |
    25 |
    26 | 27 | 28 |
    29 |
    30 |
    31 | 32 |

    The content of the form inputs and the words samurai, shogun, bushido, daisho and seppuku are disrecommended in the password, and the score will adjust properly.

    33 |
    34 |
    35 |
    36 |
    37 |
    38 |
    39 |
    40 |
    41 |
    jQuery(document).ready(function () {
    42 |     "use strict";
    43 |     var options = {};
    44 |     options.ui = {
    45 |         bootstrap3: true,
    46 |         container: "#pwd-container",
    47 |         viewports: {
    48 |             progress: ".pwstrength_viewport_progress",
    49 |             verdict: ".pwstrength_viewport_verdict"
    50 |         }
    51 |     };
    52 |     options.common = {
    53 |         onLoad: function () {
    54 |             $('#messages').text('Start typing password');
    55 |         },
    56 |         zxcvbn: true,
    57 |         zxcvbnTerms: ['samurai', 'shogun', 'bushido', 'daisho', 'seppuku'],
    58 |         userInputs: ['#year', '#familyname']
    59 |     };
    60 |     $(':password').pwstrength(options);
    61 | });
    62 |
    63 |
    64 |

    Go back

    65 |
    66 | 67 | 68 | 69 | 70 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /examples/bootstrap4/barCssClasses.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Bootstrap 4 Password Strength Meter Example - Extra Css Classes for the Progress Bar 5 | 6 | 7 | 8 |
    9 |

    Bootstrap 4 Password Strength Meter Example - Extra Css Classes for the Progress Bar

    10 |
    11 |
    12 |
    13 |
    14 | 15 | 16 |
    17 |
    18 |
    19 |
    20 |
    21 |
    22 |
    23 |
    24 |
    25 |
    26 |
    27 |
    28 |
    jQuery(document).ready(function () {
    29 |     "use strict";
    30 |     var options = {};
    31 |     options.ui = {
    32 |         container: "#pwd-container",
    33 |         showVerdictsInsideProgressBar: true,
    34 |         viewports: {
    35 |             progress: ".pwstrength_viewport_progress"
    36 |         },
    37 |         progressBarExtraCssClasses: "progress-bar-striped progress-bar-animated"
    38 |     };
    39 |     options.common = {
    40 |         debug: true,
    41 |         onLoad: function () {
    42 |             $('#messages').text('Start typing password');
    43 |         }
    44 |     };
    45 |     $(':password').pwstrength(options);
    46 | });
    47 |
    48 |
    49 |

    Go back

    50 |
    51 | 52 | 53 | 54 | 55 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /examples/bootstrap4/i18n.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Bootstrap 4 Password Strength Meter Translations Example 5 | 6 | 7 | 8 |
    9 |

    10 |
    11 |
    12 |
    13 |
    14 | 15 | 16 |
    17 |
    18 |
    19 |
    20 |
    21 |
    22 |
    23 |
    24 |
    25 |
    26 |
    27 |
    28 |
    jQuery(document).ready(function () {
     29 |   "use strict";
     30 |   i18next.init({
     31 |     lng: 'es',
     32 |     resources: {
     33 |       es: {
     34 |         translation: {
     35 |           "wordLength": "Tu contraseña es demasiado corta",
     36 |           "wordNotEmail": "No uses tu email como tu contraseña",
     37 |           "wordSimilarToUsername": "Tu contraseña no puede contener tu nombre de usuario",
     38 |           "wordTwoCharacterClasses": "Mezcla diferentes clases de caracteres",
     39 |           "wordRepetitions": "Demasiadas repeticiones",
     40 |           "wordSequences": "Tu contraseña contiene secuencias",
     41 |           "errorList": "Errores:",
     42 |           "veryWeak": "Muy Débil",
     43 |           "weak": "Débil",
     44 |           "normal": "Normal",
     45 |           "medium": "Media",
     46 |           "strong": "Fuerte",
     47 |           "veryStrong": "Muy Fuerte",
     48 | 
     49 |           "start": "Comience a escribir la contraseña",
     50 |           "label": "Contraseña",
     51 |           "pageTitle": "Bootstrap 4 Password Strength Meter - Ejemplo de Traducciones",
     52 |           "goBack": "Atrás"
     53 |         }
     54 |       }
     55 |     }
     56 |   }, function () {
     57 |     // Initialized and ready to go
     58 | 
     59 |     var options = {};
     60 |     options.ui = {
     61 |         container: "#pwd-container",
     62 |         showVerdictsInsideProgressBar: true,
     63 |         viewports: {
     64 |             progress: ".pwstrength_viewport_progress"
     65 |         },
     66 |         showErrors: true
     67 |     };
     68 |     options.common = {
     69 |         debug: true,
     70 |         onLoad: function () {
     71 |             $('#messages').html(i18next.t('start'));
     72 |         }
     73 |     };
     74 |     $(':password').pwstrength(options);
     75 | 
     76 |     $('.container').find('label').html(i18next.t('label')).end()
     77 |                    .find('h1').html(i18next.t('pageTitle')).end()
     78 |                    .find('a').html(i18next.t('goBack'));
     79 |   });
     80 | });
    81 |
    82 |
    83 |

    84 |
    85 | 86 | 87 | 88 | 89 | 90 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /examples/bootstrap4/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Bootstrap 4 Password Strength Meter Example 5 | 6 | 7 | 8 |
    9 |

    Bootstrap 4 Password Strength Meter Example

    10 |
    11 |
    12 |
    13 |
    14 | 15 | 16 |
    17 |
    18 |
    19 |
    20 |
    21 |
    22 |
    23 |
    24 |
    25 |
    26 |
    27 |
    28 |
    jQuery(document).ready(function () {
    29 |     "use strict";
    30 |     var options = {};
    31 |     options.ui = {
    32 |         container: "#pwd-container",
    33 |         viewports: {
    34 |             progress: ".pwstrength_viewport_progress"
    35 |         },
    36 |         showVerdictsInsideProgressBar: true
    37 |     };
    38 |     options.common = {
    39 |         debug: true,
    40 |         onLoad: function () {
    41 |             $('#messages').text('Start typing password');
    42 |         }
    43 |     };
    44 |     $(':password').pwstrength(options);
    45 | });
    46 |
    47 |
    48 |

    Go back

    49 |
    50 | 51 | 52 | 53 | 54 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /examples/bootstrap4/popover.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Bootstrap 4 Password Strength Meter Example - Popover 5 | 6 | 7 | 8 |
    9 |

    Bootstrap 4 Password Strength Meter Example - Popover

    10 |
    11 |
    12 |
    13 |
    14 | 15 | 16 | 17 | 18 |
    19 |
    20 |
    21 |
    22 |
    23 |
    24 |
    25 |
    26 |
    27 |
    28 |
    29 |
    30 |
    jQuery(document).ready(function () {
    31 |     "use strict";
    32 |     var options = {};
    33 |     options.ui = {
    34 |         container: "#pwd-container",
    35 |         viewports: {
    36 |             progress: ".pwstrength_viewport_progress"
    37 |         },
    38 |         showPopover: true,
    39 |         showErrors: true
    40 |     };
    41 |     options.common = {
    42 |         debug: true,
    43 |         onLoad: function () {
    44 |             $('#messages').text('Start typing password');
    45 |         }
    46 |     };
    47 |     $(':password').pwstrength(options);
    48 | });
    49 |
    50 |
    51 |

    Go back

    52 |
    53 | 54 | 55 | 56 | 57 | 58 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /examples/bootstrap4/status.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Bootstrap 4 Password Strength Meter Example - Status 5 | 6 | 7 | 8 |
    9 |

    Bootstrap 4 Password Strength Meter Example - Status

    10 |
    11 |
    12 |
    13 |
    14 | 15 | 16 |
    17 | 18 |
    19 |
    20 |
    21 |
    22 |
    23 |
    jQuery(document).ready(function () {
    24 |     "use strict";
    25 |     var options = {};
    26 |     options.ui = {
    27 |         container: "#pwd-container",
    28 |         showStatus: true,
    29 |         showProgressBar: false,
    30 |         viewports: {
    31 |             verdict: ".pwstrength_viewport_verdict"
    32 |         }
    33 |     };
    34 |     $(':password').pwstrength(options);
    35 | });
    36 |
    37 |
    38 |

    Go back

    39 |
    40 | 41 | 42 | 43 | 44 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /examples/bootstrap4/username.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Bootstrap 4 Password Strength Meter Example - Interact with Other Inputs 5 | 6 | 7 | 8 |
    9 |

    Bootstrap 4 Password Strength Meter Example - Interact with Other Inputs

    10 |
    11 |
    12 |
    13 |
    14 | 15 | 16 |
    17 |
    18 | 19 | 20 |
    21 |
    22 |
    23 |
    24 |
    25 |
    26 |
    27 |
    28 |
    29 |
    30 |
    31 |
    32 |
    jQuery(document).ready(function () {
    33 |     "use strict";
    34 |     var options = {};
    35 |     options.ui = {
    36 |         container: "#pwd-container",
    37 |         showVerdictsInsideProgressBar: true,
    38 |         viewports: {
    39 |             progress: ".pwstrength_viewport_progress"
    40 |         }
    41 |     };
    42 |     options.common = {
    43 |         debug: true,
    44 |         usernameField: "#username"
    45 |     };
    46 |     $(':password').pwstrength(options);
    47 | });
    48 |
    49 |
    50 |

    Go back

    51 |
    52 | 53 | 54 | 55 | 56 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /examples/bootstrap4/zxcvbn.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Bootstrap 4 Password Strength Meter Example - zxcvbn 5 | 6 | 7 | 8 |
    9 |

    Bootstrap 4 Password Strength Meter Example - zxcvbn

    10 |
    11 |
    12 |
    13 |
    14 | 15 | 16 |
    17 |
    18 | 19 | 20 |
    21 |
    22 | 23 | 24 |
    25 |
    26 | 27 | 28 |
    29 |
    30 |
    31 | 32 |

    The content of the form inputs and the words samurai, shogun, bushido, daisho and seppuku are disrecommended in the password, and the score will adjust properly.

    33 |
    34 |
    35 |
    36 |
    37 |
    38 |
    39 |
    40 |
    41 |
    jQuery(document).ready(function () {
    42 |     "use strict";
    43 |     var options = {};
    44 |     options.ui = {
    45 |         container: "#pwd-container",
    46 |         viewports: {
    47 |             progress: ".pwstrength_viewport_progress",
    48 |             verdict: ".pwstrength_viewport_verdict"
    49 |         }
    50 |     };
    51 |     options.common = {
    52 |         onLoad: function () {
    53 |             $('#messages').text('Start typing password');
    54 |         },
    55 |         zxcvbn: true,
    56 |         zxcvbnTerms: ['samurai', 'shogun', 'bushido', 'daisho', 'seppuku'],
    57 |         userInputs: ['#year', '#familyname']
    58 |     };
    59 |     $(':password').pwstrength(options);
    60 | });
    61 |
    62 |
    63 |

    Go back

    64 |
    65 | 66 | 67 | 68 | 69 | 70 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /examples/bootstrap5/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Bootstrap 5 Password Strength Meter Example 9 | 11 | 12 | 13 | 14 |
    15 |

    Bootstrap 5 Password Strength Meter Example

    16 |
    17 |
    18 |
    19 | 20 | 21 |
    22 |
    23 |
    24 |
    25 |
    26 |
    27 |
    28 |
    29 |
    30 |
    jQuery(document).ready(function () {
    31 |     "use strict";
    32 |     var options = {};
    33 |     options.ui = {
    34 |         container: "#pwd-container",
    35 |         viewports: {
    36 |             progress: ".pwstrength_viewport_progress"
    37 |         },
    38 |         showVerdictsInsideProgressBar: true
    39 |     };
    40 |     options.common = {
    41 |         debug: true,
    42 |         onLoad: function () {
    43 |             $('#messages').text('Start typing password');
    44 |         }
    45 |     };
    46 |     $(':password').pwstrength(options);
    47 | });
    48 |

    Go back

    49 |
    50 | 51 | 53 | 56 | 57 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /examples/bootstrap5/popover.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Bootstrap 5 Password Strength Meter Example - Popover 9 | 11 | 12 | 13 | 14 |
    15 |

    Bootstrap 5 Password Strength Meter Example - Popover

    16 |
    17 |
    18 |
    19 | 20 | 21 | 22 | 23 |
    24 |
    25 |
    26 |
    27 |
    28 |
    29 |
    30 |
    31 |
    32 |
    jQuery(document).ready(function () {
    33 |     "use strict";
    34 |     var options = {};
    35 |     options.ui = {
    36 |         container: "#pwd-container",
    37 |         viewports: {
    38 |             progress: ".pwstrength_viewport_progress"
    39 |         },
    40 |         showPopover: true,
    41 |         showErrors: true
    42 |     };
    43 |     options.common = {
    44 |         debug: true,
    45 |         onLoad: function () {
    46 |             $('#messages').text('Start typing password');
    47 |         }
    48 |     };
    49 |     $(':password').pwstrength(options);
    50 | });
    51 |

    Go back

    52 |
    53 | 54 | 56 | 59 | 60 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /examples/bootstrap5/rule_options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Bootstrap 5 Password Strength Meter Example - Rule Options 9 | 11 | 12 | 13 | 14 |
    15 |

    Bootstrap 5 Password Strength Meter Example - Rule Options

    16 |
    17 |
    18 |
    19 | 20 | 21 |
    22 |
    23 |
    24 |
    25 |
    26 |
    27 |
    28 |
    29 |
    30 |
    jQuery(document).ready(function () {
    31 |     "use strict";
    32 |     var options = {};
    33 |     options.rules = {
    34 |         activated: {
    35 |             wordTwoCharacterClasses: true
    36 |         },
    37 |         specialCharClass: "[!,@,#,$,%,^,&,*,?,_,~,+]"
    38 |     };
    39 |     options.ui = {
    40 |         container: "#pwd-container",
    41 |         viewports: {
    42 |             progress: ".pwstrength_viewport_progress"
    43 |         },
    44 |         showVerdictsInsideProgressBar: true
    45 |     };
    46 |     options.common = {
    47 |         debug: true,
    48 |         onLoad: function () {
    49 |             $('#messages').text('Start typing password');
    50 |         }
    51 |     };
    52 |     $(':password').pwstrength(options);
    53 | });
    54 |

    Go back

    55 |
    56 | 57 | 59 | 62 | 63 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jQuery Password Strength Meter for Twitter Bootstrap Examples 5 | 6 | 7 | 8 |
    9 |
    10 |

    jQuery Password Strength Meter for Twitter Bootstrap

    11 | 12 |

    Examples

    13 |

    This examples expect the example directory to be serverd as 14 | root, all the path are written based on that assumption.

    15 | 16 |

    Bootstrap 5

    17 |
      18 |
    1. Basic usage
    2. 19 |
    3. Show verdict and errors in a popover
    4. 20 |
    5. Personalize special chars rule options
    6. 21 |
    22 | 23 |

    Bootstrap 4

    24 |
      25 |
    1. Basic usage
    2. 26 |
    3. Interact with other inputs
    4. 27 |
    5. Show verdict and errors in a popover
    6. 28 |
    7. Use zxcvbn to calculate the password strength
    8. 29 |
    9. Show the password strength as a status in the field
    10. 30 |
    11. Use translations
    12. 31 |
    13. Progress bar extra css classes
    14. 32 |
    33 | 34 |

    Bootstrap 3

    35 |
      36 |
    1. Basic usage
    2. 37 |
    3. Interact with other inputs
    4. 38 |
    5. Show verdict and errors in a popover
    6. 39 |
    7. Use zxcvbn to calculate the password strength
    8. 40 |
    9. Show the password strength as a status in the field
    10. 41 |
    11. Use translations
    12. 42 |
    13. Progress bar extra css classes
    14. 43 |
    44 | 45 |

    Bootstrap 2

    46 |
      47 |
    1. Basic usage
    2. 48 |
    3. Multiple password inputs in one page
    4. 49 |
    5. Adding custom rules
    6. 50 |
    7. Show verdict and errors in a popover
    8. 51 |
    52 |
    53 |
    54 | 55 | 56 | -------------------------------------------------------------------------------- /locales/ar.json: -------------------------------------------------------------------------------- 1 | { 2 | "wordMinLength": "كلمة المرور قصيرة جداً", 3 | "wordMaxLength": "كلمة المرور طويلة جدا", 4 | "wordInvalidChar": "تحتوي كلمة المرور على رموز غير صالحة", 5 | "wordNotEmail": "لا تستخدم بريدك الإلكتروني ككلمة مرور", 6 | "wordSimilarToUsername": "لا يمكن ان تحتوي كلمة المرور على إسم المستخدم", 7 | "wordTwoCharacterClasses": "إستخدم فئات أحرف مختلفة", 8 | "wordRepetitions": "تكرارات كثيرة", 9 | "wordSequences": "تحتوي كلمة المرور على أنماط متتابعة", 10 | "errorList": "الأخطاء:", 11 | "veryWeak": "ضعيفة جداً", 12 | "weak": "ضعيفة", 13 | "normal": "عادية", 14 | "medium": "متوسطة", 15 | "strong": "قوية", 16 | "veryStrong": "قوية جداً" 17 | } 18 | -------------------------------------------------------------------------------- /locales/cs.json: -------------------------------------------------------------------------------- 1 | { 2 | "wordMinLength": "Vaše heslo je příliš krátké", 3 | "wordMaxLength": "Vaše heslo je příliš dlouhé", 4 | "wordInvalidChar": "Vaše heslo obsahuje neplatný znak", 5 | "wordNotEmail": "Nepoužívejte Váš email jako Vaše heslo", 6 | "wordSimilarToUsername": "Vaše heslo nesmí obsahovat přihlašovací jméno", 7 | "wordTwoCharacterClasses": "Použijte různé druhy znaků", 8 | "wordRepetitions": "Příliš mnoho opakování", 9 | "wordSequences": "Vaše heslo obsahuje postupnost", 10 | "errorList": "Chyby:", 11 | "veryWeak": "Velmi slabé", 12 | "weak": "Slabé", 13 | "normal": "Normální", 14 | "medium": "Středně silné", 15 | "strong": "Silné", 16 | "veryStrong": "Velmi silné" 17 | } -------------------------------------------------------------------------------- /locales/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "wordMinLength": "Das Passwort ist zu kurz", 3 | "wordMaxLength": "Das Passwort ist zu lang", 4 | "wordInvalidChar": "Das Passwort enthält ein ungültiges Zeichen", 5 | "wordNotEmail": "Das Passwort darf die E-Mail Adresse nicht enthalten", 6 | "wordSimilarToUsername": "Das Passwort darf den Benutzernamen nicht enthalten", 7 | "wordTwoCharacterClasses": "Bitte Buchstaben und Ziffern verwenden", 8 | "wordRepetitions": "Zu viele Wiederholungen", 9 | "wordSequences": "Das Passwort enthält Buchstabensequenzen", 10 | "errorList": "Fehler:", 11 | "veryWeak": "Sehr schwach", 12 | "weak": "Schwach", 13 | "normal": "Normal", 14 | "medium": "Mittel", 15 | "strong": "Stark", 16 | "veryStrong": "Sehr stark" 17 | } 18 | -------------------------------------------------------------------------------- /locales/el.json: -------------------------------------------------------------------------------- 1 | { 2 | "wordMinLength": "Ο κωδικός πρόσβασης δεν έχει τον ελάχιστο αριθμό χαρακτήρων", 3 | "wordMaxLength": "Ο κωδικός πρόσβασής σας είναι πολύ μεγάλος", 4 | "wordInvalidChar": "Ο κωδικός πρόσβασής σας περιέχει έναν μη έγκυρο χαρακτήρα", 5 | "wordNotEmail": "Μη χρησιμοποιείτε το email ως κωδικό", 6 | "wordSimilarToUsername": "Ο κωδικός πρόσβασης δεν πρέπει να περιέχει το username", 7 | "wordTwoCharacterClasses": "Χρησιμοποιήστε διαφορετικές κλάσεις χαρακτήρων", 8 | "wordRepetitions": "Πολλές επαναλήψεις", 9 | "wordSequences": "Ο κωδικός πρόσβασης περιέχει επαναλήψεις", 10 | "errorList": "Σφάλματα:", 11 | "veryWeak": "Πολύ Αδύνατος", 12 | "weak": "Αδύνατος", 13 | "normal": "Κανονικός", 14 | "medium": "Μέτριος", 15 | "strong": "Δυνατός", 16 | "veryStrong": "Πολύ Δυνατός" 17 | } 18 | -------------------------------------------------------------------------------- /locales/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "wordMinLength": "Your password is too short", 3 | "wordMaxLength": "Your password is too long", 4 | "wordInvalidChar": "Your password contains an invalid character", 5 | "wordNotEmail": "Do not use your email as your password", 6 | "wordSimilarToUsername": "Your password cannot contain your username", 7 | "wordTwoCharacterClasses": "Use different character classes", 8 | "wordRepetitions": "Too many repetitions", 9 | "wordSequences": "Your password contains sequences", 10 | "errorList": "Errors:", 11 | "veryWeak": "Very Weak", 12 | "weak": "Weak", 13 | "normal": "Normal", 14 | "medium": "Medium", 15 | "strong": "Strong", 16 | "veryStrong": "Very Strong" 17 | } 18 | -------------------------------------------------------------------------------- /locales/eo.json: -------------------------------------------------------------------------------- 1 | { 2 | "wordMinLength": "Via pasvorto estas tro mallonga", 3 | "wordMaxLength": "Via pasvorto estas tro longa", 4 | "wordInvalidChar": "Via pasvorto enhavas nevalidan karaktero", 5 | "wordNotEmail": "Ne uzu vian retpoŝtadreson kiel la pasvorton", 6 | "wordSimilarToUsername": "Via pasvorto enhavas vian uzanto-nomon", 7 | "wordTwoCharacterClasses": "Uzu signojn de diversaj tipoj (ekz., literoj kaj ciferoj)", 8 | "wordRepetitions": "Tro multaj ripetiĝantaj signoj", 9 | "wordSequences": "Via pasvorto enhavas simplan sinsekvon de signoj", 10 | "errorList": "Eraroj:", 11 | "veryWeak": "Trosimpla", 12 | "weak": "Malforta", 13 | "normal": "Mezforta", 14 | "medium": "Akceptebla", 15 | "strong": "Forta", 16 | "veryStrong": "Elstare Forta" 17 | } 18 | -------------------------------------------------------------------------------- /locales/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "wordMinLength": "Tu contraseña es demasiado corta", 3 | "wordMaxLength": "Tu contraseña es muy larga", 4 | "wordInvalidChar": "Tu contraseña contiene un carácter no válido", 5 | "wordNotEmail": "No uses tu email como tu contraseña", 6 | "wordSimilarToUsername": "Tu contraseña no puede contener tu nombre de usuario", 7 | "wordTwoCharacterClasses": "Mezcla diferentes clases de caracteres", 8 | "wordRepetitions": "Demasiadas repeticiones", 9 | "wordSequences": "Tu contraseña contiene secuencias", 10 | "errorList": "Errores:", 11 | "veryWeak": "Muy Débil", 12 | "weak": "Débil", 13 | "normal": "Normal", 14 | "medium": "Media", 15 | "strong": "Fuerte", 16 | "veryStrong": "Muy Fuerte" 17 | } 18 | -------------------------------------------------------------------------------- /locales/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "wordMinLength": "Votre mot de passe est trop court", 3 | "wordMaxLength": "Votre mot de passe est trop long", 4 | "wordInvalidChar": "Votre mot de passe contient un caractère invalide", 5 | "wordNotEmail": "Ne pas utiliser votre adresse e-mail comme mot de passe", 6 | "wordSimilarToUsername": "Votre mot de passe ne peut pas contenir votre nom d'utilisateur", 7 | "wordTwoCharacterClasses": "Utilisez différents type de caractères", 8 | "wordRepetitions": "Trop de répétitions", 9 | "wordSequences": "Votre mot de passe contient des séquences", 10 | "errorList": "Erreurs:", 11 | "veryWeak": "Très Faible", 12 | "weak": "Faible", 13 | "normal": "Normal", 14 | "medium": "Moyen", 15 | "strong": "Fort", 16 | "veryStrong": "Très Fort" 17 | } 18 | -------------------------------------------------------------------------------- /locales/it.json: -------------------------------------------------------------------------------- 1 | { 2 | "wordMinLength": "La tua password è troppo corta", 3 | "wordMaxLength": "La tua password è troppo lunga", 4 | "wordInvalidChar": "La tua password contiene un carattere non valido", 5 | "wordNotEmail": "Non usare la tua e-mail come password", 6 | "wordSimilarToUsername": "La tua password non può contenere il tuo nome", 7 | "wordTwoCharacterClasses": "Usa classi di caratteri diversi", 8 | "wordRepetitions": "Troppe ripetizioni", 9 | "wordSequences": "La tua password contiene sequenze", 10 | "errorList": "Errori:", 11 | "veryWeak": "Molto debole", 12 | "weak": "Debole", 13 | "normal": "Normale", 14 | "medium": "Media", 15 | "strong": "Forte", 16 | "veryStrong": "Molto forte" 17 | } 18 | -------------------------------------------------------------------------------- /locales/nl.json: -------------------------------------------------------------------------------- 1 | { 2 | "wordMinLength": "Uw wachtwoord is te kort", 3 | "wordMaxLength": "Uw wachtwoord is te lang", 4 | "wordInvalidChar": "Uw wachtwoord bevat ongeldige tekens", 5 | "wordNotEmail": "Gebruik niet uw e-mailadres als uw wachtwoord", 6 | "wordSimilarToUsername": "Uw wachtwoord mag niet uw gebruikersnaam bevatten", 7 | "wordTwoCharacterClasses": "Gebruik verschillende tekens", 8 | "wordRepetitions": "Te veel herhalingen", 9 | "wordSequences": "Uw wachtwoord gebruikt reeksen", 10 | "errorList": "Fouten:", 11 | "veryWeak": "Erg zwak", 12 | "weak": "Zwak", 13 | "normal": "Normaal", 14 | "medium": "Gemiddeld", 15 | "strong": "Sterk", 16 | "veryStrong": "Erg sterk" 17 | } 18 | -------------------------------------------------------------------------------- /locales/no.json: -------------------------------------------------------------------------------- 1 | { 2 | "wordMinLength": "Ditt passord er for kort", 3 | "wordMaxLength": "Ditt passord er for langt", 4 | "wordInvalidChar": "Ditt passord inneholder et ugyldig tegn", 5 | "wordNotEmail": "Ikke bruk din epost som ditt passord", 6 | "wordSimilarToUsername": "Ditt passord er for likt ditt brukernavn", 7 | "wordTwoCharacterClasses": "Bruk en kombinasjon av bokstaver, tall og andre tegn", 8 | "wordRepetitions": "For mange repitisjoner", 9 | "wordSequences": "Ditt passord inneholder repeterende tegn", 10 | "errorList": "Feil:", 11 | "veryWeak": "Veldig Svakt", 12 | "weak": "Svakt", 13 | "normal": "Normal", 14 | "medium": "Medium", 15 | "strong": "Sterkt", 16 | "veryStrong": "Veldig Sterkt" 17 | } 18 | -------------------------------------------------------------------------------- /locales/pl.json: -------------------------------------------------------------------------------- 1 | { 2 | "wordMinLength": "Hasło jest zbyt krótkie", 3 | "wordMaxLength": "Hasło jest za długie", 4 | "wordInvalidChar": "Hasło zawiera nieprawidłowy znak", 5 | "wordNotEmail": "Hasło nie może być Twoim emailem", 6 | "wordSimilarToUsername": "Hasło nie może zawierać nazwy użytkownika", 7 | "wordTwoCharacterClasses": "Użyj innych klas znaków", 8 | "wordRepetitions": "Zbyt wiele powtórzeń", 9 | "wordSequences": "Hasło zawiera sekwencje", 10 | "errorList": "Błędy:", 11 | "veryWeak": "Bardzo słabe", 12 | "weak": "Słabe", 13 | "normal": "Normalne", 14 | "medium": "Średnie", 15 | "strong": "Silne", 16 | "veryStrong": "Bardzo silne" 17 | } 18 | -------------------------------------------------------------------------------- /locales/pt.json: -------------------------------------------------------------------------------- 1 | { 2 | "wordMinLength": "Sua senha é muito curta", 3 | "wordMaxLength": "Sua senha é muito longa", 4 | "wordInvalidChar": "Sua senha contém um caractere inválido", 5 | "wordNotEmail": "Não use seu e-mail como senha", 6 | "wordSimilarToUsername": "Sua senha não pode conter o seu nome de usuário", 7 | "wordTwoCharacterClasses": "Use diferentes classes de caracteres", 8 | "wordRepetitions": "Muitas repetições", 9 | "wordSequences": "Sua senha contém sequências", 10 | "errorList": "Erros:", 11 | "veryWeak": "Muito Fraca", 12 | "weak": "Fraca", 13 | "normal": "Normal", 14 | "medium": "Média", 15 | "strong": "Forte", 16 | "veryStrong": "Muito Forte" 17 | } 18 | -------------------------------------------------------------------------------- /locales/ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "wordMinLength": "Слишком короткий пароль", 3 | "wordMaxLength": "Ваш пароль слишком длинный", 4 | "wordInvalidChar": "Ваш пароль содержит недопустимый символ", 5 | "wordNotEmail": "Не используйте e-mail в качестве пароля", 6 | "wordSimilarToUsername": "Пароль не должен содержать логин", 7 | "wordTwoCharacterClasses": "Используйте разные классы символов", 8 | "wordRepetitions": "Слишком много повторений", 9 | "wordSequences": "Пароль содержит последовательности", 10 | "errorList": "Ошибки:", 11 | "veryWeak": "Очень слабый", 12 | "weak": "Слабый", 13 | "normal": "Нормальный", 14 | "medium": "Средний", 15 | "strong": "Сильный", 16 | "veryStrong": "Очень сильный" 17 | } 18 | -------------------------------------------------------------------------------- /locales/sk.json: -------------------------------------------------------------------------------- 1 | { 2 | "wordMinLength": "Vaše heslo je príliž krátke", 3 | "wordMaxLength": "Vaše heslo je príliš dlhé", 4 | "wordInvalidChar": "Vaše heslo obsahuje neplatný znak", 5 | "wordNotEmail": "Nepoužívajte Váš email ako Vaše heslo", 6 | "wordSimilarToUsername": "Vaše heslo nesmie obsahovať prihlasovacie meno", 7 | "wordTwoCharacterClasses": "Použite rôzne druhy znakov", 8 | "wordRepetitions": "Príliš veľa opakovaní", 9 | "wordSequences": "Vaše heslo obsahuje postupnosť", 10 | "errorList": "Chyby:", 11 | "veryWeak": "Veľmi slabé", 12 | "weak": "Slabé", 13 | "normal": "Normálne", 14 | "medium": "Stredne silné", 15 | "strong": "Silné", 16 | "veryStrong": "Veľmi silné" 17 | } 18 | -------------------------------------------------------------------------------- /locales/th.json: -------------------------------------------------------------------------------- 1 | { 2 | "wordMinLength": "รหัสผ่านของคุณสั้นเกินไป", 3 | "wordMaxLength": "รหัสผ่านของคุณยาวเกินไป", 4 | "wordInvalidChar": "รหัสผ่านของคุณมีอักษรที่ไม่ถูกต้อง", 5 | "wordNotEmail": "คุณไม่สามารถใช้รหัสผ่านเหมือนกับอีเมล์ของคุณได้", 6 | "wordSimilarToUsername": "รหัสผ่านไม่ควรประกอบด้วยคำที่เป็น username", 7 | "wordTwoCharacterClasses": "ลองเป็นกลุ่มคำใหม่", 8 | "wordRepetitions": "มีอักษรซ้ำเยอะเกินไป", 9 | "wordSequences": "รหัสผ่านของคุณเดาง่ายเกินไป", 10 | "errorList": "Errors:", 11 | "veryWeak": "เดาง่ายมาก", 12 | "weak": "เดาง่าย", 13 | "normal": "พอใช้", 14 | "medium": "กำลังดี", 15 | "strong": "ค่อนข้างดี", 16 | "veryStrong": "ดีมาก" 17 | } 18 | -------------------------------------------------------------------------------- /locales/tr.json: -------------------------------------------------------------------------------- 1 | { 2 | "wordMinLength": "Girdiğiniz şifre çok Kısa", 3 | "wordMaxLength": "Parolanız çok uzun", 4 | "wordInvalidChar": "Şifreniz geçersiz bir karakter içeriyor", 5 | "wordNotEmail": "E-mail adresinizi şifreniz içerisinde kullanmayınız", 6 | "wordSimilarToUsername": "Kullanıcı Adınızı şifreniz içerisinde kullanmayınız", 7 | "wordTwoCharacterClasses": "Başka karakter sınıfı kullanınız", 8 | "wordRepetitions": "Çok fazla tekrar var", 9 | "wordSequences": "Şifreniz Dizi içermektedir", 10 | "errorList": "Hatalar:", 11 | "veryWeak": "Çok Zayıf", 12 | "weak": "Zayıf", 13 | "normal": "Normal", 14 | "medium": "Orta", 15 | "strong": "Güçlü", 16 | "veryStrong": "Çok Güçlü" 17 | } 18 | -------------------------------------------------------------------------------- /locales/zh-TW.json: -------------------------------------------------------------------------------- 1 | { 2 | "wordMinLength": "您的密碼太短", 3 | "wordMaxLength": "您的密碼太長", 4 | "wordInvalidChar": "您的密碼包含無效字符", 5 | "wordNotEmail": "不要使用電子郵件作為密碼", 6 | "wordSimilarToUsername": "您的密碼不能包含您的用戶名", 7 | "wordTwoCharacterClasses": "使用不同的字元類型 例如: 大小寫混合", 8 | "wordRepetitions": "太多的重複。例如:1111", 9 | "wordSequences": "你的密碼包含連續英/數字 例如:123 or abc", 10 | "errorList": "錯誤:", 11 | "veryWeak": "非常弱", 12 | "weak": "弱", 13 | "normal": "普通", 14 | "medium": "中等", 15 | "strong": "強", 16 | "veryStrong": "非常強" 17 | } 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pwstrength-bootstrap", 3 | "version": "3.1.3", 4 | "description": "jQuery plugin for Twitter Bootstrap that provides rulesets for visualy displaying the quality of a users typed in password.", 5 | "author": "Alejandro Blanco ", 6 | "homepage": "https://github.com/ablanco/jquery.pwstrength.bootstrap", 7 | "bugs": { 8 | "url": "https://github.com/ablanco/jquery.pwstrength.bootstrap/issues" 9 | }, 10 | "keywords": [ 11 | "bootstrap", 12 | "password", 13 | "strength", 14 | "meter", 15 | "jquery-plugin", 16 | "ecosystem:jquery" 17 | ], 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/ablanco/jquery.pwstrength.bootstrap.git" 21 | }, 22 | "jsdelivr": "dist/pwstrength-bootstrap.min.js", 23 | "license": "(GPL-3.0 OR MIT)", 24 | "scripts": { 25 | "test": "grunt test" 26 | }, 27 | "peerDependencies": { 28 | "bootstrap": ">=2.0.0", 29 | "jquery": ">=1.7.0" 30 | }, 31 | "devDependencies": { 32 | "grunt": "^1.4.0", 33 | "grunt-contrib-concat": "^1.0.1", 34 | "grunt-contrib-uglify": "^5.0.1", 35 | "grunt-eslint": "^23.0.0", 36 | "grunt-jasmine-node": "^0.3.1", 37 | "grunt-shell": "^3.0.1", 38 | "jquery": ">=1.7.0", 39 | "jsdom": "^22.1.0" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /spec/rules.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node, jasmine */ 2 | 3 | 'use strict'; 4 | 5 | var jsdom = require('jsdom').jsdom, 6 | jQuery = require('jquery'), 7 | rulesEngine = require('../src/rules.js'), 8 | options = { 9 | common: { 10 | minChar: 6, 11 | usernameField: '#username' 12 | }, 13 | rules: { 14 | activated: { 15 | wordNotEmail: true, 16 | wordMinLength: true, 17 | wordMaxLength: false, 18 | wordInvalidChar: false, 19 | wordSimilarToUsername: true, 20 | wordSequences: true, 21 | wordTwoCharacterClasses: false, 22 | wordRepetitions: false, 23 | wordLowercase: true, 24 | wordUppercase: true, 25 | wordOneNumber: true, 26 | wordThreeNumbers: true, 27 | wordOneSpecialChar: true, 28 | wordTwoSpecialChar: true, 29 | wordUpperLowerCombo: true, 30 | wordLetterNumberCombo: true, 31 | wordLetterNumberCharCombo: true, 32 | wordIsACommonPassword: true 33 | }, 34 | scores: { 35 | wordNotEmail: -100, 36 | wordMinLength: -50, 37 | wordMaxLength: -50, 38 | wordInvalidChar: -100, 39 | wordSimilarToUsername: -100, 40 | wordSequences: -50, 41 | wordTwoCharacterClasses: 2, 42 | wordRepetitions: -25, 43 | wordLowercase: 1, 44 | wordUppercase: 3, 45 | wordOneNumber: 3, 46 | wordThreeNumbers: 5, 47 | wordOneSpecialChar: 3, 48 | wordTwoSpecialChar: 5, 49 | wordUpperLowerCombo: 2, 50 | wordLetterNumberCombo: 2, 51 | wordLetterNumberCharCombo: 2, 52 | wordIsACommonPassword: -100 53 | }, 54 | raisePower: 1.4, 55 | commonPasswords: ['123456', 'password', '12345678', 'qwerty'], 56 | specialCharClass: '[!,@,#,$,%,^,&,*,?,_,~]' 57 | }, 58 | ui: { 59 | spanError: function(ignore, rule) { 60 | return rule; 61 | } 62 | }, 63 | instances: { 64 | errors: [] 65 | } 66 | }, 67 | strictOptions, 68 | relaxedOptions, 69 | $; 70 | 71 | $ = jQuery(jsdom().defaultView); 72 | 73 | strictOptions = $.extend(true, {}, options); 74 | strictOptions.rules.activated.wordTwoCharacterClasses = true; 75 | strictOptions.rules.activated.wordRepetitions = true; 76 | 77 | relaxedOptions = $.extend(true, {}, options); 78 | relaxedOptions.rules.activated.wordNotEmail = false; 79 | relaxedOptions.rules.activated.wordMinLength = false; 80 | relaxedOptions.rules.activated.wordSequences = false; 81 | 82 | describe('Applying default rules', function() { 83 | it('to a common "password"', function() { 84 | var password = 'password'; 85 | expect(rulesEngine.executeRules(options, password)).toEqual( 86 | -80.62082632004744 87 | ); 88 | }); 89 | 90 | it('to a mixed "charsand19283746"', function() { 91 | var password = 'charsand19283746'; 92 | expect(rulesEngine.executeRules(options, password)).toEqual( 93 | 59.50293012833273 94 | ); 95 | }); 96 | 97 | it('to one with upper cases', function() { 98 | var password = 'QAZwsxTGByhn'; 99 | expect(rulesEngine.executeRules(options, password)).toEqual( 100 | 38.423040924494714 101 | ); 102 | }); 103 | 104 | it('to one with symbols', function() { 105 | var password = 'qpwo#(ei*$&%^'; 106 | expect(rulesEngine.executeRules(options, password)).toEqual( 107 | 47.26775666655811 108 | ); 109 | }); 110 | 111 | it('to one with everything', function() { 112 | var password = '3vEr!t#iNg1$f!n3'; 113 | expect(rulesEngine.executeRules(options, password)).toEqual( 114 | 74.50293012833274 115 | ); 116 | }); 117 | 118 | it('to the perfect one', function() { 119 | var password = 'qm2oUY!%$32znheSK&*3@#'; 120 | expect(rulesEngine.executeRules(options, password)).toEqual( 121 | 101.75159003283396 122 | ); 123 | }); 124 | 125 | it('to one with sequences', function() { 126 | var password = 'qwertypasswdisbad'; 127 | expect(rulesEngine.executeRules(options, password)).toEqual( 128 | 3.799339527161436 129 | ); 130 | }); 131 | 132 | it('to one with reversed sequences', function() { 133 | var password = '0987ytrewqisbad'; 134 | expect(rulesEngine.executeRules(options, password)).toEqual( 135 | 5.312654085941652 136 | ); 137 | }); 138 | 139 | it('to one with an email', function() { 140 | var password = 'test@example.com'; 141 | expect(rulesEngine.executeRules(options, password)).toEqual( 142 | -45.49706987166727 143 | ); 144 | }); 145 | 146 | it('to a "short" one', function() { 147 | var password = 'short'; 148 | expect(rulesEngine.executeRules(options, password)).toEqual( 149 | -39.48173030642061 150 | ); 151 | }); 152 | }); 153 | 154 | describe('Applying stricter rules', function() { 155 | it('to two-classes "passWORD"', function() { 156 | var password = 'passWORD'; 157 | expect(rulesEngine.executeRules(strictOptions, password)).toEqual( 158 | 26.379173679952558 159 | ); 160 | }); 161 | 162 | it('to one with repetitions', function() { 163 | var password = 'rreepppeatttt'; 164 | expect(rulesEngine.executeRules(strictOptions, password)).toEqual( 165 | 12.267756666558107 166 | ); 167 | }); 168 | }); 169 | 170 | describe('Applying relaxed rules', function() { 171 | it('to one with sequences', function() { 172 | var password = 'qwertypasswdisbad'; 173 | expect(rulesEngine.executeRules(relaxedOptions, password)).toEqual(1); 174 | }); 175 | 176 | it('to one with an email', function() { 177 | var password = 'test@example.com'; 178 | expect(rulesEngine.executeRules(relaxedOptions, password)).toEqual(6); 179 | }); 180 | 181 | it('to a "short" one', function() { 182 | var password = 'short'; 183 | expect(rulesEngine.executeRules(relaxedOptions, password)).toEqual(1); 184 | }); 185 | }); 186 | 187 | describe('Looking for sequences', function() { 188 | beforeEach(function() { 189 | options.instances.errors = []; 190 | }); 191 | 192 | it('at the beginning of the password', function() { 193 | var password = 'abcx'; 194 | expect( 195 | rulesEngine.validation.wordSequences(options, password, 123) 196 | ).toEqual(123); 197 | }); 198 | 199 | it('at the end of the password', function() { 200 | var password = 'xabc'; 201 | expect( 202 | rulesEngine.validation.wordSequences(options, password, 123) 203 | ).toEqual(123); 204 | }); 205 | 206 | it('and not finding them', function() { 207 | var password = 'mdiw93jc65oak$&!'; 208 | expect( 209 | rulesEngine.validation.wordSequences(options, password, 123) 210 | ).toEqual(0); 211 | }); 212 | }); 213 | -------------------------------------------------------------------------------- /src/i18n.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery Password Strength plugin for Twitter Bootstrap 3 | * 4 | * Copyright (c) 2008-2013 Tane Piper 5 | * Copyright (c) 2013 Alejandro Blanco 6 | * Dual licensed under the MIT and GPL licenses. 7 | */ 8 | 9 | // eslint-disable-next-line no-implicit-globals 10 | var i18n = {}; 11 | 12 | (function(i18next) { 13 | 'use strict'; 14 | 15 | i18n.fallback = { 16 | wordMinLength: 'Your password is too short', 17 | wordMaxLength: 'Your password is too long', 18 | wordInvalidChar: 'Your password contains an invalid character', 19 | wordNotEmail: 'Do not use your email as your password', 20 | wordSimilarToUsername: 'Your password cannot contain your username', 21 | wordTwoCharacterClasses: 'Use different character classes', 22 | wordRepetitions: 'Too many repetitions', 23 | wordSequences: 'Your password contains sequences', 24 | errorList: 'Errors:', 25 | veryWeak: 'Very Weak', 26 | weak: 'Weak', 27 | normal: 'Normal', 28 | medium: 'Medium', 29 | strong: 'Strong', 30 | veryStrong: 'Very Strong' 31 | }; 32 | 33 | i18n.t = function(key) { 34 | var result = ''; 35 | 36 | // Try to use i18next.com 37 | if (i18next) { 38 | result = i18next.t(key); 39 | } else { 40 | // Fallback to english 41 | result = i18n.fallback[key]; 42 | } 43 | 44 | return result === key ? '' : result; 45 | }; 46 | })(window.i18next); 47 | -------------------------------------------------------------------------------- /src/methods.js: -------------------------------------------------------------------------------- 1 | /*global ui, rulesEngine, defaultOptions, zxcvbn */ 2 | 3 | /* 4 | * jQuery Password Strength plugin for Twitter Bootstrap 5 | * 6 | * Copyright (c) 2008-2013 Tane Piper 7 | * Copyright (c) 2013 Alejandro Blanco 8 | * Dual licensed under the MIT and GPL licenses. 9 | */ 10 | 11 | // eslint-disable-next-line no-implicit-globals 12 | var methods = {}; 13 | 14 | (function($) { 15 | 'use strict'; 16 | var onKeyUp, onPaste, applyToAll; 17 | 18 | onKeyUp = function(event) { 19 | var $el = $(event.target), 20 | options = $el.data('pwstrength-bootstrap'), 21 | word = $el.val(), 22 | userInputs, 23 | verdictText, 24 | verdictLevel, 25 | score; 26 | 27 | if (options === undefined) { 28 | return; 29 | } 30 | 31 | options.instances.errors = []; 32 | if (word.length === 0) { 33 | score = undefined; 34 | } else { 35 | if (options.common.zxcvbn) { 36 | userInputs = []; 37 | $.each( 38 | options.common.userInputs.concat([ 39 | options.common.usernameField 40 | ]), 41 | function(idx, selector) { 42 | var value = $(selector).val(); 43 | if (value) { 44 | userInputs.push(value); 45 | } 46 | } 47 | ); 48 | userInputs = userInputs.concat(options.common.zxcvbnTerms); 49 | score = zxcvbn(word, userInputs).guesses; 50 | score = Math.log(score) * Math.LOG2E; 51 | } else { 52 | score = rulesEngine.executeRules(options, word); 53 | } 54 | if (typeof options.common.onScore === 'function') { 55 | score = options.common.onScore(options, word, score); 56 | } 57 | } 58 | ui.updateUI(options, $el, score); 59 | verdictText = ui.getVerdictAndCssClass(options, score); 60 | verdictLevel = verdictText[1]; 61 | verdictText = verdictText[0]; 62 | 63 | if (options.common.debug) { 64 | console.log(score + ' - ' + verdictText); 65 | } 66 | 67 | if (typeof options.common.onKeyUp === 'function') { 68 | options.common.onKeyUp(event, { 69 | score: score, 70 | verdictText: verdictText, 71 | verdictLevel: verdictLevel 72 | }); 73 | } 74 | }; 75 | 76 | onPaste = function(event) { 77 | // This handler is necessary because the paste event fires before the 78 | // content is actually in the input, so we cannot read its value right 79 | // away. Therefore, the timeouts. 80 | var $el = $(event.target), 81 | word = $el.val(), 82 | tries = 0, 83 | callback; 84 | 85 | callback = function() { 86 | var newWord = $el.val(); 87 | 88 | if (newWord !== word) { 89 | onKeyUp(event); 90 | } else if (tries < 3) { 91 | tries += 1; 92 | setTimeout(callback, 100); 93 | } 94 | }; 95 | 96 | setTimeout(callback, 100); 97 | }; 98 | 99 | methods.init = function(settings) { 100 | this.each(function(idx, el) { 101 | // Make it deep extend (first param) so it extends also the 102 | // rules and other inside objects 103 | var clonedDefaults = $.extend(true, {}, defaultOptions), 104 | localOptions = $.extend(true, clonedDefaults, settings), 105 | $el = $(el); 106 | 107 | localOptions.instances = {}; 108 | $el.data('pwstrength-bootstrap', localOptions); 109 | 110 | $.each(localOptions.common.events, function(ignore, eventName) { 111 | var handler = eventName === 'paste' ? onPaste : onKeyUp; 112 | $el.on(eventName, handler); 113 | }); 114 | 115 | ui.initUI(localOptions, $el); 116 | $el.trigger('keyup'); 117 | 118 | if (typeof localOptions.common.onLoad === 'function') { 119 | localOptions.common.onLoad(); 120 | } 121 | }); 122 | 123 | return this; 124 | }; 125 | 126 | methods.destroy = function() { 127 | this.each(function(idx, el) { 128 | var $el = $(el), 129 | options = $el.data('pwstrength-bootstrap'), 130 | elements = ui.getUIElements(options, $el); 131 | elements.$progressbar.remove(); 132 | elements.$verdict.remove(); 133 | elements.$errors.remove(); 134 | $el.removeData('pwstrength-bootstrap'); 135 | }); 136 | 137 | return this; 138 | }; 139 | 140 | methods.forceUpdate = function() { 141 | this.each(function(idx, el) { 142 | var event = { target: el }; 143 | onKeyUp(event); 144 | }); 145 | 146 | return this; 147 | }; 148 | 149 | methods.addRule = function(name, method, score, active) { 150 | this.each(function(idx, el) { 151 | var options = $(el).data('pwstrength-bootstrap'); 152 | 153 | options.rules.activated[name] = active; 154 | options.rules.scores[name] = score; 155 | options.rules.extra[name] = method; 156 | }); 157 | 158 | return this; 159 | }; 160 | 161 | applyToAll = function(rule, prop, value) { 162 | this.each(function(idx, el) { 163 | $(el).data('pwstrength-bootstrap').rules[prop][rule] = value; 164 | }); 165 | }; 166 | 167 | methods.changeScore = function(rule, score) { 168 | applyToAll.call(this, rule, 'scores', score); 169 | 170 | return this; 171 | }; 172 | 173 | methods.ruleActive = function(rule, active) { 174 | applyToAll.call(this, rule, 'activated', active); 175 | 176 | return this; 177 | }; 178 | 179 | methods.ruleIsMet = function(rule) { 180 | var rulesMetCnt = 0; 181 | 182 | if (rule === 'wordMinLength') { 183 | rule = 'wordMinLengthStaticScore'; 184 | } else if (rule === 'wordMaxLength') { 185 | rule = 'wordMaxLengthStaticScore'; 186 | } 187 | 188 | this.each(function(idx, el) { 189 | var options = $(el).data('pwstrength-bootstrap'), 190 | ruleFunction = rulesEngine.validation[rule], 191 | result; 192 | 193 | if (typeof ruleFunction !== 'function') { 194 | ruleFunction = options.rules.extra[rule]; 195 | } 196 | if (typeof ruleFunction === 'function') { 197 | result = ruleFunction(options, $(el).val(), 1); 198 | if ($.isNumeric(result)) { 199 | rulesMetCnt += result; 200 | } 201 | } 202 | }); 203 | 204 | return rulesMetCnt === this.length; 205 | }; 206 | 207 | $.fn.pwstrength = function(method) { 208 | var result; 209 | 210 | if (methods[method]) { 211 | result = methods[method].apply( 212 | this, 213 | Array.prototype.slice.call(arguments, 1) 214 | ); 215 | } else if (typeof method === 'object' || !method) { 216 | result = methods.init.apply(this, arguments); 217 | } else { 218 | $.error( 219 | 'Method ' + 220 | method + 221 | ' does not exist on jQuery.pwstrength-bootstrap' 222 | ); 223 | } 224 | 225 | return result; 226 | }; 227 | })(jQuery); 228 | -------------------------------------------------------------------------------- /src/options.js: -------------------------------------------------------------------------------- 1 | /*global i18n */ 2 | 3 | /* 4 | * jQuery Password Strength plugin for Twitter Bootstrap 5 | * 6 | * Copyright (c) 2008-2013 Tane Piper 7 | * Copyright (c) 2013 Alejandro Blanco 8 | * Dual licensed under the MIT and GPL licenses. 9 | */ 10 | 11 | // eslint-disable-next-line no-implicit-globals 12 | var defaultOptions = {}; 13 | 14 | defaultOptions.common = {}; 15 | defaultOptions.common.minChar = 6; 16 | defaultOptions.common.maxChar = 20; 17 | defaultOptions.common.usernameField = '#username'; 18 | defaultOptions.common.invalidCharsRegExp = new RegExp(/[\s,'"]/); 19 | defaultOptions.common.userInputs = [ 20 | // Selectors for input fields with user input 21 | ]; 22 | defaultOptions.common.onLoad = undefined; 23 | defaultOptions.common.onKeyUp = undefined; 24 | defaultOptions.common.onScore = undefined; 25 | defaultOptions.common.zxcvbn = false; 26 | defaultOptions.common.zxcvbnTerms = [ 27 | // List of disrecommended words 28 | ]; 29 | defaultOptions.common.events = ['keyup', 'change', 'paste']; 30 | defaultOptions.common.debug = false; 31 | 32 | defaultOptions.rules = {}; 33 | defaultOptions.rules.extra = {}; 34 | defaultOptions.rules.scores = { 35 | wordNotEmail: -100, 36 | wordMinLength: -50, 37 | wordMaxLength: -50, 38 | wordInvalidChar: -100, 39 | wordSimilarToUsername: -100, 40 | wordSequences: -20, 41 | wordTwoCharacterClasses: 2, 42 | wordRepetitions: -25, 43 | wordLowercase: 1, 44 | wordUppercase: 3, 45 | wordOneNumber: 3, 46 | wordThreeNumbers: 5, 47 | wordOneSpecialChar: 3, 48 | wordTwoSpecialChar: 5, 49 | wordUpperLowerCombo: 2, 50 | wordLetterNumberCombo: 2, 51 | wordLetterNumberCharCombo: 2, 52 | wordIsACommonPassword: -100 53 | }; 54 | defaultOptions.rules.activated = { 55 | wordNotEmail: true, 56 | wordMinLength: true, 57 | wordMaxLength: false, 58 | wordInvalidChar: false, 59 | wordSimilarToUsername: true, 60 | wordSequences: true, 61 | wordTwoCharacterClasses: true, 62 | wordRepetitions: true, 63 | wordLowercase: true, 64 | wordUppercase: true, 65 | wordOneNumber: true, 66 | wordThreeNumbers: true, 67 | wordOneSpecialChar: true, 68 | wordTwoSpecialChar: true, 69 | wordUpperLowerCombo: true, 70 | wordLetterNumberCombo: true, 71 | wordLetterNumberCharCombo: true, 72 | wordIsACommonPassword: true 73 | }; 74 | defaultOptions.rules.raisePower = 1.4; 75 | defaultOptions.rules.specialCharClass = '[!,@,#,$,%,^,&,*,?,_,~]'; 76 | // List taken from https://github.com/danielmiessler/SecLists (MIT License) 77 | defaultOptions.rules.commonPasswords = [ 78 | '123456', 79 | 'password', 80 | '12345678', 81 | 'qwerty', 82 | '123456789', 83 | '12345', 84 | '1234', 85 | '111111', 86 | '1234567', 87 | 'dragon', 88 | '123123', 89 | 'baseball', 90 | 'abc123', 91 | 'football', 92 | 'monkey', 93 | 'letmein', 94 | '696969', 95 | 'shadow', 96 | 'master', 97 | '666666', 98 | 'qwertyuiop', 99 | '123321', 100 | 'mustang', 101 | '1234567890', 102 | 'michael', 103 | '654321', 104 | 'pussy', 105 | 'superman', 106 | '1qaz2wsx', 107 | '7777777', 108 | 'fuckyou', 109 | '121212', 110 | '000000', 111 | 'qazwsx', 112 | '123qwe', 113 | 'killer', 114 | 'trustno1', 115 | 'jordan', 116 | 'jennifer', 117 | 'zxcvbnm', 118 | 'asdfgh', 119 | 'hunter', 120 | 'buster', 121 | 'soccer', 122 | 'harley', 123 | 'batman', 124 | 'andrew', 125 | 'tigger', 126 | 'sunshine', 127 | 'iloveyou', 128 | 'fuckme', 129 | '2000', 130 | 'charlie', 131 | 'robert', 132 | 'thomas', 133 | 'hockey', 134 | 'ranger', 135 | 'daniel', 136 | 'starwars', 137 | 'klaster', 138 | '112233', 139 | 'george', 140 | 'asshole', 141 | 'computer', 142 | 'michelle', 143 | 'jessica', 144 | 'pepper', 145 | '1111', 146 | 'zxcvbn', 147 | '555555', 148 | '11111111', 149 | '131313', 150 | 'freedom', 151 | '777777', 152 | 'pass', 153 | 'fuck', 154 | 'maggie', 155 | '159753', 156 | 'aaaaaa', 157 | 'ginger', 158 | 'princess', 159 | 'joshua', 160 | 'cheese', 161 | 'amanda', 162 | 'summer', 163 | 'love', 164 | 'ashley', 165 | '6969', 166 | 'nicole', 167 | 'chelsea', 168 | 'biteme', 169 | 'matthew', 170 | 'access', 171 | 'yankees', 172 | '987654321', 173 | 'dallas', 174 | 'austin', 175 | 'thunder', 176 | 'taylor', 177 | 'matrix' 178 | ]; 179 | 180 | defaultOptions.ui = {}; 181 | defaultOptions.ui.bootstrap2 = false; 182 | defaultOptions.ui.bootstrap3 = false; 183 | defaultOptions.ui.colorClasses = [ 184 | 'danger', 185 | 'danger', 186 | 'danger', 187 | 'warning', 188 | 'warning', 189 | 'success' 190 | ]; 191 | defaultOptions.ui.showProgressBar = true; 192 | defaultOptions.ui.progressBarEmptyPercentage = 1; 193 | defaultOptions.ui.progressBarMinWidth = 1; 194 | defaultOptions.ui.progressBarMinPercentage = 1; 195 | defaultOptions.ui.progressExtraCssClasses = ''; 196 | defaultOptions.ui.progressBarExtraCssClasses = ''; 197 | defaultOptions.ui.showPopover = false; 198 | defaultOptions.ui.popoverPlacement = 'bottom'; 199 | defaultOptions.ui.showStatus = false; 200 | defaultOptions.ui.spanError = function(options, key) { 201 | 'use strict'; 202 | var text = options.i18n.t(key); 203 | if (!text) { 204 | return ''; 205 | } 206 | return '' + text + ''; 207 | }; 208 | defaultOptions.ui.popoverError = function(options) { 209 | 'use strict'; 210 | var errors = options.instances.errors, 211 | errorsTitle = options.i18n.t('errorList'), 212 | message = 213 | '
    ' + 214 | errorsTitle + 215 | '
      '; 216 | 217 | jQuery.each(errors, function(idx, err) { 218 | message += '
    • ' + err + '
    • '; 219 | }); 220 | message += '
    '; 221 | return message; 222 | }; 223 | defaultOptions.ui.showVerdicts = true; 224 | defaultOptions.ui.showVerdictsInsideProgressBar = false; 225 | defaultOptions.ui.useVerdictCssClass = false; 226 | defaultOptions.ui.showErrors = false; 227 | defaultOptions.ui.showScore = false; 228 | defaultOptions.ui.container = undefined; 229 | defaultOptions.ui.viewports = { 230 | progress: undefined, 231 | verdict: undefined, 232 | errors: undefined, 233 | score: undefined 234 | }; 235 | defaultOptions.ui.scores = [0, 14, 26, 38, 50]; 236 | 237 | defaultOptions.i18n = {}; 238 | defaultOptions.i18n.t = i18n.t; 239 | -------------------------------------------------------------------------------- /src/rules.js: -------------------------------------------------------------------------------- 1 | /*global module, require */ 2 | 3 | /* 4 | * jQuery Password Strength plugin for Twitter Bootstrap 5 | * 6 | * Copyright (c) 2008-2013 Tane Piper 7 | * Copyright (c) 2013 Alejandro Blanco 8 | * Dual licensed under the MIT and GPL licenses. 9 | */ 10 | 11 | // eslint-disable-next-line no-implicit-globals 12 | var rulesEngine = {}; 13 | 14 | /* eslint-disable */ 15 | try { 16 | if (!jQuery && module && module.exports) { 17 | var jQuery = require('jquery'), 18 | jsdom = require('jsdom').jsdom; 19 | jQuery = jQuery(jsdom().defaultView); 20 | } 21 | } catch (ignore) { 22 | // Nothing to do 23 | } 24 | /* eslint-enable */ 25 | 26 | (function($) { 27 | 'use strict'; 28 | var validation = {}; 29 | 30 | rulesEngine.forbiddenSequences = [ 31 | '0123456789', 32 | 'abcdefghijklmnopqrstuvwxyz', 33 | 'qwertyuiop', 34 | 'asdfghjkl', 35 | 'zxcvbnm', 36 | '!@#$%^&*()_+' 37 | ]; 38 | 39 | validation.wordNotEmail = function(options, word, score) { 40 | if ( 41 | word.match( 42 | /^([\w!#$%&'*+\-/=?^`{|}~]+\.)*[\w!#$%&'*+\-/=?^`{|}~]+@((((([a-z0-9]{1}[a-z0-9-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(:\d{1,5})?)$/i 43 | ) 44 | ) { 45 | return score; 46 | } 47 | return 0; 48 | }; 49 | 50 | validation.wordMinLength = function(options, word, score) { 51 | var wordlen = word.length, 52 | lenScore = Math.pow(wordlen, options.rules.raisePower); 53 | if (wordlen < options.common.minChar) { 54 | lenScore = lenScore + score; 55 | } 56 | return lenScore; 57 | }; 58 | 59 | validation.wordMaxLength = function(options, word, score) { 60 | var wordlen = word.length, 61 | lenScore = Math.pow(wordlen, options.rules.raisePower); 62 | if (wordlen > options.common.maxChar) { 63 | return score; 64 | } 65 | return lenScore; 66 | }; 67 | 68 | validation.wordInvalidChar = function(options, word, score) { 69 | if (options.common.invalidCharsRegExp.test(word)) { 70 | return score; 71 | } 72 | return 0; 73 | }; 74 | 75 | validation.wordMinLengthStaticScore = function(options, word, score) { 76 | return word.length < options.common.minChar ? 0 : score; 77 | }; 78 | 79 | validation.wordMaxLengthStaticScore = function(options, word, score) { 80 | return word.length > options.common.maxChar ? 0 : score; 81 | }; 82 | 83 | validation.wordSimilarToUsername = function(options, word, score) { 84 | var username = $(options.common.usernameField).val(); 85 | if ( 86 | username && 87 | word 88 | .toLowerCase() 89 | .match( 90 | username 91 | .replace(/[-[\]/{}()*+=?:.\\^$|!,]/g, '\\$&') 92 | .toLowerCase() 93 | ) 94 | ) { 95 | return score; 96 | } 97 | return 0; 98 | }; 99 | 100 | validation.wordTwoCharacterClasses = function(options, word, score) { 101 | var specialCharRE = new RegExp( 102 | '(.' + options.rules.specialCharClass + ')' 103 | ); 104 | 105 | if ( 106 | word.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/) || 107 | (word.match(/([a-zA-Z])/) && word.match(/([0-9])/)) || 108 | (word.match(specialCharRE) && word.match(/[a-zA-Z0-9_]/)) 109 | ) { 110 | return score; 111 | } 112 | return; 113 | }; 114 | 115 | validation.wordRepetitions = function(options, word, score) { 116 | if (word.match(/(.)\1\1/)) { 117 | return score; 118 | } 119 | return 0; 120 | }; 121 | 122 | validation.wordSequences = function(options, word, score) { 123 | var found = false, 124 | j; 125 | 126 | if (word.length > 2) { 127 | $.each(rulesEngine.forbiddenSequences, function(idx, seq) { 128 | var sequences; 129 | if (found) { 130 | return; 131 | } 132 | sequences = [ 133 | seq, 134 | seq 135 | .split('') 136 | .reverse() 137 | .join('') 138 | ]; 139 | $.each(sequences, function(ignore, sequence) { 140 | for (j = 0; j < word.length - 2; j += 1) { 141 | // iterate the word trough a sliding window of size 3: 142 | if ( 143 | sequence.indexOf( 144 | word.toLowerCase().substring(j, j + 3) 145 | ) > -1 146 | ) { 147 | found = true; 148 | } 149 | } 150 | }); 151 | }); 152 | if (found) { 153 | return score; 154 | } 155 | } 156 | return 0; 157 | }; 158 | 159 | validation.wordLowercase = function(options, word, score) { 160 | return word.match(/[a-z]/) && score; 161 | }; 162 | 163 | validation.wordUppercase = function(options, word, score) { 164 | return word.match(/[A-Z]/) && score; 165 | }; 166 | 167 | validation.wordOneNumber = function(options, word, score) { 168 | return word.match(/\d+/) && score; 169 | }; 170 | 171 | validation.wordThreeNumbers = function(options, word, score) { 172 | return word.match(/(.*[0-9].*[0-9].*[0-9])/) && score; 173 | }; 174 | 175 | validation.wordOneSpecialChar = function(options, word, score) { 176 | var specialCharRE = new RegExp(options.rules.specialCharClass); 177 | return word.match(specialCharRE) && score; 178 | }; 179 | 180 | validation.wordTwoSpecialChar = function(options, word, score) { 181 | var twoSpecialCharRE = new RegExp( 182 | '(.*' + 183 | options.rules.specialCharClass + 184 | '.*' + 185 | options.rules.specialCharClass + 186 | ')' 187 | ); 188 | 189 | return word.match(twoSpecialCharRE) && score; 190 | }; 191 | 192 | validation.wordUpperLowerCombo = function(options, word, score) { 193 | return word.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/) && score; 194 | }; 195 | 196 | validation.wordLetterNumberCombo = function(options, word, score) { 197 | return word.match(/([a-zA-Z])/) && word.match(/([0-9])/) && score; 198 | }; 199 | 200 | validation.wordLetterNumberCharCombo = function(options, word, score) { 201 | var letterNumberCharComboRE = new RegExp( 202 | '([a-zA-Z0-9].*' + 203 | options.rules.specialCharClass + 204 | ')|(' + 205 | options.rules.specialCharClass + 206 | '.*[a-zA-Z0-9])' 207 | ); 208 | 209 | return word.match(letterNumberCharComboRE) && score; 210 | }; 211 | 212 | validation.wordIsACommonPassword = function(options, word, score) { 213 | if ($.inArray(word, options.rules.commonPasswords) >= 0) { 214 | return score; 215 | } 216 | return 0; 217 | }; 218 | 219 | rulesEngine.validation = validation; 220 | 221 | rulesEngine.executeRules = function(options, word) { 222 | var totalScore = 0; 223 | 224 | $.each(options.rules.activated, function(rule, active) { 225 | var score, funct, result, errorMessage; 226 | 227 | if (active) { 228 | score = options.rules.scores[rule]; 229 | funct = rulesEngine.validation[rule]; 230 | 231 | if (typeof funct !== 'function') { 232 | funct = options.rules.extra[rule]; 233 | } 234 | 235 | if (typeof funct === 'function') { 236 | result = funct(options, word, score); 237 | if (result) { 238 | totalScore += result; 239 | } 240 | if (result < 0 || (!$.isNumeric(result) && !result)) { 241 | errorMessage = options.ui.spanError(options, rule); 242 | if (errorMessage.length > 0) { 243 | options.instances.errors.push(errorMessage); 244 | } 245 | } 246 | } 247 | } 248 | }); 249 | 250 | return totalScore; 251 | }; 252 | })(jQuery); 253 | 254 | try { 255 | if (module && module.exports) { 256 | module.exports = rulesEngine; 257 | } 258 | } catch (ignore) { 259 | // Nothing to do 260 | } 261 | -------------------------------------------------------------------------------- /src/ui.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery Password Strength plugin for Twitter Bootstrap 3 | * 4 | * Copyright (c) 2008-2013 Tane Piper 5 | * Copyright (c) 2013 Alejandro Blanco 6 | * Dual licensed under the MIT and GPL licenses. 7 | */ 8 | 9 | // eslint-disable-next-line no-implicit-globals 10 | var ui = {}; 11 | 12 | (function($) { 13 | 'use strict'; 14 | 15 | var statusClasses = ['error', 'warning', 'success'], 16 | verdictKeys = [ 17 | 'veryWeak', 18 | 'weak', 19 | 'normal', 20 | 'medium', 21 | 'strong', 22 | 'veryStrong' 23 | ]; 24 | 25 | ui.getContainer = function(options, $el) { 26 | var $container; 27 | 28 | $container = $(options.ui.container); 29 | if (!($container && $container.length === 1)) { 30 | $container = $el.parent(); 31 | } 32 | return $container; 33 | }; 34 | 35 | ui.findElement = function($container, viewport, cssSelector) { 36 | if (viewport) { 37 | return $container.find(viewport).find(cssSelector); 38 | } 39 | return $container.find(cssSelector); 40 | }; 41 | 42 | ui.getUIElements = function(options, $el) { 43 | var $container, result; 44 | 45 | if (options.instances.viewports) { 46 | return options.instances.viewports; 47 | } 48 | 49 | $container = ui.getContainer(options, $el); 50 | 51 | result = {}; 52 | result.$progressbar = ui.findElement( 53 | $container, 54 | options.ui.viewports.progress, 55 | 'div.progress' 56 | ); 57 | if (options.ui.showVerdictsInsideProgressBar) { 58 | result.$verdict = result.$progressbar.find('span.password-verdict'); 59 | } 60 | 61 | if (!options.ui.showPopover) { 62 | if (!options.ui.showVerdictsInsideProgressBar) { 63 | result.$verdict = ui.findElement( 64 | $container, 65 | options.ui.viewports.verdict, 66 | 'span.password-verdict' 67 | ); 68 | } 69 | result.$errors = ui.findElement( 70 | $container, 71 | options.ui.viewports.errors, 72 | 'ul.error-list' 73 | ); 74 | } 75 | result.$score = ui.findElement( 76 | $container, 77 | options.ui.viewports.score, 78 | 'span.password-score' 79 | ); 80 | 81 | options.instances.viewports = result; 82 | return result; 83 | }; 84 | 85 | ui.initHelper = function(options, $el, html, viewport) { 86 | var $container = ui.getContainer(options, $el); 87 | if (viewport) { 88 | $container.find(viewport).append(html); 89 | } else { 90 | $(html).insertAfter($el); 91 | } 92 | }; 93 | 94 | ui.initVerdict = function(options, $el) { 95 | ui.initHelper( 96 | options, 97 | $el, 98 | '', 99 | options.ui.viewports.verdict 100 | ); 101 | }; 102 | 103 | ui.initErrorList = function(options, $el) { 104 | ui.initHelper( 105 | options, 106 | $el, 107 | '
      ', 108 | options.ui.viewports.errors 109 | ); 110 | }; 111 | 112 | ui.initScore = function(options, $el) { 113 | ui.initHelper( 114 | options, 115 | $el, 116 | '', 117 | options.ui.viewports.score 118 | ); 119 | }; 120 | 121 | ui.initUI = function(options, $el) { 122 | if (options.ui.showPopover) { 123 | ui.initPopover(options, $el); 124 | } else { 125 | if (options.ui.showErrors) { 126 | ui.initErrorList(options, $el); 127 | } 128 | if ( 129 | options.ui.showVerdicts && 130 | !options.ui.showVerdictsInsideProgressBar 131 | ) { 132 | ui.initVerdict(options, $el); 133 | } 134 | } 135 | if (options.ui.showProgressBar) { 136 | ui.initProgressBar(options, $el); 137 | } 138 | if (options.ui.showScore) { 139 | ui.initScore(options, $el); 140 | } 141 | }; 142 | 143 | ui.updateVerdict = function(options, $el, cssClass, text) { 144 | var $verdict = ui.getUIElements(options, $el).$verdict; 145 | $verdict.removeClass(options.ui.colorClasses.join(' ')); 146 | if (cssClass > -1) { 147 | $verdict.addClass(options.ui.colorClasses[cssClass]); 148 | } 149 | if (options.ui.showVerdictsInsideProgressBar) { 150 | $verdict.css('white-space', 'nowrap'); 151 | } 152 | $verdict.html(text); 153 | }; 154 | 155 | ui.updateErrors = function(options, $el, remove) { 156 | var $errors = ui.getUIElements(options, $el).$errors, 157 | html = ''; 158 | 159 | if (!remove) { 160 | $.each(options.instances.errors, function(idx, err) { 161 | html += '
    • ' + err + '
    • '; 162 | }); 163 | } 164 | $errors.html(html); 165 | }; 166 | 167 | ui.updateScore = function(options, $el, score, remove) { 168 | var $score = ui.getUIElements(options, $el).$score, 169 | html = ''; 170 | 171 | if (!remove) { 172 | html = score.toFixed(2); 173 | } 174 | $score.html(html); 175 | }; 176 | 177 | ui.updateFieldStatus = function(options, $el, cssClass, remove) { 178 | var $target = $el; 179 | 180 | if (options.ui.bootstrap2) { 181 | $target = $el.parents('.control-group').first(); 182 | } else if (options.ui.bootstrap3) { 183 | $target = $el.parents('.form-group').first(); 184 | } 185 | 186 | $.each(statusClasses, function(idx, css) { 187 | css = ui.cssClassesForBS(options, css); 188 | $target.removeClass(css); 189 | }); 190 | 191 | if (remove) { 192 | return; 193 | } 194 | 195 | cssClass = statusClasses[Math.floor(cssClass / 2)]; 196 | cssClass = ui.cssClassesForBS(options, cssClass); 197 | $target.addClass(cssClass); 198 | }; 199 | 200 | ui.cssClassesForBS = function(options, css) { 201 | if (options.ui.bootstrap3) { 202 | css = 'has-' + css; 203 | } else if (!options.ui.bootstrap2) { 204 | // BS4 205 | if (css === 'error') { 206 | css = 'danger'; 207 | } 208 | css = 'border-' + css; 209 | } 210 | return css; 211 | }; 212 | 213 | ui.getVerdictAndCssClass = function(options, score) { 214 | var level, verdict; 215 | 216 | if (score === undefined) { 217 | return ['', 0]; 218 | } 219 | 220 | if (score <= options.ui.scores[0]) { 221 | level = 0; 222 | } else if (score < options.ui.scores[1]) { 223 | level = 1; 224 | } else if (score < options.ui.scores[2]) { 225 | level = 2; 226 | } else if (score < options.ui.scores[3]) { 227 | level = 3; 228 | } else if (score < options.ui.scores[4]) { 229 | level = 4; 230 | } else { 231 | level = 5; 232 | } 233 | 234 | verdict = verdictKeys[level]; 235 | 236 | return [options.i18n.t(verdict), level]; 237 | }; 238 | 239 | ui.updateUI = function(options, $el, score) { 240 | var cssClass, verdictText, verdictCssClass; 241 | 242 | cssClass = ui.getVerdictAndCssClass(options, score); 243 | verdictText = score === 0 ? '' : cssClass[0]; 244 | cssClass = cssClass[1]; 245 | verdictCssClass = options.ui.useVerdictCssClass ? cssClass : -1; 246 | 247 | if (options.ui.showProgressBar) { 248 | ui.showProgressBar( 249 | options, 250 | $el, 251 | score, 252 | cssClass, 253 | verdictCssClass, 254 | verdictText 255 | ); 256 | } 257 | 258 | if (options.ui.showStatus) { 259 | ui.updateFieldStatus(options, $el, cssClass, score === undefined); 260 | } 261 | 262 | if (options.ui.showPopover) { 263 | ui.updatePopover(options, $el, verdictText, score === undefined); 264 | } else { 265 | if ( 266 | options.ui.showVerdicts && 267 | !options.ui.showVerdictsInsideProgressBar 268 | ) { 269 | ui.updateVerdict(options, $el, verdictCssClass, verdictText); 270 | } 271 | if (options.ui.showErrors) { 272 | ui.updateErrors(options, $el, score === undefined); 273 | } 274 | } 275 | 276 | if (options.ui.showScore) { 277 | ui.updateScore(options, $el, score, score === undefined); 278 | } 279 | }; 280 | })(jQuery); 281 | -------------------------------------------------------------------------------- /src/ui.popover.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery Password Strength plugin for Twitter Bootstrap 3 | * 4 | * Copyright (c) 2008-2013 Tane Piper 5 | * Copyright (c) 2013 Alejandro Blanco 6 | * Dual licensed under the MIT and GPL licenses. 7 | */ 8 | 9 | /* global ui, bootstrap, $ */ 10 | 11 | (function() { 12 | 'use strict'; 13 | 14 | ui.initPopover = function(options, $el) { 15 | try { 16 | $el.popover('destroy'); 17 | } catch (error) { 18 | // Bootstrap 4.2.X onwards 19 | $el.popover('dispose'); 20 | } 21 | $el.popover({ 22 | html: true, 23 | placement: options.ui.popoverPlacement, 24 | trigger: 'manual', 25 | content: ' ' 26 | }); 27 | }; 28 | 29 | ui.updatePopover = function(options, $el, verdictText, remove) { 30 | var popover = $el.data('bs.popover'), 31 | html = '', 32 | hide = true, 33 | bootstrap5 = false, 34 | itsVisible = false; 35 | 36 | if ( 37 | options.ui.showVerdicts && 38 | !options.ui.showVerdictsInsideProgressBar && 39 | verdictText.length > 0 40 | ) { 41 | html = 42 | '
      ' + 43 | verdictText + 44 | '
      '; 45 | hide = false; 46 | } 47 | if (options.ui.showErrors) { 48 | if (options.instances.errors.length > 0) { 49 | hide = false; 50 | } 51 | html += options.ui.popoverError(options); 52 | } 53 | 54 | if (hide || remove) { 55 | $el.popover('hide'); 56 | return; 57 | } 58 | 59 | if (options.ui.bootstrap2) { 60 | popover = $el.data('popover'); 61 | } else if (!popover) { 62 | // Bootstrap 5 63 | popover = bootstrap.Popover.getInstance($el[0]); 64 | bootstrap5 = true; 65 | } 66 | 67 | if (bootstrap5) { 68 | itsVisible = $(popover.tip).is(':visible'); 69 | } else { 70 | itsVisible = popover.$arrow && popover.$arrow.parents('body').length > 0; 71 | } 72 | 73 | if (itsVisible) { 74 | if (bootstrap5) { 75 | $(popover.tip).find('.popover-body').html(html); 76 | } else { 77 | $el.find('+ .popover .popover-content').html(html); 78 | } 79 | } else { 80 | // It's hidden 81 | if (options.ui.bootstrap2 || options.ui.bootstrap3) { 82 | popover.options.content = html; 83 | } else if (bootstrap5) { 84 | popover._config.content = html; 85 | } else { 86 | popover.config.content = html; 87 | } 88 | $el.popover('show'); 89 | } 90 | }; 91 | })(); 92 | -------------------------------------------------------------------------------- /src/ui.progressbar.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery Password Strength plugin for Twitter Bootstrap 3 | * 4 | * Copyright (c) 2008-2013 Tane Piper 5 | * Copyright (c) 2013 Alejandro Blanco 6 | * Dual licensed under the MIT and GPL licenses. 7 | */ 8 | 9 | /* global ui */ 10 | 11 | (function($) { 12 | 'use strict'; 13 | 14 | ui.percentage = function(options, score, maximun) { 15 | var result = Math.floor((100 * score) / maximun), 16 | min = options.ui.progressBarMinPercentage; 17 | 18 | result = result <= min ? min : result; 19 | result = result > 100 ? 100 : result; 20 | return result; 21 | }; 22 | 23 | ui.initProgressBar = function(options, $el) { 24 | var $container = ui.getContainer(options, $el), 25 | progressbar = '
      '; 40 | 41 | if (options.ui.showVerdictsInsideProgressBar) { 42 | progressbar += ''; 43 | } 44 | 45 | progressbar += '
      '; 46 | 47 | if (options.ui.viewports.progress) { 48 | $container.find(options.ui.viewports.progress).append(progressbar); 49 | } else { 50 | $(progressbar).insertAfter($el); 51 | } 52 | }; 53 | 54 | ui.showProgressBar = function( 55 | options, 56 | $el, 57 | score, 58 | cssClass, 59 | verdictCssClass, 60 | verdictText 61 | ) { 62 | var barPercentage; 63 | 64 | if (score === undefined) { 65 | barPercentage = options.ui.progressBarEmptyPercentage; 66 | } else { 67 | barPercentage = ui.percentage(options, score, options.ui.scores[4]); 68 | } 69 | ui.updateProgressBar(options, $el, cssClass, barPercentage); 70 | if (options.ui.showVerdictsInsideProgressBar) { 71 | ui.updateVerdict(options, $el, verdictCssClass, verdictText); 72 | } 73 | }; 74 | 75 | ui.updateProgressBar = function(options, $el, cssClass, percentage) { 76 | var $progressbar = ui.getUIElements(options, $el).$progressbar, 77 | $bar = $progressbar.find('.progress-bar'), 78 | cssPrefix = 'progress-'; 79 | 80 | if (options.ui.bootstrap2) { 81 | $bar = $progressbar.find('.bar'); 82 | cssPrefix = ''; 83 | } 84 | 85 | $.each(options.ui.colorClasses, function(idx, value) { 86 | if (options.ui.bootstrap2 || options.ui.bootstrap3) { 87 | $bar.removeClass(cssPrefix + 'bar-' + value); 88 | } else { 89 | $bar.removeClass('bg-' + value); 90 | } 91 | }); 92 | if (options.ui.bootstrap2 || options.ui.bootstrap3) { 93 | $bar.addClass( 94 | cssPrefix + 'bar-' + options.ui.colorClasses[cssClass] 95 | ); 96 | } else { 97 | $bar.addClass('bg-' + options.ui.colorClasses[cssClass]); 98 | } 99 | if (percentage > 0) { 100 | $bar.css('min-width', options.ui.progressBarMinWidth + 'px'); 101 | } else { 102 | $bar.css('min-width', ''); 103 | } 104 | $bar.css('width', percentage + '%'); 105 | }; 106 | })(jQuery); 107 | --------------------------------------------------------------------------------