2017-08-28 12:27:45 -04:00
<!DOCTYPE html>
<html dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
<meta name="viewport" content="user-scalable=1.0,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="format-detection" content="telephone=no" />
2019-03-30 18:48:21 -04:00
<link type="text/css" href="styles/style.css" media="screen" rel="stylesheet" title="CSS" />
2017-08-28 12:27:45 -04:00
<script type="text/javascript" src="scripts/common-0.0.1.js"></script>
2019-03-26 03:14:45 -04:00
<script keeplink=1 type="text/javascript" src="scripts/u2f-api.js"></script>
2019-03-30 18:48:21 -04:00
<title> {{{ title }}} - Login</title>
2017-08-28 12:27:45 -04:00
</head>
2019-03-30 18:48:21 -04:00
<body id="body" onload="if (typeof(startup) !== 'undefined') startup();" class="arg_hide">
<div id=container>
2017-08-28 12:27:45 -04:00
<div id=mastheadx></div>
2019-03-30 18:48:21 -04:00
<div id=masthead>
<div class="title"> {{{ title }}} </div>
<div class="title2"> {{{ title2 }}} </div>
2017-08-28 12:27:45 -04:00
</div>
2019-03-30 18:48:21 -04:00
<div id=page_content>
<div id=topbar class="noselect style3">
<div id=toggle title="Toggle full width" onclick="toggleFullScreen(1)">↔</div>
2018-09-17 16:44:14 -04:00
</div>
2019-04-09 18:51:05 -04:00
<div id=column_l style="height:calc(100vh - 111px)">
2017-08-28 12:27:45 -04:00
<h1>Welcome</h1>
2019-02-13 16:22:52 -05:00
<div id="welcomeText" style="display:none">Connect to your home or office devices from anywhere in the world using <a href="http://www.meshcommander.com/meshcentral2">MeshCentral</a>, the real time, open source remote monitoring and management web site. You will need to download and install a management agent on your computers. Once installed, computers will show up in the "My Devices" section of this web site and you will be able to monitor them and take control of them.</div>
2019-03-30 18:48:21 -04:00
<table id="centralTable" style="">
2017-08-28 12:27:45 -04:00
<tr>
2019-04-04 17:01:35 -04:00
<td id="welcomeimage">
2018-11-02 21:13:32 -04:00
<picture>
2019-03-30 18:48:21 -04:00
<img alt="" src=welcome.jpg />
2018-11-02 21:13:32 -04:00
</picture>
2017-08-28 12:27:45 -04:00
</td>
2019-03-30 18:48:21 -04:00
<td id="logincell">
<div id=loginpanel style="display:none">
2017-08-28 12:27:45 -04:00
<form action="login" method=post>
<div id=message1>
{{{ message }}}
</div>
<div>
<b>Log In</b>
</div>
<table>
<tr>
2017-09-08 14:37:37 -04:00
<td align=right width=100>Username:</td>
2018-04-05 19:45:56 -04:00
<td><input id=username type=text maxlength=64 name=username onchange=validateLogin(1) onkeyup=validateLogin(1,event) /></td>
2017-08-28 12:27:45 -04:00
</tr>
<tr>
2017-09-08 14:37:37 -04:00
<td align=right>Password:</td>
2018-04-05 19:45:56 -04:00
<td><input id=password type=password maxlength=256 name=password autocomplete=off onchange=validateLogin(2) onkeyup=validateLogin(2,event) /></td>
2017-08-28 12:27:45 -04:00
</tr>
<tr>
2017-09-08 14:37:37 -04:00
<td><div id=showPassHintLink style=display:none><a onclick=showPassHint() style="cursor:pointer">Show Hint</a></div></td>
2017-08-28 12:27:45 -04:00
<td align=right><input id=loginButton type=submit value="Log In" disabled="disabled" /></td>
</tr>
</table>
2017-12-12 19:04:54 -05:00
<div id="hrAccountDiv" style="display:none"><hr /></div>
<div id="resetAccountDiv" style="display:none;padding:2px">
Forgot username/password? <a onclick=xgo(3) style=cursor:pointer>Reset account</a>.
</div>
<div id="newAccountDiv" style="display:none;padding:2px">
Don't have an account? <a onclick=xgo(2) style=cursor:pointer>Create one</a>.
2017-08-28 12:27:45 -04:00
</div>
</form>
</div>
2019-03-30 18:48:21 -04:00
<div id=createpanel style="display:none">
2017-09-08 14:37:37 -04:00
<form action=createaccount method=post>
2017-08-28 12:27:45 -04:00
<div id=message2>
{{{ message }}}
</div>
<div>
<b>Account Creation</b>
</div>
2019-03-30 18:48:21 -04:00
<div id="passwordPolicyCallout" style="display:none;"></div>
2017-08-28 12:27:45 -04:00
<table>
<tr>
2018-08-22 19:18:01 -04:00
<td id="nuUser" align=right width=100>Username:</td>
2018-04-05 19:45:56 -04:00
<td><input id=ausername type=text name=username onchange=validateCreate(1) maxlength=64 onkeydown=haltReturn(event) onkeyup=validateCreate(1,event) /></td>
2017-08-28 12:27:45 -04:00
</tr>
<tr>
2018-08-22 19:18:01 -04:00
<td id="nuEmail" align=right width=100>Email:</td>
2018-04-05 19:45:56 -04:00
<td><input id=aemail type=text name=email onchange=validateCreate(2) maxlength=256 onkeydown=haltReturn(event) onkeyup=validateCreate(2,event) /></td>
2017-08-28 12:27:45 -04:00
</tr>
<tr>
2018-08-22 19:18:01 -04:00
<td id="nuPass1" align=right>Password:</td>
2019-02-27 21:48:50 -05:00
<td><input id=apassword1 type=password name=password1 autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(3,event) onkeyup=validateCreate(3,event) /></td>
2017-08-28 12:27:45 -04:00
</tr>
<tr>
2018-08-22 19:18:01 -04:00
<td id="nuPass2" align=right>Password:</td>
2019-02-27 21:48:50 -05:00
<td><input id=apassword2 type=password name=password2 autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(4,event) onkeyup=validateCreate(4,event) /></td>
2017-09-08 14:37:37 -04:00
</tr>
2019-02-28 19:17:22 -05:00
<tr id="createPanelHint" style="display:none">
2018-08-22 19:18:01 -04:00
<td id="nuHint" align=right>Password Hint:</td>
2019-02-27 21:48:50 -05:00
<td><input id=apasswordhint type=text name=apasswordhint autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(5,event) onkeyup=validateCreate(5,event) /></td>
2017-08-28 12:27:45 -04:00
</tr>
2017-09-15 14:45:06 -04:00
<tr id=newAccountPass title="Enter the account creation token">
2018-08-22 19:18:01 -04:00
<td id="nuToken" align=right>Creation Token:</td>
2019-02-27 21:48:50 -05:00
<td><input id=anewaccountpass type=password name=anewaccountpass autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(6,event) onkeyup=validateCreate(6,event) /></td>
2017-09-15 14:45:06 -04:00
</tr>
2017-08-28 12:27:45 -04:00
<tr>
<td colspan=2>
<div style=float:right><input id=createButton type=submit value="Create Account" disabled="disabled" /></div>
<div id=passWarning style="padding-top:6px"></div>
</td>
</tr>
</table>
<hr /><a onclick=xgo(1) style=cursor:pointer>Back to login</a>
</form>
</div>
2019-03-30 18:48:21 -04:00
<div id=resetpanel style="display:none">
2017-12-12 19:04:54 -05:00
<form action=resetaccount method=post>
<div id=message3>
{{{ message }}}
</div>
<div>
<b>Account Reset</b>
</div>
<table>
<tr>
<td align=right width=100>Email:</td>
2018-04-05 19:45:56 -04:00
<td><input id=remail type=text name=email maxlength=256 onchange=validateReset() onkeyup=validateReset(event) /></td>
2017-12-12 19:04:54 -05:00
</tr>
<tr>
<td colspan=2>
<div style=float:right><input id=eresetButton type=submit value="Reset Account" disabled="disabled" /></div>
<div id=passWarning style="padding-top:6px"></div>
</td>
</tr>
</table>
<hr /><a onclick=xgo(1) style=cursor:pointer>Back to login</a>
</form>
</div>
2019-03-30 18:48:21 -04:00
<div id=tokenpanel style="display:none">
2019-01-15 21:21:03 -05:00
<form action=tokenlogin method=post autocomplete=off>
<div id=message4>
{{{ message }}}
</div>
<table>
<tr>
<td align=right width=100>Login token:</td>
2019-02-08 01:30:33 -05:00
<td>
2019-02-19 18:38:27 -05:00
<input id=tokenInput type=text name=token maxlength=50 onchange=checkToken(event) onpaste=resetCheckToken(event) onkeyup=checkToken(event) onkeydown=checkToken(event) />
2019-02-10 19:04:36 -05:00
<input id=hwtokenInput type=text name=hwtoken style="display:none" />
2019-02-08 01:30:33 -05:00
</td>
2019-01-15 21:21:03 -05:00
</tr>
<tr>
<td colspan=2>
<div style=float:right><input id=tokenOkButton type=submit value="Login" disabled="disabled" /></div>
</td>
</tr>
</table>
<hr /><a onclick=xgo(1) style=cursor:pointer>Back to login</a>
</form>
</div>
2019-03-30 18:48:21 -04:00
<div id=resettokenpanel style="display:none">
2019-02-11 17:41:15 -05:00
<form action=resetaccount method=post>
<div id=message5>
{{{ message }}}
</div>
<table>
<tr>
<td align=right width=100>Login token:</td>
<td>
<input id=resetTokenInput type=text name=token maxlength=50 onchange=resetCheckToken(event) onkeyup=resetCheckToken(event) onkeydown=resetCheckToken(event) />
<input id=resetHwtokenInput type=text name=hwtoken style="display:none" />
</td>
</tr>
<tr>
<td colspan=2>
<div style=float:right><input id=resetTokenOkButton type=submit value="Login" disabled="disabled" /></div>
</td>
</tr>
</table>
<hr /><a onclick=xgo(1) style=cursor:pointer>Back to login</a>
</form>
</div>
2019-03-30 18:48:21 -04:00
<div id=resetpasswordpanel style="display:none">
2019-02-27 21:48:50 -05:00
<form action=resetpassword method=post>
<div id=message6>
{{{ message }}}
</div>
2019-03-30 18:48:21 -04:00
<div id="rpasswordPolicyCallout" style="display:none;"></div>
2019-02-27 21:48:50 -05:00
<table>
<tr>
<td id="rnuPass1" width=100 align=right>Password:</td>
<td><input id=rapassword1 type=password name=rpassword1 autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validatePassReset(3,event) onkeyup=validatePassReset(3,event) /></td>
</tr>
<tr>
<td id="rnuPass2" align=right>Password:</td>
<td><input id=rapassword2 type=password name=rpassword2 autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validatePassReset(4,event) onkeyup=validatePassReset(4,event) /></td>
</tr>
2019-02-28 19:17:22 -05:00
<tr id="resetpasswordpanelHint" style="display:none">
2019-02-27 21:48:50 -05:00
<td id="rnuHint" align=right>Password Hint:</td>
<td><input id=rapasswordhint type=text name=rpasswordhint autocomplete=off maxlength=256 onkeydown=haltReturn(event) onchange=validatePassReset(5,event) onkeyup=validatePassReset(5,event) /></td>
</tr>
<tr>
<td colspan=2>
<div style=float:right><input id=resetPassButton type=submit value="Reset Password" disabled="disabled" /></div>
<div id=rpassWarning style="padding-top:6px"></div>
</td>
</tr>
</table>
<hr /><a onclick=xgo(1) style=cursor:pointer>Back to login</a>
</form>
</div>
2017-08-28 12:27:45 -04:00
</td>
</tr>
</table>
<br />
</div>
2019-03-30 18:48:21 -04:00
<div id="footer">
<div class="footer1"> {{{ footer }}} </div>
<div class="footer2">
{{{ rootCertLink }}}
<a href=terms>Terms & Privacy</a>
</div>
2017-08-28 12:27:45 -04:00
</div>
</div>
</div>
2019-03-30 18:48:21 -04:00
<div id=dialog style="display:none">
<div id=dialogHeader>
2017-09-08 14:37:37 -04:00
<div id=id_dialogclose style=float:right;padding:5px;cursor:pointer onclick=setDialogMode()><b>X</b></div>
<div id=id_dialogtitle style=padding:5px></div>
<div style=width:100%;margin:6px></div>
</div>
2019-03-30 18:48:21 -04:00
<div id=dialogBody>
<div id=dialog1>
<div id=id_dialogMessage style=""></div>
2017-09-08 14:37:37 -04:00
</div>
2019-03-30 18:48:21 -04:00
<div id=dialog2 style="">
2017-09-08 14:37:37 -04:00
<div id=id_dialogOptions></div>
</div>
</div>
2019-03-30 18:48:21 -04:00
<div id="idx_dlgButtonBar" style="">
<input id="idx_dlgCancelButton" type="button" value="Cancel" style="" onclick="dialogclose(0)">
<input id="idx_dlgOkButton" type="button" value="OK" style="" onclick="dialogclose(1)">
2017-09-08 14:37:37 -04:00
</div>
</div>
2017-08-28 12:27:45 -04:00
<script>
2018-08-27 15:24:15 -04:00
'use strict';
2017-09-08 14:37:37 -04:00
var passhint = " {{{ passhint }}} ";
2018-08-23 19:55:06 -04:00
var newAccountPass = parseInt(' {{{ newAccountPass }}} ');
2018-09-06 18:58:34 -04:00
var emailCheck = (' {{{ emailcheck }}} ' == 'true');
2018-12-20 17:14:37 -05:00
var passRequirements = " {{{ passRequirements }}} ";
2019-03-25 17:43:58 -04:00
var hardwareKeyChallenge = decodeURIComponent(' {{{ hkey }}} ');
2019-02-28 19:17:22 -05:00
if (passRequirements != "") { passRequirements = JSON.parse(decodeURIComponent(passRequirements)); } else { passRequirements = { }; }
var passRequirementsEx = ((passRequirements.min != null) || (passRequirements.max != null) || (passRequirements.upper != null) || (passRequirements.lower != null) || (passRequirements.numeric != null) || (passRequirements.nonalpha != null));
2018-08-23 19:55:06 -04:00
var features = parseInt(' {{{ features }}} ');
2018-09-17 16:44:14 -04:00
var webPageFullScreen = getstore('webPageFullScreen', true);
if (webPageFullScreen == 'false') { webPageFullScreen = false; }
if (webPageFullScreen == 'true') { webPageFullScreen = true; }
2019-02-13 16:22:52 -05:00
var welcomeText = decodeURIComponent(" {{{ welcometext }}} ");
2019-02-28 19:17:22 -05:00
var currentpanel = 0;
2018-09-17 16:44:14 -04:00
toggleFullScreen();
2017-09-08 14:37:37 -04:00
2017-08-28 12:27:45 -04:00
function startup() {
2017-12-19 11:50:19 -05:00
if ((features & 32) == 0) {
// Guard against other site's top frames (web bugs).
var loc = null;
try { loc = top.location.toString().toLowerCase(); } catch (e) { }
if (top != self && (loc == null || top.active == false)) { top.location = self.location; return; }
}
2019-02-28 19:17:22 -05:00
QV('createPanelHint', passRequirements.hint === true);
QV('resetpasswordpanelHint', passRequirements.hint === true);
2019-02-13 16:22:52 -05:00
// Display the welcome text
if (welcomeText) { QH('welcomeText', welcomeText); }
QV('welcomeText', true);
2017-09-08 14:37:37 -04:00
window.onresize = center;
center();
2019-02-13 16:22:52 -05:00
2017-08-28 12:27:45 -04:00
validateLogin();
validateCreate();
2018-08-23 19:55:06 -04:00
if (' {{ log in mode }} ' != '') { go(parseInt(' {{ log in mode }} ')); } else { go(1); }
2018-12-02 02:41:57 -05:00
QV('newAccountDiv', (' {{{ newAccount }}} ' != '0') && (' {{{ newAccount }}} ' != 'false')); // If new accounts are not allowed, don't display the new account link.
2017-09-08 14:37:37 -04:00
if ((passhint != null) && (passhint.length > 0)) { QV("showPassHintLink", true); }
2017-09-20 17:44:22 -04:00
QV("newAccountPass", (newAccountPass == 1));
2017-12-12 19:04:54 -05:00
QV("resetAccountDiv", (emailCheck == true));
2017-12-14 17:57:52 -05:00
QV("hrAccountDiv", (emailCheck == true) || (newAccountPass == 1));
2019-02-08 01:30:33 -05:00
if (' {{ log in mode }} ' == '4') {
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
2019-03-23 01:33:53 -04:00
if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) {
2019-03-26 03:14:45 -04:00
hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer;
2019-03-23 01:33:53 -04:00
2019-03-26 03:14:45 -04:00
var publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout }
2019-03-23 01:33:53 -04:00
for (var i = 0; i < hardwareKeyChallenge.keyIds.length; i++) {
publicKeyCredentialRequestOptions.allowCredentials.push(
2019-03-26 03:14:45 -04:00
{ id: Uint8Array.from(atob(hardwareKeyChallenge.keyIds[i]), function (c) { return c.charCodeAt(0) }), type: 'public-key', transports: ['usb', 'ble', 'nfc'], }
2019-03-23 01:33:53 -04:00
);
}
// New WebAuthn hardware keys
navigator.credentials.get( { publicKey: publicKeyCredentialRequestOptions }).then(
function (rawAssertion) {
var assertion = {
2019-03-25 14:32:16 -04:00
id: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.rawId))),
clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.clientDataJSON))),
userHandle: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.userHandle))),
signature: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.signature))),
authenticatorData: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.authenticatorData))),
2019-03-23 01:33:53 -04:00
};
2019-03-23 15:14:53 -04:00
Q('hwtokenInput').value = JSON.stringify(assertion);
QE('tokenOkButton', true);
Q('tokenOkButton').click();
2019-03-23 01:33:53 -04:00
},
2019-03-23 15:14:53 -04:00
function (error) { console.log('credentials-get error', error); }
2019-03-23 01:33:53 -04:00
);
} else if ((hardwareKeyChallenge != null) && u2fSupported()) {
// Old U2F hardware keys
2019-02-10 19:04:36 -05:00
window.u2f.sign(hardwareKeyChallenge.appId, hardwareKeyChallenge.challenge, hardwareKeyChallenge.registeredKeys, function (authResponse) {
2019-02-28 19:17:22 -05:00
if ((currentpanel == 4) && authResponse.signatureData) {
2019-02-10 19:04:36 -05:00
Q('hwtokenInput').value = JSON.stringify(authResponse);
2019-02-08 01:30:33 -05:00
QE('tokenOkButton', true);
Q('tokenOkButton').click();
}
2019-02-10 19:04:36 -05:00
}, hardwareKeyChallenge.timeoutSeconds);
2019-02-08 01:30:33 -05:00
}
}
2019-02-11 17:41:15 -05:00
if (' {{ log in mode }} ' == '5') {
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
2019-03-25 14:32:16 -04:00
if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) {
2019-03-26 03:14:45 -04:00
hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer;
2019-03-25 14:32:16 -04:00
2019-03-26 03:14:45 -04:00
var publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout }
2019-03-25 14:32:16 -04:00
for (var i = 0; i < hardwareKeyChallenge.keyIds.length; i++) {
publicKeyCredentialRequestOptions.allowCredentials.push(
2019-03-26 03:14:45 -04:00
{ id: Uint8Array.from(atob(hardwareKeyChallenge.keyIds[i]), function (c) { return c.charCodeAt(0) }), type: 'public-key', transports: ['usb', 'ble', 'nfc'], }
2019-03-25 14:32:16 -04:00
);
}
// New WebAuthn hardware keys
navigator.credentials.get( { publicKey: publicKeyCredentialRequestOptions }).then(
function (rawAssertion) {
var assertion = {
id: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.rawId))),
clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.clientDataJSON))),
userHandle: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.userHandle))),
signature: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.signature))),
authenticatorData: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.authenticatorData))),
};
Q('resetHwtokenInput').value = JSON.stringify(assertion);
QE('resetTokenOkButton', true);
Q('resetTokenOkButton').click();
},
function (error) { console.log('credentials-get error', error); }
);
} else if ((hardwareKeyChallenge != null) && u2fSupported()) {
// Old U2F hardware keys
2019-02-11 17:41:15 -05:00
window.u2f.sign(hardwareKeyChallenge.appId, hardwareKeyChallenge.challenge, hardwareKeyChallenge.registeredKeys, function (authResponse) {
2019-02-28 19:17:22 -05:00
if ((currentpanel == 5) && authResponse.signatureData) {
2019-02-11 17:41:15 -05:00
Q('resetHwtokenInput').value = JSON.stringify(authResponse);
QE('resetTokenOkButton', true);
Q('resetTokenOkButton').click();
}
}, hardwareKeyChallenge.timeoutSeconds);
}
}
2017-09-08 14:37:37 -04:00
}
function showPassHint() {
messagebox("Password Hint", passhint);
2017-08-28 12:27:45 -04:00
}
function xgo(x) {
QV('message1', false);
QV('message2', false);
2019-02-11 17:41:15 -05:00
QV('message3', false);
QV('message4', false);
QV('message5', false);
2019-02-27 21:48:50 -05:00
QV('message6', false);
2017-08-28 12:27:45 -04:00
go(x);
}
function go(x) {
2019-02-28 19:17:22 -05:00
currentpanel = x;
2017-09-13 14:25:57 -04:00
setDialogMode(0);
2017-09-08 14:37:37 -04:00
QV("showPassHintLink", false);
2017-08-28 12:27:45 -04:00
QV('loginpanel', x == 1);
QV('createpanel', x == 2);
2017-12-12 19:04:54 -05:00
QV('resetpanel', x == 3);
2019-01-15 21:21:03 -05:00
QV('tokenpanel', x == 4);
2019-02-11 17:41:15 -05:00
QV('resettokenpanel', x == 5);
2019-02-27 21:48:50 -05:00
QV('resetpasswordpanel', x == 6);
2017-12-14 17:57:52 -05:00
if (x == 1) { Q('username').focus(); }
if (x == 2) { Q('ausername').focus(); }
if (x == 3) { Q('remail').focus(); }
2019-01-15 21:21:03 -05:00
if (x == 4) { Q('tokenInput').focus(); }
2019-02-27 21:48:50 -05:00
if (x == 5) { Q('resetTokenInput').focus(); }
if (x == 6) { Q('rapassword1').focus(); }
2017-08-28 12:27:45 -04:00
}
2017-12-14 17:57:52 -05:00
function validateLogin(box, e) {
2018-05-16 18:49:12 -04:00
var ok = ((Q('username').value.length > 0) && (Q('username').value.indexOf(' ') == -1) && (Q('password').value.length > 0));
2017-08-28 12:27:45 -04:00
QE('loginButton', ok);
2017-09-13 14:25:57 -04:00
setDialogMode(0);
2018-05-16 18:49:12 -04:00
if ((e != null) && (e.keyCode == 13)) { if (box == 1) { Q('password').focus(); } else if (box == 2) { Q('loginButton').click(); } }
2017-12-14 17:57:52 -05:00
if (e != null) { haltEvent(e); }
2017-08-28 12:27:45 -04:00
}
2018-12-20 17:14:37 -05:00
function validateCreate(box, e) {
2017-09-13 14:25:57 -04:00
setDialogMode(0);
2018-08-22 19:18:01 -04:00
var userok = (Q('ausername').value.length > 0) && (Q('ausername').value.indexOf(' ') == -1);
var emailok = (validateEmail(Q('aemail').value) == true);
var pass1ok = (Q('apassword1').value.length > 0);
var pass2ok = (Q('apassword2').value.length > 0) && (Q('apassword2').value == Q('apassword1').value);
var newAccOk = (newAccountPass == 0) || (Q('anewaccountpass').value.length > 0);
var ok = (userok && emailok && pass1ok && pass2ok && newAccOk);
// Color the fields
QS('nuUser').color = userok?'black':'#7b241c';
QS('nuEmail').color = emailok?'black':'#7b241c';
QS('nuPass1').color = pass1ok?'black':'#7b241c';
QS('nuPass2').color = pass2ok?'black':'#7b241c';
QS('nuToken').color = newAccOk?'black':'#7b241c';
2017-08-28 12:27:45 -04:00
if (Q('apassword1').value == '') {
QH('passWarning', '');
2019-02-19 18:38:27 -05:00
QV('passwordPolicyCallout', false);
2017-08-28 12:27:45 -04:00
} else {
2019-02-28 19:17:22 -05:00
if (!passRequirementsEx) {
2018-12-20 17:14:37 -05:00
// No password requirements, display password strength
var passStrength = checkPasswordStrength(Q('apassword1').value);
if (passStrength >= 80) { QH('passWarning', '<span style=color:green><b>Strong Password</b><span>'); }
else if (passStrength >= 60) { QH('passWarning', '<span style=color:blue><b>Good Password</b><span>'); }
else { QH('passWarning', '<span style=color:red><b>Weak Password</b><span>'); }
} else {
// Password requirements provided, use that
var passReq = checkPasswordRequirements(Q('apassword1').value, passRequirements);
if (passReq == false) {
ok = false;
QS('nuPass1').color = '#7b241c';
QS('nuPass2').color = '#7b241c';
2019-02-12 22:23:40 -05:00
QH('passWarning', '<div style=color:red;cursor:pointer onclick=showPasswordPolicy()><b>Password Policy</b><div>'); // This is also a link to the password policy
2019-02-19 18:38:27 -05:00
QV('passwordPolicyCallout', true);
QH('passwordPolicyCallout', passwordPolicyText(Q('apassword1').value));
2018-12-20 17:14:37 -05:00
} else {
QH('passWarning', '');
2019-02-19 18:38:27 -05:00
QV('passwordPolicyCallout', false);
2018-12-20 17:14:37 -05:00
}
}
2017-08-28 12:27:45 -04:00
}
2017-12-14 17:57:52 -05:00
if ((e != null) && (e.keyCode == 13)) {
if (box == 1) { Q('aemail').focus(); }
if (box == 2) { Q('apassword1').focus(); }
if (box == 3) { Q('apassword2').focus(); }
if (box == 4) { Q('apasswordhint').focus(); }
2018-05-16 18:49:12 -04:00
if (box == 5) { if (newAccountPass == 1) { Q('anewaccountpass').focus(); } else { Q('createButton').click(); } }
if (box == 6) { Q('createButton').click(); }
2017-12-14 17:57:52 -05:00
}
if (e != null) { haltEvent(e); }
2018-12-20 17:14:37 -05:00
QE('createButton', ok);
2017-08-28 12:27:45 -04:00
}
2019-02-27 21:48:50 -05:00
function validatePassReset(box, e) {
setDialogMode(0);
var pass1ok = (Q('rapassword1').value.length > 0);
var pass2ok = (Q('rapassword2').value.length > 0) && (Q('rapassword2').value == Q('rapassword1').value);
var ok = (pass1ok && pass2ok);
// Color the fields
QS('rnuPass1').color = pass1ok ? 'black' : '#7b241c';
QS('rnuPass2').color = pass2ok ? 'black' : '#7b241c';
if (Q('rapassword1').value == '') {
QH('rpassWarning', '');
QV('rpasswordPolicyCallout', false);
} else {
2019-02-28 19:17:22 -05:00
if (!passRequirementsEx) {
2019-02-27 21:48:50 -05:00
// No password requirements, display password strength
var passStrength = checkPasswordStrength(Q('rapassword1').value);
if (passStrength >= 80) { QH('rpassWarning', '<span style=color:green><b>Strong Password</b><span>'); }
else if (passStrength >= 60) { QH('rpassWarning', '<span style=color:blue><b>Good Password</b><span>'); }
else { QH('rpassWarning', '<span style=color:red><b>Weak Password</b><span>'); }
} else {
// Password requirements provided, use that
var passReq = checkPasswordRequirements(Q('rapassword1').value, passRequirements);
if (passReq == false) {
ok = false;
QS('rnuPass1').color = '#7b241c';
QS('rnuPass2').color = '#7b241c';
QH('rpassWarning', '<div style=color:red;cursor:pointer onclick=showPasswordPolicy()><b>Password Policy</b><div>'); // This is also a link to the password policy
QV('rpasswordPolicyCallout', true);
QH('rpasswordPolicyCallout', passwordPolicyText(Q('rapassword1').value));
} else {
QH('rpassWarning', '');
QV('rpasswordPolicyCallout', false);
}
}
}
if ((e != null) && (e.keyCode == 13)) {
if (box == 2) { Q('rapassword1').focus(); }
if (box == 3) { Q('rapassword2').focus(); }
if (box == 4) { Q('rapasswordhint').focus(); }
if (box == 6) { Q('resetPassButton').click(); }
}
if (e != null) { haltEvent(e); }
QE('resetPassButton', ok);
}
2019-02-19 18:38:27 -05:00
function passwordPolicyText(pass) {
2019-02-12 22:23:40 -05:00
var policy = '<div style=text-align:left>';
2019-02-19 18:38:27 -05:00
var counts = strCount(pass);
if (passRequirements.min && ((pass == null) || (pass.length < passRequirements.min))) { policy += 'Minimum length of ' + passRequirements.min + '<br />'; }
if (passRequirements.max && ((pass == null) || (pass.length > passRequirements.max))) { policy += 'Maximum length of ' + passRequirements.max + '<br />'; }
if (passRequirements.upper && ((pass == null) || (counts.upper < passRequirements.upper))) { policy += '' + passRequirements.upper + ' upper case<br />'; }
if (passRequirements.lower && ((pass == null) || (counts.lower < passRequirements.lower))) { policy += '' + passRequirements.lower + ' lower case<br />'; }
if (passRequirements.numeric && ((pass == null) || (counts.numeric < passRequirements.numeric))) { policy += '' + passRequirements.numeric + ' numeric<br />'; }
if (passRequirements.nonalpha && ((pass == null) || (counts.nonalpha < passRequirements.nonalpha))) { policy += passRequirements.nonalpha + ' non-alphanumeric<br />'; }
2019-02-12 22:23:40 -05:00
policy += '</div>';
2019-02-19 18:38:27 -05:00
return policy;
}
function showPasswordPolicy() {
messagebox("Password Policy", passwordPolicyText());
2019-02-12 22:23:40 -05:00
}
2017-12-14 17:57:52 -05:00
function validateReset(e) {
2017-12-12 19:04:54 -05:00
setDialogMode(0);
2018-05-16 18:49:12 -04:00
var x = validateEmail(Q('remail').value);
2017-12-14 17:57:52 -05:00
QE('eresetButton', x);
if ((e != null) && (e.keyCode == 13) && (x == true)) {
Q('eresetButton').click();
}
if (e != null) { haltEvent(e); }
2017-12-12 19:04:54 -05:00
}
2017-08-28 12:27:45 -04:00
// Return a password strength score
function checkPasswordStrength(password) {
var r = 0, letters = { }, varCount = 0, variations = { digits: /\d/.test(password), lower: /[a-z]/.test(password), upper: /[A-Z]/.test(password), nonWords: /\W/.test(password) }
if (!password) return 0;
for (var i = 0; i< password.length; i++) { letters[password[i]] = (letters[password[i]] || 0) + 1; r += 5.0 / letters[password[i]]; }
for (var c in variations) { varCount += (variations[c] == true) ? 1 : 0; }
return parseInt(r + (varCount - 1) * 10);
}
2017-09-08 14:37:37 -04:00
2018-12-20 17:14:37 -05:00
// Check password requirements
function checkPasswordRequirements(password, requirements) {
if ((requirements == null) || (requirements == '') || (typeof requirements != 'object')) return true;
if (requirements.min) { if (password.length < requirements.min) return false; }
if (requirements.max) { if (password.length > requirements.max) return false; }
2019-02-19 18:38:27 -05:00
var counts = strCount(password);
if (requirements.numeric && (counts.numeric < requirements.numeric)) return false;
if (requirements.lower && (counts.lower < requirements.lower)) return false;
if (requirements.upper && (counts.upper < requirements.upper)) return false;
if (requirements.nonalpha && (counts.nonalpha < requirements.nonalpha)) return false;
return true;
}
function strCount(password) {
var counts = { numeric: 0, lower: 0, upper: 0, nonalpha: 0 };
if (typeof password != 'string') return counts;
2018-12-20 17:14:37 -05:00
for (var i = 0; i < password.length; i++) {
2019-02-19 18:38:27 -05:00
if (/\d/.test(password[i])) { counts.numeric++; }
if (/[a-z]/.test(password[i])) { counts.lower++; }
if (/[A-Z]/.test(password[i])) { counts.upper++; }
if (/\W/.test(password[i])) { counts.nonalpha++; }
2018-12-20 17:14:37 -05:00
}
2019-02-19 18:38:27 -05:00
return counts;
2018-12-20 17:14:37 -05:00
}
2017-09-08 14:37:37 -04:00
2019-02-05 22:07:12 -05:00
function checkToken() {
var t1 = Q('tokenInput').value;
2019-02-08 12:24:00 -05:00
var t2 = t1.split(' ').join('');
2019-02-05 22:07:12 -05:00
if (t1 != t2) { Q('tokenInput').value = t2; }
2019-02-08 12:24:00 -05:00
QE('tokenOkButton', (Q('tokenInput').value.length == 6) || (Q('tokenInput').value.length == 8) || (Q('tokenInput').value.length == 44));
2019-02-05 22:07:12 -05:00
}
2019-01-15 21:21:03 -05:00
2019-02-11 17:41:15 -05:00
function resetCheckToken() {
var t1 = Q('resetTokenInput').value;
var t2 = t1.split(' ').join('');
if (t1 != t2) { Q('resetTokenInput').value = t2; }
QE('resetTokenOkButton', (Q('resetTokenInput').value.length == 6) || (Q('resetTokenInput').value.length == 8) || (Q('resetTokenInput').value.length == 44));
}
2017-09-08 14:37:37 -04:00
//
// POPUP DIALOG
//
// undefined = Hidden, 1 = Generic Message
var xxdialogMode;
var xxdialogFunc;
var xxdialogButtons;
var xxdialogTag;
var xxcurrentView = 0;
// Display a dialog box
// Parameters: Dialog Mode (0 = none), Dialog Title, Buttons (1 = OK, 2 = Cancel, 3 = OK & Cancel), Call back function(0 = Cancel, 1 = OK), Dialog Content (Mode 2 only)
function setDialogMode(x, y, b, f, c, tag) {
xxdialogMode = x;
xxdialogFunc = f;
xxdialogButtons = b;
xxdialogTag = tag;
QE('idx_dlgOkButton', true);
QV('idx_dlgOkButton', b & 1);
QV('idx_dlgCancelButton', b & 2);
QV('id_dialogclose', (b & 2) || (b & 8));
QV('idx_dlgButtonBar', b & 7);
if (y) QH('id_dialogtitle', y);
for (var i = 1; i < 24; i++) { QV('dialog' + i, i == x); } // Edit this line when more dialogs are added
QV('dialog', x);
if (c) { if (x == 2) { QH('id_dialogOptions', c); } else { QH('id_dialogMessage', c); } }
}
function dialogclose(x) {
var f = xxdialogFunc;
var b = xxdialogButtons;
var t = xxdialogTag;
setDialogMode();
if (((b & 8) || x) && f) f(x, t);
}
2018-09-17 16:44:14 -04:00
// Toggle the web page to full screen
function toggleFullScreen(toggle) {
if (toggle === 1) { webPageFullScreen = !webPageFullScreen; putstore('webPageFullScreen', webPageFullScreen); }
if (webPageFullScreen == false) {
2019-03-30 18:48:21 -04:00
// By adding body class, it will change a style of all ellements using CSS selector
// No need for JS anymore and it will be consistent style for all the templates.
QC('body').remove("fullscreen");
2018-09-17 16:44:14 -04:00
} else {
2019-03-30 18:48:21 -04:00
QC('body').add("fullscreen");
2018-09-17 16:44:14 -04:00
}
QV('body', true);
center();
}
function center() {
2019-03-30 18:48:21 -04:00
/* Now we use CSS media to achive the same effect as deleted JS */
2018-09-19 13:37:19 -04:00
2018-09-17 16:44:14 -04:00
if (webPageFullScreen == false) {
QS('centralTable')['margin-top'] = '';
} else {
var h = (Q('column_l').clientHeight / 2) - 250;
if (h < 0) h = 0;
QS('centralTable')['margin-top'] = h + 'px';
}
}
2017-09-08 14:37:37 -04:00
function messagebox(t, m) { QH('id_dialogMessage', m); setDialogMode(1, t, 1); }
function statusbox(t, m) { QH('id_dialogMessage', m); setDialogMode(1, t); }
function getDocWidth() { if (window.innerWidth) return window.innerWidth; if (document.documentElement && document.documentElement.clientWidth && document.documentElement.clientWidth != 0) return document.documentElement.clientWidth; return document.getElementsByTagName('body')[0].clientWidth; }
2017-12-14 17:57:52 -05:00
function haltEvent(e) { if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
function haltReturn(e) { if (e.keyCode == 13) { haltEvent(e); } }
2018-10-16 17:51:54 -04:00
function validateEmail(v) { var emailReg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9] { 1,3}\.[0-9] { 1,3}\.[0-9] { 1,3}\.[0-9] { 1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z] { 2,}))$/; return emailReg.test(v); } // New version
2018-09-17 16:44:14 -04:00
function putstore(name, val) { try { if (typeof (localStorage) === 'undefined') return; localStorage.setItem(name, val); } catch (e) { } }
function getstore(name, val) { try { if (typeof (localStorage) === 'undefined') return val; var v = localStorage.getItem(name); if ((v == null) || (v == null)) return val; return v; } catch (e) { return val; } }
2019-02-08 01:30:33 -05:00
function u2fSupported() { return (window.u2f && ((navigator.userAgent.indexOf('Chrome/') > 0) || (navigator.userAgent.indexOf('Firefox/') > 0) || (navigator.userAgent.indexOf('Opera/') > 0) || (navigator.userAgent.indexOf('Safari/') > 0))); }
2017-09-08 14:37:37 -04:00
2017-08-28 12:27:45 -04:00
</script>
</body>
</html>