├── LICENSE ├── README.md ├── jquery.simplePassMeter.js ├── package.json ├── simplePassMeter.css └── simplePassMeterSprite.png /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Jordan Kasper 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 all 13 | 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 THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | jQuery SimplePassMeter 2 | ---- 3 | 4 | SimplePassMeter is a 5 | jQuery plug-in 6 | that makes checking the requirements for - and the strength of - 7 | passwords much easier. Everything is ready to go out of the box, or 8 | you can configure many of the pieces to make this plug-in exactly 9 | what you need. 10 | 11 | **Requires jQuery 1.2+** 12 | 13 | **Features** 14 | * Simple to implement 15 | * Displays password requirement violations as well as strength 16 | * Completely customizable 17 | * Supports custom password requirements 18 | * 7.9 Kb minified 19 | 20 | 21 | ### Options and Features 22 | 23 | **_All options_** 24 | 25 | ```text 26 | 'showOnFocus': BOOLEAN If true, only shows the password strength meter UI when the field 27 | is focused 28 | 'showOnValue': BOOLEAN If true, only shows the password strength meter UI when the field 29 | has some value (in other words, once the user has typed something), 30 | this is regardless of any focus 31 | 'location': STRING Location of the meter UI, one of 't'op, 'b'ottom, 'l'eft, or 32 | 'r'ight. NOTE that 't'op is not very reliably placed, and could 33 | overflow into your field. 34 | 'offset': NUMBER Pixels that the meter UI is offset from the password field. 35 | 'container': STRING jQuery selector for the containing element you want the UI to be in. 36 | NOTE: this disables absolute positioning and thus makes the 37 | 'location' and 'offset' fields obsolete. 38 | 39 | 'requirements': OBJECT All requirements that the password must fulfill, an object with 40 | sub-objects. Each requirement should have a unique key, and be 41 | itself an object with various members (see below). You can specify 42 | only some of the members in order to use the other default members; 43 | for example, specify the "minLength" requirement, but only the 44 | "value" member thus using the default "callback" function. 45 | 46 | 'requirementName': OBJECT A requirment specification, the name of the requirement is the key 47 | for the "requirements" option object. 48 | 49 | 'value': VARIOUS, The "value" for any requirement must evaluate to true (no zero, no 50 | false, no null), but can be requirement-specific as in the 51 | "minLength" case where the number of characters is used. In others 52 | where the value is not specific use a simple boolean true. 53 | 54 | 'regex': STRING, If the user's input matches the regex, the requirement is 55 | considered passed. 56 | 57 | *** NOTE: Use "regex" OR "callback", not both. 58 | 59 | 'callback': FUNCTION, If a requirement specifies a "callback" then this function will be 60 | called with two arguments - the input from the user and value for 61 | this requirement - to test whether the current input fulfills the 62 | requirement. The function should return the boolean true if it does, 63 | false otherwise. 64 | 65 | 'message': STRING The message to display to the user if they do not meet the 66 | requirement. It can contain the special sequence "%V" (without 67 | quotes) which will be replaced by the value parameter for this 68 | requirement. 69 | 70 | 'ratings': ARRAY The categories for the ratings, an array of objects. When an input 71 | hits the "minScore" for a given rating, its values will be used. 72 | NOTE THAT THESE MUST BE IN ORDER BY THE "minScore" ATTRIBUTE. 73 | OBJECT: 74 | 'minScore': NUMBER, The minimum score to hit to trigger this rating. 75 | 76 | 'className': STRING, The CSS class to place on the "simplePassMeter" div when this rating 77 | is used. 78 | 79 | 'text': STRING The text to place in the meter UI when this rating is triggered. 80 | ``` 81 | 82 | **_Built-in Requirements_** 83 | 84 | 85 | These requirements are already built-in, just specify them in the 86 | 'requirements' block of your options object to turn them on. 87 | See the Examples page for details on common usage. 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 |
NameDescriptionSample Code
minLengthMinimum length'minLength': { 'value': 6 }
noMatchFieldThe password cannot match the value of the given field'noMatchField': { 'value': '#username' }
matchFieldThe password MUST match the value of the given field'matchField': { 'value': '#verify' }
lettersPassword must contain at least one letter'letters': { 'value': true }
numbersPassword must contain at least one number'numbers': { 'value': true }
lowerPassword must contain at least one lower-case letter'lower': { 'value': true }
upperPassword must contain at least one upper-case letter'upper': { 'value': true }
specialPassword must contain at least one special character'special': { 'value': true }
140 | 141 | **_Built-in Ratings_** 142 | 143 | These ratings are already built-in, they will be used unless you 144 | override them. See the Examples page for 145 | details on specifying your own ratings. 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 |
Score ThresholdClass NameMeter Text
0meterFailYou need a stronger password
25meterWarnYour password is a bit weak
50meterGoodYour password is good
75meterExcelGreat password!
178 | 179 | **_Style Options_** 180 | 181 | The simplePassMeter UI is completely CSS driven using standard 182 | XHTML tags, classes, and IDs. Below is a sample styling, but 183 | this is by far not the most you can do. 184 | 185 | ```html 186 |
187 |

188 | 189 | Your password is good 190 |

191 |
192 |
193 |
194 |
195 | ``` 196 | 197 | ```css 198 | .simplePassMeter { 199 | border: 1px solid #aaa; 200 | background-color: #f3f3f3; 201 | color: #666; 202 | font-size: 0.8em; 203 | padding: 1px 5px 0 5px; 204 | margin: 0; 205 | width: 19em; 206 | } 207 | 208 | .meterFail { border: 1px solid #daa; background-color: #fdd; } 209 | .meterWarn { border: 1px solid #fd6; background-color: #feb; } 210 | .meterGood { border: 1px solid #ada; background-color: #dfd; } 211 | .meterExcel { border: 1px solid #aad; background-color: #ddf; } 212 | 213 | .simplePassMeterBar { background-color: #ddd; } 214 | .meterFail .simplePassMeterProgress { background-color: #f66; } 215 | .meterWarn .simplePassMeterProgress { background-color: #fd6; } 216 | .meterGood .simplePassMeterProgress { background-color: #ada; } 217 | .meterExcel .simplePassMeterProgress { background-color: #88f; } 218 | ``` 219 | 220 | ### Basic Usage 221 | 222 | ```html 223 | 224 | ``` 225 | 226 | ```js 227 | $('#myPassword').simplePassMeter(); 228 | ``` 229 | 230 | #### Adding in a few custom requirements... 231 | 232 | ```js 233 | $('#myPassword').simplePassMeter({ 234 | 'requirements': { 235 | 'minLength': {'value': 10}, // at least 10 characters 236 | 'lower': {'value': true}, // at least 1 lower-case letter 237 | 'upper': {'value': true}, // at least 1 upper-case letter 238 | 'special': {'value': true} // at least 1 special character 239 | } 240 | }); 241 | ``` 242 | 243 | You can find [more examples](http://jordankasper.com/jquery/meter/examples) on my personal site. 244 | 245 | ### Tested Browsers 246 | 247 | * Mozilla Firefox 2/3 248 | * Microsoft Internet Explorer 7/8 249 | * Google Chrome 250 | * Opera 251 | -------------------------------------------------------------------------------- /jquery.simplePassMeter.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2010 Jordan Kasper 2 | * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) 3 | * Copyright notice and license must remain intact for legal use 4 | * Requires: jQuery 1.2+ 5 | * 6 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 7 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 8 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 9 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 10 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 11 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 12 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | * 14 | * Fore more usage documentation and examples, visit: 15 | * http://jkdesign.org/faq/ 16 | * 17 | * Basic usage: 18 | * 19 | * 20 | * 21 | * $('#myPassword').simplePassMeter(); // Most simple form (all default options) 22 | * 23 | * // ----- OR ----- // 24 | * 25 | * ALL OPTIONS: 26 | * $('#myPassword').simplePassMeter({ 27 | * 'showOnFocus': ..., // BOOLEAN If true, only shows the password strength meter UI when the field is focused 28 | * 'showOnValue': ..., // BOOLEAN If true, only shows the password strength meter UI when the field has some value (in other words, once the user has typed something), this is regardless of any focus 29 | * 'location': '...', // STRING Location of the meter UI, one of 't'op, 'b'ottom, 'l'eft, or 'r'ight. NOTE" 't'op is not very reliably placed, and could overflow into your field. 30 | * 'offset': ..., // NUMBER Pixels that the meter UI is offset from the password field. 31 | * 'container': ..., // STRING jQuery selector for the containing element you want the UI to be in. NOTE: this disables absolute positioning and thus makes the 'location' and 'offset' fields obsolete. 32 | * 'defaultText': ..., // STRING Text to show in password strength meter when no password is typed (i.e. user hasn't started typing yet) 33 | * 34 | * 'requirements': { // OBJECT All requirements that the password must fulfill. Each requirement should have a unique key, and be itself an object with various members (see below). You can specify only some of the members in order to use the other default members; for example, specify the "minLength" requirement, but only the "value" member thus using the default "callback" function. 35 | * 'requirementName': { // STRING The name of the requirement used as the key for the "requirements" option object. 36 | * 'value': ..., // VARIOUS The value for any requirement must evaluate to true (no zero, no false, no null), but can be requirement-specific as in the "minLength" case where the number of characters is used. In others where the value is not specific, a simple boolean _true_ can be used 37 | * 'regex': '...', // STRING If the user's input matches the regex, the requirement is considered passed. 38 | * // *** NOTE: Use "regex" OR "callback", not both. 39 | * 'callback': ..., // FUNCTON If a requirement specifies a "callback" then this function will be called with two arguments - the input from the user and value for this requirement - to test whether the current input fulfills the requirement. The function should return the boolean true if it does, false otherwise. 40 | * 'message': '...' // STRING The message to display to the user if they do not meet the requirement. It can contain the special sequence %V which will be replaced by the value parameter for this requirement. 41 | * }, 42 | * ... 43 | * }, 44 | * 45 | * 'ratings': [ // ARRAY The categories for the ratings. When an input hits the "minScore" for a given rating, its values will be used. NOTE THAT THESE MUST BE IN ORDER BY THE "minScore" ATTRIBUTE. 46 | * { 47 | * 'minScore': ..., // NUMBER The minimum score to hit to trigger this rating. 48 | * 'className': '...', // STRING The CSS class to place on the "simplePassMeter" div when this rating is used. 49 | * 'text': '...' // STRING The text to place in the meter UI when this rating is triggered. 50 | * }, 51 | * ... 52 | * ] 53 | * }); 54 | * 55 | * $('#myPassword').bind("score.simplePassMeter", function(jQEvent, score) { 56 | * // do something with "score" 57 | * }); 58 | * 59 | * 60 | * REVISIONS: 61 | * 0.1 Initial release 62 | * 0.2 Added ability to capture the score of the password entered using binding (to the "score.simplePassMeter" event on the password node) 63 | * 0.3 Bug fix on bad requirement 64 | * Added option to only show UI on field focus 65 | * Added option to only show UI on field value (regardless of focus) 66 | * Fixed matchField requirement to check for activity on verification field before stating the passwords don't match 67 | * Reduce file size 68 | * 0.4 Fixed issues with strength checks not being done 69 | * Changed values for some strength checks to be more appropriate 70 | * Fixed bug in sequentialAndConsecutive check due to invalid string concatenation 71 | * Fix to matchField active check 72 | * Added option to set initial text on strength meter 73 | * 0.5 Fixed bug with matchField requirement 74 | * Added jQuery chaining (ugh, should have been in v0.1, sorry) 75 | * 0.6 Fixed issue with noMatch option when the matching field has no value (thanks to Sathish) 76 | * Work around to selecting the meter node by ID which messes up in certain cases (thanks to Jason M) 77 | */ 78 | ;(function($) { 79 | 80 | $.fn.simplePassMeter = function(o) { 81 | var n = this; 82 | if (n.length < 1) { return n; } 83 | 84 | o = (o)?o:{}; 85 | o = audit($.extend({}, $.fn.simplePassMeter.defaults, o)); 86 | 87 | n.each(function() { 88 | if (this.tagName.toLowerCase() == 'input' && 89 | this.type == 'password') { 90 | setup(this, o); 91 | } 92 | }); 93 | 94 | return n; 95 | }; 96 | 97 | // ---------------- Private Helpers --------------- // 98 | 99 | function audit(o) { 100 | var d = $.fn.simplePassMeter.defaults; 101 | o.showOnFocus = !!o.showOnFocus; 102 | o.showOnValue = !!o.showOnValue; 103 | 104 | o.location = ($.inArray(o.location, ['t','r','b','l']) < 0)?d.location:o.location; 105 | o.offset = (Number(o.offset))?Number(o.offset):d.offset; 106 | 107 | var c = o.container; 108 | c = (c)?$(c):null; 109 | o.container = (c && c.length)?c:null; 110 | 111 | // requirements 112 | var rq = o.requirements; 113 | if (!rq) { 114 | rq = d.requirements; 115 | } else { 116 | for (var k in rq) { 117 | if (!d.requirements[k]) { 118 | // not in our defaults, does it have everything we need? 119 | if (typeof rq[k].value == 'undefined' || 120 | typeof rq[k].message != 'string' || 121 | (typeof rq[k].regex != 'string' && 122 | !$.isFunction(rq[k].callback))) { 123 | rq[k] = null; 124 | continue; 125 | } else { 126 | continue; // we can use it, but we can't audit it any more 127 | } 128 | } 129 | 130 | // Audit specific requirements 131 | if (typeof rq[k].value == 'undefined') { 132 | rq[k].value = d.requirements[k].value; 133 | } 134 | if (typeof rq[k].message != 'string') { 135 | rq[k].message = d.requirements[k].message; 136 | } 137 | if (typeof rq[k].regex != 'string' && 138 | d.requirements[k].regex) { 139 | rq[k].regex = d.requirements[k].regex; 140 | } 141 | if (!$.isFunction(rq[k].callback) && 142 | d.requirements[k].callback) { 143 | rq[k].callback = d.requirements[k].callback; 144 | } 145 | 146 | // key-specific checks 147 | if (k == 'minLength') { 148 | if (!Number(rq[k].value) || rq[k].value < 1) { 149 | rq[k].value = d.requirements[k].value; 150 | } 151 | } 152 | } 153 | } 154 | // Special setup for matchField 155 | if (rq['matchField']) { 156 | $(rq['matchField'].value).bind('keyup.simplePassMeterMatch', function() { 157 | $(this) 158 | .attr('active', 'true') 159 | .unbind('keyup.simplePassMeterMatch'); 160 | }); 161 | } 162 | 163 | // ratings 164 | if (!o.ratings || !o.ratings.length) { 165 | o.ratings = d.ratings; 166 | } else { 167 | var ps = 0; 168 | // they need to be increasing in score 169 | for (var i=0, l=o.ratings.length; i") 193 | .append("

"+ 194 | "
") 195 | .appendTo('body') 196 | .css('padding-bottom', '8px'); 197 | n.attr('aria-controls', mId); 198 | 199 | if (o.container) { 200 | o.container.append(b); 201 | b.css('position', 'relative'); 202 | } else { 203 | b.css('position', 'absolute'); 204 | reposition(n, b, o); 205 | } 206 | 207 | var m = b.find('.simplePassMeterBar') 208 | .css({ 209 | 'position': 'absolute', 210 | 'bottom': '0.15em', 211 | 'left': '5px', 212 | 'height': '5px', 213 | 'width': '95%' 214 | }); 215 | var mp = m.find('.simplePassMeterProgress') 216 | .css({ 217 | 'height': '5px', 218 | 'width': '0%' 219 | }); 220 | 221 | n.bind('keyup.simplePassMeter', function() { 222 | n.attr('active', 'true'); 223 | testPass(n, b, o); 224 | }); 225 | 226 | if (o.showOnFocus) { 227 | b.hide(); 228 | n 229 | .bind('focus.simplePassMeter', function() { 230 | b.show(); 231 | }) 232 | .bind('blur.simplePassMeter', function() { 233 | b.hide(); 234 | }); 235 | } 236 | 237 | if (o.showOnValue) { 238 | n.bind('keyup.simplePassMeter', function() { 239 | if (this.value.length < 1) { 240 | b.hide(); 241 | } else { 242 | b.show(); 243 | } 244 | }); 245 | n.trigger('keyup.simplePassMeter'); 246 | } 247 | 248 | // Look through reqs to see if there are other fields to watch 249 | $.each(o.requirements, function(key, req) { 250 | if (/.+Field$/.test(key)) { 251 | var f = $(req.value); 252 | if (f.length == 1) { 253 | f.bind('keyup.simplePassMeter', function() { 254 | testPass(n, b, o); 255 | }); 256 | } 257 | } 258 | }); 259 | 260 | if (!o.container) { 261 | $(window).resize(function() { 262 | reposition(n, b, o); 263 | }); 264 | } 265 | 266 | reset(b, o); 267 | } 268 | 269 | function reposition(n, box, o) { 270 | var t, b, r, l, ielr; 271 | t = b = l = r = 'auto'; 272 | ielr = (document.all)?2:0; 273 | 274 | var pos = n.offset(); 275 | var pl = pos.left; 276 | var pt = pos.top; 277 | 278 | if (o.location == 't') { 279 | l = pl + 'px'; 280 | t = (pt - box.height() - 10 - o.offset)+'px'; 281 | 282 | } else if (o.location == 'b') { 283 | l = pl + 'px'; 284 | t = (pt + n.height() + 7 + o.offset)+'px'; 285 | 286 | } else if (o.location == 'l') { 287 | r = ($('body').width() - pl + o.offset)+'px'; 288 | t = pt + 'px'; 289 | 290 | } else { // 'r'ight (and the default) 291 | l = (pl + n.width() + 4 + ielr + o.offset)+'px'; 292 | t = pt + 'px'; 293 | } 294 | 295 | box.css({ 296 | 'top': t, 297 | 'right': r, 298 | 'bottom': b, 299 | 'left': l 300 | }); 301 | } 302 | 303 | function testPass(n, b, o) { 304 | var p = n.val(); 305 | if (p.length < 1) { 306 | reset(b, o); 307 | n.trigger('score.simplePassMeter', [0]); 308 | return; 309 | } 310 | 311 | // go through reqs 312 | var m = ''; 313 | var re, rm; 314 | var rq = o.requirements; 315 | for (var k in rq) { 316 | if (rq[k] && rq[k].value) { 317 | if (rq[k].regex && rq[k].regex.length > 0) { 318 | re = new RegExp(rq[k].regex); 319 | if (!re.test(p)) { 320 | if (m.length > 0) { m += '
'; } 321 | // use %V in message for value of requirement 322 | m += rq[k].message.replace('%V', rq[k].value); 323 | } 324 | } else if (rq[k].callback && $.isFunction(rq[k].callback)) { 325 | if (!rq[k].callback(p, rq[k].value)) { 326 | if (m.length > 0) { m += '
'; } 327 | // use %V in message for value of requirement 328 | m += rq[k].message.replace('%V', rq[k].value); 329 | } 330 | } 331 | } 332 | } 333 | 334 | // go through strength checks if passed reqs 335 | var s = 0; 336 | for (var t in strength) { 337 | s += strength[t](p); 338 | } 339 | 340 | // round score and cap it at 100 341 | s = Math.min(Math.round(s), 100); 342 | console.debug('score: '+s); 343 | 344 | setMeterUI(b, s, o, (m.length > 0)?m:null); 345 | n.trigger('score.simplePassMeter', [s]); 346 | } 347 | 348 | var letters = "abcdefghijklmnopqrstuvwxyz"; 349 | var strength = { 350 | 'testNumChars': function(p) { 351 | return (p.length * 4); 352 | }, 353 | 354 | 'testUpper': function(p) { 355 | var m = p.match(/[A-Z]/g); 356 | if (m) { 357 | return ((m.length - 1) * 3); 358 | } 359 | return 0; 360 | }, 361 | 362 | 'testLower': function(p) { 363 | var m = p.match(/[a-z]/g); 364 | if (m) { 365 | return ((m.length - 1) * 3); 366 | } 367 | return 0; 368 | }, 369 | 370 | 'testLettersOnly': function(p) { 371 | if (/^[a-zA-Z]+$/.test(p)) { 372 | return p.length * -1; 373 | } 374 | return 0; 375 | }, 376 | 377 | 'testNumbers': function(p) { 378 | var m = p.match(/[0-9]/g); 379 | if (m) { 380 | return ((m.length - 1) * 3); 381 | } 382 | return 0; 383 | }, 384 | 385 | 'testNumbersOnly': function(p) { 386 | if (/^[0-9]+$/.test(p)) { 387 | return p.length * -1; 388 | } 389 | return 0; 390 | }, 391 | 392 | 'testSpecial': function(p) { 393 | var m = p.match(/[^a-zA-Z0-9]/g); 394 | if (m) { 395 | return (m.length * 6.5); 396 | } 397 | return 0; 398 | }, 399 | 400 | 'testSequentialAndConsecutive': function(p) { 401 | var m = []; 402 | var ch = p.split(''); 403 | var hl = '', hn = '', hsl = '', hsn = ''; 404 | var cn, ln, lli, cli; 405 | for (var i=0, l=ch.length; i 0) { m.push(hl); } // store letter match 411 | if (hsl.length > 2) { m.push(hsl); } // store seq letter match 412 | hl = hsl = ''; // reset letter strings 413 | hn += ''+cn; 414 | 415 | if (hsn.length == 0) { 416 | hsn += ''+cn; 417 | } else { 418 | ln = Number(hsn.substr(hsn.length-1)); 419 | if (cn == (ln+1) || cn == (ln-1)) { 420 | // we have a sequence 421 | hsn += ''+cn; 422 | } 423 | } 424 | 425 | // We have a letter 426 | } else if (letters.indexOf(ch[i]) > -1) { 427 | if (hn.length > 0) { m.push(hn); } // store number match 428 | if (hsn.length > 2) { m.push(hsn); } // store seq number match 429 | hn = hsn = ''; // reset number strings 430 | 431 | hl += ch[i]; 432 | 433 | if (hsl.length == 0) { 434 | hsl += ch[i]; 435 | } else { 436 | lli = letters.indexOf(hsn.substr(hsn.length-1)); 437 | cli = letters.indexOf(ch[i]); 438 | if (cli == (lli+1)) { 439 | // we have a sequence 440 | hsl += ch[i]; 441 | } 442 | } 443 | } 444 | } 445 | if (hn.length > 0) { m.push(hn); } // store last number match 446 | if (hl.length > 0) { m.push(hl); } // store last letter match 447 | 448 | var c = 0; 449 | for (var i=0, l=m.length; i= o.ratings[i].minScore) { 494 | r = i; 495 | } 496 | } 497 | b.removeClass(c); 498 | 499 | // Only use score class if message not set (which means we failed reqs) 500 | if (!m) { 501 | b.addClass(o.ratings[r].className); 502 | } else { 503 | // Didn't pass reqs, always lowest score class 504 | b.addClass(o.ratings[0].className); 505 | } 506 | 507 | b.find('.simplePassMeterText') 508 | .html(((m)?m:o.ratings[r].text)); 509 | } 510 | 511 | 512 | // ----------- Static properties ----------- // 513 | 514 | $.fn.simplePassMeter.uid = 0; 515 | $.fn.simplePassMeter.defaults = { 516 | 'showOnFocus': false, // BOOLEAN If true, only shows the password strength meter UI when the field is focused 517 | 'showOnValue': false, // BOOLEAN If true, only shows the password strength meter UI when the field has some value (in other words, once the user has typed something), this is regardless of any focus 518 | 'location': 'r', // STRING Location of the meter UI, one of 't'op, 'b'ottom, 'l'eft, or 'r'ight. NOTE that 't'op is not very reliably placed, and could overflow into your field. 519 | 'offset': 3, // NUMBER Pixels that the meter UI is offset from the password field. 520 | 'container': null, // STRING jQuery selector for the containing element you want the UI to be in. NOTE: this disables absolute positioning and thus makes the 'location' and 'offset' fields obsolete. 521 | 'defaultText': 'Password Strength', // STRING Text to show in password strength meter when no password is typed (i.e. user hasn't started typing yet) 522 | 523 | 'requirements': { // OBJECT All requirements that the password must fulfill. 524 | // Each requirement should have a unique key, and be itself an object with various members (see below). You can specify only some of the members in order to use the other default members; for example, specify the "minLength" requirement, but only the "value" member thus using the default "callback" function. 525 | 'minLength': { 526 | 'value': 8, // VARIOUS The value for any requirement must evaluate to true (hence no zeros, no false, no null), but can be requirement-specific as in the "minLength" case. 527 | 'callback': function(p, v) { // FUNCTON If a requirement specifies a "callback" then this function will be called with two arguments - the input from the user and value for this requirement option - to test whether the current input fulfills the requirement. The function should return the boolean true if it does, false otherwise. 528 | p = ''+p; 529 | if (p.length >= v) { 530 | return true; 531 | } 532 | return false; 533 | }, 534 | 'message': 'Passwords need to be %V characters or more' // STRING The message to display to the user if they do not meet the requirement. It can contain the special sequence %V which will be replaced by the value parameter for this requirement. 535 | }, 536 | 'noMatchField': { 537 | 'value': null, // STRING The jQuery search string for the node to restrict on 538 | 'callback': function(p, v) { 539 | v = $(v).val(); 540 | if (v && v.length && p.indexOf(v) > -1) { 541 | return false; 542 | } 543 | return true; 544 | }, 545 | 'message': 'Your password cannot contain your username' 546 | }, 547 | 'matchField': { 548 | 'value': null, // STRING The jQuery search string for the node to restrict on 549 | 'callback': function(p, v) { 550 | v = $(v); 551 | var m = v.val(); 552 | var d = (v.attr('active') && (m && m.length > 0))?1:0; 553 | if (d && m != p) { 554 | return false; 555 | } 556 | return true; 557 | }, 558 | 'message': 'The two passwords you entered don\'t match' 559 | }, 560 | 561 | // Note the remainder of the requirements use a "regex" option versus a "callback" option. If the user's input matches the regex, the requirement is considered passed. 562 | 'letters': {'value': true, 'regex': '[a-zA-Z]+', 'message': 'You must have at least one letter'}, 563 | 'numbers': {'value': true, 'regex': '[0-9]+', 'message': 'You must have at least one number'}, 564 | 565 | // These requirements are turned off by default 566 | 'lower': {'value': false, 'regex': '[a-z]+', 'message': 'You must have at least one lower case letter'}, 567 | 'upper': {'value': false, 'regex': '[A-Z]+', 'message': 'You must have at least one upper case letter'}, 568 | 'special': {'value': false, 'regex': '[^a-zA-Z0-9]+', 'message': 'You must have at least one special character'} 569 | }, 570 | 571 | 'ratings': [ // ARRAY The categories for the ratings. When an input hits the "minScore" for a given rating, its values will be used. NOTE THAT THESE MUST BE IN ORDER BY THE "minScore" ATTRIBUTE. 572 | {'minScore': 0, // NUMBER The minimum score to hit to trigger this rating. 573 | 'className': 'meterFail', // STRING The CSS class to place on the "simplePassMeter" div when this rating is used. 574 | 'text': 'You need a stronger password' // STRING The text to place in the meter UI when this rating is triggered. 575 | }, 576 | {'minScore': 25, 577 | 'className': 'meterWarn', 578 | 'text': 'Your password is a bit weak' 579 | }, 580 | {'minScore': 50, 581 | 'className': 'meterGood', 582 | 'text': 'Your password is good' 583 | }, 584 | {'minScore': 75, 585 | 'className': 'meterExcel', 586 | 'text': 'Great password!' 587 | } 588 | ] 589 | }; 590 | 591 | 592 | })(jQuery); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-simple-pass-meter", 3 | "version": "0.6.0", 4 | "description": "Simple password strength checker and UI", 5 | "main": "jquery.simplePassMeter.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/jakerella/jquerySimplePassMeter.git" 9 | }, 10 | "keywords": [ 11 | "jquery-plugin", 12 | "form", 13 | "password", 14 | "authentication", 15 | "validation" 16 | ], 17 | "author": "Jordan Kasper", 18 | "license": "MIT", 19 | "bugs": { 20 | "url": "https://github.com/jakerella/jquerySimplePassMeter/issues" 21 | }, 22 | "homepage": "https://github.com/jakerella/jquerySimplePassMeter" 23 | } 24 | -------------------------------------------------------------------------------- /simplePassMeter.css: -------------------------------------------------------------------------------- 1 | 2 | .simplePassMeter { 3 | border: 1px solid #aaa; 4 | background-color: #f3f3f3; 5 | color: #666; 6 | font-size: 0.8em; 7 | padding: 1px 5px 0 5px; 8 | margin: 0; 9 | width: 19em; 10 | } 11 | 12 | .meterFail { border: 1px solid #daa; background-color: #fdd; } 13 | .meterWarn { border: 1px solid #fd6; background-color: #feb; } 14 | .meterGood { border: 1px solid #ada; background-color: #dfd; } 15 | .meterExcel { border: 1px solid #aad; background-color: #ddf; } 16 | 17 | .simplePassMeterBar { background-color: #ddd; } 18 | .meterFail .simplePassMeterProgress { background-color: #f66; } 19 | .meterWarn .simplePassMeterProgress { background-color: #fd6; } 20 | .meterGood .simplePassMeterProgress { background-color: #ada; } 21 | .meterExcel .simplePassMeterProgress { background-color: #88f; } 22 | 23 | .simplePassMeter p { margin: 0; } 24 | .simplePassMeterIcon { height: 16px; width: 16px; float: left; } 25 | .meterFail .simplePassMeterIcon, 26 | .meterWarn .simplePassMeterIcon, 27 | .meterGood .simplePassMeterIcon, 28 | .meterExcel .simplePassMeterIcon { 29 | background-image: url('simplePassMeterSprite.png'); 30 | background-repeat: no-repeat; 31 | } 32 | .meterExcel .simplePassMeterIcon { background-position: 0 0; } 33 | .meterFail .simplePassMeterIcon { background-position: 0 -17px; } 34 | .meterGood .simplePassMeterIcon { background-position: 0 -34px; } 35 | .meterWarn .simplePassMeterIcon { background-position: 0 -51px; } 36 | 37 | .simplePassMeterText { margin-left: 2px; } 38 | -------------------------------------------------------------------------------- /simplePassMeterSprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakerella/jquerySimplePassMeter/01628f4976d61a9456f12250bce9ee9d58c495cc/simplePassMeterSprite.png --------------------------------------------------------------------------------