/**
 * @author Walter Wimberly
 * @version 1.0 RC
 * @classDescription Strong Password JS tool to measure how strong
 * a password is.  It allows you to configure various settings.
 * This tool calculates how strong a password is while it is being
 * entered by the user.
 */

/**
 *
 * @constructor
 * @param {Array}	settings	Associative array of all of the settings
 * that you can configure.
 * @param {Function}	fn		a function that can be called when the
 * password is being checked if it is being called at real time - this
 * allows for other sytles to be used if desired.
 */
function StrongPassword(settings, fn) {
	/**
	 * Stores the current strength of the password in a numeric fashion.
	 * @type int
	 */
	this.strength = 0;
	/**
	 * Show the strength of the password (as a number) be shown while is it calculated.
	 * @type boolean
	 */
	this.showStrength = false;
	/**
	 * Should numbers be allowed within the password.
	 * @type boolean
	 */
	this.allowNumbers = settings && settings.allowNumbers ? settings.allowNumbers : true;
	/**
	 * Should special characters (!@#$%^&*()-+_<>?/;[]{}) be allowed
	 * within the password.
	 * @type boolean
	 */
	this.allowSpecialChars = settings && settings.allowSpecialChars ? settings.allowSpecialChars : true;
	
	/**
	 * The minimum length of the password
	 * @type int
	 */
	 this.minAllowLength = settings && settings.minAllowLength ? settings.minAllowLength : 6;

	/**
	 * The minimum "strength" score that is allowed for processing.
	 * @type int
	 */
	this.minAllowScore = settings && settings.minAllowScore ? settings.minAllowScore : 5;
	/**
	 * The minimum "strength" score that is allowed for a "Weak" setting.
	 * Very similar to the minAllowScore
	 * @type int
	 */
	this.minWeakScore = settings && settings.minWeakScore ? settings.minWeakScore : 7;
	/**
	 * The minimum "strength" score that is allowed for an "Intermediate"
	 * level strength password.
	 * @type int
	 */
	this.minIntermediateScore = settings && settings.minIntermediateScore ? settings.minIntermediateScore : 10;
	/**
	 * The minimum "strength" score that is allowed for a "Strong" level
	 * strength password.
	 * @type int
	 */
	this.minStrongScore = settings && settings.minStrongScore ? settings.minStrongScore : 20;
	/**
	 * What text is displayed if the password is too weak to be allowed.
	 * This can be any valid HTML string.
	 * @type string
	 */
	this.scoreTextNotAcceptable = settings && settings.scoreTextNotAcceptable ? settings.scoreTextNotAcceptable : 'Password Not Acceptable';
	/**
	 * What text is displayed if the password is too short to be allowed.
	 * This can be any valid HTML string.
	 * @type string
	 */
	this.scoreTextShort = settings && settings.scoreTextShort ? settings.scoreTextShort : 'Too short';
	/**
	 * What text is displayed if the password is of weak strength.
	 * This can be any valid HTML string.
	 * @type string
	 */
	this.scoreTextWeak = settings && settings.scoreTextWeak ? settings.scoreTextWeak : '<em>Weak Password</em>';
	/**
	 * What text is displayed if the password is of intermediate strength.
	 * This can be any valid HTML string.
	 * @type string
	 */
	this.scoreTextIntermediate = settings && settings.scoreTextIntermediate ? settings.scoreTextIntermediate : '<em>Just</em> Strong Enough Password';
	/**
	 * What text is displayed if the password is of strong strength.
	 * This can be any valid HTML string.
	 * @type string
	 */
	this.scoreTextStrong = settings && settings.scoreTextStrong ? settings.scoreTextStrong : '<strong>Strong</strong> Passsword';
	/**
	 * What is the URL for the file to look up against standard passwords.
	 * Default value is blank.  If the field is blank, then it will skip
	 * this step.
	 * @type string
	 */
	this.dictionaryLookupFile = settings && settings.dictionary ? settings.dictionary : '';
	/**
	 * The id value of the password field to check.  If it is not passed,
	 * a default value of password will be used.
	 * @type string
	 */
	this.src = settings && settings.source ? settings.source : 'password';
	/**
	 * This private variable is used to store the created DOM element which
	 * shows the value of the strength of the password.
	 * @private
	 * @type string
	 */
	this.target = this.src + '_target';
	/**
	 * If true (default) then it will check the password as it is being typed.
	 * @type boolean
	 */
	this.realTimeCheck = settings && settings.realTimeCheck ? settings.realTimeCheck : true;
	/**
	 * If true then the password will be check against login. If too similar,
	 * it is considered weaker.
	 * @type boolean
	 */
    this.checkLogin = true;


	// avoid confusion over this within an event handler
	var wdSPSelf = this;

	// initialize the strength counter
	$('#' + this.src).after('<span id="' + this.target + '" class="wdStrengthHint"></span>');
	if( this.realTimeCheck ) {
		$('#' + this.src).keyup( function() {
			// reset strength
			wdSPSelf.strength = 0;
			var password = $('#' + wdSPSelf.src).val();
            wdSPSelf.strength += password.length;
           
           


			// define the regular expression necessary
			var rmatch = '!@#$%^&*()-+_<>?/;[]{}';

			for(i = 0, max = password.length - 1; i < max; i++) {
				// take one away if the two characters next to each other are equal
				if(password.charAt(i) == password.charAt(i + 1)) {
					wdSPSelf.strength-=2;
				}

				// give points for using capital letters
				if(password.charAt(i) == password.charAt(i).toUpperCase()) {
					wdSPSelf.strength++;
				}

				// give points for using numbers
				if(wdSPSelf.allowNumbers) {
					if(password.charAt(i) * 1 == password.charAt(i)) {
						wdSPSelf.strength++;

						// check for numbers in a sequence
						tmpNum = password[i] * 1;
						if(tmpNum + 1 == password.charAt(i + 1) ||
							tmpNum - 1 == password.charAt(i + 1)) {
								wdSPSelf.strength-=3;
							}
					}
				}

				// give points for using special characters
				if(wdSPSelf.allowSpecialChars) {
					for(j = 0; j < rmatch.length; j++) {
						if(rmatch.charAt(j) == password.charAt(i)  ) {
							wdSPSelf.strength+=3;
						}
					}
				}
			}

			// final password strength checks

			// prevent 'cheating' with only upper case letters
			if(password == password.toUpperCase()) {
				wdSPSelf.strength -= password.length;
			}
			
			//prevent passwords from being to similar to login
			if(wdSPSelf.checkLogin) {
                var login = $('#login').val();
    			var rePwd = new RegExp('^.?' + login + '.?$');
    			if(password.match(rePwd)) {
                    wdSPSelf.strength = 0;
                }
            }

			// demonstrates mixed case?
			if(password != password.toLowerCase()) {
				wdSPSelf.strength += 5;
			}

			// final error? checks
			// link to server side code and check to see if it is a dictionary
			// word or a partial dictionary word - fancy checks will even
			// convert numbers into letters to see if its a simple/conversion
			// check, but still a word...
			if(self.dictionaryLookupFile !== '' && self.dictionaryLookupFile !== undefined) {
				$.getJSON(self.dictionaryLookupFile, {value: password},
					function(json) {
						wdSPSelf.strength += json.returnValue;
					});
			}
			
			//Password length check
			if(password.length < wdSPSelf.minAllowLength) {
                wdSPSelf.strength = (wdSPSelf.strength >= wdSPSelf.minAllowScore) ? (wdSPSelf.minAllowScore - 1) : wdSPSelf.strength;
                //console.log('strength ' + wdSPSelf.strength);
            }

			// remove existing classes
			$('#' + wdSPSelf.target).removeClass($('#' + wdSPSelf.target).attr("class"));
			$('#' + wdSPSelf.target).addClass('wdStrengthHint');


                        //laffers: mark the text box erroneous if pw too weak
                        /*if(wdSPSelf.strength < wdSPSelf.minAllowScore) {
                                displayError($('#password1').get(0), 'pwstrength', wdSPSelf.scoreTextNotAcceptable);
                        } else {
                                removeErrorNode($('#password1').get(0), 'pwstrength');
                        }*/
                        
			// set the strength values...
			cssClass = 'wdTooWeakPassword';
			targetText = wdSPSelf.scoreTextNotAcceptable;

			if(wdSPSelf.strength > wdSPSelf.minStrongScore) {
				cssClass = 'wdStrongPassword';
				targetText = wdSPSelf.scoreTextStrong;
			} else if(wdSPSelf.strength >= wdSPSelf.minIntermediateScore) {
				cssClass = 'wdIntermediatePassword';
				targetText = wdSPSelf.scoreTextIntermediate;
			} else if(wdSPSelf.strength >= wdSPSelf.minWeakScore) {
				cssClass = 'wdWeakPassword';
				targetText = wdSPSelf.scoreTextWeak;
			} else if(password.length <= wdSPSelf.minAllowLength) {
                cssClass = 'wdTooShortPassword';
                targetText = wdSPSelf.scoreTextShort;
            }

			if(wdSPSelf.showStrength) {
				$('#' + wdSPSelf.target).html(wdSPSelf.strength + ' : ' + targetText);
			} else {
				$('#' + wdSPSelf.target).html(targetText);
			}

			$('#' + wdSPSelf.target).addClass(cssClass);

			try {
				fn();
			} catch( e ) {
				// do nothing...user generated error, and we don't need to worry about their mistakes.
			}
		});
	}
}

function showStrength()
{
	var strn = sp.getPasswordStrength();	// get what the current strength of the
											// password is - notice that we need to
											// know the object name of the password.
	var targetStrength = 30;				// what is the target strength of the password
	var per = strn / targetStrength;		// percentage to target strength
	if(per > 1 ) {
		per = 1;
	}
	var newLen = parseInt(120 * per);
	$('#strengthIndicator').removeClass($('#strengthIndicator').attr("class"));

	if(strn >= sp.minStrongScore) {
		$("#strengthIndicator").addClass('wdStrongPassword');
	} else if(strn >= sp.minIntermediateScore) {
		$("#strengthIndicator").addClass('wdIntermediatePassword');
	} else if(strn >= sp.minWeakScore) {
		$("#strengthIndicator").addClass('wdWeakPassword');
	} else {
		$("#strengthIndicator").addClass('wdTooWeakPassword');
	}

	if(newLen < 1) {
		newLen = 1;
	}

	// set the width of the image so one can see how wide the
	// strength indicator is.
	$("#strengthIndicator").attr("width", newLen);
}

StrongPassword.prototype = {
	getPasswordStrength: function() {
		return this.strength;
	}
}

sp = null;
window.addEventListener('load', function()
{
    sp = new StrongPassword({
            allowSpecialChars: true,
            minAllowScore: 7,
            minAllowLength: 6,
    		scoreTextNotAcceptable: 'Too simple',
    		scoreTextShort: 'Too short',
    		scoreTextWeak: 'Weak',
    		scoreTextIntermediate: 'Medium',
    		scoreTextStrong: 'Strong',
    		source: 'password1'
		    }, showStrength
        )
}, false);

