/**
**    Original File: password-meter.js
**    Created by: Rene Schwietzke (mail@03146f06.net)
**    Created on: 2008-12-01
**    Last modified: 2008-12-30
**    Version: 1.0.0
**
**    License Information:
**    -------------------------------------------------------------------------
**    Copyright (C) 2008 Rene Schwietzke
**
**    This program is free software; you can redistribute it and/or modify it
**    under the terms of the GNU General Public License as published by the
**    Free Software Foundation; either version 2 of the License, or (at your
**    option) any later version.
**    
**    This program is distributed in the hope that it will be useful, but
**    WITHOUT ANY WARRANTY; without even the implied warranty of
**    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
**    General Public License for more details.
**    
**    You should have received a copy of the GNU General Public License along
**    with this program; if not, write to the Free Software Foundation, Inc.,
**    59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
**    
**    Based on original work by Jeff Todnem published under GPL 2.
** 
**    Original File: pwd_meter.js (http://www.passwordmeter.com/)
**    Created by: Jeff Todnem (http://www.todnem.com/)
**
**    History:
**    -------------------------------------------------------------------------
**    v1.0.0 : initial version
**    v1.0.1 : fixed rounding problem by adjusting float2str
**
**    ToDo: 
**    -------------------------------------------------------------------------
**    * Punish first or last letter uppercase characters only
**    * Punish special characters only at the end
**    * Punish numbers only at the end
**    * Filter common patterns, such as 12.12.2008 12/20/2009 2008-12-13
**    * Seem to contain a year 19XX or 20XX
**/
PasswordMeter.prototype = ( 
{		
	// the version of the password meter
	version: "1.0.1",
	
	COMPLEXITY:
	{
	    VERYWEAK: 0,
	    WEAK: 1,
	    GOOD: 2,
	    STRONG: 3,
	    VERYSTRONG: 4
	},
	
	STATUS:
	{
	    FAILED: 0,
	    PASSED: 1,
	    EXCEEDED: 2
	},
	
    // little string helper to reverse a string
    strReverse: function(str) 
    {
        var newstring = "";
        for (var s = 0; s < str.length; s++) 
        {
            newstring = str.charAt(s) + newstring;
        }
        return newstring;
    },
    
    int2str: function(aNumber) 
    {
		if (aNumber == 0) 
		{
			return "0";
		}
		else 
		{
			return parseInt(aNumber, 10);
		}
    },

    float2str: function(aNumber) 
    {
		if (aNumber == 0) 
		{
			return "0.00";
		}
		else 
		{
			return parseFloat(aNumber.toFixed(2));
		}
    },

	// helper for the status
	// <0 failed
	// 0  passed
	// >0 exceeded
	determineStatus: function(aNumber)
	{
		if (aNumber == 0) 
		{ 
			return this.STATUS.PASSED;
		}
		else if (aNumber > 0) 
		{ 
			return this.STATUS.EXCEEDED;
		}
		else 
		{ 
			return this.STATUS.FAILED;
		}
	},
	
	// helper for the status
	// 0  passed
	// !=0 failed
	determineBinaryStatus: function(aNumber)
	{
		if (aNumber == 0) 
		{ 
			return this.STATUS.PASSED;
		}
		else 
		{ 
			return this.STATUS.FAILED;
		}
	}
});

function PasswordMeter()
{
	this.Score = 
	{
		count: 0,
		adjusted: 0,
		beforeRedundancy: 0
	};
	
	// the complexity index
	this.Complexity = 
	{
		limits: [20, 40, 60, 80, 100],
		value: this.COMPLEXITY.VERYWEAK
	};
	
	// the length of the password
	this.PasswordLength =
	{
		count  : 0,
		minimum: 5,
		formula: "TBD",
		status : this.STATUS.FAILED,
		rating : 0,
		factor : 0.5, // per character bonus
		bonus  : 10, // minimum reached? Get a bonus.
		penalty: -20 // if we stay under minimum, we get punished
	};
	
	// recommended password length
	this.RecommendedPasswordLength = 
	{
		count  : 0,
		minimum: 8,
		formula: "TBD",
		status : this.STATUS.FAILED,
		rating : 0,
		factor : 1.2,
		bonus  : 10,
		penalty: -10
	};
	
	// Basic requirements are:
	// 1) Password Length
	// 2) Uppercase letter use
	// 3) Lowercase letter use
	// 4) Numeric character use
	// 5) Symbol use
	this.BasicRequirements =
	{
		count  : 0, 
		minimum: 3, // have to be matched to get the bonus
		formula: "TBD", 
		status : this.STATUS.FAILED, 
		rating : 0, 
		factor : 1, 
		bonus  : 10, 
		penalty: -10
	};
	
	// how much redundancy is permitted, if the password is
	// long enough. we will skip the redudancy penalty if this
	// number is not exceeded (meaning redundancy < this number)
	this.Redundancy =
	{
		value    : 1, // 1 means, not double characters, default to start
		permitted: 2, // 2 means, in average every character can occur twice
		formula  : "TBD",
		status   : this.STATUS.FAILED,
		rating   : 0
	};
	
	// number of uppercase letters, such as A-Z	
	this.UppercaseLetters =
	{
		count  : 0,
		minimum: 1,
		formula: "TBD",
		status : this.STATUS.FAILED,
		rating : 0,
		factor : 0,
		bonus  : 10,
		penalty: -10
	};
	
	// number of lowercase letters, such as a-z	
	this.LowercaseLetters =
	{
		count  : 0,
		minimum: 1,
		formula: "TBD",
		status : this.STATUS.FAILED,
		rating : 0,
		factor : 0,
		bonus  : 10,
		penalty: -10
	};
	
	// number of numeric characters
	this.Numerics =
	{
		count  : 0,
		minimum: 1,
		formula: "TBD",
		status : this.STATUS.FAILED,
		rating : 0,
		factor : 0,
		bonus  : 10,
		penalty: -10
	};
	
	// number of symbol characters
	this.Symbols =
	{
		count  : 0,
		minimum: 1,
		formula: "TBD",
		status : this.STATUS.FAILED,
		rating : 0,
		factor : 0,
		bonus  : 10,
		penalty: -10
	};
	
	// number of dedicated symbols in the middle
	this.MiddleSymbols =
	{
		count  : 0,
		minimum: 1,
		formula: "TBD",
		status : this.STATUS.FAILED,
		rating : 0,
		factor : 0,
		bonus  : 10,
		penalty: -10
	};
	
	// number of dedicated numbers in the middle
	this.MiddleNumerics = 
	{
		count  : 0,
		minimum: 1,
		formula: "TBD",
		status : this.STATUS.FAILED,
		rating : 0,
		factor : 0,
		bonus  : 10,
		penalty: -10
	};
	
	// how many sequential characters should be checked
	// such as "abc" or "MNO" to be not part of the password
	this.SequentialLetters = 
	{
		data: "abcdefghijklmnopqrstuvwxyz",
		length: 3,
		
		count: 0,
		
		formula: "TBD",
		status: this.STATUS.FAILED,
		rating: 0,
		factor: -1,
		bonus: 0,
		penalty: -10
	};
	
	// how many sequential characters should be checked
	// such as "123" to be not part of the password
	this.SequentialNumerics =
	{
		data: "0123456789",
		length: 3,
		
		count: 0,
		
		formula: "TBD",
		status: this.STATUS.FAILED,
		rating: 0,
		factor: -1,
		bonus: 0,
		penalty: -10
	};

	// keyboard patterns to check, typical sequences from your
	// keyboard
	this.KeyboardPatterns =
	{
		// german and english keyboard text
		data: [	"qwertzuiop", "asdfghjkl", "yxcvbnm", "!\"§$%&/()=", // de
				"1234567890", // de numbers
				"qaywsxedcrfvtgbzhnujmik,ol.pö-üä+#", // de up-down
			   
			    "qwertyuiop", "asdfghjkl", "zyxcvbnm", "!@#$%^&*()_", // en
				"1234567890", // en numbers
		        "qazwsxedcrfvtgbyhnujmik,ol.p;/[']\\" // en up-down
		],
		length: 4, // how long is the pattern to check and blame for?
		
		count: 0, // how much of these pattern can be found
		
		formula: "TBD",
		status: this.STATUS.FAILED,
		rating: 0,
		factor: -1, // each occurence is punished with that factor
		bonus: 0,
		penalty: -10
	};

	// check for repeated sequences, like in catcat
	this.RepeatedSequences = 
	{
		length: 3,

		count  : 0,
		formula: "TBD",
		status : this.STATUS.FAILED,
		rating : 0,
		factor : 0,
		bonus  : 0,
		penalty: -10
	};

	// check for repeated sequences, like in catcat
	this.MirroredSequences = 
	{
		length: 3,

		count  : 0,
		formula: "TBD",
		status : this.STATUS.FAILED,
		rating : 0,
		factor : 0,
		bonus  : 0,
		penalty: -10
	};

	// this check our password and sets all object properties accordingly
    this.checkPassword = function(password)
    {
        // do we have data to check?
        if (!password)
        {
            // no, leave
            password = "";
        }
        
        // check the password and set all values
        var nTmpAlphaUC = -1;
        var nTmpAlphaLC = -1;
        var nTmpNumber  = -1;
        var nTmpSymbol  = -1;

		// how long is the password?
        this.PasswordLength.count = password.length;
        this.RecommendedPasswordLength.count = password.length;
        
        // split it, all characters are permitted so far
        var passwordArray = password.split("");

        // Loop through password to check for Symbol, Numeric, Lowercase 
		// and Uppercase pattern matches
        for (var a = 0; a < passwordArray.length; a++) 
        {
			// check uppercase letters
            if (passwordArray[a].match(/[A-Z]/g)) 
            {
                if (nTmpAlphaUC != -1) 
				{ 
					// check last uppercase position, when the previous one, store
					// the information
					if ((nTmpAlphaUC + 1) == a) 
					{ 
						this.nConsecutiveUppercaseLetters++; 
						this.nConsecutiveLetters++; 
					} 
				}
				// store the last uppercase position
                nTmpAlphaUC = a;

                this.UppercaseLetters.count++;
            }
			// check lowercase
            else if (passwordArray[a].match(/[a-z]/g)) 
            { 
                if (nTmpAlphaLC != -1) 
				{ 
					if ((nTmpAlphaLC + 1) == a) 
					{ 
						this.nConsecutiveLowercaseLetters++; 
						this.nConsecutiveLetters++; 
					} 
				}
                nTmpAlphaLC = a;
                this.LowercaseLetters.count++;
            }
			// check numeric
            else if (passwordArray[a].match(/[0-9]/g)) 
            { 
                if (a > 0 && a < (passwordArray.length - 1)) 
				{ 
					this.MiddleNumerics.count++; 
				}
                if (nTmpNumber != -1) 
				{ 
					if ((nTmpNumber + 1) == a) 
					{ 
						this.nConsecutiveNumbers++; 
						this.nConsecutiveLetters++; 
					} 
				}
                nTmpNumber = a;
                this.Numerics.count++;
            }
			// check all extra characters
            else if (passwordArray[a].match(new RegExp(/[^a-zA-Z0-9]/g))) 
            { 
                if (a > 0 && a < (passwordArray.length - 1)) 
				{ 
					this.MiddleSymbols.count++; 
				}
                if (nTmpSymbol != -1) 
				{ 
					if ((nTmpSymbol + 1) == a) 
					{ 
						this.nConsecutiveSymbols++; 
						this.nConsecutiveLetters++; 
					} 
				}
                nTmpSymbol = a;
                this.Symbols.count++;
            }
        }

		// check the variance of symbols or better the redundancy
		// makes only sense for at least two characters
		if (passwordArray.length > 1) 
		{
			var uniqueCharacters = new Array();
		    for (var a = 0; a < passwordArray.length; a++) 
			{
				var found = false;
				
				for (var b = a + 1; b < passwordArray.length; b++) 
				{
					if (passwordArray[a] == passwordArray[b]) 
					{
						found = true;
					}
				}
				if (found == false)
				{
					uniqueCharacters.push(passwordArray[a]);
				}
			}

			// calculate a redundancy number
			this.Redundancy.value = passwordArray.length / uniqueCharacters.length;
		}
		
        // Check for sequential alpha string patterns (forward and reverse) but only, if the string
		// has already a length to check for, does not make sense to check the password "ab" for the
		// sequential data "abc"
		var lowercasedPassword = password.toLowerCase();

		if (this.PasswordLength.count >= this.SequentialLetters.length) 
		{
			for (var s = 0; s < this.SequentialLetters.data.length - this.SequentialLetters.length; s++) 
			{
				var sFwd = this.SequentialLetters.data.substring(s, s + this.SequentialLetters.length);
				var sRev = this.strReverse(sFwd);
				
				if (lowercasedPassword.indexOf(sFwd) != -1) 
				{
					this.SequentialLetters.count++;
				}
				if (lowercasedPassword.indexOf(sRev) != -1) 
				{
					this.SequentialLetters.count++;
				}
			}
		}

		// Check for sequential numeric string patterns (forward and reverse)
		if (this.PasswordLength.count >= this.SequentialNumerics.length)
		{	
			for (var s = 0; s < this.SequentialNumerics.data.length - this.SequentialNumerics.length; s++) 
			{
				var sFwd = this.SequentialNumerics.data.substring(s, s + this.SequentialNumerics.length);
				var sRev = this.strReverse(sFwd);
				
				if (lowercasedPassword.indexOf(sFwd) != -1) 
				{
					this.SequentialNumerics.count++;
				}
				if (lowercasedPassword.indexOf(sRev) != -1) 
				{
					this.SequentialNumerics.count++;
				}
			}
		}

		// Check common keyboard patterns
		var patternsMatched = new Array();
		if (this.PasswordLength.count >= this.KeyboardPatterns.length)
		{	
			for (p in this.KeyboardPatterns.data) 
			{
				var pattern = this.KeyboardPatterns.data[p];
				
				for (var s = 0; s < pattern.length - this.KeyboardPatterns.length; s++) 
				{
					var sFwd = pattern.substring(s, s + this.KeyboardPatterns.length);
					var sRev = this.strReverse(sFwd);
					
					if (lowercasedPassword.indexOf(sFwd) != -1) 
					{
						if (patternsMatched[sFwd] == undefined) 
						{
							this.KeyboardPatterns.count++;
							patternsMatched[sFwd] = sFwd;
						}
					}
					if (lowercasedPassword.indexOf(sRev) != -1) 
					{
						if (patternsMatched[sRev] == undefined) 
						{
							this.KeyboardPatterns.count++;
							patternsMatched[sRev] = sRev;
						}
					}
				}
			}
		}

        // Try to find repeated sequences of characters.
		if (this.PasswordLength.count > this.RepeatedSequences.length) 
		{
			for (var s = 0; s < lowercasedPassword.length - this.RepeatedSequences.length; s++) 
			{
				var sFwd = lowercasedPassword.substring(s, s + this.RepeatedSequences.length);
				
				var result = lowercasedPassword.indexOf(sFwd, s + this.RepeatedSequences.length);
				if (result != -1) 
				{
					this.RepeatedSequences.count++;
				}
			}
		}

        // Try to find mirrored sequences of characters.
		if (this.PasswordLength.count > this.MirroredSequences.length) 
		{
			for (var s = 0; s < lowercasedPassword.length - this.MirroredSequences.length; s++) 
			{
				var sFwd = lowercasedPassword.substring(s, s + this.MirroredSequences.length);
				var sRev = this.strReverse(sFwd);
				
				var result = lowercasedPassword.indexOf(sRev, s + this.MirroredSequences.length);
				if (result != -1) 
				{
					this.MirroredSequences.count++;
				}
			}
		}

		//*************************************************************************
		//* Initial score based on length
		//*************************************************************************
        this.Score.count = this.PasswordLength.count * this.PasswordLength.factor;
		
		//*************************************************************************
		//* PasswordLength
		//* credit additional length or punish "under" length
		//*************************************************************************
		if (this.PasswordLength.count < this.PasswordLength.minimum)
		{
			this.PasswordLength.rating = this.PasswordLength.penalty;
		}
		else if (this.PasswordLength.count >= this.PasswordLength.minimum)
		{
			// credit additional characters over minimum
			this.PasswordLength.rating  = this.PasswordLength.bonus + 
				(this.PasswordLength.count - this.PasswordLength.minimum) * this.PasswordLength.factor;
		}
		this.Score.count += this.PasswordLength.rating;

		//*************************************************************************
		//* RecommendedPasswordLength
		//* Credit reaching the recommended password length or put a 
		//* penalty on it
		//*************************************************************************
		if (this.PasswordLength.count >= this.RecommendedPasswordLength.minimum)
		{
			this.RecommendedPasswordLength.rating = 
								this.RecommendedPasswordLength.bonus +
								(this.PasswordLength.count - this.RecommendedPasswordLength.minimum) * this.RecommendedPasswordLength.factor;
		}
		else
		{
			this.RecommendedPasswordLength.rating = this.RecommendedPasswordLength.penalty;
		}
		this.Score.count += this.RecommendedPasswordLength.rating;

		//*************************************************************************
		//* LowercaseLetters
		//* Honor or punish the Lowercase letter use
		//*************************************************************************
		if (this.LowercaseLetters.count > 0) 
		{	
			this.LowercaseLetters.rating = this.LowercaseLetters.bonus + (this.LowercaseLetters.count * this.LowercaseLetters.factor);
		}
		else
		{
			this.LowercaseLetters.rating = this.LowercaseLetters.penalty;
		}
		this.Score.count += this.LowercaseLetters.rating;

		//*************************************************************************
		//* UppercaseLetters
		//* Honor or punish the uppercase letter use
		//*************************************************************************
		if (this.UppercaseLetters.count > 0) 
		{	
			this.UppercaseLetters.rating = this.UppercaseLetters.bonus + (this.UppercaseLetters.count * this.UppercaseLetters.factor);
		}
		else
		{
			this.UppercaseLetters.rating = this.UppercaseLetters.penalty;
		}
		this.Score.count += this.UppercaseLetters.rating;
		
		//*************************************************************************
		//* Numerics
		//* Honor or punish the Numerics letter use
		//*************************************************************************
		if (this.Numerics.count > 0) 
		{	
			this.Numerics.rating = this.Numerics.bonus + (this.Numerics.count * this.Numerics.factor);
		}
		else
		{
			this.Numerics.rating = this.Numerics.penalty;
		}
		this.Score.count += this.Numerics.rating;

		//*************************************************************************
		//* Symbols
		//* Honor or punish the Symbols letter use
		//*************************************************************************
		if (this.Symbols.count > 0) 
		{	
			this.Symbols.rating = this.Symbols.bonus + (this.Symbols.count * this.Symbols.factor);
		}
		else
		{
			this.Symbols.rating = this.Symbols.penalty;
		}
		this.Score.count += this.Symbols.rating;

		//*************************************************************************
		//* MiddleSymbols
		//* Honor or punish the MiddleSymbols letter use
		//*************************************************************************
		if (this.MiddleSymbols.count > 0) 
		{	
			this.MiddleSymbols.rating = this.MiddleSymbols.bonus + (this.MiddleSymbols.count * this.MiddleSymbols.factor);
		}
		else
		{
			this.MiddleSymbols.rating = this.MiddleSymbols.penalty;
		}
		this.Score.count += this.MiddleSymbols.rating;

		//*************************************************************************
		//* MiddleNumerics
		//* Honor or punish the MiddleNumerics letter use
		//*************************************************************************
		if (this.MiddleNumerics.count > 0) 
		{	
			this.MiddleNumerics.rating = this.MiddleNumerics.bonus + (this.MiddleNumerics.count * this.MiddleNumerics.factor);
		}
		else
		{
			this.MiddleNumerics.rating = this.MiddleNumerics.penalty;
		}
		this.Score.count += this.MiddleNumerics.rating;

		//*************************************************************************
		//* SequentialLetters
		//* Honor or punish the SequentialLetters letter use
		//*************************************************************************
		if (this.SequentialLetters.count == 0) 
		{	
			this.SequentialLetters.rating = this.SequentialLetters.bonus;
		}
		else
		{
			this.SequentialLetters.rating = this.SequentialLetters.penalty + (this.SequentialLetters.count * this.SequentialLetters.factor);
		}
		this.Score.count += this.SequentialLetters.rating;

		//*************************************************************************
		//* SequentialNumerics
		//* Honor or punish the SequentialNumerics letter use
		//*************************************************************************
		if (this.SequentialNumerics.count == 0) 
		{	
			this.SequentialNumerics.rating = this.SequentialNumerics.bonus;
		}
		else
		{
			this.SequentialNumerics.rating = this.SequentialNumerics.penalty + (this.SequentialNumerics.count * this.SequentialNumerics.factor);
		}
		this.Score.count += this.SequentialNumerics.rating;

		//*************************************************************************
		//* KeyboardPatterns
		//* Honor or punish the KeyboardPatterns letter use
		//*************************************************************************
		if (this.KeyboardPatterns.count == 0) 
		{	
			this.KeyboardPatterns.rating = this.KeyboardPatterns.bonus;
		}
		else
		{
			this.KeyboardPatterns.rating = this.KeyboardPatterns.penalty + (this.KeyboardPatterns.count * this.KeyboardPatterns.factor);
		}
		this.Score.count += this.KeyboardPatterns.rating;

		//*************************************************************************
		//* RepeatedSequences
		//* Honor or punish the RepeatedSequences letter use
		//*************************************************************************
		if (this.RepeatedSequences.count == 0) 
		{	
			this.RepeatedSequences.rating = this.RepeatedSequences.bonus;
		}
		else
		{
			this.RepeatedSequences.rating = this.RepeatedSequences.penalty + (this.RepeatedSequences.count * this.RepeatedSequences.factor);
		}
		this.Score.count += this.RepeatedSequences.rating;

		//*************************************************************************
		//* MirroredSequences
		//* Punish the MirroredSequences
		//*************************************************************************
		if (this.MirroredSequences.count == 0) 
		{	
			this.MirroredSequences.rating = this.MirroredSequences.bonus;
		}
		else
		{
			this.MirroredSequences.rating = this.MirroredSequences.penalty + (this.MirroredSequences.count * this.MirroredSequences.factor);
		}
		this.Score.count += this.MirroredSequences.rating;

		//*************************************************************************
		//* Count our BasicRequirements and set the status
		//*************************************************************************
		this.BasicRequirements.count = 0;

		// password length
		this.PasswordLength.status = this.determineStatus(this.PasswordLength.count - this.PasswordLength.minimum);
		if (this.PasswordLength.status != this.STATUS.FAILED)
		{
			// requirement met
			this.BasicRequirements.count++; 
		}

		// uppercase letters
		this.UppercaseLetters.status = this.determineStatus(this.UppercaseLetters.count - this.UppercaseLetters.minimum);
		if (this.UppercaseLetters.status != this.STATUS.FAILED)
		{
			// requirement met
			this.BasicRequirements.count++; 
		}

		// lowercase letters
		this.LowercaseLetters.status = this.determineStatus(this.LowercaseLetters.count - this.LowercaseLetters.minimum);
		if (this.LowercaseLetters.status != this.STATUS.FAILED)
		{
			// requirement met
			this.BasicRequirements.count++; 
		}

		// numerics
		this.Numerics.status = this.determineStatus(this.Numerics.count - this.Numerics.minimum);
		if (this.Numerics.status != this.STATUS.FAILED)
		{
			// requirement met
			this.BasicRequirements.count++; 
		}

		// symbols
		this.Symbols.status = this.determineStatus(this.Symbols.count - this.Symbols.minimum);
		if (this.Symbols.status != this.STATUS.FAILED)
		{
			// requirement met
			this.BasicRequirements.count++; 
		}

		// judge the requirement status
		this.BasicRequirements.status = this.determineStatus(this.BasicRequirements.count - this.BasicRequirements.minimum);
		if (this.BasicRequirements.status != this.STATUS.FAILED)
		{
			this.BasicRequirements.rating = 
							this.BasicRequirements.bonus + 
							(this.BasicRequirements.factor * this.BasicRequirements.count);
		}
		else
		{
			this.BasicRequirements.rating = this.BasicRequirements.penalty;				
		}
		this.Score.count += this.BasicRequirements.rating;

		// no basic requirements
		this.RecommendedPasswordLength.status = this.determineStatus(this.PasswordLength.count - this.RecommendedPasswordLength.minimum);
		this.MiddleNumerics.status = this.determineStatus(this.MiddleNumerics.count - this.MiddleNumerics.minimum);
		this.MiddleSymbols.status = this.determineStatus(this.MiddleSymbols.count - this.MiddleSymbols.minimum);
		this.SequentialLetters.status = this.determineBinaryStatus(this.SequentialLetters.count);
		this.SequentialNumerics.status = this.determineBinaryStatus(this.SequentialNumerics.count);
		this.KeyboardPatterns.status = this.determineBinaryStatus(this.KeyboardPatterns.count);
		this.RepeatedSequences.status = this.determineBinaryStatus(this.RepeatedSequences.count);
		this.MirroredSequences.status = this.determineBinaryStatus(this.MirroredSequences.count);

		// save value before redundancy
		this.Score.beforeRedundancy = this.Score.count;

		// apply the redundancy after adjusting it
		// is the password length requirement fulfilled?
		
		if (this.PasswordLength.status != this.STATUS.FAILED) 
		{
			if (this.Redundancy.value <= this.Redundancy.permitted) 
			{
				// skip redundancy penalty			
			}
			else 
			{
				// penalty will be applied but with a small bonus
				if (this.Score.count > 0) 
				{
					var value = (this.Redundancy.value - this.Redundancy.permitted) + 1;
					this.Score.count = this.Score.count * (1 / value);
				}
			}
		}
		else 
		{
			// fullpenalty, because password is not long enough, only for a positive score
			if (this.Score.count > 0) 
			{
				this.Score.count = this.Score.count * (1 / this.Redundancy.value);
			}
		}

		// level it out
		if (this.Score.count > 100) 
		{ 
			this.Score.adjusted = 100; 
		} 
		else if (this.Score.count < 0) 
		{ 
			this.Score.adjusted = 0; 
		}
		else
		{
			this.Score.adjusted = this.Score.count;
		}
		
		// judge it
		for (var i = 0; i < this.Complexity.limits.length; i++)
		{
			if (this.Score.adjusted <= this.Complexity.limits[i])
			{
				this.Complexity.value = i;
				break;
			}
		}

		return this.Complexity.value;		
    };
}

