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 ****/
|
/**** BASE STYLE ****/
|
||||||
|
* {
|
||||||
|
font: inherit;
|
||||||
|
color: inherit;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
background: radial-gradient(black 15%, transparent 16%) 0 0,
|
background: radial-gradient(black 15%, transparent 16%) 0 0,
|
||||||
radial-gradient(black 15%, transparent 16%) 8px 8px,
|
radial-gradient(black 15%, transparent 16%) 8px 8px,
|
||||||
@ -14,6 +20,7 @@ html {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background: radial-gradient(transparent 16%, black);
|
background: radial-gradient(transparent 16%, black);
|
||||||
|
|
||||||
@ -25,88 +32,129 @@ body {
|
|||||||
|
|
||||||
font-family: 'Exo 2',
|
font-family: 'Exo 2',
|
||||||
"HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica,
|
"HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica,
|
||||||
Arial, "Lucida Grande", sans-serif;
|
Arial, "Lucida Grande", sans-serif;
|
||||||
font-weight: 300;
|
|
||||||
color: #DDD;
|
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 ****/
|
/**** LAYOUT ****/
|
||||||
section {
|
.flex {
|
||||||
display: none;
|
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 {
|
.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">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link rel="stylesheet" href="css/main.css?2">
|
<link rel="stylesheet" href="css/main.css?2">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="flex">
|
||||||
<header>
|
<header>
|
||||||
<p><strong>BETA</strong> - This site may not yet work on your browser.<br>
|
<p>
|
||||||
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>
|
<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>
|
<span id="error"></span>
|
||||||
</header>
|
</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">
|
<main id="main" class="flex">
|
||||||
<h1>Site</h1>
|
<form action="#" id="identity" class="flex form active">
|
||||||
<p>
|
<h1>Identity</h1>
|
||||||
<input id="siteName" type="text" placeholder="Site Name (eg. apple.com)" /><br>
|
<input id="userName" type="text" placeholder="Your Full Name" required />
|
||||||
<span class="small">
|
<input id="masterPassword" type="password" placeholder="Your Master Password" required autocomplete="nope" />
|
||||||
<input id="siteCounter" type="number" placeholder="Site Counter" min="1" max="100" value="1" class="half" />
|
<span class="small flex flex-row">
|
||||||
<select id="siteType" class="half">
|
<label>
|
||||||
<option value="pin">PIN</option>
|
<select id="version">
|
||||||
<option value="short">Short</option>
|
<option value="0">V0</option>
|
||||||
<option value="basic">Basic</option>
|
<option value="1">V1</option>
|
||||||
<option value="medium">Medium</option>
|
<option value="2">V2</option>
|
||||||
<option value="long" selected>Long</option>
|
<option value="3" selected>V3</option>
|
||||||
<option value="maximum">Maximum</option>
|
</select>
|
||||||
<option value="name">Name</option>
|
Algorithm
|
||||||
<option value="phrase">Phrase</option>
|
</label>
|
||||||
</select>
|
<label>
|
||||||
</span><br>
|
<input type="checkbox" id="rememberName" />
|
||||||
<h2 id="sitePassword" title="Your password is ..."></h2><br>
|
Remember Name?
|
||||||
<input id="logout" type="submit" value="⎋" />
|
</label>
|
||||||
<input type="image" src="img/spinner.svg" />
|
</span>
|
||||||
</p>
|
<button type="submit">⏎</button>
|
||||||
</section>
|
</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>
|
<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>
|
<p>
|
||||||
<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>
|
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>
|
||||||
Usage implies agreement with our <a href="https://masterpassword.app/privacy.html">privacy policy and disclaimer</a>.</p>
|
<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>
|
</footer>
|
||||||
|
|
||||||
<!-- Scripts -->
|
<!-- 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/dependencies.js?4"></script>
|
||||||
<script src="js/main.js?3"></script>
|
<script src="js/main.js?3"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,115 +1,92 @@
|
|||||||
var mpw, error;
|
(function() {
|
||||||
|
const errorText = document.getElementById("error");
|
||||||
|
const sitePassword = document.getElementById("sitePassword");
|
||||||
|
|
||||||
function updateMPW() {
|
const identity = document.getElementById("identity");
|
||||||
mpw = null;
|
const userName = document.getElementById("userName");
|
||||||
startWork();
|
const masterPassword = document.getElementById("masterPassword");
|
||||||
mpw = new MPW( $('#userName')[0].value, $('#masterPassword')[0].value, $('#version')[0].value );
|
const version = document.getElementById("version");
|
||||||
mpw.key.then(
|
|
||||||
function() {
|
const site = document.getElementById("site");
|
||||||
doneWork();
|
const siteName = document.getElementById("siteName");
|
||||||
},
|
const siteCounter = document.getElementById("siteCounter");
|
||||||
function(reason) {
|
const siteType = document.getElementById("siteType");
|
||||||
error = reason;
|
|
||||||
|
|
||||||
|
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;
|
mpw = null;
|
||||||
doneWork();
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
}
|
|
||||||
function startWork() {
|
|
||||||
update(true);
|
|
||||||
}
|
|
||||||
function doneWork() {
|
|
||||||
update(false);
|
|
||||||
}
|
|
||||||
function update(working) {
|
|
||||||
var screen = mpw? 'site': 'identity';
|
|
||||||
|
|
||||||
// Screen Name
|
userName.value = null;
|
||||||
if (screen == 'identity') {
|
masterPassword.value = null;
|
||||||
$('#identity').addClass('active');
|
siteName.value = null;
|
||||||
|
sitePassword.textContent = null;
|
||||||
|
|
||||||
if (!working)
|
site.classList.remove('active');
|
||||||
$('#userName').focus();
|
identity.classList.add('active');
|
||||||
}
|
|
||||||
else {
|
|
||||||
$('#identity').removeClass('active');
|
|
||||||
$('#userName')[0].value = $('#masterPassword')[0].value = '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (screen == 'site') {
|
function update(accept, reject) {
|
||||||
$('#site').addClass('active');
|
errorText.textContent = null;
|
||||||
|
main.classList.add("working");
|
||||||
if (!working)
|
return mpw.key
|
||||||
$('#siteName').focus();
|
.then(accept || (x => x))
|
||||||
}
|
.catch(error => Promise.reject(errorText.textContent = error))
|
||||||
else {
|
.catch(reject || (x => Promise.reject(x)))
|
||||||
$('#site').removeClass('active');
|
.finally(() => main.classList.remove("working"))
|
||||||
$('#siteName').val(null);
|
;
|
||||||
$('#sitePassword').text(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Working
|
function selectText() {
|
||||||
if (working && screen == 'identity')
|
if (document.body.createTextRange) { //ms
|
||||||
$('#identity').addClass('working').find('input, select').attr('disabled', 'disabled');
|
const range = document.body.createTextRange();
|
||||||
else
|
range.moveToElementText(this);
|
||||||
$('#identity').removeClass('working').find('input, select').removeAttr('disabled');
|
range.select();
|
||||||
|
} else if (window.getSelection) { //all others
|
||||||
|
const range = document.createRange();
|
||||||
|
range.selectNodeContents(this);
|
||||||
|
|
||||||
if (working && screen == 'site')
|
const selection = window.getSelection();
|
||||||
$('#site').addClass('working');
|
selection.removeAllRanges();
|
||||||
else
|
selection.addRange(range);
|
||||||
$('#site').removeClass('working');
|
}
|
||||||
|
|
||||||
// Error
|
|
||||||
$('#error').text(error);
|
|
||||||
}
|
|
||||||
function updateSite() {
|
|
||||||
if (!mpw) {
|
|
||||||
doneWork();
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
startWork();
|
identity.addEventListener('submit', generateKey);
|
||||||
mpw.generatePassword( $('#siteName')[0].value, $('#siteCounter')[0].valueAsNumber, $('#siteType')[0].value )
|
site.addEventListener('input', generatePassword);
|
||||||
.then( function (sitePassword) {
|
document.getElementById("logout").addEventListener('click', logout);
|
||||||
$('#sitePassword').text(sitePassword);
|
|
||||||
doneWork();
|
|
||||||
}, function (reason) {
|
|
||||||
error = reason;
|
|
||||||
doneWork();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function selectText(element) {
|
|
||||||
var doc = document, range, selection;
|
|
||||||
|
|
||||||
if (doc.body.createTextRange) { //ms
|
sitePassword.addEventListener('click', selectText);
|
||||||
range = doc.body.createTextRange();
|
|
||||||
range.moveToElementText(element);
|
const rememberName = document.getElementById("rememberName");
|
||||||
range.select();
|
rememberName.addEventListener("change", e => userName.autocomplete = (e.target.checked ? "name" : "nope"));
|
||||||
} else if (window.getSelection) { //all others
|
rememberName.dispatchEvent(new Event("change"));
|
||||||
selection = window.getSelection();
|
|
||||||
range = doc.createRange();
|
|
||||||
range.selectNodeContents(element);
|
for (const input of document.querySelectorAll("input[placeholder]")) {
|
||||||
selection.removeAllRanges();
|
input.title = input.placeholder;
|
||||||
selection.addRange(range);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
// Clear values on page refresh
|
||||||
$(function() {
|
logout();
|
||||||
$('#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();
|
|
||||||
});
|
|
||||||
|
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