I'm trying to create a password generator based on the options provided by the user. My current script allows users to select uppercase, lowercase, numeric and special characters. This works perfectly and strings are generated to to the user's required length however upon generation, numbers cluster at the string with letters clustering at the beginning. A single special character parts the two. Do you have any suggestions on how to improve the process?
$('document').ready(function() {
$('button').click(function() {
var lower = "";
var upper = "";
var numeric = "";
var special = "";
var string_length = "";
if($('#12').is(':checked')) { string_length = 12; };
if($('#16').is(':checked')) { string_length = 16; };
if($('#18').is(':checked')) { string_length = 18; };
if($('#22').is(':checked')) { string_length = 22; };
if($('#24').is(':checked')) { string_length = 24; };
if($('#custom').is(':checked')) { $('#custom').show(); $('#custom').val(); } else { $('#custom').hide(); };
if($('#ch1').is(':checked')) { lower = "abcdefghijklmnopqrstuvwxyz"; } else { lower = ""; };
if($('#ch2').is(':checked')) { upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; } else { upper = ""; };
if($('#ch3').is(':checked')) { numeric = "0123456789"; } else { numeric = ""; };
if($('#ch4').is(':checked')) { special = "!£$%^&*()_-+={};:#~#?/"; } else { special = ""; };
var chars = lower + upper + numeric + special;
var randomstring = '';
var charCount = 0;
var numCount = 0;
for (var i=0; i<string_length; i++) {
if((Math.floor(Math.random() * 2) == 0) && numCount < 3 || charCount >= 5) {
var rnum = Math.floor(Math.random() * 10);
randomstring += rnum;
numCount += 1;
} else {
var rnum = Math.floor(Math.random() * chars.length);
randomstring += chars.substring(rnum,rnum+1);
charCount += 1;
}
}
$('span.string').html(randomstring);
});
});
The options 16 length, lowercase, uppercase, numeric and special characters returns something like e046pzw%65760294.
This line is your culprit:
if((Math.floor(Math.random() * 2) == 0) && numCount < 3 || charCount >= 5) {
It says:
The first 3 characters have a bit over 50/50 chance of being numbers. The "then" is always a number and the "else" is a number sometimes depending on options.
After you have 5 "else" selected chars (which means after col 8), you will always have a number.
This is because the "&&" takes precedence over the "||". I suggest using some parentheses to surround the OR clause if you want to have a 50/50 plus chance of using the digit. I also included an alternate way to do 50/50.
if ((Math.random() < 0.5) && (numCount < 3 || charCount >= 5)) {
I'm not sure why you want numbers to have precedence.
An alternative solution. Just my five cents:
$(function(){
$('input, select').change(function(){
var s = $('input[type="checkbox"]:checked').map(function(i, v){
return v.value;
}).get().join(''),
result = '';
for(var i=0; i < $('#length').val(); i++)
result += s.charAt(Math.floor(Math.random() * s.length));
$('#result').val(result);
});
});
Just to give you some ideas. I'm fully aware of that this doesn't take any "type count" in to consideration.
http://jsfiddle.net/m5y3e/
Related
I have the following function that validates a digits input consisted of only numbers based on Luhn Algorithm:
function isCheckdigitCorrect(value) {
// accept only digits, dashes or spaces
if (/[^0-9-\s]+/.test(value)) return false;
var nCheck = 0, nDigit = 0, bEven = false;
value = value.replace(/\D/g, "");
for (var n = value.length - 1; n >= 0; n--) {
var cDigit = value.charAt(n),
nDigit = parseInt(cDigit, 10);
if (bEven) {
if ((nDigit *= 2) > 9) nDigit -= 9;
}
nCheck += nDigit;
bEven = !bEven;
}
return (nCheck % 10) == 0;
}
Is there anyway that I can validate also alphanumerics, so let's suppose I have a valid ID: AC813(6) , () is the checksum. So is there a way that I can prevent users having to type mistakenly AF813(6) so this would tell user incorrect ID.
I appreciate your help
Substituting digits for alphabetic characters to calculate a checksum severely reduces the robustness of the check, and the simplest suggestion I can come up with is to use the Luhn mod N algorithm described on Wikipedia.
Translating the algorithm into JavaScipt was relatively straight forward: the following is not my code but a translation from the wiki article - so I won't pretend it is optimal. It is intended to work with strings of case insensitive ASCII alphabetic characters and decimal digits. For documentation see the wiki.
// based on https://en.wikipedia.org/wiki/Luhn_mod_N_algorithm
var charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
function NumberOfValidInputCharacters () { return charset.length; }
function CodePointFromCharacter(character) { return charset.indexOf(character)};
function CharacterFromCodePoint( codePoint) { return charset[codePoint]};
function GenerateCheckCharacter (input) {
var factor = 2;
var sum = 0;
var n = NumberOfValidInputCharacters();
input = input.toUpperCase();
// Starting from the right and working leftwards is easier since
// the initial "factor" will always be "2"
for (var i = input.length - 1; i >= 0; i--) {
var codePoint = CodePointFromCharacter(input[i]);
if( codePoint < 0) {
return "";
}
var addend = factor * codePoint;
// Alternate the "factor" that each "codePoint" is multiplied by
factor = (factor == 2) ? 1 : 2;
// Sum the digits of the "addend" as expressed in base "n"
addend = Math.floor(addend / n) + (addend % n);
sum += addend;
}
// Calculate the number that must be added to the "sum"
// to make it divisible by "n"
var remainder = sum % n;
var checkCodePoint = (n - remainder) % n;
return CharacterFromCodePoint(checkCodePoint);
}
function ValidateCheckCharacter(input) {
var factor = 1;
var sum = 0;
var n = NumberOfValidInputCharacters();
input = input.toUpperCase();
// Starting from the right, work leftwards
// Now, the initial "factor" will always be "1"
// since the last character is the check character
for (var i = input.length - 1; i >= 0; i--) {
var codePoint = CodePointFromCharacter(input[i]);
if( codePoint < 0) {
return false;
}
var addend = factor * codePoint;
// Alternate the "factor" that each "codePoint" is multiplied by
factor = (factor == 2) ? 1 : 2;
// Sum the digits of the "addend" as expressed in base "n"
addend = Math.floor(addend / n) + (addend % n);
sum += addend;
}
var remainder = sum % n;
return (remainder == 0);
}
// quick test:
console.log ("check character for 'abcde234': %s",
GenerateCheckCharacter("abcde234"));
console.log( "validate 'abcde2349' : %s " ,
ValidateCheckCharacter( "abcde2349"));
console.log( "validate 'abcde234X' : %s" ,
ValidateCheckCharacter( "abcde234X"));
If you just want to do the Luhn algorithm with letters replacing some of the numbers, then include an additional step to convert letters to numbers within your function.
So if you wanted to allow say A, B, C, D that convert to 0, 1, 2, 3 then you could do:
function isCheckdigitCorrect(value) {
// Letter to number mapping
var letters = {a:'0', b:'1', c:'2', d:'3'};
// Convert letters to their number equivalents, if they have one
value = value.split('').reduce(function(s, c){
return s += letters[c.toLowerCase()] || c;
},'');
// Continue as currently
// accept only digits, dashes or spaces
if (/[^0-9-\s]+/.test(value)) return false;
var nCheck = 0, nDigit = 0, bEven = false;
value = value.replace(/\D/g, "");
for (var n = value.length - 1; n >= 0; n--) {
var cDigit = value.charAt(n),
nDigit = parseInt(cDigit, 10);
if (bEven) {
if ((nDigit *= 2) > 9) nDigit -= 9;
}
nCheck += nDigit;
bEven = !bEven;
}
return (nCheck % 10) == 0;
}
// In the following, A = 0 and D = 3
console.log(isCheckdigitCorrect('375767AA4D6AA21'));
You can implement other algorithms in a similar way.
I've been stuck on this last part of my assignment for the longest time. I'm trying to loop through the alphabet using modulus. delta is the number of letters you have to move forward or backwards to get the real letter. SO if given getchars("H",-2), the function is supposed to return F. A problem arises however if the chars.charAt(chars.getIndexOf(data.charAt(i))) ever equals a number less than 0. I want to be able to give my function ("A", -1) or any negative number and have it return "Z".
This is an assignment for class so if possible please keep it to just modulus. I've been working on this last part for like 2 hours now.
function getChars(data,delta)
{
var chars;
var i;
var foundAt;
var newString;
chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
data = data.toUpperCase();
delta = Math.min(chars.length, delta);
i = 0;
newString = "";
while (i < data.length)
{
if(delta <= 0)
{
foundAt = (chars.indexOf(data.charAt(i)) + delta) ;window.alert(foundAt)
//newString = newString + chars.charAt(foundAt);
//i = i + 1;
}
else if((chars.indexOf(data.charAt(i)) < 0))
{
foundAt = data.charAt(i);
newString = newString + foundAt;
i = i + 1;
}
else
{
foundAt = ((chars.indexOf(data.charAt(0 + i)) + delta)) % chars.length;window.alert(foundAt);
newString = newString + chars.charAt(foundAt);window.alert(newString);
i = i + 1;
}
}
//return newString;
}
To be flexible, you can use i = chars.length - 1; and first after that do the found at.
You have to use you own modulus function :
function modulus(n,m) {
return ((n%m)+m)%m;
};
With your following code :
function getChars(data,delta)
{
var chars;
var i;
var foundAt;
var newString;
chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
data = data.toUpperCase();
i = 0;
newString = "";
while (i < data.length)
{
newString += chars.charAt(modulus(chars.indexOf(data[i])+delta,26))
i++;
}
return newString;
}
How to generate random numbers with below criteria in Javascript
String should contain at least 4 lower case letters from [a-z]
String should contain at least 4 upper case letters from [A-Z]
String should contain at least 4 numbers from [0-9]
Note: I don't want to use any JS library due to leagacy reasons
I tried below code but it doesn't match above criteria for example sometimes it does not contain numbers at all....
function randomString(length, chars) {
var mask = '';
if (chars.indexOf('a') > -1) mask += 'abcdefghijklmnopqrstuvwxyz';
if (chars.indexOf('A') > -1) mask += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
if (chars.indexOf('#') > -1) mask += '0123456789';
var result = '';
for (var i = length; i > 0; --i) result += mask[Math.round(Math.random() * (mask.length - 1))];
return result;
}
document.write(randomString(12, 'aA#'));
Is there any better approach to do it?
Here's how you can do it: generate an array which represents your criteria, shuffle it and fill the array.
Example:
function makeRandomString(criteria) {
// From http://stackoverflow.com/q/2450954/3371119
function shuffle(array) {
var currentIndex = array.length,
temporaryValue, randomIndex;
// While there remain elements to shuffle...
while (0 !== currentIndex) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
}
// Choose a random character from a string
function chooseRandom(str) {
return str[Math.floor(Math.random() * str.length)];
}
// Shuffle the criteria
shuffle(criteria);
var result = "";
// Build the resulting string by choosing a random character from each part
for (var i = 0; i < criteria.length; ++i) result += chooseRandom(criteria[i]);
return result;
}
Example usage:
// Some constants explaining the criteria
var lowercase = "abcdefghijklmnopqrstuvwxyz";
var uppercase = lowercase.toUpperCase();
var numbers = "0123456789";
// Note: if you don't like typing all that, change the names to L, N, and U
var criteria = [lowercase, lowercase, lowercase, lowercase, // 4 lowercase
numbers, numbers, numbers, numbers, // 4 numbers
uppercase, uppercase, uppercase, uppercase // 4 uppercase
];
console.log(makeRandomString(criteria));
Or even better (much less typing):
function repeat(elem, n) {
var result = [];
for (var i = 0; i < n; ++i) result.push(elem);
return result;
}
var criteria = repeat(lowercase, 4)
.concat(repeat(uppercase, 4))
.concat(repeat(numbers, 4));
console.log(makeRandomString(criteria));
Following your same philosophy, you could try something like this. You will generate a random string with 4 characters of each group at least (so at least 12 characters), and up to the number of characters you specify as the parameter.
var LOWER_CASE_MASK = 'abcdefghijklmnopqrstuvwxyz';
var UPPER_CASE_MASK = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
var NUMBER_MASK = '0123456789';
var ALL_MASK = LOWER_CASE_MASK + UPPER_CASE_MASK + NUMBER_MASK;
function randomString(length) {
var result = '';
for (var i = 0; i < 4; i++) {
result += randomise(LOWER_CASE_MASK);
result += randomise(UPPER_CASE_MASK);
result += randomise(NUMBER_MASK);
}
for (var j = 0; j < length - 12; j++) {
result += ALL_MASK[Math.round(Math.random() * (ALL_MASK.length - 1))];
}
return shuffle(result);
}
function randomise(string) {
return string[Math.round(Math.random() * (string.length - 1))];
}
function shuffle(string) {
var parts = string.split('');
for (var i = parts.length; i > 0;) {
var random = parseInt(Math.random() * i);
var temp = parts[--i];
parts[i] = parts[random];
parts[random] = temp;
}
return parts.join('');
}
document.write(randomString(20));
The shuffle function is an implementation of the Fisher-Yates shuffle.
Plunker here.
Hope this helps.
Here is my solution :
var text = " ";
var numChars = "0123456789";
var upCaseChars="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var lowCaseChars="abcdefghijklmnopqrstuvwxyz";
for( var i=0; i < 4; i++ )
{
text += numChars.charAt(Math.floor(Math.random() * numChars.length));
text += upCaseChars.charAt(Math.floor(Math.random() * upCaseChars.length));
text += lowCaseChars.charAt(Math.floor(Math.random() * lowCaseChars.length));
}
console.log(text);
I think it should do the trick, you can test it here.
Any ideas on the following? I want to input a number into a function and insert dashes "-" between the odd digits. So 4567897 would become "456789-7". What I have so far is to convert the number into a string and then an array, then look for two odd numbers in a row and use the .splice() method to add the dashes where appropriate. It does not work and I figure I may not be on the right track anyway, and that there has to be a simpler solution.
function DashInsert(num) {
var numArr = num.toString().split('');
for (var i = 0; i < numArr.length; i++){
if (numArr[i]%2 != 0){
if (numArr[i+1]%2 != 0) {
numArr.splice(i, 0, "-");
}
}
}
return numArr;
}
The problem is you're changing the thing you're iterating over. If instead you maintain a separate output and input...
function insertDashes(num) {
var inStr = String(num);
var outStr = inStr[0], ii;
for (ii = 1; ii < inStr.length; ii++) {
if (inStr[ii-1] % 2 !== 0 && inStr[ii] % 2 !== 0) {
outStr += '-';
}
outStr += inStr[ii];
}
return outStr;
}
You can try using regular expressions
'4567897'.replace(/([13579])(?=[13579])/g, '$1-')
Regex Explained
So, we find an odd number (([13579]) is a capturing group meaning we can use it as a reference in the replacement $1) ensure that it is followed by another odd number in the non-capturing positive lookahead ((?=[13579])) and replace the matched odd number adding the - prefix
Here is the function to do it:
function dashes(number){
var numString = '';
var numArr = number.toString().split('');
console.log(numArr);
for(i = 0; i < numArr.length; i++){
if(numArr[i] % 2 === 1 && numArr[i+1] % 2 === 1){
numString += numArr[i] + '-';
}else{
numString += numArr[i];
}
}
console.log(numString);
}
dashes(456379);
Tested and everything.
Edit: OrangeDog's answer was posted earlier (by nearly a full half hour), I just wanted to make an answer which uses your code since you're almost there.
Using another array instead of splicing into one you were looping through (this happens to return a string using join):
var num = 4567897;
function DashInsert(num) {
var numArr = num.toString().split('');
var len = numArr.length;
var final = [];
for (var i = 0; i < len; i++){
final.push(numArr[i]);
if (numArr[i]%2 != 0){
if (i+1 < len && numArr[i+1]%2 != 0) {
final.push("-")
}
}
}
return final.join("");
}
alert(DashInsert(num));
function dashInsert(str) {
var arrayNumbers = str.split("");
var newString = "";
for (var i = 0; i < arrayNumbers.length; i++){
if(arrayNumbers[i] % 2 === 1 && arrayNumbers[i + 1] % 2 === 1){
newString = newString + arrayNumbers[i] + "-";
} else {
newString = newString + arrayNumbers[i];
}
}
return newString;
}
var result = dashInsert("3453246");
console.log(result);
I'm using the following JavaScript. The final result is display sequence character.
But I want to display alphanumeric in-sequence order. How do I do that?
var disp = '';
var string = '';
var i;
var chars = "0123456789abcdefghiklmnopqrstuvwxyz";
var ran_unrounded;
var ran_number;
var rnum;
for (i = 0; i < 5; i++) {
rnum = Math.floor(Math.random() * chars.length);
string += chars.substring(rnum, rnum + 1);
ran_unrounded = Math.random() * 3;
ran_number = Math.floor(ran_unrounded);
//document.write(chars.substring(rnum, rnum + 1));
// alert('rnum', rnum, '--', rnum + 1);
disp = chars.substring(rnum, rnum + 8);
}
OK, so from the clarification in the comments above the requirement is to generate a string that contains two random "words", where each "word" has four characters selected at random from a predefined set of available characters.
Following is one way to do that:
var chars = "0123456789abcdefghiklmnopqrstuvwxyz";
function getWord(numChars) {
var word = "",
i;
for (i = 0; i < numChars; i++)
word += chars.charAt(Math.floor(Math.random() * chars.length));
return word;
}
function getWords(numWords, numCharsPerWord) {
var words = [],
i;
for (i = 0; i < numWords; i++)
words.push(getWord(numCharsPerWord));
return words.join(" ");
}
console.log( getWords(2, 4) ); // "a8ak 1wp9"
console.log( getWords(3, 4) ); // "7ua1 zh80 yy3r"
console.log( getWords(2, 5) ); // "j5ms2 e4xn8"
Demo: http://jsfiddle.net/dgnwh/