mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-08-28 11:39:21 +03:00
2-Step Verification (Google Authenticator)
Small fixes
This commit is contained in:
parent
cae0cc2f77
commit
419b47a81e
51 changed files with 1747 additions and 419 deletions
|
|
@ -7,6 +7,7 @@ function AdminSecurity()
|
|||
{
|
||||
this.csrfProtection = ko.observable(!!RL.settingsGet('UseTokenProtection'));
|
||||
this.openPGP = ko.observable(!!RL.settingsGet('OpenPGP'));
|
||||
this.allowTwoFactorAuth = ko.observable(!!RL.settingsGet('AllowTwoFactorAuth'));
|
||||
|
||||
this.adminLogin = ko.observable(RL.settingsGet('AdminLogin'));
|
||||
this.adminPassword = ko.observable('');
|
||||
|
|
@ -72,6 +73,12 @@ AdminSecurity.prototype.onBuild = function ()
|
|||
'OpenPGP': bValue ? '1' : '0'
|
||||
});
|
||||
});
|
||||
|
||||
this.allowTwoFactorAuth.subscribe(function (bValue) {
|
||||
RL.remote().saveAdminConfig(Utils.emptyFunction, {
|
||||
'AllowTwoFactorAuth': bValue ? '1' : '0'
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
AdminSecurity.prototype.onHide = function ()
|
||||
|
|
|
|||
|
|
@ -937,6 +937,11 @@ RainLoopApp.prototype.bootstart = function ()
|
|||
Utils.removeSettingsViewModel(SettingsOpenPGP);
|
||||
}
|
||||
|
||||
if (!RL.settingsGet('AllowTwoFactorAuth'))
|
||||
{
|
||||
Utils.removeSettingsViewModel(SettingsSecurity);
|
||||
}
|
||||
|
||||
if (!bGoogle && !bFacebook && !bTwitter)
|
||||
{
|
||||
Utils.removeSettingsViewModel(SettingsSocialScreen);
|
||||
|
|
|
|||
|
|
@ -317,6 +317,9 @@ Enums.Notification = {
|
|||
'DomainNotAllowed': 109,
|
||||
'AccountNotAllowed': 110,
|
||||
|
||||
'AccountTwoFactorAuthRequired': 120,
|
||||
'AccountTwoFactorAuthError': 121,
|
||||
|
||||
'CantGetMessageList': 201,
|
||||
'CantGetMessage': 202,
|
||||
'CantDeleteMessage': 203,
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ NewHtmlEditorWrapper.prototype.init = function ()
|
|||
{
|
||||
self.editor.on('instanceReady', function () {
|
||||
|
||||
self.editor.setKeystroke(window.CKEDITOR.CTRL + 65/* A */, 'selectAll');
|
||||
self.editor.setKeystroke(window.CKEDITOR.CTRL + 65 /* A */, 'selectAll');
|
||||
|
||||
self.fOnReady();
|
||||
self.__resizable = true;
|
||||
|
|
|
|||
|
|
@ -568,6 +568,9 @@ Utils.initNotificationLanguage = function ()
|
|||
NotificationI18N[Enums.Notification.DomainNotAllowed] = Utils.i18n('NOTIFICATIONS/DOMAIN_NOT_ALLOWED');
|
||||
NotificationI18N[Enums.Notification.AccountNotAllowed] = Utils.i18n('NOTIFICATIONS/ACCOUNT_NOT_ALLOWED');
|
||||
|
||||
NotificationI18N[Enums.Notification.AccountTwoFactorAuthRequired] = Utils.i18n('NOTIFICATIONS/ACCOUNT_TWO_FACTOR_AUTH_REQUIRED');
|
||||
NotificationI18N[Enums.Notification.AccountTwoFactorAuthError] = Utils.i18n('NOTIFICATIONS/ACCOUNT_TWO_FACTOR_AUTH_ERROR');
|
||||
|
||||
NotificationI18N[Enums.Notification.CantGetMessageList] = Utils.i18n('NOTIFICATIONS/CANT_GET_MESSAGE_LIST');
|
||||
NotificationI18N[Enums.Notification.CantGetMessage] = Utils.i18n('NOTIFICATIONS/CANT_GET_MESSAGE');
|
||||
NotificationI18N[Enums.Notification.CantDeleteMessage] = Utils.i18n('NOTIFICATIONS/CANT_DELETE_MESSAGE');
|
||||
|
|
@ -672,14 +675,14 @@ Utils.delegateRun = function (oObject, sMethodName, aParameters, nDelay)
|
|||
Utils.killCtrlAandS = function (oEvent)
|
||||
{
|
||||
oEvent = oEvent || window.event;
|
||||
if (oEvent)
|
||||
if (oEvent && oEvent.ctrlKey && !oEvent.shiftKey && !oEvent.altKey)
|
||||
{
|
||||
var
|
||||
oSender = oEvent.target || oEvent.srcElement,
|
||||
iKey = oEvent.keyCode || oEvent.which
|
||||
;
|
||||
|
||||
if (oEvent.ctrlKey && iKey === Enums.EventKeyCode.S)
|
||||
if (iKey === Enums.EventKeyCode.S)
|
||||
{
|
||||
oEvent.preventDefault();
|
||||
return;
|
||||
|
|
@ -690,7 +693,7 @@ Utils.killCtrlAandS = function (oEvent)
|
|||
return;
|
||||
}
|
||||
|
||||
if (oEvent.ctrlKey && iKey === Enums.EventKeyCode.A)
|
||||
if (iKey === Enums.EventKeyCode.A)
|
||||
{
|
||||
if (window.getSelection)
|
||||
{
|
||||
|
|
@ -1716,48 +1719,3 @@ Utils.selectElement = function (element)
|
|||
}
|
||||
/* jshint onevar: true */
|
||||
};
|
||||
|
||||
Utils.openPgpImportPublicKeys = function (sPublicKeysArmored)
|
||||
{
|
||||
if (window.openpgp && RL)
|
||||
{
|
||||
var
|
||||
oOpenpgpKeyring = RL.data().openpgpKeyring,
|
||||
oImported = window.openpgp.key.readArmored(sPublicKeysArmored)
|
||||
;
|
||||
|
||||
if (oOpenpgpKeyring && oImported && !oImported.err && Utils.isArray(oImported.keys) &&
|
||||
0 < oImported.keys.length)
|
||||
{
|
||||
_.each(oImported.keys, function (oPrivKey) {
|
||||
if (oPrivKey)
|
||||
{
|
||||
window.console.log(oPrivKey);
|
||||
// var oKey = oOpenpgpKeyring.getKeysForKeyId(oPrivKey.primaryKey.getFingerprint());
|
||||
// if (oKey && oKey[0])
|
||||
// {
|
||||
// if (oKey[0].isPublic())
|
||||
// {
|
||||
// oPrivKey.update(oKey[0]);
|
||||
// oOpenpgpKeyring.publicKeys.removeForId(oPrivKey.primaryKey.getFingerprint());
|
||||
// oOpenpgpKeyring.privateKeys.push(oPrivKey);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// oKey[0].update(oPrivKey);
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// oOpenpgpKeyring.importKey(oPrivKey.armored);
|
||||
// }
|
||||
}
|
||||
});
|
||||
|
||||
oOpenpgpKeyring.store();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -151,15 +151,14 @@ FolderModel.prototype.initComputed = function ()
|
|||
{
|
||||
return '' + iCount;
|
||||
}
|
||||
else if (0 < iUnread && Enums.FolderType.Trash !== iType && Enums.FolderType.SentItems !== iType)
|
||||
else if (0 < iUnread && Enums.FolderType.Trash !== iType && Enums.FolderType.Archive !== iType && Enums.FolderType.SentItems !== iType)
|
||||
{
|
||||
return '' + iUnread;
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
// return 0 < iUnread && (Enums.FolderType.Inbox === iType || Enums.FolderType.Spam === iType) ? '' + iUnread :
|
||||
// (0 < iCount && Enums.FolderType.Draft === iType ? '' + iCount : '');
|
||||
|
||||
}, this);
|
||||
|
||||
this.canBeDeleted = ko.computed(function () {
|
||||
|
|
|
|||
120
dev/Settings/Security.js
Normal file
120
dev/Settings/Security.js
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
/* RainLoop Webmail (c) RainLoop Team | Licensed under CC BY-NC-SA 3.0 */
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
function SettingsSecurity()
|
||||
{
|
||||
this.processing = ko.observable(false);
|
||||
this.clearing = ko.observable(false);
|
||||
|
||||
this.viewUser = ko.observable('');
|
||||
this.viewEnable = ko.observable(false);
|
||||
this.viewEnable.subs = true;
|
||||
this.twoFactorStatus = ko.observable(false);
|
||||
|
||||
this.viewSecret = ko.observable('');
|
||||
this.viewBackupCodes = ko.observable('');
|
||||
this.viewUrl = ko.observable('');
|
||||
|
||||
this.bFirst = true;
|
||||
|
||||
this.viewTwoFactorStatus = ko.computed(function () {
|
||||
Globals.langChangeTrigger();
|
||||
return Utils.i18n(
|
||||
this.twoFactorStatus() ?
|
||||
'SETTINGS_SECURITY/TWO_FACTOR_SECRET_CONFIGURED_DESC' :
|
||||
'SETTINGS_SECURITY/TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC'
|
||||
);
|
||||
}, this);
|
||||
|
||||
this.onResult = _.bind(this.onResult, this);
|
||||
}
|
||||
|
||||
Utils.addSettingsViewModel(SettingsSecurity, 'SettingsSecurity', 'SETTINGS_LABELS/LABEL_SECURITY_NAME', 'security');
|
||||
|
||||
SettingsSecurity.prototype.createTwoFactor = function ()
|
||||
{
|
||||
this.processing(true);
|
||||
RL.remote().createTwoFactor(this.onResult);
|
||||
};
|
||||
|
||||
SettingsSecurity.prototype.enableTwoFactor = function ()
|
||||
{
|
||||
this.processing(true);
|
||||
RL.remote().enableTwoFactor(this.onResult, this.viewEnable());
|
||||
};
|
||||
|
||||
SettingsSecurity.prototype.testTwoFactor = function ()
|
||||
{
|
||||
kn.showScreenPopup(PopupsTwoFactorTestViewModel);
|
||||
};
|
||||
|
||||
SettingsSecurity.prototype.clearTwoFactor = function ()
|
||||
{
|
||||
this.viewSecret('');
|
||||
this.viewBackupCodes('');
|
||||
this.viewUrl('');
|
||||
|
||||
this.clearing(true);
|
||||
RL.remote().clearTwoFactor(this.onResult);
|
||||
};
|
||||
|
||||
SettingsSecurity.prototype.onShow = function ()
|
||||
{
|
||||
this.viewSecret('');
|
||||
this.viewBackupCodes('');
|
||||
this.viewUrl('');
|
||||
};
|
||||
|
||||
SettingsSecurity.prototype.onResult = function (sResult, oData)
|
||||
{
|
||||
this.processing(false);
|
||||
this.clearing(false);
|
||||
|
||||
if (Enums.StorageResultType.Success === sResult && oData && oData.Result)
|
||||
{
|
||||
this.viewUser(Utils.pString(oData.Result.User));
|
||||
this.viewEnable(!!oData.Result.Enable);
|
||||
this.twoFactorStatus(!!oData.Result.IsSet);
|
||||
|
||||
this.viewSecret(Utils.pString(oData.Result.Secret));
|
||||
this.viewBackupCodes(Utils.pString(oData.Result.BackupCodes).replace(/[\s]+/g, ' '));
|
||||
this.viewUrl(Utils.pString(oData.Result.Url));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.viewUser('');
|
||||
this.viewEnable(false);
|
||||
this.twoFactorStatus(false);
|
||||
|
||||
this.viewSecret('');
|
||||
this.viewBackupCodes('');
|
||||
this.viewUrl('');
|
||||
}
|
||||
|
||||
if (this.bFirst)
|
||||
{
|
||||
this.bFirst = false;
|
||||
var self = this;
|
||||
this.viewEnable.subscribe(function (bValue) {
|
||||
if (this.viewEnable.subs)
|
||||
{
|
||||
RL.remote().enableTwoFactor(function (sResult, oData) {
|
||||
if (Enums.StorageResultType.Success !== sResult || !oData || !oData.Result)
|
||||
{
|
||||
self.viewEnable.subs = false;
|
||||
self.viewEnable(false);
|
||||
self.viewEnable.subs = true;
|
||||
}
|
||||
}, bValue);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
};
|
||||
|
||||
SettingsSecurity.prototype.onBuild = function ()
|
||||
{
|
||||
this.processing(true);
|
||||
RL.remote().getTwoFactor(this.onResult);
|
||||
};
|
||||
|
|
@ -34,18 +34,74 @@ WebMailAjaxRemoteStorage.prototype.folders = function (fCallback)
|
|||
* @param {string} sPassword
|
||||
* @param {boolean} bSignMe
|
||||
* @param {string=} sLanguage
|
||||
* @param {string=} sAdditionalCode
|
||||
*/
|
||||
WebMailAjaxRemoteStorage.prototype.login = function (fCallback, sEmail, sLogin, sPassword, bSignMe, sLanguage)
|
||||
WebMailAjaxRemoteStorage.prototype.login = function (fCallback, sEmail, sLogin, sPassword, bSignMe, sLanguage, sAdditionalCode)
|
||||
{
|
||||
this.defaultRequest(fCallback, 'Login', {
|
||||
'Email': sEmail,
|
||||
'Login': sLogin,
|
||||
'Password': sPassword,
|
||||
'Language': sLanguage || '',
|
||||
'AdditionalCode': sAdditionalCode || '',
|
||||
'SignMe': bSignMe ? '1' : '0'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?Function} fCallback
|
||||
*/
|
||||
WebMailAjaxRemoteStorage.prototype.getTwoFactor = function (fCallback)
|
||||
{
|
||||
this.defaultRequest(fCallback, 'GetTwoFactorInfo');
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?Function} fCallback
|
||||
*/
|
||||
WebMailAjaxRemoteStorage.prototype.createTwoFactor = function (fCallback)
|
||||
{
|
||||
this.defaultRequest(fCallback, 'CreateTwoFactorSecret');
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?Function} fCallback
|
||||
*/
|
||||
WebMailAjaxRemoteStorage.prototype.clearTwoFactor = function (fCallback)
|
||||
{
|
||||
this.defaultRequest(fCallback, 'ClearTwoFactorInfo');
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?Function} fCallback
|
||||
* @param {string} sCode
|
||||
*/
|
||||
WebMailAjaxRemoteStorage.prototype.testTwoFactor = function (fCallback, sCode)
|
||||
{
|
||||
this.defaultRequest(fCallback, 'TestTwoFactorInfo', {
|
||||
'Code': sCode
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?Function} fCallback
|
||||
* @param {boolean} bEnable
|
||||
*/
|
||||
WebMailAjaxRemoteStorage.prototype.enableTwoFactor = function (fCallback, bEnable)
|
||||
{
|
||||
this.defaultRequest(fCallback, 'EnableTwoFactor', {
|
||||
'Enable': bEnable ? '1' : '0'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?Function} fCallback
|
||||
*/
|
||||
WebMailAjaxRemoteStorage.prototype.clearTwoFactorInfo = function (fCallback)
|
||||
{
|
||||
this.defaultRequest(fCallback, 'ClearTwoFactorInfo');
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?Function} fCallback
|
||||
* @param {string} sEmail
|
||||
|
|
|
|||
|
|
@ -72,6 +72,9 @@ function WebMailDataStorage()
|
|||
this.signatureToAll = ko.observable(false);
|
||||
this.replyTo = ko.observable('');
|
||||
|
||||
// security
|
||||
this.enableTwoFactor = ko.observable(false);
|
||||
|
||||
// accounts
|
||||
this.accounts = ko.observableArray([]);
|
||||
this.accountsLoading = ko.observable(false).extend({'throttle': 100});
|
||||
|
|
@ -434,6 +437,7 @@ WebMailDataStorage.prototype.populateDataOnStart = function()
|
|||
this.replyTo(RL.settingsGet('ReplyTo'));
|
||||
this.signature(RL.settingsGet('Signature'));
|
||||
this.signatureToAll(!!RL.settingsGet('SignatureToAll'));
|
||||
this.enableTwoFactor(!!RL.settingsGet('EnableTwoFactor'));
|
||||
|
||||
this.lastFoldersHash = RL.local().get(Enums.ClientSideKeyName.FoldersLashHash) || '';
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@
|
|||
@import "Languages.less";
|
||||
@import "AddAccount.less";
|
||||
@import "OpenPgpKey.less";
|
||||
@import "TwoFactor.less";
|
||||
@import "Identity.less";
|
||||
@import "AdvancedSearch.less";
|
||||
@import "MessageList.less";
|
||||
|
|
|
|||
7
dev/Styles/TwoFactor.less
Normal file
7
dev/Styles/TwoFactor.less
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
.popups {
|
||||
.b-two-factor-test-content {
|
||||
.modal-header {
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,11 @@ function LoginViewModel()
|
|||
this.password = ko.observable('');
|
||||
this.signMe = ko.observable(false);
|
||||
|
||||
this.additionalCode = ko.observable('');
|
||||
this.additionalCode.error = ko.observable(false);
|
||||
this.additionalCode.focused = ko.observable(false);
|
||||
this.additionalCode.visibility = ko.observable(false);
|
||||
|
||||
this.logoImg = Utils.trim(RL.settingsGet('LoginLogo'));
|
||||
this.loginDescription = Utils.trim(RL.settingsGet('LoginDescription'));
|
||||
this.logoCss = Utils.trim(RL.settingsGet('LoginCss'));
|
||||
|
|
@ -29,6 +34,8 @@ function LoginViewModel()
|
|||
|
||||
this.email.subscribe(function () {
|
||||
this.emailError(false);
|
||||
this.additionalCode('');
|
||||
this.additionalCode.visibility(false);
|
||||
}, this);
|
||||
|
||||
this.login.subscribe(function () {
|
||||
|
|
@ -39,6 +46,14 @@ function LoginViewModel()
|
|||
this.passwordError(false);
|
||||
}, this);
|
||||
|
||||
this.additionalCode.subscribe(function () {
|
||||
this.additionalCode.error(false);
|
||||
}, this);
|
||||
|
||||
this.additionalCode.visibility.subscribe(function () {
|
||||
this.additionalCode.error(false);
|
||||
}, this);
|
||||
|
||||
this.submitRequest = ko.observable(false);
|
||||
this.submitError = ko.observable('');
|
||||
|
||||
|
|
@ -68,7 +83,12 @@ function LoginViewModel()
|
|||
this.emailError('' === Utils.trim(this.email()));
|
||||
this.passwordError('' === Utils.trim(this.password()));
|
||||
|
||||
if (this.emailError() || this.passwordError())
|
||||
if (this.additionalCode.visibility())
|
||||
{
|
||||
this.additionalCode.error('' === Utils.trim(this.additionalCode()));
|
||||
}
|
||||
|
||||
if (this.emailError() || this.passwordError() || this.additionalCode.error())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -81,12 +101,28 @@ function LoginViewModel()
|
|||
{
|
||||
if (oData.Result)
|
||||
{
|
||||
RL.loginAndLogoutReload();
|
||||
if (oData.TwoFactorAuth)
|
||||
{
|
||||
this.additionalCode('');
|
||||
this.additionalCode.visibility(true);
|
||||
this.additionalCode.focused(true);
|
||||
|
||||
this.submitRequest(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
RL.loginAndLogoutReload();
|
||||
}
|
||||
}
|
||||
else if (oData.ErrorCode)
|
||||
{
|
||||
this.submitRequest(false);
|
||||
this.submitError(Utils.getNotification(oData.ErrorCode));
|
||||
|
||||
if ('' === this.submitError())
|
||||
{
|
||||
this.submitError(Utils.getNotification(Enums.Notification.UnknownError));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -100,7 +136,8 @@ function LoginViewModel()
|
|||
}
|
||||
|
||||
}, this), this.email(), this.login(), this.password(), !!this.signMe(),
|
||||
this.bSendLanguage ? this.mainLanguage() : '');
|
||||
this.bSendLanguage ? this.mainLanguage() : '',
|
||||
this.additionalCode.visibility() ? this.additionalCode() : '');
|
||||
|
||||
return true;
|
||||
|
||||
|
|
|
|||
|
|
@ -929,12 +929,12 @@ PopupsComposeViewModel.prototype.onBuild = function ()
|
|||
|
||||
if (oEvent && self.modalVisibility() && RL.data().useKeyboardShortcuts())
|
||||
{
|
||||
if (self.bAllowCtrlS && oEvent.ctrlKey && Enums.EventKeyCode.S === oEvent.keyCode)
|
||||
if (self.bAllowCtrlS && oEvent.ctrlKey && !oEvent.shiftKey && !oEvent.altKey && Enums.EventKeyCode.S === oEvent.keyCode)
|
||||
{
|
||||
self.saveCommand();
|
||||
bResult = false;
|
||||
}
|
||||
else if (oEvent.ctrlKey && Enums.EventKeyCode.Enter === oEvent.keyCode)
|
||||
else if (oEvent.ctrlKey && !oEvent.shiftKey && !oEvent.altKey && Enums.EventKeyCode.Enter === oEvent.keyCode)
|
||||
{
|
||||
self.sendCommand();
|
||||
bResult = false;
|
||||
|
|
|
|||
55
dev/ViewModels/PopupsTwoFactorTestViewModel.js
Normal file
55
dev/ViewModels/PopupsTwoFactorTestViewModel.js
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/* RainLoop Webmail (c) RainLoop Team | Licensed under CC BY-NC-SA 3.0 */
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends KnoinAbstractViewModel
|
||||
*/
|
||||
function PopupsTwoFactorTestViewModel()
|
||||
{
|
||||
KnoinAbstractViewModel.call(this, 'Popups', 'PopupsTwoFactorTest');
|
||||
|
||||
var self = this;
|
||||
|
||||
this.code = ko.observable('');
|
||||
this.code.focused = ko.observable(false);
|
||||
this.code.status = ko.observable(null);
|
||||
|
||||
this.testing = ko.observable(false);
|
||||
|
||||
// commands
|
||||
this.testCode = Utils.createCommand(this, function () {
|
||||
|
||||
this.testing(true);
|
||||
RL.remote().testTwoFactor(function (sResult, oData) {
|
||||
|
||||
self.testing(false);
|
||||
self.code.status(Enums.StorageResultType.Success === sResult && oData && oData.Result ? true : false);
|
||||
|
||||
}, this.code());
|
||||
|
||||
}, function () {
|
||||
return '' !== this.code() && !this.testing();
|
||||
});
|
||||
|
||||
Knoin.constructorEnd(this);
|
||||
}
|
||||
|
||||
Utils.extendAsViewModel('PopupsTwoFactorTestViewModel', PopupsTwoFactorTestViewModel);
|
||||
|
||||
PopupsTwoFactorTestViewModel.prototype.clearPopup = function ()
|
||||
{
|
||||
this.code('');
|
||||
this.code.focused(false);
|
||||
this.code.status(null);
|
||||
this.testing(false);
|
||||
};
|
||||
|
||||
PopupsTwoFactorTestViewModel.prototype.onShow = function ()
|
||||
{
|
||||
this.clearPopup();
|
||||
};
|
||||
|
||||
PopupsTwoFactorTestViewModel.prototype.onFocus = function ()
|
||||
{
|
||||
this.code.focused(true);
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue