Created
June 17, 2020 04:14
-
-
Save kosalvann/c36ea719f299b0c46019d48abd225fda to your computer and use it in GitHub Desktop.
Random Password Generator A random password generator tool // source https://jsbin.com/juriwij
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="description" content="A random password generator tool"> | |
<meta name="viewport" content="width=device-width"> | |
<title>Random Password Generator</title> | |
<link href="https://fonts.googleapis.com/css2?family=Material+Icons&PT+Mono&Roboto:wght@400;700&display=swap" rel="stylesheet"> | |
<style id="jsbin-css"> | |
*, :after, :before { | |
font-size: 0.90rem; | |
font-family: 'Roboto', arial, sans-serif; | |
box-sizing: border-box; | |
-moz-osx-font-smoothing: grayscale; | |
-webkit-font-smoothing: antialiased; | |
text-rendering: optimizeLegibility; | |
} | |
html, body { | |
padding: 0; | |
margin: 0; | |
background-color: #ffffff; | |
height: 100%; | |
box-sizing: border-box; | |
-webkit-font-smoothing: antialiased; | |
-moz-osx-font-smoothing: grayscale; | |
} | |
body { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
background: #f3f7fa; | |
background: linear-gradient(345deg, #f3f7fa 0%, #ffffff 100%); | |
} | |
body * { | |
color: #2a354f; | |
font-family: 'Roboto', arial, sans-serif; | |
line-height: 1.43; | |
letter-spacing: 0.025em; | |
box-sizing: border-box; | |
-webkit-transition: all 0.5s; | |
transition: all 0.5s; | |
} | |
#app { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
padding: 20px; | |
width: 80%; | |
height: 100%; | |
font-size: 15px; | |
font-family: arial,sans-serif; | |
line-height: 1.2; | |
} | |
h2 { | |
display: flex; | |
flex-direction: row; | |
align-items: center; | |
font-size: 18px; | |
color: #2b78fe; | |
letter-spacing: -0.015em; | |
font-weight: 600; | |
line-height: 1.8; | |
} | |
h2 .material-icons { | |
margin-right: 6px; | |
color: #2b78fe; | |
} | |
.container { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
width: 100%; | |
background-color: #ffffff; | |
border-radius: 5px; | |
border: 1px solid #ddd; | |
} | |
section { | |
display: flex; | |
flex-direction: row; | |
align-items: stretch; | |
justify-content: center; | |
width: 100%; | |
} | |
section.head { | |
border-bottom: 1px solid #ddd; | |
} | |
.password { | |
display: flex; | |
align-items: center; | |
flex: 1; | |
padding: 10px 20px; | |
font-size: 17px; | |
font-family: 'PT Mono', monospace; | |
color: #666; | |
letter-spacing: 0.05em; | |
border: 0 none; | |
outline: none; | |
border-top-left-radius:5px; | |
-webkit-transition: all 0.5s; | |
transition: all 0.5s; | |
} | |
.head.selected .password { | |
color: #000; | |
background: #f6f6f6; | |
} | |
.head .icon { | |
padding: 10px; | |
cursor: pointer; | |
border-left: 1px solid #ddd; | |
} | |
.head.selected .icon { | |
color: #11ab7c; | |
} | |
section.settings { | |
padding: 20px; | |
} | |
section.settings > div { | |
display: flex; | |
flex-direction: row; | |
align-items: center; | |
justify-content: center; | |
margin: 0 7px; | |
} | |
section.settings .material-icons { | |
margin-right: 5px; | |
color: #2a354f; | |
} | |
section.settings .symbols { | |
cursor: pointer; | |
} | |
.material-icons { | |
font-family: 'Material Icons'; | |
font-weight: normal; | |
font-style: normal; | |
font-size: 24px; | |
display: inline-block; | |
line-height: 1; | |
text-transform: none; | |
letter-spacing: normal; | |
word-wrap: normal; | |
white-space: nowrap; | |
direction: ltr; | |
-webkit-font-smoothing: antialiased; | |
text-rendering: optimizeLegibility; | |
-moz-osx-font-smoothing: grayscale; | |
font-feature-settings: 'liga'; | |
} | |
section.range { | |
flex-direction: row; | |
align-items: center; | |
justify-content: center; | |
border-bottom: 1px solid #ddd; | |
padding-right: 20px; | |
} | |
section.range .output { | |
flex: 1 auto; | |
text-align: center; | |
margin-right: 20px; | |
padding: 10px 20px; | |
font-size: 13px; | |
color: #ddd; | |
border-right: 1px solid #ddd; | |
} | |
section.range .output .value { | |
font-size: 17px; | |
font-weight: 600; | |
font-family: 'PT Mono', monospace; | |
color: #333; | |
} | |
input[type="range"] { | |
-webkit-appearance: none; | |
width: 85%; | |
height: 100%; | |
background: transparent; | |
} | |
input[type="range"]:focus { | |
outline: none; | |
} | |
input[type="range"]::-webkit-slider-thumb { | |
-webkit-appearance: none; | |
height: 24px; | |
width: 24px; | |
border-radius: 50%; | |
background: #ffffff; | |
margin-top: -10px; | |
border: 2px solid rgba(0, 0, 0, 0.2); | |
cursor: pointer; | |
} | |
input[type="range"]::-webkit-slider-runnable-track { | |
width: 60%; | |
height: 5px; | |
background: rgba(0, 0, 0, 0.1); | |
border-radius: 3rem; | |
cursor: pointer; | |
} | |
input[type="range"]::-ms-track { | |
width: 60%; | |
cursor: pointer; | |
height: 9px; | |
-ms-transition: all 0.5s; | |
transition: all 0.5s; | |
background: transparent; | |
border-color: transparent; | |
color: transparent; | |
} | |
input[type="range"]::-ms-thumb { | |
height: 16px; | |
width: 16px; | |
border-radius: 50%; | |
background: #ffffff; | |
margin-top: -5px; | |
box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); | |
cursor: pointer; | |
} | |
input[type="range"]::-ms-fill-lower { | |
background: #bdbdbd; | |
border-radius: 3rem; | |
} | |
input[type="range"]:focus::-ms-fill-lower { | |
background: #ff6e40; | |
} | |
input[type="range"]::-ms-fill-upper { | |
background: #bdbdbd; | |
border-radius: 3rem; | |
} | |
input[type="range"]:focus::-ms-fill-upper { | |
background: #ff6e40; | |
} | |
input[type="range"]::-moz-range-thumb { | |
height: 16px; | |
width: 16px; | |
border-radius: 50%; | |
background: #ffffff; | |
margin-top: -5px; | |
box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); | |
cursor: pointer; | |
} | |
input[type="range"]::-moz-range-track { | |
width: 60%; | |
height: 9px; | |
background: #bdbdbd; | |
border-radius: 3rem; | |
-moz-transition: all 0.5s; | |
transition: all 0.5s; | |
cursor: pointer; | |
} | |
input[type="range"]:hover::-moz-range-track { | |
background: #ff6e40; | |
} | |
/* | |
* Media Query | |
*/ | |
@media only screen and (max-width: 528px) { | |
#app, | |
.container { | |
width: 100%; | |
} | |
h2 { | |
font-size: 17px | |
} | |
section.head { | |
flex-direction: column; | |
} | |
.password { | |
flex: 1; | |
border-top-right-radius: 5px; | |
text-align: center; | |
} | |
.head .button.icon { | |
display: none; | |
} | |
} | |
</style> | |
</head> | |
<body> | |
<div id="app"> | |
<h2><span class="material-icons">security</span>Password Generator</h2> | |
<div class="container"> | |
<section class="head"> | |
<input class="password" id="password" readonly="readonly"/> | |
<span class="copy button material-icons icon">sync</span> | |
</section> | |
<section class="range"> | |
<div class="output"> | |
<div>Length</div> | |
<span class="value">16</span> | |
</div> | |
<input id="slider" type="range" min="6" max="35" value="16"> | |
</section> | |
<section class="settings"> | |
<div class="letters"> | |
<span class="material-icons">radio_button_checked</span> | |
<label>Letters</label> | |
</div> | |
<div class="numbers"> | |
<span class="material-icons">radio_button_checked</span> | |
<label>Numbers</label> | |
</div> | |
<div class="symbols"> | |
<span class="material-icons">radio_button_unchecked</span> | |
<label>Symbols</label> | |
</div> | |
</section> | |
</div> | |
</div> | |
<script id="jsbin-javascript"> | |
let // Global variables | |
headSection = document.querySelector('.head') | |
, password = document.querySelector('.password') | |
, button = document.querySelector('.copy.button') | |
, symbolsButton = document.querySelector('.settings .symbols') | |
, symbolsCheckBox = document.querySelector('.symbols .material-icons') | |
, slider = document.querySelector('#slider') | |
, sliderValue = document.querySelector('.output .value') | |
// The initial state of the settings | |
, selectionState = { | |
'symbols': false | |
}; | |
/* | |
* The alphanumeric characters and symbols | |
* used to generate passwords | |
*/ | |
const CHARSET = { | |
'numeric' : '0123456789', | |
'alpha': 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', | |
'symbols': '/^_@&[<->]\:{.+},$' | |
}; | |
/* | |
* Load the functions to generate the password | |
*/ | |
window.onload = (e) => { | |
password.value = renderPassword(slider.value, selectionState['symbols']); | |
// Regenerate password on these events | |
changePasswordOnSlide(e); | |
reloadPassword(e); | |
symbolButtonHandler(e); | |
}; | |
/* | |
* Toggle the symbol button between true/false state | |
*/ | |
const symbolButtonHandler = (e) => { | |
symbolsButton.addEventListener('click', (e) => { | |
let renderSymbol = selectionState['symbols'] = !selectionState['symbols']; | |
password.value = renderPassword(slider.value, renderSymbol); | |
// Disable/enable symbols in password | |
symbolsCheckBox.textContent | |
= selectionState['symbols'] | |
? 'radio_button_checked' | |
: 'radio_button_unchecked'; | |
}); | |
} | |
/* | |
* Handle the copy-to-clipboard functionality | |
*/ | |
const copyToClipboard = () => { | |
// Select & copy the password value | |
button.innerText = 'assignment_turned_in'; | |
password.select(); | |
document.execCommand('copy'); | |
// Return to original state | |
setTimeout(() => { | |
button.innerText = 'sync'; | |
headSection.classList.remove('selected'); | |
}, 400); | |
} | |
/* | |
* The logic to generate a random string | |
* | |
* @param charset String characters use for the password | |
* @param len The desired length of the password via the slider | |
*/ | |
const generatePassword = (charset, len) => { | |
var result = ""; | |
for (var i = 0; i < len; i++) { | |
result += charset[Math.floor(Math.random() * 52 + 1)]; | |
} | |
// Ensure we only return the exact length | |
return result.substr(0, len); | |
} | |
/* | |
* The logic to regenerate password when using the slider | |
*/ | |
const changePasswordOnSlide = (e) => { | |
slider.oninput = (e) => { | |
sliderValue.textContent = slider.value; | |
setTimeout(() => { | |
// Generate password on slide | |
password.value = renderPassword(slider.value); | |
headSection.classList.add('selected'); | |
copyToClipboard(); | |
// Return to original state | |
setTimeout(() => { | |
headSection.classList.remove('selected'); | |
}, 400); | |
}, 0) | |
}; | |
} | |
/* | |
* The logic to regenerate password when | |
* the reload button is clicked | |
*/ | |
const reloadPassword = (e) => { | |
button.addEventListener('click', (e) => { | |
setTimeout(() => { | |
copyToClipboard(); | |
}, 0 | |
, password.value = renderPassword(slider.value) | |
, headSection.classList.add('selected') | |
) | |
}) | |
} | |
/* | |
* Determine if the symbol is enabled and merge all of the combined | |
* alphanumeric characters and symbol from the charset | |
* | |
* @param len The desired length of the password via the slider | |
* @param renderSymbols Determined if symbol are included in the password | |
*/ | |
const renderPassword = (len, renderSymbols = selectionState['symbols']) => { | |
let charSet = CHARSET['numeric'].split('').concat( | |
renderSymbols ? CHARSET['symbols'].split('') : [], | |
CHARSET['alpha'].split('') | |
); | |
return generatePassword(charSet, len) | |
} | |
</script> | |
<script id="jsbin-source-css" type="text/css">*, :after, :before { | |
font-size: 0.90rem; | |
font-family: 'Roboto', arial, sans-serif; | |
box-sizing: border-box; | |
-moz-osx-font-smoothing: grayscale; | |
-webkit-font-smoothing: antialiased; | |
text-rendering: optimizeLegibility; | |
} | |
html, body { | |
padding: 0; | |
margin: 0; | |
background-color: #ffffff; | |
height: 100%; | |
box-sizing: border-box; | |
-webkit-font-smoothing: antialiased; | |
-moz-osx-font-smoothing: grayscale; | |
} | |
body { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
background: #f3f7fa; | |
background: linear-gradient(345deg, #f3f7fa 0%, #ffffff 100%); | |
} | |
body * { | |
color: #2a354f; | |
font-family: 'Roboto', arial, sans-serif; | |
line-height: 1.43; | |
letter-spacing: 0.025em; | |
box-sizing: border-box; | |
-webkit-transition: all 0.5s; | |
transition: all 0.5s; | |
} | |
#app { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
padding: 20px; | |
width: 80%; | |
height: 100%; | |
font-size: 15px; | |
font-family: arial,sans-serif; | |
line-height: 1.2; | |
} | |
h2 { | |
display: flex; | |
flex-direction: row; | |
align-items: center; | |
font-size: 18px; | |
color: #2b78fe; | |
letter-spacing: -0.015em; | |
font-weight: 600; | |
line-height: 1.8; | |
} | |
h2 .material-icons { | |
margin-right: 6px; | |
color: #2b78fe; | |
} | |
.container { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
width: 100%; | |
background-color: #ffffff; | |
border-radius: 5px; | |
border: 1px solid #ddd; | |
} | |
section { | |
display: flex; | |
flex-direction: row; | |
align-items: stretch; | |
justify-content: center; | |
width: 100%; | |
} | |
section.head { | |
border-bottom: 1px solid #ddd; | |
} | |
.password { | |
display: flex; | |
align-items: center; | |
flex: 1; | |
padding: 10px 20px; | |
font-size: 17px; | |
font-family: 'PT Mono', monospace; | |
color: #666; | |
letter-spacing: 0.05em; | |
border: 0 none; | |
outline: none; | |
border-top-left-radius:5px; | |
-webkit-transition: all 0.5s; | |
transition: all 0.5s; | |
} | |
.head.selected .password { | |
color: #000; | |
background: #f6f6f6; | |
} | |
.head .icon { | |
padding: 10px; | |
cursor: pointer; | |
border-left: 1px solid #ddd; | |
} | |
.head.selected .icon { | |
color: #11ab7c; | |
} | |
section.settings { | |
padding: 20px; | |
} | |
section.settings > div { | |
display: flex; | |
flex-direction: row; | |
align-items: center; | |
justify-content: center; | |
margin: 0 7px; | |
} | |
section.settings .material-icons { | |
margin-right: 5px; | |
color: #2a354f; | |
} | |
section.settings .symbols { | |
cursor: pointer; | |
} | |
.material-icons { | |
font-family: 'Material Icons'; | |
font-weight: normal; | |
font-style: normal; | |
font-size: 24px; | |
display: inline-block; | |
line-height: 1; | |
text-transform: none; | |
letter-spacing: normal; | |
word-wrap: normal; | |
white-space: nowrap; | |
direction: ltr; | |
-webkit-font-smoothing: antialiased; | |
text-rendering: optimizeLegibility; | |
-moz-osx-font-smoothing: grayscale; | |
font-feature-settings: 'liga'; | |
} | |
section.range { | |
flex-direction: row; | |
align-items: center; | |
justify-content: center; | |
border-bottom: 1px solid #ddd; | |
padding-right: 20px; | |
} | |
section.range .output { | |
flex: 1 auto; | |
text-align: center; | |
margin-right: 20px; | |
padding: 10px 20px; | |
font-size: 13px; | |
color: #ddd; | |
border-right: 1px solid #ddd; | |
} | |
section.range .output .value { | |
font-size: 17px; | |
font-weight: 600; | |
font-family: 'PT Mono', monospace; | |
color: #333; | |
} | |
input[type="range"] { | |
-webkit-appearance: none; | |
width: 85%; | |
height: 100%; | |
background: transparent; | |
} | |
input[type="range"]:focus { | |
outline: none; | |
} | |
input[type="range"]::-webkit-slider-thumb { | |
-webkit-appearance: none; | |
height: 24px; | |
width: 24px; | |
border-radius: 50%; | |
background: #ffffff; | |
margin-top: -10px; | |
border: 2px solid rgba(0, 0, 0, 0.2); | |
cursor: pointer; | |
} | |
input[type="range"]::-webkit-slider-runnable-track { | |
width: 60%; | |
height: 5px; | |
background: rgba(0, 0, 0, 0.1); | |
border-radius: 3rem; | |
cursor: pointer; | |
} | |
input[type="range"]::-ms-track { | |
width: 60%; | |
cursor: pointer; | |
height: 9px; | |
-ms-transition: all 0.5s; | |
transition: all 0.5s; | |
background: transparent; | |
border-color: transparent; | |
color: transparent; | |
} | |
input[type="range"]::-ms-thumb { | |
height: 16px; | |
width: 16px; | |
border-radius: 50%; | |
background: #ffffff; | |
margin-top: -5px; | |
box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); | |
cursor: pointer; | |
} | |
input[type="range"]::-ms-fill-lower { | |
background: #bdbdbd; | |
border-radius: 3rem; | |
} | |
input[type="range"]:focus::-ms-fill-lower { | |
background: #ff6e40; | |
} | |
input[type="range"]::-ms-fill-upper { | |
background: #bdbdbd; | |
border-radius: 3rem; | |
} | |
input[type="range"]:focus::-ms-fill-upper { | |
background: #ff6e40; | |
} | |
input[type="range"]::-moz-range-thumb { | |
height: 16px; | |
width: 16px; | |
border-radius: 50%; | |
background: #ffffff; | |
margin-top: -5px; | |
box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); | |
cursor: pointer; | |
} | |
input[type="range"]::-moz-range-track { | |
width: 60%; | |
height: 9px; | |
background: #bdbdbd; | |
border-radius: 3rem; | |
-moz-transition: all 0.5s; | |
transition: all 0.5s; | |
cursor: pointer; | |
} | |
input[type="range"]:hover::-moz-range-track { | |
background: #ff6e40; | |
} | |
/* | |
* Media Query | |
*/ | |
@media only screen and (max-width: 528px) { | |
#app, | |
.container { | |
width: 100%; | |
} | |
h2 { | |
font-size: 17px | |
} | |
section.head { | |
flex-direction: column; | |
} | |
.password { | |
flex: 1; | |
border-top-right-radius: 5px; | |
text-align: center; | |
} | |
.head .button.icon { | |
display: none; | |
} | |
} | |
</script> | |
<script id="jsbin-source-javascript" type="text/javascript">let // Global variables | |
headSection = document.querySelector('.head') | |
, password = document.querySelector('.password') | |
, button = document.querySelector('.copy.button') | |
, symbolsButton = document.querySelector('.settings .symbols') | |
, symbolsCheckBox = document.querySelector('.symbols .material-icons') | |
, slider = document.querySelector('#slider') | |
, sliderValue = document.querySelector('.output .value') | |
// The initial state of the settings | |
, selectionState = { | |
'symbols': false | |
}; | |
/* | |
* The alphanumeric characters and symbols | |
* used to generate passwords | |
*/ | |
const CHARSET = { | |
'numeric' : '0123456789', | |
'alpha': 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', | |
'symbols': '/^_@&[<->]\:{.+},$' | |
}; | |
/* | |
* Load the functions to generate the password | |
*/ | |
window.onload = (e) => { | |
password.value = renderPassword(slider.value, selectionState['symbols']); | |
// Regenerate password on these events | |
changePasswordOnSlide(e); | |
reloadPassword(e); | |
symbolButtonHandler(e); | |
}; | |
/* | |
* Toggle the symbol button between true/false state | |
*/ | |
const symbolButtonHandler = (e) => { | |
symbolsButton.addEventListener('click', (e) => { | |
let renderSymbol = selectionState['symbols'] = !selectionState['symbols']; | |
password.value = renderPassword(slider.value, renderSymbol); | |
// Disable/enable symbols in password | |
symbolsCheckBox.textContent | |
= selectionState['symbols'] | |
? 'radio_button_checked' | |
: 'radio_button_unchecked'; | |
}); | |
} | |
/* | |
* Handle the copy-to-clipboard functionality | |
*/ | |
const copyToClipboard = () => { | |
// Select & copy the password value | |
button.innerText = 'assignment_turned_in'; | |
password.select(); | |
document.execCommand('copy'); | |
// Return to original state | |
setTimeout(() => { | |
button.innerText = 'sync'; | |
headSection.classList.remove('selected'); | |
}, 400); | |
} | |
/* | |
* The logic to generate a random string | |
* | |
* @param charset String characters use for the password | |
* @param len The desired length of the password via the slider | |
*/ | |
const generatePassword = (charset, len) => { | |
var result = ""; | |
for (var i = 0; i < len; i++) { | |
result += charset[Math.floor(Math.random() * 52 + 1)]; | |
} | |
// Ensure we only return the exact length | |
return result.substr(0, len); | |
} | |
/* | |
* The logic to regenerate password when using the slider | |
*/ | |
const changePasswordOnSlide = (e) => { | |
slider.oninput = (e) => { | |
sliderValue.textContent = slider.value; | |
setTimeout(() => { | |
// Generate password on slide | |
password.value = renderPassword(slider.value); | |
headSection.classList.add('selected'); | |
copyToClipboard(); | |
// Return to original state | |
setTimeout(() => { | |
headSection.classList.remove('selected'); | |
}, 400); | |
}, 0) | |
}; | |
} | |
/* | |
* The logic to regenerate password when | |
* the reload button is clicked | |
*/ | |
const reloadPassword = (e) => { | |
button.addEventListener('click', (e) => { | |
setTimeout(() => { | |
copyToClipboard(); | |
}, 0 | |
, password.value = renderPassword(slider.value) | |
, headSection.classList.add('selected') | |
) | |
}) | |
} | |
/* | |
* Determine if the symbol is enabled and merge all of the combined | |
* alphanumeric characters and symbol from the charset | |
* | |
* @param len The desired length of the password via the slider | |
* @param renderSymbols Determined if symbol are included in the password | |
*/ | |
const renderPassword = (len, renderSymbols = selectionState['symbols']) => { | |
let charSet = CHARSET['numeric'].split('').concat( | |
renderSymbols ? CHARSET['symbols'].split('') : [], | |
CHARSET['alpha'].split('') | |
); | |
return generatePassword(charSet, len) | |
} | |
</script></body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
*, :after, :before { | |
font-size: 0.90rem; | |
font-family: 'Roboto', arial, sans-serif; | |
box-sizing: border-box; | |
-moz-osx-font-smoothing: grayscale; | |
-webkit-font-smoothing: antialiased; | |
text-rendering: optimizeLegibility; | |
} | |
html, body { | |
padding: 0; | |
margin: 0; | |
background-color: #ffffff; | |
height: 100%; | |
box-sizing: border-box; | |
-webkit-font-smoothing: antialiased; | |
-moz-osx-font-smoothing: grayscale; | |
} | |
body { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
background: #f3f7fa; | |
background: linear-gradient(345deg, #f3f7fa 0%, #ffffff 100%); | |
} | |
body * { | |
color: #2a354f; | |
font-family: 'Roboto', arial, sans-serif; | |
line-height: 1.43; | |
letter-spacing: 0.025em; | |
box-sizing: border-box; | |
-webkit-transition: all 0.5s; | |
transition: all 0.5s; | |
} | |
#app { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
padding: 20px; | |
width: 80%; | |
height: 100%; | |
font-size: 15px; | |
font-family: arial,sans-serif; | |
line-height: 1.2; | |
} | |
h2 { | |
display: flex; | |
flex-direction: row; | |
align-items: center; | |
font-size: 18px; | |
color: #2b78fe; | |
letter-spacing: -0.015em; | |
font-weight: 600; | |
line-height: 1.8; | |
} | |
h2 .material-icons { | |
margin-right: 6px; | |
color: #2b78fe; | |
} | |
.container { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
width: 100%; | |
background-color: #ffffff; | |
border-radius: 5px; | |
border: 1px solid #ddd; | |
} | |
section { | |
display: flex; | |
flex-direction: row; | |
align-items: stretch; | |
justify-content: center; | |
width: 100%; | |
} | |
section.head { | |
border-bottom: 1px solid #ddd; | |
} | |
.password { | |
display: flex; | |
align-items: center; | |
flex: 1; | |
padding: 10px 20px; | |
font-size: 17px; | |
font-family: 'PT Mono', monospace; | |
color: #666; | |
letter-spacing: 0.05em; | |
border: 0 none; | |
outline: none; | |
border-top-left-radius:5px; | |
-webkit-transition: all 0.5s; | |
transition: all 0.5s; | |
} | |
.head.selected .password { | |
color: #000; | |
background: #f6f6f6; | |
} | |
.head .icon { | |
padding: 10px; | |
cursor: pointer; | |
border-left: 1px solid #ddd; | |
} | |
.head.selected .icon { | |
color: #11ab7c; | |
} | |
section.settings { | |
padding: 20px; | |
} | |
section.settings > div { | |
display: flex; | |
flex-direction: row; | |
align-items: center; | |
justify-content: center; | |
margin: 0 7px; | |
} | |
section.settings .material-icons { | |
margin-right: 5px; | |
color: #2a354f; | |
} | |
section.settings .symbols { | |
cursor: pointer; | |
} | |
.material-icons { | |
font-family: 'Material Icons'; | |
font-weight: normal; | |
font-style: normal; | |
font-size: 24px; | |
display: inline-block; | |
line-height: 1; | |
text-transform: none; | |
letter-spacing: normal; | |
word-wrap: normal; | |
white-space: nowrap; | |
direction: ltr; | |
-webkit-font-smoothing: antialiased; | |
text-rendering: optimizeLegibility; | |
-moz-osx-font-smoothing: grayscale; | |
font-feature-settings: 'liga'; | |
} | |
section.range { | |
flex-direction: row; | |
align-items: center; | |
justify-content: center; | |
border-bottom: 1px solid #ddd; | |
padding-right: 20px; | |
} | |
section.range .output { | |
flex: 1 auto; | |
text-align: center; | |
margin-right: 20px; | |
padding: 10px 20px; | |
font-size: 13px; | |
color: #ddd; | |
border-right: 1px solid #ddd; | |
} | |
section.range .output .value { | |
font-size: 17px; | |
font-weight: 600; | |
font-family: 'PT Mono', monospace; | |
color: #333; | |
} | |
input[type="range"] { | |
-webkit-appearance: none; | |
width: 85%; | |
height: 100%; | |
background: transparent; | |
} | |
input[type="range"]:focus { | |
outline: none; | |
} | |
input[type="range"]::-webkit-slider-thumb { | |
-webkit-appearance: none; | |
height: 24px; | |
width: 24px; | |
border-radius: 50%; | |
background: #ffffff; | |
margin-top: -10px; | |
border: 2px solid rgba(0, 0, 0, 0.2); | |
cursor: pointer; | |
} | |
input[type="range"]::-webkit-slider-runnable-track { | |
width: 60%; | |
height: 5px; | |
background: rgba(0, 0, 0, 0.1); | |
border-radius: 3rem; | |
cursor: pointer; | |
} | |
input[type="range"]::-ms-track { | |
width: 60%; | |
cursor: pointer; | |
height: 9px; | |
-ms-transition: all 0.5s; | |
transition: all 0.5s; | |
background: transparent; | |
border-color: transparent; | |
color: transparent; | |
} | |
input[type="range"]::-ms-thumb { | |
height: 16px; | |
width: 16px; | |
border-radius: 50%; | |
background: #ffffff; | |
margin-top: -5px; | |
box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); | |
cursor: pointer; | |
} | |
input[type="range"]::-ms-fill-lower { | |
background: #bdbdbd; | |
border-radius: 3rem; | |
} | |
input[type="range"]:focus::-ms-fill-lower { | |
background: #ff6e40; | |
} | |
input[type="range"]::-ms-fill-upper { | |
background: #bdbdbd; | |
border-radius: 3rem; | |
} | |
input[type="range"]:focus::-ms-fill-upper { | |
background: #ff6e40; | |
} | |
input[type="range"]::-moz-range-thumb { | |
height: 16px; | |
width: 16px; | |
border-radius: 50%; | |
background: #ffffff; | |
margin-top: -5px; | |
box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); | |
cursor: pointer; | |
} | |
input[type="range"]::-moz-range-track { | |
width: 60%; | |
height: 9px; | |
background: #bdbdbd; | |
border-radius: 3rem; | |
-moz-transition: all 0.5s; | |
transition: all 0.5s; | |
cursor: pointer; | |
} | |
input[type="range"]:hover::-moz-range-track { | |
background: #ff6e40; | |
} | |
/* | |
* Media Query | |
*/ | |
@media only screen and (max-width: 528px) { | |
#app, | |
.container { | |
width: 100%; | |
} | |
h2 { | |
font-size: 17px | |
} | |
section.head { | |
flex-direction: column; | |
} | |
.password { | |
flex: 1; | |
border-top-right-radius: 5px; | |
text-align: center; | |
} | |
.head .button.icon { | |
display: none; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let // Global variables | |
headSection = document.querySelector('.head') | |
, password = document.querySelector('.password') | |
, button = document.querySelector('.copy.button') | |
, symbolsButton = document.querySelector('.settings .symbols') | |
, symbolsCheckBox = document.querySelector('.symbols .material-icons') | |
, slider = document.querySelector('#slider') | |
, sliderValue = document.querySelector('.output .value') | |
// The initial state of the settings | |
, selectionState = { | |
'symbols': false | |
}; | |
/* | |
* The alphanumeric characters and symbols | |
* used to generate passwords | |
*/ | |
const CHARSET = { | |
'numeric' : '0123456789', | |
'alpha': 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', | |
'symbols': '/^_@&[<->]\:{.+},$' | |
}; | |
/* | |
* Load the functions to generate the password | |
*/ | |
window.onload = (e) => { | |
password.value = renderPassword(slider.value, selectionState['symbols']); | |
// Regenerate password on these events | |
changePasswordOnSlide(e); | |
reloadPassword(e); | |
symbolButtonHandler(e); | |
}; | |
/* | |
* Toggle the symbol button between true/false state | |
*/ | |
const symbolButtonHandler = (e) => { | |
symbolsButton.addEventListener('click', (e) => { | |
let renderSymbol = selectionState['symbols'] = !selectionState['symbols']; | |
password.value = renderPassword(slider.value, renderSymbol); | |
// Disable/enable symbols in password | |
symbolsCheckBox.textContent | |
= selectionState['symbols'] | |
? 'radio_button_checked' | |
: 'radio_button_unchecked'; | |
}); | |
} | |
/* | |
* Handle the copy-to-clipboard functionality | |
*/ | |
const copyToClipboard = () => { | |
// Select & copy the password value | |
button.innerText = 'assignment_turned_in'; | |
password.select(); | |
document.execCommand('copy'); | |
// Return to original state | |
setTimeout(() => { | |
button.innerText = 'sync'; | |
headSection.classList.remove('selected'); | |
}, 400); | |
} | |
/* | |
* The logic to generate a random string | |
* | |
* @param charset String characters use for the password | |
* @param len The desired length of the password via the slider | |
*/ | |
const generatePassword = (charset, len) => { | |
var result = ""; | |
for (var i = 0; i < len; i++) { | |
result += charset[Math.floor(Math.random() * 52 + 1)]; | |
} | |
// Ensure we only return the exact length | |
return result.substr(0, len); | |
} | |
/* | |
* The logic to regenerate password when using the slider | |
*/ | |
const changePasswordOnSlide = (e) => { | |
slider.oninput = (e) => { | |
sliderValue.textContent = slider.value; | |
setTimeout(() => { | |
// Generate password on slide | |
password.value = renderPassword(slider.value); | |
headSection.classList.add('selected'); | |
copyToClipboard(); | |
// Return to original state | |
setTimeout(() => { | |
headSection.classList.remove('selected'); | |
}, 400); | |
}, 0) | |
}; | |
} | |
/* | |
* The logic to regenerate password when | |
* the reload button is clicked | |
*/ | |
const reloadPassword = (e) => { | |
button.addEventListener('click', (e) => { | |
setTimeout(() => { | |
copyToClipboard(); | |
}, 0 | |
, password.value = renderPassword(slider.value) | |
, headSection.classList.add('selected') | |
) | |
}) | |
} | |
/* | |
* Determine if the symbol is enabled and merge all of the combined | |
* alphanumeric characters and symbol from the charset | |
* | |
* @param len The desired length of the password via the slider | |
* @param renderSymbols Determined if symbol are included in the password | |
*/ | |
const renderPassword = (len, renderSymbols = selectionState['symbols']) => { | |
let charSet = CHARSET['numeric'].split('').concat( | |
renderSymbols ? CHARSET['symbols'].split('') : [], | |
CHARSET['alpha'].split('') | |
); | |
return generatePassword(charSet, len) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment