Accessibility fixes

This commit is contained in:
varjolintu 2019-02-20 15:09:50 +02:00
parent c3a36027b5
commit 2c480fe62d
11 changed files with 479 additions and 203 deletions

View file

@ -23,6 +23,10 @@
"message": "Save credentials",
"description": "Save credentials using the extension popup icon."
},
"contextMenuShowPasswordGenerator": {
"message": "Show password generator",
"description": "Show password generator text."
},
"rememberCredentialsPopup": {
"message": "Create or modify the credentials by clicking on the extension icon.",
"description": "Notification text when saving credentials from the context menu."
@ -95,6 +99,10 @@
"message": "No logins found.",
"description": "No logins found."
},
"passwordGeneratorIcon": {
"message": "Password generator icon",
"description": "Alt attribute text of the password generator icon."
},
"passwordGeneratorPlaceholder": {
"message": "Generated password",
"description": "Input field placeholder for generated password."
@ -183,6 +191,10 @@
"message": "Please confirm your selection or choose more fields as <em>String fields</em>.",
"description": "Confirm a selection text when choosing custom login fields which contains string fields."
},
"defineKeyboardText": {
"message": "You can also use the numbers to choose the input fields from keyboard.",
"description": "Help text when choosing custom login fields."
},
"username": {
"message": "Username",
"description": "General text for username."
@ -671,6 +683,10 @@
"message": "No saved custom login fields found.",
"description": "Shown when no saved custom credentials are saved."
},
"optionsCustomFieldsTableCaption": {
"message": "You can manage saved custom login fields using the table below.",
"description": "Table caption of Custom login fields page."
},
"optionsCustomFieldsTabHelpTextFirst": {
"message": "If KeePassXC-Browser detects the wrong login fields, you are able to specify the correct fields yourself.",
"description": "Saved custom login fields info text, first part."
@ -679,10 +695,6 @@
"message": "Go to the page and click on the KeePassXC-Browser icon, then select ",
"description": "Saved custom login fields info text, second part."
},
"optionsCustomFieldsTabHelpTextThird": {
"message": "On this page you can manage saved custom login fields.",
"description": "Saved custom login fields info text, third part."
},
"optionsCustomFieldsRemove": {
"message": "Remove saved custom login fields?",
"description": "Confirmation text when removing saved custom login fields."
@ -720,7 +732,7 @@
"description": "Site preferences info text, fourth part."
},
"optionsSitePreferencesManualAddText": {
"message": "Add URL manually:",
"message": "Add URL manually",
"description": "Label for adding site manually on Site preferences tab."
},
"optionsSitePreferencesConfirmation": {
@ -731,6 +743,14 @@
"message": "KeePassXC-Browser will enable all features for this site and remove username-only detection.",
"description": "Part of confirmation text when removing site preferences."
},
"optionsSitePreferencesTableCaption": {
"message": "You can manage saved site preferences using the table below.",
"description": "Table caption of Site Preferences page."
},
"optionsSitePreferencesSelect": {
"message": "Option to ignore extension features for the page.",
"description": "Title text of select element in Site Preferences."
},
"optionsAboutChrome": {
"message": "Visit KeePassXC-Browser in the Chrome Web Store.",
"description": "About page link to Chrome Web Store."
@ -752,11 +772,43 @@
"description": "About page text of KeePassXC version."
},
"optionsAboutContributors": {
"message": "Contributors:",
"message": "Contributors",
"description": "About page text of contributors."
},
"optionsAboutLicenses": {
"message": "Licenses:",
"message": "Licenses",
"description": "About page text of licenses."
},
"optionsAboutReportBugs": {
"message": "Report bugs",
"description": "About page text of reporting bugs."
},
"optionsAboutCreateNewIssue": {
"message": "Create new issue on GitHub",
"description": "Link text for creating a new issue."
},
"optionsAboutDebugInfo": {
"message": "The following debug info can be used",
"description": "Debug info text."
},
"optionsErrorInvalidValue": {
"message": "Invalid value. Please check the input.",
"description": "Error shown when input field has an incorrect value."
},
"optionsErrorInvalidURL": {
"message": "Invalid URL. Please check the input.",
"description": "Error shown when input field has an incorrect URL."
},
"optionsErrorValueExists": {
"message": "The value already exists in the list.",
"description": "Error shown when trying to add a duplicate value to the list."
},
"openNewTab": {
"message": "Opens a new tab",
"description": "Title attribute text."
},
"httpAuthDialog": {
"message": "HTTP Basic Authentication dialog",
"description": "Options page HTTP Basic Auth dialog text"
}
}

View file

@ -81,11 +81,12 @@ browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
browser.runtime.onMessage.addListener(kpxcEvent.onMessage);
const contextMenuItems = [
{title: tr('contextMenuFillUsernameAndPassword'), action: 'fill_username_password'},
{title: tr('contextMenuFillPassword'), action: 'fill_password'},
{title: tr('contextMenuFillTOTP'), action: 'fill_totp'},
{title: tr('contextMenuShowPasswordGeneratorIcons'), action: 'activate_password_generator'},
{title: tr('contextMenuSaveCredentials'), action: 'remember_credentials'}
{ title: tr('contextMenuFillUsernameAndPassword'), action: 'fill_username_password' },
{ title: tr('contextMenuFillPassword'), action: 'fill_password' },
{ title: tr('contextMenuFillTOTP'), action: 'fill_totp' },
{ title: tr('contextMenuShowPasswordGeneratorIcons'), action: 'activate_password_generator' },
{ title: tr('contextMenuSaveCredentials'), action: 'remember_credentials' },
{ title: tr('contextMenuShowPasswordGenerator'), action: 'show_password_generator' }
];
const menuContexts = ['editable'];

View file

@ -117,15 +117,15 @@ input.genpw-text {
}
.b2c-modal-backdrop:after {
content:'';
content: '';
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: #000000;
opacity: 0.8;
filter: alpha(opacity=80);
opacity: 0.5;
filter: alpha(opacity=50);
}
#b2c-cipDefine-fields {
@ -142,7 +142,7 @@ input.genpw-text {
left: 150px;
text-align: left;
cursor: pointer;
background-color:rgba(255,255,255,0.3);
background-color: rgba(0,0,0,0.8);
font-size: 15px;
}
@ -168,12 +168,28 @@ input.genpw-text {
margin-bottom: 3px;
}
#b2c-keyboardHelp {
font-size: 0.75em;
}
#b2c-keyboardHelp kbd {
background-color: #eee;
border-radius: 3px;
border: 1px solid #b4b4b4;
color: #333;
display: inline-block;
line-height: 1;
padding: 2px 4px;
white-space: nowrap;
}
.b2c-fixed-field {
position: absolute;
border: 2px solid #efefef;
color: #fff;
cursor: pointer;
z-index: 2147483646;
text-align: left;
text-align: center;
font-weight: bold;
background-color:rgba(239,239,239,0.4);
}

View file

@ -37,21 +37,21 @@ browser.runtime.onMessage.addListener(function(req, sender, callback) {
cip.setValueWithChange(cip.u, cip.credentials[req.id].login);
combination = cipFields.getCombination('username', cip.u);
browser.runtime.sendMessage({
action: 'page_set_login_id', args: [req.id]
action: 'page_set_login_id', args: [ req.id ]
});
cip.u.focus();
}
if (cip.p) {
cip.setValueWithChange(cip.p, cip.credentials[req.id].password);
browser.runtime.sendMessage({
action: 'page_set_login_id', args: [req.id]
action: 'page_set_login_id', args: [ req.id ]
});
combination = cipFields.getCombination('password', cip.p);
}
let list = [];
if (cip.fillInStringFields(combination.fields, cip.credentials[req.id].stringFields, list)) {
cipForm.destroy(false, {'password': list.list[0], 'username': list.list[1]});
cipForm.destroy(false, { 'password': list.list[0], 'username': list.list[1] });
}
}
} else if (req.action === 'fill_username_password') {
@ -89,9 +89,10 @@ browser.runtime.onMessage.addListener(function(req, sender, callback) {
});
} else if (req.action === 'ignore-site') {
cip.ignoreSite(req.args);
}
else if (req.action === 'check_database_hash' && 'hash' in req) {
} else if (req.action === 'check_database_hash' && 'hash' in req) {
cip.detectDatabaseChange(req.hash);
} else if (req.action === 'show_password_generator') {
cipPassword.showDialog();
}
}
});
@ -108,7 +109,7 @@ cipAutocomplete.init = function(field) {
}
const acMenu = jQuery('#kpxc-ac-menu');
if (acMenu.length == 0) {
if (acMenu.length === 0) {
jQuery('<div id=\"kpxc-ac-menu\" class=\"kpxc\"></div>').appendTo('body');
}
@ -240,6 +241,7 @@ cipPassword.createDialog = function() {
.attr('type', 'text')
.attr('aria-describedby', 'cip-genpw-quality')
.attr('placeholder', tr('passwordGeneratorPlaceholder'))
.attr('tabindex', -1)
.addClass('genpw-text ui-widget-content ui-corner-all')
.on('change keypress paste textInput input', function() {
jQuery('#cip-genpw-btn-clipboard:first').removeClass('btn-success');
@ -369,6 +371,7 @@ cipPassword.createIcon = function(field) {
.addClass('cip-genpw-icon')
.addClass($className)
.attr('title', tr('passwordGeneratorGenerateText'))
.attr('alt', tr('passwordGeneratorIcon'))
.css('z-index', '9999')
.css('width', $size)
.css('height', $size)
@ -408,6 +411,10 @@ cipPassword.createIcon = function(field) {
jQuery('body').append($icon);
};
cipPassword.showDialog = function() {
$('.cip-genpw-icon').click();
};
cipPassword.setIconPosition = function($icon, $field) {
$icon.css('top', $field.offset().top + $icon.data('offset') + 1)
.css('left', $field.offset().left + $field.outerWidth() - $icon.data('size') - $icon.data('offset'));
@ -556,6 +563,7 @@ cipDefine.selection = {
fields: []
};
cipDefine.eventFieldClick = null;
cipDefine.keyDown = null;
cipDefine.init = function() {
const $backdrop = jQuery('<div>').attr('id', 'b2c-backdrop').addClass('b2c-modal-backdrop');
@ -575,6 +583,14 @@ cipDefine.init = function() {
cipDefine.resetSelection();
cipDefine.prepareStep1();
cipDefine.markAllUsernameFields($chooser);
document.addEventListener('keydown', cipDefine.keyDown);
};
cipDefine.close = function() {
$('div#b2c-backdrop').remove();
$('div#b2c-cipDefine-fields').remove();
document.removeEventListener('keydown', cipDefine.keyDown);
};
cipDefine.initDescription = function() {
@ -583,85 +599,44 @@ cipDefine.initDescription = function() {
$description.append($h1);
const $help = jQuery('<div>').addClass('b2c-chooser-help').attr('id', 'b2c-help');
$description.append($help);
const $keyboardHelp = jQuery('<div>').addClass('b2c-chooser-help').attr('id', 'b2c-keyboardHelp');
$description.append($keyboardHelp);
// Show keyboard shortcuts help text
jQuery('div#b2c-keyboardHelp').text(`${tr('optionsKeyboardShortcutsHeader')}:`).css('margin-bottom', '5px');
jQuery('div#b2c-keyboardHelp').append($('<br><kbd>Escape</kbd>')).append(' ' + tr('defineDismiss'));
jQuery('div#b2c-keyboardHelp').append($('<br><kbd>S</kbd>')).append(' ' + tr('defineSkip'));
jQuery('div#b2c-keyboardHelp').append($('<br><kbd>A</kbd>')).append(' ' + tr('defineAgain'));
jQuery('div#b2c-keyboardHelp').append($('<br><kbd>C</kbd>')).append(' ' + tr('defineConfirm'));
jQuery('div#b2c-keyboardHelp').append($('<br><kbd>M</kbd>')).append(' ' + tr('defineMore'));
jQuery('div#b2c-keyboardHelp').append($('<br><kbd>D</kbd>')).append(' ' + tr('defineDiscard'));
const $btnDismiss = jQuery('<button>').text(tr('defineDismiss')).attr('id', 'b2c-btn-dismiss')
.addClass('btn')
.addClass('btn-danger')
.click(function(e) {
jQuery('div#b2c-backdrop').remove();
jQuery('div#b2c-cipDefine-fields').remove();
});
.click(cipDefine.close);
const $btnSkip = jQuery('<button>').text(tr('defineSkip')).attr('id', 'b2c-btn-skip')
.addClass('btn')
.addClass('btn-info')
.css('margin-right', '5px')
.click(function() {
if (jQuery(this).data('step') === '1') {
cipDefine.selection.username = null;
cipDefine.prepareStep2();
cipDefine.markAllPasswordFields(jQuery('#b2c-cipDefine-fields'), false);
} else if (jQuery(this).data('step') === '2') {
cipDefine.selection.password = null;
cipDefine.prepareStep3();
cipDefine.markAllStringFields(jQuery('#b2c-cipDefine-fields'));
}
});
.click(cipDefine.skip);
const $btnMore = jQuery('<button>').text(tr('defineMore')).attr('id', 'b2c-btn-more')
.addClass('btn')
.addClass('btn-info')
.css('margin-right', '5px')
.css('margin-left', '5px')
.click(function() {
cipDefine.prepareStep2();
cipDefine.markAllPasswordFields(jQuery('#b2c-cipDefine-fields'), true);
});
.click(cipDefine.more);
const $btnAgain = jQuery('<button>').text(tr('defineAgain')).attr('id', 'b2c-btn-again')
.addClass('btn')
.addClass('btn-warning')
.css('margin-right', '5px')
.click(function(e) {
cipDefine.resetSelection();
cipDefine.prepareStep1();
cipDefine.markAllUsernameFields(jQuery('#b2c-cipDefine-fields'));
})
.click(cipDefine.again)
.hide();
const $btnConfirm = jQuery('<button>').text(tr('defineConfirm')).attr('id', 'b2c-btn-confirm')
.addClass('btn')
.addClass('btn-primary')
.css('margin-right', '15px')
.click(function(e) {
if (!cip.settings['defined-custom-fields']) {
cip.settings['defined-custom-fields'] = {};
}
if (cipDefine.selection.username) {
cipDefine.selection.username = cipFields.prepareId(cipDefine.selection.username);
}
if (cipDefine.selection.password) {
cipDefine.selection.password = cipFields.prepareId(cipDefine.selection.password);
}
const fieldIds = [];
const fieldKeys = Object.keys(cipDefine.selection.fields);
for (const i of fieldKeys) {
fieldIds.push(cipFields.prepareId(i));
}
const location = cip.getDocumentLocation();
cip.settings['defined-custom-fields'][location] = {
username: cipDefine.selection.username,
password: cipDefine.selection.password,
fields: fieldIds
};
browser.runtime.sendMessage({
action: 'save_settings',
args: [ cip.settings ]
});
jQuery('button#b2c-btn-dismiss').click();
})
.click(cipDefine.confirm)
.hide();
$description.append($btnConfirm);
@ -672,7 +647,7 @@ cipDefine.initDescription = function() {
const location = cip.getDocumentLocation();
if (cip.settings['defined-custom-fields'] && cip.settings['defined-custom-fields'][location]) {
const $p = jQuery('<p>').html(tr('defineAlreadySelected') + '<br />');
const $p = jQuery('<p>').addClass('alreadySelected').html(tr('defineAlreadySelected') + '<br />');
const $btnDiscard = jQuery('<button>')
.attr('id', 'btn-warning')
.text(tr('defineDiscard'))
@ -680,20 +655,7 @@ cipDefine.initDescription = function() {
.addClass('btn')
.addClass('btn-sm')
.addClass('btn-danger')
.click(function(e) {
delete cip.settings['defined-custom-fields'][location];
browser.runtime.sendMessage({
action: 'save_settings',
args: [ cip.settings ]
});
browser.runtime.sendMessage({
action: 'load_settings'
});
jQuery(this).parent('p').remove();
});
.click(cipDefine.discard);
$p.append($btnDiscard);
$description.append($p);
}
@ -718,9 +680,10 @@ cipDefine.isFieldSelected = function($cipId) {
};
cipDefine.markAllUsernameFields = function($chooser) {
cipDefine.eventFieldClick = function(e) {
cipDefine.selection.username = jQuery(this).data('cip-id');
jQuery(this).addClass('b2c-fixed-username-field').text(tr('username')).unbind('click');
cipDefine.eventFieldClick = function(e, elem) {
const field = elem ? jQuery(elem) : jQuery(this);
cipDefine.selection.username = field.data('cip-id');
field.addClass('b2c-fixed-username-field').text(tr('username')).unbind('click');
cipDefine.prepareStep2();
cipDefine.markAllPasswordFields(jQuery('#b2c-cipDefine-fields'));
};
@ -728,9 +691,10 @@ cipDefine.markAllUsernameFields = function($chooser) {
};
cipDefine.markAllPasswordFields = function($chooser, more) {
cipDefine.eventFieldClick = function(e) {
cipDefine.selection.password = jQuery(this).data('cip-id');
jQuery(this).addClass('b2c-fixed-password-field').text(tr('password')).unbind('click');
cipDefine.eventFieldClick = function(e, elem) {
const field = elem ? jQuery(elem) : jQuery(this);
cipDefine.selection.password = field.data('cip-id');
field.addClass('b2c-fixed-password-field').text(tr('password')).unbind('click');
cipDefine.prepareStep3();
cipDefine.markAllStringFields(jQuery('#b2c-cipDefine-fields'));
};
@ -742,16 +706,18 @@ cipDefine.markAllPasswordFields = function($chooser, more) {
};
cipDefine.markAllStringFields = function($chooser) {
cipDefine.eventFieldClick = function(e) {
cipDefine.selection.fields[jQuery(this).data('cip-id')] = true;
cipDefine.eventFieldClick = function(e, elem) {
const field = elem ? jQuery(elem) : jQuery(this);
cipDefine.selection.fields[field.data('cip-id')] = true;
const count = Object.keys(cipDefine.selection.fields).length;
jQuery(this).addClass('b2c-fixed-string-field').text(tr('defineStringField') + String(count)).unbind('click');
field.addClass('b2c-fixed-string-field').text(tr('defineStringField') + String(count)).unbind('click');
jQuery('button#b2c-btn-confirm:first').addClass('b2c-btn-primary').attr('disabled', false);
};
cipDefine.markFields($chooser, cipFields.inputQueryPattern + ', select');
};
cipDefine.markFields = function($chooser, $pattern) {
let index = 1;
jQuery($pattern).each(function() {
if (cipDefine.isFieldSelected(jQuery(this).data('cip-id'))) {
return true;
@ -764,27 +730,36 @@ cipDefine.markFields = function($chooser, $pattern) {
.css('width', jQuery(this).outerWidth())
.css('height', jQuery(this).outerHeight())
.attr('data-cip-id', jQuery(this).attr('data-cip-id'))
.text(String(index))
.click(cipDefine.eventFieldClick)
.hover(function() {jQuery(this).addClass('b2c-fixed-hover-field');}, function() {jQuery(this).removeClass('b2c-fixed-hover-field');});
.hover(function() {
jQuery(this).addClass('b2c-fixed-hover-field');
}, function() {
jQuery(this).removeClass('b2c-fixed-hover-field');
});
$chooser.append($field);
++index;
}
});
$('b2c-fixed-field:first').focus();
};
cipDefine.prepareStep1 = function() {
jQuery('div#b2c-help').text('').css('margin-bottom', 0);
jQuery('div#b2c-help').text(tr('defineKeyboardText')).css('margin-bottom', '5px');
jQuery('div#b2c-cipDefine-fields').removeData('username');
jQuery('div#b2c-cipDefine-fields').removeData('password');
jQuery('div.b2c-fixed-field', jQuery('div#b2c-cipDefine-fields')).remove();
jQuery('div:first', jQuery('div#b2c-cipDefine-description')).text(tr('defineChooseUsername'));
jQuery('button#b2c-btn-skip:first').data('step', '1').show();
jQuery('button#b2c-btn-confirm:first').removeClass('b2c-btn-primary').attr('disabled', false);
jQuery('button#b2c-btn-confirm:first').hide();
jQuery('button#b2c-btn-again:first').hide();
jQuery('button#b2c-btn-more:first').hide();
};
cipDefine.prepareStep2 = function() {
jQuery('div#b2c-help').text('').css('margin-bottom', 0);
jQuery('div#b2c-help').text(tr('defineKeyboardText')).css('margin-bottom', '5px');
jQuery('div.b2c-fixed-field:not(.b2c-fixed-username-field)', jQuery('div#b2c-cipDefine-fields')).remove();
jQuery('div:first', jQuery('div#b2c-cipDefine-description')).text(tr('defineChoosePassword'));
jQuery('button#b2c-btn-skip:first').data('step', '2');
@ -805,6 +780,115 @@ cipDefine.prepareStep3 = function() {
jQuery('div:first', jQuery('div#b2c-cipDefine-description')).text(tr('defineConfirmSelection'));
};
cipDefine.skip = function() {
if ($('button#b2c-btn-skip:first').data('step') === '1') {
cipDefine.selection.username = null;
cipDefine.prepareStep2();
cipDefine.markAllPasswordFields(jQuery('#b2c-cipDefine-fields'), false);
} else if ($('button#b2c-btn-skip:first').data('step') === '2') {
cipDefine.selection.password = null;
cipDefine.prepareStep3();
cipDefine.markAllStringFields(jQuery('#b2c-cipDefine-fields'));
}
};
cipDefine.again = function() {
cipDefine.resetSelection();
cipDefine.prepareStep1();
cipDefine.markAllUsernameFields(jQuery('#b2c-cipDefine-fields'));
};
cipDefine.more = function() {
if ($('button#b2c-btn-skip:first').data('step') === '2') {
cipDefine.prepareStep2();
cipDefine.markAllPasswordFields(jQuery('#b2c-cipDefine-fields'), true);
}
};
cipDefine.confirm = function() {
if ($('button#b2c-btn-skip:first').data('step') !== '3') {
return;
}
if (!cip.settings['defined-custom-fields']) {
cip.settings['defined-custom-fields'] = {};
}
if (cipDefine.selection.username) {
cipDefine.selection.username = cipFields.prepareId(cipDefine.selection.username);
}
if (cipDefine.selection.password) {
cipDefine.selection.password = cipFields.prepareId(cipDefine.selection.password);
}
const fieldIds = [];
const fieldKeys = Object.keys(cipDefine.selection.fields);
for (const i of fieldKeys) {
fieldIds.push(cipFields.prepareId(i));
}
const location = cip.getDocumentLocation();
cip.settings['defined-custom-fields'][location] = {
username: cipDefine.selection.username,
password: cipDefine.selection.password,
fields: fieldIds
};
browser.runtime.sendMessage({
action: 'save_settings',
args: [ cip.settings ]
});
cipDefine.close();
};
cipDefine.discard = function() {
const location = cip.getDocumentLocation();
delete cip.settings['defined-custom-fields'][location];
browser.runtime.sendMessage({
action: 'save_settings',
args: [ cip.settings ]
});
browser.runtime.sendMessage({
action: 'load_settings'
});
$('p.alreadySelected').remove();
};
// Handle the keyboard events
cipDefine.keyDown = function(e) {
if (e.key === 'Escape') {
cipDefine.close();
} else if (e.keyCode >= 49 && e.keyCode <= 57) {
// Select input field by number
e.preventDefault();
const index = e.keyCode - 48;
const inputFields = $('div.b2c-fixed-field:not(.b2c-fixed-password-field,.b2c-fixed-username-field)');
if (inputFields.length >= index) {
cipDefine.eventFieldClick(e, inputFields[index - 1]);
}
} else if (e.key === 's') {
e.preventDefault();
cipDefine.skip();
} else if (e.key === 'a') {
e.preventDefault();
cipDefine.again();
} else if (e.key === 'c') {
e.preventDefault();
cipDefine.confirm();
} else if (e.key === 'm') {
e.preventDefault();
cipDefine.more();
} else if (e.key === 'd') {
e.preventDefault();
cipDefine.discard();
}
};
var cipFields = {};

View file

@ -81,6 +81,13 @@
"default": "Alt+Shift+T",
"mac": "MacCtrl+Shift+T"
}
},
"show_password_generator": {
"description": "__MSG_contextMenuShowPasswordGenerator__",
"suggested_key": {
"default": "Alt+Shift+G",
"mac": "MacCtrl+Shift+G"
}
}
},
"web_accessible_resources": [

View file

@ -7,6 +7,10 @@ table td {
width: 100%;
}
input:invalid {
border: 2px solid red;
}
/* Custom container */
.container {
margin: 0 auto;
@ -42,7 +46,8 @@ table td {
}
.navbar-default .navbar-nav > li > a:hover,
.navbar-default .navbar-nav > li > a:focus {
color: #cccccc;
color: #ffffff;
background-color: #5f9f40;
}
.navbar-default .navbar-nav > .active > a,
.navbar-default .navbar-nav > .active > a:hover,

View file

@ -5,23 +5,24 @@
<meta charset="UTF-8">
<link rel="stylesheet" href="bootstrap.min.css" />
<link rel="stylesheet" href="options.css" />
<script type="text/javascript" src="../browser-polyfill.min.js"></script>
<script type="text/javascript" src="../global.js"></script>
<script type="text/javascript" src="../jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="bootstrap.min.js"></script>
<script type="text/javascript" src="options.js"></script>
<script src="../browser-polyfill.min.js"></script>
<script src="../global.js"></script>
<script src="../jquery-3.3.1.min.js"></script>
<script src="bootstrap.min.js"></script>
<script src="options.js"></script>
<script src="../translate.js" defer></script>
</head>
<body>
<div class="container">
<h3 class="muted"><img src="/icons/keepassxc_48x48.png" alt="logo" /> KeePassXC-Browser</h3>
<h3 class="muted"><img src="/icons/keepassxc_48x48.png" alt="logo"> KeePassXC-Browser</h3>
<nav class="navbar navbar-default">
<ul class="nav navbar-nav">
<li class="active"><a href="#general-settings"><span data-i18n="optionsMenuGeneral"/></a></li>
<li><a href="#connected-databases"><span data-i18n="optionsMenuConnectedDatabases"/></a></li>
<li><a href="#custom-fields"><span data-i18n="optionsMenuCustomFields"/></a></li>
<li><a href="#site-preferences"><span data-i18n="optionsMenuSitePreferences"></a></li>
<li><a href="#about"><span data-i18n="optionsMenuAbout"/></a></li>
<li class="active"><a href="#general-settings" tabindex="1"><span data-i18n="optionsMenuGeneral"></span></a></li>
<li><a href="#connected-databases" tabindex="2"><span data-i18n="optionsMenuConnectedDatabases"></span></a></li>
<li><a href="#custom-fields" tabindex="3"><span data-i18n="optionsMenuCustomFields"></span></a></li>
<li><a href="#site-preferences" tabindex="4"><span data-i18n="optionsMenuSitePreferences"></span></a></li>
<li><a href="#about" tabindex="5"><span data-i18n="optionsMenuAbout"></span></a></li>
</ul>
</nav>
@ -31,18 +32,19 @@
<hr />
<h3 data-i18n="optionsKeyboardShortcutsHeader"></h3>
<p>
<span data-i18n="contextMenuFillUsernameAndPassword"></span>: <span id="fill-username-password-shortcut">error</span><br />
<span data-i18n="contextMenuFillPassword"></span>: <span id="fill-password-shortcut">error</span><br />
<span data-i18n="contextMenuFillTOTP"></span>: <span id="fill-totp-shortcut">error</span>
<span data-i18n="contextMenuFillUsernameAndPassword"></span>: <span id="fill_username_password-shortcut">error</span><br />
<span data-i18n="contextMenuFillPassword"></span>: <span id="fill_password-shortcut">error</span><br />
<span data-i18n="contextMenuFillTOTP"></span>: <span id="fill_totp-shortcut">error</span><br />
<span data-i18n="contextMenuShowPasswordGenerator"></span>: <span id="show_password_generator-shortcut">error</span>
</p>
<p id="chrome-only"><button class="btn btn-sm btn-primary" id="configureCommands" type="button"><span class="glyphicon glyphicon-cog"></span><span data-i18n="optionsConfigureShortcuts"/></button></p>
<p id="chrome-only"><button class="btn btn-sm btn-primary" id="configureCommands" type="button" data-i18n="[title]openNewTab"><span class="glyphicon glyphicon-cog"></span><span data-i18n="optionsConfigureShortcuts"></span></button></p>
<p>
<div class="form-group">
<label for="blinkTimeout" data-i18n="optionsLabelBlinkTime"></label>
<div class="control-group">
<div class="input-append">
<input type="number" id="blinkTimeout" placeholder="7500" value="7500" min="-1"/>
<button class="btn btn-sm btn-primary" id="blinkTimeoutButton" type="button"><span class="glyphicon glyphicon-floppy-disk"></span> <span data-i18n="optionsButtonSave"/></button>
<button class="btn btn-sm btn-primary" id="blinkTimeoutButton" type="button"><span class="glyphicon glyphicon-floppy-disk"></span> <span data-i18n="optionsButtonSave"></span></button>
</div>
</div>
<span class="help-inline">
@ -58,7 +60,7 @@
<div class="control-group">
<div class="input-append">
<input type="number" id="blinkMinTimeout" placeholder="2000" value="2000" min="-1"/>
<button class="btn btn-sm btn-primary" id="blinkMinTimeoutButton" type="button"><span class="glyphicon glyphicon-floppy-disk"></span> <span data-i18n="optionsButtonSave"/></button>
<button class="btn btn-sm btn-primary" id="blinkMinTimeoutButton" type="button"><span class="glyphicon glyphicon-floppy-disk"></span> <span data-i18n="optionsButtonSave"></span></button>
</div>
</div>
<span class="help-inline">
@ -76,7 +78,7 @@
<div class="control-group">
<div class="input-append">
<input type="number" id="allowedRedirect" placeholder="1" value="1" min="1"/>
<button class="btn btn-sm btn-primary" id="allowedRedirectButton" type="button"><span class="glyphicon glyphicon-floppy-disk"></span> <span data-i18n="optionsButtonSave"/></button>
<button class="btn btn-sm btn-primary" id="allowedRedirectButton" type="button"><span class="glyphicon glyphicon-floppy-disk"></span> <span data-i18n="optionsButtonSave"></span></button>
</div>
</div>
<span class="help-inline">
@ -90,7 +92,7 @@
<p>
<div class="checkbox">
<label class="checkbox">
<input type="checkbox" name="usePasswordGenerator" value="true"/><span data-i18n="optionsCheckboxUsePasswordGenerator"/>
<input type="checkbox" name="usePasswordGenerator" value="true"/><span data-i18n="optionsCheckboxUsePasswordGenerator"></span>
</label>
<span class="help-block">
<span data-i18n="optionsUsePasswordGeneratorHelpText"></span>
@ -102,7 +104,7 @@
<p>
<div class="checkbox">
<label class="checkbox">
<input type="checkbox" name="autoRetrieveCredentials" value="true" /><span data-i18n="optionsCheckboxAutoRetrieveCredentials"/>
<input type="checkbox" name="autoRetrieveCredentials" value="true" /><span data-i18n="optionsCheckboxAutoRetrieveCredentials"></span>
</label>
<span class="help-block" data-i18n="optionsAutoRetrieveCredentialsHelpText"></span>
</div>
@ -111,7 +113,7 @@
<p>
<div class="checkbox">
<label class="checkbox">
<input type="checkbox" name="autoFillSingleEntry" value="false" /><span data-i18n="optionsCheckboxAutoFillSingleEntry"/>
<input type="checkbox" name="autoFillSingleEntry" value="false" /><span data-i18n="optionsCheckboxAutoFillSingleEntry"></span>
</label>
<span class="help-block" data-i18n="optionsAutoFillSingleEntryHelpText"></span>
<span class="bg-danger" data-i18n="optionsAutoFillSingleEntryWarning"></span>
@ -121,7 +123,7 @@
<p>
<div class="checkbox">
<label class="checkbox">
<input type="checkbox" name="autoCompleteUsernames" value="true" /><span data-i18n="optionsCheckboxAutoCompleteUsernames"/>
<input type="checkbox" name="autoCompleteUsernames" value="true" /><span data-i18n="optionsCheckboxAutoCompleteUsernames"></span>
</label>
<span class="help-block" data-i18n="optionsAutocompleteUsernamesHelpText"></span>
</div>
@ -130,13 +132,13 @@
<p>
<div class="checkbox">
<label class="checkbox">
<input type="checkbox" name="showNotifications" value="true" /><span data-i18n="optionsCheckboxShowNotifications"/>
<input type="checkbox" name="showNotifications" value="true" /><span data-i18n="optionsCheckboxShowNotifications"></span>
</label>
<span class="help-block" data-i18n="optionsShowNotificationsHelpText"></span>
</div>
<div class="checkbox">
<label class="checkbox">
<input type="checkbox" name="showLoginNotifications" value="true" /><span data-i18n="optionsCheckboxShowLoginNotifications"/>
<input type="checkbox" name="showLoginNotifications" value="true" /><span data-i18n="optionsCheckboxShowLoginNotifications"></span>
</label>
</div>
</p>
@ -144,7 +146,7 @@
<p>
<div class="checkbox">
<label class="checkbox">
<input type="checkbox" name="saveDomainOnly" value="true" /><span data-i18n="optionsSaveDomainOnly"/>
<input type="checkbox" name="saveDomainOnly" value="true" /><span data-i18n="optionsSaveDomainOnly"></span>
</label>
<span class="help-block" data-i18n="optionsSaveDomainOnlyHelpText"></span>
</div>
@ -152,10 +154,10 @@
<hr />
<p>
<div data-i18n="optionsRadioText"></div>
<label class="radio-inline"><input type="radio" name="checkUpdateKeePassXC" value="3" checked="true"/><span data-i18n="optionsRadioThreeDays"/></label>
<label class="radio-inline"><input type="radio" name="checkUpdateKeePassXC" value="7" /><span data-i18n="optionsRadioWeek"/></label>
<label class="radio-inline"><input type="radio" name="checkUpdateKeePassXC" value="30" /><span data-i18n="optionsRadioMonth"/></label>
<label class="radio-inline"><input type="radio" name="checkUpdateKeePassXC" value="0" /><span data-i18n="optionsRadioNever"/></label>
<label class="radio-inline"><input type="radio" name="checkUpdateKeePassXC" value="3" checked><span data-i18n="optionsRadioThreeDays"></span></label>
<label class="radio-inline"><input type="radio" name="checkUpdateKeePassXC" value="7" /><span data-i18n="optionsRadioWeek"></span></label>
<label class="radio-inline"><input type="radio" name="checkUpdateKeePassXC" value="30" /><span data-i18n="optionsRadioMonth"></span></label>
<label class="radio-inline"><input type="radio" name="checkUpdateKeePassXC" value="0" /><span data-i18n="optionsRadioNever"></span></label>
</p>
<div class="help-block kphVersion">
<span data-i18n="optionsVersionInfoText"></span>
@ -168,14 +170,14 @@
</div>
<div>
<span data-i18n="optionsLatestVersion" i18n-placeholder="<em class='latestVersion'></em>"></span>
<button class="btn btn-sm btn-primary checkUpdateKeePassXC"><span class="glyphicon glyphicon-refresh"></span> <span data-i18n="optionsButtonUpdate"/></button>
<button class="btn btn-sm btn-primary checkUpdateKeePassXC"><span class="glyphicon glyphicon-refresh"></span> <span data-i18n="optionsButtonUpdate"></span></button>
</div>
</div>
<hr />
<p>
<div class="checkbox">
<label class="checkbox">
<input type="checkbox" name="autoFillAndSend" value="0" /><span data-i18n="optionsCheckboxAutoFillAndSend"/>
<input type="checkbox" name="autoFillAndSend" value="0" /><span data-i18n="optionsCheckboxAutoFillAndSend"></span>
</label>
<span class="help-block">
<span data-i18n="optionsAutoFillAndSendHelpText"></span>
@ -183,7 +185,8 @@
<span data-i18n="optionsAutoFillAndSendHelpTextSecond"></span>
</span>
</div>
<img src="/options/http-auth-dialog.png" alt="http-auth-dialog" />
<!--<img src="/options/http-auth-dialog.png" alt="http-auth-dialog" />-->
<img src="/options/http-auth-dialog.png" data-i18n="[alt]httpAuthDialog"/>
</p>
</div>
@ -191,15 +194,15 @@
<div class="tab" id="tab-connected-databases">
<h2 data-i18n="optionsConnectedDatabasesTab"></h2>
<hr />
<p data-i18n="optionsConnectedDatabasesText"/>
<table class="table table-striped table-bordered table-hover">
<caption data-i18n="optionsConnectedDatabasesText"></caption>
<thead>
<tr>
<th><span data-i18n="optionsDatabaseIdentifier"></span></th>
<th><span data-i18n="optionsDatabaseKey"></span></th>
<th><span data-i18n="optionsDatabaseLastUsed"></span></th>
<th><span data-i18n="optionsDatabaseCreated"></span></th>
<th><span data-i18n="optionsDatabaseDelete"></span></th>
<th scope="col"><span data-i18n="optionsDatabaseIdentifier"></span></th>
<th scope="col"><span data-i18n="optionsDatabaseKey"></span></th>
<th scope="col"><span data-i18n="optionsDatabaseLastUsed"></span></th>
<th scope="col"><span data-i18n="optionsDatabaseCreated"></span></th>
<th scope="col"><span data-i18n="optionsDatabaseDelete"></span></th>
</tr>
</thead>
<tbody>
@ -233,7 +236,7 @@
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true"><span class="glyphicon glyphicon-remove"></span> <span data-i18n="optionsButtonCancel"></span></button>
<button class="btn yes btn-primary"><span class="glyphicon glyphicon-ok"></span> <span data-i18n="optionsButtonRemoveNow"></span></button>
<button class="btn yes btn-primary" autofocus><span class="glyphicon glyphicon-ok"></span> <span data-i18n="optionsButtonRemoveNow"></span></button>
</div>
</div>
</div>
@ -248,14 +251,13 @@
<span data-i18n="optionsCustomFieldsTabHelpTextFirst"></span>
<br />
<span data-i18n="optionsCustomFieldsTabHelpTextSecond"></span><em><span data-i18n="popupChooseCredentialsText"></span></em>.
<br />
<span data-i18n="optionsCustomFieldsTabHelpTextThird"></span>
</p>
<table class="table table-striped table-bordered table-hover">
<caption data-i18n="optionsCustomFieldsTableCaption"></caption>
<thead>
<tr>
<th><span data-i18n="optionsColumnPageURL"></span></th>
<th><span data-i18n="optionsColumnDelete"></span></th>
<th scope="col"><span data-i18n="optionsColumnPageURL"></span></th>
<th scope="col"><span data-i18n="optionsColumnDelete"></span></th>
</tr>
</thead>
<tbody>
@ -281,7 +283,7 @@
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true"><span class="glyphicon glyphicon-remove"></span> <span data-i18n="optionsButtonCancel"></span></button>
<button class="btn yes btn-primary"><span class="glyphicon glyphicon-ok"></span> <span data-i18n="optionsButtonRemoveNow"></span></button>
<button class="btn yes btn-primary" autofocus><span class="glyphicon glyphicon-ok"></span> <span data-i18n="optionsButtonRemoveNow"></span></button>
</div>
</div>
</div>
@ -303,22 +305,23 @@
</p>
<hr />
<div class="form-group">
<label for="ignoreManualAdd" data-i18n="optionsSitePreferencesManualAddText"></label>
<div class="control-group">
<label for="manualUrl" data-i18n="optionsSitePreferencesManualAddText"></label>
<div class="control-group" id="manualUrlGroup">
<div class="input-append">
<input type="url" id="manualUrl"/>
<input type="url" id="manualUrl" pattern="ftp://.*|http://.*|https://.*" minlength="10">
<button class="btn btn-sm btn-primary" id="sitePreferencesManualAdd" type="button"><span class="glyphicon glyphicon-plus-sign"></span> <span data-i18n="optionsButtonAdd"></span></button>
</div>
</div>
</div>
<hr />
<table class="table table-striped table-bordered table-hover">
<caption data-i18n="optionsSitePreferencesTableCaption"></caption>
<thead>
<tr>
<th><span data-i18n="optionsColumnPageURL"></span></th>
<th><span data-i18n="optionsColumnIgnore"></span></th>
<th><span data-i18n="optionsColumnUsernameOnly"></span></th>
<th><span data-i18n="optionsColumnDelete"></span></th>
<th scope="col"><span data-i18n="optionsColumnPageURL"></span></th>
<th scope="col"><span data-i18n="optionsColumnIgnore"></span></th>
<th scope="col"><span data-i18n="optionsColumnUsernameOnly"></span></th>
<th scope="col"><span data-i18n="optionsColumnDelete"></span></th>
</tr>
</thead>
<tbody>
@ -328,13 +331,13 @@
<tr class="clone">
<td></td>
<td>
<select name="ignore">
<select name="ignore" data-i18n="[title]optionsSitePreferencesSelect">
<option value="ignoreNothing" data-i18n="optionsSelectionNothing"></option>
<option value="ignoreNormal" data-i18n="optionsSelectionNormal"></option>
<option value="ignoreFull" data-i18n="optionsSelectionFull"></option>
</select>
</td>
<td><input type="checkbox" name="usernameOnly" value="false" /></td>
<td><input type="checkbox" name="usernameOnly" value="false" data-i18n="[title]optionsColumnUsernameOnly"/></td>
<td><button class="btn delete btn-danger btn"><span class="glyphicon glyphicon-remove-sign"></span> <span data-i18n="optionsButtonRemove"></span></button></td>
</tr>
</tbody>
@ -352,7 +355,7 @@
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true"><span class="glyphicon glyphicon-remove"></span> <span data-i18n="optionsButtonCancel"></span></button>
<button class="btn yes btn-primary"><span class="glyphicon glyphicon-ok"></span> <span data-i18n="optionsButtonRemoveNow"></span></button>
<button class="btn yes btn-primary" autofocus><span class="glyphicon glyphicon-ok"></span> <span data-i18n="optionsButtonRemoveNow"></span></button>
</div>
</div>
</div>
@ -364,13 +367,13 @@
<h2 data-i18n="optionsAboutTab"></h2>
<hr />
<p>
<a target="_blank" href="https://chrome.google.com/webstore/detail/keepassxc-browser/oboonakemofpalcgghocfoadofidjkkk"><span data-i18n="optionsAboutChrome"/></a>
<a target="_blank" href="https://chrome.google.com/webstore/detail/keepassxc-browser/oboonakemofpalcgghocfoadofidjkkk" data-i18n="[title]openNewTab" tabindex="6"><span data-i18n="optionsAboutChrome"></span></a>
</p>
<p>
<a target="_blank" href="https://addons.mozilla.org/en-US/firefox/addon/keepassxc-browser/"><span data-i18n="optionsAboutMozilla"/></a>
<a target="_blank" href="https://addons.mozilla.org/en-US/firefox/addon/keepassxc-browser/" data-i18n="[title]openNewTab" tabindex="7"><span data-i18n="optionsAboutMozilla"></span></a>
</p>
<p>
<a target="_blank" href="https://github.com/keepassxreboot/keepassxc-browser"><span data-i18n="optionsAboutGitHub"/></a>.
<a target="_blank" href="https://github.com/keepassxreboot/keepassxc-browser" data-i18n="[title]openNewTab" tabindex="8"><span data-i18n="optionsAboutGitHub"></span></a>
</p>
<hr />
<p>
@ -381,14 +384,16 @@
</p>
<hr />
<p>
<span data-i18n="optionsAboutContributors"></span><br>
<a target="_blank" href="https://github.com/pfn/">Perry Nguyen</a>, <a target="_blank" href="http://lukas-schulze.de">Lukas Schulze</a>,
<a target="_blank" href="https://github.com/smorks">Andy Brandt</a>, <a target="_blank" href="https://github.com/varjolintu/">Sami Vänttinen</a>,
<a target="_blank" href="https://github.com/keepassxreboot/keepassxc-browser">KeePassXC Team</a>.
<span data-i18n="optionsAboutContributors"></span>:<br>
<a target="_blank" href="https://github.com/pfn/" data-i18n="[title]openNewTab" tabindex="9">Perry Nguyen</a>,
<a target="_blank" href="http://lukas-schulze.de" data-i18n="[title]openNewTab" tabindex="10">Lukas Schulze</a>,
<a target="_blank" href="https://github.com/smorks" data-i18n="[title]openNewTab" tabindex="11">Andy Brandt</a>,
<a target="_blank" href="https://github.com/varjolintu/" data-i18n="[title]openNewTab" tabindex="12">Sami Vänttinen</a>,
<a target="_blank" href="https://github.com/keepassxreboot/keepassxc-browser" data-i18n="[title]openNewTab" tabindex="13">KeePassXC Team</a>.
</p>
<hr />
<p>
<span data-i18n="optionsAboutLicenses"></span><br>
<span data-i18n="optionsAboutLicenses"></span>:<br>
<details>
<summary>Bootstrap</summary>
@ -545,13 +550,22 @@
</pre>
</details>
</p>
<hr />
<p>
<span data-i18n="optionsAboutReportBugs"></span>:<br>
<a target="_blank" href="https://github.com/keepassxreboot/keepassxc-browser/issues/new" tabindex="14"><span data-i18n="optionsAboutCreateNewIssue"></span></a>
<p><span data-i18n="optionsAboutDebugInfo"></span>:
<p>KeePassXC - <span class="kpxcVersion"></span>
<br>KeePassXC-Browser - <span class="kpxcbrVersion"></span>
<br>Operating system: <span class="kpxcbrOS"></span>
<br>Browser: <span class="kpxcbrBrowser"></span>
</p>
</p>
</div>
<hr>
<div class="footer">
<p>(C) 2017-2018 - KeePassXC Team</p>
<p>(C) 2017-2019 - KeePassXC Team</p>
</div>
</div> <!-- /container -->
<script type="text/javascript" src="../translate.js"></script>
</body>
</html>

View file

@ -23,6 +23,9 @@ $(function() {
options.initAbout();
});
});
// Set options page language to HTML element from locale
$('html').attr('lang', browser.i18n.getUILanguage());
});
var options = options || {};
@ -54,7 +57,9 @@ options.saveSettingsPromise = function() {
options.saveSetting = function(name) {
const id = '#' + name;
$(id).closest('.control-group').removeClass('error').addClass('success');
setTimeout(() => { $(id).closest('.control-group').removeClass('success'); }, 2500);
setTimeout(() => {
$(id).closest('.control-group').removeClass('success');
}, 2500);
browser.storage.local.set({ 'settings': options.settings });
browser.runtime.sendMessage({
@ -121,7 +126,9 @@ options.initGeneralSettings = function() {
browser.commands.getAll().then(function(commands) {
commands.forEach(function(command) {
var shortcut = document.getElementById(`${command.name}-shortcut`);
if (!shortcut) return;
if (!shortcut) {
return;
}
shortcut.textContent = command.shortcut || 'not configured';
});
});
@ -133,7 +140,12 @@ options.initGeneralSettings = function() {
});
$('#blinkTimeoutButton').click(function() {
const blinkTimeout = $.trim($('#blinkTimeout').val());
const input = document.querySelector('#blinkTimeout');
if (!input.validity.valid) {
options.createWarning(input, tr('optionsErrorInvalidValue'));
}
const blinkTimeout = input.value;
const blinkTimeoutval = blinkTimeout !== '' ? Number(blinkTimeout) : defaultSettings.blinkTimeout;
options.settings['blinkTimeout'] = blinkTimeoutval;
@ -167,6 +179,7 @@ options.showKeePassXCVersions = function(response) {
$('#tab-general-settings .kphVersion:first em.yourVersion:first').text(response.current);
$('#tab-general-settings .kphVersion:first em.latestVersion:first').text(response.latest);
$('#tab-about em.versionKPH').text(response.current);
$('#tab-about span.kpxcVersion').text(response.current);
$('#tab-general-settings button.checkUpdateKeePassXC:first').attr('disabled', false);
};
@ -181,6 +194,9 @@ options.initConnectedDatabases = function() {
$('#dialogDeleteConnectedDatabase').data('hash', $(this).closest('tr').data('hash'));
$('#dialogDeleteConnectedDatabase .modal-body:first span:first').text($(this).closest('tr').children('td:first').text());
$('#dialogDeleteConnectedDatabase').modal('show');
$('#dialogDeleteConnectedDatabase').on('shown.bs.modal', () => {
$('#dialogDeleteConnectedDatabase').find('[autofocus]').focus();
});
});
$('#dialogDeleteConnectedDatabase .modal-footer:first button.yes:first').click(function(e) {
@ -240,6 +256,9 @@ options.initCustomCredentialFields = function() {
$('#dialogDeleteCustomCredentialFields').data('tr-id', $(this).closest('tr').attr('id'));
$('#dialogDeleteCustomCredentialFields .modal-body:first strong:first').text($(this).closest('tr').children('td:first').text());
$('#dialogDeleteCustomCredentialFields').modal('show');
$('#dialogDeleteCustomCredentialFields').on('shown.bs.modal', () => {
$('#dialogDeleteCustomCredentialFields').find('[autofocus]').focus();
});
});
$('#dialogDeleteCustomCredentialFields .modal-footer:first button.yes:first').click(function(e) {
@ -287,6 +306,9 @@ options.initSitePreferences = function() {
$('#dialogDeleteSite').data('tr-id', $(this).closest('tr').attr('id'));
$('#dialogDeleteSite .modal-body:first strong:first').text($(this).closest('tr').children('td:first').text());
$('#dialogDeleteSite').modal('show');
$('#dialogDeleteSite').on('shown.bs.modal', () => {
$('#dialogDeleteSite').find('[autofocus]').focus();
});
});
$('#tab-site-preferences tr.clone:first input[type=checkbox]:first').change(function() {
@ -310,15 +332,37 @@ options.initSitePreferences = function() {
});
$('#manualUrl').keyup(function(event) {
if (event.keyCode === 13) {
if (event.key === 'Enter') {
$('#sitePreferencesManualAdd').click();
}
});
$('#sitePreferencesManualAdd').click(function(e) {
e.preventDefault();
let value = $('#manualUrl').val();
const manualUrl = document.querySelector('#manualUrl');
if (!manualUrl) {
return;
}
// Show error for invalid input
if (!manualUrl.validity.valid) {
options.createWarning(manualUrl, tr('optionsErrorInvalidURL'));
return;
}
const errorMessage = tr('optionsErrorValueExists');
let value = manualUrl.value;
if (value.length > 10 && value.length <= 2000) {
// Fills the last / char if needed. This ensures the compatibility with Match Patterns
if (slashNeededForUrl(value)) {
value += '/';
}
// Check if the URL is already in the list
if (options.settings['sitePreferences'].some(s => s.url === value)) {
options.createWarning(manualUrl, errorMessage);
return;
}
if (options.settings['sitePreferences'] === undefined) {
options.settings['sitePreferences'] = [];
}
@ -327,11 +371,6 @@ options.initSitePreferences = function() {
const trClone = $('#tab-site-preferences table tr.clone:first').clone(true);
trClone.removeClass('clone');
// Fills the last / char if needed. This ensures the compatibility with Match Patterns
if (slashNeededForUrl(value)) {
value += '/';
}
const tr = trClone.clone(true);
tr.data('url', value);
tr.attr('id', 'tr-scf' + newValue);
@ -342,8 +381,7 @@ options.initSitePreferences = function() {
options.settings['sitePreferences'].push({ url: value, ignore: IGNORE_NOTHING, usernameOnly: false });
options.saveSettings();
$('#manualUrl').val('');
manualUrl.value = '';
}
});
@ -393,10 +431,44 @@ options.initSitePreferences = function() {
};
options.initAbout = function() {
$('#tab-about em.versionCIP').text(browser.runtime.getManifest().version);
const version = browser.runtime.getManifest().version;
$('#tab-about em.versionCIP').text(version);
$('#tab-about span.kpxcbrVersion').text(version);
$('#tab-about span.kpxcbrOS').text(navigator.platform);
$('#tab-about span.kpxcbrBrowser').text(getBrowserId());
// Hides keyboard shortcut configure button if Firefox version is < 60 (API is not compatible)
if (isFirefox() && Number(navigator.userAgent.substr(navigator.userAgent.lastIndexOf('/') + 1, 2)) < 60) {
$('#chrome-only').remove();
}
};
options.createWarning = function(elem, text) {
const banner = document.createElement('div');
banner.classList.add('alert', 'alert-dismissible', 'alert-danger', 'fade', 'in');
banner.style.position = 'absolute';
banner.style.left = String(elem.offsetLeft) + 'px';
banner.style.top = String(elem.offsetTop + elem.offsetHeight) + 'px';
banner.style.padding = '0px';
banner.style.width = '300px';
banner.textContent = text;
elem.parentElement.append(banner);
// Destroy the warning after five seconds
setTimeout(() => {
elem.parentElement.removeChild(banner);
}, 5000);
};
const getBrowserId = function() {
if (navigator.userAgent.indexOf('Firefox') > -1) {
return 'Mozilla Firefox ' + navigator.userAgent.substr(navigator.userAgent.lastIndexOf('/') + 1);
} else if (navigator.userAgent.indexOf('Chrome') > -1) {
let startPos = navigator.userAgent.indexOf('Chrome');
startPos = navigator.userAgent.indexOf('/', startPos) + 1;
const version = navigator.userAgent.substring(startPos, navigator.userAgent.indexOf('Safari'));
return 'Chrome/Chromium ' + version;
}
return 'Other/Unknown';
};

View file

@ -24,23 +24,29 @@
<hr />
<div class="conf-content">
<div id="conf-row">
<div><span data-i18n="contextMenuFillUsernameAndPassword"></span></div>
<input type="text" id="fill-username-password"/>
<div><label for="fill_username_password" data-i18n="contextMenuFillUsernameAndPassword"></label></div>
<input type="text" id="fill_username_password"/>
<button class="btn btn-sm btn-primary" id="buttonSaveUserandPass" type="button"><span class="glyphicon glyphicon-floppy-disk"></span> <span data-i18n="optionsButtonSave"/></button>
<button class="btn btn-sm btn-danger"><span class="glyphicon glyphicon-remove-sign"></span> <span data-i18n="optionsButtonReset"></span></button>
</div>
<div id="conf-row">
<div><span data-i18n="contextMenuFillPassword"></span></div>
<input type="text" id="fill-password"/>
<div><label for="fill_password" data-i18n="contextMenuFillPassword"></label></div>
<input type="text" id="fill_password"/>
<button class="btn btn-sm btn-primary" id="buttonSavePass" type="button"><span class="glyphicon glyphicon-floppy-disk"></span> <span data-i18n="optionsButtonSave"/></button>
<button class="btn btn-sm btn-danger"><span class="glyphicon glyphicon-remove-sign"></span> <span data-i18n="optionsButtonReset"></span></button>
</div>
<div id="conf-row">
<div><span data-i18n="contextMenuFillTOTP"></span></div>
<input type="text" id="fill-totp"/>
<div><label for="fill_totp" data-i18n="contextMenuFillTOTP"></label></div>
<input type="text" id="fill_totp"/>
<button class="btn btn-sm btn-primary" id="buttonSaveTotp" type="button"><span class="glyphicon glyphicon-floppy-disk"></span> <span data-i18n="optionsButtonSave"/></button>
<button class="btn btn-sm btn-danger"><span class="glyphicon glyphicon-remove-sign"></span> <span data-i18n="optionsButtonReset"></span></button>
</div>
<div id="conf-row">
<div><label for="show_password_generator" data-i18n="contextMenuShowPasswordGenerator"></label></div>
<input type="text" id="show_password_generator"/>
<button class="btn btn-sm btn-primary" id="buttonSaveGenerator" type="button"><span class="glyphicon glyphicon-floppy-disk"></span> <span data-i18n="optionsButtonSave"/></button>
<button class="btn btn-sm btn-danger"><span class="glyphicon glyphicon-remove-sign"></span> <span data-i18n="optionsButtonReset"></span></button>
</div>
</div>
</div>
</body>

View file

@ -11,14 +11,14 @@ document.querySelectorAll('input').forEach((b) => {
const saveButtons = document.querySelectorAll('.btn-primary');
for (const b of saveButtons) {
b.addEventListener('click', (e) => {
updateShortcut(b.parentElement.children[1].getAttribute('id'))
updateShortcut(b.parentElement.children[1].getAttribute('id'));
});
}
const resetButtons = document.querySelectorAll('.btn-danger');
for (const b of resetButtons) {
b.addEventListener('click', (e) => {
resetShortcut(b.parentElement.children[1].getAttribute('id'))
resetShortcut(b.parentElement.children[1].getAttribute('id'));
});
}
@ -31,7 +31,7 @@ async function handleKeyDown(e) {
tempArray.push(key);
keyArray.push(key);
}
};
}
async function handleKeyUp(e) {
if (!e.repeat) {
@ -55,7 +55,7 @@ async function handleKeyUp(e) {
e.currentTarget.value = text;
}
}
};
}
async function updateKeys() {
const commands = await browser.commands.getAll();
@ -65,7 +65,7 @@ async function updateKeys() {
elem.value = c.shortcut;
}
}
};
}
async function updateShortcut(shortcut) {
try {
@ -78,13 +78,13 @@ async function updateShortcut(shortcut) {
console.log('Cannot change shortcut: ' + e);
createBanner('danger', shortcut);
}
};
}
async function resetShortcut(shortcut) {
await browser.commands.reset(shortcut);
createBanner('info', shortcut);
updateKeys();
};
}
// Ctrl behaves differently on different OS's. macOS needs to return MacCtrl instead of Ctrl (which will be handled as Command)
function handleControl() {
@ -112,6 +112,6 @@ function createBanner(type, shortcut) {
setTimeout(() => {
document.body.removeChild(banner);
}, 5000);
};
}
document.addEventListener('DOMContentLoaded', updateKeys);

View file

@ -2,14 +2,33 @@
const items = document.querySelectorAll('[data-i18n]');
for (const item of items) {
const key = item.getAttribute('data-i18n');
let key = item.getAttribute('data-i18n');
if (key) {
let attr = '';
[ attr, key] = trAttribute(key);
const placeholder = item.getAttribute('i18n-placeholder');
const translation = placeholder ? browser.i18n.getMessage(key, placeholder) : browser.i18n.getMessage(key);
if (item.hasAttribute('href')) {
if (attr) {
item.setAttribute(attr, translation);
} else if (item.hasAttribute('href')) {
item.text = translation;
} else {
item.innerHTML = translation;
}
// Remove the translation attribute from the HTML element
item.removeAttribute('data-i18n');
}
}
// Check if a custom attribute is being translated using [attribute]key
function trAttribute(key) {
let attr = /\[(\w+)\]/.exec(key);
if (attr) {
attr = attr[1];
key = key.slice(key.indexOf(']') + 1);
}
return [ attr, key ];
};