Merge branch 'web-enhancements' into 'master'
Web client refactor and enhancements See merge request MasterPassword/MasterPassword!252
This commit is contained in:
commit
c3dc4b6725
@ -1,6 +1,12 @@
|
||||
@import url(//fonts.googleapis.com/css?family=Flamenco:300|Exo+2:400,100,900);
|
||||
@import url(https://fonts.googleapis.com/css?family=Flamenco:300|Exo+2:400,100,900);
|
||||
|
||||
/**** BASE STYLE ****/
|
||||
* {
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
html {
|
||||
background: radial-gradient(black 15%, transparent 16%) 0 0,
|
||||
radial-gradient(black 15%, transparent 16%) 8px 8px,
|
||||
@ -14,6 +20,7 @@ html {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
background: radial-gradient(transparent 16%, black);
|
||||
|
||||
@ -25,88 +32,129 @@ body {
|
||||
|
||||
font-family: 'Exo 2',
|
||||
"HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica,
|
||||
Arial, "Lucida Grande", sans-serif;
|
||||
font-weight: 300;
|
||||
Arial, "Lucida Grande", sans-serif;
|
||||
color: #DDD;
|
||||
text-align: center;
|
||||
}
|
||||
h1, h2, h3, h4 {
|
||||
margin: 0;
|
||||
}
|
||||
h1 {
|
||||
font-size: 5em;
|
||||
}
|
||||
a {
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
input, select {
|
||||
background: rgba(0, 0, 0, .3);
|
||||
border: 1px solid #000;
|
||||
border-radius: 4px;
|
||||
|
||||
width: 67%;
|
||||
height: 2em;
|
||||
margin: 1ex 0;
|
||||
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
text-align: center;
|
||||
}
|
||||
input:focus, select:focus {
|
||||
outline: none;
|
||||
box-shadow: 0 0 50px #333;
|
||||
}
|
||||
input.half, select.half {
|
||||
width: 33%;
|
||||
}
|
||||
input.minimal, select.minimal {
|
||||
width: auto;
|
||||
}
|
||||
input[type="submit"], input[type="image"] {
|
||||
background: transparent;
|
||||
border: none;
|
||||
|
||||
width: auto;
|
||||
|
||||
font-size: 2em;
|
||||
}
|
||||
input[type="submit"] {
|
||||
display: inline;
|
||||
}
|
||||
#identity.working input[type="submit"] {
|
||||
display: none;
|
||||
}
|
||||
input[type="image"] {
|
||||
display: none;
|
||||
}
|
||||
#identity.working input[type="image"] {
|
||||
display: inline;
|
||||
}
|
||||
#error {
|
||||
color: red;
|
||||
}
|
||||
header, section, footer {
|
||||
overflow: hidden;
|
||||
clear: both;
|
||||
}
|
||||
header, footer {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
section {
|
||||
font-size: 2.5em;
|
||||
}
|
||||
|
||||
|
||||
/**** LAYOUT ****/
|
||||
section {
|
||||
display: none;
|
||||
.flex {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
|
||||
.flex-row {
|
||||
flex-direction: row;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
section.active {
|
||||
display: block;
|
||||
|
||||
.flex-row > * {
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.flex-row label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
|
||||
}
|
||||
|
||||
.flex-row label > input,
|
||||
.flex-row label > select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.small {
|
||||
font-size: 0.5em;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.form > button,
|
||||
#spinner {
|
||||
font-size: 8rem;
|
||||
background: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#main {
|
||||
font-size: 2rem;
|
||||
width: 67%;
|
||||
font-weight: lighter;
|
||||
}
|
||||
|
||||
.form,
|
||||
.form > * {
|
||||
width: 100%;
|
||||
margin: 1ex 0;
|
||||
}
|
||||
|
||||
#main .form {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#main .form.active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.form > h1 {
|
||||
font-size: 6rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
margin-bottom: .5ex;
|
||||
}
|
||||
|
||||
header,
|
||||
footer {
|
||||
font-size: 0.8rem;
|
||||
margin: .5rem 0;
|
||||
}
|
||||
|
||||
input,
|
||||
select {
|
||||
background: rgba(0, 0, 0, .3);
|
||||
border: 1px solid #000;
|
||||
border-radius: 4px;
|
||||
height: 2em;
|
||||
}
|
||||
|
||||
input:focus,
|
||||
select:focus,
|
||||
button:focus,
|
||||
button:hover {
|
||||
outline: none;
|
||||
box-shadow: 0 0 50px #333;
|
||||
}
|
||||
|
||||
option {
|
||||
background: black;
|
||||
}
|
||||
|
||||
#spinner {
|
||||
display: none;
|
||||
height: 1.5em;
|
||||
}
|
||||
|
||||
#main.working .form > button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#main.working #spinner {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#sitePassword {
|
||||
margin: 0;
|
||||
font-size: 4rem;
|
||||
padding: 1rem;
|
||||
border: solid 1px gray;
|
||||
}
|
||||
|
||||
#sitePassword:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#error {
|
||||
color: red;
|
||||
}
|
||||
|
@ -9,62 +9,77 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="css/main.css?2">
|
||||
</head>
|
||||
<body>
|
||||
<body class="flex">
|
||||
<header>
|
||||
<p><strong>BETA</strong> - This site may not yet work on your browser.<br>
|
||||
Passwords are generated locally, your master password is not sent to any server. See <a href="https://gitlab.com/MasterPassword/MasterPassword/tree/master/platform-independent/web">the source</a>.</p>
|
||||
<p>
|
||||
<strong>BETA</strong> - This site may not yet work on your browser.<br>
|
||||
Passwords are generated locally, your master password is not sent to any server. See <a href="https://gitlab.com/MasterPassword/MasterPassword/tree/master/platform-independent/web">the source</a>.
|
||||
</p>
|
||||
<span id="error"></span>
|
||||
</header>
|
||||
<section id="identity" class="active"><form action="#">
|
||||
<h1>Identity</h1>
|
||||
<p>
|
||||
<input id="userName" type="text" placeholder="Your Full Name" /><br>
|
||||
<input id="masterPassword" type="password" placeholder="Your Master Password" /><br>
|
||||
<input type="submit" value="⏎" /><br>
|
||||
<input type="image" src="img/spinner.svg" />
|
||||
<select id="version" class="minimal">
|
||||
<option value="0">V0</option>
|
||||
<option value="1">V1</option>
|
||||
<option value="2">V2</option>
|
||||
<option value="3" selected>V3</option>
|
||||
</select>
|
||||
</p>
|
||||
</form></section>
|
||||
|
||||
<section id="site">
|
||||
<h1>Site</h1>
|
||||
<p>
|
||||
<input id="siteName" type="text" placeholder="Site Name (eg. apple.com)" /><br>
|
||||
<span class="small">
|
||||
<input id="siteCounter" type="number" placeholder="Site Counter" min="1" max="100" value="1" class="half" />
|
||||
<select id="siteType" class="half">
|
||||
<option value="pin">PIN</option>
|
||||
<option value="short">Short</option>
|
||||
<option value="basic">Basic</option>
|
||||
<option value="medium">Medium</option>
|
||||
<option value="long" selected>Long</option>
|
||||
<option value="maximum">Maximum</option>
|
||||
<option value="name">Name</option>
|
||||
<option value="phrase">Phrase</option>
|
||||
</select>
|
||||
</span><br>
|
||||
<h2 id="sitePassword" title="Your password is ..."></h2><br>
|
||||
<input id="logout" type="submit" value="⎋" />
|
||||
<input type="image" src="img/spinner.svg" />
|
||||
</p>
|
||||
</section>
|
||||
<main id="main" class="flex">
|
||||
<form action="#" id="identity" class="flex form active">
|
||||
<h1>Identity</h1>
|
||||
<input id="userName" type="text" placeholder="Your Full Name" required />
|
||||
<input id="masterPassword" type="password" placeholder="Your Master Password" required autocomplete="nope" />
|
||||
<span class="small flex flex-row">
|
||||
<label>
|
||||
<select id="version">
|
||||
<option value="0">V0</option>
|
||||
<option value="1">V1</option>
|
||||
<option value="2">V2</option>
|
||||
<option value="3" selected>V3</option>
|
||||
</select>
|
||||
Algorithm
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" id="rememberName" />
|
||||
Remember Name?
|
||||
</label>
|
||||
</span>
|
||||
<button type="submit">⏎</button>
|
||||
</form>
|
||||
|
||||
<section id="site" class="flex form">
|
||||
<h1>Site</h1>
|
||||
<input id="siteName" type="text" placeholder="Site Name (eg. apple.com)" inputmode="url" />
|
||||
<span class="small flex flex-row">
|
||||
<label>
|
||||
<select id="siteType">
|
||||
<option value="pin">PIN</option>
|
||||
<option value="short">Short</option>
|
||||
<option value="basic">Basic</option>
|
||||
<option value="medium">Medium</option>
|
||||
<option value="long" selected>Long</option>
|
||||
<option value="maximum">Maximum</option>
|
||||
<option value="name">Name</option>
|
||||
<option value="phrase">Phrase</option>
|
||||
</select>
|
||||
Type
|
||||
</label>
|
||||
<label>
|
||||
<input id="siteCounter" type="number" placeholder="Site Counter" min="1" max="100" value="1" />
|
||||
Counter
|
||||
</label>
|
||||
</span>
|
||||
<button id="logout">⎋</button>
|
||||
</section>
|
||||
|
||||
<img id="spinner" src="img/spinner.svg" />
|
||||
<h2 id="sitePassword" title="Your password is ..."></h2>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>This page was made possible thanks to <a href="https://tomthorogood.co.uk/" title="Tom Thorogood">tmthrgd</a>'s <a href="https://github.com/tmthrgd/mpw-js">mpw-js</a>.<br>
|
||||
<em><a href="https://masterpassword.app">Master Password</a> is a security product and algorithm by <a href="http://www.lhunath.com" title="Maarten Billemont">Maarten Billemont</a>, <a href="http://www.lyndir.com">Lyndir</a> (© 2011-2014).</em><br>
|
||||
Usage implies agreement with our <a href="https://masterpassword.app/privacy.html">privacy policy and disclaimer</a>.</p>
|
||||
<p>
|
||||
This page was made possible thanks to <a href="https://tomthorogood.co.uk/" title="Tom Thorogood">tmthrgd</a>'s <a href="https://github.com/tmthrgd/mpw-js">mpw-js</a>.<br>
|
||||
<em><a href="https://masterpassword.app">Master Password</a> is a security product and algorithm by <a href="http://www.lhunath.com" title="Maarten Billemont">Maarten Billemont</a>, <a href="http://www.lyndir.com">Lyndir</a> (© 2011-2014).</em><br>
|
||||
Usage implies agreement with our <a href="https://masterpassword.app/privacy.html">privacy policy and disclaimer</a>.
|
||||
</p>
|
||||
</footer>
|
||||
|
||||
<!-- Scripts -->
|
||||
<script>document.write('<script src="js/vendor/jquery-2.1.1.min.js"><\/script>')</script>
|
||||
<script>window.jQuery || document.write('<script src="js/vendor/jquery-2.1.1.js"><\/script>')</script>
|
||||
<script src="js/dependencies.js?4"></script>
|
||||
<script src="js/main.js?3"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
@ -1,115 +1,92 @@
|
||||
var mpw, error;
|
||||
(function() {
|
||||
const errorText = document.getElementById("error");
|
||||
const sitePassword = document.getElementById("sitePassword");
|
||||
|
||||
function updateMPW() {
|
||||
mpw = null;
|
||||
startWork();
|
||||
mpw = new MPW( $('#userName')[0].value, $('#masterPassword')[0].value, $('#version')[0].value );
|
||||
mpw.key.then(
|
||||
function() {
|
||||
doneWork();
|
||||
},
|
||||
function(reason) {
|
||||
error = reason;
|
||||
const identity = document.getElementById("identity");
|
||||
const userName = document.getElementById("userName");
|
||||
const masterPassword = document.getElementById("masterPassword");
|
||||
const version = document.getElementById("version");
|
||||
|
||||
const site = document.getElementById("site");
|
||||
const siteName = document.getElementById("siteName");
|
||||
const siteCounter = document.getElementById("siteCounter");
|
||||
const siteType = document.getElementById("siteType");
|
||||
|
||||
|
||||
let mpw;
|
||||
function generateKey(e) {
|
||||
e.preventDefault();
|
||||
|
||||
mpw = new MPW(userName.value, masterPassword.value, version.value);
|
||||
update(() => {
|
||||
identity.classList.remove('active');
|
||||
site.classList.add('active');
|
||||
siteName.select();
|
||||
});
|
||||
}
|
||||
|
||||
function generatePassword() {
|
||||
update(() => mpw.generatePassword(siteName.value, siteCounter.valueAsNumber, siteType.value))
|
||||
.then(pass => sitePassword.textContent = pass)
|
||||
.catch(_ => sitePassword.textContent = null);
|
||||
}
|
||||
|
||||
function logout() {
|
||||
if (mpw) {
|
||||
mpw.invalidate();
|
||||
mpw = null;
|
||||
doneWork();
|
||||
}
|
||||
);
|
||||
}
|
||||
function startWork() {
|
||||
update(true);
|
||||
}
|
||||
function doneWork() {
|
||||
update(false);
|
||||
}
|
||||
function update(working) {
|
||||
var screen = mpw? 'site': 'identity';
|
||||
|
||||
// Screen Name
|
||||
if (screen == 'identity') {
|
||||
$('#identity').addClass('active');
|
||||
userName.value = null;
|
||||
masterPassword.value = null;
|
||||
siteName.value = null;
|
||||
sitePassword.textContent = null;
|
||||
|
||||
if (!working)
|
||||
$('#userName').focus();
|
||||
}
|
||||
else {
|
||||
$('#identity').removeClass('active');
|
||||
$('#userName')[0].value = $('#masterPassword')[0].value = '';
|
||||
site.classList.remove('active');
|
||||
identity.classList.add('active');
|
||||
}
|
||||
|
||||
if (screen == 'site') {
|
||||
$('#site').addClass('active');
|
||||
|
||||
if (!working)
|
||||
$('#siteName').focus();
|
||||
}
|
||||
else {
|
||||
$('#site').removeClass('active');
|
||||
$('#siteName').val(null);
|
||||
$('#sitePassword').text(null);
|
||||
function update(accept, reject) {
|
||||
errorText.textContent = null;
|
||||
main.classList.add("working");
|
||||
return mpw.key
|
||||
.then(accept || (x => x))
|
||||
.catch(error => Promise.reject(errorText.textContent = error))
|
||||
.catch(reject || (x => Promise.reject(x)))
|
||||
.finally(() => main.classList.remove("working"))
|
||||
;
|
||||
}
|
||||
|
||||
// Working
|
||||
if (working && screen == 'identity')
|
||||
$('#identity').addClass('working').find('input, select').attr('disabled', 'disabled');
|
||||
else
|
||||
$('#identity').removeClass('working').find('input, select').removeAttr('disabled');
|
||||
function selectText() {
|
||||
if (document.body.createTextRange) { //ms
|
||||
const range = document.body.createTextRange();
|
||||
range.moveToElementText(this);
|
||||
range.select();
|
||||
} else if (window.getSelection) { //all others
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(this);
|
||||
|
||||
if (working && screen == 'site')
|
||||
$('#site').addClass('working');
|
||||
else
|
||||
$('#site').removeClass('working');
|
||||
|
||||
// Error
|
||||
$('#error').text(error);
|
||||
}
|
||||
function updateSite() {
|
||||
if (!mpw) {
|
||||
doneWork();
|
||||
return
|
||||
const selection = window.getSelection();
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
}
|
||||
}
|
||||
|
||||
startWork();
|
||||
mpw.generatePassword( $('#siteName')[0].value, $('#siteCounter')[0].valueAsNumber, $('#siteType')[0].value )
|
||||
.then( function (sitePassword) {
|
||||
$('#sitePassword').text(sitePassword);
|
||||
doneWork();
|
||||
}, function (reason) {
|
||||
error = reason;
|
||||
doneWork();
|
||||
});
|
||||
}
|
||||
function selectText(element) {
|
||||
var doc = document, range, selection;
|
||||
identity.addEventListener('submit', generateKey);
|
||||
site.addEventListener('input', generatePassword);
|
||||
document.getElementById("logout").addEventListener('click', logout);
|
||||
|
||||
if (doc.body.createTextRange) { //ms
|
||||
range = doc.body.createTextRange();
|
||||
range.moveToElementText(element);
|
||||
range.select();
|
||||
} else if (window.getSelection) { //all others
|
||||
selection = window.getSelection();
|
||||
range = doc.createRange();
|
||||
range.selectNodeContents(element);
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
sitePassword.addEventListener('click', selectText);
|
||||
|
||||
const rememberName = document.getElementById("rememberName");
|
||||
rememberName.addEventListener("change", e => userName.autocomplete = (e.target.checked ? "name" : "nope"));
|
||||
rememberName.dispatchEvent(new Event("change"));
|
||||
|
||||
|
||||
for (const input of document.querySelectorAll("input[placeholder]")) {
|
||||
input.title = input.placeholder;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$(function() {
|
||||
$('#identity form').on('submit', function() {
|
||||
updateMPW();
|
||||
return false;
|
||||
});
|
||||
$('#site input, #site select').on('change input keyup', function() {
|
||||
updateSite();
|
||||
});
|
||||
$('#logout').on('click', function() {
|
||||
mpw = null;
|
||||
doneWork();
|
||||
});
|
||||
$('#sitePassword').on('click', function() {
|
||||
selectText(this);
|
||||
});
|
||||
|
||||
doneWork();
|
||||
});
|
||||
// Clear values on page refresh
|
||||
logout();
|
||||
})();
|
||||
|
9190
platform-independent/web/js/vendor/jquery-2.1.1.js
vendored
9190
platform-independent/web/js/vendor/jquery-2.1.1.js
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user