/* Build date: 07/11/11 14:11:45 */ /* Copyright (c) 2011, Mark Watkinson All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Mark Watkinson BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. passStrengthify - a password strength notification plugin for jQuery Usage: ... $('#pass').passStrengthify( [options] ); Options: { security: int [0, 3) (default: 1) output: $element, rawEntropy: bool, // show raw entropy instead of a text description minimum: int >= 0, levels: Array, (str - Text descriptions) colours: Array (str - CSS colours), tests: Array (regex) labels: { // text for labels passwordStrength : "Password Strength:", tooShort: "Too Short" } } The levels/colours/tests arrays are as follows: levels - a list of descriptions where each index corresponds to the number of tests passed colours - a list of colours which correspond to the number of tests passed tests - a list of regex tests to perform (match == pass) If any of these are given, the size of levels and colours must be equal and it must be one greater than the size of 'tests'. The default size of tests is 8. The first colour is used as the default background colour. */ (function($){ $.fn.passStrengthify = function(options) { var $el = $(this), $out_el, // Test boundary presets, corresponds to the security option. // These steps aren't supposed to be linear. presets = [ [0, 8, 16, 32, 48, 64, 72], [0, 16, 32, 48, 64, 78, 92], [0, 32, 64, 78, 92, 108, 128] ], /**************************************************** /* /* CUSTOMIZATION FOR RENTALPAD /* /****************************************************/ // These next two should be the same size (levels & colors)... /* levels = ["Very Weak", "Very Weak", "Weak", "Weak", "Moderate", "Good", "Strong", "Very Strong"], colours = ['gray', 'red', 'red', '#C00000', 'orange', '#0099FF', 'blue', 'green'], */ levels = ["Poor", "Very Weak", "Weak", "Ok", "Moderate", "Good", "Strong", "Very Strong"], colours = ['gray', 'red', 'orange', '#2CCC2C', 'green', '#0099FF', 'blue', 'green'], /**************************************************** /* /* END CUSTOMIZATION FOR RENTALPAD /* /****************************************************/ // and this one should be one fewer. // see presets. tests = presets[0], text = $('').css('margin-left', '1em'), /**************************************************** /* /* CUSTOMIZATION FOR RENTALPAD /* /****************************************************/ rentalpad_custom = $(''); /**************************************************** /* /* END CUSTOMIZATION FOR RENTALPAD /* /****************************************************/ progress_blocks = [], i = 0, minimum=0, rawEntropy = false, // Creates the output for a given score. makeOutput = function(score, entropy, tooshort) { var max = progress_blocks.length, progress_colour, default_colour, text_colour, text_; if (tooshort) text_ = options.labels.tooShort; else if(rawEntropy) text_ = Math.round(entropy*100)/100 + ' bits'; else text_ = levels[score]; /**************************************************** /* /* CUSTOMIZATION FOR RENTALPAD /* /****************************************************/ rentalpad_custom.val(score); //score is 0-7 /**************************************************** /* /* END CUSTOMIZATION FOR RENTALPAD /* /****************************************************/ text_colour = colours[score]; progress_colour = colours[score]; default_colour = colours[0]; text.text(text_).css('color', text_colour); for (i=0; i= 26) rel_freq *= (26/alphabet_size); return rel_freq; }, calculateEntropy = function(password) { var alphabet_size = 0, passed = 0, regexes = { "[a-z]": 26, "[A-Z]": 26, // we don't regard a simple numeric append as a real // increase in complexity. "(\\d[^\\d])|(^\\d+$)" : 10, "[\\W_]": 32 // there are 32 other printable ascii chars }; // we're going to be mean here and apply some preprocessing. // Collapse repetition. password = password.replace(/(.)(\1)(\1)+/gi, '$1$2'); // Collapse sequences. password = password.replace( /(a)(b(c(d(e(f(g(h(i(j(k(l(m(n(o(p(q(r(u(v(w(x(y(z)?)?)?)?)?)?)?)?)?)?)?)?)?)?)?)?)?)?)?)?)?)?)?/gi, '$1'); password = password.replace( /(0)(1(2(3(4(5(6(7(8(9)?)?)?)?)?)?)?)?)?/g, '$1'); password = password.replace( /(1)(2(3(4(5(6(7(8(9(0)?)?)?)?)?)?)?)?)?/g, '$1'); // collapse trailing numbers password = password.replace(/([^\d])(\d)(\d)+$/, '$1$2'); if (!password.length) return 0; for (var r in regexes) { if (regexTest(new RegExp(r), password)) alphabet_size += regexes[r]; } if (!alphabet_size) return 0; // log2 x = loge x/loge 2 var total_entropy = 0; for (var i=0; i').css('display', 'inline-block') .addClass('passStrengthify'); // this needs a good rewrite return $(this).each(function() { if (!options.element) { $(this).parent().append($out_el); } else { options.element.append($out_el); } if (options.minimum) minimum = options.minimum; if (typeof options.security == 'undefined') options.security = 1; if (options.security >= 0 && options.security < presets.length) tests = presets[options.security]; if (!options.levels) options.levels = levels; if (!options.colours) options.colours = colours; if (!options.tests) options.tests = tests; if (options.levels && options.colours && options.tests) { if (options.levels.length == options.colours.length && options.colours.length == options.tests.length+1) { levels = options.levels; colours = options.colours; tests = options.tests; } } if (options.rawEntropy) rawEntropy = true; /**************************************************** /* /* CUSTOMIZATION FOR RENTALPAD /* /****************************************************/ $out_el.append(rentalpad_custom); /**************************************************** /* /* END CUSTOMIZATION FOR RENTALPAD /* /****************************************************/ $out_el.append( $('
').append( $('').css('font-size', 'smaller') .text(options.labels.passwordStrength).append(text) ) ); var max_width = 125.0; var margin = 3; var width = Math.round((max_width - margin*tests.length)/tests.length); for(i=0; i').css('height', '3px') .css('width', width + 'px') .css('margin-right', margin + 'px') .css('max-height', '3px') .css('font-size', '1px') // for IE 6 .css('float', 'left'); progress_blocks.push($e); $out_el.append($e); } // keypress fires all the time when a user holds down a key, but it fires // before this.val() is updated. So we bind to both keypress and keyup $el.keypress(change); $el.keyup(change); $el.trigger('keyup'); }); }; })(jQuery);