Restructure some popups to use <form>

This commit is contained in:
the-djmaze 2022-02-25 13:12:44 +01:00
parent 169dbfecca
commit b82d26b71b
7 changed files with 201 additions and 227 deletions

View file

@ -2,7 +2,6 @@ import { getNotification } from 'Common/Translator';
import Remote from 'Remote/User/Fetch'; import Remote from 'Remote/User/Fetch';
import { decorateKoCommands } from 'Knoin/Knoin';
import { AbstractViewPopup } from 'Knoin/AbstractViews'; import { AbstractViewPopup } from 'Knoin/AbstractViews';
export class AccountPopupView extends AbstractViewPopup { export class AccountPopupView extends AbstractViewPopup {
@ -26,39 +25,32 @@ export class AccountPopupView extends AbstractViewPopup {
this.email.subscribe(() => this.emailError(false)); this.email.subscribe(() => this.emailError(false));
this.password.subscribe(() => this.passwordError(false)); this.password.subscribe(() => this.passwordError(false));
decorateKoCommands(this, {
addAccountCommand: self => !self.submitRequest()
});
} }
addAccountCommand() { submitForm() {
this.emailError(!this.email().trim()); if (!this.submitRequest()) {
this.passwordError(!this.password().trim()); const email = this.email().trim(), pass = this.password();
this.emailError(!email);
if (this.emailError() || this.passwordError()) { this.passwordError(!pass);
return false; if (!this.emailError() && pass) {
} this.submitRequest(true);
Remote.request('AccountSetup', (iError, data) => {
this.submitRequest(true); this.submitRequest(false);
if (iError) {
Remote.request('AccountSetup', (iError, data) => { this.submitError(getNotification(iError));
this.submitRequest(false); this.submitErrorAdditional((data && data.ErrorMessageAdditional) || '');
if (iError) { } else {
this.submitError(getNotification(iError)); rl.app.accountsAndIdentities();
this.submitErrorAdditional((data && data.ErrorMessageAdditional) || ''); this.closeCommand();
} else { }
rl.app.accountsAndIdentities(); }, {
this.closeCommand(); Email: email,
} Password: pass,
}, { New: this.isNew() ? 1 : 0
Email: this.email(), }
Password: this.password(), );
New: this.isNew() ? 1 : 0
} }
); }
return true;
} }
onShow(account) { onShow(account) {

View file

@ -11,7 +11,6 @@ import { SettingsUserStore } from 'Stores/User/Settings';
import Remote from 'Remote/User/Fetch'; import Remote from 'Remote/User/Fetch';
import { decorateKoCommands } from 'Knoin/Knoin';
import { AbstractViewPopup } from 'Knoin/AbstractViews'; import { AbstractViewPopup } from 'Knoin/AbstractViews';
import { setFolder, getFolderFromCacheList } from 'Common/Cache'; import { setFolder, getFolderFromCacheList } from 'Common/Cache';
@ -42,48 +41,42 @@ export class FolderCreatePopupView extends AbstractViewPopup {
); );
this.defaultOptionsAfterRender = defaultOptionsAfterRender; this.defaultOptionsAfterRender = defaultOptionsAfterRender;
decorateKoCommands(this, {
createFolderCommand: self => self.simpleFolderNameValidation(self.folderName())
});
} }
createFolderCommand() { submitForm() {
let parentFolderName = this.selectedParentValue(); if (/^[^\\/]+$/g.test(this.folderName())) {
if (!parentFolderName && 1 < FolderUserStore.namespace.length) { let parentFolderName = this.selectedParentValue();
parentFolderName = FolderUserStore.namespace.slice(0, FolderUserStore.namespace.length - 1); if (!parentFolderName && 1 < FolderUserStore.namespace.length) {
} parentFolderName = FolderUserStore.namespace.slice(0, FolderUserStore.namespace.length - 1);
}
Remote.abort('Folders').post('FolderCreate', FolderUserStore.foldersCreating, { Remote.abort('Folders').post('FolderCreate', FolderUserStore.foldersCreating, {
Folder: this.folderName(), Folder: this.folderName(),
Parent: parentFolderName, Parent: parentFolderName,
Subscribe: this.folderSubscribe() ? 1 : 0 Subscribe: this.folderSubscribe() ? 1 : 0
}) })
.then( .then(
data => { data => {
const folder = getFolderFromCacheList(parentFolderName), const folder = getFolderFromCacheList(parentFolderName),
subFolder = FolderModel.reviveFromJson(data.Result), subFolder = FolderModel.reviveFromJson(data.Result),
folders = (folder ? folder.subFolders : FolderUserStore.folderList); folders = (folder ? folder.subFolders : FolderUserStore.folderList);
setFolder(subFolder); setFolder(subFolder);
folders.push(subFolder); folders.push(subFolder);
sortFolders(folders); sortFolders(folders);
/* /*
var collator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'}); var collator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'});
console.log((folder ? folder.subFolders : FolderUserStore.folderList).sort(collator.compare)); console.log((folder ? folder.subFolders : FolderUserStore.folderList).sort(collator.compare));
*/ */
}, },
error => { error => {
FolderUserStore.folderListError( FolderUserStore.folderListError(
getNotification(error.code, '', Notification.CantCreateFolder) getNotification(error.code, '', Notification.CantCreateFolder)
+ '.\n' + error.message); + '.\n' + error.message);
} }
); );
this.closeCommand(); this.closeCommand();
} }
simpleFolderNameValidation(sName) {
return /^[^\\/]+$/g.test(sName);
} }
onShow() { onShow() {

View file

@ -2,7 +2,6 @@ import { getNotification } from 'Common/Translator';
import Remote from 'Remote/User/Fetch'; import Remote from 'Remote/User/Fetch';
import { decorateKoCommands } from 'Knoin/Knoin';
import { AbstractViewPopup } from 'Knoin/AbstractViews'; import { AbstractViewPopup } from 'Knoin/AbstractViews';
const reEmail = /^[^@\s]+@[^@\s]+$/; const reEmail = /^[^@\s]+@[^@\s]+$/;
@ -60,60 +59,57 @@ export class IdentityPopupView extends AbstractViewPopup {
this.replyTo.valueHasMutated(); this.replyTo.valueHasMutated();
this.bcc.valueHasMutated(); this.bcc.valueHasMutated();
*/ */
decorateKoCommands(this, {
addOrEditIdentityCommand: self => !self.submitRequest()
});
} }
addOrEditIdentityCommand() { submitForm() {
if (this.signature && this.signature.__fetchEditorValue) { if (!this.submitRequest()) {
this.signature.__fetchEditorValue(); if (this.signature && this.signature.__fetchEditorValue) {
} this.signature.__fetchEditorValue();
if (!this.emailHasError()) {
this.emailHasError(!this.email().trim());
}
if (this.emailHasError()) {
if (!this.owner()) {
this.emailFocused(true);
} }
return false; if (!this.emailHasError()) {
} this.emailHasError(!this.email().trim());
}
if (this.replyToHasError()) { if (this.emailHasError()) {
this.replyToFocused(true); if (!this.owner()) {
return false; this.emailFocused(true);
}
if (this.bccHasError()) {
this.bccFocused(true);
return false;
}
this.submitRequest(true);
Remote.request('IdentityUpdate', iError => {
this.submitRequest(false);
if (iError) {
this.submitError(getNotification(iError));
} else {
rl.app.accountsAndIdentities();
this.closeCommand();
} }
}, {
Id: this.id,
Email: this.email(),
Name: this.name(),
ReplyTo: this.replyTo(),
Bcc: this.bcc(),
Signature: this.signature(),
SignatureInsertBefore: this.signatureInsertBefore() ? 1 : 0
}
);
return true; return;
}
if (this.replyToHasError()) {
this.replyToFocused(true);
return;
}
if (this.bccHasError()) {
this.bccFocused(true);
return;
}
this.submitRequest(true);
Remote.request('IdentityUpdate', iError => {
this.submitRequest(false);
if (iError) {
this.submitError(getNotification(iError));
} else {
rl.app.accountsAndIdentities();
this.closeCommand();
}
}, {
Id: this.id,
Email: this.email(),
Name: this.name(),
ReplyTo: this.replyTo(),
Bcc: this.bcc(),
Signature: this.signature(),
SignatureInsertBefore: this.signatureInsertBefore() ? 1 : 0
}
);
}
} }
clearPopup() { clearPopup() {

View file

@ -5,33 +5,30 @@
<span data-bind="visible: !isNew()" data-i18n="POPUPS_ADD_ACCOUNT/TITLE_UPDATE_ACCOUNT"></span> <span data-bind="visible: !isNew()" data-i18n="POPUPS_ADD_ACCOUNT/TITLE_UPDATE_ACCOUNT"></span>
</h3> </h3>
</header> </header>
<form class="modal-body form-horizontal" autocomplete="off"> <form id="accountform" class="modal-body form-horizontal" autocomplete="off" data-bind="submit: submitForm">
<div class="alert" data-bind="visible: '' !== submitError()"> <div class="alert" data-bind="visible: '' !== submitError()">
<a href="#" class="close" data-bind="click: function () { submitError('') }">×</a> <a href="#" class="close" data-bind="click: function () { submitError('') }">×</a>
<span data-bind="text: submitError"></span> <span data-bind="text: submitError"></span>
<div data-bind="visible: submitErrorAdditional"> <div data-bind="visible: submitErrorAdditional, text: submitErrorAdditional"></div>
<br>
<span data-bind="text: submitErrorAdditional"></span>
</div>
</div> </div>
<div class="control-group" data-bind="css: {'error': emailError}"> <div class="control-group" data-bind="css: {'error': emailError}">
<label data-i18n="GLOBAL/EMAIL"></label> <label data-i18n="GLOBAL/EMAIL"></label>
<strong style="margin-top: 5px;" data-bind="visible: !isNew(), text: email"></strong> <strong style="margin-top: 5px;" data-bind="visible: !isNew(), text: email"></strong>
<input type="email" class="input-xlarge" <input type="email" class="input-xlarge"
autofocus="" autocorrect="off" autocapitalize="off" spellcheck="false" autofocus="" autocorrect="off" autocapitalize="off" spellcheck="false"
data-bind="visible: isNew, textInput: email, onEnter: addAccountCommand"> data-bind="visible: isNew, textInput: email">
</div> </div>
<div class="control-group" data-bind="css: {'error': passwordError}"> <div class="control-group" data-bind="css: {'error': passwordError}">
<label data-i18n="GLOBAL/PASSWORD"></label> <label data-i18n="GLOBAL/PASSWORD"></label>
<input type="password" class="input-xlarge" autocomplete="new-password" autocorrect="off" autocapitalize="off" spellcheck="false" <input type="password" class="input-xlarge" autocomplete="new-password" autocorrect="off" autocapitalize="off" spellcheck="false"
data-bind="value: password, onEnter: addAccountCommand"> required="" data-bind="value: password">
</div> </div>
</form> </form>
<footer> <footer>
<a class="btn buttonAddAccount" data-bind="command: addAccountCommand"> <button form="accountform" class="btn buttonAddAccount">
<i data-bind="visible: isNew, css: {'icon-user-add': !submitRequest(), 'icon-spinner': submitRequest()}"></i> <i data-bind="visible: isNew, css: {'icon-user-add': !submitRequest(), 'icon-spinner': submitRequest()}"></i>
<span data-bind="visible: isNew" data-i18n="POPUPS_ADD_ACCOUNT/BUTTON_ADD_ACCOUNT"></span> <span data-bind="visible: isNew" data-i18n="POPUPS_ADD_ACCOUNT/BUTTON_ADD_ACCOUNT"></span>
<i data-bind="visible: !isNew(), css: {'icon-ok': !submitRequest(), 'icon-spinner': submitRequest()}"></i> <i data-bind="visible: !isNew(), css: {'icon-ok': !submitRequest(), 'icon-spinner': submitRequest()}"></i>
<span data-bind="visible: !isNew()" data-i18n="POPUPS_ADD_ACCOUNT/BUTTON_UPDATE_ACCOUNT"></span> <span data-bind="visible: !isNew()" data-i18n="POPUPS_ADD_ACCOUNT/BUTTON_UPDATE_ACCOUNT"></span>
</a> </button>
</footer> </footer>

View file

@ -2,88 +2,86 @@
<a href="#" class="close" data-bind="command: closeCommand">×</a> <a href="#" class="close" data-bind="command: closeCommand">×</a>
<h3 data-i18n="SEARCH/TITLE_ADV"></h3> <h3 data-i18n="SEARCH/TITLE_ADV"></h3>
</header> </header>
<div class="modal-body"> <form id="modal-body searchform" class="form-horizontal" action="#/" autocomplete="off" data-bind="submit: submitForm">
<form id="searchform" class="form-horizontal" action="#/" autocomplete="off" data-bind="submit: submitForm"> <div class="row">
<div class="row"> <div class="span4">
<div class="span4"> <div class="control-group">
<div class="control-group"> <label data-i18n="GLOBAL/FROM"></label>
<label data-i18n="GLOBAL/FROM"></label> <input type="text" autofocus="" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
<input type="text" autofocus="" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" data-bind="value: from, onEsc: closeCommand">
data-bind="value: from, onEsc: closeCommand">
</div>
<div class="control-group">
<label data-i18n="GLOBAL/TO"></label>
<input type="text" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
data-bind="value: to, onEsc: closeCommand">
</div>
<div class="control-group">
<label data-i18n="GLOBAL/SUBJECT"></label>
<input type="text" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
data-bind="value: subject, onEsc: closeCommand">
</div>
<div class="control-group">
<label data-i18n="SEARCH/LABEL_ADV_TEXT"></label>
<input type="text" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
data-bind="value: text, onEsc: closeCommand">
</div>
</div> </div>
<div class="span4"> <div class="control-group">
<div class="control-group"> <label data-i18n="GLOBAL/TO"></label>
<label data-i18n="SEARCH/LABEL_ADV_DATE"></label> <input type="text" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
data-bind="value: to, onEsc: closeCommand">
</div>
<div class="control-group">
<label data-i18n="GLOBAL/SUBJECT"></label>
<input type="text" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
data-bind="value: subject, onEsc: closeCommand">
</div>
<div class="control-group">
<label data-i18n="SEARCH/LABEL_ADV_TEXT"></label>
<input type="text" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
data-bind="value: text, onEsc: closeCommand">
</div>
</div>
<div class="span4">
<div class="control-group">
<label data-i18n="SEARCH/LABEL_ADV_DATE"></label>
<div data-bind="component: {
name: 'Select',
params: {
options: selectedDates,
value: selectedDateValue,
optionsText: 'name',
optionsValue: 'id'
}
}"></div>
</div>
<!-- ko if: showMultisearch -->
<div class="control-group">
<label data-i18n="SEARCH/LABEL_ADV_SUBFOLDERS"></label>
<div data-bind="component: {
name: 'Select',
params: {
options: selectedTree,
value: selectedTreeValue,
optionsText: 'name',
optionsValue: 'id'
}
}"></div>
</div>
<!-- /ko -->
<div class="control-group">
<label></label>
<div>
<div data-bind="component: { <div data-bind="component: {
name: 'Select', name: 'Checkbox',
params: { params: {
options: selectedDates, label: 'SEARCH/LABEL_ADV_UNSEEN',
value: selectedDateValue, value: unseen
optionsText: 'name',
optionsValue: 'id'
} }
}"></div> }"></div>
</div>
<!-- ko if: showMultisearch -->
<div class="control-group">
<label data-i18n="SEARCH/LABEL_ADV_SUBFOLDERS"></label>
<div data-bind="component: { <div data-bind="component: {
name: 'Select', name: 'Checkbox',
params: { params: {
options: selectedTree, label: 'SEARCH/LABEL_ADV_FLAGGED',
value: selectedTreeValue, value: starred
optionsText: 'name', }
optionsValue: 'id' }"></div>
<div data-bind="component: {
name: 'Checkbox',
params: {
label: 'SEARCH/LABEL_ADV_HAS_ATTACHMENT',
value: hasAttachment
} }
}"></div> }"></div>
</div>
<!-- /ko -->
<div class="control-group">
<label></label>
<div>
<div data-bind="component: {
name: 'Checkbox',
params: {
label: 'SEARCH/LABEL_ADV_UNSEEN',
value: unseen
}
}"></div>
<div data-bind="component: {
name: 'Checkbox',
params: {
label: 'SEARCH/LABEL_ADV_FLAGGED',
value: starred
}
}"></div>
<div data-bind="component: {
name: 'Checkbox',
params: {
label: 'SEARCH/LABEL_ADV_HAS_ATTACHMENT',
value: hasAttachment
}
}"></div>
</div>
</div> </div>
</div> </div>
</div> </div>
</form> </div>
</div> </form>
<footer> <footer>
<button type="submit" form="searchform" class="btn buttonAdvSearch" data-icon="🔎" data-i18n="GLOBAL/SEARCH"></button> <button form="searchform" class="btn buttonAdvSearch" data-icon="🔎" data-i18n="GLOBAL/SEARCH"></button>
</footer> </footer>

View file

@ -2,31 +2,29 @@
<a href="#" class="close" data-bind="command: closeCommand">×</a> <a href="#" class="close" data-bind="command: closeCommand">×</a>
<h3 data-i18n="POPUPS_CREATE_FOLDER/TITLE_CREATE_FOLDER"></h3> <h3 data-i18n="POPUPS_CREATE_FOLDER/TITLE_CREATE_FOLDER"></h3>
</header> </header>
<div class="modal-body"> <form id="createfolderform" class="modal-body form-horizontal" autocomplete="off" data-bind="submit: submitForm">
<div class="form-horizontal"> <div class="control-group">
<div class="control-group"> <label data-i18n="POPUPS_CREATE_FOLDER/LABEL_PARENT"></label>
<label data-i18n="POPUPS_CREATE_FOLDER/LABEL_PARENT"></label> <select data-bind="options: parentFolderSelectList, value: selectedParentValue,
<select data-bind="options: parentFolderSelectList, value: selectedParentValue, optionsText: 'name', optionsValue: 'id', optionsAfterRender: defaultOptionsAfterRender"></select>
optionsText: 'name', optionsValue: 'id', optionsAfterRender: defaultOptionsAfterRender"></select>
</div>
<div class="control-group">
<label data-i18n="GLOBAL/NAME"></label>
<input type="text"
autofocus="" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
data-bind="textInput: folderName, onEnter: createFolderCommand">
</div>
<div class="control-group" data-bind="component: {
name: 'Checkbox',
params: {
label: 'SETTINGS_FOLDERS/BUTTON_SUBSCRIBE',
value: folderSubscribe
}
}"></div>
</div> </div>
</div> <div class="control-group">
<label data-i18n="GLOBAL/NAME"></label>
<input type="text"
autofocus="" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
required="" pattern="^[^\\/]+$" data-bind="textInput: folderName">
</div>
<div class="control-group" data-bind="component: {
name: 'Checkbox',
params: {
label: 'SETTINGS_FOLDERS/BUTTON_SUBSCRIBE',
value: folderSubscribe
}
}"></div>
</form>
<footer> <footer>
<a class="btn buttonCreate" data-bind="command: createFolderCommand"> <button form="createfolderform" class="btn buttonCreate">
<i class="icon-folder-add"></i> <i class="icon-folder-add"></i>
<span data-i18n="POPUPS_CREATE_FOLDER/BUTTON_CREATE"></span> <span data-i18n="POPUPS_CREATE_FOLDER/BUTTON_CREATE"></span>
</a> </button>
</footer> </footer>

View file

@ -5,7 +5,7 @@
<span data-bind="visible: edit" data-i18n="POPUPS_IDENTITY/TITLE_UPDATE_IDENTITY"></span> <span data-bind="visible: edit" data-i18n="POPUPS_IDENTITY/TITLE_UPDATE_IDENTITY"></span>
</h3> </h3>
</header> </header>
<div class="modal-body"> <form id="identityform" class="modal-body" autocomplete="off" data-bind="submit: submitForm">
<div class="form-horizontal g-ui-user-select-none"> <div class="form-horizontal g-ui-user-select-none">
<div class="alert" data-bind="visible: '' !== submitError()"> <div class="alert" data-bind="visible: '' !== submitError()">
<a href="#" class="close" data-bind="click: function () { submitError('') }">×</a> <a href="#" class="close" data-bind="click: function () { submitError('') }">×</a>
@ -17,26 +17,26 @@
<div class="textEmail" data-bind="text: email, visible: owner"></div> <div class="textEmail" data-bind="text: email, visible: owner"></div>
<input type="email" class="input-xlarge" autofocus="" <input type="email" class="input-xlarge" autofocus=""
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
data-bind="visible: !owner(), value: email, onEnter: addOrEditIdentityCommand, hasfocus: emailFocused"> data-bind="visible: !owner(), value: email, hasfocus: emailFocused">
</div> </div>
</div> </div>
<div class="control-group"> <div class="control-group">
<label data-i18n="GLOBAL/NAME"></label> <label data-i18n="GLOBAL/NAME"></label>
<input type="text" class="input-xlarge" <input type="text" class="input-xlarge"
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
data-bind="value: name, onEnter: addOrEditIdentityCommand"> data-bind="value: name">
</div> </div>
<div class="control-group" data-bind="visible: showReplyTo, css: {'error': replyToHasError}"> <div class="control-group" data-bind="visible: showReplyTo, css: {'error': replyToHasError}">
<label data-i18n="GLOBAL/REPLY_TO"></label> <label data-i18n="GLOBAL/REPLY_TO"></label>
<input type="text" class="inputReplyTo input-xlarge" <input type="text" class="inputReplyTo input-xlarge"
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
data-bind="value: replyTo, onEnter: addOrEditIdentityCommand, hasfocus: replyToFocused"> data-bind="value: replyTo, hasfocus: replyToFocused">
</div> </div>
<div class="control-group" data-bind="visible: showBcc, css: {'error': bccHasError}"> <div class="control-group" data-bind="visible: showBcc, css: {'error': bccHasError}">
<label data-i18n="GLOBAL/BCC"></label> <label data-i18n="GLOBAL/BCC"></label>
<input type="text" class="inputBcc input-xlarge" <input type="text" class="inputBcc input-xlarge"
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
data-bind="value: bcc, onEnter: addOrEditIdentityCommand, hasfocus: bccFocused"> data-bind="value: bcc, hasfocus: bccFocused">
</div> </div>
<div class="control-group" data-bind="visible: !showReplyTo() || !showBcc()"> <div class="control-group" data-bind="visible: !showReplyTo() || !showBcc()">
<div> <div>
@ -63,12 +63,12 @@
}"></div> }"></div>
</div> </div>
<div class="e-signature-place" data-bind="editor: signature"></div> <div class="e-signature-place" data-bind="editor: signature"></div>
</div> </form>
<footer> <footer>
<a class="btn buttonAddIdentity" data-bind="command: addOrEditIdentityCommand"> <button form="identityform" class="btn buttonAddIdentity">
<i data-bind="visible: !edit(), css: {'icon-user-add': !submitRequest(), 'icon-spinner': submitRequest()}"></i> <i data-bind="visible: !edit(), css: {'icon-user-add': !submitRequest(), 'icon-spinner': submitRequest()}"></i>
<span data-bind="visible: !edit()" data-i18n="POPUPS_IDENTITY/BUTTON_ADD_IDENTITY"></span> <span data-bind="visible: !edit()" data-i18n="POPUPS_IDENTITY/BUTTON_ADD_IDENTITY"></span>
<i data-bind="visible: edit, css: {'icon-ok': !submitRequest(), 'icon-spinner': submitRequest()}"></i> <i data-bind="visible: edit, css: {'icon-ok': !submitRequest(), 'icon-spinner': submitRequest()}"></i>
<span data-bind="visible: edit" data-i18n="POPUPS_IDENTITY/BUTTON_UPDATE_IDENTITY"></span> <span data-bind="visible: edit" data-i18n="POPUPS_IDENTITY/BUTTON_UPDATE_IDENTITY"></span>
</a> </button>
</footer> </footer>