/** 
 * Checks the strength of a password 
 * based upon it's length and combination of characters 
 */ 
function strengthChecker(outputId) 
{ 
        /** 
         * Id of the node we want to show output in 
         * @var string ID 
         * @private 
         */ 
        var elementId = outputId; 
        /** 
         * An internal timeout.  Gets used in loading 
         * ... possibly pointless. 
         * @var timeout 
         * @private 
         */ 
        var tm = 0; 
        /** 
         * Passwords are given a strength score 
         * @var float score 
         * @private 
         */ 
        var score = false; 
        /** 
         * The actual DOM node for the guage we append 
         * to elementId 
         * @var node gauge 
         * @private 
         */ 
        var gaugeNode; 
        /** 
         * The weight given for the various strength enhancers 
         * @var array (float) points 
         * @private 
         */ 
        var criteriaPoints = new Array(); 
        criteriaPoints['CaseChange'] = 3; 
        criteriaPoints['Length'] = 1; 
        criteriaPoints['SpecialChars'] = 6; 
        criteriaPoints['AlphaNum'] = 3; 
        criteriaPoints['NoNumbers'] = -0.5; 
        criteriaPoints['NoSpecialChars'] = -0.4; 
        criteriaPoints['NoLetters'] = -1; 
        criteriaPoints['NoCaseChange'] = -0.5; 
        criteriaPoints['Duplicates'] = -1; 
        
        /** 
         * Does the initial loading of the password gauge 
         * upon instantiation if the page is idle 
         * @return void 
         * @private 
         */ 
        var load = function() 
        { 
                //The DOM tree may not have finished building yet 
                if (document.getElementById(elementId)) 
                { 
                        if (score === false) updateOutput(); 
                        if (tm) window.clearTimeout(tm); 
                } 
                else //If the element doesn't exist, look again in 200ms 
                { 
                        tm = window.setTimeout(function() { load(); }, 200); 
                } 
        } 
        /** 
         * Update what is displayed for the gauge at elementId 
         * @return void 
         * @private 
         */ 
        var updateOutput = function() 
        {
			 img = document.getElementById(elementId);
             if (score == 0) img.src = STATIC_IMAGES+'/spacer.gif';
			 else if (score < 10) img.src = STATIC_IMAGES+'/pass_gradient_red.gif';
			 else if (score >= 10 && score <= 25) img.src = STATIC_IMAGES+'/pass_gradient_orange.gif';
			 else if (score > 25) img.src = STATIC_IMAGES+'/pass_gradient_green.gif';
			 upd_ct_g(score, elementId);
        } 
        /** 
         * Reads a new password and then gives it a score 
         * The password gauge is then updated 
         * @param string password value 
         * @return float score 
         */ 
        this.check = function(v) 
        { 
                score = 0; 
                //Score based upon length 
                score += v.length * criteriaPoints['Length']; 
                
                var collected = new Array(); 
                var lower = 0; 
                var upper = 0; 
                var numbers = 0; 
                var specialChars = 0; 
                var duplicates = 0; 
                var lettersOnly = ''; 
                var numbersOnly = ''; 
                var charsOnly = ''; 

                for (var i = 0; i < v.length; i++) 
                { 
                        var letter = v.substr(i, 1); 
                        if (collected.hasValue(letter)) duplicates++; 

						collected.push(letter); 
						if (letter.match(/[a-z]/)) 
                        { 
                                lettersOnly += letter; 
                                lower++; 
                        } 
                        else if (letter.match(/[A-Z]/)) 
                        { 
                                lettersOnly += letter; 
                                upper++; 
                        } 
                        else if (letter.match(/\d/)) 
                        { 
                                numbersOnly += letter; 
                                numbers++; 
                        } 
                        else if (letter.match(/[\W\-\. ]/)) 
                        { 
                                charsOnly += letter; 
                                specialChars++; 
                        } 
                }
                //Points based upon case change 
                var caseDiff = Math.abs(upper - lower); 
                score += parseFloat((lettersOnly.length - caseDiff) * criteriaPoints['CaseChange']); 
                //Alpha Numeric Points 
                var alphaNumDiff = Math.abs(upper+lower - numbers); 
                score += parseFloat(((lettersOnly.length + numbersOnly.length) - alphaNumDiff) * criteriaPoints['AlphaNum']); 
                //Special Character Points 
                score += parseFloat(specialChars * criteriaPoints['SpecialChars']); 
                //Penalise for lack of numbers 
                if (!numbers) 
                { 
                        score += parseFloat(v.length * criteriaPoints['NoNumbers']); 
                } 
                //Penalise for lack of letters 
                if (!lower && !upper) 
                { 
                        score += parseFloat(v.length * criteriaPoints['NoLetters']); 
                } 
                //Penalise for lack of special chars 
                if (!specialChars) 
                { 
                        score += parseFloat(v.length * criteriaPoints['NoSpecialChars']); 
                } 
                //Penalise for lack of changing case 
                if ((upper || lower) && (!upper || !lower)) 
                { 
                        score += parseFloat(v.length * criteriaPoints['NoCaseChange']); 
                } 
                //Penalise for duplicate chars 
                score += parseFloat(duplicates * criteriaPoints['Duplicates']); 
                
                //Now update the gauge 
                updateOutput(); 
                return score; 
        } 
        /** 
         * Allow the user to change the points weightings 
         * @param string criteria (See criteriaPoints) 
         * @param float points 
         * @return bool successful 
         */ 
        this.setPoints = function(criteria, pnts) 
        { 
                if (criteriaPoints[criteria]) 
                { 
                        criteriaPoints[criteria] = parseFloat(pnts); 
                        return true; 
                } 
                else return false; 
        } 
        
        //At end of instantiation load the gauge 
        load(); 
        
        //Just comes in useful 
        Array.prototype.hasValue = function(v) 
        { 
                for (var i in this) 
                { 
                        if (this[i] == v) return true; 
                } 
                return false; 
        } 
} 

var pw = new strengthChecker('pw_gauge');
