Focused state for custom checkbox (Closes #604)

This commit is contained in:
RainLoop Team 2015-04-25 17:50:10 +04:00
parent 48b91fb957
commit a11dcbed4b
25 changed files with 209 additions and 65 deletions

View file

@ -284,14 +284,17 @@
ko.components.register('x-script', require('Component/Script')); ko.components.register('x-script', require('Component/Script'));
if (Settings.settingsGet('MaterialDesign') && Globals.bAnimationSupported) if (/**false && /**/Settings.settingsGet('MaterialDesign') && Globals.bAnimationSupported)
{ {
ko.components.register('Checkbox', require('Component/MaterialDesign/Checkbox')); ko.components.register('Checkbox', require('Component/MaterialDesign/Checkbox'));
ko.components.register('CheckboxSimple', require('Component/Checkbox'));
} }
else else
{ {
// ko.components.register('Checkbox', require('Component/Classic/Checkbox')); // ko.components.register('Checkbox', require('Component/Classic/Checkbox'));
// ko.components.register('CheckboxSimple', require('Component/Classic/Checkbox'));
ko.components.register('Checkbox', require('Component/Checkbox')); ko.components.register('Checkbox', require('Component/Checkbox'));
ko.components.register('CheckboxSimple', require('Component/Checkbox'));
} }
Translator.initOnStartOrLangChange(Translator.initNotificationLanguage, Translator); Translator.initOnStartOrLangChange(Translator.initNotificationLanguage, Translator);

View file

@ -48,6 +48,7 @@
this.inverted = Utils.isUnd(oParams.inverted) ? false : !!oParams.inverted; this.inverted = Utils.isUnd(oParams.inverted) ? false : !!oParams.inverted;
this.labeled = !Utils.isUnd(oParams.label); this.labeled = !Utils.isUnd(oParams.label);
this.labelAnimated = !!oParams.labelAnimated;
} }
_.extend(AbstracCheckbox.prototype, AbstractComponent.prototype); _.extend(AbstracCheckbox.prototype, AbstractComponent.prototype);
@ -59,6 +60,13 @@
} }
}; };
AbstracCheckbox.prototype.keypress = function() {
if (!this.readOnly && this.enable() && !this.disable())
{
this.value(!this.value());
}
};
AbstracCheckbox.componentExportHelper = AbstractComponent.componentExportHelper; AbstracCheckbox.componentExportHelper = AbstractComponent.componentExportHelper;
module.exports = AbstracCheckbox; module.exports = AbstracCheckbox;

27
dev/External/ko.js vendored
View file

@ -317,6 +317,18 @@
} }
}; };
ko.bindingHandlers.keypress = {
'init': function (oElement, fValueAccessor, fAllBindingsAccessor, oViewModel) {
$(oElement).on('keypress.koKeyPress', function (oEvent) {
fValueAccessor().call(oViewModel, oEvent);
});
ko.utils.domNodeDisposal.addDisposeCallback(oElement, function () {
$(oElement).off('keypress.koKeyPress');
});
}
};
ko.bindingHandlers.onEnter = { ko.bindingHandlers.onEnter = {
'init': function (oElement, fValueAccessor, fAllBindingsAccessor, oViewModel) { 'init': function (oElement, fValueAccessor, fAllBindingsAccessor, oViewModel) {
$(oElement).on('keypress.koOnEnter', function (oEvent) { $(oElement).on('keypress.koOnEnter', function (oEvent) {
@ -333,6 +345,21 @@
} }
}; };
ko.bindingHandlers.onTab = {
'init': function (oElement, fValueAccessor, fAllBindingsAccessor, oViewModel) {
$(oElement).on('keydown.koOnTab', function (oEvent) {
if (oEvent && 9 === window.parseInt(oEvent.keyCode, 10))
{
return fValueAccessor().call(oViewModel, !!oEvent.shiftKey);
}
});
ko.utils.domNodeDisposal.addDisposeCallback(oElement, function () {
$(oElement).off('keydown.koOnTab');
});
}
};
ko.bindingHandlers.onEsc = { ko.bindingHandlers.onEsc = {
'init': function (oElement, fValueAccessor, fAllBindingsAccessor, oViewModel) { 'init': function (oElement, fValueAccessor, fAllBindingsAccessor, oViewModel) {
$(oElement).on('keypress.koOnEsc', function (oEvent) { $(oElement).on('keypress.koOnEsc', function (oEvent) {

View file

@ -6,8 +6,15 @@
} }
.flag-name { .flag-name {
border-bottom: 1px dashed #555; border-bottom: 1px dashed #555;
cursor: pointer; cursor: pointer;
padding: 2px;
&:focus {
outline: 1px;
outline-style: dotted;
}
} }
} }

View file

@ -37,13 +37,36 @@
.e-component { .e-component {
&.e-select {
select:focus {
outline: 1px;
outline-style: dotted;
}
}
&.e-checkbox { &.e-checkbox {
margin-bottom: 6px;
margin-left: -2px;
padding: 2px;
cursor: pointer; cursor: pointer;
&:focus {
outline: 1px;
outline-style: dotted;
}
.e-checkbox-icon {
padding: 1px 0 0 1px;
}
&.disabled { &.disabled {
cursor: default; cursor: default;
color: #999; color: #999;
outline: 0;
outline-style: none;
} }
} }
@ -60,11 +83,12 @@
.e-component.material-design { .e-component.material-design {
margin-top: 2px;
margin-bottom: 6px;
&.e-checkbox { &.e-checkbox {
margin-top: 2px;
padding: 2px 2px 1px 2px;
.sub-checkbox-container { .sub-checkbox-container {
display: inline-block; display: inline-block;
position: relative; position: relative;

View file

@ -60,7 +60,11 @@
} }
} }
.signMeLabel, .languageLabel { .languageLabel {
margin-top: 5px;
}
.signMeLabel .e-checkbox {
margin-top: 5px; margin-top: 5px;
} }

View file

@ -22,5 +22,11 @@
.flag-name { .flag-name {
border-bottom: 1px dashed #555; border-bottom: 1px dashed #555;
cursor: pointer; cursor: pointer;
padding: 2px;
&:focus {
outline: 1px;
outline-style: dotted;
}
} }
} }

View file

@ -21,6 +21,12 @@
color: #336699; color: #336699;
text-decoration: underline; text-decoration: underline;
cursor: pointer; cursor: pointer;
padding: 2px;
&:focus {
outline: 1px;
outline-style: dotted;
}
} }
.g-ui-min-height-300 { .g-ui-min-height-300 {
@ -181,12 +187,21 @@ e-spinner {
} }
.e-languages { .e-languages {
margin-top: 8px; margin-top: 8px;
color: #333; color: #333;
.flag-name { .flag-name {
color: #333; color: #333;
/*text-decoration: underline;*/
border-bottom: 1px dashed #333; border-bottom: 1px dashed #333;
cursor: pointer;
padding: 2px;
&:focus {
outline: 1px;
outline-style: dotted;
}
} }
} }

View file

@ -7,7 +7,7 @@ label {
cursor: pointer; cursor: pointer;
} }
label.inline { label.inline, span.inline {
display: inline-block; display: inline-block;
} }

View file

@ -8,6 +8,7 @@
ko = require('ko'), ko = require('ko'),
Utils = require('Common/Utils'), Utils = require('Common/Utils'),
Translator = require('Common/Translator'),
MessageStore = require('Stores/User/Message'), MessageStore = require('Stores/User/Message'),
@ -46,6 +47,19 @@
this.cancelCommand(); this.cancelCommand();
}); });
this.selectedDates = ko.computed(function () {
Translator.trigger();
return [
{'id': -1, 'name': Translator.i18n('SEARCH/LABEL_ADV_DATE_ALL')},
{'id': 3, 'name': Translator.i18n('SEARCH/LABEL_ADV_DATE_3_DAYS')},
{'id': 7, 'name': Translator.i18n('SEARCH/LABEL_ADV_DATE_7_DAYS')},
{'id': 30, 'name': Translator.i18n('SEARCH/LABEL_ADV_DATE_MONTH')},
{'id': 90, 'name': Translator.i18n('SEARCH/LABEL_ADV_DATE_3_MONTHS')},
{'id': 180, 'name': Translator.i18n('SEARCH/LABEL_ADV_DATE_6_MONTHS')},
{'id': 365, 'name': Translator.i18n('SEARCH/LABEL_ADV_DATE_YEAR')}
];
}, this);
kn.constructorEnd(this); kn.constructorEnd(this);
} }

View file

@ -525,7 +525,6 @@
if (window.Dropbox) if (window.Dropbox)
{ {
window.Dropbox.choose({ window.Dropbox.choose({
//'iframe': true,
'success': function(aFiles) { 'success': function(aFiles) {
if (aFiles && aFiles[0] && aFiles[0]['link']) if (aFiles && aFiles[0] && aFiles[0]['link'])
@ -1436,7 +1435,7 @@
Events.sub('window.resize.real', this.resizerTrigger); Events.sub('window.resize.real', this.resizerTrigger);
Events.sub('window.resize.real', _.debounce(this.resizerTrigger, 50)); Events.sub('window.resize.real', _.debounce(this.resizerTrigger, 50));
if (this.dropboxEnabled()) if (this.dropboxEnabled() && this.dropboxApiKey() && !window.Dropbox)
{ {
oScript = window.document.createElement('script'); oScript = window.document.createElement('script');
oScript.type = 'text/javascript'; oScript.type = 'text/javascript';

View file

@ -467,6 +467,21 @@
]); ]);
}; };
LoginUserView.prototype.selectLanguageOnTab = function (bShift)
{
if (!bShift)
{
var self = this;
_.delay(function () {
self.emailFocus(true);
}, 5);
return false;
}
return true;
};
module.exports = LoginUserView; module.exports = LoginUserView;
}()); }());

View file

@ -2,7 +2,7 @@
"name": "RainLoop", "name": "RainLoop",
"title": "RainLoop Webmail", "title": "RainLoop Webmail",
"version": "1.8.4", "version": "1.8.4",
"release": "311", "release": "312",
"description": "Simple, modern & fast web-based email client", "description": "Simple, modern & fast web-based email client",
"homepage": "http://rainloop.net", "homepage": "http://rainloop.net",
"main": "gulpfile.js", "main": "gulpfile.js",

View file

@ -66,7 +66,7 @@
.thm-border-radius(@login-border-radius); .thm-border-radius(@login-border-radius);
.thm-box-shadow(@login-box-shadow); .thm-box-shadow(@login-box-shadow);
.legend, .checkboxSignMe, .checkboxAdditionalCodeSignMe, .g-ui-link, .social-button, .language-button { .legend, .e-checkbox-icon, .g-ui-link, .social-button, .language-button {
color: @login-color !important; color: @login-color !important;
} }
} }
@ -85,7 +85,7 @@
color: @languages-color; color: @languages-color;
.flag-name { .flag-name {
color: @languages-color; color: @languages-color;
border-bottom: 1px dashed @languages-color; border-color: @languages-color;
} }
} }

View file

@ -26,9 +26,16 @@
<div class="control-group"> <div class="control-group">
<label class="control-label" data-i18n="TAB_CONTACTS/LABEL_STORAGE_TYPE"></label> <label class="control-label" data-i18n="TAB_CONTACTS/LABEL_STORAGE_TYPE"></label>
<div class="controls"> <div class="controls">
<select data-bind="options: contactsTypesOptions, value: mainContactsType, <div data-bind="component: {
optionsText: 'name', optionsValue: 'id', optionsAfterRender: defautOptionsAfterRender, saveTrigger: contactsTypeTrigger"></select> name: 'Select',
<div data-bind="saveTrigger: contactsTypeTrigger"></div> params: {
options: contactsTypesOptions,
value: mainContactsType,
trigger: contactsTypeTrigger,
optionsText: 'name',
optionsValue: 'id'
}
}"></div>
</div> </div>
</div> </div>
<br /> <br />

View file

@ -15,7 +15,7 @@
<span class="flag-wrapper"> <span class="flag-wrapper">
<span data-bind="css: 'flag flag-' + language()" style=""></span> <span data-bind="css: 'flag flag-' + language()" style=""></span>
</span> </span>
<span class="flag-name" data-bind="text: languageFullName, click: selectLanguage"></span> <span class="flag-name" tabindex="0" data-bind="text: languageFullName, click: selectLanguage, keypress: selectLanguage"></span>
&nbsp;&nbsp; &nbsp;&nbsp;
<div data-bind="component: { <div data-bind="component: {
name: 'SaveTrigger', name: 'SaveTrigger',
@ -31,7 +31,7 @@
<span class="flag-wrapper"> <span class="flag-wrapper">
<span data-bind="css: 'flag flag-' + languageAdmin()" style=""></span> <span data-bind="css: 'flag flag-' + languageAdmin()" style=""></span>
</span> </span>
<span class="flag-name" data-bind="text: languageAdminFullName, click: selectLanguageAdmin"></span> <span class="flag-name" tabindex="0" data-bind="text: languageAdminFullName, click: selectLanguageAdmin, keypress: selectLanguageAdmin"></span>
&nbsp;&nbsp; &nbsp;&nbsp;
<div data-bind="component: { <div data-bind="component: {
name: 'SaveTrigger', name: 'SaveTrigger',

View file

@ -1,10 +1,10 @@
<label class="e-component e-checkbox inline" data-bind="click: click, css: { 'disabled': disable() || !enable() }"> <span class="e-component e-checkbox inline" tabindex="0" data-bind="click: click, keypress: keypress, css: { 'disabled': disable() || !enable() }">
<i data-bind="css: value() ? <i role="checkbox" class="e-checkbox-icon" data-bind="css: value() ?
(inverted ? 'icon-checkbox-unchecked' : 'icon-checkbox-checked') : (inverted ? 'icon-checkbox-unchecked' : 'icon-checkbox-checked') :
(inverted ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked') (inverted ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked')
"></i> "></i>
<!-- ko if: labeled --> <!-- ko if: labeled -->
&nbsp;&nbsp; &nbsp;&nbsp;
<span class="sub-label i18n" data-bind="attr: {'data-i18n': label}"></span> <span class="sub-label i18n" data-bind="attr: {'data-i18n': label}, css: {'i18n-animation': labelAnimated}"></span>
<!-- /ko --> <!-- /ko -->
</label> </span>

View file

@ -2,6 +2,6 @@
<input type="checkbox" data-bind="checked: value, disable: disable() || !enable()" style="margin: 0 0 1px 0;" /> <input type="checkbox" data-bind="checked: value, disable: disable() || !enable()" style="margin: 0 0 1px 0;" />
<!-- ko if: labeled --> <!-- ko if: labeled -->
&nbsp;&nbsp; &nbsp;&nbsp;
<span class="sub-label i18n" data-bind="attr: {'data-i18n': label}"></span> <span class="sub-label i18n" data-bind="attr: {'data-i18n': label}, css: {'i18n-animation': labelAnimated}"></span>
<!-- /ko --> <!-- /ko -->
</label> </label>

View file

@ -1,10 +1,10 @@
<label class="e-component e-checkbox material-design inline" data-bind="click: click, css: { 'disabled': disable() || !enable() }"> <span class="e-component e-checkbox material-design inline" tabindex="0" data-bind="click: click, keypress: keypress, css: { 'disabled': disable() || !enable() }">
<div class="sub-checkbox-container"> <div class="sub-checkbox-container" role="checkbox">
<div class="sub-checkbox" data-bind="css: {'checked': (value() && !inverted) || (!value() && inverted), <div class="sub-checkbox" data-bind="css: {'checked': (value() && !inverted) || (!value() && inverted),
'unchecked': (!value() && !inverted) || (value() && inverted), 'unchecked': (!value() && !inverted) || (value() && inverted),
'box': animationBox, 'checkmark': animationCheckmark}"></div> 'box': animationBox, 'checkmark': animationCheckmark}"></div>
</div> </div>
<!-- ko if: labeled --> <!-- ko if: labeled -->
<span class="sub-label i18n" data-bind="attr: {'data-i18n': label}"></span> <span class="sub-label i18n" data-bind="attr: {'data-i18n': label}, css: {'i18n-animation': labelAnimated}"></span>
<!-- /ko --> <!-- /ko -->
</label> </span>

View file

@ -1,6 +1,6 @@
<div class="e-component e-radio" data-bind="foreach: values"> <div class="e-component e-radio" data-bind="foreach: values">
<label data-bind="click: $parent.click, css: {'inline': $parent.inline}"> <label tabindex="0" data-bind="click: $parent.click, css: {'inline': $parent.inline}">
<i data-bind="css: $parent.value() === value ? 'icon-radio-checked' : 'icon-radio-unchecked'"></i> <i role="radio" data-bind="css: $parent.value() === value ? 'icon-radio-checked' : 'icon-radio-unchecked'"></i>
&nbsp;&nbsp; &nbsp;&nbsp;
<span class="sub-label i18n" data-bind="attr: {'data-i18n': label}"></span> <span class="sub-label i18n" data-bind="attr: {'data-i18n': label}"></span>
</label> </label>

View file

@ -1,18 +1,20 @@
<!-- ko if: preLabeled --> <div class="e-component e-select inline">
<span class="i18n" data-bind="attr: {'data-i18n': preLabel}"></span> <!-- ko if: preLabeled -->
&nbsp;&nbsp; <span class="i18n" data-bind="attr: {'data-i18n': preLabel}"></span>
<!-- /ko --> &nbsp;&nbsp;
<select data-bind="options: options, value: value, enable: enable, optionsText: optionsText, optionsValue: optionsValue, <!-- /ko -->
css: className, optionsAfterRender: defautOptionsAfterRender"></select> <select data-bind="options: options, value: value, enable: enable, optionsText: optionsText, optionsValue: optionsValue,
<!-- ko if: labeled --> css: className, optionsAfterRender: defautOptionsAfterRender"></select>
&nbsp; <!-- ko if: labeled -->
<span class="i18n" data-bind="attr: {'data-i18n': label}"></span> &nbsp;
&nbsp; <span class="i18n" data-bind="attr: {'data-i18n': label}"></span>
<!-- /ko --> &nbsp;
<!-- ko if: triggered --> <!-- /ko -->
&nbsp; <!-- ko if: triggered -->
<div data-bind="component: { &nbsp;
name: 'SaveTrigger', <div data-bind="component: {
params: { value: trigger } name: 'SaveTrigger',
}"></div> params: { value: trigger }
<!-- /ko --> }"></div>
<!-- /ko -->
</div>

View file

@ -57,11 +57,13 @@
</div> </div>
</div> </div>
<div class="controls" data-bind="visible: additionalCode.visibility()"> <div class="controls" data-bind="visible: additionalCode.visibility()">
<label class="additionalCodeSignMeLabel" data-bind="click: function () { additionalCodeSignMe(!additionalCodeSignMe()); }"> <div class="additionalCodeSignMeLabel" data-bind="component: {
<i data-bind="css: additionalCodeSignMe() ? 'checkboxAdditionalCodeSignMe icon-checkbox-checked' : 'checkboxAdditionalCodeSignMe icon-checkbox-unchecked'"></i> name: 'CheckboxSimple',
&nbsp;&nbsp; params: {
<span class="i18n" data-i18n="LOGIN/LABEL_DONT_ASK_VERIFICATION_CODE"></span> label: 'LOGIN/LABEL_DONT_ASK_VERIFICATION_CODE',
</label> value: additionalCodeSignMe
}
}"></div>
</div> </div>
{{INCLUDE/BottomControlGroup/PLACE}} {{INCLUDE/BottomControlGroup/PLACE}}
<div class="controls"> <div class="controls">
@ -85,11 +87,21 @@
<i data-bind="css: langRequest() ? 'icon-spinner animated' : 'icon-world'"></i> <i data-bind="css: langRequest() ? 'icon-spinner animated' : 'icon-world'"></i>
</a> </a>
</div> </div>
<label class="signMeLabel inline" data-bind="click: function () { signMe(!signMe()); }, visible: signMeVisibility">
<div class="signMeLabel" data-bind="component: {
name: 'CheckboxSimple',
params: {
label: 'LOGIN/LABEL_SIGN_ME',
labelAnimated: true,
value: signMe
}
}"></div>
<!-- <label class="signMeLabel inline" data-bind="click: function () { signMe(!signMe()); }, visible: signMeVisibility">
<i data-bind="css: signMe() ? 'checkboxSignMe icon-checkbox-checked' : 'checkboxSignMe icon-checkbox-unchecked'"></i> <i data-bind="css: signMe() ? 'checkboxSignMe icon-checkbox-checked' : 'checkboxSignMe icon-checkbox-unchecked'"></i>
&nbsp;&nbsp; &nbsp;&nbsp;
<span class="i18n i18n-animation" data-i18n="LOGIN/LABEL_SIGN_ME"></span> <span class="i18n i18n-animation" data-i18n="LOGIN/LABEL_SIGN_ME"></span>
</label> </label>-->
</div> </div>
<div class="controls clearfix" data-bind="visible: '' !== forgotPasswordLinkUrl || '' !== registrationLinkUrl"> <div class="controls clearfix" data-bind="visible: '' !== forgotPasswordLinkUrl || '' !== registrationLinkUrl">
<div class="forgot-link thm-forgot pull-left" data-bind="visible: '' !== forgotPasswordLinkUrl" style="text-align: center"> <div class="forgot-link thm-forgot pull-left" data-bind="visible: '' !== forgotPasswordLinkUrl" style="text-align: center">
@ -110,11 +122,12 @@
<label class="flag-selector"> <label class="flag-selector">
<i data-bind="css: langRequest() ? 'icon-spinner animated' : 'icon-world'"></i> <i data-bind="css: langRequest() ? 'icon-spinner animated' : 'icon-world'"></i>
&nbsp;&nbsp; &nbsp;&nbsp;
<span class="flag-name" data-bind="text: languageFullName, click: selectLanguage"></span> <span class="flag-name" tabindex="0" data-bind="text: languageFullName, click: selectLanguage, keypress: selectLanguage, onTab: selectLanguageOnTab"></span>
</label> </label>
</div> </div>
{{INCLUDE/BottomFooter/PLACE}} {{INCLUDE/BottomFooter/PLACE}}
</center> </center>
</div> </div>
<a href="#" onclick="return false;"></a>
<div class="loginAfter"></div> <div class="loginAfter"></div>
</div> </div>

View file

@ -54,15 +54,15 @@
<span class="i18n" data-i18n="SEARCH/LABEL_ADV_DATE"></span> <span class="i18n" data-i18n="SEARCH/LABEL_ADV_DATE"></span>
</label> </label>
<div class="controls"> <div class="controls">
<select data-bind="value: selectedDateValue"> <div data-bind="component: {
<option value="-1" class="i18n" data-i18n="SEARCH/LABEL_ADV_DATE_ALL"></option> name: 'Select',
<option value="3" class="i18n" data-i18n="SEARCH/LABEL_ADV_DATE_3_DAYS"></option> params: {
<option value="7" class="i18n" data-i18n="SEARCH/LABEL_ADV_DATE_7_DAYS"></option> options: selectedDates,
<option value="30" class="i18n" data-i18n="SEARCH/LABEL_ADV_DATE_MONTH"></option> value: selectedDateValue,
<option value="90" class="i18n" data-i18n="SEARCH/LABEL_ADV_DATE_3_MONTHS"></option> optionsText: 'name',
<option value="180" class="i18n" data-i18n="SEARCH/LABEL_ADV_DATE_6_MONTHS"></option> optionsValue: 'id'
<option value="365" class="i18n" data-i18n="SEARCH/LABEL_ADV_DATE_YEAR"></option> }
</select> }"></div>
</div> </div>
</div> </div>
<div class="control-group"> <div class="control-group">

View file

@ -12,7 +12,7 @@
<span class="flag-wrapper"> <span class="flag-wrapper">
<span data-bind="css: 'flag flag-' + language()" style=""></span> <span data-bind="css: 'flag flag-' + language()" style=""></span>
</span> </span>
<span class="flag-name" data-bind="text: languageFullName, click: selectLanguage"></span> <span class="flag-name" tabindex="0" data-bind="text: languageFullName, click: selectLanguage, keypress: selectLanguage"></span>
&nbsp;&nbsp; &nbsp;&nbsp;
<div data-bind="component: { <div data-bind="component: {
name: 'SaveTrigger', name: 'SaveTrigger',

View file

@ -26,8 +26,8 @@
<label class="control-label"></label> <label class="control-label"></label>
<div class="controls"> <div class="controls">
<i class="icon-lock" /> <i class="icon-lock" />
&nbsp;&nbsp; &nbsp;
<span class="i18n g-ui-link" data-i18n="SETTINGS_SECURITY/LABEL_CONFIGURATE_TWO_FACTOR" data-bind="click:configureTwoFactor"></span> <span class="i18n g-ui-link" tabindex="0" data-i18n="SETTINGS_SECURITY/LABEL_CONFIGURATE_TWO_FACTOR" data-bind="click: configureTwoFactor, keypress: configureTwoFactor"></span>
</div> </div>
</div> </div>
</div> </div>