MeshCentral/views/login2.handlebars
2022-03-11 08:15:36 -08:00

954 lines
60 KiB
Handlebars

<!DOCTYPE html>
<html dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
<head lang="en">
<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" />
<meta name="robots" content="noindex,nofollow">
<link rel="shortcut icon" type="image/x-icon" href="{{{domainurl}}}favicon.ico" />
<link keeplink=1 type="text/css" href="styles/style.css" media="screen" rel="stylesheet" title="CSS" />
<link rel="apple-touch-icon" href="/favicon-303x303.png" />
<script type="text/javascript" src="scripts/common-0.0.1{{min}}.js"></script>
<script keeplink=1 type="text/javascript" src="scripts/u2f-api{{min}}.js"></script>
<title>{{{title}}} - Login</title>
<style>
#body {
background-color: cadetblue;
background: linear-gradient(to bottom right, #369, #036);
}
#flink a:link {
color: #c8c8c8;
}
#flink a:visited {
color: #c8c8c8;
}
#flink a:hover {
color: #c8c8c8;
}
#flink a:active {
color: #c8c8c8;
}
#welcomeText a:link {
color: #FFF;
}
#welcomeText a:visited {
color: #FFF;
}
#welcomeText a:hover {
color: #FFF;
}
#welcomeText a:active {
color: #FFF;
}
</style>
</head>
<body id="body" onload="if (typeof(startup) !== 'undefined') startup();" class="arg_hide login">
<div id="backgroundImage" style="position:absolute;left:0;bottom:0;z-index:-1;height:60%;width:100%;opacity:0.1;background-image:url('welcome.png');background-repeat:no-repeat;background-position:left bottom;background-size:contain"></div>
<table id="centralTable" class="container" style="height:100%;z-index:1">
<tr>
<td id="logincell">
{{{titlehtml}}}
<img id="loginPicture" />
<div style="font-size:46px;font-family:Arial,Helvetica,sans-serif;font-weight:bold;padding-bottom:10px;color:#c8c8c8;text-shadow: 2px 2px 2px #000;">{{{title1}}}<sup style="font-size:20px"> {{{title2}}}</sup></div>
<div id=loginpanel style="display:none;box-shadow:1px 1px 4px #000">
<form id=loginpanelform method=post>
<input type=hidden name=action value=login />
<div id=message1></div>
<div id="loginuserpassdiv" style="display:none">
<table style="width:100%">
<tr>
<td>
<input id=username title="Username" style="box-sizing:border-box;width:280px;border:0;border-radius:4px;padding:8px;background-color:#FFF8CC" {{{autocomplete}}}="username" placeholder="Username" type=text maxlength=64 name=username onchange=validateLogin(1) onkeyup=validateLogin(1,event) />
</td>
</tr>
<tr>
<td>
<input id=password title="Password" style="box-sizing:border-box;width:280px;border:0;border-radius:4px;padding:8px;background-color:#FFF8CC" {{{autocomplete}}}="current-password" placeholder="Password" type=password maxlength=256 name=password onchange=validateLogin(2) onkeyup=validateLogin(2,event) />
</td>
</tr>
<tr>
<td>
<div id=showPassHintLink title="Password Hint" style="box-sizing:border-box;width:280px;border:0;border-radius:4px;padding:8px;background-color:#FFF8CC;display:none"><a onclick="return showPassHint(event);" href="#" style="cursor:pointer">Show Hint</a></div>
</td>
</tr>
<tr>
<td>
<input id=loginButton style="box-sizing:border-box;width:280px;border:0;border-radius:4px;padding:6px" onclick="submitButtonClicked('loginpanelform')" type=button value="Log In" disabled="disabled" />
</td>
</tr>
</table>
<div id="hrAccountDiv" style="display:none"><hr /></div>
<div id="resetAccountDiv" style="display:none;padding:2px">
<span id="resetAccountSpan">Forgot username/password?</span> <a onclick="return xgo(3,event);" href="#" style=cursor:pointer>Reset account</a>.
</div>
<div id="newAccountDiv" style="display:none;padding:2px;padding-top:10px">
Don&#39;t have an account? <a onclick="return xgo(2,event);" href="#" style=cursor:pointer>Create one</a>.
</div>
</div>
<input id=loginformargs name="urlargs" type="hidden" value="" />
<div id="authStrategies" style="display:none">
<hr id="loginuserpasshr" style="display:none" />
<div style="margin-bottom:8px">Log in using an existing account</div>
<a id="auth-twitter" href="auth-twitter" style="display:none"><img src="images/login/twitter32.png" srcset="images/login/twitter64.png 2x" loading="lazy" width="32" height="32" style="margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer" title="Sign-in using Twitter" /></a>
<a id="auth-google" href="auth-google" style="display:none"><img src="images/login/google32.png" srcset="images/login/google64.png 2x" loading="lazy" width="32" height="32" style="margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer" title="Sign-in using Google" /></a>
<a id="auth-github" href="auth-github" style="display:none"><img src="images/login/github32.png" srcset="images/login/github64.png 2x" loading="lazy" width="32" height="32" style="margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer" title="Sign-in using GitHub" /></a>
<a id="auth-reddit" href="auth-reddit" style="display:none"><img src="images/login/reddit32.png" srcset="images/login/reddit64.png 2x" loading="lazy" width="32" height="32" style="margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer" title="Sign-in using Reddit" /></a>
<a id="auth-azure" href="auth-azure" style="display:none"><img src="images/login/azure32.png" srcset="images/login/azure64.png 2x" loading="lazy" width="32" height="32" style="margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer" title="Sign-in using Azure" /></a>
<a id="auth-jumpcloud" href="auth-jumpcloud" style="display:none"><img src="images/login/jumpcloud32.png" srcset="images/login/jumpcloud64.png 2x" loading="lazy" width="32" height="32" style="margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer" title="Sign-in using JumpCloud" /></a>
<a id="auth-intel" href="auth-intel" style="display:none"><img src="images/login/intel32.png" srcset="images/login/intel64.png 2x" loading="lazy" width="32" height="32" style="margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer" title="Sign-in using Intel" /></a>
<a id="auth-saml" href="auth-saml" style="display:none"><img src="images/login/generic32.png" srcset="images/login/generic64.png 2x" loading="lazy" width="32" height="32" style="margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer" title="Single Sign-in" /></a>
</div>
</form>
</div>
<div id=createpanel style="display:none;position:relative">
<form id=createpanelform method=post>
<input type=hidden name=action value=createaccount />
<div id=message2></div>
<div>
<b>Account Creation</b>
</div>
<div id="passwordPolicyCallout" style="display:none"></div>
<table>
<tr id="nuUserRow">
<td id="nuUser" align=right width=100>Username:</td>
<td><input id=ausername type=text {{{autocomplete}}}="username" name=username onchange=validateCreate(1) maxlength=64 onkeydown=haltReturn(event) onkeyup=validateCreate(1,event) /></td>
</tr>
<tr>
<td id="nuEmail" align=right width=100>Email:</td>
<td><input id=aemail type=text {{{autocomplete}}}="email" inputmode="email" name=email onchange=validateCreate(2) maxlength=256 onkeydown=haltReturn(event) onkeyup=validateCreate(2,event) /></td>
</tr>
<tr>
<td id="nuPass1" align=right>Password:</td>
<td><input id=apassword1 type=password name=password1 {{{autocomplete}}}="new-password" maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(3,event) onkeyup=validateCreate(3,event) /></td>
</tr>
<tr>
<td id="nuPass2" align=right>Password:</td>
<td><input id=apassword2 type=password name=password2 {{{autocomplete}}}="new-password" maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(4,event) onkeyup=validateCreate(4,event) /></td>
</tr>
<tr id="createPanelHint" style="display:none">
<td id="nuHint" align=right>Password Hint:</td>
<td><input id=apasswordhint type=text name=apasswordhint {{{autocomplete}}}=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(5,event) onkeyup=validateCreate(5,event) /></td>
</tr>
<tr id=newAccountPass title="Enter the account creation token">
<td id="nuToken" align=right>Creation Token:</td>
<td><input id=anewaccountpass type=password name=anewaccountpass {{{autocomplete}}}=off maxlength=256 onkeydown=haltReturn(event) onchange=validateCreate(6,event) onkeyup=validateCreate(6,event) /></td>
</tr>
<tr>
<td colspan=2>
<div style=float:right><input id=createButton type="button" onclick="submitButtonClicked('createpanelform')" value="Create Account" disabled="disabled" /></div>
<div id=passWarning style="padding-top:6px"></div>
</td>
</tr>
</table>
<hr /><a onclick="return xgo(1,event);" href="#" style=cursor:pointer>Back to login</a>
<input id=createformargs name="urlargs" type="hidden" value="" />
</form>
</div>
<div id=resetpanel style="display:none">
<form id=resetpanelform method=post>
<input type=hidden name=action value=resetaccount />
<div id=message3></div>
<div>
<b>Account Reset</b>
</div>
<table style="width:100%;margin-top:4px;margin-bottom:4px">
<tr>
<td>
<input id=remail title="Email" style="box-sizing:border-box;width:280px;border:0;border-radius:4px;padding:8px;background-color:#FFF8CC" {{{autocomplete}}}="username" placeholder="Email" type=text maxlength=256 name=email onchange=validateReset() onkeyup=validateReset(event) />
</td>
</tr>
<tr>
<td>
<input id=eresetButton style="box-sizing:border-box;width:280px;border:0;border-radius:4px;padding:6px" type="button" onclick="submitButtonClicked('resetpanelform')" value="Reset Account" disabled="disabled" />
</td>
</tr>
</table>
<hr /><a onclick="return xgo(1,event);" href="#" style=cursor:pointer>Back to login</a>
<input id=resetformargs name="urlargs" type="hidden" value="" />
</form>
</div>
<div id=tokenpanel style="display:none">
<form id=tokenpanelform method=post {{{autocomplete}}}=off>
<input type=hidden name=action value=tokenlogin />
<input type=hidden name=hwstate value="{{{hwstate}}}" />
<div id=message4></div>
<table style="width:100%">
<tr>
<td>
<input id=tokenInput {{{autocomplete}}}="one-time-code" title="Token" style="box-sizing:border-box;width:280px;border:0;border-radius:4px;padding:8px;background-color:#FFF8CC" placeholder="Token" type=text maxlength=50 name=token onchange=checkToken(event) onpaste=resetCheckToken(event) onkeyup=checkToken(event) onkeydown=checkToken(event) /><br />
<input id=hwtokenInput type=text name=hwtoken style="display:none" />
</td>
</tr>
<tr>
<td>
<div>
<input id=tokenOkButton style="box-sizing:border-box;width:280px;border:0;border-radius:4px;padding:6px" type="button" onclick="submitButtonClicked('tokenpanelform')" value="Log In" disabled="disabled" />
</div>
</td>
</tr>
<tr>
<td style="align-content:center;padding-top:10px">
<label id=tokenInputRememberLabel><input id=tokenInputRemember name=remembertoken type=checkbox /><span id=tokenInputRememberSpan></span></label>
</td>
</tr>
<tr id="2farow">
<td>
<hr />
<div>
<img id=securityKeyButton src="images/login/2fa-key-48.png" srcset="images/login/2fa-key-96.png 2x" title="Use Security Key" loading="lazy" width="48" height="48" style="display:none;margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer;background-color:#FFF" onclick="useSecurityKey(1)" />
<img id=smsKeyButton src="images/login/2fa-sms-48.png" srcset="images/login/2fa-sms-96.png 2x" title="SMS" loading="lazy" width="48" height="48" style="display:none;margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer;background-color:#FFF" onclick="useSMSToken(1)" />
<img id=emailKeyButton src="images/login/2fa-mail-48.png" srcset="images/login/2fa-mail-96.png 2x" title="Email" loading="lazy" width="48" height="48" style="display:none;margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer;background-color:#FFF" onclick="useEmailToken(1)" />
<img id=pushKeyButton src="images/login/2fa-push-48.png" srcset="images/login/2fa-push-96.png 2x" title="Device Authentication" loading="lazy" width="48" height="48" style="display:none;margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer;background-color:#FFF" onclick="usePushToken(1)" />
</div>
</td>
</tr>
</table>
<hr /><a onclick="return xgo(1,event);" href="#" style=cursor:pointer>Back to login</a>
<input id=tokenformargs name="urlargs" type="hidden" value="" />
</form>
</div>
<div id=resettokenpanel style="display:none">
<form id=resettokenpanelform method=post>
<input type=hidden name=action value=resetaccount />
<div id=message5></div>
<table style="width:100%">
<tr>
<td>
<input id=resetTokenInput {{{autocomplete}}}="one-time-code" title="Token" style="box-sizing:border-box;width:280px;border:0;border-radius:4px;padding:8px;background-color:#FFF8CC" placeholder="Token" type=text maxlength=50 name=token onchange=resetCheckToken(event) onpaste=resetCheckToken(event) onkeyup=resetCheckToken(event) onkeydown=resetCheckToken(event) /><br />
<input id=resetHwtokenInput type=text name=hwtoken style="display:none" />
</td>
</tr>
<tr>
<td>
<div>
<input id=resetTokenOkButton style="box-sizing:border-box;width:280px;border:0;border-radius:4px;padding:6px" type="button" onclick="submitButtonClicked('resettokenpanelform')" value="Log In" disabled="disabled" />
</div>
</td>
</tr>
<tr id="2farow2">
<td colspan=2>
<hr />
<div>
<img id=securityKeyButton2 src="images/login/2fa-key-48.png" srcset="images/login/2fa-key-96.png 2x" title="Use Security Key" loading="lazy" width="48" height="48" style="display:none;margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer;background-color:#FFF" onclick="useSecurityKey(2)" />
<img id=smsKeyButton2 src="images/login/2fa-sms-48.png" srcset="images/login/2fa-sms-96.png 2x" title="SMS" loading="lazy" width="48" height="48" style="display:none;margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer;background-color:#FFF" onclick="useSMSToken(2)" />
<img id=emailKeyButton2 src="images/login/2fa-mail-48.png" srcset="images/login/2fa-mail-96.png 2x" title="Email" loading="lazy" width="48" height="48" style="display:none;margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer;background-color:#FFF" onclick="useEmailToken(2)" />
<img id=pushKeyButton2 src="images/login/2fa-push-48.png" srcset="images/login/2fa-push-96.png 2x" title="Device Authentication" loading="lazy" width="48" height="48" style="display:none;margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer;background-color:#FFF" onclick="usePushToken(2)" />
</div>
</td>
</tr>
</table>
<hr /><a onclick="return xgo(1,event);" href="#" style=cursor:pointer>Back to login</a>
<input id=resettokenformargs name="urlargs" type="hidden" value="" />
</form>
</div>
<div id=resetpasswordpanel style="display:none;position:relative">
<form id=resetpasswordpanelform method=post>
<input type=hidden name=action value=resetpassword />
<div id=message6></div>
<div id="rpasswordPolicyCallout" style="display:none"></div>
<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>
<tr id="resetpasswordpanelHint" style="display:none">
<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="button" onclick="submitButtonClicked('resetpasswordpanelform')" value="Reset Password" disabled="disabled" /></div>
<div id=rpassWarning style="padding-top:6px"></div>
</td>
</tr>
</table>
<hr /><a onclick="return xgo(1,event);" href="#" style=cursor:pointer>Back to login</a>
<input id=resetpasswordformargs name="urlargs" type="hidden" value="" />
</form>
</div>
<div id=checkemailpanel style="display:none;position:relative">
<form id=checkemailpanelform method=post>
<input type=hidden name=action value=checkemail />
<div id=message7></div>
<table id="checkCheckOperations" style="width:100%;display:none">
<tr>
<td>
<div id="unconfirmedEmail"></div><br />
</td>
</tr>
<tr>
<td>
<div>
<input id=changeEmailButton type=button value="Change Email Address" onclick="changeEmailAddress()" />
<input id=checkEmailButton2 type=button value="Resend Confirmation Email" onclick="resentEmailConfirmation()" />
<input id=checkEmailButton type="button" onclick="submitButtonClicked('checkemailpanelform')" style="display:none" />
<input id=checkEmailVal name=email type="hidden" value="" />
</div>
</td>
</tr>
</table>
<hr /><a onclick="return xgo(1,event);" href="#" style=cursor:pointer>Back to login</a>
<input id=checkemailformargs name="urlargs" type="hidden" value="" />
</form>
</div>
<div id=waitpushpanel style="display:none">
<form id=waitpushpanelform method=post>
<input type=hidden name=action value=pushlogin />
<div id=message8></div>
<table style="width:100%">
<tr>
<td style="align-content:center;padding-top:10px">
<img id="waitpushpanelimage" src="images/login/push-150.png" srcset="images/login/push-300.png 2x" style="opacity:0.3" loading="lazy" width="265" height="150" />
</td>
</tr>
<tr>
<td style="align-content:center;padding-top:10px">
<label id=tokenInputRememberLabel2><input id=tokenInputRemember2 name=remembertoken type=checkbox /><span id=tokenInputRememberSpan2></span></label>
</td>
</tr>
</table>
<hr /><a onclick="return xgo(1,event);" href="#" style=cursor:pointer>Back to login</a>
<input id=pushtokenformargs name="urlargs" type="hidden" value="" />
<input id=pushtokenInput name="hwstate" type="hidden" value="" />
<input id=pushOkButton onclick="submitButtonClicked('waitpushpanelform')" style="display:none" />
</form>
</div>
</td>
</tr>
<tr id="welcomeTextRow"><td><div id="welcomeText" style="color:white;text-align:center;margin-left:20px;margin-right:20px"></div></td></tr>
<tr style="height:20px">
<td>
<div>
<div id="flink" style="float:right;margin-right:4px;color:#c8c8c8">{{{rootCertLink}}}&nbsp;<a href=terms>Terms &amp; Privacy</a></div>
<div id="flink" style="margin-left:4px;color:#c8c8c8">{{{footer}}}</div>
</div>
</td>
</tr>
</table>
<div id=dialog style="display:none">
<div id=dialogHeader>
<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>
<div id=dialogBody>
<div id=dialog1>
<div id=id_dialogMessage style=""></div>
</div>
<div id=dialog2 style="">
<div id=id_dialogOptions></div>
</div>
</div>
<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)">
</div>
</div>
<script>
'use strict';
var welcomePictureFullScreen = (decodeURIComponent('{{{welcomePictureFullScreen}}}') === 'true');
var passlogin = '{{{passlogin}}}';
var passhint = '{{{passhint}}}';
var loginMode = '{{{loginmode}}}';
var newAccount = '{{{newAccount}}}';
var newAccountPass = parseInt('{{{newAccountPass}}}');
var emailCheck = '{{{emailcheck}}}';
var passRequirements = '{{{passRequirements}}}';
var hardwareKeyChallenge = decodeURIComponent('{{{hkey}}}');
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));
var features = parseInt('{{{features}}}');
var welcomeText = decodeURIComponent('{{{welcometext}}}');
var currentpanel = 0;
var publicKeyCredentialRequestOptions = null;
var otpemail = (decodeURIComponent('{{{otpemail}}}') === 'true');
var otpsms = (decodeURIComponent('{{{otpsms}}}') === 'true');
var otppush = (decodeURIComponent('{{{otppush}}}') === 'true');
var autofido = (decodeURIComponent('{{{autofido}}}') === 'true');
var twoFactorCookieDays = parseInt('{{{twoFactorCookieDays}}}');
var authStrategies = '{{{authStrategies}}}'.split(',');
var tokenTimeout = parseInt('{{{tokenTimeout}}}');
var websocket = null;
var formSubmitted = false;
function startup() {
if (decodeURIComponent('{{{loginpicture}}}') == 'true') { Q('loginPicture').src = "loginlogo.png"; }
QV('welcomeTextRow', welcomeText != '');
QH('welcomeText', welcomeText);
// Change the background image to full screen
if (welcomePictureFullScreen) {
QS('backgroundImage').height = '100%';
QS('backgroundImage').opacity = '1';
QS('backgroundImage')['background-position'] = 'center center';
QS('backgroundImage')['background-size'] = 'cover';
}
// Display the right server message
var i;
var messageid = parseInt('{{{messageid}}}');
var okmessages = ['', "If valid, reset mail sent.", "Email sent.", "Email verification required, check your mailbox and click the confirmation link.", "SMS sent.", "Sending notification..."];
var failmessages = ["Unable to create account.", "Account limit reached.", "Existing account with this email address.", "Invalid account creation token.", "Username already exists.", "Password rejected, use a different one.", "Invalid email.", "Account not found.", "Invalid token, try again.", "Unable to sent email.", "Account locked.", "Access denied.", "Login failed, check username and password.", "Password change requested.", "IP address blocked, try again later.", "Server under maintenance.", "Unable to send device notification."];
if (messageid > 0) {
var msg = '';
if ((messageid < 100) && (messageid < okmessages.length)) { msg = okmessages[messageid]; }
else if ((messageid >= 100) && ((messageid - 100) < failmessages.length)) { msg = failmessages[messageid - 100]; }
if (msg != '') {
if (messageid >= 100) { msg = ('<span class="msg error"><b style=color:#8C001A>' + msg + '<b></span><br /><br />'); } else { msg = ('<span class="msg success"><b>' + msg + '</b></span><br /><br />'); }
for (i = 1; i < 9; i++) { QH('message' + i, msg); }
}
}
// Fix links if a loginKey if used
var urlargs = parseUriArgs();
//if (urlargs.key) { Q('termsLinkFooter').href += '?key=' + urlargs.key; }
// Show user/pass login
passlogin = (passlogin == 'true');
if (urlargs.passlogin === 1) { passlogin = true; }
if (urlargs.passlogin === 0) { passlogin = false; }
QV('loginuserpassdiv', passlogin)
QV('loginuserpasshr', passlogin)
// Setup two factor cookie time
if (twoFactorCookieDays > 0) {
QV('tokenInputRememberLabel', true);
QH('tokenInputRememberSpan', format("Remember this device for {0} days.", twoFactorCookieDays));
QV('tokenInputRememberLabel2', true);
QH('tokenInputRememberSpan2', format("Remember this device for {0} days.", twoFactorCookieDays));
} else {
QV('tokenInputRememberLabel', false);
QV('tokenInputRememberLabel2', false);
}
// If URL arguments are provided, add them to form posts
if (window.location.href.indexOf('?') > 0) {
var xurlargs = window.location.href.substring(window.location.href.indexOf('?'));
Q('loginformargs').value = xurlargs;
Q('createformargs').value = xurlargs;
Q('resetformargs').value = xurlargs;
Q('tokenformargs').value = xurlargs;
Q('pushtokenformargs').value = xurlargs;
Q('resettokenformargs').value = xurlargs;
Q('resetpasswordformargs').value = xurlargs;
Q('checkemailformargs').value = xurlargs;
}
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; }
}
if (features & 0x200000) { // Email is username
Q('username').placeholder = "Email";
QH('resetAccountSpan', "Forgot password?");
QV('nuUserRow', false);
}
QV('createPanelHint', passRequirements.hint === true);
QV('resetpasswordpanelHint', passRequirements.hint === true);
// Setup authentication strategies
if (authStrategies != '') {
QV('authStrategies', true);
if (authStrategies.indexOf('twitter') >= 0) { QV('auth-twitter', true); }
if (authStrategies.indexOf('google') >= 0) { QV('auth-google', true); }
if (authStrategies.indexOf('github') >= 0) { QV('auth-github', true); }
if (authStrategies.indexOf('reddit') >= 0) { QV('auth-reddit', true); }
if (authStrategies.indexOf('azure') >= 0) { QV('auth-azure', true); }
if (authStrategies.indexOf('jumpcloud') >= 0) { QV('auth-jumpcloud', true); }
if (authStrategies.indexOf('intel') >= 0) { QV('auth-intel', true); }
if (authStrategies.indexOf('saml') >= 0) { QV('auth-saml', true); }
}
validateLogin();
validateCreate();
if (loginMode.length != 0) { go(parseInt(loginMode)); } else { go(1); }
QV('newAccountDiv', (newAccount === '1') || (newAccount === 'true')); // If new accounts are not allowed, don't display the new account link.
if ((passhint != null) && (passhint.length > 0)) { QV('showPassHintLink', true); }
QV('newAccountPass', (newAccountPass == 1));
QV('resetAccountDiv', (emailCheck == 'true'));
QV('hrAccountDiv', (emailCheck == 'true') || (newAccountPass == 1));
if (loginMode == '4') {
if (tokenTimeout > 0) { setTimeout(function () { Q('hwtokenInput').value = '**timeout**'; QE('tokenOkButton', true); Q('tokenOkButton').click(); }, tokenTimeout); }
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
var twofakey = (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn');
var emailkey = otpemail && (messageid != 2) && (messageid != 4);
var smskey = otpsms && (messageid != 2) && (messageid != 4);
var pushkey = otppush && (messageid != 2) && (messageid != 4);
QV('securityKeyButton', twofakey);
QV('emailKeyButton', emailkey);
QV('smsKeyButton', smskey);
QV('pushKeyButton', pushkey);
QV('2farow', twofakey || emailkey || smskey || pushkey);
// If hardware key is an option, trigger it now
if (autofido && twofakey) { setTimeout(function () { useSecurityKey(1); }, 300); }
}
if (loginMode == '5') {
if (tokenTimeout > 0) { setTimeout(function () { Q('hwtokenInput').value = '**timeout**'; QE('tokenOkButton', true); Q('tokenOkButton').click(); }, tokenTimeout); }
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
var twofakey = (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn');
var emailkey = otpemail && (messageid != 2) && (messageid != 4);
var smskey = otpsms && (messageid != 2) && (messageid != 4);
var pushkey = otppush && (messageid != 2) && (messageid != 4);
QV('securityKeyButton2', twofakey);
QV('emailKeyButton2', emailkey);
QV('smsKeyButton2', smskey);
QV('pushKeyButton', pushkey);
QV('2farow2', twofakey || emailkey || smskey || pushkey);
// If hardware key is an option, trigger it now
if (autofido && twofakey) { setTimeout(function () { useSecurityKey(2); }, 300); }
}
if (loginMode == '8') {
// Perform websocket connection to server to wait for device authentication
websocket = new WebSocket(passhint);
websocket.onopen = function (e) { QS('waitpushpanelimage')['opacity'] = '1'; }
websocket.onmessage = function (e) {
if (typeof e.data != 'string') { this.close(); }
var r = null;
try { r = JSON.parse(e.data); } catch (ex) { }
if (r.sent === true) {
// Request was sent
QH('message8', '<span class="msg success"><b>' + format("Request sent, {0}.", r.code) + '</b></span><br /><br />');
} else if (r.sent === false) {
// Request failed to send
QH('message8', '<span class="msg error"><b style=color:#8C001A>' + "Failed to send request." + '<b></span><br /><br />');
QV('tokenInputRememberLabel2', false);
this.close();
} else if (r.approved === true) {
// Request approved
this.close();
QV('tokenInputRememberLabel2', false);
QH('message8', '<span class="msg success"><b>' + "Request Accepted." + '</b></span><br /><br />');
Q('pushtokenInput').value = r.token;
Q('pushOkButton').click();
} else {
// Request rejected
QH('message8', '<span class="msg error"><b style=color:#8C001A>' + "Access Rejected." + '<b></span><br /><br />');
QV('tokenInputRememberLabel2', false);
this.close();
}
}
websocket.onclose = function (e) { QS('waitpushpanelimage')['opacity'] = '0.3'; }
websocket.onerror = function (e) {
QH('message8', '<span class="msg error"><b style=color:#8C001A>' + "Connection Error" + '<b></span><br /><br />');
QS('waitpushpanelimage')['opacity'] = '0.5';
}
}
}
// Use a hardware security key
function useSecurityKey(panelAction) {
if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) {
if (typeof hardwareKeyChallenge.challenge == 'string') { hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer; }
publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout }
for (var i = 0; i < hardwareKeyChallenge.keyIds.length; i++) {
publicKeyCredentialRequestOptions.allowCredentials.push(
{ id: Uint8Array.from(atob(hardwareKeyChallenge.keyIds[i]), function (c) { return c.charCodeAt(0) }), type: 'public-key', transports: ['usb', 'ble', 'nfc', 'internal'] }
);
}
// 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))),
};
if (panelAction == 1) {
Q('hwtokenInput').value = JSON.stringify(assertion);
QE('tokenOkButton', true);
Q('tokenOkButton').click();
} else if (panelAction == 2) {
Q('resetHwtokenInput').value = JSON.stringify(assertion);
QE('resetTokenOkButton', true);
Q('resetTokenOkButton').click();
}
},
function (error) { console.log('credentials-get error', error); }
);
}
}
function useEmailToken(panelAction) {
if (otpemail != true) return;
setDialogMode(1, "Secure Login", 3, useEmailKeyEx, "Send token to registered email address?", panelAction);
}
function useEmailKeyEx(b, panelAction) {
if (panelAction == 1) {
Q('hwtokenInput').value = '**email**';
QE('tokenOkButton', true);
Q('tokenOkButton').click();
} else if (panelAction == 2) {
Q('resetHwtokenInput').value = '**email**';
QE('resetTokenOkButton', true);
Q('resetTokenOkButton').click();
}
}
function useSMSToken(panelAction) {
if (otpsms != true) return;
setDialogMode(1, "Secure Login", 3, useSMSTokenEx, "Send token to registered phone number?", panelAction);
}
function useSMSTokenEx(b, panelAction) {
if (panelAction == 1) {
Q('hwtokenInput').value = '**sms**';
QE('tokenOkButton', true);
Q('tokenOkButton').click();
} else if (panelAction == 2) {
Q('resetHwtokenInput').value = '**sms**';
QE('resetTokenOkButton', true);
Q('resetTokenOkButton').click();
}
}
function usePushToken(panelAction) {
if (panelAction == 1) {
Q('hwtokenInput').value = '**push**';
QE('tokenOkButton', true);
Q('tokenOkButton').click();
} else if (panelAction == 2) {
Q('resetHwtokenInput').value = '**push**';
QE('resetTokenOkButton', true);
Q('resetTokenOkButton').click();
}
}
function showPassHint(e) {
messagebox("Password Hint", passhint);
haltEvent(e);
return false;
}
function xgo(x, e) {
QV('message1', false);
QV('message2', false);
QV('message3', false);
QV('message4', false);
QV('message5', false);
QV('message6', false);
QV('message7', false);
go(x);
haltEvent(e);
return false;
}
function go(x) {
currentpanel = x;
setDialogMode(0);
QV('showPassHintLink', false);
QV('loginpanel', x == 1);
QV('createpanel', x == 2);
QV('resetpanel', x == 3);
QV('tokenpanel', x == 4);
QV('resettokenpanel', x == 5);
QV('resetpasswordpanel', x == 6);
QV('checkemailpanel', x == 7);
QV('waitpushpanel', x == 8);
if (x == 1) { Q('username').focus(); }
if (x == 2) { if (features & 0x200000) { Q('aemail').focus(); } else { Q('ausername').focus(); } } // Email is username
if (x == 3) { Q('remail').focus(); }
if (x == 4) { Q('tokenInput').focus(); }
if (x == 5) { Q('resetTokenInput').focus(); }
if (x == 6) { Q('rapassword1').focus(); }
if (x == 7) {
QH('unconfirmedEmail', passhint);
QV('checkCheckOperations', Q('unconfirmedEmail').innerHTML != '');
QH('checkEmailVal', passhint);
}
}
function submitButtonClicked(v) {
if (!formSubmitted) { formSubmitted = true; document.getElementById(v).submit(); }
}
function validateLogin(box, e) {
var ok = ((Q('username').value.length > 0) && (Q('username').value.indexOf(' ') == -1) && (Q('password').value.length > 0));
QE('loginButton', ok);
setDialogMode(0);
if ((e != null) && (e.keyCode == 13)) {
if ((box == 1) && (Q('username').value != '')) { Q('password').focus(); }
else if ((box == 2) && (Q('password').value != '')) { Q('loginButton').click(); }
}
if (e != null) { haltEvent(e); }
}
function validateCreate(box, e) {
setDialogMode(0);
var userok = false;
if (features & 0x200000) { userok = true; } else { userok = (Q('ausername').value.length > 0) && (Q('ausername').value.indexOf(' ') == -1) && (Q('ausername').value.indexOf('"') == -1) && (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';
if (Q('apassword1').value == '') {
QH('passWarning', '');
QV('passwordPolicyCallout', false);
} else {
if (!passRequirementsEx) {
// 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';
QH('passWarning', '<div style=color:red;cursor:pointer onclick=showPasswordPolicy()><b>' + "Password Policy" + '</b><div>'); // This is also a link to the password policy
QV('passwordPolicyCallout', true);
QH('passwordPolicyCallout', passwordPolicyText(Q('apassword1').value));
} else {
QH('passWarning', '');
QV('passwordPolicyCallout', false);
}
}
}
if ((e != null) && (e.keyCode == 13)) {
if ((box == 1) && userok) { Q('aemail').focus(); }
if ((box == 2) && emailok) { Q('apassword1').focus(); }
if ((box == 3) && pass1ok) { Q('apassword2').focus(); }
if ((box == 4) && pass2ok) { if (passRequirements.hint === true) { Q('apasswordhint').focus(); } else { box = 5; } }
if (box == 5) { if (newAccountPass == 1) { Q('anewaccountpass').focus(); } else { box = 6; } }
if (box == 6) { Q('createButton').click(); }
}
if (e != null) { haltEvent(e); }
QE('createButton', ok);
}
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 {
if (!passRequirementsEx) {
// 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);
}
function passwordPolicyText(pass) {
var policy = '<div style=text-align:left>';
var counts = strCount(pass);
if (passRequirements.min && ((pass == null) || (pass.length < passRequirements.min))) { policy += format("Minimum length of {0}", passRequirements.min) + '<br />'; }
if (passRequirements.max && ((pass == null) || (pass.length > passRequirements.max))) { policy += format("Maximum length of {0}", passRequirements.max) + '<br />'; }
if (passRequirements.upper && ((pass == null) || (counts.upper < passRequirements.upper))) { policy += format("{0} upper case", passRequirements.upper) + '<br />'; }
if (passRequirements.lower && ((pass == null) || (counts.lower < passRequirements.lower))) { policy += format("{0} lower case", passRequirements.lower) + '<br />'; }
if (passRequirements.numeric && ((pass == null) || (counts.numeric < passRequirements.numeric))) { policy += format("{0} numeric", passRequirements.numeric) + '<br />'; }
if (passRequirements.nonalpha && ((pass == null) || (counts.nonalpha < passRequirements.nonalpha))) { policy += format("{0} non-alphanumeric", passRequirements.nonalpha) + '<br />'; }
policy += '</div>';
return policy;
}
function showPasswordPolicy() {
messagebox("Password Policy", passwordPolicyText());
}
function validateReset(e) {
setDialogMode(0);
var x = validateEmail(Q('remail').value);
QE('eresetButton', x);
if ((e != null) && (e.keyCode == 13) && (x == true)) {
Q('eresetButton').click();
}
if (e != null) { haltEvent(e); }
}
// 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);
}
// 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; }
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;
for (var i = 0; i < password.length; i++) {
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++; }
}
return counts;
}
function checkToken() {
var t1 = Q('tokenInput').value;
var t2 = t1.split(' ').join('');
if (t1 != t2) { Q('tokenInput').value = t2; }
QE('tokenOkButton', (Q('tokenInput').value.length == 6) || (Q('tokenInput').value.length == 8) || (Q('tokenInput').value.length == 44));
}
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));
}
function changeEmailAddress() {
var email = Q('unconfirmedEmail').innerHTML;
var x = addHtmlValue("Email", '<input id=dp1email style=width:230px maxlength=256 value="' + email + '" {{{autocomplete}}}=off onchange=validateEmailAddress() onkeyup=validateEmailAddress() />');
setDialogMode(1, "Email Confirmation", 3, changeEmailAddressEx, x);
validateEmailAddress();
}
function validateEmailAddress() {
QE('idx_dlgOkButton', (validateEmail(Q('dp1email').value) == true));
}
function changeEmailAddressEx() {
Q('checkEmailVal').value = Q('dp1email').value;
QH('unconfirmedEmail', Q('dp1email').value);
Q('checkEmailButton').click();
}
function resentEmailConfirmation() {
Q('checkEmailVal').value = Q('unconfirmedEmail').innerHTML;
Q('checkEmailButton').click();
}
//
// 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);
}
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; }
function haltEvent(e) { if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
function haltReturn(e) { if (e.keyCode == 13) { haltEvent(e); } }
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
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; } }
function format(format) { var args = Array.prototype.slice.call(arguments, 1); return format.replace(/{(\d+)}/g, function (match, number) { return typeof args[number] != 'undefined' ? args[number] : match; }); };
function addTextLink(subtext, text, link) { var i = text.toLowerCase().indexOf(subtext.toLowerCase()); if (i == -1) { return text; } return text.substring(0, i) + '<a href="' + link + '">' + subtext + '</a>' + text.substring(i + subtext.length); }
function addHtmlValue(t, v) { return '<table><td style=width:120px;text-align:left>' + t + '<td><b>' + v + '</b></table>'; }
</script>
</body>
</html>