mirror of
https://github.com/keepassxreboot/keepassxc-browser.git
synced 2026-03-11 08:54:43 +00:00
Compare commits
No commits in common. "develop" and "1.9.9.2" have entirely different histories.
83 changed files with 1798 additions and 5564 deletions
195
.eslintrc
Normal file
195
.eslintrc
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
{
|
||||
"extends": "eslint:recommended",
|
||||
"ignorePatterns": ["**/*.min.js"],
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2022": true,
|
||||
"jquery": true,
|
||||
"webextensions": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 13
|
||||
},
|
||||
"rules": {
|
||||
"array-bracket-spacing": ["error", "always"],
|
||||
"arrow-body-style": "warn",
|
||||
"arrow-parens": "off",
|
||||
"arrow-spacing": "warn",
|
||||
"block-scoped-var": "off",
|
||||
"brace-style": ["warn", "1tbs"],
|
||||
"camelcase": "warn",
|
||||
"class-methods-use-this": "off",
|
||||
"comma-dangle": "off",
|
||||
"computed-property-spacing": ["error", "never"],
|
||||
"consistent-return": "off",
|
||||
"dot-notation": "off",
|
||||
"eqeqeq": "error",
|
||||
"func-names": "off",
|
||||
"function-paren-newline": "off",
|
||||
"guard-for-in": "off",
|
||||
"implicit-arrow-linebreak": "off",
|
||||
"indent": ["error", 4],
|
||||
"linebreak-style": ["error", "unix"],
|
||||
"lines-around-comment": "off",
|
||||
"max-len": "off",
|
||||
"no-alert": "error",
|
||||
"no-await-in-loop": "off",
|
||||
"no-bitwise": "off",
|
||||
"no-console": "off",
|
||||
"no-continue": "off",
|
||||
"no-control-regex": "off",
|
||||
"no-else-return": "off",
|
||||
"no-empty-function": ["error", { "allow": ["arrowFunctions", "methods", "asyncMethods"] }],
|
||||
"no-extend-native": "off",
|
||||
"no-global-assign": "off",
|
||||
"no-lonely-if": "off",
|
||||
"no-loop-func": "off",
|
||||
"no-mixed-operators": "off",
|
||||
"no-mixed-spaces-and-tabs": ["error", "smart-tabs"],
|
||||
"no-multi-spaces": "warn",
|
||||
"no-param-reassign": "off",
|
||||
"no-plusplus": "off",
|
||||
"no-prototype-builtins": "off",
|
||||
"no-redeclare": "off",
|
||||
"no-restricted-globals": "off",
|
||||
"no-restricted-syntax": "off",
|
||||
"no-return-await": "off",
|
||||
"no-tabs": "warn",
|
||||
"no-underscore-dangle": "off",
|
||||
"no-unused-vars": ["warn", { "args": "none", "varsIgnorePattern": "[A-Z_]+|tr|[$]" }],
|
||||
"no-use-before-define": "off",
|
||||
"no-useless-escape": "off",
|
||||
"no-useless-return": "off",
|
||||
"no-var": "error",
|
||||
"object-curly-spacing": ["warn", "always"],
|
||||
"object-shorthand": "off",
|
||||
"operator-linebreak": "off",
|
||||
"prefer-arrow-callback": "off",
|
||||
"prefer-const": "warn",
|
||||
"prefer-destructuring": "off",
|
||||
"prefer-template": "off",
|
||||
"quote-props": "off",
|
||||
"quotes": ["error", "single", { "avoidEscape": true }],
|
||||
"semi": ["error", "always"],
|
||||
"space-before-blocks": "warn",
|
||||
"space-before-function-paren": "off",
|
||||
"space-in-parens": ["error", "never"],
|
||||
"space-unary-ops": "off",
|
||||
"spaced-comment": "off",
|
||||
"strict": "off",
|
||||
"vars-on-top": "off"
|
||||
},
|
||||
"globals": {
|
||||
"_called": "readonly",
|
||||
"acceptedOTPFields": "readonly",
|
||||
"assertInputFields": "readonly",
|
||||
"assertPasswordChangeFields": "readonly",
|
||||
"assertRegex": "readonly",
|
||||
"assertSearchField": "readonly",
|
||||
"assertSearchForm": "readonly",
|
||||
"assertTOTPField": "readonly",
|
||||
"AssociatedAction": "readonly",
|
||||
"AuthenticatorAssertionResponse": "readonly",
|
||||
"AuthenticatorAttestationResponse": "readonly",
|
||||
"Autocomplete": "readonly",
|
||||
"BannerPosition": "readonly",
|
||||
"BLUE_BUTTON": "readonly",
|
||||
"bootstrap": "readonly",
|
||||
"browser": "readonly",
|
||||
"browserAction": "readonly",
|
||||
"CHECK_UPDATE_NEVER": "readonly",
|
||||
"CHECK_UPDATE_ONE_MONTH": "readonly",
|
||||
"CHECK_UPDATE_ONE_WEEK": "readonly",
|
||||
"CHECK_UPDATE_THREE_DAYS": "readonly",
|
||||
"cloneInto": "readonly",
|
||||
"compareVersion": "readonly",
|
||||
"createResult": "readonly",
|
||||
"createStylesheet": "readonly",
|
||||
"DatabaseState": "readonly",
|
||||
"debugLogMessage": "readonly",
|
||||
"EXTENSION_NAME": "readonly",
|
||||
"getCurrentTab": "readonly",
|
||||
"getLoginData": "readonly",
|
||||
"getTopLevelDomainFromUrl": "readonly",
|
||||
"GRAY_BUTTON_CLASS": "readonly",
|
||||
"GREEN_BUTTON": "readonly",
|
||||
"httpAuth": "readonly",
|
||||
"Icon": "readonly",
|
||||
"IGNORE_AUTOSUBMIT": "readonly",
|
||||
"IGNORE_FULL": "readonly",
|
||||
"IGNORE_NORMAL": "readonly",
|
||||
"IGNORE_NOTHING": "readonly",
|
||||
"importScripts": "readonly",
|
||||
"IMPROVED_DETECTION_PREDEFINED_SITELIST": "readonly",
|
||||
"initColorTheme": "readonly",
|
||||
"isEdge": "readonly",
|
||||
"isFirefox": "readonly",
|
||||
"keepass": "readonly",
|
||||
"keepassClient": "readonly",
|
||||
"kpActions": "readonly",
|
||||
"kpErrors": "readonly",
|
||||
"kpxc": "readonly",
|
||||
"kpxcAssert": "readonly",
|
||||
"kpxcBanner": "readonly",
|
||||
"kpxcCustomLoginFieldsBanner": "readonly",
|
||||
"kpxcEvent": "readonly",
|
||||
"kpxcFields": "readonly",
|
||||
"kpxcFill": "readonly",
|
||||
"kpxcForm": "readonly",
|
||||
"kpxcIcons": "readonly",
|
||||
"kpxcObserverHelper": "readonly",
|
||||
"kpxcPasswordGenerator": "readonly",
|
||||
"kpxcPasswordIcons": "readonly",
|
||||
"kpxcSites": "readonly",
|
||||
"kpxcTOTPAutocomplete": "readonly",
|
||||
"kpxcTOTPIcons": "readonly",
|
||||
"kpxcUI": "readonly",
|
||||
"kpxcUserAutocomplete": "readonly",
|
||||
"kpxcUsernameIcons": "readonly",
|
||||
"logDebug": "readonly",
|
||||
"logError": "readonly",
|
||||
"kpxcPasskeysUtils": "readonly",
|
||||
"ManualFill": "readonly",
|
||||
"matchesWithNodeName": "readonly",
|
||||
"MAX_AUTOCOMPLETE_NAME_LEN": "readonly",
|
||||
"MAX_OPACITY": "readonly",
|
||||
"MAX_TOTP_INPUT_LENGTH": "readonly",
|
||||
"menuContexts": "readonly",
|
||||
"MIN_INPUT_FIELD_OFFSET_WIDTH": "readonly",
|
||||
"MIN_INPUT_FIELD_WIDTH_PX": "readonly",
|
||||
"MIN_OPACITY": "readonly",
|
||||
"MIN_TOTP_INPUT_LENGTH": "readonly",
|
||||
"module": "readonly",
|
||||
"nacl": "readonly",
|
||||
"ORANGE_BUTTON": "readonly",
|
||||
"page": "readonly",
|
||||
"Pixels": "readonly",
|
||||
"PublicKeyCredential": "readonly",
|
||||
"PREDEFINED_SITELIST": "readonly",
|
||||
"RED_BUTTON": "readonly",
|
||||
"retrieveColorScheme": "readonly",
|
||||
"sendMessage": "readonly",
|
||||
"showNotification": "readonly",
|
||||
"siteMatch": "readonly",
|
||||
"slashNeededForUrl": "readonly",
|
||||
"SORT_BY_GROUP_AND_TITLE": "readonly",
|
||||
"SORT_BY_GROUP_AND_USERNAME": "readonly",
|
||||
"SORT_BY_MATCHING_CREDENTIALS_SETTING": "readonly",
|
||||
"SORT_BY_RELEVANT_ENTRY": "readonly",
|
||||
"SORT_BY_TITLE": "readonly",
|
||||
"SORT_BY_USERNAME": "readonly",
|
||||
"statusResponse": "readonly",
|
||||
"Tests": "readonly",
|
||||
"tr": "readonly",
|
||||
"trimURL": "readonly",
|
||||
"updateDefaultPasswordManager": "readonly"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["./*.js"],
|
||||
"env": {
|
||||
"node": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
196
.github/CONTRIBUTING.md
vendored
196
.github/CONTRIBUTING.md
vendored
|
|
@ -1,196 +0,0 @@
|
|||
|
||||
# Contributing to KeePassXC-Browser
|
||||
|
||||
The following is a set of guidelines for contributing to KeePassXC-Browser on GitHub.
|
||||
These are just guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request.
|
||||
|
||||
#### Table of contents
|
||||
|
||||
[What should I know before I get started?](#what-should-i-know-before-i-get-started)
|
||||
* [Open Source Contribution Policy](#open-source-contribution-policy)
|
||||
|
||||
[How can I contribute?](#how-can-i-contribute)
|
||||
* [Feature requests](#feature-requests)
|
||||
* [Bug reports](#bug-reports)
|
||||
* [Discuss with the team](#discuss-with-the-team)
|
||||
* [Your first code contribution](#your-first-code-contribution)
|
||||
* [Using AI](#using-ai)
|
||||
* [Pull requests](#pull-requests)
|
||||
* [Translations](#translations)
|
||||
|
||||
[Styleguides](#styleguides)
|
||||
* [Git branch strategy](#git-branch-strategy)
|
||||
* [Git commit messages](#git-commit-messages)
|
||||
* [Coding styleguide](#coding-styleguide)
|
||||
|
||||
|
||||
## What should I know before I get started?
|
||||
### Open Source Contribution Policy
|
||||
**Source**: [Version 0.3, 2015–11–18](https://medium.com/@jmaynard/a-contribution-policy-for-open-source-that-works-bfc4600c9d83#.i9ntbhmad)
|
||||
|
||||
#### Policy
|
||||
|
||||
We will accept contributions of good code that we can use from anyone.
|
||||
|
||||
#### What this means
|
||||
|
||||
This means exactly that. We don’t care about anything but your code. We don’t care about your race, religion, national origin, biological gender, perceived gender, sexual orientation, lifestyle, political viewpoint, or anything extraneous like that. We will neither reject your contribution nor grant it preferential treatment on any basis except the code itself. We do, however, reserve the right to limit your access to our community if you violate our [Code of Conduct][code-of-conduct].
|
||||
|
||||
#### If Your Contribution Is Rejected
|
||||
|
||||
If we reject your contribution, it means only that we do not consider it suitable for our project in the form it was submitted. It is not personal. If you ask civilly, we will be happy to discuss it with you and help you understand why it was rejected, and if possible improve it so we can accept it.
|
||||
|
||||
## How can I contribute?
|
||||
### Feature requests
|
||||
|
||||
We're always looking for suggestions to improve our application. If you have a suggestion to improve an existing feature, or would like to suggest a completely new feature for KeePassXC, please use the [issue tracker on GitHub][issues-section].
|
||||
|
||||
### Bug reports
|
||||
|
||||
Our software isn't always perfect, but we strive to always improve our work. You may file bug reports in the issue tracker.
|
||||
|
||||
Before submitting a bug report, check if the problem has already been reported. Please refrain from opening a duplicate issue. If you want to add further information to an existing issue, simply add a comment on that issue.
|
||||
|
||||
### Discuss with the team
|
||||
|
||||
As with feature requests, you can talk to the KeePassXC team about bugs, new features, other issues and pull requests on the dedicated issue tracker, on the [Matrix development channel](https://matrix.to/#/!RhJPJPGwQIFVQeXqZa:matrix.org?via=matrix.org), or in the IRC channel on Libera.Chat (`#keepassxc-dev` on `irc.libera.chat`, or use a [webchat link](https://web.libera.chat/#keepassxc-dev)).
|
||||
|
||||
### Your first code contribution
|
||||
|
||||
Unsure where to begin contributing to KeePassXC? You can start by looking through these `beginner` and `help-wanted` issues:
|
||||
|
||||
* [Beginner issues][beginner] – issues which should only require a few lines of code, and a test or two.
|
||||
* ['Help wanted' issues][help-wanted] – issues which should be a bit more involved than `beginner` issues.
|
||||
|
||||
Both issue lists are sorted by total number of comments. While not perfect, looking at the number of comments on an issue can give a general idea of how much an impact a given change will have.
|
||||
|
||||
### Using AI
|
||||
|
||||
Submissions written using generative AI (even partially) are not accepted.
|
||||
|
||||
### Pull requests
|
||||
|
||||
Along with our desire to hear your feedback and suggestions, we're also interested in accepting direct assistance in the form of code.
|
||||
|
||||
All pull requests must comply with the above requirements and with the [styleguides](#styleguides).
|
||||
|
||||
### Translations
|
||||
|
||||
Translations are managed on [Transifex](https://explore.transifex.com/keepassxc/keepassxc-browser/) which offers a web interface.
|
||||
Please join an existing language team or request a new one if there is none.
|
||||
|
||||
If you open a Pull Request with new strings that require translations, it is enough to include new strings to the `_locales/en/messages.json` file.
|
||||
|
||||
### Architecture
|
||||
|
||||
Some basic information about the extension architecture is described in the [Extension details][extension-details] wiki page.
|
||||
|
||||
## Styleguides
|
||||
|
||||
### Git branch strategy
|
||||
|
||||
The Branch Strategy is based on [git-flow-lite](http://nvie.com/posts/a-successful-git-branching-model/).
|
||||
|
||||
* **develop** – points to the development of the next release, contains tested and reviewed code
|
||||
* **feature/**[name] – points to a branch with a new feature, one which is candidate for merge into develop (subject to rebase)
|
||||
* **fix/**[name] – points to a branch with a fix for a particular issue ID
|
||||
* **refactor/**[name] - points to a branch with a refactored feature
|
||||
|
||||
|
||||
### Git commit messages
|
||||
|
||||
* Use the present tense ("Add feature" not "Added feature")
|
||||
* Use the imperative mood ("Move cursor to…" not "Moves cursor to…")
|
||||
* Limit the first line to 72 characters or less
|
||||
* Reference issues and pull requests liberally
|
||||
* If your pull request fixes an existing issue, add "Fixes #ISSUENUMBER" to your pull request description
|
||||
|
||||
### Coding styleguide
|
||||
|
||||
The coding style of the project is applied using `ESLint` and `Prettier` tools. A thorough description
|
||||
of the coding style can be found in the `.eslintrc` and `.prettierrc` files, but the main conventions are presented here.
|
||||
|
||||
ESLint can be run manually by using the command `npm run lint`. Prettier formatting is not used by default because it can break some specific code styles, but can be enabled modifying the `.prettierignore` file.
|
||||
|
||||
#### Naming convention
|
||||
`lowerCamelCase`
|
||||
|
||||
For names made of only one word, the first letter should be lowercase.
|
||||
For names made of multiple concatenated words, the first letter of the whole is lowercase, and the first letter of each subsequent word is capitalized.
|
||||
Applies to functions and Objects as well.
|
||||
|
||||
`UpperCamelCase`
|
||||
|
||||
For classes and enum-like Objects.
|
||||
|
||||
`ALL_CAPS`
|
||||
|
||||
For global/const variables and enum-like Object values.
|
||||
|
||||
#### Indention
|
||||
- For **JavaScript files** (*.js): 4 spaces
|
||||
- For **HTML files** (*.ui*): 2 spaces
|
||||
- For **JSON files** (*.json): 2 spaces
|
||||
- For **TypeScript files** (*.ts): 2 spaces
|
||||
|
||||
#### Global/const variables
|
||||
```javascript
|
||||
const TIMEOUT_VALUE 2000;
|
||||
const DEFAULT_VALUE = 'all';
|
||||
```
|
||||
|
||||
#### Classes
|
||||
```javascript
|
||||
class Icon {
|
||||
constructor(databaseState) {
|
||||
this.databaseState = databaseState;
|
||||
this.icon = null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Enum-like Objects
|
||||
```javascript
|
||||
const ManualFill = {
|
||||
NONE: 0,
|
||||
PASSWORD: 1,
|
||||
BOTH: 2
|
||||
};
|
||||
```
|
||||
|
||||
#### Braces
|
||||
```javascript
|
||||
if (condition) {
|
||||
doSomething();
|
||||
} else {
|
||||
doSomethingElse();
|
||||
}
|
||||
|
||||
const exampleFunction = async function() {
|
||||
doSomething();
|
||||
};
|
||||
```
|
||||
|
||||
#### Multiple conditions
|
||||
```javascript
|
||||
if (condition1 === condition2
|
||||
|| condition3 === condition4
|
||||
|| condition5 === condition6) {
|
||||
doSomething();
|
||||
}
|
||||
|
||||
if (['text1', 'text2', 'text3'].includes(inputText)) {
|
||||
doSomething();
|
||||
}
|
||||
```
|
||||
|
||||
#### Variables
|
||||
|
||||
Always use `const` and `let` to define a variable. Do not use `var`.
|
||||
|
||||
|
||||
[beginner]:https://github.com/keepassxreboot/keepassxc-browser/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22
|
||||
[code-of-conduct]:https://github.com/keepassxreboot/keepassxc/blob/develop/CODE-OF-CONDUCT.md
|
||||
[help-wanted]:https://github.com/keepassxreboot/keepassxc-browser/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22help%20wanted%22
|
||||
[issues-section]:https://github.com/keepassxreboot/keepassxc-browser/issues
|
||||
[extension-details]:https://github.com/keepassxreboot/keepassxc-browser/wiki/Extension-details@
|
||||
91
CHANGELOG
91
CHANGELOG
|
|
@ -1,94 +1,3 @@
|
|||
1.10.0.1 (2026-03-08)
|
||||
=========================
|
||||
- Remove webRequestBlocking permission from V3 manifests [#2893]
|
||||
- Fix dynamic password input detection when replacing username [#2896]
|
||||
|
||||
1.10.0 (2026-03-04)
|
||||
=========================
|
||||
- Add preliminary support for Safari [#2800]
|
||||
- Add 'mfaCode' to accepted 2FA token fields (thanks hostops) [#2804]
|
||||
- Add "postcode" to ignored TOTP list (thanks JackDunnCodes) [#2793]
|
||||
- Add support for Related Origin Requests with passkeys [#2828]
|
||||
- Add support for features list [#2848]
|
||||
- Add support for publicKey in passkeys response [#2782]
|
||||
- Fix hiding form switch animation when options page loads (thanks stefansundin) [#2807]
|
||||
- Fix TOTP field detection with Microsoft sites [#2832]
|
||||
- Fix the ability to fake same-orogin with passkeys (thanks a2kolbasov) [#2849]
|
||||
- Fix detecting transitions [#2855]
|
||||
- Fix Google password input detection [#2861]
|
||||
- Fix Epic Games and Paypal password input detection [#2863]
|
||||
- Fix using invalid parameters with MutationObserver [#2784]
|
||||
- Fix ignoring partial nodeNames [#2745]
|
||||
- Fix incorrect error message on keyboard fill [#2814]
|
||||
- Fix reopen database shortcut (thanks a2kolbasov) [#2767]
|
||||
- Fix showing notifications always on the top window [#2789]
|
||||
- Fix missing digits in TOTP fill on some sites (thanks ElementW) [#2794]
|
||||
- Fix setting default redirect allowance to 3 (thanks SinnySupernova) [#2797]
|
||||
- Fix createObserver() check [#2873]
|
||||
- Fix retry on input field detection with Custom Login Fields [#2875]
|
||||
- Fix detecting new password input on forms [#2878]
|
||||
- Fix form submit to ignore password related buttons [#2882]
|
||||
- Refactor icon handling [#2791]
|
||||
- Update webextension-polyfill library [#2822]
|
||||
|
||||
1.9.11 (2025-11-26)
|
||||
=========================
|
||||
- Add support for reseting all settings to defaults [#2720]
|
||||
- Add support for toJSON() with createPublicKeyCredential (passkeys) [#2734]
|
||||
- Add site exception for dei.gr [#2760]
|
||||
- Add submit button exception for Dubverse [#2759]
|
||||
- Add support for selecting a custom submit button with Custom Login Fields [#2756]
|
||||
- Fix check for Firefox browser [#2724, #2725]
|
||||
- Fix JavaScript error in console (thanks ygoe) [#2721]
|
||||
- Fix overlap check with labels [#2731]
|
||||
- Fix checking overlays before fill [#2733]
|
||||
- Fix requerying form if password input changed [#2738]
|
||||
- Fix incorrect fields variable [#2740]
|
||||
- Fix malformed page content with passkeys script injection (thanks a2kolbasov) [#2757]
|
||||
- Fix layout issues with banners [#2764]
|
||||
- Update Playwright libraries [#2727]
|
||||
|
||||
1.9.10 (2025-10-05)
|
||||
=========================
|
||||
- Add Disable passkeys option to Site Preferences [#2694]
|
||||
- Add overlay exceptions feature to Predefined Sites [#2693]
|
||||
- Change DOM to use popover for icons and content [#2687]
|
||||
- Change some icons [#2706]
|
||||
- Fix detecting existing login combination [#2682]
|
||||
- Fix Mutation Observer to handle multiple element mutations (thanks mihai-vlc) [#2635]
|
||||
- Fix using popovers with Firefox ESR 115 [#2690]
|
||||
- Fix filling relevant entries [#2691]
|
||||
- Fix filling wrapped input fields [#2699]
|
||||
- Fix filling username to already filled field [#2700]
|
||||
- Fix ignore regex list (thanks EamonNerbonne) [#2711]
|
||||
- Fix is element inside function [#2714]
|
||||
- Fix handling window offsets for DOM content positioning [#2698]
|
||||
- Refactor Site Preferences options settings page [#2666]
|
||||
- Refactor browser information function [#2709]
|
||||
|
||||
1.9.9.6 (2025-09-09)
|
||||
=========================
|
||||
- Add caching to overlay detection for improved performance [#2670]
|
||||
- Fix protection against form overlays [#2676]
|
||||
- Fix using custom functions with Elements in TreeWalker [#2677]
|
||||
|
||||
1.9.9.5 (2025-09-03)
|
||||
=========================
|
||||
- Fix topmost input element check with labels [#2667]
|
||||
- Fix Custom Login Fields with topmost element check [#2673]
|
||||
|
||||
1.9.9.4 (2025-08-29)
|
||||
=========================
|
||||
- Fix topmost element check with Shadow DOM [#2659]
|
||||
|
||||
1.9.9.3 (2025-08-27)
|
||||
=========================
|
||||
- Add protection against style changes for wrapper div elements [#2646]
|
||||
- Add protection against for sandboxed iframes [#2648]
|
||||
- Add protection against overlays [#2651]
|
||||
- Add protection against html, body and form opacity [#2653]
|
||||
- Add error message for untrusted iframes during fill [#2649]
|
||||
|
||||
1.9.9.2 (2025-08-21)
|
||||
=========================
|
||||
- Fix parent element opacity check for better clickjacking attempt prevention [#2641]
|
||||
|
|
|
|||
11
README.md
11
README.md
|
|
@ -1,12 +1,15 @@
|
|||
# KeePassXC-Browser
|
||||
|
||||
Browser extension for [KeePassXC](https://keepassxc.org/) with [native messaging](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_messaging).
|
||||
Browser extension for [KeePassXC](https://keepassxc.org/) with [Native Messaging](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_messaging).
|
||||
|
||||
Based on [pfn](https://github.com/pfn)'s [chromeIPass](https://github.com/pfn/passifox).
|
||||
Some changes merged also from [smorks](https://github.com/smorks)' [KeePassHttp-Connector](https://github.com/smorks/keepasshttp-connector).
|
||||
|
||||
## Download and use
|
||||
|
||||
This browser extension was first supported in KeePassXC 2.3.0 (release end of 2017). In general it is advised to only use the latest available release.
|
||||
|
||||
Get the extension for [Firefox](https://addons.mozilla.org/en-US/firefox/addon/keepassxc-browser/) or [Chrome/Chromium](https://chromewebstore.google.com/detail/keepassxc-browser/oboonakemofpalcgghocfoadofidjkkk) or [Microsoft Edge](https://microsoftedge.microsoft.com/addons/detail/pdffhmdngciaglkoonimfcmckehcpafo).
|
||||
Get the extension for [Firefox](https://addons.mozilla.org/en-US/firefox/addon/keepassxc-browser/) or [Chrome/Chromium](https://chromewebstore.google.com/detail/keepassxc-browser/oboonakemofpalcgghocfoadofidjkkk) or [Microsoft Edge](https://microsoftedge.microsoft.com/addons/detail/pdffhmdngciaglkoonimfcmckehcpafo) (requires KeePassXC 2.5.3 or newer).
|
||||
|
||||
Please see this [document](https://keepassxc.org/docs/KeePassXC_GettingStarted.html#_browser_integration) for instructions how to configure KeePassXC in order to connect the database correctly.
|
||||
|
||||
|
|
@ -49,10 +52,6 @@ Check [keepassxc-protocol](keepassxc-protocol.md) for the details about the mess
|
|||
|
||||
Translations are managed on [Transifex](https://explore.transifex.com/keepassxc/keepassxc-browser/) which offers a web interface. Please join an existing language team or request a new one if there is none.
|
||||
|
||||
## Contributing
|
||||
|
||||
You may directly contribute your own code by submitting a pull request. Please read the [CONTRIBUTING](.github/CONTRIBUTING.md) document for further information.
|
||||
|
||||
## Development and testing
|
||||
|
||||
See [wiki](https://github.com/keepassxreboot/keepassxc-browser/wiki/Loading-the-extension-manually).
|
||||
|
|
|
|||
19
dist/manifest_chromium.json
vendored
19
dist/manifest_chromium.json
vendored
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"manifest_version": 3,
|
||||
"name": "KeePassXC-Browser",
|
||||
"version": "1.10.0.1",
|
||||
"version_name": "1.10.0.1",
|
||||
"minimum_chrome_version": "124",
|
||||
"version": "1.9.9.2",
|
||||
"version_name": "1.9.9.2",
|
||||
"minimum_chrome_version": "93",
|
||||
"description": "__MSG_extensionDescription__",
|
||||
"author": "KeePassXC Team",
|
||||
"icons": {
|
||||
|
|
@ -53,8 +53,7 @@
|
|||
"content/fields.js",
|
||||
"content/fill.js",
|
||||
"content/form.js",
|
||||
"content/icon.js",
|
||||
"content/icon-handler.js",
|
||||
"content/icons.js",
|
||||
"content/keepassxc-browser.js",
|
||||
"content/observer-helper.js",
|
||||
"content/pwgen.js",
|
||||
|
|
@ -119,15 +118,12 @@
|
|||
"choose_credential_fields": {
|
||||
"description": "__MSG_popupChooseCredentialsText__"
|
||||
},
|
||||
"retrieve_credentials_forced": {
|
||||
"description": "__MSG_credentialsRetrieveButton__"
|
||||
"retrive_credentials_forced": {
|
||||
"description": "__MSG_popupReopenButton__"
|
||||
},
|
||||
"request_autotype": {
|
||||
"description": "__MSG_contextMenuRequestGlobalAutoType__"
|
||||
},
|
||||
"reopen_database": {
|
||||
"description": "__MSG_popupReopenButton__"
|
||||
},
|
||||
"reload_extension": {
|
||||
"description": "__MSG_popupReloadButton__"
|
||||
}
|
||||
|
|
@ -170,7 +166,8 @@
|
|||
"tabs",
|
||||
"webNavigation",
|
||||
"webRequest",
|
||||
"webRequestAuthProvider"
|
||||
"webRequestAuthProvider",
|
||||
"webRequestBlocking"
|
||||
],
|
||||
"optional_permissions": [
|
||||
"privacy"
|
||||
|
|
|
|||
14
dist/manifest_firefox.json
vendored
14
dist/manifest_firefox.json
vendored
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"manifest_version": 2,
|
||||
"name": "KeePassXC-Browser",
|
||||
"version": "1.10.0.1",
|
||||
"version": "1.9.9.2",
|
||||
"description": "__MSG_extensionDescription__",
|
||||
"author": "KeePassXC Team",
|
||||
"icons": {
|
||||
|
|
@ -66,8 +66,7 @@
|
|||
"content/fields.js",
|
||||
"content/fill.js",
|
||||
"content/form.js",
|
||||
"content/icon.js",
|
||||
"content/icon-handler.js",
|
||||
"content/icons.js",
|
||||
"content/keepassxc-browser.js",
|
||||
"content/observer-helper.js",
|
||||
"content/pwgen.js",
|
||||
|
|
@ -132,15 +131,12 @@
|
|||
"choose_credential_fields": {
|
||||
"description": "__MSG_popupChooseCredentialsText__"
|
||||
},
|
||||
"retrieve_credentials_forced": {
|
||||
"description": "__MSG_credentialsRetrieveButton__"
|
||||
"retrive_credentials_forced": {
|
||||
"description": "__MSG_popupReopenButton__"
|
||||
},
|
||||
"request_autotype": {
|
||||
"description": "__MSG_contextMenuRequestGlobalAutoType__"
|
||||
},
|
||||
"reopen_database": {
|
||||
"description": "__MSG_popupReopenButton__"
|
||||
},
|
||||
"reload_extension": {
|
||||
"description": "__MSG_popupReloadButton__"
|
||||
}
|
||||
|
|
@ -185,7 +181,7 @@
|
|||
"applications": {
|
||||
"gecko": {
|
||||
"id": "keepassxc-browser@keepassxc.org",
|
||||
"strict_min_version": "100.0"
|
||||
"strict_min_version": "96.0"
|
||||
}
|
||||
},
|
||||
"storage": {
|
||||
|
|
|
|||
|
|
@ -1,216 +0,0 @@
|
|||
import { defineConfig, globalIgnores } from "eslint/config";
|
||||
import globals from "globals";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import js from "@eslint/js";
|
||||
import { FlatCompat } from "@eslint/eslintrc";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended,
|
||||
allConfig: js.configs.all
|
||||
});
|
||||
|
||||
export default defineConfig([globalIgnores(["**/*.min.js"]), {
|
||||
extends: compat.extends("eslint:recommended"),
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.jquery,
|
||||
...globals.webextensions,
|
||||
_called: "readonly",
|
||||
acceptedOTPFields: "readonly",
|
||||
assertInputFields: "readonly",
|
||||
assertPasswordChangeFields: "readonly",
|
||||
assertRegex: "readonly",
|
||||
assertSearchField: "readonly",
|
||||
assertSearchForm: "readonly",
|
||||
assertTOTPField: "readonly",
|
||||
AssociatedAction: "readonly",
|
||||
AuthenticatorAssertionResponse: "readonly",
|
||||
AuthenticatorAttestationResponse: "readonly",
|
||||
Autocomplete: "readonly",
|
||||
BannerPosition: "readonly",
|
||||
BLUE_BUTTON: "readonly",
|
||||
bootstrap: "readonly",
|
||||
browser: "readonly",
|
||||
browserAction: "readonly",
|
||||
CHECK_UPDATE_NEVER: "readonly",
|
||||
CHECK_UPDATE_ONE_MONTH: "readonly",
|
||||
CHECK_UPDATE_ONE_WEEK: "readonly",
|
||||
CHECK_UPDATE_THREE_DAYS: "readonly",
|
||||
cloneInto: "readonly",
|
||||
compareVersion: "readonly",
|
||||
createResult: "readonly",
|
||||
createStylesheet: "readonly",
|
||||
DatabaseState: "readonly",
|
||||
debugLogMessage: "readonly",
|
||||
DEFINED_CUSTOM_FIELDS: "readonly",
|
||||
elementsOverlap: "readonly",
|
||||
EXTENSION_NAME: "readonly",
|
||||
getCurrentTab: "readonly",
|
||||
getIconClass: "readonly",
|
||||
getLoginData: "readonly",
|
||||
getTopLevelDomainFromUrl: "readonly",
|
||||
GRAY_BUTTON_CLASS: "readonly",
|
||||
GREEN_BUTTON: "readonly",
|
||||
httpAuth: "readonly",
|
||||
Icon: "readonly",
|
||||
IGNORE_AUTOSUBMIT: "readonly",
|
||||
IGNORE_FULL: "readonly",
|
||||
IGNORE_NORMAL: "readonly",
|
||||
IGNORE_NOTHING: "readonly",
|
||||
IGNORE_PASSKEYS: "readonly",
|
||||
importScripts: "readonly",
|
||||
IMPROVED_DETECTION_PREDEFINED_SITELIST: "readonly",
|
||||
initColorTheme: "readonly",
|
||||
isEdge: "readonly",
|
||||
isElementInside: "readonly",
|
||||
isFirefox: "readonly",
|
||||
isIframeAllowed: "readonly",
|
||||
isSafari: "readonly",
|
||||
keepass: "readonly",
|
||||
keepassClient: "readonly",
|
||||
kpActions: "readonly",
|
||||
kpErrors: "readonly",
|
||||
kpxc: "readonly",
|
||||
kpxcAssert: "readonly",
|
||||
kpxcBanner: "readonly",
|
||||
kpxcCustomLoginFieldsBanner: "readonly",
|
||||
kpxcEvent: "readonly",
|
||||
kpxcFields: "readonly",
|
||||
kpxcFill: "readonly",
|
||||
kpxcForm: "readonly",
|
||||
kpxcIcons: "readonly",
|
||||
kpxcObserverHelper: "readonly",
|
||||
kpxcPasswordGenerator: "readonly",
|
||||
kpxcPasswordIcons: "readonly",
|
||||
kpxcSites: "readonly",
|
||||
kpxcTOTPAutocomplete: "readonly",
|
||||
kpxcTOTPIcons: "readonly",
|
||||
kpxcUI: "readonly",
|
||||
kpxcUserAutocomplete: "readonly",
|
||||
kpxcUsernameIcons: "readonly",
|
||||
logDebug: "readonly",
|
||||
logError: "readonly",
|
||||
kpxcPasskeysUtils: "readonly",
|
||||
ManualFill: "readonly",
|
||||
matchesWithNodeName: "readonly",
|
||||
MAX_AUTOCOMPLETE_NAME_LEN: "readonly",
|
||||
MAX_OPACITY: "readonly",
|
||||
MAX_TOTP_INPUT_LENGTH: "readonly",
|
||||
menuContexts: "readonly",
|
||||
MIN_INPUT_FIELD_OFFSET_WIDTH: "readonly",
|
||||
MIN_INPUT_FIELD_WIDTH_PX: "readonly",
|
||||
MIN_OPACITY: "readonly",
|
||||
MIN_TOTP_INPUT_LENGTH: "readonly",
|
||||
module: "readonly",
|
||||
nacl: "readonly",
|
||||
OBSERVER_OPTIONS: "readonly",
|
||||
ORANGE_BUTTON: "readonly",
|
||||
page: "readonly",
|
||||
Pixels: "readonly",
|
||||
PublicKeyCredential: "readonly",
|
||||
PREDEFINED_SITELIST: "readonly",
|
||||
RED_BUTTON: "readonly",
|
||||
retrieveColorScheme: "readonly",
|
||||
sendMessage: "readonly",
|
||||
showNotification: "readonly",
|
||||
siteMatch: "readonly",
|
||||
SitePreferences: "readonly",
|
||||
slashNeededForUrl: "readonly",
|
||||
SORT_BY_GROUP_AND_TITLE: "readonly",
|
||||
SORT_BY_GROUP_AND_USERNAME: "readonly",
|
||||
SORT_BY_MATCHING_CREDENTIALS_SETTING: "readonly",
|
||||
SORT_BY_RELEVANT_ENTRY: "readonly",
|
||||
SORT_BY_TITLE: "readonly",
|
||||
SORT_BY_USERNAME: "readonly",
|
||||
statusResponse: "readonly",
|
||||
Tests: "readonly",
|
||||
tr: "readonly",
|
||||
trimURL: "readonly",
|
||||
updateDefaultPasswordManager: "readonly",
|
||||
},
|
||||
ecmaVersion: 13,
|
||||
sourceType: "script",
|
||||
},
|
||||
rules: {
|
||||
"array-bracket-spacing": ["error", "always"],
|
||||
"arrow-body-style": "warn",
|
||||
"arrow-parens": "off",
|
||||
"arrow-spacing": "warn",
|
||||
"block-scoped-var": "off",
|
||||
"brace-style": ["warn", "1tbs"],
|
||||
"camelcase": "warn",
|
||||
"class-methods-use-this": "off",
|
||||
"comma-dangle": "off",
|
||||
"computed-property-spacing": ["error", "never"],
|
||||
"consistent-return": "off",
|
||||
"dot-notation": "off",
|
||||
"eqeqeq": "error",
|
||||
"func-names": "off",
|
||||
"function-paren-newline": "off",
|
||||
"guard-for-in": "off",
|
||||
"implicit-arrow-linebreak": "off",
|
||||
"indent": ["error", 4],
|
||||
"linebreak-style": ["error", "unix"],
|
||||
"lines-around-comment": "off",
|
||||
"max-len": "off",
|
||||
"no-alert": "error",
|
||||
"no-await-in-loop": "off",
|
||||
"no-bitwise": "off",
|
||||
"no-console": "off",
|
||||
"no-continue": "off",
|
||||
"no-control-regex": "off",
|
||||
"no-else-return": "off",
|
||||
"no-empty-function": ["error", {
|
||||
"allow": ["arrowFunctions", "methods", "asyncMethods"],
|
||||
}],
|
||||
"no-extend-native": "off",
|
||||
"no-global-assign": "off",
|
||||
"no-lonely-if": "off",
|
||||
"no-loop-func": "off",
|
||||
"no-mixed-operators": "off",
|
||||
"no-mixed-spaces-and-tabs": ["error", "smart-tabs"],
|
||||
"no-multi-spaces": "warn",
|
||||
"no-param-reassign": "off",
|
||||
"no-plusplus": "off",
|
||||
"no-prototype-builtins": "off",
|
||||
"no-redeclare": "off",
|
||||
"no-restricted-globals": "off",
|
||||
"no-restricted-syntax": "off",
|
||||
"no-return-await": "off",
|
||||
"no-tabs": "warn",
|
||||
"no-underscore-dangle": "off",
|
||||
"no-unused-vars": ["warn", {
|
||||
"args": "none",
|
||||
"caughtErrorsIgnorePattern": "^_",
|
||||
"varsIgnorePattern": "[A-Z_]+|tr|[$]",
|
||||
}],
|
||||
"no-use-before-define": "off",
|
||||
"no-useless-escape": "off",
|
||||
"no-useless-return": "off",
|
||||
"no-var": "error",
|
||||
"object-curly-spacing": ["warn", "always"],
|
||||
"object-shorthand": "off",
|
||||
"operator-linebreak": "off",
|
||||
"prefer-arrow-callback": "off",
|
||||
"prefer-const": "warn",
|
||||
"prefer-destructuring": "off",
|
||||
"prefer-template": "off",
|
||||
"quote-props": "off",
|
||||
"quotes": ["error", "single", {
|
||||
"avoidEscape": true,
|
||||
}],
|
||||
"semi": ["error", "always"],
|
||||
"space-before-blocks": "warn",
|
||||
"space-before-function-paren": "off",
|
||||
"space-in-parens": ["error", "never"],
|
||||
"space-unary-ops": "off",
|
||||
"spaced-comment": "off",
|
||||
"strict": "off",
|
||||
"vars-on-top": "off",
|
||||
},
|
||||
}]);
|
||||
|
|
@ -319,13 +319,9 @@
|
|||
"description": "Clear save data button text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineStringField": {
|
||||
"message": "Текстово поле",
|
||||
"message": "Поле за низ",
|
||||
"description": "Text for string field."
|
||||
},
|
||||
"defineSubmitButton": {
|
||||
"message": "Изпращащ бутон",
|
||||
"description": "Text for Submit Button."
|
||||
},
|
||||
"defineChooseUsername": {
|
||||
"message": "Избиране поле за потребителското име",
|
||||
"description": "Choosing a username field text when choosing Custom Login Fields."
|
||||
|
|
@ -342,13 +338,9 @@
|
|||
"message": "Избиране на поле за низ",
|
||||
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineChooseSubmitButton": {
|
||||
"message": "Избиране на изпращащ бутон",
|
||||
"description": "Choose submit button selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineHelpText": {
|
||||
"message": "Потвърдете избора си или изберете повече полета като текстови полета.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
|
||||
"message": "Потвърдете избора си или изберете повече полета като полета за низ.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
|
||||
},
|
||||
"defineKeyboardText": {
|
||||
"message": "Също можете да използвате цифрите, за да избирате полета посредством клавиатурата.",
|
||||
|
|
@ -370,10 +362,6 @@
|
|||
"message": "Полета за низове",
|
||||
"description": "General text for a String Fields."
|
||||
},
|
||||
"submitButton": {
|
||||
"message": "Изпращащ бутон",
|
||||
"description": "Custom Submit Button text."
|
||||
},
|
||||
"credentialsNoUsername": {
|
||||
"message": "- без потребителско име -",
|
||||
"description": "Shown when no username is set in the credentials."
|
||||
|
|
@ -382,10 +370,6 @@
|
|||
"message": "Не са намерени данни за вход.",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsBlockedInIframe": {
|
||||
"message": "Попълването на регистрации е спряно в несигурна рамка.",
|
||||
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "Повече от една регистрация е намерена в KeePassXC!",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
|
|
@ -406,10 +390,6 @@
|
|||
"message": "Изтекъл",
|
||||
"description": "If a credential is expired, this is appended to the title label."
|
||||
},
|
||||
"credentialsRetrieveButton": {
|
||||
"message": "Получаване на регистрации",
|
||||
"description": "Manual retrieve credentials button text."
|
||||
},
|
||||
"fieldsFill": {
|
||||
"message": "Грешка: Не са намерени полета, които да бъдат попълнени.",
|
||||
"description": "Alert message when no fields are found to fill in."
|
||||
|
|
@ -694,10 +674,6 @@
|
|||
"message": "Предпочитания за сайтове",
|
||||
"description": "Site Preferences page header."
|
||||
},
|
||||
"optionsSitePreferencesSettings": {
|
||||
"message": "Настройки",
|
||||
"description": "Site Preferences settings button text."
|
||||
},
|
||||
"optionsMenuAbout": {
|
||||
"message": "Относно",
|
||||
"description": "About page header."
|
||||
|
|
@ -750,10 +726,6 @@
|
|||
"message": "Изнасяне на настройки",
|
||||
"description": "Export settings button text."
|
||||
},
|
||||
"optionsButtonResetSettings": {
|
||||
"message": "Нулиране на настройките",
|
||||
"description": "Reset all settings button text."
|
||||
},
|
||||
"optionsLabelDefaultGroup": {
|
||||
"message": "Група по подразбиране при добавяне на регистрации:",
|
||||
"description": "Default group options text."
|
||||
|
|
@ -1058,10 +1030,6 @@
|
|||
"message": "Изисква KeePassXC издание: $1",
|
||||
"description": "KeePassXC version requirement text."
|
||||
},
|
||||
"optionsMinimumKeePassXCVersionRequired": {
|
||||
"message": "Разширението изисква KeePassXC издание $1. По-ранни издания могат да доведат до нежелано поведение.",
|
||||
"description": "KeePassXC minimum version requirement text."
|
||||
},
|
||||
"optionsDefault": {
|
||||
"message": "По подразбиране: $1",
|
||||
"description": "Default setting text."
|
||||
|
|
@ -1098,10 +1066,6 @@
|
|||
"message": "Текущите настройки ще бъдат презаписани. Наистина ли желаете да бъдат внесени настройките от файла?",
|
||||
"description": "Import settings confirmation dialog help text."
|
||||
},
|
||||
"optionsResetSettingsDialogText": {
|
||||
"message": "Желаете ли настройките да бъдат нулирани до подразбираните стойности?",
|
||||
"description": "Reset all settings dialog text."
|
||||
},
|
||||
"optionsConnectedDatabasesText": {
|
||||
"message": "Хранилища, свързани с KeePassXC-Browser.",
|
||||
"description": "Info text about connected databases."
|
||||
|
|
@ -1146,8 +1110,8 @@
|
|||
"message": "Адрес на страница",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnFeatures": {
|
||||
"message": "Възможности",
|
||||
"optionsColumnIgnore": {
|
||||
"message": "Пренебрегване",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnUsernameOnly": {
|
||||
|
|
@ -1178,10 +1142,6 @@
|
|||
"message": "Изключване на автоматично изпращане",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionPasskeys": {
|
||||
"message": "Изключване на Passkeys",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionFull": {
|
||||
"message": "Изключване на всички функции",
|
||||
"description": "Site Preferences option selection."
|
||||
|
|
|
|||
|
|
@ -370,10 +370,6 @@
|
|||
"message": "Chyba:\nNenalezena žádná uživatelská jména.",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsBlockedInIframe": {
|
||||
"message": "Credential fill blocked in untrusted iframe.",
|
||||
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "Chyba:\nV KeePassXC nalezeno více než jedno uživateské jméno!\nKliknutím na ikonu KeePassXC-Browser zobrazíte další možnosti.",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
|
|
|
|||
|
|
@ -319,13 +319,9 @@
|
|||
"description": "Clear save data button text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineStringField": {
|
||||
"message": "String Field",
|
||||
"message": "Strengfelt",
|
||||
"description": "Text for string field."
|
||||
},
|
||||
"defineSubmitButton": {
|
||||
"message": "Submit Button",
|
||||
"description": "Text for Submit Button."
|
||||
},
|
||||
"defineChooseUsername": {
|
||||
"message": "Vælg et brugernavnsfelt",
|
||||
"description": "Choosing a username field text when choosing Custom Login Fields."
|
||||
|
|
@ -342,13 +338,9 @@
|
|||
"message": "Vælg strengfelter",
|
||||
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineChooseSubmitButton": {
|
||||
"message": "Choose submit button",
|
||||
"description": "Choose submit button selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineHelpText": {
|
||||
"message": "Please confirm your selection or choose more fields as String Fields.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
|
||||
"message": "Bekræft venligst dine valg eller vælge flere felter as strengfelter.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
|
||||
},
|
||||
"defineKeyboardText": {
|
||||
"message": "Du kan også bruge nummeret til at vælge inputfelterne fra tastaturet.",
|
||||
|
|
@ -370,10 +362,6 @@
|
|||
"message": "Strengfelter",
|
||||
"description": "General text for a String Fields."
|
||||
},
|
||||
"submitButton": {
|
||||
"message": "Submit Button",
|
||||
"description": "Custom Submit Button text."
|
||||
},
|
||||
"credentialsNoUsername": {
|
||||
"message": "- intet brugernavn -",
|
||||
"description": "Shown when no username is set in the credentials."
|
||||
|
|
@ -382,10 +370,6 @@
|
|||
"message": "Fandt ingen logins.",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsBlockedInIframe": {
|
||||
"message": "Credential fill blocked in untrusted iframe.",
|
||||
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "Fandt mere end ét login i KeePassXC!",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
|
|
@ -406,10 +390,6 @@
|
|||
"message": "Udløbet",
|
||||
"description": "If a credential is expired, this is appended to the title label."
|
||||
},
|
||||
"credentialsRetrieveButton": {
|
||||
"message": "Retrieve credentials",
|
||||
"description": "Manual retrieve credentials button text."
|
||||
},
|
||||
"fieldsFill": {
|
||||
"message": "Fejl: Kan ikke finde felterne som skal udfyldes.",
|
||||
"description": "Alert message when no fields are found to fill in."
|
||||
|
|
@ -694,10 +674,6 @@
|
|||
"message": "Sideindstillinger",
|
||||
"description": "Site Preferences page header."
|
||||
},
|
||||
"optionsSitePreferencesSettings": {
|
||||
"message": "Settings",
|
||||
"description": "Site Preferences settings button text."
|
||||
},
|
||||
"optionsMenuAbout": {
|
||||
"message": "Om",
|
||||
"description": "About page header."
|
||||
|
|
@ -750,10 +726,6 @@
|
|||
"message": "Eksportér indstillinger",
|
||||
"description": "Export settings button text."
|
||||
},
|
||||
"optionsButtonResetSettings": {
|
||||
"message": "Reset all settings",
|
||||
"description": "Reset all settings button text."
|
||||
},
|
||||
"optionsLabelDefaultGroup": {
|
||||
"message": "Standardgruppe for gemte nye adgangskoder:",
|
||||
"description": "Default group options text."
|
||||
|
|
@ -1058,10 +1030,6 @@
|
|||
"message": "Kræver KeePassXC-version: $1",
|
||||
"description": "KeePassXC version requirement text."
|
||||
},
|
||||
"optionsMinimumKeePassXCVersionRequired": {
|
||||
"message": "The extension requires KeePassXC version: $1. Earlier versions can cause unwanted behavior.",
|
||||
"description": "KeePassXC minimum version requirement text."
|
||||
},
|
||||
"optionsDefault": {
|
||||
"message": "Standard: $1",
|
||||
"description": "Default setting text."
|
||||
|
|
@ -1098,10 +1066,6 @@
|
|||
"message": "De nuværende indstillinger tilsidesættes. Vil du virkelig importere indstillingsfilen?",
|
||||
"description": "Import settings confirmation dialog help text."
|
||||
},
|
||||
"optionsResetSettingsDialogText": {
|
||||
"message": "All settings will be reset to defaults. Are you sure?",
|
||||
"description": "Reset all settings dialog text."
|
||||
},
|
||||
"optionsConnectedDatabasesText": {
|
||||
"message": "Databaser som er forbundet til KeePassXC-Browser.",
|
||||
"description": "Info text about connected databases."
|
||||
|
|
@ -1146,8 +1110,8 @@
|
|||
"message": "Sidens URL",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnFeatures": {
|
||||
"message": "Features",
|
||||
"optionsColumnIgnore": {
|
||||
"message": "Ignorer",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnUsernameOnly": {
|
||||
|
|
@ -1178,10 +1142,6 @@
|
|||
"message": "Slå autoindsendelse fra",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionPasskeys": {
|
||||
"message": "Disable passkeys",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionFull": {
|
||||
"message": "Deaktivér alle funktionaliteter",
|
||||
"description": "Site Preferences option selection."
|
||||
|
|
|
|||
|
|
@ -322,10 +322,6 @@
|
|||
"message": "Zeichenkettenfeld",
|
||||
"description": "Text for string field."
|
||||
},
|
||||
"defineSubmitButton": {
|
||||
"message": "Senden-Schaltfläche",
|
||||
"description": "Text for Submit Button."
|
||||
},
|
||||
"defineChooseUsername": {
|
||||
"message": "Feld für Benutzernamen auswählen",
|
||||
"description": "Choosing a username field text when choosing Custom Login Fields."
|
||||
|
|
@ -342,13 +338,9 @@
|
|||
"message": "Zeichenkettenfelder auswählen",
|
||||
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineChooseSubmitButton": {
|
||||
"message": "Senden-Schaltfläche auswählen",
|
||||
"description": "Choose submit button selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineHelpText": {
|
||||
"message": "Bitte bestätigen Sie Ihre Auswahl oder wählen Sie weitere Felder als Zeichenkettenfelder aus.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
|
||||
"message": "Bitte bestätigen Sie Ihre Auswahl oder wählen Sie weitere Felder als <em>Zeichenkettenfelder</em> aus.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
|
||||
},
|
||||
"defineKeyboardText": {
|
||||
"message": "Sie können die Zahlen auch verwenden, um die Eingabefelder über die Tastatur auszuwählen.",
|
||||
|
|
@ -370,10 +362,6 @@
|
|||
"message": "Zeichenkettenfelder",
|
||||
"description": "General text for a String Fields."
|
||||
},
|
||||
"submitButton": {
|
||||
"message": "Senden-Schaltfläche",
|
||||
"description": "Custom Submit Button text."
|
||||
},
|
||||
"credentialsNoUsername": {
|
||||
"message": "- kein Benutzername -",
|
||||
"description": "Shown when no username is set in the credentials."
|
||||
|
|
@ -382,10 +370,6 @@
|
|||
"message": "Keine Anmeldungen gefunden.",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsBlockedInIframe": {
|
||||
"message": "Ausfüllen der Anmeldedaten in nicht vertrauenswürdigen iframes blockiert.",
|
||||
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "In KeePassXC wurde mehr als eine Anmeldung gefunden!",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
|
|
@ -406,10 +390,6 @@
|
|||
"message": "Abgelaufen",
|
||||
"description": "If a credential is expired, this is appended to the title label."
|
||||
},
|
||||
"credentialsRetrieveButton": {
|
||||
"message": "Anmeldedaten abrufen",
|
||||
"description": "Manual retrieve credentials button text."
|
||||
},
|
||||
"fieldsFill": {
|
||||
"message": "Fehler:\nAuszufüllende Felder können nicht gefunden werden.",
|
||||
"description": "Alert message when no fields are found to fill in."
|
||||
|
|
@ -694,10 +674,6 @@
|
|||
"message": "Seiteneinstellungen",
|
||||
"description": "Site Preferences page header."
|
||||
},
|
||||
"optionsSitePreferencesSettings": {
|
||||
"message": "Einstellungen",
|
||||
"description": "Site Preferences settings button text."
|
||||
},
|
||||
"optionsMenuAbout": {
|
||||
"message": "Über",
|
||||
"description": "About page header."
|
||||
|
|
@ -750,10 +726,6 @@
|
|||
"message": "Einstellungen exportieren",
|
||||
"description": "Export settings button text."
|
||||
},
|
||||
"optionsButtonResetSettings": {
|
||||
"message": "Alle Einstellungen zurücksetzen",
|
||||
"description": "Reset all settings button text."
|
||||
},
|
||||
"optionsLabelDefaultGroup": {
|
||||
"message": "Standardgruppe zum Speichern neuer Passwörter:",
|
||||
"description": "Default group options text."
|
||||
|
|
@ -1058,10 +1030,6 @@
|
|||
"message": "Benötigt KeePassXC-Version: $1",
|
||||
"description": "KeePassXC version requirement text."
|
||||
},
|
||||
"optionsMinimumKeePassXCVersionRequired": {
|
||||
"message": "Die Erweiterung erfordert die KeePassXC-Version: $1. Frühere Versionen können zu unerwünschtem Verhalten führen.",
|
||||
"description": "KeePassXC minimum version requirement text."
|
||||
},
|
||||
"optionsDefault": {
|
||||
"message": "Standard: $1",
|
||||
"description": "Default setting text."
|
||||
|
|
@ -1098,10 +1066,6 @@
|
|||
"message": "Die aktuellen Einstellungen werden überschrieben. Möchten Sie die Einstellungsdatei wirklich importieren?",
|
||||
"description": "Import settings confirmation dialog help text."
|
||||
},
|
||||
"optionsResetSettingsDialogText": {
|
||||
"message": "Alle Einstellungen werden auf die Standardeinstellungen zurückgesetzt. Sind Sie sicher?",
|
||||
"description": "Reset all settings dialog text."
|
||||
},
|
||||
"optionsConnectedDatabasesText": {
|
||||
"message": "Die folgenden KeePassXC-Datenbanken sind mit dem KeePassXC-Browser verbunden.",
|
||||
"description": "Info text about connected databases."
|
||||
|
|
@ -1146,8 +1110,8 @@
|
|||
"message": "Seiten-URL",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnFeatures": {
|
||||
"message": "Funktionen",
|
||||
"optionsColumnIgnore": {
|
||||
"message": "Ignorieren",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnUsernameOnly": {
|
||||
|
|
@ -1178,10 +1142,6 @@
|
|||
"message": "Automatisches Absenden deaktivieren",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionPasskeys": {
|
||||
"message": "Passkeys deaktivieren",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionFull": {
|
||||
"message": "Alle Funktionen deaktivieren",
|
||||
"description": "Site Preferences option selection."
|
||||
|
|
|
|||
|
|
@ -319,13 +319,9 @@
|
|||
"description": "Clear save data button text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineStringField": {
|
||||
"message": "String Field",
|
||||
"message": "Πεδίο String",
|
||||
"description": "Text for string field."
|
||||
},
|
||||
"defineSubmitButton": {
|
||||
"message": "Submit Button",
|
||||
"description": "Text for Submit Button."
|
||||
},
|
||||
"defineChooseUsername": {
|
||||
"message": "Επιλέξτε ένα πεδίο ονόματος χρήστη",
|
||||
"description": "Choosing a username field text when choosing Custom Login Fields."
|
||||
|
|
@ -342,13 +338,9 @@
|
|||
"message": "Επιλέξτε Πεδία Strings",
|
||||
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineChooseSubmitButton": {
|
||||
"message": "Choose submit button",
|
||||
"description": "Choose submit button selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineHelpText": {
|
||||
"message": "Please confirm your selection or choose more fields as String Fields.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
|
||||
"message": "Επιβεβαιώστε την επιλογή σας ή επιλέξτε περισσότερα πεδία ως πεδία String.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
|
||||
},
|
||||
"defineKeyboardText": {
|
||||
"message": "Μπορείτε επίσης να χρησιμοποιήσετε τους αριθμούς για να επιλέξετε τα πεδία εισαγωγής από το πληκτρολόγιο.",
|
||||
|
|
@ -370,10 +362,6 @@
|
|||
"message": "Πεδία Strings",
|
||||
"description": "General text for a String Fields."
|
||||
},
|
||||
"submitButton": {
|
||||
"message": "Submit Button",
|
||||
"description": "Custom Submit Button text."
|
||||
},
|
||||
"credentialsNoUsername": {
|
||||
"message": "- χωρίς όνομα χρήστη -",
|
||||
"description": "Shown when no username is set in the credentials."
|
||||
|
|
@ -382,10 +370,6 @@
|
|||
"message": "Δεν βρέθηκαν συνδέσεις.",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsBlockedInIframe": {
|
||||
"message": "Credential fill blocked in untrusted iframe.",
|
||||
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "Περισσότερες από μία συνδέσεις βρέθηκαν στο KeePassXC!",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
|
|
@ -406,10 +390,6 @@
|
|||
"message": "Έληξε",
|
||||
"description": "If a credential is expired, this is appended to the title label."
|
||||
},
|
||||
"credentialsRetrieveButton": {
|
||||
"message": "Retrieve credentials",
|
||||
"description": "Manual retrieve credentials button text."
|
||||
},
|
||||
"fieldsFill": {
|
||||
"message": "Σφάλμα: Δεν είναι δυνατή η εύρεση πεδίων για συμπλήρωση.",
|
||||
"description": "Alert message when no fields are found to fill in."
|
||||
|
|
@ -694,10 +674,6 @@
|
|||
"message": "Site Preferences",
|
||||
"description": "Site Preferences page header."
|
||||
},
|
||||
"optionsSitePreferencesSettings": {
|
||||
"message": "Settings",
|
||||
"description": "Site Preferences settings button text."
|
||||
},
|
||||
"optionsMenuAbout": {
|
||||
"message": "Σχετικά με",
|
||||
"description": "About page header."
|
||||
|
|
@ -750,10 +726,6 @@
|
|||
"message": "Ρυθμίσεις εξαγωγής",
|
||||
"description": "Export settings button text."
|
||||
},
|
||||
"optionsButtonResetSettings": {
|
||||
"message": "Reset all settings",
|
||||
"description": "Reset all settings button text."
|
||||
},
|
||||
"optionsLabelDefaultGroup": {
|
||||
"message": "Προεπιλεγμένη ομάδα για την αποθήκευση νέων κωδικών πρόσβασης:",
|
||||
"description": "Default group options text."
|
||||
|
|
@ -1058,10 +1030,6 @@
|
|||
"message": "Απαιτείται έκδοση KeePassXC: $1",
|
||||
"description": "KeePassXC version requirement text."
|
||||
},
|
||||
"optionsMinimumKeePassXCVersionRequired": {
|
||||
"message": "The extension requires KeePassXC version: $1. Earlier versions can cause unwanted behavior.",
|
||||
"description": "KeePassXC minimum version requirement text."
|
||||
},
|
||||
"optionsDefault": {
|
||||
"message": "Προεπιλογή: $1",
|
||||
"description": "Default setting text."
|
||||
|
|
@ -1098,10 +1066,6 @@
|
|||
"message": "Οι τρέχουσες ρυθμίσεις θα παρακαμφθούν. Θέλετε πραγματικά να εισαγάγετε το αρχείο ρυθμίσεων;",
|
||||
"description": "Import settings confirmation dialog help text."
|
||||
},
|
||||
"optionsResetSettingsDialogText": {
|
||||
"message": "All settings will be reset to defaults. Are you sure?",
|
||||
"description": "Reset all settings dialog text."
|
||||
},
|
||||
"optionsConnectedDatabasesText": {
|
||||
"message": "Συνδεδεμένες βάσεις δεδομένων με το KeePassXC-Browser.",
|
||||
"description": "Info text about connected databases."
|
||||
|
|
@ -1146,8 +1110,8 @@
|
|||
"message": "Διεύθυνση URL σελίδας",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnFeatures": {
|
||||
"message": "Features",
|
||||
"optionsColumnIgnore": {
|
||||
"message": "Αγνοήστε",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnUsernameOnly": {
|
||||
|
|
@ -1178,10 +1142,6 @@
|
|||
"message": "Απενεργοποίηση Αυτόματης Υποβολής",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionPasskeys": {
|
||||
"message": "Disable passkeys",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionFull": {
|
||||
"message": "Απενεργοποίηση όλων των λειτουργιών",
|
||||
"description": "Site Preferences option selection."
|
||||
|
|
|
|||
|
|
@ -319,13 +319,9 @@
|
|||
"description": "Clear save data button text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineStringField": {
|
||||
"message": "String Field",
|
||||
"message": "String field",
|
||||
"description": "Text for string field."
|
||||
},
|
||||
"defineSubmitButton": {
|
||||
"message": "Submit Button",
|
||||
"description": "Text for Submit Button."
|
||||
},
|
||||
"defineChooseUsername": {
|
||||
"message": "Choose a username field",
|
||||
"description": "Choosing a username field text when choosing Custom Login Fields."
|
||||
|
|
@ -342,13 +338,9 @@
|
|||
"message": "Choose String Fields",
|
||||
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineChooseSubmitButton": {
|
||||
"message": "Choose submit button",
|
||||
"description": "Choose submit button selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineHelpText": {
|
||||
"message": "Please confirm your selection or choose more fields as String Fields.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
|
||||
"message": "Please confirm your selection or choose more fields as String fields.",
|
||||
"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.",
|
||||
|
|
@ -370,10 +362,6 @@
|
|||
"message": "String Fields",
|
||||
"description": "General text for a String Fields."
|
||||
},
|
||||
"submitButton": {
|
||||
"message": "Submit Button",
|
||||
"description": "Custom Submit Button text."
|
||||
},
|
||||
"credentialsNoUsername": {
|
||||
"message": "- no username -",
|
||||
"description": "Shown when no username is set in the credentials."
|
||||
|
|
@ -382,10 +370,6 @@
|
|||
"message": "No logins found.",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsBlockedInIframe": {
|
||||
"message": "Credential fill blocked in untrusted iframe.",
|
||||
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "More than one login was found in KeePassXC!",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
|
|
@ -406,10 +390,6 @@
|
|||
"message": "Expired",
|
||||
"description": "If a credential is expired, this is appended to the title label."
|
||||
},
|
||||
"credentialsRetrieveButton": {
|
||||
"message": "Retrieve credentials",
|
||||
"description": "Manual retrieve credentials button text."
|
||||
},
|
||||
"fieldsFill": {
|
||||
"message": "Error: Cannot find fields to fill in.",
|
||||
"description": "Alert message when no fields are found to fill in."
|
||||
|
|
@ -694,10 +674,6 @@
|
|||
"message": "Site Preferences",
|
||||
"description": "Site Preferences page header."
|
||||
},
|
||||
"optionsSitePreferencesSettings": {
|
||||
"message": "Settings",
|
||||
"description": "Site Preferences settings button text."
|
||||
},
|
||||
"optionsMenuAbout": {
|
||||
"message": "About",
|
||||
"description": "About page header."
|
||||
|
|
@ -750,10 +726,6 @@
|
|||
"message": "Export settings",
|
||||
"description": "Export settings button text."
|
||||
},
|
||||
"optionsButtonResetSettings": {
|
||||
"message": "Reset all settings",
|
||||
"description": "Reset all settings button text."
|
||||
},
|
||||
"optionsLabelDefaultGroup": {
|
||||
"message": "Default group for saving new passwords:",
|
||||
"description": "Default group options text."
|
||||
|
|
@ -1058,10 +1030,6 @@
|
|||
"message": "Requires KeePassXC version: $1",
|
||||
"description": "KeePassXC version requirement text."
|
||||
},
|
||||
"optionsMinimumKeePassXCVersionRequired": {
|
||||
"message": "The extension requires KeePassXC version: $1. Earlier versions can cause unwanted behavior.",
|
||||
"description": "KeePassXC minimum version requirement text."
|
||||
},
|
||||
"optionsDefault": {
|
||||
"message": "Default: $1",
|
||||
"description": "Default setting text."
|
||||
|
|
@ -1098,10 +1066,6 @@
|
|||
"message": "Current settings will be overridden. Do you really want to import the settings file?",
|
||||
"description": "Import settings confirmation dialog help text."
|
||||
},
|
||||
"optionsResetSettingsDialogText": {
|
||||
"message": "All settings will be reset to defaults. Are you sure?",
|
||||
"description": "Reset all settings dialog text."
|
||||
},
|
||||
"optionsConnectedDatabasesText": {
|
||||
"message": "Databases connected to KeePassXC-Browser.",
|
||||
"description": "Info text about connected databases."
|
||||
|
|
@ -1146,8 +1110,8 @@
|
|||
"message": "Page URL",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnFeatures": {
|
||||
"message": "Features",
|
||||
"optionsColumnIgnore": {
|
||||
"message": "Ignore",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnUsernameOnly": {
|
||||
|
|
@ -1178,10 +1142,6 @@
|
|||
"message": "Disable Auto-Submit",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionPasskeys": {
|
||||
"message": "Disable passkeys",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionFull": {
|
||||
"message": "Disable all features",
|
||||
"description": "Site Preferences option selection."
|
||||
|
|
|
|||
|
|
@ -319,13 +319,9 @@
|
|||
"description": "Clear save data button text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineStringField": {
|
||||
"message": "String Field",
|
||||
"message": "String field",
|
||||
"description": "Text for string field."
|
||||
},
|
||||
"defineSubmitButton": {
|
||||
"message": "Submit Button",
|
||||
"description": "Text for Submit Button."
|
||||
},
|
||||
"defineChooseUsername": {
|
||||
"message": "Choose a username field",
|
||||
"description": "Choosing a username field text when choosing Custom Login Fields."
|
||||
|
|
@ -342,13 +338,9 @@
|
|||
"message": "Choose String Fields",
|
||||
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineChooseSubmitButton": {
|
||||
"message": "Choose submit button",
|
||||
"description": "Choose submit button selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineHelpText": {
|
||||
"message": "Please confirm your selection or choose more fields as String Fields.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
|
||||
"message": "Please confirm your selection or choose more fields as String fields.",
|
||||
"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.",
|
||||
|
|
@ -370,10 +362,6 @@
|
|||
"message": "String Fields",
|
||||
"description": "General text for a String Fields."
|
||||
},
|
||||
"submitButton": {
|
||||
"message": "Submit Button",
|
||||
"description": "Custom Submit Button text."
|
||||
},
|
||||
"credentialsNoUsername": {
|
||||
"message": "- no username -",
|
||||
"description": "Shown when no username is set in the credentials."
|
||||
|
|
@ -382,10 +370,6 @@
|
|||
"message": "No logins found.",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsBlockedInIframe": {
|
||||
"message": "Credential fill blocked in untrusted iframe.",
|
||||
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "More than one login was found in KeePassXC!",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
|
|
@ -406,10 +390,6 @@
|
|||
"message": "Expired",
|
||||
"description": "If a credential is expired, this is appended to the title label."
|
||||
},
|
||||
"credentialsRetrieveButton": {
|
||||
"message": "Retrieve credentials",
|
||||
"description": "Manual retrieve credentials button text."
|
||||
},
|
||||
"fieldsFill": {
|
||||
"message": "Error: Cannot find fields to fill in.",
|
||||
"description": "Alert message when no fields are found to fill in."
|
||||
|
|
@ -694,10 +674,6 @@
|
|||
"message": "Site Preferences",
|
||||
"description": "Site Preferences page header."
|
||||
},
|
||||
"optionsSitePreferencesSettings": {
|
||||
"message": "Settings",
|
||||
"description": "Site Preferences settings button text."
|
||||
},
|
||||
"optionsMenuAbout": {
|
||||
"message": "About",
|
||||
"description": "About page header."
|
||||
|
|
@ -750,10 +726,6 @@
|
|||
"message": "Export settings",
|
||||
"description": "Export settings button text."
|
||||
},
|
||||
"optionsButtonResetSettings": {
|
||||
"message": "Reset all settings",
|
||||
"description": "Reset all settings button text."
|
||||
},
|
||||
"optionsLabelDefaultGroup": {
|
||||
"message": "Default group for saving new passwords:",
|
||||
"description": "Default group options text."
|
||||
|
|
@ -1058,10 +1030,6 @@
|
|||
"message": "Requires KeePassXC version: $1",
|
||||
"description": "KeePassXC version requirement text."
|
||||
},
|
||||
"optionsMinimumKeePassXCVersionRequired": {
|
||||
"message": "The extension requires KeePassXC version: $1. Earlier versions can cause unwanted behaviour.",
|
||||
"description": "KeePassXC minimum version requirement text."
|
||||
},
|
||||
"optionsDefault": {
|
||||
"message": "Default: $1",
|
||||
"description": "Default setting text."
|
||||
|
|
@ -1098,10 +1066,6 @@
|
|||
"message": "Current settings will be overridden. Do you really want to import the settings file?",
|
||||
"description": "Import settings confirmation dialog help text."
|
||||
},
|
||||
"optionsResetSettingsDialogText": {
|
||||
"message": "All settings will be reset to defaults. Are you sure?",
|
||||
"description": "Reset all settings dialog text."
|
||||
},
|
||||
"optionsConnectedDatabasesText": {
|
||||
"message": "Databases connected to KeePassXC-Browser.",
|
||||
"description": "Info text about connected databases."
|
||||
|
|
@ -1146,8 +1110,8 @@
|
|||
"message": "Page URL",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnFeatures": {
|
||||
"message": "Features",
|
||||
"optionsColumnIgnore": {
|
||||
"message": "Ignore",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnUsernameOnly": {
|
||||
|
|
@ -1178,10 +1142,6 @@
|
|||
"message": "Disable Auto-Submit",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionPasskeys": {
|
||||
"message": "Disable passkeys",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionFull": {
|
||||
"message": "Disable all features",
|
||||
"description": "Site Preferences option selection."
|
||||
|
|
|
|||
|
|
@ -322,10 +322,6 @@
|
|||
"message": "Campo de cadena",
|
||||
"description": "Text for string field."
|
||||
},
|
||||
"defineSubmitButton": {
|
||||
"message": "Botón Enviar",
|
||||
"description": "Text for Submit Button."
|
||||
},
|
||||
"defineChooseUsername": {
|
||||
"message": "Elija un campo nombre de usuario",
|
||||
"description": "Choosing a username field text when choosing Custom Login Fields."
|
||||
|
|
@ -342,13 +338,9 @@
|
|||
"message": "Elija Campos de Cadena",
|
||||
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineChooseSubmitButton": {
|
||||
"message": "Seleccione el botón enviar",
|
||||
"description": "Choose submit button selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineHelpText": {
|
||||
"message": "Confirme su selección o elija más campos como campos de cadena.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
|
||||
"message": "Confirme su selección o elija más campos como Campos de cadena.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
|
||||
},
|
||||
"defineKeyboardText": {
|
||||
"message": "También puede utilizar los números para elegir los campos de entrada desde el teclado.",
|
||||
|
|
@ -370,10 +362,6 @@
|
|||
"message": "Campos de Cadena",
|
||||
"description": "General text for a String Fields."
|
||||
},
|
||||
"submitButton": {
|
||||
"message": "Botón Enviar",
|
||||
"description": "Custom Submit Button text."
|
||||
},
|
||||
"credentialsNoUsername": {
|
||||
"message": "- sin nombre de usuario -",
|
||||
"description": "Shown when no username is set in the credentials."
|
||||
|
|
@ -382,10 +370,6 @@
|
|||
"message": "No se encontraron inicios de sesión.",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsBlockedInIframe": {
|
||||
"message": "Relleno de credenciales bloqueado en iframe que no es de confianza.",
|
||||
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "¡Se encontró más de un inicio de sesión en KeePassXC!",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
|
|
@ -406,10 +390,6 @@
|
|||
"message": "Caducado",
|
||||
"description": "If a credential is expired, this is appended to the title label."
|
||||
},
|
||||
"credentialsRetrieveButton": {
|
||||
"message": "Retrieve credentials",
|
||||
"description": "Manual retrieve credentials button text."
|
||||
},
|
||||
"fieldsFill": {
|
||||
"message": "Error: No se pueden encontrar campos para rellenar.",
|
||||
"description": "Alert message when no fields are found to fill in."
|
||||
|
|
@ -694,10 +674,6 @@
|
|||
"message": "Preferencias del sitio",
|
||||
"description": "Site Preferences page header."
|
||||
},
|
||||
"optionsSitePreferencesSettings": {
|
||||
"message": "Configuración",
|
||||
"description": "Site Preferences settings button text."
|
||||
},
|
||||
"optionsMenuAbout": {
|
||||
"message": "Acerca de",
|
||||
"description": "About page header."
|
||||
|
|
@ -750,10 +726,6 @@
|
|||
"message": "Exportar configuración",
|
||||
"description": "Export settings button text."
|
||||
},
|
||||
"optionsButtonResetSettings": {
|
||||
"message": "Restablecer todos los ajustes",
|
||||
"description": "Reset all settings button text."
|
||||
},
|
||||
"optionsLabelDefaultGroup": {
|
||||
"message": "Grupo predeterminado para guardar nuevas contraseñas:",
|
||||
"description": "Default group options text."
|
||||
|
|
@ -1058,10 +1030,6 @@
|
|||
"message": "Requiere la versión de KeePassXC: $1",
|
||||
"description": "KeePassXC version requirement text."
|
||||
},
|
||||
"optionsMinimumKeePassXCVersionRequired": {
|
||||
"message": "The extension requires KeePassXC version: $1. Earlier versions can cause unwanted behavior.",
|
||||
"description": "KeePassXC minimum version requirement text."
|
||||
},
|
||||
"optionsDefault": {
|
||||
"message": "Por defecto: $1",
|
||||
"description": "Default setting text."
|
||||
|
|
@ -1098,10 +1066,6 @@
|
|||
"message": "La configuración actual será sobrescrita. ¿Desea importar el archivo de configuración?",
|
||||
"description": "Import settings confirmation dialog help text."
|
||||
},
|
||||
"optionsResetSettingsDialogText": {
|
||||
"message": "Todos los ajustes se restablecerán a los valores predeterminados. ¿Está seguro?",
|
||||
"description": "Reset all settings dialog text."
|
||||
},
|
||||
"optionsConnectedDatabasesText": {
|
||||
"message": "Las siguientes bases de datos KeePassXC están conectadas a KeePassXC-Browser.",
|
||||
"description": "Info text about connected databases."
|
||||
|
|
@ -1146,8 +1110,8 @@
|
|||
"message": "URL de la página",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnFeatures": {
|
||||
"message": "Características",
|
||||
"optionsColumnIgnore": {
|
||||
"message": "Ignorar",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnUsernameOnly": {
|
||||
|
|
@ -1178,10 +1142,6 @@
|
|||
"message": "Deshabilitar autoenvío",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionPasskeys": {
|
||||
"message": "Deshabilitar claves de acceso",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionFull": {
|
||||
"message": "Desactivar todas las características",
|
||||
"description": "Site Preferences option selection."
|
||||
|
|
|
|||
|
|
@ -319,13 +319,9 @@
|
|||
"description": "Clear save data button text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineStringField": {
|
||||
"message": "Lisämerkkijonokenttä",
|
||||
"message": "Lisämerkkijonokenttä #",
|
||||
"description": "Text for string field."
|
||||
},
|
||||
"defineSubmitButton": {
|
||||
"message": "Lähetä-nappi",
|
||||
"description": "Text for Submit Button."
|
||||
},
|
||||
"defineChooseUsername": {
|
||||
"message": "Valitse käyttäjätunnuksen kenttä",
|
||||
"description": "Choosing a username field text when choosing Custom Login Fields."
|
||||
|
|
@ -342,13 +338,9 @@
|
|||
"message": "Valitse lisämerkkijonokentät",
|
||||
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineChooseSubmitButton": {
|
||||
"message": "Valitse lähetä-nappi",
|
||||
"description": "Choose submit button selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineHelpText": {
|
||||
"message": "Vahvista valintasi tai merkitse lisää kenttiä lisämerkkijonokentiksi.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
|
||||
"message": "Vahvista valintasi tai merkitse lisää kenttiä <em>lisämerkkijonokentiksi</em>.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
|
||||
},
|
||||
"defineKeyboardText": {
|
||||
"message": "Voit myös valita kenttiä käyttämällä näppäimistön numeroita.",
|
||||
|
|
@ -370,10 +362,6 @@
|
|||
"message": "Lisämerkkijonokentät",
|
||||
"description": "General text for a String Fields."
|
||||
},
|
||||
"submitButton": {
|
||||
"message": "Lähetä-nappi",
|
||||
"description": "Custom Submit Button text."
|
||||
},
|
||||
"credentialsNoUsername": {
|
||||
"message": "- ei käyttäjätunnusta -",
|
||||
"description": "Shown when no username is set in the credentials."
|
||||
|
|
@ -382,10 +370,6 @@
|
|||
"message": "Virhe:\nTunnuksia ei löydy.",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsBlockedInIframe": {
|
||||
"message": "Tietueiden täyttö estettiin epäluotettavaan iframe-upotukseen.",
|
||||
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "Virhe:\nKeePassXC löysi useampia tilitietoja!\nPaina selainlaajennuksen ikonia nähdäksesi lisävalinnat.",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
|
|
@ -406,10 +390,6 @@
|
|||
"message": "Vanhentunut",
|
||||
"description": "If a credential is expired, this is appended to the title label."
|
||||
},
|
||||
"credentialsRetrieveButton": {
|
||||
"message": "Hae tietueet",
|
||||
"description": "Manual retrieve credentials button text."
|
||||
},
|
||||
"fieldsFill": {
|
||||
"message": "Virhe:\nTäytön tarvitsemia kenttiä ei löydy.",
|
||||
"description": "Alert message when no fields are found to fill in."
|
||||
|
|
@ -694,10 +674,6 @@
|
|||
"message": "Sivustokohtaiset asetukset",
|
||||
"description": "Site Preferences page header."
|
||||
},
|
||||
"optionsSitePreferencesSettings": {
|
||||
"message": "Asetukset",
|
||||
"description": "Site Preferences settings button text."
|
||||
},
|
||||
"optionsMenuAbout": {
|
||||
"message": "Tietoja",
|
||||
"description": "About page header."
|
||||
|
|
@ -750,10 +726,6 @@
|
|||
"message": "Vie asetukset",
|
||||
"description": "Export settings button text."
|
||||
},
|
||||
"optionsButtonResetSettings": {
|
||||
"message": "Palauta kaikki asetukset oletuksiin",
|
||||
"description": "Reset all settings button text."
|
||||
},
|
||||
"optionsLabelDefaultGroup": {
|
||||
"message": "Oletusryhmä johon uudet salasanat tallennetaan:",
|
||||
"description": "Default group options text."
|
||||
|
|
@ -1058,10 +1030,6 @@
|
|||
"message": "Vaatii KeePassXC:n version: $1",
|
||||
"description": "KeePassXC version requirement text."
|
||||
},
|
||||
"optionsMinimumKeePassXCVersionRequired": {
|
||||
"message": "Tämä selainlaajennus vaatii KeePassXC-version: $1. Aiemmat versiot voivat aiheuttaa epätoivottua käytöstä.",
|
||||
"description": "KeePassXC minimum version requirement text."
|
||||
},
|
||||
"optionsDefault": {
|
||||
"message": "Oletus: $1",
|
||||
"description": "Default setting text."
|
||||
|
|
@ -1098,10 +1066,6 @@
|
|||
"message": "Nykyiset asetukset ylikirjoitetaan. Oletko varma, että haluat tuoda asetustiedoston?",
|
||||
"description": "Import settings confirmation dialog help text."
|
||||
},
|
||||
"optionsResetSettingsDialogText": {
|
||||
"message": "Kaikki asetukset palautetaan oletuksiin. Oletko varma?",
|
||||
"description": "Reset all settings dialog text."
|
||||
},
|
||||
"optionsConnectedDatabasesText": {
|
||||
"message": "Seuraavat KeePassXC:n tietokannat ovat yhdistettynä selainlaajennukseen.",
|
||||
"description": "Info text about connected databases."
|
||||
|
|
@ -1146,8 +1110,8 @@
|
|||
"message": "Sivun URL",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnFeatures": {
|
||||
"message": "Ominaisuudet",
|
||||
"optionsColumnIgnore": {
|
||||
"message": "Älä huomioi",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnUsernameOnly": {
|
||||
|
|
@ -1178,10 +1142,6 @@
|
|||
"message": "Automattisyöttö pois päältä",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionPasskeys": {
|
||||
"message": "Poista pääsyavaimet käytöstä",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionFull": {
|
||||
"message": "Poista kaikki ominaisuudet käytöstä",
|
||||
"description": "Site Preferences option selection."
|
||||
|
|
|
|||
|
|
@ -319,13 +319,9 @@
|
|||
"description": "Clear save data button text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineStringField": {
|
||||
"message": "Champ de chaînes de caractères",
|
||||
"message": "Champ de chaîne de caractères",
|
||||
"description": "Text for string field."
|
||||
},
|
||||
"defineSubmitButton": {
|
||||
"message": "Bouton d’envoi",
|
||||
"description": "Text for Submit Button."
|
||||
},
|
||||
"defineChooseUsername": {
|
||||
"message": "Choisissez un champ de nom d’utilisateur",
|
||||
"description": "Choosing a username field text when choosing Custom Login Fields."
|
||||
|
|
@ -342,13 +338,9 @@
|
|||
"message": "Choisissez des champs de chaînes de caractères",
|
||||
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineChooseSubmitButton": {
|
||||
"message": "Choisir le bouton d’envoi",
|
||||
"description": "Choose submit button selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineHelpText": {
|
||||
"message": "Confirmez votre choix ou choisissez d’autres champs comme champs de chaînes de caractères.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
|
||||
},
|
||||
"defineKeyboardText": {
|
||||
"message": "Vous pouvez aussi utiliser les chiffres pour choisir les champs d’entrée avec le clavier.",
|
||||
|
|
@ -370,10 +362,6 @@
|
|||
"message": "Champs de type chaîne",
|
||||
"description": "General text for a String Fields."
|
||||
},
|
||||
"submitButton": {
|
||||
"message": "Bouton d’envoi",
|
||||
"description": "Custom Submit Button text."
|
||||
},
|
||||
"credentialsNoUsername": {
|
||||
"message": "– aucun nom d’utilisateur –",
|
||||
"description": "Shown when no username is set in the credentials."
|
||||
|
|
@ -382,10 +370,6 @@
|
|||
"message": "Aucun identifiant n’a été trouvé.",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsBlockedInIframe": {
|
||||
"message": "La saisie des identifiants a été bloquée par un cadre intégré non fiable",
|
||||
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "Plus d’un identifiant a été trouvé dans KeePassXC",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
|
|
@ -406,10 +390,6 @@
|
|||
"message": "est expiré",
|
||||
"description": "If a credential is expired, this is appended to the title label."
|
||||
},
|
||||
"credentialsRetrieveButton": {
|
||||
"message": "Obtenir les identifiants.",
|
||||
"description": "Manual retrieve credentials button text."
|
||||
},
|
||||
"fieldsFill": {
|
||||
"message": "Erreur : Impossible de trouver des champs à remplir.",
|
||||
"description": "Alert message when no fields are found to fill in."
|
||||
|
|
@ -694,10 +674,6 @@
|
|||
"message": "Préférences des sites",
|
||||
"description": "Site Preferences page header."
|
||||
},
|
||||
"optionsSitePreferencesSettings": {
|
||||
"message": "Paramètres",
|
||||
"description": "Site Preferences settings button text."
|
||||
},
|
||||
"optionsMenuAbout": {
|
||||
"message": "À propos",
|
||||
"description": "About page header."
|
||||
|
|
@ -750,10 +726,6 @@
|
|||
"message": "Exporter les paramètres",
|
||||
"description": "Export settings button text."
|
||||
},
|
||||
"optionsButtonResetSettings": {
|
||||
"message": "Réinitialiser tous les paramètres",
|
||||
"description": "Reset all settings button text."
|
||||
},
|
||||
"optionsLabelDefaultGroup": {
|
||||
"message": "Groupe par défaut pour l’enregistrement des nouveaux mots de passe :",
|
||||
"description": "Default group options text."
|
||||
|
|
@ -1058,10 +1030,6 @@
|
|||
"message": "Exige KeePassXC version : $1",
|
||||
"description": "KeePassXC version requirement text."
|
||||
},
|
||||
"optionsMinimumKeePassXCVersionRequired": {
|
||||
"message": "L’extension nécessite la version KeePassXC : $1. Les versions antérieures peuvent entraîner des comportements indésirables.",
|
||||
"description": "KeePassXC minimum version requirement text."
|
||||
},
|
||||
"optionsDefault": {
|
||||
"message": "Par défaut : $1",
|
||||
"description": "Default setting text."
|
||||
|
|
@ -1098,10 +1066,6 @@
|
|||
"message": "Les paramètres actuels seront remplacés. Voulez-vous importer le fichier de paramètres ?",
|
||||
"description": "Import settings confirmation dialog help text."
|
||||
},
|
||||
"optionsResetSettingsDialogText": {
|
||||
"message": "Tous les paramètres seront réinitialisés aux valeurs par défaut. Confirmez-vous ?",
|
||||
"description": "Reset all settings dialog text."
|
||||
},
|
||||
"optionsConnectedDatabasesText": {
|
||||
"message": "Bases de données KeePassXC connectées à KeePassXC-Browser.",
|
||||
"description": "Info text about connected databases."
|
||||
|
|
@ -1146,8 +1110,8 @@
|
|||
"message": "URL de la page",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnFeatures": {
|
||||
"message": "Fonctions",
|
||||
"optionsColumnIgnore": {
|
||||
"message": "Ignorer",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnUsernameOnly": {
|
||||
|
|
@ -1178,10 +1142,6 @@
|
|||
"message": "Désactiver l’envoi automatique",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionPasskeys": {
|
||||
"message": "Désactiver les clés d’accès",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionFull": {
|
||||
"message": "Désactiver toutes les fonctions",
|
||||
"description": "Site Preferences option selection."
|
||||
|
|
|
|||
|
|
@ -319,13 +319,9 @@
|
|||
"description": "Clear save data button text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineStringField": {
|
||||
"message": "String Field",
|
||||
"message": "שדה מחרוזת #",
|
||||
"description": "Text for string field."
|
||||
},
|
||||
"defineSubmitButton": {
|
||||
"message": "Submit Button",
|
||||
"description": "Text for Submit Button."
|
||||
},
|
||||
"defineChooseUsername": {
|
||||
"message": "נא לבחור שדה שם משתמש",
|
||||
"description": "Choosing a username field text when choosing Custom Login Fields."
|
||||
|
|
@ -342,13 +338,9 @@
|
|||
"message": "נא לבחור שדה מחרוזת",
|
||||
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineChooseSubmitButton": {
|
||||
"message": "Choose submit button",
|
||||
"description": "Choose submit button selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineHelpText": {
|
||||
"message": "Please confirm your selection or choose more fields as String Fields.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
|
||||
"message": "נא לאשר את הבחירה או לבחור שדות נוספים כשדות מחרוזת.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
|
||||
},
|
||||
"defineKeyboardText": {
|
||||
"message": "ניתן להשתמש גם במספרים לבחירת שדות הקלט מלוח המקשים.",
|
||||
|
|
@ -370,10 +362,6 @@
|
|||
"message": "שדות מחרוזת",
|
||||
"description": "General text for a String Fields."
|
||||
},
|
||||
"submitButton": {
|
||||
"message": "Submit Button",
|
||||
"description": "Custom Submit Button text."
|
||||
},
|
||||
"credentialsNoUsername": {
|
||||
"message": "- אין שם משתמש -",
|
||||
"description": "Shown when no username is set in the credentials."
|
||||
|
|
@ -382,10 +370,6 @@
|
|||
"message": "לא נמצאו התחברויות.",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsBlockedInIframe": {
|
||||
"message": "Credential fill blocked in untrusted iframe.",
|
||||
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "נמצאה יותר מהתחברות אחת ל־KeePassXC!",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
|
|
@ -406,10 +390,6 @@
|
|||
"message": "תפוגה",
|
||||
"description": "If a credential is expired, this is appended to the title label."
|
||||
},
|
||||
"credentialsRetrieveButton": {
|
||||
"message": "Retrieve credentials",
|
||||
"description": "Manual retrieve credentials button text."
|
||||
},
|
||||
"fieldsFill": {
|
||||
"message": "שגיאה: לא ניתן למצוא שדות למילוי.",
|
||||
"description": "Alert message when no fields are found to fill in."
|
||||
|
|
@ -694,10 +674,6 @@
|
|||
"message": "העדפות אתר",
|
||||
"description": "Site Preferences page header."
|
||||
},
|
||||
"optionsSitePreferencesSettings": {
|
||||
"message": "Settings",
|
||||
"description": "Site Preferences settings button text."
|
||||
},
|
||||
"optionsMenuAbout": {
|
||||
"message": "אודות",
|
||||
"description": "About page header."
|
||||
|
|
@ -750,10 +726,6 @@
|
|||
"message": "ייצוא הגדרות",
|
||||
"description": "Export settings button text."
|
||||
},
|
||||
"optionsButtonResetSettings": {
|
||||
"message": "Reset all settings",
|
||||
"description": "Reset all settings button text."
|
||||
},
|
||||
"optionsLabelDefaultGroup": {
|
||||
"message": "קבוצת ברירת מחדל לשמירת ססמאות חדשות:",
|
||||
"description": "Default group options text."
|
||||
|
|
@ -1058,10 +1030,6 @@
|
|||
"message": "נדרשת גרסת KeePassXC: $1",
|
||||
"description": "KeePassXC version requirement text."
|
||||
},
|
||||
"optionsMinimumKeePassXCVersionRequired": {
|
||||
"message": "The extension requires KeePassXC version: $1. Earlier versions can cause unwanted behavior.",
|
||||
"description": "KeePassXC minimum version requirement text."
|
||||
},
|
||||
"optionsDefault": {
|
||||
"message": "ברירת מחדל: $1",
|
||||
"description": "Default setting text."
|
||||
|
|
@ -1098,10 +1066,6 @@
|
|||
"message": "ההגדרות הנוכחיות יידרסו. האם לייבא את קובץ ההגדרות?",
|
||||
"description": "Import settings confirmation dialog help text."
|
||||
},
|
||||
"optionsResetSettingsDialogText": {
|
||||
"message": "All settings will be reset to defaults. Are you sure?",
|
||||
"description": "Reset all settings dialog text."
|
||||
},
|
||||
"optionsConnectedDatabasesText": {
|
||||
"message": "מסדי נתונים מחוברים לדפדפן־KeePassXC.",
|
||||
"description": "Info text about connected databases."
|
||||
|
|
@ -1146,8 +1110,8 @@
|
|||
"message": "מען URL עמוד",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnFeatures": {
|
||||
"message": "Features",
|
||||
"optionsColumnIgnore": {
|
||||
"message": "להתעלם",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnUsernameOnly": {
|
||||
|
|
@ -1178,10 +1142,6 @@
|
|||
"message": "השבתת הגשה אוטומטית",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionPasskeys": {
|
||||
"message": "Disable passkeys",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionFull": {
|
||||
"message": "השבתת כל התכונות",
|
||||
"description": "Site Preferences option selection."
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
"description": "Add button title text on Site Preferences tab."
|
||||
},
|
||||
"editSitePreferenceButtonTitle": {
|
||||
"message": "URL-cím szerkesztése ezen webhely beállításai számára.",
|
||||
"message": "Edit URL for this Site Preferences.",
|
||||
"description": "Edit button title text on Site Preferences tab."
|
||||
},
|
||||
"allowIframeButtonTitle": {
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
"description": "Connect button title text."
|
||||
},
|
||||
"copyDebugInfoButtonTitle": {
|
||||
"message": "Hibakeresési információk másolása a vágólapra",
|
||||
"message": "Copy debug info to clipboard",
|
||||
"description": "Copy debug info button title text on About tab."
|
||||
},
|
||||
"dismissHttpAuthButtonTitle": {
|
||||
|
|
@ -322,10 +322,6 @@
|
|||
"message": "Karakterláncmező",
|
||||
"description": "Text for string field."
|
||||
},
|
||||
"defineSubmitButton": {
|
||||
"message": "Beküldés gomb",
|
||||
"description": "Text for Submit Button."
|
||||
},
|
||||
"defineChooseUsername": {
|
||||
"message": "Válasszon egy felhasználónév-mezőt",
|
||||
"description": "Choosing a username field text when choosing Custom Login Fields."
|
||||
|
|
@ -342,13 +338,9 @@
|
|||
"message": "Válasszon egy karakterláncmezőt",
|
||||
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineChooseSubmitButton": {
|
||||
"message": "Beküldés gomb kijelölése",
|
||||
"description": "Choose submit button selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineHelpText": {
|
||||
"message": "Erősítse meg a választását vagy válasszon több mezőt <em>karakterlánc mezőkként</em>.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
|
||||
},
|
||||
"defineKeyboardText": {
|
||||
"message": "Használhatja a billentyűzeten lévő számokat is a beviteli mezők kiválasztásához.",
|
||||
|
|
@ -370,10 +362,6 @@
|
|||
"message": "Karakterláncmezők",
|
||||
"description": "General text for a String Fields."
|
||||
},
|
||||
"submitButton": {
|
||||
"message": "Beküldés gomb",
|
||||
"description": "Custom Submit Button text."
|
||||
},
|
||||
"credentialsNoUsername": {
|
||||
"message": "– nincs felhasználónév –",
|
||||
"description": "Shown when no username is set in the credentials."
|
||||
|
|
@ -382,10 +370,6 @@
|
|||
"message": "Hiba:\nNem találhatók bejelentkezések.",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsBlockedInIframe": {
|
||||
"message": "A hitelesítő adatok kitöltése blokkolása a nem megbízható iframe-ben.",
|
||||
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "Hiba:\nTöbb mint egy bejelentkezés található a KeePassXC programban!\nNyomja meg a KeePassXC-böngésző ikont a több lehetőségért.",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
|
|
@ -406,10 +390,6 @@
|
|||
"message": "Lejárt",
|
||||
"description": "If a credential is expired, this is appended to the title label."
|
||||
},
|
||||
"credentialsRetrieveButton": {
|
||||
"message": "Hitelesítő adatok lekérése",
|
||||
"description": "Manual retrieve credentials button text."
|
||||
},
|
||||
"fieldsFill": {
|
||||
"message": "Hiba:\nNem találhatók kitöltendő mezők.",
|
||||
"description": "Alert message when no fields are found to fill in."
|
||||
|
|
@ -647,11 +627,11 @@
|
|||
"description": "Dismiss button text when in HTTP Authentication popup."
|
||||
},
|
||||
"optionsDefaultSettingsTitle": {
|
||||
"message": "Alapértelmezett beállítások",
|
||||
"message": "Default settings",
|
||||
"description": "Default settings title for Getting Started page."
|
||||
},
|
||||
"optionsWelcomeTitle": {
|
||||
"message": "KeePassXC-böngésző",
|
||||
"message": "KeePassXC-Browser",
|
||||
"description": "Main card title for Getting Started page."
|
||||
},
|
||||
"optionsTitle": {
|
||||
|
|
@ -694,10 +674,6 @@
|
|||
"message": "Oldalbeállítások",
|
||||
"description": "Site Preferences page header."
|
||||
},
|
||||
"optionsSitePreferencesSettings": {
|
||||
"message": "Beállítások",
|
||||
"description": "Site Preferences settings button text."
|
||||
},
|
||||
"optionsMenuAbout": {
|
||||
"message": "Névjegy",
|
||||
"description": "About page header."
|
||||
|
|
@ -723,7 +699,7 @@
|
|||
"description": "Add button text."
|
||||
},
|
||||
"optionsButtonEdit": {
|
||||
"message": "Szerkesztés",
|
||||
"message": "Edit",
|
||||
"description": "Edit button text."
|
||||
},
|
||||
"optionsButtonUpdate": {
|
||||
|
|
@ -750,10 +726,6 @@
|
|||
"message": "Beállítások exportálása",
|
||||
"description": "Export settings button text."
|
||||
},
|
||||
"optionsButtonResetSettings": {
|
||||
"message": "Minden beállítás visszaállítása",
|
||||
"description": "Reset all settings button text."
|
||||
},
|
||||
"optionsLabelDefaultGroup": {
|
||||
"message": "Alapértelmezett csoport az új jelszavak mentéséhez:",
|
||||
"description": "Default group options text."
|
||||
|
|
@ -811,7 +783,7 @@
|
|||
"description": "Automatically fill-in single credential entry checkbox text."
|
||||
},
|
||||
"optionsCheckboxAutoFillRelevantCredential": {
|
||||
"message": "Releváns hitelesítésiadat-bejegyzések automatikus kitöltése",
|
||||
"message": "Automatically fill in relevant credential entries",
|
||||
"description": "Automatically fill-in relevant credential entry checkbox text."
|
||||
},
|
||||
"optionsCheckboxAutoFillSingleTotp": {
|
||||
|
|
@ -855,11 +827,11 @@
|
|||
"description": "Show login notifications checkbox text."
|
||||
},
|
||||
"optionsDefaultPasswordManager": {
|
||||
"message": "Beállítás alapértelmezett jelszókezelőnek.",
|
||||
"message": "Set as default password manager",
|
||||
"description": "Default password manager checkbox text."
|
||||
},
|
||||
"optionsDefaultPasswordManagerHelpText": {
|
||||
"message": "A KeePassXC-böngészőt beállítja alapértelmezett jelszókezelőnek a böngészőben.",
|
||||
"message": "Sets KeePassXC-Browser as the default password manager for the browser.",
|
||||
"description": "Default password manager help text."
|
||||
},
|
||||
"optionsRedirectAllowance": {
|
||||
|
|
@ -959,7 +931,7 @@
|
|||
"description": "OTP field icon option help text."
|
||||
},
|
||||
"optionsHideIconsAlertText": {
|
||||
"message": "Szükség esetén az ikonok manuálisan is elrejthetők Shift+kattintással.",
|
||||
"message": "You can also hide icons manually when needed with shift+click.",
|
||||
"description": "Hide icons alert text text."
|
||||
},
|
||||
"optionsAutoRetrieveCredentialsHelpText": {
|
||||
|
|
@ -975,7 +947,7 @@
|
|||
"description": "Auto-Fill Single Entry option help text."
|
||||
},
|
||||
"optionsAutoFillRelevantCredentialHelpText": {
|
||||
"message": "Releváns hitelesítésiadat-bejegyzések automatikus kitöltése ha egy második kitöltés is szükséges.",
|
||||
"message": "Automatically fill in the relevant credential if a second fill is needed.",
|
||||
"description": "Auto-Fill Relevant Credential option help text."
|
||||
},
|
||||
"optionsAutoFillSingleEntryWarning": {
|
||||
|
|
@ -1058,10 +1030,6 @@
|
|||
"message": "Szükséges KeePassXC verzió: $1",
|
||||
"description": "KeePassXC version requirement text."
|
||||
},
|
||||
"optionsMinimumKeePassXCVersionRequired": {
|
||||
"message": "A kiterjesztéshez a KeePassXC ezen verziója szükséges: $1. A korábbi verziók nem kívánt viselkedést okozhatnak.",
|
||||
"description": "KeePassXC minimum version requirement text."
|
||||
},
|
||||
"optionsDefault": {
|
||||
"message": "Alapértelmezett: $1",
|
||||
"description": "Default setting text."
|
||||
|
|
@ -1098,10 +1066,6 @@
|
|||
"message": "A jelenlegi beállítások felül lesznek írva. Biztos, hogy importálja a beállításfájlt?",
|
||||
"description": "Import settings confirmation dialog help text."
|
||||
},
|
||||
"optionsResetSettingsDialogText": {
|
||||
"message": "Minden beállítás alapértelmezett értékre áll vissza. Valóban ezt szeretné?",
|
||||
"description": "Reset all settings dialog text."
|
||||
},
|
||||
"optionsConnectedDatabasesText": {
|
||||
"message": "A következő KeePassXC adatbázisok vannak kapcsolódva a KeePassXC-böngészőhöz.",
|
||||
"description": "Info text about connected databases."
|
||||
|
|
@ -1146,8 +1110,8 @@
|
|||
"message": "Oldal URL",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnFeatures": {
|
||||
"message": "Képességek",
|
||||
"optionsColumnIgnore": {
|
||||
"message": "Mellőzés",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnUsernameOnly": {
|
||||
|
|
@ -1178,10 +1142,6 @@
|
|||
"message": "Automatikus beküldés letiltása",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionPasskeys": {
|
||||
"message": "Jelkulcsok letiltása",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionFull": {
|
||||
"message": "Az összes funkció letiltása",
|
||||
"description": "Site Preferences option selection."
|
||||
|
|
@ -1435,31 +1395,31 @@
|
|||
"description": "Lock database button title text."
|
||||
},
|
||||
"welcomeText": {
|
||||
"message": "Üdvözöljük a KeePassXC-böngészőben!",
|
||||
"message": "Welcome to KeePassXC-Browser!",
|
||||
"description": "Main title of Getting Started page."
|
||||
},
|
||||
"documentationGettingStarted": {
|
||||
"message": "A bevezető útmutatónk segítségével gyorsan elsajátíthatja a használatot.",
|
||||
"message": "Our Getting Started Guide will get you up and running quickly.",
|
||||
"description": "Getting Started document text."
|
||||
},
|
||||
"documentationUserGuide": {
|
||||
"message": "Átfogóbb dokumentációra lenne szüksége? A felhasználói útmutatónk segíthet.",
|
||||
"message": "Looking for more comprehensive documentation? Our User Guide is there to help.",
|
||||
"description": "User Guide text."
|
||||
},
|
||||
"documentationTroubleshootingGuide": {
|
||||
"message": "Segítségre van szüksége a böngésző integrációjának hibaelhárításában? Olvassa el a hibaelhárítási útmutatót.",
|
||||
"message": "Need help troubleshooting the browser integration? Check the Troubleshooting Guide.",
|
||||
"description": "Troubleshooting Guide text."
|
||||
},
|
||||
"gettingStartedWelcomeText": {
|
||||
"message": "Üdvözlet a KeePassXC-böngészőben, a KeePassXC hivatalos böngészőbővítményében.",
|
||||
"message": "Welcome to KeePassXC-Browser, the official browser extension for KeePassXC.",
|
||||
"description": "Welcome to KeePassXC-Browser, the official browser extension for KeePassXC."
|
||||
},
|
||||
"gettingStartedSecondWelcomeText": {
|
||||
"message": "Kérjük, nézze át az alapértelmezett beállításokat és ellenőrizze az összes kívánt opciót.",
|
||||
"message": "Please go through the default settings and check all your preferred options.",
|
||||
"description": "Please go through the default settings and check all your preferred options."
|
||||
},
|
||||
"gettingStartedNewUser": {
|
||||
"message": "Új felhasználó? Nézze meg a hivatkozott dokumentációt!",
|
||||
"message": "Are you a new user? Check links to our documentation.",
|
||||
"description": "Are you a new user? Check links to our documentation."
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -319,13 +319,9 @@
|
|||
"description": "Clear save data button text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineStringField": {
|
||||
"message": "String Field",
|
||||
"message": "Ruas lema",
|
||||
"description": "Text for string field."
|
||||
},
|
||||
"defineSubmitButton": {
|
||||
"message": "Submit Button",
|
||||
"description": "Text for Submit Button."
|
||||
},
|
||||
"defineChooseUsername": {
|
||||
"message": "Pilih ruas nama pengguna",
|
||||
"description": "Choosing a username field text when choosing Custom Login Fields."
|
||||
|
|
@ -342,13 +338,9 @@
|
|||
"message": "Pilih Ruas String",
|
||||
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineChooseSubmitButton": {
|
||||
"message": "Choose submit button",
|
||||
"description": "Choose submit button selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineHelpText": {
|
||||
"message": "Please confirm your selection or choose more fields as String Fields.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
|
||||
"message": "Harap konfirmasi pilihan Anda atau pilih lebih banyak bidang sebagai bidang String.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
|
||||
},
|
||||
"defineKeyboardText": {
|
||||
"message": "Anda juga dapat menggunakan angka untuk memilih bidang input dari keyboard.",
|
||||
|
|
@ -370,10 +362,6 @@
|
|||
"message": "Ruas String",
|
||||
"description": "General text for a String Fields."
|
||||
},
|
||||
"submitButton": {
|
||||
"message": "Submit Button",
|
||||
"description": "Custom Submit Button text."
|
||||
},
|
||||
"credentialsNoUsername": {
|
||||
"message": "- tidak ada nama pengguna -",
|
||||
"description": "Shown when no username is set in the credentials."
|
||||
|
|
@ -382,10 +370,6 @@
|
|||
"message": "Login tidak ditemukan.",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsBlockedInIframe": {
|
||||
"message": "Credential fill blocked in untrusted iframe.",
|
||||
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "Lebih dari satu login ditemukan di KeePassXC!",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
|
|
@ -406,10 +390,6 @@
|
|||
"message": "Kedaluwarsa",
|
||||
"description": "If a credential is expired, this is appended to the title label."
|
||||
},
|
||||
"credentialsRetrieveButton": {
|
||||
"message": "Retrieve credentials",
|
||||
"description": "Manual retrieve credentials button text."
|
||||
},
|
||||
"fieldsFill": {
|
||||
"message": "Error: Tidak dapat menemukan bidang yang harus diisi.",
|
||||
"description": "Alert message when no fields are found to fill in."
|
||||
|
|
@ -694,10 +674,6 @@
|
|||
"message": "Site Preferences",
|
||||
"description": "Site Preferences page header."
|
||||
},
|
||||
"optionsSitePreferencesSettings": {
|
||||
"message": "Settings",
|
||||
"description": "Site Preferences settings button text."
|
||||
},
|
||||
"optionsMenuAbout": {
|
||||
"message": "Tentang",
|
||||
"description": "About page header."
|
||||
|
|
@ -750,10 +726,6 @@
|
|||
"message": "Ekspor pengaturan",
|
||||
"description": "Export settings button text."
|
||||
},
|
||||
"optionsButtonResetSettings": {
|
||||
"message": "Reset all settings",
|
||||
"description": "Reset all settings button text."
|
||||
},
|
||||
"optionsLabelDefaultGroup": {
|
||||
"message": "Grup baku untuk menyimpan sandi baru:",
|
||||
"description": "Default group options text."
|
||||
|
|
@ -1058,10 +1030,6 @@
|
|||
"message": "Membutuhkan KeePassXC versi: $1",
|
||||
"description": "KeePassXC version requirement text."
|
||||
},
|
||||
"optionsMinimumKeePassXCVersionRequired": {
|
||||
"message": "The extension requires KeePassXC version: $1. Earlier versions can cause unwanted behavior.",
|
||||
"description": "KeePassXC minimum version requirement text."
|
||||
},
|
||||
"optionsDefault": {
|
||||
"message": "Standar: $1",
|
||||
"description": "Default setting text."
|
||||
|
|
@ -1098,10 +1066,6 @@
|
|||
"message": "Pengaturan saat ini akan diganti. Apakah Anda benar-benar ingin mengimpor berkas pengaturan?",
|
||||
"description": "Import settings confirmation dialog help text."
|
||||
},
|
||||
"optionsResetSettingsDialogText": {
|
||||
"message": "All settings will be reset to defaults. Are you sure?",
|
||||
"description": "Reset all settings dialog text."
|
||||
},
|
||||
"optionsConnectedDatabasesText": {
|
||||
"message": "Basis data terhubung ke KeePassXC-Browser.",
|
||||
"description": "Info text about connected databases."
|
||||
|
|
@ -1146,8 +1110,8 @@
|
|||
"message": "Halaman URL",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnFeatures": {
|
||||
"message": "Features",
|
||||
"optionsColumnIgnore": {
|
||||
"message": "Abaikan",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnUsernameOnly": {
|
||||
|
|
@ -1178,10 +1142,6 @@
|
|||
"message": "Nonaktifkan Pengiriman Otomatis",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionPasskeys": {
|
||||
"message": "Disable passkeys",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionFull": {
|
||||
"message": "Nonaktifkan semua fitur",
|
||||
"description": "Site Preferences option selection."
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
"description": "Add button title text on Site Preferences tab."
|
||||
},
|
||||
"editSitePreferenceButtonTitle": {
|
||||
"message": "Modifica URL per queste preferenze sito",
|
||||
"message": "Modifica URL per queste Preferenze Sito",
|
||||
"description": "Edit button title text on Site Preferences tab."
|
||||
},
|
||||
"allowIframeButtonTitle": {
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
"description": "Connect button title text."
|
||||
},
|
||||
"copyDebugInfoButtonTitle": {
|
||||
"message": "Copia informazioni debug negli appunti",
|
||||
"message": "Copia informazioni di debug negli appunti",
|
||||
"description": "Copy debug info button title text on About tab."
|
||||
},
|
||||
"dismissHttpAuthButtonTitle": {
|
||||
|
|
@ -244,7 +244,7 @@
|
|||
"description": "Error notification shown when not connected to KeePassXC."
|
||||
},
|
||||
"errorCurrentDatabaseNotConnected": {
|
||||
"message": "Il database attuale non è connesso.",
|
||||
"message": "Il database corrente non è connesso.",
|
||||
"description": "Error notification shown when current database is not connected during action."
|
||||
},
|
||||
"passwordGeneratorErrorTooLong": {
|
||||
|
|
@ -322,10 +322,6 @@
|
|||
"message": "Campo stringa",
|
||||
"description": "Text for string field."
|
||||
},
|
||||
"defineSubmitButton": {
|
||||
"message": "Pulsante Invia",
|
||||
"description": "Text for Submit Button."
|
||||
},
|
||||
"defineChooseUsername": {
|
||||
"message": "Scegli un campo nome utente",
|
||||
"description": "Choosing a username field text when choosing Custom Login Fields."
|
||||
|
|
@ -342,13 +338,9 @@
|
|||
"message": "Scegli campi stringa",
|
||||
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineChooseSubmitButton": {
|
||||
"message": "Scegli pulsante Invia",
|
||||
"description": "Choose submit button selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineHelpText": {
|
||||
"message": "Conferma la selezione o scegli più campi come campi stringa.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
|
||||
"message": "Conferma la selezione o scegli altri campi come campi stringa.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
|
||||
},
|
||||
"defineKeyboardText": {
|
||||
"message": "Puoi anche usare i numeri per scegliere i campi di input dalla tastiera.",
|
||||
|
|
@ -370,10 +362,6 @@
|
|||
"message": "Campi stringa",
|
||||
"description": "General text for a String Fields."
|
||||
},
|
||||
"submitButton": {
|
||||
"message": "Pulsante Invia",
|
||||
"description": "Custom Submit Button text."
|
||||
},
|
||||
"credentialsNoUsername": {
|
||||
"message": "- nessun nome utente -",
|
||||
"description": "Shown when no username is set in the credentials."
|
||||
|
|
@ -382,16 +370,12 @@
|
|||
"message": "Errore:\nNessun accesso trovato.",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsBlockedInIframe": {
|
||||
"message": "Riempimento delle credenziali bloccato in iframe non attendibile.",
|
||||
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "In KeePassXC è stato trovato più di un accesso!",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
},
|
||||
"credentialsMultipleFoundSecondLine": {
|
||||
"message": "Per ulteriori opzioni seelziona l'icona di KeePassXC-Browser.",
|
||||
"message": "Premi l'icona di KeePassXC-Browser per ulteriori opzioni.",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
},
|
||||
"credentialsNoUsernameFound": {
|
||||
|
|
@ -406,10 +390,6 @@
|
|||
"message": "Scaduta",
|
||||
"description": "If a credential is expired, this is appended to the title label."
|
||||
},
|
||||
"credentialsRetrieveButton": {
|
||||
"message": "Recupera credenziali",
|
||||
"description": "Manual retrieve credentials button text."
|
||||
},
|
||||
"fieldsFill": {
|
||||
"message": "Errore: impossibile trovare campi da compilare.",
|
||||
"description": "Alert message when no fields are found to fill in."
|
||||
|
|
@ -503,7 +483,7 @@
|
|||
"description": "Checking status message in popup."
|
||||
},
|
||||
"popupNotConfigured": {
|
||||
"message": "KeePassXC-Browser non è stato configurato. \nPer associarlo a KeePassXC seleziona il pulsante 'Connetti'.",
|
||||
"message": "KeePassXC-Browser non è stato configurato. Premi il pulsante connetti per associarlo a KeePassXC.",
|
||||
"description": "A popup message shown when the extension has not been connected to KeePassXC."
|
||||
},
|
||||
"popupNeedReconfigure": {
|
||||
|
|
@ -511,7 +491,7 @@
|
|||
"description": "A popup message shown when the extension has been disconnected from KeePassXC."
|
||||
},
|
||||
"popupNeedReconfigureMessage": {
|
||||
"message": "Per stabilire una nuova connessione seleziona il pulsante 'Riconnetti'.",
|
||||
"message": "Premi il pulsante riconnetti per stabilire una nuova connessione.",
|
||||
"description": "A popup message shown when reconnect is needed."
|
||||
},
|
||||
"popupConfiguredNotAssociated": {
|
||||
|
|
@ -551,7 +531,7 @@
|
|||
"description": "A message shown choosing what credentials user wants to update."
|
||||
},
|
||||
"rememberChooseGroup": {
|
||||
"message": "Per memorizzare le nuove credenziali scegli un gruppo.",
|
||||
"message": "Scegli un gruppo per memorizzare le nuove credenziali.",
|
||||
"description": "A message shown choosing what group user wants to use for new credentials."
|
||||
},
|
||||
"rememberInfoDefaultGroupNotFound": {
|
||||
|
|
@ -694,10 +674,6 @@
|
|||
"message": "Preferenze sito",
|
||||
"description": "Site Preferences page header."
|
||||
},
|
||||
"optionsSitePreferencesSettings": {
|
||||
"message": "Impostazioni",
|
||||
"description": "Site Preferences settings button text."
|
||||
},
|
||||
"optionsMenuAbout": {
|
||||
"message": "Info",
|
||||
"description": "About page header."
|
||||
|
|
@ -750,10 +726,6 @@
|
|||
"message": "Esporta impostazioni",
|
||||
"description": "Export settings button text."
|
||||
},
|
||||
"optionsButtonResetSettings": {
|
||||
"message": "Ripristina tutte le impostazioni",
|
||||
"description": "Reset all settings button text."
|
||||
},
|
||||
"optionsLabelDefaultGroup": {
|
||||
"message": "Gruppo predefinito salvataggio nuove password:",
|
||||
"description": "Default group options text."
|
||||
|
|
@ -763,7 +735,7 @@
|
|||
"description": "Default group help text."
|
||||
},
|
||||
"optionsLabelDefaultPasskeyGroup": {
|
||||
"message": "Gruppo predefinito salvataggio nuove passkey:",
|
||||
"message": "Gruppo predefinito per il salvataggio delle nuove passkeys:",
|
||||
"description": "Default passkey group options text."
|
||||
},
|
||||
"optionsLabelDefaultGroupCheckboxText": {
|
||||
|
|
@ -855,11 +827,11 @@
|
|||
"description": "Show login notifications checkbox text."
|
||||
},
|
||||
"optionsDefaultPasswordManager": {
|
||||
"message": "Imposta come password manager predefinito",
|
||||
"message": "Imposta come password manager di default",
|
||||
"description": "Default password manager checkbox text."
|
||||
},
|
||||
"optionsDefaultPasswordManagerHelpText": {
|
||||
"message": "Imposta KeePassXC-Browser come password manager predefinito per il browser.",
|
||||
"message": "Imposta KeePassXC-Browser come password manager di default per il browser.",
|
||||
"description": "Default password manager help text."
|
||||
},
|
||||
"optionsRedirectAllowance": {
|
||||
|
|
@ -959,7 +931,7 @@
|
|||
"description": "OTP field icon option help text."
|
||||
},
|
||||
"optionsHideIconsAlertText": {
|
||||
"message": "Puoi nascondere le icone manualmente quando occorre con Maiusc+Clic",
|
||||
"message": "Puoi nascondere le icone manualmente quando occorre con Shift+Click",
|
||||
"description": "Hide icons alert text text."
|
||||
},
|
||||
"optionsAutoRetrieveCredentialsHelpText": {
|
||||
|
|
@ -975,7 +947,7 @@
|
|||
"description": "Auto-Fill Single Entry option help text."
|
||||
},
|
||||
"optionsAutoFillRelevantCredentialHelpText": {
|
||||
"message": "Se è necessario un secondo riempimento compila automaticamente le credenziali pertinenti.",
|
||||
"message": "Compila automaticamente le credenziali pertinenti se è necessario un secondo riempimento.",
|
||||
"description": "Auto-Fill Relevant Credential option help text."
|
||||
},
|
||||
"optionsAutoFillSingleEntryWarning": {
|
||||
|
|
@ -1058,10 +1030,6 @@
|
|||
"message": "Richiede KeePassXC versione: $1",
|
||||
"description": "KeePassXC version requirement text."
|
||||
},
|
||||
"optionsMinimumKeePassXCVersionRequired": {
|
||||
"message": "L'estensione richiede la versione KeePassXC: $1. Le versioni precedenti possono causare comportamenti indesiderati.",
|
||||
"description": "KeePassXC minimum version requirement text."
|
||||
},
|
||||
"optionsDefault": {
|
||||
"message": "Predefinito: $1",
|
||||
"description": "Default setting text."
|
||||
|
|
@ -1098,10 +1066,6 @@
|
|||
"message": "Le impostazioni attuali verranno sovrascritte. Vuoi veramente importare le impostazioni?",
|
||||
"description": "Import settings confirmation dialog help text."
|
||||
},
|
||||
"optionsResetSettingsDialogText": {
|
||||
"message": "Tutte le impostazioni verranno ripristinate ai valori predefiniti. \nVuoi continuare?",
|
||||
"description": "Reset all settings dialog text."
|
||||
},
|
||||
"optionsConnectedDatabasesText": {
|
||||
"message": "Database KeePassXC connessi a KeePassXC-Browser.",
|
||||
"description": "Info text about connected databases."
|
||||
|
|
@ -1115,7 +1079,7 @@
|
|||
"description": "Confirmation text when removing database from the list."
|
||||
},
|
||||
"optionsDatabasesRemoveIdentifierConfirmFirst": {
|
||||
"message": "Vuoi rimuovere l'identificatore $1 dall'elenco database? ",
|
||||
"message": "Vuoi davvero rimuovere l'identificatore $1 dall'elenco del database? ",
|
||||
"description": "Confirmation text when removing database from the list."
|
||||
},
|
||||
"optionsDatabasesRemoveIdentifierConfirmSecond": {
|
||||
|
|
@ -1146,8 +1110,8 @@
|
|||
"message": "URL pagina",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnFeatures": {
|
||||
"message": "Funzionalità",
|
||||
"optionsColumnIgnore": {
|
||||
"message": "Ignora",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnUsernameOnly": {
|
||||
|
|
@ -1178,10 +1142,6 @@
|
|||
"message": "Disabilita l'invio automatico",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionPasskeys": {
|
||||
"message": "Disabilita passkeys",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionFull": {
|
||||
"message": "Disattiva tutte le funzionalità",
|
||||
"description": "Site Preferences option selection."
|
||||
|
|
@ -1435,23 +1395,23 @@
|
|||
"description": "Lock database button title text."
|
||||
},
|
||||
"welcomeText": {
|
||||
"message": "Benvenuto in KeePassXC-Browser!",
|
||||
"message": "Benvenuto su KeePassXC-Browser!",
|
||||
"description": "Main title of Getting Started page."
|
||||
},
|
||||
"documentationGettingStarted": {
|
||||
"message": "Grazie alla nostra 'Guida introduttiva' sarai subito operativo.",
|
||||
"message": "Grazie alla nostra Guida introduttiva sarai subito operativo.",
|
||||
"description": "Getting Started document text."
|
||||
},
|
||||
"documentationUserGuide": {
|
||||
"message": "Cerchi una documentazione più completa? \nLa nostra 'Guida per l'utente' è qui per aiutarti.",
|
||||
"message": "Cerchi una documentazione più completa? La nostra Guida per l'utente è qui per aiutarti.",
|
||||
"description": "User Guide text."
|
||||
},
|
||||
"documentationTroubleshootingGuide": {
|
||||
"message": "Hai bisogno di aiuto per risolvere i problemi di integrazione del browser? Consulta la 'Guida alla risoluzione dei problemi'.",
|
||||
"message": "Hai bisogno di aiuto per risolvere i problemi di integrazione del browser? Consulta la Guida alla risoluzione dei problemi.",
|
||||
"description": "Troubleshooting Guide text."
|
||||
},
|
||||
"gettingStartedWelcomeText": {
|
||||
"message": "Benvenuti in KeePassXC-Browser, l'estensione ufficiale del browser per KeePassXC.",
|
||||
"message": "Benvenuti su KeePassXC-Browser, l'estensione ufficiale del browser per KeePassXC.",
|
||||
"description": "Welcome to KeePassXC-Browser, the official browser extension for KeePassXC."
|
||||
},
|
||||
"gettingStartedSecondWelcomeText": {
|
||||
|
|
@ -1459,7 +1419,7 @@
|
|||
"description": "Please go through the default settings and check all your preferred options."
|
||||
},
|
||||
"gettingStartedNewUser": {
|
||||
"message": "Sei un nuovo utente? \nConsulta i collegamenti alla nostra documentazione.",
|
||||
"message": "Sei un nuovo utente? Consulta i link alla nostra documentazione.",
|
||||
"description": "Are you a new user? Check links to our documentation."
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
"description": "Add button title text on Site Preferences tab."
|
||||
},
|
||||
"editSitePreferenceButtonTitle": {
|
||||
"message": "このサイト設定の URL を編集します。",
|
||||
"message": "Edit URL for this Site Preferences.",
|
||||
"description": "Edit button title text on Site Preferences tab."
|
||||
},
|
||||
"allowIframeButtonTitle": {
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
"description": "Connect button title text."
|
||||
},
|
||||
"copyDebugInfoButtonTitle": {
|
||||
"message": "クリップボードにデバッグ情報をコピー",
|
||||
"message": "Copy debug info to clipboard",
|
||||
"description": "Copy debug info button title text on About tab."
|
||||
},
|
||||
"dismissHttpAuthButtonTitle": {
|
||||
|
|
@ -319,13 +319,9 @@
|
|||
"description": "Clear save data button text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineStringField": {
|
||||
"message": "String Field",
|
||||
"message": "文字列フィールド",
|
||||
"description": "Text for string field."
|
||||
},
|
||||
"defineSubmitButton": {
|
||||
"message": "Submit Button",
|
||||
"description": "Text for Submit Button."
|
||||
},
|
||||
"defineChooseUsername": {
|
||||
"message": "ユーザー名フィールドを選択してください",
|
||||
"description": "Choosing a username field text when choosing Custom Login Fields."
|
||||
|
|
@ -342,13 +338,9 @@
|
|||
"message": "文字列フィールドを選択してください",
|
||||
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineChooseSubmitButton": {
|
||||
"message": "Choose submit button",
|
||||
"description": "Choose submit button selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineHelpText": {
|
||||
"message": "Please confirm your selection or choose more fields as String Fields.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
|
||||
"message": "選択したものを確認するか、文字列フィールドとして別のフィールドを追加で選択してください。",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
|
||||
},
|
||||
"defineKeyboardText": {
|
||||
"message": "キーボードの数字キーで入力フィールドを選択できます。",
|
||||
|
|
@ -370,10 +362,6 @@
|
|||
"message": "文字列フィールド",
|
||||
"description": "General text for a String Fields."
|
||||
},
|
||||
"submitButton": {
|
||||
"message": "Submit Button",
|
||||
"description": "Custom Submit Button text."
|
||||
},
|
||||
"credentialsNoUsername": {
|
||||
"message": "- ユーザー名なし -",
|
||||
"description": "Shown when no username is set in the credentials."
|
||||
|
|
@ -382,10 +370,6 @@
|
|||
"message": "ログイン情報が見つかりません。",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsBlockedInIframe": {
|
||||
"message": "Credential fill blocked in untrusted iframe.",
|
||||
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "KeePassXC に複数のログイン情報が見つかりました!",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
|
|
@ -406,10 +390,6 @@
|
|||
"message": "期限切れ",
|
||||
"description": "If a credential is expired, this is appended to the title label."
|
||||
},
|
||||
"credentialsRetrieveButton": {
|
||||
"message": "Retrieve credentials",
|
||||
"description": "Manual retrieve credentials button text."
|
||||
},
|
||||
"fieldsFill": {
|
||||
"message": "エラー: 入力するフィールドを見つけられません。",
|
||||
"description": "Alert message when no fields are found to fill in."
|
||||
|
|
@ -694,10 +674,6 @@
|
|||
"message": "サイト設定",
|
||||
"description": "Site Preferences page header."
|
||||
},
|
||||
"optionsSitePreferencesSettings": {
|
||||
"message": "Settings",
|
||||
"description": "Site Preferences settings button text."
|
||||
},
|
||||
"optionsMenuAbout": {
|
||||
"message": "情報",
|
||||
"description": "About page header."
|
||||
|
|
@ -723,7 +699,7 @@
|
|||
"description": "Add button text."
|
||||
},
|
||||
"optionsButtonEdit": {
|
||||
"message": "編集",
|
||||
"message": "Edit",
|
||||
"description": "Edit button text."
|
||||
},
|
||||
"optionsButtonUpdate": {
|
||||
|
|
@ -750,10 +726,6 @@
|
|||
"message": "設定をエクスポート",
|
||||
"description": "Export settings button text."
|
||||
},
|
||||
"optionsButtonResetSettings": {
|
||||
"message": "Reset all settings",
|
||||
"description": "Reset all settings button text."
|
||||
},
|
||||
"optionsLabelDefaultGroup": {
|
||||
"message": "新しいパスワードを保存するデフォルトのグループ:",
|
||||
"description": "Default group options text."
|
||||
|
|
@ -811,7 +783,7 @@
|
|||
"description": "Automatically fill-in single credential entry checkbox text."
|
||||
},
|
||||
"optionsCheckboxAutoFillRelevantCredential": {
|
||||
"message": "関連する資格情報を自動的に補完する",
|
||||
"message": "Automatically fill in relevant credential entries",
|
||||
"description": "Automatically fill-in relevant credential entry checkbox text."
|
||||
},
|
||||
"optionsCheckboxAutoFillSingleTotp": {
|
||||
|
|
@ -855,11 +827,11 @@
|
|||
"description": "Show login notifications checkbox text."
|
||||
},
|
||||
"optionsDefaultPasswordManager": {
|
||||
"message": "デフォルトのパスワードマネージャーに設定する",
|
||||
"message": "Set as default password manager",
|
||||
"description": "Default password manager checkbox text."
|
||||
},
|
||||
"optionsDefaultPasswordManagerHelpText": {
|
||||
"message": "KeePassXC-Browser をブラウザーのデフォルトのパスワードマネージャーに設定します",
|
||||
"message": "Sets KeePassXC-Browser as the default password manager for the browser.",
|
||||
"description": "Default password manager help text."
|
||||
},
|
||||
"optionsRedirectAllowance": {
|
||||
|
|
@ -871,7 +843,7 @@
|
|||
"description": "Redirect allowance help text."
|
||||
},
|
||||
"optionsCheckboxAutoFillAndSend": {
|
||||
"message": "HTTP ベーシック認証への資格情報の補完を許可する",
|
||||
"message": "HTTP Basic 認証の資格情報の入力を許可する",
|
||||
"description": "Allow filling HTTP Basic Auth credentials checkbox text."
|
||||
},
|
||||
"optionsDebugLogging": {
|
||||
|
|
@ -959,7 +931,7 @@
|
|||
"description": "OTP field icon option help text."
|
||||
},
|
||||
"optionsHideIconsAlertText": {
|
||||
"message": "必要に応じて Shift + クリックでアイコンを隠すこともできます。",
|
||||
"message": "You can also hide icons manually when needed with shift+click.",
|
||||
"description": "Hide icons alert text text."
|
||||
},
|
||||
"optionsAutoRetrieveCredentialsHelpText": {
|
||||
|
|
@ -975,7 +947,7 @@
|
|||
"description": "Auto-Fill Single Entry option help text."
|
||||
},
|
||||
"optionsAutoFillRelevantCredentialHelpText": {
|
||||
"message": "2 回目の入力が必要な場合、関連する資格情報を自動的に補完します。",
|
||||
"message": "Automatically fill in the relevant credential if a second fill is needed.",
|
||||
"description": "Auto-Fill Relevant Credential option help text."
|
||||
},
|
||||
"optionsAutoFillSingleEntryWarning": {
|
||||
|
|
@ -1055,13 +1027,9 @@
|
|||
"description": "Settings page info text about the latest KeePassXC version."
|
||||
},
|
||||
"optionsKeePassXCVersionRequired": {
|
||||
"message": "次のバージョン以上の KeePassXC が必要です: $1",
|
||||
"message": "次のバージョンの KeePassXC が必要です: $1",
|
||||
"description": "KeePassXC version requirement text."
|
||||
},
|
||||
"optionsMinimumKeePassXCVersionRequired": {
|
||||
"message": "The extension requires KeePassXC version: $1. Earlier versions can cause unwanted behavior.",
|
||||
"description": "KeePassXC minimum version requirement text."
|
||||
},
|
||||
"optionsDefault": {
|
||||
"message": "デフォルト: $1",
|
||||
"description": "Default setting text."
|
||||
|
|
@ -1098,10 +1066,6 @@
|
|||
"message": "現在の設定を上書きします。本当に設定ファイルをインポートしますか?",
|
||||
"description": "Import settings confirmation dialog help text."
|
||||
},
|
||||
"optionsResetSettingsDialogText": {
|
||||
"message": "All settings will be reset to defaults. Are you sure?",
|
||||
"description": "Reset all settings dialog text."
|
||||
},
|
||||
"optionsConnectedDatabasesText": {
|
||||
"message": "KeePassXC-Browser に接続されているデータベースです。",
|
||||
"description": "Info text about connected databases."
|
||||
|
|
@ -1146,8 +1110,8 @@
|
|||
"message": "ページの URL",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnFeatures": {
|
||||
"message": "Features",
|
||||
"optionsColumnIgnore": {
|
||||
"message": "無視",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnUsernameOnly": {
|
||||
|
|
@ -1178,10 +1142,6 @@
|
|||
"message": "自動送信を無効にする",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionPasskeys": {
|
||||
"message": "Disable passkeys",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionFull": {
|
||||
"message": "すべての機能を無効にする",
|
||||
"description": "Site Preferences option selection."
|
||||
|
|
@ -1435,7 +1395,7 @@
|
|||
"description": "Lock database button title text."
|
||||
},
|
||||
"welcomeText": {
|
||||
"message": "KeePassXC-Browser にようこそ!",
|
||||
"message": "Welcome to KeePassXC-Browser!",
|
||||
"description": "Main title of Getting Started page."
|
||||
},
|
||||
"documentationGettingStarted": {
|
||||
|
|
|
|||
|
|
@ -319,13 +319,9 @@
|
|||
"description": "Clear save data button text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineStringField": {
|
||||
"message": "String Field",
|
||||
"message": "문자열 필드",
|
||||
"description": "Text for string field."
|
||||
},
|
||||
"defineSubmitButton": {
|
||||
"message": "Submit Button",
|
||||
"description": "Text for Submit Button."
|
||||
},
|
||||
"defineChooseUsername": {
|
||||
"message": "사용자 이름 필드 선택",
|
||||
"description": "Choosing a username field text when choosing Custom Login Fields."
|
||||
|
|
@ -342,13 +338,9 @@
|
|||
"message": "문자열 필드 선택",
|
||||
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineChooseSubmitButton": {
|
||||
"message": "Choose submit button",
|
||||
"description": "Choose submit button selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineHelpText": {
|
||||
"message": "Please confirm your selection or choose more fields as String Fields.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
|
||||
"message": "선택 사항을 확인하거나 더 많은 <em>문자열 필드</em>를 선택하십시오.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
|
||||
},
|
||||
"defineKeyboardText": {
|
||||
"message": "키보드에서 숫자를 눌러서 입력 필드를 선택할 수 있습니다.",
|
||||
|
|
@ -370,10 +362,6 @@
|
|||
"message": "문자열 필드",
|
||||
"description": "General text for a String Fields."
|
||||
},
|
||||
"submitButton": {
|
||||
"message": "Submit Button",
|
||||
"description": "Custom Submit Button text."
|
||||
},
|
||||
"credentialsNoUsername": {
|
||||
"message": "- 사용자 이름 없음 -",
|
||||
"description": "Shown when no username is set in the credentials."
|
||||
|
|
@ -382,10 +370,6 @@
|
|||
"message": "오류:\n로그인을 찾을 수 없습니다.",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsBlockedInIframe": {
|
||||
"message": "Credential fill blocked in untrusted iframe.",
|
||||
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "오류:\nKeePassXC에 하나 이상의 로그인 정보가 있습니다!\nKeePassXC-브라우저 아이콘을 눌러서 더 많은 옵션을 보십시오.",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
|
|
@ -406,10 +390,6 @@
|
|||
"message": "만료됨",
|
||||
"description": "If a credential is expired, this is appended to the title label."
|
||||
},
|
||||
"credentialsRetrieveButton": {
|
||||
"message": "Retrieve credentials",
|
||||
"description": "Manual retrieve credentials button text."
|
||||
},
|
||||
"fieldsFill": {
|
||||
"message": "오류:\n입력할 필드를 찾을 수 없습니다.",
|
||||
"description": "Alert message when no fields are found to fill in."
|
||||
|
|
@ -694,10 +674,6 @@
|
|||
"message": "Site Preferences",
|
||||
"description": "Site Preferences page header."
|
||||
},
|
||||
"optionsSitePreferencesSettings": {
|
||||
"message": "Settings",
|
||||
"description": "Site Preferences settings button text."
|
||||
},
|
||||
"optionsMenuAbout": {
|
||||
"message": "정보",
|
||||
"description": "About page header."
|
||||
|
|
@ -750,10 +726,6 @@
|
|||
"message": "설정 내보내기",
|
||||
"description": "Export settings button text."
|
||||
},
|
||||
"optionsButtonResetSettings": {
|
||||
"message": "Reset all settings",
|
||||
"description": "Reset all settings button text."
|
||||
},
|
||||
"optionsLabelDefaultGroup": {
|
||||
"message": "새 암호를 저장할 기본 그룹:",
|
||||
"description": "Default group options text."
|
||||
|
|
@ -1058,10 +1030,6 @@
|
|||
"message": "필요한 KeePassXC 버전: $1",
|
||||
"description": "KeePassXC version requirement text."
|
||||
},
|
||||
"optionsMinimumKeePassXCVersionRequired": {
|
||||
"message": "The extension requires KeePassXC version: $1. Earlier versions can cause unwanted behavior.",
|
||||
"description": "KeePassXC minimum version requirement text."
|
||||
},
|
||||
"optionsDefault": {
|
||||
"message": "기본값: $1",
|
||||
"description": "Default setting text."
|
||||
|
|
@ -1098,10 +1066,6 @@
|
|||
"message": "현재 설정을 덮어쓸 것입니다. 설정 파일을 가져오시겠습니까?",
|
||||
"description": "Import settings confirmation dialog help text."
|
||||
},
|
||||
"optionsResetSettingsDialogText": {
|
||||
"message": "All settings will be reset to defaults. Are you sure?",
|
||||
"description": "Reset all settings dialog text."
|
||||
},
|
||||
"optionsConnectedDatabasesText": {
|
||||
"message": "다음 KeePassXC 데이터베이스가 KeePassXC-브라우저에 연결되어 있습니다.",
|
||||
"description": "Info text about connected databases."
|
||||
|
|
@ -1146,8 +1110,8 @@
|
|||
"message": "페이지 URL",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnFeatures": {
|
||||
"message": "Features",
|
||||
"optionsColumnIgnore": {
|
||||
"message": "무시",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnUsernameOnly": {
|
||||
|
|
@ -1178,10 +1142,6 @@
|
|||
"message": "자동 제출 비활성화",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionPasskeys": {
|
||||
"message": "Disable passkeys",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionFull": {
|
||||
"message": "모든 기능 비활성화",
|
||||
"description": "Site Preferences option selection."
|
||||
|
|
|
|||
|
|
@ -319,13 +319,9 @@
|
|||
"description": "Clear save data button text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineStringField": {
|
||||
"message": "String Field",
|
||||
"message": "Eilutės laukas",
|
||||
"description": "Text for string field."
|
||||
},
|
||||
"defineSubmitButton": {
|
||||
"message": "Submit Button",
|
||||
"description": "Text for Submit Button."
|
||||
},
|
||||
"defineChooseUsername": {
|
||||
"message": "Pasirinkite vartotojo vardo lauką",
|
||||
"description": "Choosing a username field text when choosing Custom Login Fields."
|
||||
|
|
@ -342,13 +338,9 @@
|
|||
"message": "Pasirinkite eilučių laukus",
|
||||
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineChooseSubmitButton": {
|
||||
"message": "Choose submit button",
|
||||
"description": "Choose submit button selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineHelpText": {
|
||||
"message": "Please confirm your selection or choose more fields as String Fields.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
|
||||
"message": "Patvirtinkite savo pasirinkimą arba pasirinkite daugiau laukų kaip eilutės laukus.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
|
||||
},
|
||||
"defineKeyboardText": {
|
||||
"message": "Taip pat galite naudoti skaičius, kad pasirinktumėte įvesties laukus iš klaviatūros.",
|
||||
|
|
@ -370,10 +362,6 @@
|
|||
"message": "Eilučių laukai",
|
||||
"description": "General text for a String Fields."
|
||||
},
|
||||
"submitButton": {
|
||||
"message": "Submit Button",
|
||||
"description": "Custom Submit Button text."
|
||||
},
|
||||
"credentialsNoUsername": {
|
||||
"message": "- nėra naudotojo vardo -",
|
||||
"description": "Shown when no username is set in the credentials."
|
||||
|
|
@ -382,10 +370,6 @@
|
|||
"message": "Nerasta jokių prisijungimų.",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsBlockedInIframe": {
|
||||
"message": "Credential fill blocked in untrusted iframe.",
|
||||
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "KeePassXC buvo rastas daugiau nei vienas prisijungimas!",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
|
|
@ -406,10 +390,6 @@
|
|||
"message": "Nebegalioja",
|
||||
"description": "If a credential is expired, this is appended to the title label."
|
||||
},
|
||||
"credentialsRetrieveButton": {
|
||||
"message": "Retrieve credentials",
|
||||
"description": "Manual retrieve credentials button text."
|
||||
},
|
||||
"fieldsFill": {
|
||||
"message": "Klaida: nepavyksta rasti pildomų laukų.",
|
||||
"description": "Alert message when no fields are found to fill in."
|
||||
|
|
@ -694,10 +674,6 @@
|
|||
"message": "Svetainės nuostatos",
|
||||
"description": "Site Preferences page header."
|
||||
},
|
||||
"optionsSitePreferencesSettings": {
|
||||
"message": "Settings",
|
||||
"description": "Site Preferences settings button text."
|
||||
},
|
||||
"optionsMenuAbout": {
|
||||
"message": "Apie",
|
||||
"description": "About page header."
|
||||
|
|
@ -750,10 +726,6 @@
|
|||
"message": "Eksportuoti nustatymus",
|
||||
"description": "Export settings button text."
|
||||
},
|
||||
"optionsButtonResetSettings": {
|
||||
"message": "Reset all settings",
|
||||
"description": "Reset all settings button text."
|
||||
},
|
||||
"optionsLabelDefaultGroup": {
|
||||
"message": "Numatytoji naujų slaptažodžių įrašymo grupė:",
|
||||
"description": "Default group options text."
|
||||
|
|
@ -1058,10 +1030,6 @@
|
|||
"message": "Reikalinga KeePassXC versija: $1",
|
||||
"description": "KeePassXC version requirement text."
|
||||
},
|
||||
"optionsMinimumKeePassXCVersionRequired": {
|
||||
"message": "The extension requires KeePassXC version: $1. Earlier versions can cause unwanted behavior.",
|
||||
"description": "KeePassXC minimum version requirement text."
|
||||
},
|
||||
"optionsDefault": {
|
||||
"message": "Numatytoji reikšmė: $1",
|
||||
"description": "Default setting text."
|
||||
|
|
@ -1098,10 +1066,6 @@
|
|||
"message": "Dabartiniai nustatymai bus nepaisomi. Ar tikrai norite importuoti nustatymų failą?",
|
||||
"description": "Import settings confirmation dialog help text."
|
||||
},
|
||||
"optionsResetSettingsDialogText": {
|
||||
"message": "All settings will be reset to defaults. Are you sure?",
|
||||
"description": "Reset all settings dialog text."
|
||||
},
|
||||
"optionsConnectedDatabasesText": {
|
||||
"message": "Duomenų bazės, prijungtos prie \"KeePassXC-Browser\".",
|
||||
"description": "Info text about connected databases."
|
||||
|
|
@ -1146,8 +1110,8 @@
|
|||
"message": "Puslapio URL",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnFeatures": {
|
||||
"message": "Features",
|
||||
"optionsColumnIgnore": {
|
||||
"message": "Nepaisyti",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnUsernameOnly": {
|
||||
|
|
@ -1178,10 +1142,6 @@
|
|||
"message": "Automatinio pateikimo išjungimas",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionPasskeys": {
|
||||
"message": "Disable passkeys",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionFull": {
|
||||
"message": "Išjungti visas ypatybes",
|
||||
"description": "Site Preferences option selection."
|
||||
|
|
|
|||
|
|
@ -319,13 +319,9 @@
|
|||
"description": "Clear save data button text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineStringField": {
|
||||
"message": "String Field",
|
||||
"message": "Tekstfelt",
|
||||
"description": "Text for string field."
|
||||
},
|
||||
"defineSubmitButton": {
|
||||
"message": "Submit Button",
|
||||
"description": "Text for Submit Button."
|
||||
},
|
||||
"defineChooseUsername": {
|
||||
"message": "Velg et brukernavnfelt",
|
||||
"description": "Choosing a username field text when choosing Custom Login Fields."
|
||||
|
|
@ -342,13 +338,9 @@
|
|||
"message": "Velg tekstfelt",
|
||||
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineChooseSubmitButton": {
|
||||
"message": "Choose submit button",
|
||||
"description": "Choose submit button selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineHelpText": {
|
||||
"message": "Please confirm your selection or choose more fields as String Fields.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
|
||||
"message": "Bekreft utvalget, eller velg flere felt som tekstfelt.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
|
||||
},
|
||||
"defineKeyboardText": {
|
||||
"message": "Du kan også bruke tallene til å velge innputtfeltene fra tastatur.",
|
||||
|
|
@ -370,10 +362,6 @@
|
|||
"message": "Tekstfelt",
|
||||
"description": "General text for a String Fields."
|
||||
},
|
||||
"submitButton": {
|
||||
"message": "Submit Button",
|
||||
"description": "Custom Submit Button text."
|
||||
},
|
||||
"credentialsNoUsername": {
|
||||
"message": "- ikke noe brukernavn -",
|
||||
"description": "Shown when no username is set in the credentials."
|
||||
|
|
@ -382,10 +370,6 @@
|
|||
"message": "Fant ingen pålogginger.",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsBlockedInIframe": {
|
||||
"message": "Credential fill blocked in untrusted iframe.",
|
||||
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "Mer enn én pålogging ble funnet i KeePassXC!",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
|
|
@ -406,10 +390,6 @@
|
|||
"message": "Utløpt",
|
||||
"description": "If a credential is expired, this is appended to the title label."
|
||||
},
|
||||
"credentialsRetrieveButton": {
|
||||
"message": "Retrieve credentials",
|
||||
"description": "Manual retrieve credentials button text."
|
||||
},
|
||||
"fieldsFill": {
|
||||
"message": "Feil: Finner ikke felt å fylle ut.",
|
||||
"description": "Alert message when no fields are found to fill in."
|
||||
|
|
@ -694,10 +674,6 @@
|
|||
"message": "Site Preferences",
|
||||
"description": "Site Preferences page header."
|
||||
},
|
||||
"optionsSitePreferencesSettings": {
|
||||
"message": "Settings",
|
||||
"description": "Site Preferences settings button text."
|
||||
},
|
||||
"optionsMenuAbout": {
|
||||
"message": "Om",
|
||||
"description": "About page header."
|
||||
|
|
@ -750,10 +726,6 @@
|
|||
"message": "Eksporter innstillinger",
|
||||
"description": "Export settings button text."
|
||||
},
|
||||
"optionsButtonResetSettings": {
|
||||
"message": "Reset all settings",
|
||||
"description": "Reset all settings button text."
|
||||
},
|
||||
"optionsLabelDefaultGroup": {
|
||||
"message": "Standardgruppe for lagring av nye passord:",
|
||||
"description": "Default group options text."
|
||||
|
|
@ -1058,10 +1030,6 @@
|
|||
"message": "Krever KeePassXC-versjon: $1",
|
||||
"description": "KeePassXC version requirement text."
|
||||
},
|
||||
"optionsMinimumKeePassXCVersionRequired": {
|
||||
"message": "The extension requires KeePassXC version: $1. Earlier versions can cause unwanted behavior.",
|
||||
"description": "KeePassXC minimum version requirement text."
|
||||
},
|
||||
"optionsDefault": {
|
||||
"message": "Standard: $1",
|
||||
"description": "Default setting text."
|
||||
|
|
@ -1098,10 +1066,6 @@
|
|||
"message": "Gjeldende innstillinger vil bli overstyrt. Vil du virkelig importere filen med nye innstillinger?",
|
||||
"description": "Import settings confirmation dialog help text."
|
||||
},
|
||||
"optionsResetSettingsDialogText": {
|
||||
"message": "All settings will be reset to defaults. Are you sure?",
|
||||
"description": "Reset all settings dialog text."
|
||||
},
|
||||
"optionsConnectedDatabasesText": {
|
||||
"message": "Databaser koblet til KeePassXC–Browser.",
|
||||
"description": "Info text about connected databases."
|
||||
|
|
@ -1146,8 +1110,8 @@
|
|||
"message": "Nettside-URL",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnFeatures": {
|
||||
"message": "Features",
|
||||
"optionsColumnIgnore": {
|
||||
"message": "Ignorere",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnUsernameOnly": {
|
||||
|
|
@ -1178,10 +1142,6 @@
|
|||
"message": "Deaktiver automatisk sending",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionPasskeys": {
|
||||
"message": "Disable passkeys",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionFull": {
|
||||
"message": "Deaktiver alle funksjoner",
|
||||
"description": "Site Preferences option selection."
|
||||
|
|
|
|||
|
|
@ -322,10 +322,6 @@
|
|||
"message": "Tekenreeksveld",
|
||||
"description": "Text for string field."
|
||||
},
|
||||
"defineSubmitButton": {
|
||||
"message": "Knop Indienen",
|
||||
"description": "Text for Submit Button."
|
||||
},
|
||||
"defineChooseUsername": {
|
||||
"message": "Kies een gebruikersnaamveld",
|
||||
"description": "Choosing a username field text when choosing Custom Login Fields."
|
||||
|
|
@ -342,13 +338,9 @@
|
|||
"message": "Kies tekenreeksvelden",
|
||||
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineChooseSubmitButton": {
|
||||
"message": "Knop Indienen kiezen",
|
||||
"description": "Choose submit button selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineHelpText": {
|
||||
"message": "Bevestig de selectie of kies meer velden als tekenreeksvelden.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
|
||||
"message": "Bevestig je selectie of stel meerdere velden in als <em>tekenreeksvelden</em>.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
|
||||
},
|
||||
"defineKeyboardText": {
|
||||
"message": "Je kunt tevens gebruikmaken van de numerieke toetsen op je toetsenbord.",
|
||||
|
|
@ -370,10 +362,6 @@
|
|||
"message": "Tekenreeksvelden",
|
||||
"description": "General text for a String Fields."
|
||||
},
|
||||
"submitButton": {
|
||||
"message": "Knop Indienen",
|
||||
"description": "Custom Submit Button text."
|
||||
},
|
||||
"credentialsNoUsername": {
|
||||
"message": "- geen gebruikersnaam -",
|
||||
"description": "Shown when no username is set in the credentials."
|
||||
|
|
@ -382,10 +370,6 @@
|
|||
"message": "Geen inloggegevens aangetroffen.",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsBlockedInIframe": {
|
||||
"message": "Invulling van de inloggegevens geblokkeerd in niet-vertrouwd iframe.",
|
||||
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "Er zijn meerdere inloggegevens aangetroffen in KeePassXC!",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
|
|
@ -406,10 +390,6 @@
|
|||
"message": "Verlopen",
|
||||
"description": "If a credential is expired, this is appended to the title label."
|
||||
},
|
||||
"credentialsRetrieveButton": {
|
||||
"message": "Inloggegevens ophalen",
|
||||
"description": "Manual retrieve credentials button text."
|
||||
},
|
||||
"fieldsFill": {
|
||||
"message": "Fout: geen invulvelden aangetroffen.",
|
||||
"description": "Alert message when no fields are found to fill in."
|
||||
|
|
@ -694,10 +674,6 @@
|
|||
"message": "Websitevoorkeuren",
|
||||
"description": "Site Preferences page header."
|
||||
},
|
||||
"optionsSitePreferencesSettings": {
|
||||
"message": "Instellingen",
|
||||
"description": "Site Preferences settings button text."
|
||||
},
|
||||
"optionsMenuAbout": {
|
||||
"message": "Over",
|
||||
"description": "About page header."
|
||||
|
|
@ -750,10 +726,6 @@
|
|||
"message": "Instellingen exporteren",
|
||||
"description": "Export settings button text."
|
||||
},
|
||||
"optionsButtonResetSettings": {
|
||||
"message": "Alle instellingen opnieuw instellen",
|
||||
"description": "Reset all settings button text."
|
||||
},
|
||||
"optionsLabelDefaultGroup": {
|
||||
"message": "Standaardgroep voor het opslaan van nieuwe wachtwoorden:",
|
||||
"description": "Default group options text."
|
||||
|
|
@ -1058,10 +1030,6 @@
|
|||
"message": "Vereiste KeePassXC-versie: $1",
|
||||
"description": "KeePassXC version requirement text."
|
||||
},
|
||||
"optionsMinimumKeePassXCVersionRequired": {
|
||||
"message": "De extensie vereist KeePassXC-versie: $1. Eerdere versies kunnen ongewenst gedrag veroorzaken.",
|
||||
"description": "KeePassXC minimum version requirement text."
|
||||
},
|
||||
"optionsDefault": {
|
||||
"message": "Standaard: $1",
|
||||
"description": "Default setting text."
|
||||
|
|
@ -1098,10 +1066,6 @@
|
|||
"message": "De huidige instellingen worden overschreven. Weet je zeker dat je het instellingenbestand wilt importeren?",
|
||||
"description": "Import settings confirmation dialog help text."
|
||||
},
|
||||
"optionsResetSettingsDialogText": {
|
||||
"message": "Alle instellingen worden teruggezet naar standaardinstellingen. Weet je het zeker?",
|
||||
"description": "Reset all settings dialog text."
|
||||
},
|
||||
"optionsConnectedDatabasesText": {
|
||||
"message": "Databases verbonden met KeePassXC-Browser.",
|
||||
"description": "Info text about connected databases."
|
||||
|
|
@ -1146,8 +1110,8 @@
|
|||
"message": "Pagina-url",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnFeatures": {
|
||||
"message": "Functies",
|
||||
"optionsColumnIgnore": {
|
||||
"message": "Negeren",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnUsernameOnly": {
|
||||
|
|
@ -1178,10 +1142,6 @@
|
|||
"message": "Automatisch indienen uitschakelen",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionPasskeys": {
|
||||
"message": "Passkeys uitschakelen",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionFull": {
|
||||
"message": "Alle functies uitschakelen",
|
||||
"description": "Site Preferences option selection."
|
||||
|
|
|
|||
|
|
@ -322,10 +322,6 @@
|
|||
"message": "Pole ciągu",
|
||||
"description": "Text for string field."
|
||||
},
|
||||
"defineSubmitButton": {
|
||||
"message": "Przycisk przesyłania",
|
||||
"description": "Text for Submit Button."
|
||||
},
|
||||
"defineChooseUsername": {
|
||||
"message": "Wybierz pole nazwy użytkownika",
|
||||
"description": "Choosing a username field text when choosing Custom Login Fields."
|
||||
|
|
@ -342,13 +338,9 @@
|
|||
"message": "Wybierz pola ciągów",
|
||||
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineChooseSubmitButton": {
|
||||
"message": "Wybierz przycisk przesyłania",
|
||||
"description": "Choose submit button selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineHelpText": {
|
||||
"message": "Potwierdź swój wybór lub wybierz więcej pól jako pola ciągów.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
|
||||
},
|
||||
"defineKeyboardText": {
|
||||
"message": "Możesz także użyć liczb, aby wybrać pola wprowadzania z klawiatury.",
|
||||
|
|
@ -370,10 +362,6 @@
|
|||
"message": "Pola ciągów",
|
||||
"description": "General text for a String Fields."
|
||||
},
|
||||
"submitButton": {
|
||||
"message": "Przycisk przesyłania",
|
||||
"description": "Custom Submit Button text."
|
||||
},
|
||||
"credentialsNoUsername": {
|
||||
"message": "- brak nazwy użytkownika -",
|
||||
"description": "Shown when no username is set in the credentials."
|
||||
|
|
@ -382,10 +370,6 @@
|
|||
"message": "Nie znaleziono danych logowania.",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsBlockedInIframe": {
|
||||
"message": "Wypełnienie danych uwierzytelniających zablokowane w niezaufanej ramce iframe.",
|
||||
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "Znaleziono więcej niż jedne dane logowania w KeePassXC!",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
|
|
@ -406,10 +390,6 @@
|
|||
"message": "Wygasłe",
|
||||
"description": "If a credential is expired, this is appended to the title label."
|
||||
},
|
||||
"credentialsRetrieveButton": {
|
||||
"message": "Pobierz dane uwierzytelniające",
|
||||
"description": "Manual retrieve credentials button text."
|
||||
},
|
||||
"fieldsFill": {
|
||||
"message": "Błąd:\nNie można znaleźć pól do wypełnienia.",
|
||||
"description": "Alert message when no fields are found to fill in."
|
||||
|
|
@ -694,10 +674,6 @@
|
|||
"message": "Ustawienia witryn",
|
||||
"description": "Site Preferences page header."
|
||||
},
|
||||
"optionsSitePreferencesSettings": {
|
||||
"message": "Ustawienia",
|
||||
"description": "Site Preferences settings button text."
|
||||
},
|
||||
"optionsMenuAbout": {
|
||||
"message": "Informacje",
|
||||
"description": "About page header."
|
||||
|
|
@ -750,10 +726,6 @@
|
|||
"message": "Eksportuj ustawienia",
|
||||
"description": "Export settings button text."
|
||||
},
|
||||
"optionsButtonResetSettings": {
|
||||
"message": "Resetuj wszystkie ustawienia",
|
||||
"description": "Reset all settings button text."
|
||||
},
|
||||
"optionsLabelDefaultGroup": {
|
||||
"message": "Domyślna grupa do zapisywania nowych haseł:",
|
||||
"description": "Default group options text."
|
||||
|
|
@ -963,7 +935,7 @@
|
|||
"description": "Hide icons alert text text."
|
||||
},
|
||||
"optionsAutoRetrieveCredentialsHelpText": {
|
||||
"message": "KeePassXC-Browser natychmiast pobierze dane uwierzytelniające po aktywowaniu karty.",
|
||||
"message": "KeePassXC-Browser natychmiast odbierze dane uwierzytelniające po aktywowaniu karty.",
|
||||
"description": "Auto-Retrive Credentials option help text."
|
||||
},
|
||||
"optionsAutomaticReconnectHelpText": {
|
||||
|
|
@ -1058,10 +1030,6 @@
|
|||
"message": "Wymaga KeePassXC w wersji: $1",
|
||||
"description": "KeePassXC version requirement text."
|
||||
},
|
||||
"optionsMinimumKeePassXCVersionRequired": {
|
||||
"message": "Rozszerzenie wymaga KeePassXC w wersji $1. Starsze wersje mogą powodować niepożądane zachowania.",
|
||||
"description": "KeePassXC minimum version requirement text."
|
||||
},
|
||||
"optionsDefault": {
|
||||
"message": "Domyślne: $1",
|
||||
"description": "Default setting text."
|
||||
|
|
@ -1098,10 +1066,6 @@
|
|||
"message": "Bieżące ustawienia zostaną zastąpione. Czy na pewno chcesz zaimportować plik ustawień?",
|
||||
"description": "Import settings confirmation dialog help text."
|
||||
},
|
||||
"optionsResetSettingsDialogText": {
|
||||
"message": "Wszystkie ustawienia zostaną przywrócone do wartości domyślnych. Czy na pewno chcesz to zrobić?",
|
||||
"description": "Reset all settings dialog text."
|
||||
},
|
||||
"optionsConnectedDatabasesText": {
|
||||
"message": "Bazy danych podłączone do KeePassXC-Browser.",
|
||||
"description": "Info text about connected databases."
|
||||
|
|
@ -1146,8 +1110,8 @@
|
|||
"message": "Adres URL strony",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnFeatures": {
|
||||
"message": "Funkcje",
|
||||
"optionsColumnIgnore": {
|
||||
"message": "Ignorowanie",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnUsernameOnly": {
|
||||
|
|
@ -1178,10 +1142,6 @@
|
|||
"message": "Wyłącz automatyczne przesyłanie",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionPasskeys": {
|
||||
"message": "Wyłącz klucze dostępu",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionFull": {
|
||||
"message": "Wyłącz wszystkie funkcje",
|
||||
"description": "Site Preferences option selection."
|
||||
|
|
|
|||
|
|
@ -319,13 +319,9 @@
|
|||
"description": "Clear save data button text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineStringField": {
|
||||
"message": "Campo do Texto",
|
||||
"message": "Campo para texto",
|
||||
"description": "Text for string field."
|
||||
},
|
||||
"defineSubmitButton": {
|
||||
"message": "Botão Enviar",
|
||||
"description": "Text for Submit Button."
|
||||
},
|
||||
"defineChooseUsername": {
|
||||
"message": "Escolha um campo para o nome de usuário",
|
||||
"description": "Choosing a username field text when choosing Custom Login Fields."
|
||||
|
|
@ -342,13 +338,9 @@
|
|||
"message": "Escolha os campos para texto",
|
||||
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineChooseSubmitButton": {
|
||||
"message": "Selecione o botão enviar",
|
||||
"description": "Choose submit button selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineHelpText": {
|
||||
"message": "Por favor, confirme sua seleção ou escolha mais campos como Campos de Texto.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
|
||||
"message": "Confirme a sua seleção ou escolha mais campos como texto.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
|
||||
},
|
||||
"defineKeyboardText": {
|
||||
"message": "Você também pode usar os números para escolher os campos de entrada do teclado.",
|
||||
|
|
@ -370,10 +362,6 @@
|
|||
"message": "Campos para texto",
|
||||
"description": "General text for a String Fields."
|
||||
},
|
||||
"submitButton": {
|
||||
"message": "Botão Enviar",
|
||||
"description": "Custom Submit Button text."
|
||||
},
|
||||
"credentialsNoUsername": {
|
||||
"message": "- sem usuário -",
|
||||
"description": "Shown when no username is set in the credentials."
|
||||
|
|
@ -382,10 +370,6 @@
|
|||
"message": "Nenhum login foi encontrado.",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsBlockedInIframe": {
|
||||
"message": "Preenchimento de credenciais bloqueado em iframe não confiável.",
|
||||
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "Mais de um login foi encontrado no KeePassXC!",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
|
|
@ -406,10 +390,6 @@
|
|||
"message": "Expirado",
|
||||
"description": "If a credential is expired, this is appended to the title label."
|
||||
},
|
||||
"credentialsRetrieveButton": {
|
||||
"message": "Recuperar credenciais",
|
||||
"description": "Manual retrieve credentials button text."
|
||||
},
|
||||
"fieldsFill": {
|
||||
"message": "Erro: Não foi possível encontrar campos para preencher.",
|
||||
"description": "Alert message when no fields are found to fill in."
|
||||
|
|
@ -694,10 +674,6 @@
|
|||
"message": "Preferências do Site",
|
||||
"description": "Site Preferences page header."
|
||||
},
|
||||
"optionsSitePreferencesSettings": {
|
||||
"message": "Configurações",
|
||||
"description": "Site Preferences settings button text."
|
||||
},
|
||||
"optionsMenuAbout": {
|
||||
"message": "Sobre",
|
||||
"description": "About page header."
|
||||
|
|
@ -750,10 +726,6 @@
|
|||
"message": "Exportar as configurações",
|
||||
"description": "Export settings button text."
|
||||
},
|
||||
"optionsButtonResetSettings": {
|
||||
"message": "Redefinir todas as configurações",
|
||||
"description": "Reset all settings button text."
|
||||
},
|
||||
"optionsLabelDefaultGroup": {
|
||||
"message": "Grupo padrão para salvar novas senhas:",
|
||||
"description": "Default group options text."
|
||||
|
|
@ -1058,10 +1030,6 @@
|
|||
"message": "Precisa da versão do KeePassXC: $1",
|
||||
"description": "KeePassXC version requirement text."
|
||||
},
|
||||
"optionsMinimumKeePassXCVersionRequired": {
|
||||
"message": "A extensão requer a versão KeePassXC: $1. Versões anteriores podem causar comportamentos indesejados.",
|
||||
"description": "KeePassXC minimum version requirement text."
|
||||
},
|
||||
"optionsDefault": {
|
||||
"message": "Padrão: $1",
|
||||
"description": "Default setting text."
|
||||
|
|
@ -1098,10 +1066,6 @@
|
|||
"message": "As configurações atuais serão substituídas. Deseja realmente importar o arquivo de configuração?",
|
||||
"description": "Import settings confirmation dialog help text."
|
||||
},
|
||||
"optionsResetSettingsDialogText": {
|
||||
"message": "Todas as configurações serão redefinidas para os padrões. Tem certeza?",
|
||||
"description": "Reset all settings dialog text."
|
||||
},
|
||||
"optionsConnectedDatabasesText": {
|
||||
"message": "Os seguintes bancos de dados do KeePassXC estão conectados ao KeePassXC-Browser.",
|
||||
"description": "Info text about connected databases."
|
||||
|
|
@ -1146,8 +1110,8 @@
|
|||
"message": "URL da página",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnFeatures": {
|
||||
"message": "Características",
|
||||
"optionsColumnIgnore": {
|
||||
"message": "Ignorar",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnUsernameOnly": {
|
||||
|
|
@ -1178,10 +1142,6 @@
|
|||
"message": "Desativar o envio automático",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionPasskeys": {
|
||||
"message": "Desativar chaves de acesso",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionFull": {
|
||||
"message": "Desativar todos os recursos",
|
||||
"description": "Site Preferences option selection."
|
||||
|
|
|
|||
|
|
@ -319,13 +319,9 @@
|
|||
"description": "Clear save data button text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineStringField": {
|
||||
"message": "String Field",
|
||||
"message": "Campo de cadeia",
|
||||
"description": "Text for string field."
|
||||
},
|
||||
"defineSubmitButton": {
|
||||
"message": "Submit Button",
|
||||
"description": "Text for Submit Button."
|
||||
},
|
||||
"defineChooseUsername": {
|
||||
"message": "Escolha o campo para o nome de utilizador",
|
||||
"description": "Choosing a username field text when choosing Custom Login Fields."
|
||||
|
|
@ -342,13 +338,9 @@
|
|||
"message": "Escolha os campos de cadeia",
|
||||
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineChooseSubmitButton": {
|
||||
"message": "Choose submit button",
|
||||
"description": "Choose submit button selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineHelpText": {
|
||||
"message": "Please confirm your selection or choose more fields as String Fields.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
|
||||
"message": "Confirme a sua seleção ou escolha mais campos como campos de cadeia",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
|
||||
},
|
||||
"defineKeyboardText": {
|
||||
"message": "Também pode usar os números para escolher os campos com o teclado",
|
||||
|
|
@ -370,10 +362,6 @@
|
|||
"message": "Campos de cadeia",
|
||||
"description": "General text for a String Fields."
|
||||
},
|
||||
"submitButton": {
|
||||
"message": "Submit Button",
|
||||
"description": "Custom Submit Button text."
|
||||
},
|
||||
"credentialsNoUsername": {
|
||||
"message": "- sem nome de utilizador -",
|
||||
"description": "Shown when no username is set in the credentials."
|
||||
|
|
@ -382,10 +370,6 @@
|
|||
"message": "Não foram encontradas credenciais",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsBlockedInIframe": {
|
||||
"message": "Credential fill blocked in untrusted iframe.",
|
||||
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "Existe mais do que uma credencial guardada no KeePassXC!",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
|
|
@ -406,10 +390,6 @@
|
|||
"message": "Caducada",
|
||||
"description": "If a credential is expired, this is appended to the title label."
|
||||
},
|
||||
"credentialsRetrieveButton": {
|
||||
"message": "Retrieve credentials",
|
||||
"description": "Manual retrieve credentials button text."
|
||||
},
|
||||
"fieldsFill": {
|
||||
"message": "Erro: não foram encontrados campos que possam ser preenchidos",
|
||||
"description": "Alert message when no fields are found to fill in."
|
||||
|
|
@ -694,10 +674,6 @@
|
|||
"message": "Preferências do site",
|
||||
"description": "Site Preferences page header."
|
||||
},
|
||||
"optionsSitePreferencesSettings": {
|
||||
"message": "Settings",
|
||||
"description": "Site Preferences settings button text."
|
||||
},
|
||||
"optionsMenuAbout": {
|
||||
"message": "Acerca",
|
||||
"description": "About page header."
|
||||
|
|
@ -750,10 +726,6 @@
|
|||
"message": "Exportar definições",
|
||||
"description": "Export settings button text."
|
||||
},
|
||||
"optionsButtonResetSettings": {
|
||||
"message": "Reset all settings",
|
||||
"description": "Reset all settings button text."
|
||||
},
|
||||
"optionsLabelDefaultGroup": {
|
||||
"message": "Grupo padrão para as novas palavras-passe:",
|
||||
"description": "Default group options text."
|
||||
|
|
@ -1058,10 +1030,6 @@
|
|||
"message": "Necessita de KeePassXC, versão: $1",
|
||||
"description": "KeePassXC version requirement text."
|
||||
},
|
||||
"optionsMinimumKeePassXCVersionRequired": {
|
||||
"message": "The extension requires KeePassXC version: $1. Earlier versions can cause unwanted behavior.",
|
||||
"description": "KeePassXC minimum version requirement text."
|
||||
},
|
||||
"optionsDefault": {
|
||||
"message": "Padrão: $1",
|
||||
"description": "Default setting text."
|
||||
|
|
@ -1098,10 +1066,6 @@
|
|||
"message": "As definições atuais serão substituídas. Tem a certeza de que deseja importar os dados do ficheiro?",
|
||||
"description": "Import settings confirmation dialog help text."
|
||||
},
|
||||
"optionsResetSettingsDialogText": {
|
||||
"message": "All settings will be reset to defaults. Are you sure?",
|
||||
"description": "Reset all settings dialog text."
|
||||
},
|
||||
"optionsConnectedDatabasesText": {
|
||||
"message": "Bases de dados conectadas com KeePassXC-Browser",
|
||||
"description": "Info text about connected databases."
|
||||
|
|
@ -1146,8 +1110,8 @@
|
|||
"message": "URL da página",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnFeatures": {
|
||||
"message": "Features",
|
||||
"optionsColumnIgnore": {
|
||||
"message": "Ignorar",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnUsernameOnly": {
|
||||
|
|
@ -1178,10 +1142,6 @@
|
|||
"message": "Desativar submissão automática",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionPasskeys": {
|
||||
"message": "Disable passkeys",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionFull": {
|
||||
"message": "Desativar todas as funcionalidades",
|
||||
"description": "Site Preferences option selection."
|
||||
|
|
|
|||
|
|
@ -319,13 +319,9 @@
|
|||
"description": "Clear save data button text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineStringField": {
|
||||
"message": "String Field",
|
||||
"message": "String field",
|
||||
"description": "Text for string field."
|
||||
},
|
||||
"defineSubmitButton": {
|
||||
"message": "Submit Button",
|
||||
"description": "Text for Submit Button."
|
||||
},
|
||||
"defineChooseUsername": {
|
||||
"message": "Choose a username field",
|
||||
"description": "Choosing a username field text when choosing Custom Login Fields."
|
||||
|
|
@ -342,13 +338,9 @@
|
|||
"message": "Choose String Fields",
|
||||
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineChooseSubmitButton": {
|
||||
"message": "Choose submit button",
|
||||
"description": "Choose submit button selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineHelpText": {
|
||||
"message": "Please confirm your selection or choose more fields as String Fields.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
|
||||
"message": "Confirmați selecția sau alegeți mai multe câmpuri ca și câmpuri de tip șir de caractere.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
|
||||
},
|
||||
"defineKeyboardText": {
|
||||
"message": "De asemenea, puteți utiliza numerele pentru a alege câmpurile de intrare de la tastatură.",
|
||||
|
|
@ -370,10 +362,6 @@
|
|||
"message": "String Fields",
|
||||
"description": "General text for a String Fields."
|
||||
},
|
||||
"submitButton": {
|
||||
"message": "Submit Button",
|
||||
"description": "Custom Submit Button text."
|
||||
},
|
||||
"credentialsNoUsername": {
|
||||
"message": "-niciun nume de utilizator-",
|
||||
"description": "Shown when no username is set in the credentials."
|
||||
|
|
@ -382,10 +370,6 @@
|
|||
"message": "Nu s-au găsit autentificări.",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsBlockedInIframe": {
|
||||
"message": "Credential fill blocked in untrusted iframe.",
|
||||
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "În KeePassXC s-au găsit mai multe autentificări!",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
|
|
@ -406,10 +390,6 @@
|
|||
"message": "Expirat",
|
||||
"description": "If a credential is expired, this is appended to the title label."
|
||||
},
|
||||
"credentialsRetrieveButton": {
|
||||
"message": "Retrieve credentials",
|
||||
"description": "Manual retrieve credentials button text."
|
||||
},
|
||||
"fieldsFill": {
|
||||
"message": "Eroare: Imposibil de găsit câmpuri de completat.",
|
||||
"description": "Alert message when no fields are found to fill in."
|
||||
|
|
@ -694,10 +674,6 @@
|
|||
"message": "Site Preferences",
|
||||
"description": "Site Preferences page header."
|
||||
},
|
||||
"optionsSitePreferencesSettings": {
|
||||
"message": "Settings",
|
||||
"description": "Site Preferences settings button text."
|
||||
},
|
||||
"optionsMenuAbout": {
|
||||
"message": "Despre",
|
||||
"description": "About page header."
|
||||
|
|
@ -750,10 +726,6 @@
|
|||
"message": "Setări de export",
|
||||
"description": "Export settings button text."
|
||||
},
|
||||
"optionsButtonResetSettings": {
|
||||
"message": "Reset all settings",
|
||||
"description": "Reset all settings button text."
|
||||
},
|
||||
"optionsLabelDefaultGroup": {
|
||||
"message": "Grup implicit pentru salvarea parolelor noi:",
|
||||
"description": "Default group options text."
|
||||
|
|
@ -1058,10 +1030,6 @@
|
|||
"message": "Necesită versiunea de KeePassXC: $1",
|
||||
"description": "KeePassXC version requirement text."
|
||||
},
|
||||
"optionsMinimumKeePassXCVersionRequired": {
|
||||
"message": "The extension requires KeePassXC version: $1. Earlier versions can cause unwanted behavior.",
|
||||
"description": "KeePassXC minimum version requirement text."
|
||||
},
|
||||
"optionsDefault": {
|
||||
"message": "Implicit: $1",
|
||||
"description": "Default setting text."
|
||||
|
|
@ -1098,10 +1066,6 @@
|
|||
"message": "Setările curente vor fi anulate. Doriți cu adevărat să importați fișierul cu setări?",
|
||||
"description": "Import settings confirmation dialog help text."
|
||||
},
|
||||
"optionsResetSettingsDialogText": {
|
||||
"message": "All settings will be reset to defaults. Are you sure?",
|
||||
"description": "Reset all settings dialog text."
|
||||
},
|
||||
"optionsConnectedDatabasesText": {
|
||||
"message": "Baze de date conectate la KeePassXC-Browser.",
|
||||
"description": "Info text about connected databases."
|
||||
|
|
@ -1146,8 +1110,8 @@
|
|||
"message": "URL Site",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnFeatures": {
|
||||
"message": "Features",
|
||||
"optionsColumnIgnore": {
|
||||
"message": "Dezactivare funcții",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnUsernameOnly": {
|
||||
|
|
@ -1178,10 +1142,6 @@
|
|||
"message": "Dezactivează funcția de Auto-Trimite",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionPasskeys": {
|
||||
"message": "Disable passkeys",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionFull": {
|
||||
"message": "Dezactivează toate funcțiile",
|
||||
"description": "Site Preferences option selection."
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@
|
|||
"description": "Name of the extension."
|
||||
},
|
||||
"addSitePreferenceButtonTitle": {
|
||||
"message": "Добавить настройки сайта для этого URL-адреса.",
|
||||
"message": "Добавить настройки сайта для этого URL.",
|
||||
"description": "Add button title text on Site Preferences tab."
|
||||
},
|
||||
"editSitePreferenceButtonTitle": {
|
||||
"message": "Изменить URL-адрес для этих настроек сайта.",
|
||||
"message": "Edit URL for this Site Preferences.",
|
||||
"description": "Edit button title text on Site Preferences tab."
|
||||
},
|
||||
"allowIframeButtonTitle": {
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
"description": "Connect button title text."
|
||||
},
|
||||
"copyDebugInfoButtonTitle": {
|
||||
"message": "Копировать отладочную информацию в буфер обмена",
|
||||
"message": "Copy debug info to clipboard",
|
||||
"description": "Copy debug info button title text on About tab."
|
||||
},
|
||||
"dismissHttpAuthButtonTitle": {
|
||||
|
|
@ -244,7 +244,7 @@
|
|||
"description": "Error notification shown when not connected to KeePassXC."
|
||||
},
|
||||
"errorCurrentDatabaseNotConnected": {
|
||||
"message": "Текущая база данных не подключена.",
|
||||
"message": "Current database is not connected.",
|
||||
"description": "Error notification shown when current database is not connected during action."
|
||||
},
|
||||
"passwordGeneratorErrorTooLong": {
|
||||
|
|
@ -319,13 +319,9 @@
|
|||
"description": "Clear save data button text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineStringField": {
|
||||
"message": "String Field",
|
||||
"message": "Строковое поле",
|
||||
"description": "Text for string field."
|
||||
},
|
||||
"defineSubmitButton": {
|
||||
"message": "Submit Button",
|
||||
"description": "Text for Submit Button."
|
||||
},
|
||||
"defineChooseUsername": {
|
||||
"message": "Выберите поле логина",
|
||||
"description": "Choosing a username field text when choosing Custom Login Fields."
|
||||
|
|
@ -342,13 +338,9 @@
|
|||
"message": "Выберите строковые поля",
|
||||
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineChooseSubmitButton": {
|
||||
"message": "Choose submit button",
|
||||
"description": "Choose submit button selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineHelpText": {
|
||||
"message": "Please confirm your selection or choose more fields as String Fields.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
|
||||
"message": "Пожалуйста, подтвердите свой выбор или выберите больше строковых полей.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
|
||||
},
|
||||
"defineKeyboardText": {
|
||||
"message": "Вы также можете использовать числа для выбора полей ввода с клавиатуры.",
|
||||
|
|
@ -370,10 +362,6 @@
|
|||
"message": "Строковые поля",
|
||||
"description": "General text for a String Fields."
|
||||
},
|
||||
"submitButton": {
|
||||
"message": "Submit Button",
|
||||
"description": "Custom Submit Button text."
|
||||
},
|
||||
"credentialsNoUsername": {
|
||||
"message": "- нет логина -",
|
||||
"description": "Shown when no username is set in the credentials."
|
||||
|
|
@ -382,10 +370,6 @@
|
|||
"message": "Не найдены логины.",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsBlockedInIframe": {
|
||||
"message": "Заполнение учётных данных заблокировано в ненадежном iframe.",
|
||||
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "В KeePassXC обнаружено более одного логина!",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
|
|
@ -406,10 +390,6 @@
|
|||
"message": "Истекло",
|
||||
"description": "If a credential is expired, this is appended to the title label."
|
||||
},
|
||||
"credentialsRetrieveButton": {
|
||||
"message": "Retrieve credentials",
|
||||
"description": "Manual retrieve credentials button text."
|
||||
},
|
||||
"fieldsFill": {
|
||||
"message": "ОШИБКА: нет полей для заполнения.",
|
||||
"description": "Alert message when no fields are found to fill in."
|
||||
|
|
@ -647,7 +627,7 @@
|
|||
"description": "Dismiss button text when in HTTP Authentication popup."
|
||||
},
|
||||
"optionsDefaultSettingsTitle": {
|
||||
"message": "Настройки по умолчанию",
|
||||
"message": "Default settings",
|
||||
"description": "Default settings title for Getting Started page."
|
||||
},
|
||||
"optionsWelcomeTitle": {
|
||||
|
|
@ -694,10 +674,6 @@
|
|||
"message": "Настройки сайта",
|
||||
"description": "Site Preferences page header."
|
||||
},
|
||||
"optionsSitePreferencesSettings": {
|
||||
"message": "Настройки",
|
||||
"description": "Site Preferences settings button text."
|
||||
},
|
||||
"optionsMenuAbout": {
|
||||
"message": "Сведения",
|
||||
"description": "About page header."
|
||||
|
|
@ -723,7 +699,7 @@
|
|||
"description": "Add button text."
|
||||
},
|
||||
"optionsButtonEdit": {
|
||||
"message": "Изменить",
|
||||
"message": "Edit",
|
||||
"description": "Edit button text."
|
||||
},
|
||||
"optionsButtonUpdate": {
|
||||
|
|
@ -750,10 +726,6 @@
|
|||
"message": "Экспорт настроек",
|
||||
"description": "Export settings button text."
|
||||
},
|
||||
"optionsButtonResetSettings": {
|
||||
"message": "Сбросить все настройки",
|
||||
"description": "Reset all settings button text."
|
||||
},
|
||||
"optionsLabelDefaultGroup": {
|
||||
"message": "Группа по умолчанию для сохранения новых паролей:",
|
||||
"description": "Default group options text."
|
||||
|
|
@ -811,7 +783,7 @@
|
|||
"description": "Automatically fill-in single credential entry checkbox text."
|
||||
},
|
||||
"optionsCheckboxAutoFillRelevantCredential": {
|
||||
"message": "Автоматически заполнять соответствующие записи учётных данных",
|
||||
"message": "Automatically fill in relevant credential entries",
|
||||
"description": "Automatically fill-in relevant credential entry checkbox text."
|
||||
},
|
||||
"optionsCheckboxAutoFillSingleTotp": {
|
||||
|
|
@ -855,11 +827,11 @@
|
|||
"description": "Show login notifications checkbox text."
|
||||
},
|
||||
"optionsDefaultPasswordManager": {
|
||||
"message": "Установить как менеджер паролей по умолчанию",
|
||||
"message": "Set as default password manager",
|
||||
"description": "Default password manager checkbox text."
|
||||
},
|
||||
"optionsDefaultPasswordManagerHelpText": {
|
||||
"message": "Устанавливает KeePassXC-Browser в качестве менеджера паролей по умолчанию для браузера.",
|
||||
"message": "Sets KeePassXC-Browser as the default password manager for the browser.",
|
||||
"description": "Default password manager help text."
|
||||
},
|
||||
"optionsRedirectAllowance": {
|
||||
|
|
@ -959,7 +931,7 @@
|
|||
"description": "OTP field icon option help text."
|
||||
},
|
||||
"optionsHideIconsAlertText": {
|
||||
"message": "При необходимости вы также можете скрыть значки вручную, нажав Shift+клик.",
|
||||
"message": "You can also hide icons manually when needed with shift+click.",
|
||||
"description": "Hide icons alert text text."
|
||||
},
|
||||
"optionsAutoRetrieveCredentialsHelpText": {
|
||||
|
|
@ -975,7 +947,7 @@
|
|||
"description": "Auto-Fill Single Entry option help text."
|
||||
},
|
||||
"optionsAutoFillRelevantCredentialHelpText": {
|
||||
"message": "Автоматически заполнить соответствующие учётные данные, если требуется повторное заполнение.",
|
||||
"message": "Automatically fill in the relevant credential if a second fill is needed.",
|
||||
"description": "Auto-Fill Relevant Credential option help text."
|
||||
},
|
||||
"optionsAutoFillSingleEntryWarning": {
|
||||
|
|
@ -1058,10 +1030,6 @@
|
|||
"message": "Требуемая версия KeePassXC: $1",
|
||||
"description": "KeePassXC version requirement text."
|
||||
},
|
||||
"optionsMinimumKeePassXCVersionRequired": {
|
||||
"message": "The extension requires KeePassXC version: $1. Earlier versions can cause unwanted behavior.",
|
||||
"description": "KeePassXC minimum version requirement text."
|
||||
},
|
||||
"optionsDefault": {
|
||||
"message": "По умолчанию: $1",
|
||||
"description": "Default setting text."
|
||||
|
|
@ -1098,10 +1066,6 @@
|
|||
"message": "Текущие настройки будут переопределены. Вы действительно хотите импортировать файл настроек?",
|
||||
"description": "Import settings confirmation dialog help text."
|
||||
},
|
||||
"optionsResetSettingsDialogText": {
|
||||
"message": "Все настройки будут сброшены на значения по умолчанию. Вы уверены?",
|
||||
"description": "Reset all settings dialog text."
|
||||
},
|
||||
"optionsConnectedDatabasesText": {
|
||||
"message": "Базы данных, подключенные к KeePassXC-Browser.",
|
||||
"description": "Info text about connected databases."
|
||||
|
|
@ -1146,8 +1110,8 @@
|
|||
"message": "Адрес страницы",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnFeatures": {
|
||||
"message": "Функции",
|
||||
"optionsColumnIgnore": {
|
||||
"message": "Игнорировать",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnUsernameOnly": {
|
||||
|
|
@ -1178,10 +1142,6 @@
|
|||
"message": "Выключить автоподстановку",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionPasskeys": {
|
||||
"message": "Отключить ключи доступа",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionFull": {
|
||||
"message": "Отключить все функции",
|
||||
"description": "Site Preferences option selection."
|
||||
|
|
@ -1435,31 +1395,31 @@
|
|||
"description": "Lock database button title text."
|
||||
},
|
||||
"welcomeText": {
|
||||
"message": "Добро пожаловать в KeePassXC-Browser!",
|
||||
"message": "Welcome to KeePassXC-Browser!",
|
||||
"description": "Main title of Getting Started page."
|
||||
},
|
||||
"documentationGettingStarted": {
|
||||
"message": "Наше руководство по началу работы поможет вам быстро приступить к работе.",
|
||||
"message": "Our Getting Started Guide will get you up and running quickly.",
|
||||
"description": "Getting Started document text."
|
||||
},
|
||||
"documentationUserGuide": {
|
||||
"message": "Ищете более подробную документацию? Наше руководство пользователя вам поможет.",
|
||||
"message": "Looking for more comprehensive documentation? Our User Guide is there to help.",
|
||||
"description": "User Guide text."
|
||||
},
|
||||
"documentationTroubleshootingGuide": {
|
||||
"message": "Нужна помощь в устранении неполадок интеграции с браузером? Ознакомьтесь с руководством по устранению неполадок.",
|
||||
"message": "Need help troubleshooting the browser integration? Check the Troubleshooting Guide.",
|
||||
"description": "Troubleshooting Guide text."
|
||||
},
|
||||
"gettingStartedWelcomeText": {
|
||||
"message": "Добро пожаловать в KeePassXC-Browser, официальное расширение браузера для KeePassXC.",
|
||||
"message": "Welcome to KeePassXC-Browser, the official browser extension for KeePassXC.",
|
||||
"description": "Welcome to KeePassXC-Browser, the official browser extension for KeePassXC."
|
||||
},
|
||||
"gettingStartedSecondWelcomeText": {
|
||||
"message": "Пожалуйста, проверьте настройки по умолчанию и выберите все предпочтительные параметры.",
|
||||
"message": "Please go through the default settings and check all your preferred options.",
|
||||
"description": "Please go through the default settings and check all your preferred options."
|
||||
},
|
||||
"gettingStartedNewUser": {
|
||||
"message": "Вы новый пользователь? Посмотрите ссылки на нашу документацию.",
|
||||
"message": "Are you a new user? Check links to our documentation.",
|
||||
"description": "Are you a new user? Check links to our documentation."
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -322,10 +322,6 @@
|
|||
"message": "Strängfält",
|
||||
"description": "Text for string field."
|
||||
},
|
||||
"defineSubmitButton": {
|
||||
"message": "Skicka-knapp",
|
||||
"description": "Text for Submit Button."
|
||||
},
|
||||
"defineChooseUsername": {
|
||||
"message": "Välj ett användarnamn-fält",
|
||||
"description": "Choosing a username field text when choosing Custom Login Fields."
|
||||
|
|
@ -342,13 +338,9 @@
|
|||
"message": "Välj sträng-fält",
|
||||
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineChooseSubmitButton": {
|
||||
"message": "Välj Skicka-knapp",
|
||||
"description": "Choose submit button selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineHelpText": {
|
||||
"message": "Bekräfta ditt val eller välj fler fält som strängfält.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
|
||||
"message": "Vänligen bekräfta ditt val eller välj fler fält som <em>Strängfält</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.",
|
||||
|
|
@ -370,10 +362,6 @@
|
|||
"message": "Strängfält",
|
||||
"description": "General text for a String Fields."
|
||||
},
|
||||
"submitButton": {
|
||||
"message": "Skicka-knapp",
|
||||
"description": "Custom Submit Button text."
|
||||
},
|
||||
"credentialsNoUsername": {
|
||||
"message": "- inget användarnamn -",
|
||||
"description": "Shown when no username is set in the credentials."
|
||||
|
|
@ -382,10 +370,6 @@
|
|||
"message": "Fel:\nInga inloggningar hittades.",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsBlockedInIframe": {
|
||||
"message": "Ifyllning av inloggningsuppgifter blockerad av otillförlitlig iframe.",
|
||||
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "Fel:\nMer än en inloggning hittades i KeePassXC!\nTryck på ikonen för KeePassXC-Browser för fler alternativ.",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
|
|
@ -406,10 +390,6 @@
|
|||
"message": "Förfallen",
|
||||
"description": "If a credential is expired, this is appended to the title label."
|
||||
},
|
||||
"credentialsRetrieveButton": {
|
||||
"message": "Hämta autentiseringsuppgifter",
|
||||
"description": "Manual retrieve credentials button text."
|
||||
},
|
||||
"fieldsFill": {
|
||||
"message": "Fel:\nKan inte hitta fält att fylla i.",
|
||||
"description": "Alert message when no fields are found to fill in."
|
||||
|
|
@ -694,10 +674,6 @@
|
|||
"message": "Sidinställningar",
|
||||
"description": "Site Preferences page header."
|
||||
},
|
||||
"optionsSitePreferencesSettings": {
|
||||
"message": "Inställningar",
|
||||
"description": "Site Preferences settings button text."
|
||||
},
|
||||
"optionsMenuAbout": {
|
||||
"message": "Om",
|
||||
"description": "About page header."
|
||||
|
|
@ -750,10 +726,6 @@
|
|||
"message": "Exportera inställningar",
|
||||
"description": "Export settings button text."
|
||||
},
|
||||
"optionsButtonResetSettings": {
|
||||
"message": "Återställ alla inställningar",
|
||||
"description": "Reset all settings button text."
|
||||
},
|
||||
"optionsLabelDefaultGroup": {
|
||||
"message": "Default group for saving new passwords:",
|
||||
"description": "Default group options text."
|
||||
|
|
@ -1058,10 +1030,6 @@
|
|||
"message": "Kräver KeePassXC version: $1",
|
||||
"description": "KeePassXC version requirement text."
|
||||
},
|
||||
"optionsMinimumKeePassXCVersionRequired": {
|
||||
"message": "The extension requires KeePassXC version: $1. Earlier versions can cause unwanted behavior.",
|
||||
"description": "KeePassXC minimum version requirement text."
|
||||
},
|
||||
"optionsDefault": {
|
||||
"message": "Förinställt: $1",
|
||||
"description": "Default setting text."
|
||||
|
|
@ -1098,10 +1066,6 @@
|
|||
"message": "Nuvarande inställningar kommer att skrivas över. Vill du verkligen importera inställningsfilen?",
|
||||
"description": "Import settings confirmation dialog help text."
|
||||
},
|
||||
"optionsResetSettingsDialogText": {
|
||||
"message": "Alla inställningar kommer att återställas till standardinställning. Är du säker?",
|
||||
"description": "Reset all settings dialog text."
|
||||
},
|
||||
"optionsConnectedDatabasesText": {
|
||||
"message": "Följande KeePassXC-databaser är anslutna till KeePassXC-Browser.",
|
||||
"description": "Info text about connected databases."
|
||||
|
|
@ -1146,8 +1110,8 @@
|
|||
"message": "Sid-URL",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnFeatures": {
|
||||
"message": "Funktioner",
|
||||
"optionsColumnIgnore": {
|
||||
"message": "Ignorera",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnUsernameOnly": {
|
||||
|
|
@ -1178,10 +1142,6 @@
|
|||
"message": "Skicka inte automatiskt",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionPasskeys": {
|
||||
"message": "Avaktivera passnycklar",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionFull": {
|
||||
"message": "Inaktivera alla funktioner",
|
||||
"description": "Site Preferences option selection."
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"extensionDescription": {
|
||||
"message": "Modern tarayıcılar için KeePassXC bütünleştirmesi",
|
||||
"message": "Modern tarayıcılar için KeePassXC bütünleşmesi",
|
||||
"description": "Name of the extension."
|
||||
},
|
||||
"addSitePreferenceButtonTitle": {
|
||||
|
|
@ -319,13 +319,9 @@
|
|||
"description": "Clear save data button text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineStringField": {
|
||||
"message": "Yazı alanı",
|
||||
"message": "Metin alanı",
|
||||
"description": "Text for string field."
|
||||
},
|
||||
"defineSubmitButton": {
|
||||
"message": "Gönder düğmesi",
|
||||
"description": "Text for Submit Button."
|
||||
},
|
||||
"defineChooseUsername": {
|
||||
"message": "Kullanıcı adı alanını seçin",
|
||||
"description": "Choosing a username field text when choosing Custom Login Fields."
|
||||
|
|
@ -339,16 +335,12 @@
|
|||
"description": "Choosing a TOTP field text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineChooseStringFields": {
|
||||
"message": "Yazı alanlarını seçin",
|
||||
"message": "Metin alanlarını seçin",
|
||||
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineChooseSubmitButton": {
|
||||
"message": "Gönder düğmesini seçin",
|
||||
"description": "Choose submit button selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineHelpText": {
|
||||
"message": "Lütfen seçiminizi onaylayın ya da yazı alanları olarak ek alanlar seçin.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
|
||||
"message": "Lütfen seçiminizi onaylayın ya da dizge alanları olarak ek alanlar seçin.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
|
||||
},
|
||||
"defineKeyboardText": {
|
||||
"message": "Giriş alanlarını tuş takımıyla seçmek için sayıları kullanabilirsiniz.",
|
||||
|
|
@ -367,13 +359,9 @@
|
|||
"description": "General text for password."
|
||||
},
|
||||
"stringFields": {
|
||||
"message": "Yazı alanları",
|
||||
"message": "Metin alanları",
|
||||
"description": "General text for a String Fields."
|
||||
},
|
||||
"submitButton": {
|
||||
"message": "Gönder düğmesi",
|
||||
"description": "Custom Submit Button text."
|
||||
},
|
||||
"credentialsNoUsername": {
|
||||
"message": "- kullanıcı adı yok -",
|
||||
"description": "Shown when no username is set in the credentials."
|
||||
|
|
@ -382,10 +370,6 @@
|
|||
"message": "Herhangi bir oturum açma bulunamadı.",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsBlockedInIframe": {
|
||||
"message": "Güvenilmeyen bir iFrame nedeniyle kimlik doğrulama bilgilerinin doldurulması engellendi.",
|
||||
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "KeePassXC üzerinde birden fazla oturum açma bulundu!",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
|
|
@ -406,10 +390,6 @@
|
|||
"message": "Süresi dolmuş",
|
||||
"description": "If a credential is expired, this is appended to the title label."
|
||||
},
|
||||
"credentialsRetrieveButton": {
|
||||
"message": "Kimlik doğrulama bilgilerini al",
|
||||
"description": "Manual retrieve credentials button text."
|
||||
},
|
||||
"fieldsFill": {
|
||||
"message": "Hata: Doldurulacak alanlar bulunamadı.",
|
||||
"description": "Alert message when no fields are found to fill in."
|
||||
|
|
@ -419,7 +399,7 @@
|
|||
"description": "Message shown when no password fields are found."
|
||||
},
|
||||
"fieldsPasswordFillNotAccepted": {
|
||||
"message": "Parolanın bir düz yazı alanına yazılması engellenir.",
|
||||
"message": "Parolanın bir düz metin alanına yazılması engellenir.",
|
||||
"description": "Message shown when password fill to a plain text field is prevented."
|
||||
},
|
||||
"rememberNothingChanged": {
|
||||
|
|
@ -694,10 +674,6 @@
|
|||
"message": "Site ayarları",
|
||||
"description": "Site Preferences page header."
|
||||
},
|
||||
"optionsSitePreferencesSettings": {
|
||||
"message": "Ayarlar",
|
||||
"description": "Site Preferences settings button text."
|
||||
},
|
||||
"optionsMenuAbout": {
|
||||
"message": "Hakkında",
|
||||
"description": "About page header."
|
||||
|
|
@ -750,10 +726,6 @@
|
|||
"message": "Ayarları dışa aktar",
|
||||
"description": "Export settings button text."
|
||||
},
|
||||
"optionsButtonResetSettings": {
|
||||
"message": "Tüm ayarları sıfırla",
|
||||
"description": "Reset all settings button text."
|
||||
},
|
||||
"optionsLabelDefaultGroup": {
|
||||
"message": "Yeni parolaların kaydedileceği varsayılan grup:",
|
||||
"description": "Default group options text."
|
||||
|
|
@ -1058,10 +1030,6 @@
|
|||
"message": "KeePassXC $1 sürümünü gerektirir",
|
||||
"description": "KeePassXC version requirement text."
|
||||
},
|
||||
"optionsMinimumKeePassXCVersionRequired": {
|
||||
"message": "Bu eklenti için KeePassXC $1 sürümü gereklidir. Önceki sürümler istenmeyen davranışlara yol açabilir.",
|
||||
"description": "KeePassXC minimum version requirement text."
|
||||
},
|
||||
"optionsDefault": {
|
||||
"message": "Varsayılan: $1",
|
||||
"description": "Default setting text."
|
||||
|
|
@ -1098,10 +1066,6 @@
|
|||
"message": "Var olan ayarlar değiştirilecek. Ayarlar dosyasını içe aktarmak istediğinize emin misiniz?",
|
||||
"description": "Import settings confirmation dialog help text."
|
||||
},
|
||||
"optionsResetSettingsDialogText": {
|
||||
"message": "Tüm ayarları varsayılan değerlerine sıfırlamak istediğinize emin misiniz? ",
|
||||
"description": "Reset all settings dialog text."
|
||||
},
|
||||
"optionsConnectedDatabasesText": {
|
||||
"message": "KeePassXC-Browser ile bağlantısı kurulmuş KeePassXC veri tabanları.",
|
||||
"description": "Info text about connected databases."
|
||||
|
|
@ -1146,8 +1110,8 @@
|
|||
"message": "Sayfa adresi",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnFeatures": {
|
||||
"message": "Özellikler",
|
||||
"optionsColumnIgnore": {
|
||||
"message": "Yok say",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnUsernameOnly": {
|
||||
|
|
@ -1178,10 +1142,6 @@
|
|||
"message": "Otomatik gönderim yapılmasın",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionPasskeys": {
|
||||
"message": "Geçiş anahtarları kullanılmasın",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionFull": {
|
||||
"message": "Tüm özellikleri kapat",
|
||||
"description": "Site Preferences option selection."
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@
|
|||
"description": "Credential is excluded."
|
||||
},
|
||||
"errorMessagePasskeysRequestCanceled": {
|
||||
"message": "Запит на використання ключів доступу скасовано.",
|
||||
"message": "Запит на Passkeys-ключі скасовано.",
|
||||
"description": "Passkeys request canceled."
|
||||
},
|
||||
"errorMessagePasskeysInvalidUserVerification": {
|
||||
|
|
@ -224,7 +224,7 @@
|
|||
"description": "Wait for timer to expire."
|
||||
},
|
||||
"errorMessagePasskeysUnknownError": {
|
||||
"message": "Невідома помилка ключів доступу.",
|
||||
"message": "Невідома помилка паролів.",
|
||||
"description": "Unknown passkeys error."
|
||||
},
|
||||
"errorMessagePasskeysInvalidChallenge": {
|
||||
|
|
@ -322,10 +322,6 @@
|
|||
"message": "Поле рядка",
|
||||
"description": "Text for string field."
|
||||
},
|
||||
"defineSubmitButton": {
|
||||
"message": "Кнопка «Відправити»",
|
||||
"description": "Text for Submit Button."
|
||||
},
|
||||
"defineChooseUsername": {
|
||||
"message": "Виберіть поле імені користувача",
|
||||
"description": "Choosing a username field text when choosing Custom Login Fields."
|
||||
|
|
@ -342,13 +338,9 @@
|
|||
"message": "Оберіть поля рядків",
|
||||
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineChooseSubmitButton": {
|
||||
"message": "Виберіть кнопку «Відправити»",
|
||||
"description": "Choose submit button selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineHelpText": {
|
||||
"message": "Підтвердьте свій вибір або виберіть більше полів як рядкові поля.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
|
||||
"message": "Підтвердьте свій вибір або виберіть більше полів як рядків.",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
|
||||
},
|
||||
"defineKeyboardText": {
|
||||
"message": "Ви також можете використовувати числа для вибору полів введення з клавіатури.",
|
||||
|
|
@ -370,10 +362,6 @@
|
|||
"message": "Поля рядків",
|
||||
"description": "General text for a String Fields."
|
||||
},
|
||||
"submitButton": {
|
||||
"message": "Кнопка «Відправити»",
|
||||
"description": "Custom Submit Button text."
|
||||
},
|
||||
"credentialsNoUsername": {
|
||||
"message": "- без імені користувача -",
|
||||
"description": "Shown when no username is set in the credentials."
|
||||
|
|
@ -382,10 +370,6 @@
|
|||
"message": "Не знайдено облікових даних.",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsBlockedInIframe": {
|
||||
"message": "Заповнення облікових даних заблоковано в ненадійному iframe.",
|
||||
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "Знайдено більше ніж один запис у KeePassXC!",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
|
|
@ -406,10 +390,6 @@
|
|||
"message": "Протерміновано",
|
||||
"description": "If a credential is expired, this is appended to the title label."
|
||||
},
|
||||
"credentialsRetrieveButton": {
|
||||
"message": "Отримати облікові дані",
|
||||
"description": "Manual retrieve credentials button text."
|
||||
},
|
||||
"fieldsFill": {
|
||||
"message": "Помилка: Не вдалося знайти поля для заповнення.",
|
||||
"description": "Alert message when no fields are found to fill in."
|
||||
|
|
@ -694,10 +674,6 @@
|
|||
"message": "Налаштування сайту",
|
||||
"description": "Site Preferences page header."
|
||||
},
|
||||
"optionsSitePreferencesSettings": {
|
||||
"message": "Налаштування",
|
||||
"description": "Site Preferences settings button text."
|
||||
},
|
||||
"optionsMenuAbout": {
|
||||
"message": "Про розширення",
|
||||
"description": "About page header."
|
||||
|
|
@ -750,10 +726,6 @@
|
|||
"message": "Експортувати налаштування",
|
||||
"description": "Export settings button text."
|
||||
},
|
||||
"optionsButtonResetSettings": {
|
||||
"message": "Скинути всі налаштування",
|
||||
"description": "Reset all settings button text."
|
||||
},
|
||||
"optionsLabelDefaultGroup": {
|
||||
"message": "Типова група для збереження нових паролів:",
|
||||
"description": "Default group options text."
|
||||
|
|
@ -1058,10 +1030,6 @@
|
|||
"message": "Потребує версію KeePassXC: $1",
|
||||
"description": "KeePassXC version requirement text."
|
||||
},
|
||||
"optionsMinimumKeePassXCVersionRequired": {
|
||||
"message": "Для розширення потрібна версія KeePassXC: $1. Більш ранні версії можуть спричинити небажану поведінку.",
|
||||
"description": "KeePassXC minimum version requirement text."
|
||||
},
|
||||
"optionsDefault": {
|
||||
"message": "Типово: $1",
|
||||
"description": "Default setting text."
|
||||
|
|
@ -1098,10 +1066,6 @@
|
|||
"message": "Поточні налаштування будуть перевизначені. Ви дійсно хочете імпортувати файл налаштувань?",
|
||||
"description": "Import settings confirmation dialog help text."
|
||||
},
|
||||
"optionsResetSettingsDialogText": {
|
||||
"message": "Всі налаштування будуть скинуті. Ви впевнені?",
|
||||
"description": "Reset all settings dialog text."
|
||||
},
|
||||
"optionsConnectedDatabasesText": {
|
||||
"message": "Під'єднані бази даних до KeePassXC-Browser.",
|
||||
"description": "Info text about connected databases."
|
||||
|
|
@ -1146,8 +1110,8 @@
|
|||
"message": "URL-адреса сторінки",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnFeatures": {
|
||||
"message": "Особливості",
|
||||
"optionsColumnIgnore": {
|
||||
"message": "Ігнорувати",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnUsernameOnly": {
|
||||
|
|
@ -1178,10 +1142,6 @@
|
|||
"message": "Вимкнути автовідправлення",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionPasskeys": {
|
||||
"message": "Вимкнути ключі доступу",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionFull": {
|
||||
"message": "Вимкнути всі можливості",
|
||||
"description": "Site Preferences option selection."
|
||||
|
|
@ -1399,11 +1359,11 @@
|
|||
"description": "Extension title in settings page"
|
||||
},
|
||||
"optionsPasskeysTitle": {
|
||||
"message": "Ключі доступу",
|
||||
"message": "Passkey-ключі",
|
||||
"description": "Passkeys settings title in settings page."
|
||||
},
|
||||
"optionsPasskeysEnable": {
|
||||
"message": "Увімкнути ключі доступу",
|
||||
"message": "Увімкнути паролі",
|
||||
"description": "Enabled passkeys option text."
|
||||
},
|
||||
"optionsPasskeysEnableHelpText": {
|
||||
|
|
@ -1411,11 +1371,11 @@
|
|||
"description": "Passkeys option help text."
|
||||
},
|
||||
"optionsPasskeysEnableFallback": {
|
||||
"message": "Увімкнути резервний метод для ключів доступу",
|
||||
"message": "Увімкнути резервний варіант паролів",
|
||||
"description": "Enabled passkeys fallback option text."
|
||||
},
|
||||
"optionsPasskeysEnableFallbackHelpText": {
|
||||
"message": "Якщо увімкнено, невдалий або скасований запит до KeePassXC спричинить власний внутрішній запит браузера на ключі доступу. Якщо вимкнено, необхідне підключення до KeePassXC, а скасований запит буде невдалим. За замовчуванням: увімкнено.",
|
||||
"message": "Коли увімкнено, невдала або скасована спроба запиту до KeePassXC викликатиме запит на паролі через внутрішню систему браузера. Якщо вимкнено, для підключення до KeePassXC буде потрібно, і скасований запит зазнає невдачі. За замовчуванням: увімкнено.",
|
||||
"description": "Passkeys fallback option help text."
|
||||
},
|
||||
"openNewTab": {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -322,10 +322,6 @@
|
|||
"message": "字符串字段",
|
||||
"description": "Text for string field."
|
||||
},
|
||||
"defineSubmitButton": {
|
||||
"message": "提交按钮",
|
||||
"description": "Text for Submit Button."
|
||||
},
|
||||
"defineChooseUsername": {
|
||||
"message": "选择用户名字段",
|
||||
"description": "Choosing a username field text when choosing Custom Login Fields."
|
||||
|
|
@ -342,13 +338,9 @@
|
|||
"message": "选择字符串字段",
|
||||
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineChooseSubmitButton": {
|
||||
"message": "选择提交按钮",
|
||||
"description": "Choose submit button selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineHelpText": {
|
||||
"message": "请确认您的选择,或选择更多字段作为字符串字段。",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
|
||||
},
|
||||
"defineKeyboardText": {
|
||||
"message": "您也可以使用键盘的数字键选择输入字段。",
|
||||
|
|
@ -370,10 +362,6 @@
|
|||
"message": "字符串字段",
|
||||
"description": "General text for a String Fields."
|
||||
},
|
||||
"submitButton": {
|
||||
"message": "提交按钮",
|
||||
"description": "Custom Submit Button text."
|
||||
},
|
||||
"credentialsNoUsername": {
|
||||
"message": "- 没有用户名 -",
|
||||
"description": "Shown when no username is set in the credentials."
|
||||
|
|
@ -382,10 +370,6 @@
|
|||
"message": "未找到登录信息。",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsBlockedInIframe": {
|
||||
"message": "凭据填充在不受信任的 iframe 中被阻止。",
|
||||
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "在 KeePassXC 中找到多条登录信息!",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
|
|
@ -406,10 +390,6 @@
|
|||
"message": "已过期",
|
||||
"description": "If a credential is expired, this is appended to the title label."
|
||||
},
|
||||
"credentialsRetrieveButton": {
|
||||
"message": "检索凭据",
|
||||
"description": "Manual retrieve credentials button text."
|
||||
},
|
||||
"fieldsFill": {
|
||||
"message": "错误:无法找到可填写的字段。",
|
||||
"description": "Alert message when no fields are found to fill in."
|
||||
|
|
@ -694,10 +674,6 @@
|
|||
"message": "网站设置",
|
||||
"description": "Site Preferences page header."
|
||||
},
|
||||
"optionsSitePreferencesSettings": {
|
||||
"message": "设置",
|
||||
"description": "Site Preferences settings button text."
|
||||
},
|
||||
"optionsMenuAbout": {
|
||||
"message": "关于",
|
||||
"description": "About page header."
|
||||
|
|
@ -750,10 +726,6 @@
|
|||
"message": "导出配置",
|
||||
"description": "Export settings button text."
|
||||
},
|
||||
"optionsButtonResetSettings": {
|
||||
"message": "重置所有设置",
|
||||
"description": "Reset all settings button text."
|
||||
},
|
||||
"optionsLabelDefaultGroup": {
|
||||
"message": "保存新密码的默认群组:",
|
||||
"description": "Default group options text."
|
||||
|
|
@ -1058,10 +1030,6 @@
|
|||
"message": "需要 KeePassXC 版本:$1",
|
||||
"description": "KeePassXC version requirement text."
|
||||
},
|
||||
"optionsMinimumKeePassXCVersionRequired": {
|
||||
"message": "该扩展需要 KeePassXC 版本:$1。早期版本可能会导致意外行为。",
|
||||
"description": "KeePassXC minimum version requirement text."
|
||||
},
|
||||
"optionsDefault": {
|
||||
"message": "默认值:$1",
|
||||
"description": "Default setting text."
|
||||
|
|
@ -1098,10 +1066,6 @@
|
|||
"message": "当前配置将会被覆盖,确定导入?",
|
||||
"description": "Import settings confirmation dialog help text."
|
||||
},
|
||||
"optionsResetSettingsDialogText": {
|
||||
"message": "所有设置都将重置为默认。您确定吗?",
|
||||
"description": "Reset all settings dialog text."
|
||||
},
|
||||
"optionsConnectedDatabasesText": {
|
||||
"message": "已连接到 KeePassXC-Browser 的数据库。",
|
||||
"description": "Info text about connected databases."
|
||||
|
|
@ -1146,8 +1110,8 @@
|
|||
"message": "页面 URL",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnFeatures": {
|
||||
"message": "功能",
|
||||
"optionsColumnIgnore": {
|
||||
"message": "忽略",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnUsernameOnly": {
|
||||
|
|
@ -1178,10 +1142,6 @@
|
|||
"message": "禁用自动提交",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionPasskeys": {
|
||||
"message": "禁用通行密钥",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionFull": {
|
||||
"message": "禁用所有功能",
|
||||
"description": "Site Preferences option selection."
|
||||
|
|
|
|||
|
|
@ -303,7 +303,7 @@
|
|||
"description": "More button text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineReset": {
|
||||
"message": "重設",
|
||||
"message": "重置",
|
||||
"description": "Reset button text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineConfirm": {
|
||||
|
|
@ -322,10 +322,6 @@
|
|||
"message": "字串欄位",
|
||||
"description": "Text for string field."
|
||||
},
|
||||
"defineSubmitButton": {
|
||||
"message": "送出按鈕",
|
||||
"description": "Text for Submit Button."
|
||||
},
|
||||
"defineChooseUsername": {
|
||||
"message": "選擇用戶名稱欄位",
|
||||
"description": "Choosing a username field text when choosing Custom Login Fields."
|
||||
|
|
@ -342,13 +338,9 @@
|
|||
"message": "選擇字串欄位",
|
||||
"description": "Choose String Fields a selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineChooseSubmitButton": {
|
||||
"message": "選擇送出按鈕",
|
||||
"description": "Choose submit button selection text when choosing Custom Login Fields."
|
||||
},
|
||||
"defineHelpText": {
|
||||
"message": "請確認您的選擇,或選擇更多欄位作為字串欄位",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains String Fields."
|
||||
"message": "請確認您的選擇,或是選擇更多欄位作為<em>字串欄位</em>。",
|
||||
"description": "Confirm a selection text when choosing Custom Login Fields which contains string fields."
|
||||
},
|
||||
"defineKeyboardText": {
|
||||
"message": "您也可以使用鍵盤的數字鍵選擇輸入欄位。",
|
||||
|
|
@ -370,10 +362,6 @@
|
|||
"message": "字串欄位",
|
||||
"description": "General text for a String Fields."
|
||||
},
|
||||
"submitButton": {
|
||||
"message": "送出按鈕",
|
||||
"description": "Custom Submit Button text."
|
||||
},
|
||||
"credentialsNoUsername": {
|
||||
"message": "- 無用戶名稱 -",
|
||||
"description": "Shown when no username is set in the credentials."
|
||||
|
|
@ -382,10 +370,6 @@
|
|||
"message": "錯誤:\n找不到登錄資訊。",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsBlockedInIframe": {
|
||||
"message": "憑證填入在不可信 iframe 而被阻止。",
|
||||
"description": "Alert message when filling credentials is blocked due to iframe restrictions."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "錯誤:\n在 KeePassXC 中找到多個登錄資訊!\n點選 KeePassXC-Browser 圖示來檢視更多選項。",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
|
|
@ -406,10 +390,6 @@
|
|||
"message": "已過期",
|
||||
"description": "If a credential is expired, this is appended to the title label."
|
||||
},
|
||||
"credentialsRetrieveButton": {
|
||||
"message": "檢索憑證",
|
||||
"description": "Manual retrieve credentials button text."
|
||||
},
|
||||
"fieldsFill": {
|
||||
"message": "錯誤:\n找不到該填入的欄位。",
|
||||
"description": "Alert message when no fields are found to fill in."
|
||||
|
|
@ -694,10 +674,6 @@
|
|||
"message": "網站設定",
|
||||
"description": "Site Preferences page header."
|
||||
},
|
||||
"optionsSitePreferencesSettings": {
|
||||
"message": "設定",
|
||||
"description": "Site Preferences settings button text."
|
||||
},
|
||||
"optionsMenuAbout": {
|
||||
"message": "關於",
|
||||
"description": "About page header."
|
||||
|
|
@ -750,10 +726,6 @@
|
|||
"message": "導出配置",
|
||||
"description": "Export settings button text."
|
||||
},
|
||||
"optionsButtonResetSettings": {
|
||||
"message": "重設所有設定",
|
||||
"description": "Reset all settings button text."
|
||||
},
|
||||
"optionsLabelDefaultGroup": {
|
||||
"message": "儲存新密碼的預設群組:",
|
||||
"description": "Default group options text."
|
||||
|
|
@ -1058,10 +1030,6 @@
|
|||
"message": "需要 KeePassXC 版本: $1",
|
||||
"description": "KeePassXC version requirement text."
|
||||
},
|
||||
"optionsMinimumKeePassXCVersionRequired": {
|
||||
"message": "此擴展需要 KeePassXC 版本:$1。更早的版本可能導致意外行為。",
|
||||
"description": "KeePassXC minimum version requirement text."
|
||||
},
|
||||
"optionsDefault": {
|
||||
"message": "預設值:$1",
|
||||
"description": "Default setting text."
|
||||
|
|
@ -1098,10 +1066,6 @@
|
|||
"message": "目前設定將會被覆寫。真的要匯入設定檔案?",
|
||||
"description": "Import settings confirmation dialog help text."
|
||||
},
|
||||
"optionsResetSettingsDialogText": {
|
||||
"message": "所有設定都將被重置為預設值。您確定嗎?",
|
||||
"description": "Reset all settings dialog text."
|
||||
},
|
||||
"optionsConnectedDatabasesText": {
|
||||
"message": "已連接至 KeePassXC-Browser 的資料庫。",
|
||||
"description": "Info text about connected databases."
|
||||
|
|
@ -1146,8 +1110,8 @@
|
|||
"message": "網頁 URL",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnFeatures": {
|
||||
"message": "特性",
|
||||
"optionsColumnIgnore": {
|
||||
"message": "忽略",
|
||||
"description": "Site Preferences list column title."
|
||||
},
|
||||
"optionsColumnUsernameOnly": {
|
||||
|
|
@ -1178,10 +1142,6 @@
|
|||
"message": "停用自動送出",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionPasskeys": {
|
||||
"message": "停用通行密鑰",
|
||||
"description": "Site Preferences option selection."
|
||||
},
|
||||
"optionsSelectionFull": {
|
||||
"message": "停用所有功能",
|
||||
"description": "Site Preferences option selection."
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ browserAction.generateIconName = async function(iconType) {
|
|||
style = page.settings.colorTheme;
|
||||
}
|
||||
}
|
||||
const filetype = (page.isFirefox || page.isSafari) ? 'svg' : 'png';
|
||||
const filetype = (isFirefox() ? 'svg' : 'png');
|
||||
return `/icons/toolbar/${style}/${name}.${filetype}`;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -400,7 +400,7 @@ function onDisconnected() {
|
|||
page.clearAllLogins();
|
||||
keepass.updatePopup('cross');
|
||||
keepass.updateDatabaseHashToContent();
|
||||
logError(`Failed to connect: ${(browser.runtime.lastError === null ? 'Unknown error' : browser.runtime.lastError?.message)}`);
|
||||
logError(`Failed to connect: ${(browser.runtime.lastError === null ? 'Unknown error' : browser.runtime.lastError.message)}`);
|
||||
}
|
||||
|
||||
keepassClient.onNativeMessage = function(response) {
|
||||
|
|
|
|||
|
|
@ -13,14 +13,14 @@ kpxcEvent.onMessage = async function(request, sender) {
|
|||
}
|
||||
};
|
||||
|
||||
kpxcEvent.showStatus = async function(tab, configured, internalPoll, forceShowDefault = false) {
|
||||
kpxcEvent.showStatus = async function(tab, configured, internalPoll) {
|
||||
let keyId = null;
|
||||
if (configured && keepass.databaseHash !== ''
|
||||
&& Object.hasOwn(keepass.keyRing, keepass.databaseHash)) {
|
||||
keyId = keepass.keyRing[keepass.databaseHash].id;
|
||||
}
|
||||
|
||||
if (!internalPoll || forceShowDefault) {
|
||||
if (!internalPoll) {
|
||||
browserAction.showDefault(tab);
|
||||
}
|
||||
|
||||
|
|
@ -76,7 +76,7 @@ kpxcEvent.onSaveSettings = async function(tab, settings) {
|
|||
kpxcEvent.onGetStatus = async function(tab, args = []) {
|
||||
// When internalPoll is true the event is triggered from content script in intervals -> don't poll KeePassXC
|
||||
try {
|
||||
const [ internalPoll = false, triggerUnlock = false, forceShowDefault ] = args;
|
||||
const [ internalPoll = false, triggerUnlock = false ] = args;
|
||||
if (!internalPoll) {
|
||||
const response = await keepass.testAssociation(tab, [ true, triggerUnlock ]);
|
||||
if (!response) {
|
||||
|
|
@ -85,7 +85,7 @@ kpxcEvent.onGetStatus = async function(tab, args = []) {
|
|||
}
|
||||
|
||||
const configured = await keepass.isConfigured();
|
||||
return kpxcEvent.showStatus(tab, configured, internalPoll, forceShowDefault);
|
||||
return kpxcEvent.showStatus(tab, configured, internalPoll);
|
||||
} catch (err) {
|
||||
logError('No status shown: ' + err);
|
||||
return Promise.reject();
|
||||
|
|
@ -241,10 +241,6 @@ kpxcEvent.sendBackToTabs = async function(tab, args = []) {
|
|||
}
|
||||
};
|
||||
|
||||
kpxcEvent.getFeaturesList = async function() {
|
||||
return keepass.featuresList;
|
||||
};
|
||||
|
||||
// All methods named in this object have to be declared BEFORE this!
|
||||
kpxcEvent.messageHandlers = {
|
||||
'add_credentials': keepass.addCredentials,
|
||||
|
|
@ -253,6 +249,7 @@ kpxcEvent.messageHandlers = {
|
|||
'banner_set_position': page.setBannerPosition,
|
||||
'check_database_hash': keepass.checkDatabaseHash,
|
||||
'check_update_keepassxc': kpxcEvent.onCheckUpdateKeePassXC,
|
||||
'compare_versions': kpxcEvent.compareMultipleVersions,
|
||||
'create_new_group': keepass.createNewGroup,
|
||||
'enable_automatic_reconnect': keepass.enableAutomaticReconnect,
|
||||
'disable_automatic_reconnect': keepass.disableAutomaticReconnect,
|
||||
|
|
@ -264,7 +261,6 @@ kpxcEvent.messageHandlers = {
|
|||
'get_database_hash': keepass.getDatabaseHash,
|
||||
'get_database_groups': keepass.getDatabaseGroups,
|
||||
'get_error_message': keepass.getErrorMessage,
|
||||
'get_features_list': kpxcEvent.getFeaturesList,
|
||||
'get_keepassxc_versions': kpxcEvent.onGetKeePassXCVersions,
|
||||
'get_login_list': page.getLoginList,
|
||||
'get_status': kpxcEvent.onGetStatus,
|
||||
|
|
@ -300,7 +296,6 @@ kpxcEvent.messageHandlers = {
|
|||
'reconnect': kpxcEvent.onReconnect,
|
||||
'remove_credentials_from_tab_information': kpxcEvent.onRemoveCredentialsFromTabInformation,
|
||||
'request_autotype': keepass.requestAutotype,
|
||||
'reset_all_settings': page.resetAllSettings,
|
||||
'retrieve_credentials': page.retrieveCredentials,
|
||||
'show_default_browseraction': browserAction.showDefault,
|
||||
'update_credentials': keepass.updateCredentials,
|
||||
|
|
|
|||
|
|
@ -6,15 +6,10 @@ httpAuth.requests = [];
|
|||
httpAuth.pendingCallbacks = [];
|
||||
|
||||
httpAuth.init = function() {
|
||||
if (page.isSafari) {
|
||||
debugLogMessage('HTTP Basic Auth implementation is not supported in Safari.');
|
||||
return;
|
||||
}
|
||||
|
||||
let handleReq = httpAuth.handleRequestPromise;
|
||||
let reqType = 'blocking';
|
||||
|
||||
if (!page.isFirefox) {
|
||||
if (!isFirefox()) {
|
||||
handleReq = httpAuth.handleRequestCallback;
|
||||
reqType = 'asyncBlocking';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,12 @@ const contextMenuItems = [
|
|||
{ title: tr('contextMenuRequestGlobalAutoType'), action: 'request_autotype' }
|
||||
];
|
||||
|
||||
const menuContexts = [ 'editable' ];
|
||||
|
||||
if (isFirefox()) {
|
||||
menuContexts.push('password');
|
||||
}
|
||||
|
||||
const initListeners = async function() {
|
||||
/**
|
||||
* Generate information structure for created tab and invoke all needed
|
||||
|
|
@ -109,8 +115,7 @@ const initListeners = async function() {
|
|||
if (contextMenuItems.some(e => e.action === command)
|
||||
|| command === 'redetect_fields'
|
||||
|| command === 'choose_credential_fields'
|
||||
|| command === 'retrieve_credentials_forced'
|
||||
|| command === 'reopen_database'
|
||||
|| command === 'retrive_credentials_forced'
|
||||
|| command === 'reload_extension') {
|
||||
const tab = await getCurrentTab();
|
||||
if (tab?.id) {
|
||||
|
|
@ -155,32 +160,26 @@ const initListeners = async function() {
|
|||
});
|
||||
};
|
||||
|
||||
const initContextMenuItems = async function() {
|
||||
page.menuContexts = [ 'editable' ];
|
||||
if (page.isFirefox) {
|
||||
page.menuContexts.push('password');
|
||||
}
|
||||
|
||||
const initContextMenuItems = async function() {
|
||||
// Create context menu items
|
||||
await browser.contextMenus.removeAll();
|
||||
for (const item of contextMenuItems) {
|
||||
try {
|
||||
await browser.contextMenus.create({
|
||||
title: item.title,
|
||||
contexts: page.menuContexts,
|
||||
contexts: menuContexts,
|
||||
visible: item.visible,
|
||||
id: item.id || item.action
|
||||
});
|
||||
} catch (e) {
|
||||
logError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
await keepass.migrateKeyRing();
|
||||
await page.initBrowser();
|
||||
await page.initSettings();
|
||||
await page.initSitePreferences();
|
||||
await page.initOpenedTabs();
|
||||
|
|
@ -190,7 +189,7 @@ const initContextMenuItems = async function() {
|
|||
await keepass.reconnect(null, 5000); // 5 second timeout for the first connect
|
||||
await keepass.enableAutomaticReconnect();
|
||||
await keepass.updateDatabase();
|
||||
} catch (_e) {
|
||||
} catch (e) {
|
||||
logError('init.js failed');
|
||||
}
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -2,31 +2,20 @@
|
|||
|
||||
const keepass = {};
|
||||
keepass.associated = { 'value': false, 'hash': null };
|
||||
keepass.featuresList = {
|
||||
downloadFaviconAfterSave: false,
|
||||
newTotp: false,
|
||||
passwordGenerator: false,
|
||||
passkeys: false,
|
||||
passkeysDefaultGroup: false,
|
||||
requiredKeePassXCVersionFound: false,
|
||||
};
|
||||
keepass.cacheTimeout = 30 * 1000; // Milliseconds
|
||||
keepass.keyPair = { publicKey: null, secretKey: null };
|
||||
keepass.serverPublicKey = '';
|
||||
keepass.clientID = '';
|
||||
keepass.currentKeePassXC = '';
|
||||
keepass.databaseHash = '';
|
||||
keepass.isConnected = false;
|
||||
keepass.isDatabaseClosed = true;
|
||||
keepass.isEncryptionKeyUnrecognized = false;
|
||||
keepass.isKeePassXCAvailable = false;
|
||||
keepass.keyPair = { publicKey: null, secretKey: null };
|
||||
keepass.isEncryptionKeyUnrecognized = false;
|
||||
keepass.currentKeePassXC = '';
|
||||
keepass.requiredKeePassXC = '2.3.1';
|
||||
keepass.latestVersionUrl = 'https://api.github.com/repos/keepassxreboot/keepassxc/releases/latest';
|
||||
keepass.cacheTimeout = 30 * 1000; // Milliseconds
|
||||
keepass.databaseHash = '';
|
||||
keepass.previousDatabaseHash = '';
|
||||
keepass.reconnectLoop = null;
|
||||
keepass.requiredKeePassXC = '2.6.0';
|
||||
keepass.serverPublicKey = '';
|
||||
|
||||
const DEFAULT_FETCH_TIMEOUT = 5000; // ms
|
||||
const MAX_RELATED_ORIGIN_LABELS = 60;
|
||||
|
||||
const kpActions = {
|
||||
SET_LOGIN: 'set-login',
|
||||
|
|
@ -183,7 +172,7 @@ keepass.generatePassword = async function(tab) {
|
|||
return '';
|
||||
}
|
||||
|
||||
if (!keepass.featuresList.passwordGenerator) {
|
||||
if (!compareVersion(keepass.requiredKeePassXC, keepass.currentKeePassXC)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
|
|
@ -240,7 +229,9 @@ keepass.associate = async function(tab) {
|
|||
|
||||
const response = await keepassClient.sendMessage(kpAction, tab, messageData, nonce, false, true);
|
||||
if (response) {
|
||||
keepass.setCryptoKey(response.id, idKey);
|
||||
// Use public key as identification key with older KeePassXC releases
|
||||
const savedKey = compareVersion('2.3.4', keepass.currentKeePassXC) ? idKey : key;
|
||||
keepass.setCryptoKey(response.id, savedKey); // Save the new identification public key as id key for the database
|
||||
keepass.associated.value = true;
|
||||
keepass.associated.hash = response.hash || 0;
|
||||
|
||||
|
|
@ -344,13 +335,7 @@ keepass.getDatabaseHash = async function(tab, args = []) {
|
|||
}
|
||||
|
||||
try {
|
||||
const request = keepassClient.buildRequest(
|
||||
kpAction,
|
||||
keepassClient.encrypt(messageData, nonce),
|
||||
nonce,
|
||||
keepass.clientID,
|
||||
triggerUnlock,
|
||||
);
|
||||
const request = keepassClient.buildRequest(kpAction, keepassClient.encrypt(messageData, nonce), nonce, keepass.clientID, triggerUnlock);
|
||||
const response = await keepassClient.sendNativeMessage(request, enableTimeout);
|
||||
if (response.message && response.nonce) {
|
||||
const res = keepassClient.decrypt(response.message, response.nonce);
|
||||
|
|
@ -427,7 +412,6 @@ keepass.changePublicKeys = async function(tab, enableTimeout = false, connection
|
|||
try {
|
||||
const response = await keepassClient.sendNativeMessage(request, enableTimeout, connectionTimeout);
|
||||
keepass.setcurrentKeePassXCVersion(response.version);
|
||||
keepass.updateFeaturesList(response.version);
|
||||
|
||||
if (!keepassClient.verifyKeyResponse(response, key, incrementedNonce)) {
|
||||
if (tab && page.tabs[tab.id]) {
|
||||
|
|
@ -560,7 +544,7 @@ keepass.createNewGroup = async function(tab, args = []) {
|
|||
|
||||
keepass.getTotp = async function(tab, args = []) {
|
||||
const [ uuid, oldTotp ] = args;
|
||||
if (!keepass.featuresList.newTotpSupported) {
|
||||
if (!compareVersion('2.6.1', keepass.currentKeePassXC, true)) {
|
||||
return oldTotp;
|
||||
}
|
||||
|
||||
|
|
@ -625,14 +609,11 @@ keepass.passkeysRegister = async function(tab, args = []) {
|
|||
const kpAction = kpActions.PASSKEYS_REGISTER;
|
||||
const nonce = keepassClient.getNonce();
|
||||
const [ publicKey, origin ] = args;
|
||||
const passkeyPublicKey = JSON.parse(JSON.stringify(publicKey));
|
||||
const relatedOrigins = await keepass.getPasskeysRelatedOrigins(passkeyPublicKey?.rp?.id);
|
||||
|
||||
const messageData = {
|
||||
action: kpAction,
|
||||
publicKey: passkeyPublicKey,
|
||||
publicKey: JSON.parse(JSON.stringify(publicKey)),
|
||||
origin: origin,
|
||||
relatedOrigins: relatedOrigins,
|
||||
groupName: page?.settings?.defaultPasskeyGroup,
|
||||
keys: keepass.getCryptoKeys()
|
||||
};
|
||||
|
|
@ -660,15 +641,13 @@ keepass.passkeysGet = async function(tab, args = []) {
|
|||
|
||||
const kpAction = kpActions.PASSKEYS_GET;
|
||||
const nonce = keepassClient.getNonce();
|
||||
const [ publicKey, origin ] = args;
|
||||
const passkeyPublicKey = JSON.parse(JSON.stringify(publicKey));
|
||||
const relatedOrigins = await keepass.getPasskeysRelatedOrigins(passkeyPublicKey?.rp?.id);
|
||||
const publicKey = args[0];
|
||||
const origin = args[1];
|
||||
|
||||
const messageData = {
|
||||
action: kpAction,
|
||||
publicKey: passkeyPublicKey,
|
||||
publicKey: JSON.parse(JSON.stringify(publicKey)),
|
||||
origin: origin,
|
||||
relatedOrigins: relatedOrigins,
|
||||
keys: keepass.getCryptoKeys()
|
||||
};
|
||||
|
||||
|
|
@ -804,9 +783,13 @@ keepass.getCryptoKeys = function() {
|
|||
|
||||
keepass.enableAutomaticReconnect = async function() {
|
||||
// Disable for Windows if KeePassXC is older than 2.3.4
|
||||
if (!page.settings.autoReconnect) {
|
||||
if (!page.settings.autoReconnect
|
||||
|| (navigator.platform.toLowerCase().includes('win')
|
||||
&& keepass.currentKeePassXC
|
||||
&& !compareVersion('2.3.4', keepass.currentKeePassXC))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (keepass.reconnectLoop === null) {
|
||||
keepass.reconnectLoop = setInterval(async () => {
|
||||
if (!keepass.isKeePassXCAvailable) {
|
||||
|
|
@ -824,9 +807,7 @@ keepass.disableAutomaticReconnect = function() {
|
|||
keepass.reconnect = async function(tab = null, connectionTimeout = 1500) {
|
||||
keepassClient.connectToNative();
|
||||
keepass.generateNewKeyPair();
|
||||
const keyChangeResult = await keepass
|
||||
.changePublicKeys(tab, !!connectionTimeout, connectionTimeout)
|
||||
.catch(() => false);
|
||||
const keyChangeResult = await keepass.changePublicKeys(tab, !!connectionTimeout, connectionTimeout).catch(() => false);
|
||||
|
||||
// Change public keys timeout
|
||||
if (!keyChangeResult) {
|
||||
|
|
@ -882,9 +863,7 @@ keepass.setcurrentKeePassXCVersion = function(version) {
|
|||
keepass.keePassXCUpdateAvailable = async function() {
|
||||
const checkUpdate = Number(page.settings.checkUpdateKeePassXC);
|
||||
if (checkUpdate !== CHECK_UPDATE_NEVER) {
|
||||
const lastChecked = keepass.latestKeePassXC.lastChecked
|
||||
? new Date(keepass.latestKeePassXC.lastChecked)
|
||||
: new Date(1986, 11, 21);
|
||||
const lastChecked = (keepass.latestKeePassXC.lastChecked) ? new Date(keepass.latestKeePassXC.lastChecked) : new Date(1986, 11, 21);
|
||||
const daysSinceLastCheck = Math.floor(((new Date()).getTime() - lastChecked.getTime()) / 86400000);
|
||||
if (daysSinceLastCheck >= checkUpdate) {
|
||||
await keepass.checkForNewKeePassXCVersion();
|
||||
|
|
@ -900,7 +879,7 @@ keepass.checkForNewKeePassXCVersion = async function() {
|
|||
let version = -1;
|
||||
|
||||
try {
|
||||
const response = await fetch(keepass.latestVersionUrl, { signal: AbortSignal.timeout(DEFAULT_FETCH_TIMEOUT) });
|
||||
const response = await fetch(keepass.latestVersionUrl);
|
||||
const jsonData = await response.json();
|
||||
if (jsonData?.tag_name && jsonData?.prerelease === false) {
|
||||
version = jsonData.tag_name;
|
||||
|
|
@ -912,44 +891,6 @@ keepass.checkForNewKeePassXCVersion = async function() {
|
|||
keepass.latestKeePassXC.lastChecked = new Date().valueOf();
|
||||
};
|
||||
|
||||
// Implements retrieval of Related Origin Requests for passkeys
|
||||
// https://www.w3.org/TR/webauthn-3/#sctn-related-origins
|
||||
keepass.getPasskeysRelatedOrigins = async function(rpId) {
|
||||
if (!rpId) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`https://${rpId}/.well-known/webauthn`, {
|
||||
signal: AbortSignal.timeout(DEFAULT_FETCH_TIMEOUT),
|
||||
});
|
||||
|
||||
// Basic reply validation, see: https://www.w3.org/TR/webauthn-3/#sctn-validating-relation-origin
|
||||
const isJson = response?.headers?.get('content-type')?.includes('application/json');
|
||||
if (!isJson) {
|
||||
logError('getRelatedOrigins error: Content-Type is not JSON');
|
||||
return [];
|
||||
}
|
||||
|
||||
const jsonData = await response.json();
|
||||
if (!Array.isArray(jsonData?.origins)
|
||||
|| jsonData?.origins?.length === 0
|
||||
|| jsonData?.origins?.length > MAX_RELATED_ORIGIN_LABELS
|
||||
|| !jsonData?.origins?.every((origin) => typeof origin === 'string')) {
|
||||
logError(
|
||||
`getRelatedOrigins error: origins is not a list of strings, or it exceeds the maximum count of ${MAX_RELATED_ORIGIN_LABELS}`,
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
return jsonData.origins;
|
||||
} catch (ex) {
|
||||
logError(`getRelatedOrigins error: ${ex}`);
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
keepass.clearErrorMessage = function(tab) {
|
||||
if (tab && page.tabs[tab.id]) {
|
||||
page.tabs[tab.id].errorMessage = undefined;
|
||||
|
|
@ -1004,25 +945,6 @@ keepass.updateDatabaseHashToContent = async function() {
|
|||
}
|
||||
};
|
||||
|
||||
keepass.updateFeaturesList = function (currentVersion) {
|
||||
const versionResults = keepass.compareMultipleVersions([
|
||||
keepass.requiredKeePassXC,
|
||||
'2.6.1',
|
||||
'2.7.0',
|
||||
'2.7.7',
|
||||
'2.7.10'
|
||||
], currentVersion);
|
||||
|
||||
keepass.featuresList = {
|
||||
downloadFaviconAfterSave: versionResults['2.7.0'],
|
||||
newTotp: versionResults['2.6.1'],
|
||||
passwordGenerator: versionResults['2.7.0'],
|
||||
passkeys: versionResults['2.7.7'],
|
||||
passkeysDefaultGroup: versionResults['2.7.10'],
|
||||
requiredKeePassXCVersionFound: versionResults[keepass.requiredKeePassXC],
|
||||
};
|
||||
};
|
||||
|
||||
// Expects an array of versions to compare
|
||||
keepass.compareMultipleVersions = function(versions, current, canBeEqual = true) {
|
||||
if (!Array.isArray(versions)) {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ const defaultSettings = {
|
|||
downloadFaviconAfterSave: false,
|
||||
passkeys: false,
|
||||
passkeysFallback: true,
|
||||
redirectAllowance: 3,
|
||||
redirectAllowance: 1,
|
||||
saveDomainOnly: true,
|
||||
showGettingStartedGuideAlert: true,
|
||||
showGroupNameInAutocomplete: true,
|
||||
|
|
@ -49,10 +49,7 @@ page.blockedTabs = [];
|
|||
page.clearCredentialsTimeout = null;
|
||||
page.currentRequest = {};
|
||||
page.currentTabId = -1;
|
||||
page.isFirefox = false;
|
||||
page.isSafari = false;
|
||||
page.manualFill = ManualFill.NONE;
|
||||
page.menuContexts = [ 'editable' ];
|
||||
page.passwordFilled = false;
|
||||
page.redirectCount = 0;
|
||||
page.submitted = false;
|
||||
|
|
@ -64,17 +61,12 @@ page.popupData = {
|
|||
popup: 'popup'
|
||||
};
|
||||
|
||||
page.initBrowser = async function() {
|
||||
page.isFirefox = isFirefox();
|
||||
page.isSafari = isSafari();
|
||||
};
|
||||
|
||||
page.initSettings = async function() {
|
||||
try {
|
||||
const item = await browser.storage.local.get({ 'settings': {} });
|
||||
|
||||
// Load managed settings if found
|
||||
if (page.isFirefox && typeof(browser.storage.managed) === 'object') {
|
||||
if (isFirefox() && typeof(browser.storage.managed) === 'object') {
|
||||
try {
|
||||
const managedSettings = await browser.storage.managed.get('settings');
|
||||
if (managedSettings?.settings) {
|
||||
|
|
@ -82,16 +74,16 @@ page.initSettings = async function() {
|
|||
item.settings = managedSettings.settings;
|
||||
}
|
||||
} catch (err) {
|
||||
debugLogMessage('page.initSettings: ' + err);
|
||||
logError('page.initSettings: ' + err);
|
||||
}
|
||||
} else if (!page.isSafari && typeof chrome.storage.managed === 'object') {
|
||||
} else if (typeof(chrome.storage.managed) === 'object') {
|
||||
chrome.storage.managed.get('settings').then((managedSettings) => {
|
||||
if (managedSettings?.settings) {
|
||||
debugLogMessage('Managed settings found.');
|
||||
item.settings = managedSettings.settings;
|
||||
}
|
||||
}).catch((err) => {
|
||||
debugLogMessage('page.initSettings: ' + err);
|
||||
logError('page.initSettings: ' + err);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -146,16 +138,6 @@ page.initSitePreferences = async function() {
|
|||
await browser.storage.local.set({ 'settings': page.settings });
|
||||
};
|
||||
|
||||
page.resetAllSettings = async function() {
|
||||
for (const [ key, value ] of Object.entries(defaultSettings)) {
|
||||
page.settings[key] = value;
|
||||
}
|
||||
|
||||
page.settings[DEFINED_CUSTOM_FIELDS] = {};
|
||||
page.settings.sitePreferences = [];
|
||||
await browser.storage.local.set({ 'settings': page.settings });
|
||||
};
|
||||
|
||||
page.switchTab = async function(tab) {
|
||||
// Clears Fill Attribute selection from context menu
|
||||
page.setFillAttributeContextMenuItemVisible(false);
|
||||
|
|
@ -275,11 +257,11 @@ page.retrieveCredentials = async function(tab, args = []) {
|
|||
return credentials;
|
||||
};
|
||||
|
||||
page.getLoginId = async function(tab, returnSingle = true) {
|
||||
page.getLoginId = async function(tab) {
|
||||
const currentTab = page.tabs[tab.id];
|
||||
|
||||
// If there's only one credential available and loginId is not set
|
||||
if (currentTab && returnSingle && !currentTab.loginId && currentTab.credentials.length === 1) {
|
||||
if (currentTab && !currentTab.loginId && currentTab.credentials.length === 1) {
|
||||
return currentTab.credentials[0].uuid;
|
||||
}
|
||||
|
||||
|
|
@ -353,15 +335,14 @@ page.fillHttpAuth = async function(tab, credentials) {
|
|||
}
|
||||
};
|
||||
|
||||
page.isSiteIgnored = async function(tab, args = []) {
|
||||
const [ currentLocation, checkPasskeys ] = args;
|
||||
page.isSiteIgnored = async function(tab, currentLocation) {
|
||||
if (!page?.settings?.sitePreferences || !currentLocation) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const site of page.settings.sitePreferences) {
|
||||
if (siteMatch(site.url, currentLocation) || site.url === currentLocation) {
|
||||
if (site.ignore === IGNORE_FULL || (checkPasskeys && site.ignore === IGNORE_PASSKEYS)) {
|
||||
if (site.ignore === IGNORE_FULL) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -484,7 +465,7 @@ page.getTopLevelDomainFromUrl = async function(domain, url) {
|
|||
url: url
|
||||
});
|
||||
}
|
||||
} catch (_e) {
|
||||
} catch (e) {
|
||||
return domain;
|
||||
}
|
||||
}
|
||||
|
|
@ -517,7 +498,7 @@ page.getBaseDomainFromUrl = async function(hostname, url) {
|
|||
|
||||
const createContextMenuItem = function({ action, args, ...options }) {
|
||||
return browser.contextMenus.create({
|
||||
contexts: page.menuContexts,
|
||||
contexts: menuContexts,
|
||||
id: action,
|
||||
...options
|
||||
});
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -1,13 +1,11 @@
|
|||
'use strict';
|
||||
|
||||
const EXTENSION_NAME = 'KeePassXC-Browser';
|
||||
const DEFINED_CUSTOM_FIELDS = 'defined-custom-fields';
|
||||
|
||||
// Site Preferences ignore options
|
||||
const IGNORE_NOTHING = 'ignoreNothing';
|
||||
const IGNORE_NORMAL = 'ignoreNormal';
|
||||
const IGNORE_AUTOSUBMIT = 'ignoreAutoSubmit';
|
||||
const IGNORE_PASSKEYS = 'ignorePasskeys';
|
||||
const IGNORE_FULL = 'ignoreFull';
|
||||
|
||||
// Credential sorting options
|
||||
|
|
@ -28,6 +26,23 @@ const URL_WILDCARD = '1kpxcwc1';
|
|||
const schemeSegment = '(\\*|http|https|ws|wss|ftp)';
|
||||
const hostSegment = '(\\*|(?:\\*\\.)?(?:[^/*]+))?';
|
||||
|
||||
const isFirefox = function() {
|
||||
return navigator.userAgent.indexOf('Firefox') !== -1 || navigator.userAgent.indexOf('Gecko/') !== -1;
|
||||
};
|
||||
|
||||
const isEdge = function() {
|
||||
return navigator.userAgent.indexOf('Edg') !== -1;
|
||||
};
|
||||
|
||||
const showNotification = function(message) {
|
||||
browser.notifications.create({
|
||||
'type': 'basic',
|
||||
'iconUrl': browser.runtime.getURL('icons/keepassxc_64x64.png'),
|
||||
'title': 'KeePassXC-Browser',
|
||||
'message': message
|
||||
});
|
||||
};
|
||||
|
||||
const AssociatedAction = {
|
||||
NOT_ASSOCIATED: 0,
|
||||
ASSOCIATED: 1,
|
||||
|
|
@ -46,47 +61,6 @@ const ManualFill = {
|
|||
BOTH: 2
|
||||
};
|
||||
|
||||
const SitePreferences = {
|
||||
ALLOW_IFRAMES: 'allowIframes',
|
||||
IMPROVED_FIELD_DETECTION: 'improvedFieldDetection',
|
||||
USERNAME_ONLY: 'usernameOnly',
|
||||
};
|
||||
|
||||
const isFirefox = function() {
|
||||
return browser.runtime.getURL('')?.startsWith('moz-extension');
|
||||
};
|
||||
|
||||
const isSafari = function() {
|
||||
return browser.runtime.getURL('')?.startsWith('safari-web-extension');
|
||||
};
|
||||
|
||||
const isEdge = function() {
|
||||
return navigator.userAgent.indexOf('Edg') !== -1;
|
||||
};
|
||||
|
||||
const getIconClass = function(className) {
|
||||
if (isFirefox()) {
|
||||
return className + '-moz';
|
||||
} else if (isSafari()) {
|
||||
return className + '-safari';
|
||||
}
|
||||
return className;
|
||||
};
|
||||
|
||||
const showNotification = function(message) {
|
||||
browser.notifications.create({
|
||||
'type': 'basic',
|
||||
'iconUrl': browser.runtime.getURL('icons/keepassxc_64x64.png'),
|
||||
'title': 'KeePassXC-Browser',
|
||||
'message': message
|
||||
});
|
||||
};
|
||||
|
||||
// Returns a string with 'px' for CSS styles
|
||||
const Pixels = function(value) {
|
||||
return String(value) + 'px';
|
||||
};
|
||||
|
||||
const compareVersion = function(minimum, current, canBeEqual = true) {
|
||||
if (!minimum || !current || minimum?.indexOf('.') === -1 || current?.indexOf('.') === -1) {
|
||||
return false;
|
||||
|
|
@ -183,10 +157,10 @@ const trimURL = function(url) {
|
|||
};
|
||||
|
||||
const debugLogMessage = function(message, extra) {
|
||||
console.debug(`[Debug ${getFileAndLine()}] ${EXTENSION_NAME} - ${message}`);
|
||||
console.log(`[Debug ${getFileAndLine()}] ${EXTENSION_NAME} - ${message}`);
|
||||
|
||||
if (extra) {
|
||||
console.debug(extra);
|
||||
console.log(extra);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -208,20 +182,10 @@ const getCurrentTab = async function() {
|
|||
return tabs?.length > 0 ? tabs[0] : undefined;
|
||||
};
|
||||
|
||||
// Check if element b is inside a
|
||||
const isElementInside = (a, b) => (b.x >= a.x && b.right <= a.right) && (b.y >= a.y && b.bottom <= a.bottom);
|
||||
|
||||
// Check if two elements overlap
|
||||
const elementsOverlap = function(rect1, rect2) {
|
||||
const overlaps = (a, b) => !(a.right < b.left || a.left > b.right || a.bottom < b.top || a.top > b.bottom);
|
||||
return isElementInside(rect1, rect2) || overlaps(rect1, rect2);
|
||||
};
|
||||
|
||||
// Exports for tests
|
||||
if (typeof module === 'object') {
|
||||
module.exports = {
|
||||
compareVersion,
|
||||
elementsOverlap,
|
||||
matchesWithNodeName,
|
||||
siteMatch,
|
||||
slashNeededForUrl,
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ kpxcSites.detectUsernameFromPage = function() {
|
|||
* @returns {boolean} True if an Element has a match with the identifier and document location
|
||||
*/
|
||||
kpxcSites.exceptionFound = function(identifier, field) {
|
||||
if ((!identifier || identifier.length === 0) && !field) {
|
||||
if (!identifier || identifier.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -80,7 +80,7 @@ kpxcSites.exceptionFound = function(identifier, field) {
|
|||
|| (typeof identifier === 'object' && [ 'password', 'form-row', 'show-password' ].every(c => identifier.contains(c))))) {
|
||||
return true;
|
||||
} else if (document.location.origin.startsWith('https://signin.ebay.')
|
||||
&& (identifier === 'null' || identifier?.value === 'null' || identifier === 'pass')) {
|
||||
&& (identifier === 'null' || identifier.value === 'null' || identifier === 'pass')) {
|
||||
return true;
|
||||
} else if (document.location.origin.startsWith('https://www.fidelity.com')) {
|
||||
if (typeof identifier === 'string') {
|
||||
|
|
@ -100,42 +100,6 @@ kpxcSites.exceptionFound = function(identifier, field) {
|
|||
} else if (document.location.origin === 'https://id.atlassian.com' &&
|
||||
Array.isArray(identifier) && identifier?.contains('password-field')) {
|
||||
return true;
|
||||
} else if (document.location.origin === 'https://app.fastmail.com'
|
||||
&& identifier?.contains('u-space-y-5') && field?.id === 'v25') {
|
||||
return true;
|
||||
} else if (document.location.origin === 'https://login.dei.gr' &&
|
||||
identifier?.value?.includes('show-reveal-password')) {
|
||||
return true;
|
||||
} else if (document.location.origin === 'https://accounts.google.com' && field?.id === 'password') {
|
||||
return true;
|
||||
} else if (document.location.origin === 'https://www.epicgames.com'
|
||||
&& ((field?.style?.opacity === '1' && field?.style?.willChange === 'auto') || identifier === 'password')) {
|
||||
return true;
|
||||
} else if (document.location.origin === 'https://www.paypal.com' && field?.id === 'splitPassword') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// Handles exceptions when returning or modifying existing combinations
|
||||
kpxcSites.combinationExceptionFound = function(existingCombination) {
|
||||
if (!existingCombination) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Exception for Google. They replace the username input with password input using identical className.
|
||||
// If detected, remove the username from the combination.
|
||||
if (document.location.origin === 'https://accounts.google.com'
|
||||
&& existingCombination?.username?.className?.length > 0
|
||||
&& existingCombination?.password?.className?.length > 0
|
||||
&& existingCombination?.username?.className === existingCombination?.password?.className) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (document.location.origin === 'https://www.paypal.com'
|
||||
&& existingCombination.password?.className?.includes('pin-password')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -250,8 +214,6 @@ kpxcSites.formSubmitButtonExceptionFound = function(form) {
|
|||
return form.querySelector('button[class*=_primary_]');
|
||||
} else if (!form && document.location.origin === 'https://www.reddit.com') {
|
||||
return $('button.login');
|
||||
} else if (form?.action === 'https://webapp.dubverse.ai/') {
|
||||
return $('button[class^=chakra]');
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
|
@ -270,20 +232,6 @@ kpxcSites.popupExceptionFound = function(combinations) {
|
|||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles exceptions where a certain element is set as a popover, and it prevents input field detections.
|
||||
* @param {object} elem Popover element
|
||||
* @returns {boolean} True if exception found
|
||||
*/
|
||||
kpxcSites.overlayExceptionFound = function(elem) {
|
||||
if (document.location.href?.startsWith('https://github.com/login')
|
||||
&& elem?.nodeName === 'TOOL-TIP' && elem?.baseURI === 'https://github.com/login') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles a few exceptions for certain sites where Username Icon is not placed properly.
|
||||
* @param {number} left Absolute left position of the icon
|
||||
|
|
|
|||
|
|
@ -100,9 +100,7 @@ class Autocomplete {
|
|||
styleSheet.addEventListener('load', () => (this.wrapper.style.display = 'block'));
|
||||
this.container = kpxcUI.createElement('div', 'kpxcAutocomplete-container', {
|
||||
id: 'kpxcAutocomplete-container',
|
||||
popover: 'manual',
|
||||
});
|
||||
this.container.style.margin = 0;
|
||||
|
||||
// Apply compact mode class
|
||||
if (kpxc.settings.useCompactMode) {
|
||||
|
|
@ -119,7 +117,6 @@ class Autocomplete {
|
|||
this.container.append(this.list);
|
||||
this.shadowRoot.append(this.container);
|
||||
document.body.append(this.wrapper);
|
||||
kpxcUI.observeWrapper(this.wrapper);
|
||||
|
||||
// Add a footer message for auto-submit
|
||||
if (this.autoSubmit) {
|
||||
|
|
@ -130,9 +127,6 @@ class Autocomplete {
|
|||
|
||||
this.updateList();
|
||||
this.container.classList.add('kpxcAutocomplete-container--visible');
|
||||
if (kpxcFields.popoverSupported) {
|
||||
this.container.showPopover({ source: inputField });
|
||||
}
|
||||
this.updatePosition();
|
||||
}
|
||||
|
||||
|
|
@ -240,9 +234,6 @@ class Autocomplete {
|
|||
}
|
||||
|
||||
this.container.classList.remove('kpxcAutocomplete-container--visible');
|
||||
if (kpxcFields.popoverSupported) {
|
||||
this.container.hidePopover();
|
||||
}
|
||||
}
|
||||
|
||||
getAllItems() {
|
||||
|
|
@ -346,7 +337,7 @@ class Autocomplete {
|
|||
|
||||
// Get body zoom radio
|
||||
const zoom = kpxcUI.bodyStyle.zoom || 1;
|
||||
|
||||
|
||||
// Calculate Y offset if menu does not fit to the bottom of the screen -> show it at the top of the input field
|
||||
const menuRect = this.container.getBoundingClientRect();
|
||||
const totalHeight = menuRect.height + rect.height;
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ kpxcBanner.destroy = async function() {
|
|||
} else {
|
||||
window.parent.document.body.removeChild(window.parent.document.body.querySelector('#kpxc-banner'));
|
||||
}
|
||||
} catch(_e) {
|
||||
} catch(e) {
|
||||
kpxcBanner.wrapper.style.display = 'hidden';
|
||||
}
|
||||
|
||||
|
|
@ -62,10 +62,10 @@ kpxcBanner.create = async function(credentials = {}) {
|
|||
const bannerInfo = kpxcUI.createElement('div', 'banner-info');
|
||||
const bannerButtons = kpxcUI.createElement('div', 'banner-buttons');
|
||||
|
||||
const className = getIconClass('kpxc-banner-icon');
|
||||
const className = (isFirefox() ? 'kpxc-banner-icon-moz' : 'kpxc-banner-icon');
|
||||
const icon = kpxcUI.createElement('span', className, { 'alt': 'logo' });
|
||||
|
||||
const infoText = kpxcUI.createElement('span', 'banner-info-text', {}, tr('rememberInfoText'));
|
||||
const infoText = kpxcUI.createElement('span', '', {}, tr('rememberInfoText'));
|
||||
const usernameText = kpxcUI.createElement('span', 'small', {}, tr('popupUsername') + ' ');
|
||||
const usernameSpan = kpxcUI.createElement('span', 'small info information-username', {}, credentials.username);
|
||||
|
||||
|
|
@ -165,7 +165,6 @@ kpxcBanner.create = async function(credentials = {}) {
|
|||
|
||||
if (window.self === window.top && !kpxcBanner.created) {
|
||||
window.parent.document.body.appendChild(wrapper);
|
||||
kpxcUI.observeWrapper(wrapper);
|
||||
kpxcBanner.created = true;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ const STEP_SELECT_USERNAME = 1;
|
|||
const STEP_SELECT_PASSWORD = 2;
|
||||
const STEP_SELECT_TOTP = 3;
|
||||
const STEP_SELECT_STRING_FIELDS = 4;
|
||||
const STEP_SELECT_SUBMIT_BUTTON = 5;
|
||||
|
||||
const CHECKBOX_OVERLAY_SIZE = 20;
|
||||
|
||||
const DEFINED_CUSTOM_FIELDS = 'defined-custom-fields';
|
||||
const FIXED_FIELD_CLASS = 'kpxcDefine-fixed-field';
|
||||
const DARK_FIXED_FIELD_CLASS = 'kpxcDefine-fixed-field-dark';
|
||||
const HOVER_FIELD_CLASS = 'kpxcDefine-fixed-hover-field';
|
||||
|
|
@ -19,10 +19,9 @@ const PASSWORD_FIELD_CLASS = 'kpxcDefine-fixed-password-field';
|
|||
const TOTP_FIELD_CLASS = 'kpxcDefine-fixed-totp-field';
|
||||
const STRING_FIELD_CLASS = 'kpxcDefine-fixed-string-field';
|
||||
|
||||
const INPUT_BUTTON_QUERY_PATTERN = 'button, input[type=submit]';
|
||||
const INPUT_QUERY_PATTERNS_START = 'input';
|
||||
const INPUT_QUERY_PATTERN_NOT_CHECKBOX = ':not([type=checkbox])';
|
||||
const INPUT_QUERY_PATTERN = ':not([disabled]):not([type=button]):not([type=radio]):not([type=color]):not([type=date]):not([type=datetime-local]):not([type=file]):not([type=hidden]):not([type=image]):not([type=month]):not([type=range]):not([type=reset]):not([type=submit]):not([type=time]):not([type=week]), select, textarea';
|
||||
const inputQueryPatternStart = 'input';
|
||||
const inputQueryPatternNotCheckbox = ':not([type=checkbox])';
|
||||
const inputQueryPattern = ':not([disabled]):not([type=button]):not([type=radio]):not([type=color]):not([type=date]):not([type=datetime-local]):not([type=file]):not([type=hidden]):not([type=image]):not([type=month]):not([type=range]):not([type=reset]):not([type=submit]):not([type=time]):not([type=week]), select, textarea';
|
||||
|
||||
const kpxcCustomLoginFieldsBanner = {};
|
||||
kpxcCustomLoginFieldsBanner.banner = undefined;
|
||||
|
|
@ -31,9 +30,8 @@ kpxcCustomLoginFieldsBanner.created = false;
|
|||
kpxcCustomLoginFieldsBanner.dataStep = STEP_NONE;
|
||||
kpxcCustomLoginFieldsBanner.infoText = undefined;
|
||||
kpxcCustomLoginFieldsBanner.wrapper = undefined;
|
||||
kpxcCustomLoginFieldsBanner.inputQueryPatternNormal =
|
||||
INPUT_QUERY_PATTERNS_START + INPUT_QUERY_PATTERN_NOT_CHECKBOX + INPUT_QUERY_PATTERN;
|
||||
kpxcCustomLoginFieldsBanner.inputQueryPatternStringFields = INPUT_QUERY_PATTERNS_START + INPUT_QUERY_PATTERN;
|
||||
kpxcCustomLoginFieldsBanner.inputQueryPatternNormal = inputQueryPatternStart + inputQueryPatternNotCheckbox + inputQueryPattern;
|
||||
kpxcCustomLoginFieldsBanner.inputQueryPatternStringFields = inputQueryPatternStart + inputQueryPattern;
|
||||
kpxcCustomLoginFieldsBanner.markedFields = [];
|
||||
kpxcCustomLoginFieldsBanner.nonSelectedElementsPattern = `div.${FIXED_FIELD_CLASS}:not(.${USERNAME_FIELD_CLASS}):not(.${PASSWORD_FIELD_CLASS}):not(.${TOTP_FIELD_CLASS}):not(.${STRING_FIELD_CLASS})`;
|
||||
|
||||
|
|
@ -46,7 +44,6 @@ kpxcCustomLoginFieldsBanner.selection = {
|
|||
totpElement: undefined,
|
||||
fields: [],
|
||||
fieldElements: [],
|
||||
submitButton: undefined,
|
||||
};
|
||||
|
||||
kpxcCustomLoginFieldsBanner.buttons = {
|
||||
|
|
@ -93,9 +90,9 @@ kpxcCustomLoginFieldsBanner.create = async function() {
|
|||
const bannerInfo = kpxcUI.createElement('div', 'banner-info');
|
||||
const bannerButtons = kpxcUI.createElement('div', 'banner-buttons');
|
||||
|
||||
const iconClassName = getIconClass('kpxc-banner-icon');
|
||||
const iconClassName = isFirefox() ? 'kpxc-banner-icon-moz' : 'kpxc-banner-icon';
|
||||
const icon = kpxcUI.createElement('span', iconClassName);
|
||||
const infoText = kpxcUI.createElement('span', 'banner-info-text', {}, tr('defineChooseCustomLoginFieldText'));
|
||||
const infoText = kpxcUI.createElement('span', '', {}, tr('defineChooseCustomLoginFieldText'));
|
||||
const separator = kpxcUI.createElement('div', 'kpxc-separator');
|
||||
const secondSeparator = kpxcUI.createElement('div', 'kpxc-separator');
|
||||
|
||||
|
|
@ -104,7 +101,6 @@ kpxcCustomLoginFieldsBanner.create = async function() {
|
|||
const passwordButton = kpxcUI.createButton(RED_BUTTON, tr('password'), kpxcCustomLoginFieldsBanner.passwordButtonClicked);
|
||||
const totpButton = kpxcUI.createButton(GREEN_BUTTON, 'TOTP', kpxcCustomLoginFieldsBanner.totpButtonClicked);
|
||||
const stringFieldsButton = kpxcUI.createButton(BLUE_BUTTON, tr('stringFields'), kpxcCustomLoginFieldsBanner.stringFieldsButtonClicked);
|
||||
const submitButton = kpxcUI.createButton(BLUE_BUTTON, tr('submitButton'), kpxcCustomLoginFieldsBanner.submitButtonClicked);
|
||||
const clearDataButton = kpxcUI.createButton(RED_BUTTON, tr('defineClearData'), kpxcCustomLoginFieldsBanner.clearData);
|
||||
const confirmButton = kpxcUI.createButton(GREEN_BUTTON, tr('defineConfirm'), kpxcCustomLoginFieldsBanner.confirm);
|
||||
const closeButton = kpxcUI.createButton(RED_BUTTON, tr('defineClose'), kpxcCustomLoginFieldsBanner.closeButtonClicked);
|
||||
|
|
@ -121,11 +117,10 @@ kpxcCustomLoginFieldsBanner.create = async function() {
|
|||
kpxcCustomLoginFieldsBanner.buttons.password = passwordButton;
|
||||
kpxcCustomLoginFieldsBanner.buttons.totp = totpButton;
|
||||
kpxcCustomLoginFieldsBanner.buttons.stringFields = stringFieldsButton;
|
||||
kpxcCustomLoginFieldsBanner.buttons.submitButton = submitButton;
|
||||
|
||||
bannerInfo.appendMultiple(icon, infoText);
|
||||
bannerButtons.appendMultiple(resetButton, separator, usernameButton, passwordButton, totpButton,
|
||||
stringFieldsButton, submitButton, secondSeparator, clearDataButton, confirmButton, closeButton);
|
||||
bannerButtons.appendMultiple(resetButton, separator, usernameButton,
|
||||
passwordButton, totpButton, stringFieldsButton, secondSeparator, clearDataButton, confirmButton, closeButton);
|
||||
banner.appendMultiple(bannerInfo, bannerButtons);
|
||||
kpxcUI.makeBannerDraggable(banner);
|
||||
|
||||
|
|
@ -164,7 +159,6 @@ kpxcCustomLoginFieldsBanner.create = async function() {
|
|||
|
||||
if (!kpxcCustomLoginFieldsBanner.created) {
|
||||
window.self.document.body.appendChild(wrapper);
|
||||
kpxcUI.observeWrapper(wrapper);
|
||||
kpxcCustomLoginFieldsBanner.created = true;
|
||||
}
|
||||
|
||||
|
|
@ -195,8 +189,7 @@ kpxcCustomLoginFieldsBanner.usernameButtonClicked = function(e) {
|
|||
|
||||
// Reset username field selection if already set
|
||||
if (kpxcCustomLoginFieldsBanner.selection.username) {
|
||||
kpxcCustomLoginFieldsBanner.removeSelection(kpxcCustomLoginFieldsBanner.selection.username,
|
||||
`div.${USERNAME_FIELD_CLASS}`);
|
||||
kpxcCustomLoginFieldsBanner.removeSelection(kpxcCustomLoginFieldsBanner.selection.username, `div.${USERNAME_FIELD_CLASS}`);
|
||||
kpxcCustomLoginFieldsBanner.selection.username = undefined;
|
||||
}
|
||||
|
||||
|
|
@ -214,8 +207,7 @@ kpxcCustomLoginFieldsBanner.passwordButtonClicked = function(e) {
|
|||
|
||||
// Reset password field selection if already set
|
||||
if (kpxcCustomLoginFieldsBanner.selection.password) {
|
||||
kpxcCustomLoginFieldsBanner.removeSelection(kpxcCustomLoginFieldsBanner.selection.password,
|
||||
`div.${PASSWORD_FIELD_CLASS}`);
|
||||
kpxcCustomLoginFieldsBanner.removeSelection(kpxcCustomLoginFieldsBanner.selection.password, `div.${PASSWORD_FIELD_CLASS}`);
|
||||
kpxcCustomLoginFieldsBanner.selection.password = undefined;
|
||||
}
|
||||
|
||||
|
|
@ -233,8 +225,7 @@ kpxcCustomLoginFieldsBanner.totpButtonClicked = function(e) {
|
|||
|
||||
// Reset TOTP field selection if already set
|
||||
if (kpxcCustomLoginFieldsBanner.selection.totp) {
|
||||
kpxcCustomLoginFieldsBanner.removeSelection(kpxcCustomLoginFieldsBanner.selection.totp,
|
||||
`div.${TOTP_FIELD_CLASS}`);
|
||||
kpxcCustomLoginFieldsBanner.removeSelection(kpxcCustomLoginFieldsBanner.selection.totp, `div.${TOTP_FIELD_CLASS}`);
|
||||
kpxcCustomLoginFieldsBanner.selection.totp = undefined;
|
||||
}
|
||||
|
||||
|
|
@ -265,25 +256,6 @@ kpxcCustomLoginFieldsBanner.stringFieldsButtonClicked = function(e) {
|
|||
sendMessageToFrames(e, 'string_field_button_clicked');
|
||||
};
|
||||
|
||||
kpxcCustomLoginFieldsBanner.submitButtonClicked = function(e) {
|
||||
if (!e.isTrusted || kpxcCustomLoginFieldsBanner.dataStep === STEP_SELECT_SUBMIT_BUTTON) {
|
||||
kpxcCustomLoginFieldsBanner.backToStart();
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset TOTP field selection if already set
|
||||
if (kpxcCustomLoginFieldsBanner.selection.submitButton) {
|
||||
kpxcCustomLoginFieldsBanner.removeSelection(kpxcCustomLoginFieldsBanner.selection.submitButton,
|
||||
`div.${STRING_FIELD_CLASS}`);
|
||||
kpxcCustomLoginFieldsBanner.selection.submitButton = undefined;
|
||||
}
|
||||
|
||||
kpxcCustomLoginFieldsBanner.prepareSubmitButtonSelection();
|
||||
kpxcCustomLoginFieldsBanner.buttons.confirm.disabled = true;
|
||||
|
||||
sendMessageToFrames(e, 'submit_button_clicked');
|
||||
};
|
||||
|
||||
kpxcCustomLoginFieldsBanner.closeButtonClicked = function(e) {
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
|
|
@ -310,8 +282,6 @@ kpxcCustomLoginFieldsBanner.updateFieldSelections = function() {
|
|||
kpxcCustomLoginFieldsBanner.prepareTOTPSelection();
|
||||
} else if (kpxcCustomLoginFieldsBanner.dataStep === STEP_SELECT_STRING_FIELDS) {
|
||||
kpxcCustomLoginFieldsBanner.prepareStringFieldSelection();
|
||||
} else if (kpxcCustomLoginFieldsBanner.dataStep === STEP_SELECT_SUBMIT_BUTTON) {
|
||||
kpxcCustomLoginFieldsBanner.prepareSubmitButtonSelection();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -344,8 +314,6 @@ kpxcCustomLoginFieldsBanner.confirm = async function(e) {
|
|||
kpxc.settings[DEFINED_CUSTOM_FIELDS][location].password = undefined;
|
||||
} else if (currentSite.totp?.[0] === path[0]) {
|
||||
kpxc.settings[DEFINED_CUSTOM_FIELDS][location].totp = undefined;
|
||||
} else if (currentSite.submitButton?.[0] === path[0]) {
|
||||
kpxc.settings[DEFINED_CUSTOM_FIELDS][location].submitButton = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -353,11 +321,10 @@ kpxcCustomLoginFieldsBanner.confirm = async function(e) {
|
|||
const passwordPath = kpxcCustomLoginFieldsBanner.selection.password;
|
||||
const totpPath = kpxcCustomLoginFieldsBanner.selection.totp;
|
||||
const stringFieldsPaths = kpxcCustomLoginFieldsBanner.selection.fields;
|
||||
const submitButtonPath = kpxcCustomLoginFieldsBanner.selection.submitButton;
|
||||
const location = kpxc.getDocumentLocation();
|
||||
const currentSettings = kpxc.settings[DEFINED_CUSTOM_FIELDS][location];
|
||||
|
||||
if (usernamePath || passwordPath || totpPath || stringFieldsPaths.length > 0 || submitButtonPath) {
|
||||
if (usernamePath || passwordPath || totpPath || stringFieldsPaths.length > 0) {
|
||||
if (currentSettings) {
|
||||
// Update the single selection to current settings
|
||||
if (usernamePath) {
|
||||
|
|
@ -378,19 +345,13 @@ kpxcCustomLoginFieldsBanner.confirm = async function(e) {
|
|||
if (stringFieldsPaths.length > 0) {
|
||||
kpxc.settings[DEFINED_CUSTOM_FIELDS][location].fields = stringFieldsPaths;
|
||||
}
|
||||
|
||||
if (submitButtonPath) {
|
||||
clearIdenticalField(submitButtonPath, location);
|
||||
kpxc.settings[DEFINED_CUSTOM_FIELDS][location].submitButton = submitButtonPath;
|
||||
}
|
||||
} else {
|
||||
// Override all fields (default, because there's no currentSettings available)
|
||||
kpxc.settings[DEFINED_CUSTOM_FIELDS][location] = {
|
||||
username: usernamePath,
|
||||
password: passwordPath,
|
||||
totp: totpPath,
|
||||
fields: stringFieldsPaths,
|
||||
submitButton: submitButtonPath,
|
||||
fields: stringFieldsPaths
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -420,8 +381,7 @@ kpxcCustomLoginFieldsBanner.resetSelection = function() {
|
|||
username: undefined,
|
||||
password: undefined,
|
||||
totp: undefined,
|
||||
fields: [],
|
||||
submitButton: undefined,
|
||||
fields: []
|
||||
};
|
||||
|
||||
kpxcCustomLoginFieldsBanner.removeMarkedFields();
|
||||
|
|
@ -463,24 +423,15 @@ kpxcCustomLoginFieldsBanner.prepareStringFieldSelection = function() {
|
|||
kpxcCustomLoginFieldsBanner.selectStringFields();
|
||||
};
|
||||
|
||||
kpxcCustomLoginFieldsBanner.prepareSubmitButtonSelection = function() {
|
||||
kpxcCustomLoginFieldsBanner.infoText.textContent = tr('defineChooseSubmitButton');
|
||||
kpxcCustomLoginFieldsBanner.dataStep = STEP_SELECT_SUBMIT_BUTTON;
|
||||
kpxcCustomLoginFieldsBanner.buttons.submitButton.classList.remove(GRAY_BUTTON_CLASS);
|
||||
kpxcCustomLoginFieldsBanner.selectField('submitButton');
|
||||
};
|
||||
|
||||
kpxcCustomLoginFieldsBanner.isFieldSelected = function(field) {
|
||||
const currentFieldId = kpxcFields.setId(field);
|
||||
const selection = kpxcCustomLoginFieldsBanner.selection;
|
||||
|
||||
if (kpxcCustomLoginFieldsBanner.markedFields.some(f => f === field)) {
|
||||
return (
|
||||
(selection.username && selection.usernameElement === field)
|
||||
|| (selection.password && selection.passwordElement === field)
|
||||
|| (selection.totp && selection.totpElement === field)
|
||||
|| (selection.submitButton && selection.submitButton === field)
|
||||
|| selection.fields.some(f => f[0] === currentFieldId[0])
|
||||
(kpxcCustomLoginFieldsBanner.selection.username && kpxcCustomLoginFieldsBanner.selection.usernameElement === field)
|
||||
|| (kpxcCustomLoginFieldsBanner.selection.password && kpxcCustomLoginFieldsBanner.selection.passwordElement === field)
|
||||
|| (kpxcCustomLoginFieldsBanner.selection.totp && kpxcCustomLoginFieldsBanner.selection.totpElement === field)
|
||||
|| kpxcCustomLoginFieldsBanner.selection.fields.some(f => f[0] === currentFieldId[0])
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -509,7 +460,7 @@ kpxcCustomLoginFieldsBanner.setSelectedField = function(elem) {
|
|||
kpxcCustomLoginFieldsBanner.buttons.close.textContent = tr('optionsButtonCancel');
|
||||
};
|
||||
|
||||
// Expects 'username', 'password', 'totp' or 'submitButton'
|
||||
// Expects 'username', 'password' or 'totp'
|
||||
kpxcCustomLoginFieldsBanner.selectField = function(fieldType) {
|
||||
kpxcCustomLoginFieldsBanner.eventFieldClick = function(e) {
|
||||
const field = kpxcCustomLoginFieldsBanner.getSelectedField(e);
|
||||
|
|
@ -565,10 +516,9 @@ kpxcCustomLoginFieldsBanner.selectStringFields = function() {
|
|||
kpxcCustomLoginFieldsBanner.markFields = function() {
|
||||
let firstInput;
|
||||
const inputs = document.querySelectorAll(
|
||||
kpxcCustomLoginFieldsBanner.dataStep === STEP_SELECT_SUBMIT_BUTTON ? INPUT_BUTTON_QUERY_PATTERN :
|
||||
(STEP_SELECT_STRING_FIELDS
|
||||
? kpxcCustomLoginFieldsBanner.inputQueryPatternStringFields
|
||||
: kpxcCustomLoginFieldsBanner.inputQueryPatternNormal));
|
||||
kpxcCustomLoginFieldsBanner.dataStep === STEP_SELECT_STRING_FIELDS
|
||||
? kpxcCustomLoginFieldsBanner.inputQueryPatternStringFields
|
||||
: kpxcCustomLoginFieldsBanner.inputQueryPatternNormal);
|
||||
const zoom = kpxcUI.bodyStyle.zoom || 1;
|
||||
|
||||
for (const i of inputs) {
|
||||
|
|
@ -594,7 +544,7 @@ kpxcCustomLoginFieldsBanner.markFields = function() {
|
|||
if (kpxcCustomLoginFieldsBanner.dataStep !== STEP_SELECT_STRING_FIELDS) {
|
||||
field.textContent = dataStepToString();
|
||||
}
|
||||
|
||||
|
||||
// Static size for the checkbox overlay
|
||||
if (i?.getLowerCaseAttribute('type') === 'checkbox') {
|
||||
field.style.width = Pixels(CHECKBOX_OVERLAY_SIZE / zoom);
|
||||
|
|
@ -724,10 +674,7 @@ kpxcCustomLoginFieldsBanner.handleTopWindowMessage = function(args) {
|
|||
kpxcCustomLoginFieldsBanner.selection.totp = selection;
|
||||
kpxcCustomLoginFieldsBanner.setSelectedField();
|
||||
} else if (message === 'string_field_selected') {
|
||||
kpxcCustomLoginFieldsBanner.selection.fields = selection;
|
||||
kpxcCustomLoginFieldsBanner.setSelectedField();
|
||||
} else if (message === 'submitButton_selected') {
|
||||
kpxcCustomLoginFieldsBanner.selection.submitButton = selection;
|
||||
kpxcCustomLoginFieldsBanner.selection.stringFields = selection;
|
||||
kpxcCustomLoginFieldsBanner.setSelectedField();
|
||||
} else if (message === 'enable_clear_data_button') {
|
||||
kpxcCustomLoginFieldsBanner.buttons.clearData.style.display = 'inline-block';
|
||||
|
|
@ -752,8 +699,6 @@ kpxcCustomLoginFieldsBanner.handleParentWindowMessage = function(args) {
|
|||
kpxcCustomLoginFieldsBanner.totpButtonClicked(e);
|
||||
} else if (message === 'string_field_button_clicked') {
|
||||
kpxcCustomLoginFieldsBanner.stringFieldsButtonClicked(e);
|
||||
} else if (message === 'submit_button_clicked') {
|
||||
kpxcCustomLoginFieldsBanner.submitButtonClicked(e);
|
||||
} else if (message === 'reset_button_clicked') {
|
||||
kpxcCustomLoginFieldsBanner.reset();
|
||||
} else if (message === 'close_button_clicked') {
|
||||
|
|
@ -821,7 +766,5 @@ const dataStepToString = function() {
|
|||
return tr('totp');
|
||||
} else if (kpxcCustomLoginFieldsBanner.dataStep === STEP_SELECT_STRING_FIELDS) {
|
||||
return tr('defineStringField');
|
||||
} else if (kpxcCustomLoginFieldsBanner.dataStep === STEP_SELECT_SUBMIT_BUTTON) {
|
||||
return tr('defineSubmitButton');
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ const MAX_SEGMENTED_FIELD_LENGTH = 100;
|
|||
* Provides methods for input field handling.
|
||||
*/
|
||||
const kpxcFields = {};
|
||||
kpxcFields.popoverSupported = true;
|
||||
|
||||
// Returns all username & password combinations detected from the inputs.
|
||||
// After username field is detected, first password field found after that will be saved as a combination.
|
||||
|
|
@ -30,7 +29,7 @@ kpxcFields.getAllCombinations = async function(inputs) {
|
|||
form: input.form
|
||||
};
|
||||
|
||||
combinations.push(kpxcFields.getExistingCombination(combination));
|
||||
combinations.push(combination);
|
||||
usernameField = null;
|
||||
} else if (kpxcTOTPIcons.isValid(input)) {
|
||||
// Dynamically added TOTP field
|
||||
|
|
@ -94,32 +93,6 @@ kpxcFields.getCombinationFromAllInputs = function() {
|
|||
return kpxc.combinations[0];
|
||||
};
|
||||
|
||||
// Checks if existing combination is found and recognized fields are added to it
|
||||
kpxcFields.getExistingCombination = function(combination) {
|
||||
// Lookup existing combinations that use the same form
|
||||
const existingCombination = kpxc.combinations?.find(c => c.form === combination?.form);
|
||||
if (existingCombination) {
|
||||
// Replace values to the existing combination
|
||||
existingCombination.username ??= combination.username;
|
||||
existingCombination.password ??= combination.password;
|
||||
if (existingCombination.passwordInputs?.length === 0) {
|
||||
existingCombination.passwordInputs = combination.passwordInputs;
|
||||
} else if (combination?.password) {
|
||||
// If password field is found in the current combination, force assign it to the existing combination
|
||||
existingCombination.password = combination.password;
|
||||
}
|
||||
|
||||
// Remove username field from combination with certain sites (replaced by password input)
|
||||
if (kpxcSites.combinationExceptionFound(existingCombination)) {
|
||||
existingCombination.username = null;
|
||||
}
|
||||
|
||||
return existingCombination;
|
||||
}
|
||||
|
||||
return combination;
|
||||
};
|
||||
|
||||
// Adds segmented TOTP fields to the combination if found
|
||||
kpxcFields.getSegmentedTOTPFields = function(inputs, combinations) {
|
||||
if (!kpxc.settings.showOTPIcon) {
|
||||
|
|
@ -438,97 +411,6 @@ kpxcFields.isSearchField = function(target) {
|
|||
return false;
|
||||
};
|
||||
|
||||
// :popover-open selector is supported only with Firefox >= 125 and Chrome >= 114
|
||||
kpxcFields.discoverOverlays = function() {
|
||||
try {
|
||||
kpxcFields.overlays = document.querySelectorAll(':popover-open, [popover]');
|
||||
} catch (e) {
|
||||
// Ignore SyntaxError (e.g., unsupported selector)
|
||||
if (!(e instanceof SyntaxError)) {
|
||||
kpxcFields.popoverSupported = false;
|
||||
logError(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Checks if element has an overlay
|
||||
kpxcFields.hasOverlay = function(elem) {
|
||||
try {
|
||||
return elem?.hasAttribute('popover') || elem?.matches(':popover-open');
|
||||
} catch (e) {
|
||||
// Ignore SyntaxError (e.g., unsupported selector)
|
||||
if (!(e instanceof SyntaxError)) {
|
||||
kpxcFields.popoverSupported = false;
|
||||
logError(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Check the visibility of existing fields
|
||||
kpxcFields.checkExistingFields = function() {
|
||||
if (kpxc.inputs?.some(input => !kpxcFields.isVisible(input))) {
|
||||
kpxc.clearAllFromPage();
|
||||
kpxc.combinations = [];
|
||||
}
|
||||
};
|
||||
|
||||
// Check for popup overlays
|
||||
kpxcFields.isOverlayOnTop = function(rect) {
|
||||
for (const overlay of kpxcFields.overlays ?? []) {
|
||||
if (kpxcSites.overlayExceptionFound(overlay)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const overlayRect = overlay?.getBoundingClientRect();
|
||||
if (overlayRect && elementsOverlap(rect, overlayRect)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if element is the topmost element
|
||||
* @param {HTMLElement} elem Element to be checked
|
||||
* @param {DOMRect} rect Precalculated DOMRect of the element
|
||||
* @returns {boolean} True if element is the topmost
|
||||
*/
|
||||
kpxcFields.isTopElement = function(elem, rect) {
|
||||
if (!elem || !rect) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const rootNode = elem.getRootNode() ?? document;
|
||||
|
||||
// Returns the topmost element from point x, height/2
|
||||
// If the input has a label as the top element and it's inside the input, allow it.
|
||||
const getTopmostElement = (element, x, elementRect) => {
|
||||
const topElement = rootNode.elementFromPoint(x, elementRect.top + (elementRect.height / 2));
|
||||
return element?.labels &&
|
||||
element.labels[0] === topElement &&
|
||||
elementsOverlap(elementRect, topElement.getBoundingClientRect())
|
||||
? element
|
||||
: topElement;
|
||||
};
|
||||
|
||||
// Check topmost element from three points inside the input
|
||||
if (matchesWithNodeName(elem, 'INPUT') && [
|
||||
getTopmostElement(elem, rect.left + (rect.width / 4), rect), // First third
|
||||
getTopmostElement(elem, rect.left + (rect.width / 2), rect), // Middle
|
||||
getTopmostElement(elem, rect.left + (rect.width / 1.33), rect), // Last third
|
||||
].some((e) => e !== elem)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if element has an overlay
|
||||
if (kpxcFields.isOverlayOnTop(rect)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// Returns true if element is visible on the page
|
||||
kpxcFields.isVisible = function(elem) {
|
||||
// Returns true if opacity is not set, otherwise check the limits
|
||||
|
|
@ -548,10 +430,6 @@ kpxcFields.isVisible = function(elem) {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!kpxcFields.isTopElement(elem, rect)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check CSS visibility
|
||||
const elemStyle = getComputedStyle(elem);
|
||||
if (elemStyle.visibility && (elemStyle.visibility === 'hidden' || elemStyle.visibility === 'collapse')
|
||||
|
|
@ -594,14 +472,14 @@ kpxcFields.traverseParents = function(element, predicate, resultFn = () => true,
|
|||
kpxcFields.useCustomLoginFields = async function() {
|
||||
const location = kpxc.getDocumentLocation();
|
||||
const creds = kpxc.settings['defined-custom-fields'][location];
|
||||
if (!creds.username && !creds.password && !creds.totp && creds.fields.length === 0 && !creds.submitButton) {
|
||||
if (!creds.username && !creds.password && !creds.totp && creds.fields.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Finds the element based on the stored ID
|
||||
const findElement = async function(fields, idArray) {
|
||||
// Finds the input field based on the stored ID
|
||||
const findInputField = async function(inputFields, idArray) {
|
||||
if (idArray) {
|
||||
const input = fields.find(e => e === kpxcFields.getId(idArray, e));
|
||||
const input = inputFields.find(e => e === kpxcFields.getId(idArray, e));
|
||||
if (input) {
|
||||
return input;
|
||||
}
|
||||
|
|
@ -618,24 +496,16 @@ kpxcFields.useCustomLoginFields = async function() {
|
|||
}
|
||||
});
|
||||
|
||||
const buttons = [];
|
||||
document.body.querySelectorAll('button, input[type=submit]').forEach(e => {
|
||||
if (e.type !== 'hidden' && !e.disabled) {
|
||||
buttons.push(e);
|
||||
}
|
||||
});
|
||||
|
||||
const [ username, password, totp, submitButton ] = await Promise.all([
|
||||
await findElement(inputFields, creds.username),
|
||||
await findElement(inputFields, creds.password),
|
||||
await findElement(inputFields, creds.totp),
|
||||
await findElement(buttons, creds.submitButton),
|
||||
const [ username, password, totp ] = await Promise.all([
|
||||
await findInputField(inputFields, creds.username),
|
||||
await findInputField(inputFields, creds.password),
|
||||
await findInputField(inputFields, creds.totp)
|
||||
]);
|
||||
|
||||
// Handle StringFields
|
||||
const stringFields = [];
|
||||
for (const sf of creds.fields) {
|
||||
const field = await findElement(inputFields, sf);
|
||||
const field = await findInputField(inputFields, sf);
|
||||
if (field) {
|
||||
stringFields.push(field);
|
||||
}
|
||||
|
|
@ -647,19 +517,13 @@ kpxcFields.useCustomLoginFields = async function() {
|
|||
kpxcTOTPIcons.newIcon(totp, kpxc.databaseState);
|
||||
}
|
||||
|
||||
// No values found
|
||||
if (!username && !password && !totp && !submitButton && stringFields?.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const combinations = [];
|
||||
combinations.push({
|
||||
username: username,
|
||||
password: password,
|
||||
passwordInputs: [ password ],
|
||||
totp: totp,
|
||||
fields: stringFields,
|
||||
submitButton: submitButton
|
||||
fields: stringFields
|
||||
});
|
||||
|
||||
return combinations;
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ kpxcFill.fillAttributeToActiveElementWith = async function(attr) {
|
|||
return;
|
||||
}
|
||||
|
||||
await kpxcFill.setValue(el, value[0]);
|
||||
kpxc.setValue(el, value[0]);
|
||||
};
|
||||
|
||||
// Fill requested from the context menu. Active element is used for combination detection
|
||||
|
|
@ -152,20 +152,20 @@ kpxcFill.fillTOTPFromUuid = async function(el, uuid) {
|
|||
return;
|
||||
}
|
||||
|
||||
await kpxcFill.setTOTPValue(el, totp);
|
||||
kpxcFill.setTOTPValue(el, totp);
|
||||
} else if (user.stringFields?.length > 0) {
|
||||
const stringFields = user.stringFields;
|
||||
for (const s of stringFields) {
|
||||
const val = s['KPH: {TOTP}'];
|
||||
if (val) {
|
||||
await kpxcFill.setTOTPValue(el, val);
|
||||
kpxcFill.setTOTPValue(el, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Set normal or segmented TOTP value
|
||||
kpxcFill.setTOTPValue = async function(elem, val) {
|
||||
kpxcFill.setTOTPValue = function(elem, val) {
|
||||
if (kpxc.credentials.length === 0) {
|
||||
logDebug('Error: Credential list is empty.');
|
||||
return;
|
||||
|
|
@ -173,22 +173,22 @@ kpxcFill.setTOTPValue = async function(elem, val) {
|
|||
|
||||
for (const comb of kpxc.combinations) {
|
||||
if (comb.totpInputs?.length > 0) {
|
||||
await kpxcFill.fillSegmentedTotp(elem, val, comb.totpInputs);
|
||||
kpxcFill.fillSegmentedTotp(elem, val, comb.totpInputs);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await kpxcFill.setValue(elem, val);
|
||||
kpxc.setValue(elem, val);
|
||||
};
|
||||
|
||||
// Fill TOTP in parts
|
||||
kpxcFill.fillSegmentedTotp = async function(elem, val, totpInputs) {
|
||||
kpxcFill.fillSegmentedTotp = function(elem, val, totpInputs) {
|
||||
if (!totpInputs.includes(elem) || val.length < totpInputs.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < totpInputs.length; ++i) {
|
||||
await kpxcFill.setValue(totpInputs[i], val[i]);
|
||||
kpxc.setValue(totpInputs[i], val[i]);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -217,7 +217,7 @@ kpxcFill.fillFromUsernameIcon = async function(combination) {
|
|||
*/
|
||||
kpxcFill.fillInCredentials = async function(combination, predefinedUsername, uuid, passOnly = false) {
|
||||
if (kpxc.credentials.length === 0) {
|
||||
showErrorNotification(`${tr('credentialsNoLoginsFound')} ${document.location.origin}`);
|
||||
kpxcUI.createNotification('error', tr('credentialsNoLoginsFound'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -245,17 +245,6 @@ kpxcFill.fillInCredentials = async function(combination, predefinedUsername, uui
|
|||
skipAutoSubmit = selectedCredentials.skipAutoSubmit === 'true';
|
||||
}
|
||||
|
||||
// Update the password field if form has been updated with a new identical element (Moodle).
|
||||
// If found, replace the new input field to the combination.
|
||||
if (combination?.form && combination?.password && !combination.form.contains(combination.password)) {
|
||||
const formInputs = kpxcObserverHelper.getInputs(combination.form);
|
||||
const newPasswordField = formInputs?.find((formInput) =>
|
||||
formInput?.getLowerCaseAttribute('type') === 'password');
|
||||
if (newPasswordField && areNamedNodeMapsEqual(combination.password.attributes, newPasswordField.attributes)) {
|
||||
combination.password = newPasswordField;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill password
|
||||
if (combination.password && matchesWithNodeName(combination.password, 'INPUT')) {
|
||||
// Show a notification if password length exceeds the length defined in input
|
||||
|
|
@ -271,28 +260,28 @@ kpxcFill.fillInCredentials = async function(combination, predefinedUsername, uui
|
|||
return;
|
||||
}
|
||||
|
||||
await kpxcFill.setValueWithChange(combination.password, selectedCredentials.password);
|
||||
kpxc.setValueWithChange(combination.password, selectedCredentials.password);
|
||||
await kpxc.setPasswordFilled(true);
|
||||
}
|
||||
|
||||
// Fill username
|
||||
if (combination.username && usernameValue && combination.username !== combination.password &&
|
||||
if (combination.username && usernameValue &&
|
||||
(!combination.username.value || combination.username.value !== usernameValue)) {
|
||||
if (!passOnly) {
|
||||
await kpxcFill.setValueWithChange(combination.username, usernameValue);
|
||||
kpxc.setValueWithChange(combination.username, usernameValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Fill StringFields
|
||||
if (selectedCredentials.stringFields?.length > 0) {
|
||||
await kpxcFill.fillInStringFields(combination.fields, selectedCredentials.stringFields);
|
||||
kpxcFill.fillInStringFields(combination.fields, selectedCredentials.stringFields);
|
||||
}
|
||||
|
||||
// Fill TOTP
|
||||
if (kpxc.settings.autoFillSingleTotp && kpxc.entryHasTotp(selectedCredentials)) {
|
||||
const totpCombination = combination?.totp || kpxc.combinations?.find(c => c.totp);
|
||||
if (totpCombination?.totp) {
|
||||
await kpxcFill.fillTOTPFromUuid(totpCombination.totp, selectedCredentials.uuid);
|
||||
kpxcFill.fillTOTPFromUuid(totpCombination.totp, selectedCredentials.uuid);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -306,7 +295,7 @@ kpxcFill.fillInCredentials = async function(combination, predefinedUsername, uui
|
|||
};
|
||||
|
||||
// Fills StringFields defined in Custom Fields
|
||||
kpxcFill.fillInStringFields = async function(fields, stringFields) {
|
||||
kpxcFill.fillInStringFields = function(fields, stringFields) {
|
||||
const filledInFields = [];
|
||||
if (fields && stringFields && fields?.length > 0 && stringFields?.length > 0) {
|
||||
for (let i = 0; i < fields.length; i++) {
|
||||
|
|
@ -318,7 +307,7 @@ kpxcFill.fillInStringFields = async function(fields, stringFields) {
|
|||
const currentField = fields[i];
|
||||
|
||||
if (currentField && stringFieldValue[0]) {
|
||||
await kpxcFill.setValue(currentField, stringFieldValue[0], true);
|
||||
kpxc.setValue(currentField, stringFieldValue[0], true);
|
||||
filledInFields.push(currentField);
|
||||
}
|
||||
}
|
||||
|
|
@ -340,8 +329,7 @@ kpxcFill.performAutoSubmit = async function(combination, skipAutoSubmit) {
|
|||
if (!skipAutoSubmit && !autoSubmitIgnoredForSite) {
|
||||
await sendMessage('page_set_autosubmit_performed');
|
||||
|
||||
// Use submit button from Custom Login Fields or detect it from the form
|
||||
const submitButton = combination?.submitButton ?? kpxcForm.getFormSubmitButton(combination.form);
|
||||
const submitButton = kpxcForm.getFormSubmitButton(combination.form);
|
||||
if (submitButton !== undefined) {
|
||||
submitButton.click();
|
||||
} else if (combination.form) {
|
||||
|
|
@ -352,79 +340,6 @@ kpxcFill.performAutoSubmit = async function(combination, skipAutoSubmit) {
|
|||
}
|
||||
};
|
||||
|
||||
// Special handling for setting value to select and checkbox elements
|
||||
kpxcFill.setValue = async function(field, value, forced = false) {
|
||||
if (field.matches('select')) {
|
||||
value = value.toLowerCase().trim();
|
||||
const options = field.querySelectorAll('option');
|
||||
|
||||
for (const o of options) {
|
||||
if (o.textContent.toLowerCase().trim() === value) {
|
||||
await kpxcFill.setValueWithChange(field, o.value);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
} else if (field.getLowerCaseAttribute('type') === 'checkbox' && value?.toLowerCase() === 'true') {
|
||||
field.checked = true;
|
||||
}
|
||||
|
||||
// Make sure the input is not wrapped inside another element (custom INPUT element)
|
||||
if (field?.nodeName !== 'INPUT' && field?.nodeName?.includes('INPUT')) {
|
||||
const childInput = field?.querySelector('input');
|
||||
const fieldsFromShadowDOM = kpxcObserverHelper.findInputsFromShadowDOM(field);
|
||||
field = childInput ?? fieldsFromShadowDOM[0];
|
||||
}
|
||||
|
||||
|
||||
await kpxcFill.setValueWithChange(field, value, forced);
|
||||
};
|
||||
|
||||
// Sets a new value to input field and triggers necessary events
|
||||
kpxcFill.setValueWithChange = async function(field, value, forced = false) {
|
||||
if (!field || (field?.readOnly && !forced)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for overlays before fill
|
||||
kpxcFields.discoverOverlays();
|
||||
const rect = field.getBoundingClientRect();
|
||||
if (kpxcFields.isOverlayOnTop(rect)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dispatchLegacyEvent = function(elem, eventName) {
|
||||
const legacyEvent = elem.ownerDocument.createEvent('Event');
|
||||
legacyEvent.initEvent(eventName, true, false);
|
||||
elem.dispatchEvent(legacyEvent);
|
||||
};
|
||||
|
||||
field.focus();
|
||||
|
||||
// Use a delay to allow focus events to trigger and to give some
|
||||
// breathing room to frameworks that rely on promises to update their
|
||||
// state, like React. Not doing so can break OTP input, see issue #2215.
|
||||
await Promise.resolve();
|
||||
|
||||
field.dispatchEvent(new FocusEvent('focus', { bubbles: false, cancelable: false }));
|
||||
field.dispatchEvent(new FocusEvent('focusin', { bubbles: true, cancelable: false }));
|
||||
|
||||
// https://w3c.github.io/uievents/#keypress-event-order
|
||||
field.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, cancelable: false, key: value }));
|
||||
field.dispatchEvent(new InputEvent('beforeinput', { bubbles: true, cancelable: false, inputType: 'insertText', data: value }));
|
||||
field.dispatchEvent(new KeyboardEvent('keypress', { bubbles: true, cancelable: false, key: value }));
|
||||
field.value = value;
|
||||
field.dispatchEvent(new Event('input', { bubbles: true, cancelable: false }));
|
||||
field.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true, cancelable: false, key: value }));
|
||||
field.dispatchEvent(new Event('change', { bubbles: true, cancelable: false }));
|
||||
|
||||
// Some pages will not accept the value change without dispatching events directly to the document
|
||||
dispatchLegacyEvent(field, 'input');
|
||||
dispatchLegacyEvent(field, 'change');
|
||||
};
|
||||
|
||||
|
||||
// Check if password fill is done to a plain text field
|
||||
const passwordFillIsAllowed = function(elem) {
|
||||
const elementIsPasswordField =
|
||||
|
|
@ -441,35 +356,9 @@ const passwordFillIsAllowed = function(elem) {
|
|||
// Show a specific error notification if current database is not connected
|
||||
const showErrorNotification = async function(errorMessage, notificationType = 'error') {
|
||||
const connectedDatabase = await sendMessage('get_connected_database');
|
||||
if (!connectedDatabase?.identifier && kpxc.databaseState === DatabaseState.UNLOCKED) {
|
||||
if (!connectedDatabase?.identifier) {
|
||||
kpxcUI.createNotification('error', tr('errorCurrentDatabaseNotConnected'));
|
||||
} else if (!await isIframeAllowed()) {
|
||||
// Special error if we are blocking due to iframe
|
||||
kpxcUI.createNotification('error', tr('credentialsBlockedInIframe'));
|
||||
} else {
|
||||
kpxcUI.createNotification(notificationType, errorMessage);
|
||||
}
|
||||
};
|
||||
|
||||
// Checks if two NamedNodeMaps (attribute lists) are equal
|
||||
const areNamedNodeMapsEqual = function(currentNodeMap, newNodeMap) {
|
||||
if (!currentNodeMap || !newNodeMap) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const fieldAttributes = Array.from(currentNodeMap);
|
||||
const newFieldAttributes = Array.from(newNodeMap);
|
||||
|
||||
if (fieldAttributes.length !== newFieldAttributes.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const attr of fieldAttributes) {
|
||||
const newAttr = newFieldAttributes.find((newAttr) => newAttr?.name === attr?.name);
|
||||
if (!newAttr || newAttr?.value !== attr?.value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ kpxcForm.activateCredentialBanner = async function(usernameValue, passwordInputs
|
|||
return;
|
||||
}
|
||||
|
||||
if (passwordField && kpxcFields.isVisible(passwordField)) {
|
||||
if (passwordField) {
|
||||
await kpxc.setPasswordFilled(true);
|
||||
}
|
||||
|
||||
|
|
@ -66,9 +66,6 @@ kpxcForm.getFormSubmitButton = function(form) {
|
|||
return;
|
||||
}
|
||||
|
||||
const hasPasswordClassOrId = (button) => button
|
||||
&& (button?.classList.value?.toLowerCase()?.includes('password')
|
||||
|| button?.id?.toLowerCase()?.includes('password'));
|
||||
const action = kpxc.submitUrl || form.action;
|
||||
|
||||
// Check if the site needs a special handling for retrieving the form submit button
|
||||
|
|
@ -91,19 +88,13 @@ kpxcForm.getFormSubmitButton = function(form) {
|
|||
b => !b.getAttribute('formAction')
|
||||
);
|
||||
if (buttons.length > 0) {
|
||||
const lastButton = buttons.at(-1);
|
||||
// Accept button if it has no indication for password
|
||||
if (!hasPasswordClassOrId(lastButton)) {
|
||||
return buttons.at(-1);
|
||||
}
|
||||
return buttons.at(-1);
|
||||
}
|
||||
|
||||
// Try to find similar buttons outside the form which are added via 'form' property
|
||||
for (const e of form.elements) {
|
||||
const isSubmitButton = matchesWithNodeName(e, 'BUTTON')
|
||||
&& (e.type === 'button' || e.type === 'submit' || e.type === '');
|
||||
const isInputButton = matchesWithNodeName(e, 'INPUT') && (e.type === 'button' || e.type === 'submit');
|
||||
if ((isSubmitButton || isInputButton) && !hasPasswordClassOrId(e)) {
|
||||
if ((matchesWithNodeName(e, 'BUTTON') && (e.type === 'button' || e.type === 'submit' || e.type === ''))
|
||||
|| (matchesWithNodeName(e, 'INPUT') && (e.type === 'button' || e.type === 'submit'))) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
|
@ -125,9 +116,9 @@ kpxcForm.getNewPassword = function(passwordInputs = []) {
|
|||
}
|
||||
|
||||
// Choose the last three password fields. The first ones are almost always for something else
|
||||
const current = passwordInputs[passwordInputs.length - 3]?.value;
|
||||
const newPass = passwordInputs[passwordInputs.length - 2]?.value;
|
||||
const repeatNew = passwordInputs[passwordInputs.length - 1]?.value;
|
||||
const current = passwordInputs[passwordInputs.length - 3].value;
|
||||
const newPass = passwordInputs[passwordInputs.length - 2].value;
|
||||
const repeatNew = passwordInputs[passwordInputs.length - 1].value;
|
||||
|
||||
if ((newPass === repeatNew && current !== newPass && current !== repeatNew)
|
||||
|| (current === newPass && repeatNew !== newPass && repeatNew !== current)) {
|
||||
|
|
@ -175,8 +166,6 @@ kpxcForm.initForm = function(form, credentialFields) {
|
|||
if (submitButton) {
|
||||
submitButton.addEventListener('click', kpxcForm.onSubmit);
|
||||
}
|
||||
|
||||
kpxcUI.pageObserver.observe(form, OBSERVER_OPTIONS);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,248 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* @Object kpxcIcons
|
||||
* Icon handling.
|
||||
*/
|
||||
const kpxcIcons = {};
|
||||
kpxcIcons.icons = [];
|
||||
kpxcIcons.iconTypes = {
|
||||
DEFAULT: 0, // Username icon
|
||||
PASSWORD: 1,
|
||||
TOTP: 2
|
||||
};
|
||||
|
||||
// Adds an icon to input field
|
||||
kpxcIcons.addIcon = async function(field, iconType) {
|
||||
if (!field || !Object.values(kpxcIcons.iconTypes).includes(iconType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let iconSet = false;
|
||||
if (iconType === kpxcIcons.iconTypes.DEFAULT && kpxcUsernameIcons.isValid(field)) {
|
||||
kpxcUsernameIcons.newIcon(field, kpxc.databaseState);
|
||||
iconSet = true;
|
||||
} else if (iconType === kpxcIcons.iconTypes.PASSWORD && kpxcPasswordIcons.isValid(field)) {
|
||||
kpxcPasswordIcons.newIcon(field, kpxc.databaseState);
|
||||
iconSet = true;
|
||||
} else if (iconType === kpxcIcons.iconTypes.TOTP && kpxcTOTPIcons.isValid(field)) {
|
||||
kpxcTOTPIcons.newIcon(field, kpxc.databaseState);
|
||||
iconSet = true;
|
||||
}
|
||||
|
||||
if (iconSet) {
|
||||
kpxcIcons.icons.push({
|
||||
field: field,
|
||||
iconType: iconType
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Adds all icons from a form struct
|
||||
kpxcIcons.addIconsFromForm = async function(form) {
|
||||
const addUsernameIcons = async function(c) {
|
||||
if (kpxc.settings.showLoginFormIcon && await kpxc.passwordFilledWithExceptions(c) === false) {
|
||||
// Special case where everything else has been hidden, but a single password field is now displayed.
|
||||
// For example PayPal and Amazon is handled like this.
|
||||
if (c.username && !c.password && c.passwordInputs.length === 1) {
|
||||
// Use password input directly from form if found (Epicgames)
|
||||
const passwordField = c.form?.querySelector('input[type=password]');
|
||||
if (c.form && passwordField) {
|
||||
kpxcIcons.addIcon(passwordField, kpxcIcons.iconTypes.DEFAULT);
|
||||
} else {
|
||||
kpxcIcons.addIcon(c.passwordInputs[0], kpxcIcons.iconTypes.DEFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
if (c.username && !c.username.readOnly) {
|
||||
kpxcIcons.addIcon(c.username, kpxcIcons.iconTypes.DEFAULT);
|
||||
} else if (c.password && (!c.username || (c.username && c.username.readOnly))) {
|
||||
// Single password field
|
||||
kpxcIcons.addIcon(c.password, kpxcIcons.iconTypes.DEFAULT);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const addPasswordIcons = async function(c) {
|
||||
// Show password icons also with forms without any username field
|
||||
if (kpxc.settings.usePasswordGeneratorIcons
|
||||
&& ((c.username && c.password) || (!c.username && c.passwordInputs.length > 0))) {
|
||||
for (const input of c.passwordInputs) {
|
||||
kpxcIcons.addIcon(input, kpxcIcons.iconTypes.PASSWORD);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const addTOTPIcons = async function(c) {
|
||||
if (c.totp && kpxc.settings.showOTPIcon) {
|
||||
kpxcIcons.addIcon(c.totp, kpxcIcons.iconTypes.TOTP);
|
||||
}
|
||||
};
|
||||
|
||||
await Promise.all([
|
||||
await addUsernameIcons(form),
|
||||
await addPasswordIcons(form),
|
||||
await addTOTPIcons(form)
|
||||
]);
|
||||
};
|
||||
|
||||
kpxcIcons.calculateIconOffset = function(field, size) {
|
||||
const offset = Math.floor((field.offsetHeight / 2) - (size / 2) - 1);
|
||||
return (offset < 0) ? 0 : offset;
|
||||
};
|
||||
|
||||
// Delete all icons that have been hidden from the page view
|
||||
kpxcIcons.deleteAllHiddenIcons = function() {
|
||||
kpxcIcons.deleteIcons(kpxcUsernameIcons.icons);
|
||||
kpxcIcons.deleteIcons(kpxcPasswordIcons.icons);
|
||||
kpxcIcons.deleteIcons(kpxcTOTPIcons.icons);
|
||||
};
|
||||
|
||||
// Delete hidden icons from the list
|
||||
kpxcIcons.deleteIcons = function(iconList) {
|
||||
const deletedIcons = [];
|
||||
for (const icon of iconList) {
|
||||
if (icon.inputField && !kpxcFields.isVisible(icon.inputField)) {
|
||||
const index = iconList.indexOf(icon);
|
||||
icon.removeIcon();
|
||||
iconList.splice(index, 1);
|
||||
deletedIcons.push(icon.inputField);
|
||||
|
||||
// Delete the input field from detected fields so the icon can be detected again
|
||||
const inputFieldIndex = kpxc.inputs.indexOf(icon.inputField);
|
||||
if (inputFieldIndex >= 0) {
|
||||
kpxc.inputs.splice(inputFieldIndex, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the same icons from kpxcIcons.icons array
|
||||
for (const input of deletedIcons) {
|
||||
const index = kpxcIcons.icons.findIndex(e => e.field === input);
|
||||
if (index >= 0) {
|
||||
kpxcIcons.icons.splice(index, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Initializes all icons needed to be shown
|
||||
kpxcIcons.initIcons = async function(combinations = []) {
|
||||
if (combinations.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const form of kpxcForm.savedForms) {
|
||||
await kpxcIcons.addIconsFromForm(form);
|
||||
}
|
||||
|
||||
// Check for other combinations that are not in any form,
|
||||
// or there's a form that wasn't present in savedForms (and it's not null)
|
||||
for (const c of combinations) {
|
||||
if (!c.form || (c.form && !kpxcForm.savedForms.some(sf => sf.form === c.form))) {
|
||||
await kpxcIcons.addIconsFromForm(c);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
kpxcIcons.hasIcon = function(field) {
|
||||
return !field ? false : kpxcIcons.icons.some(i => i.field === field);
|
||||
};
|
||||
|
||||
kpxcIcons.monitorIconPosition = function(iconClass) {
|
||||
// Handle icon position on resize
|
||||
window.addEventListener('resize', function(e) {
|
||||
kpxcIcons.updateIconPosition(iconClass);
|
||||
});
|
||||
|
||||
// Handle icon position on scroll
|
||||
window.addEventListener('scroll', function(e) {
|
||||
kpxcIcons.updateIconPosition(iconClass);
|
||||
});
|
||||
|
||||
window.addEventListener('transitionend', function(e) {
|
||||
if (matchesWithNodeName(e.target, 'INPUT') || matchesWithNodeName(e.target, 'TEXTAREA')) {
|
||||
kpxcIcons.updateIconPosition(iconClass);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
kpxcIcons.setIconPosition = function(icon, field, rtl = false, segmented = false) {
|
||||
const rect = field.getBoundingClientRect();
|
||||
const size = Number(icon.getAttribute('size'));
|
||||
const offset = kpxcIcons.calculateIconOffset(field, size);
|
||||
const zoom = kpxcUI.bodyStyle.zoom || 1;
|
||||
let left = kpxcUI.getRelativeLeftPosition(rect) / zoom;
|
||||
let top = kpxcUI.getRelativeTopPosition(rect) / zoom;
|
||||
|
||||
// Add more space for the icon to show it at the right side of the field if TOTP fields are segmented
|
||||
if (segmented) {
|
||||
left += size + 10;
|
||||
}
|
||||
|
||||
// Adjusts the icon offset for certain sites
|
||||
const iconOffset = kpxcSites.iconOffset(left, top, size, field?.getLowerCaseAttribute('type'));
|
||||
if (iconOffset) {
|
||||
left = iconOffset[0];
|
||||
top = iconOffset[1];
|
||||
}
|
||||
|
||||
const scrollTop = kpxcUI.getScrollTop() / zoom;
|
||||
const scrollLeft = kpxcUI.getScrollLeft() / zoom;
|
||||
icon.style.top = Pixels(top + scrollTop + offset + 1);
|
||||
icon.style.left = rtl
|
||||
? Pixels(left + scrollLeft + offset)
|
||||
: Pixels(left + scrollLeft + field.offsetWidth - size - offset);
|
||||
};
|
||||
|
||||
// Sets the icons to corresponding database lock status
|
||||
kpxcIcons.switchIcons = async function() {
|
||||
const uuid = await sendMessage('page_get_login_id');
|
||||
|
||||
kpxcUsernameIcons.switchIcon(kpxc.databaseState, uuid);
|
||||
kpxcPasswordIcons.switchIcon(kpxc.databaseState, uuid);
|
||||
kpxcTOTPIcons.switchIcon(kpxc.databaseState, uuid);
|
||||
};
|
||||
|
||||
/**
|
||||
* Detects if the input field appears or disappears -> show/hide the icon
|
||||
* - boundingClientRect with slightly (< -10) negative values -> hidden
|
||||
* - intersectionRatio === 0 -> hidden
|
||||
* - isIntersecting === false -> hidden
|
||||
* - intersectionRatio > 0 -> shown
|
||||
* - isIntersecting === true -> shown
|
||||
*/
|
||||
kpxcIcons.updateFromIntersectionObserver = function(iconClass, entries) {
|
||||
for (const entry of entries) {
|
||||
const rect = DOMRectToArray(entry.boundingClientRect);
|
||||
|
||||
if ((entry.intersectionRatio === 0 && !entry.isIntersecting) || (rect.some(x => x < -10))) {
|
||||
iconClass.icon.style.display = 'none';
|
||||
} else if (entry.intersectionRatio > 0 && entry.isIntersecting) {
|
||||
iconClass.icon.style.display = 'block';
|
||||
|
||||
// Wait for possible DOM animations
|
||||
setTimeout(() => {
|
||||
kpxcIcons.setIconPosition(iconClass.icon, entry.target, iconClass.rtl, iconClass.segmented);
|
||||
}, 400);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
kpxcIcons.updateIconPosition = function(iconClass) {
|
||||
if (iconClass.inputField && iconClass.icon) {
|
||||
kpxcIcons.setIconPosition(iconClass.icon, iconClass.inputField, iconClass.rtl, iconClass.segmented);
|
||||
}
|
||||
};
|
||||
|
||||
const DOMRectToArray = function (domRect) {
|
||||
return [
|
||||
domRect.bottom,
|
||||
domRect.height,
|
||||
domRect.left,
|
||||
domRect.right,
|
||||
domRect.top,
|
||||
domRect.width,
|
||||
domRect.x,
|
||||
domRect.y,
|
||||
];
|
||||
};
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const MIN_ICON_SIZE = 14;
|
||||
const MAX_ICON_SIZE = 24;
|
||||
|
||||
// Basic icon class
|
||||
class Icon {
|
||||
constructor(field, databaseState = DatabaseState.DISCONNECTED, segmented = false) {
|
||||
this.databaseState = databaseState;
|
||||
this.icon = null;
|
||||
this.inputField = null;
|
||||
this.rtl = kpxcUI.isRTL(field);
|
||||
this.segmented = segmented;
|
||||
|
||||
try {
|
||||
this.observer = new IntersectionObserver((entries) => {
|
||||
kpxcIcons.updateFromIntersectionObserver(this, entries);
|
||||
});
|
||||
} catch (err) {
|
||||
logError(err);
|
||||
}
|
||||
}
|
||||
|
||||
// Size the icon dynamically, but not greater than 24 or smaller than 14
|
||||
calculateIconSize(field) {
|
||||
return Math.max(Math.min(MAX_ICON_SIZE, field.offsetHeight - 4), MIN_ICON_SIZE);
|
||||
}
|
||||
|
||||
// Creates a wrapper div that has the icon in Shadow DOM
|
||||
createWrapper(styleSheetFilename) {
|
||||
const styleSheet = createStylesheet(styleSheetFilename);
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.style.all = 'unset';
|
||||
wrapper.style.display = 'none';
|
||||
|
||||
// Make sure the wrapper is positioned correctly without CSS styles affecting to it
|
||||
wrapper.style.position = 'absolute';
|
||||
wrapper.style.top = Pixels(0);
|
||||
wrapper.style.left = Pixels(0);
|
||||
|
||||
// Waits for stylesheet to load before displaying the element
|
||||
styleSheet.addEventListener('load', () => wrapper.style.display = 'block');
|
||||
|
||||
this.shadowRoot = wrapper.attachShadow({ mode: 'closed' });
|
||||
this.shadowRoot.append(styleSheet);
|
||||
this.shadowRoot.append(this.icon);
|
||||
document.body.append(wrapper);
|
||||
kpxcUI.observeWrapper(wrapper);
|
||||
}
|
||||
|
||||
removeIcon() {
|
||||
this.shadowRoot.removeChild(this.icon);
|
||||
document.body.removeChild(this.shadowRoot.host);
|
||||
}
|
||||
|
||||
switchIcon(state, uuid) {
|
||||
if (!this.icon) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (state === DatabaseState.UNLOCKED) {
|
||||
this.icon.style.filter = kpxc.credentials.length === 0 && !uuid ? 'saturate(0%)' : 'saturate(100%)';
|
||||
} else {
|
||||
this.icon.style.filter = 'saturate(0%)';
|
||||
}
|
||||
}
|
||||
}
|
||||
116
keepassxc-browser/content/icons.js
Normal file
116
keepassxc-browser/content/icons.js
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* @Object kpxcIcons
|
||||
* Icon handling.
|
||||
*/
|
||||
const kpxcIcons = {};
|
||||
kpxcIcons.icons = [];
|
||||
kpxcIcons.iconTypes = { USERNAME: 0, PASSWORD: 1, TOTP: 2 };
|
||||
|
||||
// Adds an icon to input field
|
||||
kpxcIcons.addIcon = async function(field, iconType) {
|
||||
if (!field || iconType < 0 || iconType > 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
let iconSet = false;
|
||||
if (iconType === kpxcIcons.iconTypes.USERNAME && kpxcUsernameIcons.isValid(field)) {
|
||||
kpxcUsernameIcons.newIcon(field, kpxc.databaseState);
|
||||
iconSet = true;
|
||||
} else if (iconType === kpxcIcons.iconTypes.PASSWORD && kpxcPasswordIcons.isValid(field)) {
|
||||
kpxcPasswordIcons.newIcon(field, kpxc.databaseState);
|
||||
iconSet = true;
|
||||
} else if (iconType === kpxcIcons.iconTypes.TOTP && kpxcTOTPIcons.isValid(field)) {
|
||||
kpxcTOTPIcons.newIcon(field, kpxc.databaseState);
|
||||
iconSet = true;
|
||||
}
|
||||
|
||||
if (iconSet) {
|
||||
kpxcIcons.icons.push({
|
||||
field: field,
|
||||
iconType: iconType
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Adds all icons from a form struct
|
||||
kpxcIcons.addIconsFromForm = async function(form) {
|
||||
const addUsernameIcons = async function(c) {
|
||||
if (kpxc.settings.showLoginFormIcon && await kpxc.passwordFilledWithExceptions(c) === false) {
|
||||
// Special case where everything else has been hidden, but a single password field is now displayed.
|
||||
// For example PayPal and Amazon is handled like this.
|
||||
if (c.username && !c.password && c.passwordInputs.length === 1) {
|
||||
kpxcIcons.addIcon(c.passwordInputs[0], kpxcIcons.iconTypes.USERNAME);
|
||||
}
|
||||
|
||||
if (c.username && !c.username.readOnly) {
|
||||
kpxcIcons.addIcon(c.username, kpxcIcons.iconTypes.USERNAME);
|
||||
} else if (c.password && (!c.username || (c.username && c.username.readOnly))) {
|
||||
// Single password field
|
||||
kpxcIcons.addIcon(c.password, kpxcIcons.iconTypes.USERNAME);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const addPasswordIcons = async function(c) {
|
||||
// Show password icons also with forms without any username field
|
||||
if (kpxc.settings.usePasswordGeneratorIcons
|
||||
&& ((c.username && c.password) || (!c.username && c.passwordInputs.length > 0))) {
|
||||
for (const input of c.passwordInputs) {
|
||||
kpxcIcons.addIcon(input, kpxcIcons.iconTypes.PASSWORD);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const addTOTPIcons = async function(c) {
|
||||
if (c.totp && kpxc.settings.showOTPIcon) {
|
||||
kpxcIcons.addIcon(c.totp, kpxcIcons.iconTypes.TOTP);
|
||||
}
|
||||
};
|
||||
|
||||
await Promise.all([
|
||||
await addUsernameIcons(form),
|
||||
await addPasswordIcons(form),
|
||||
await addTOTPIcons(form)
|
||||
]);
|
||||
};
|
||||
|
||||
// Delete all icons that have been hidden from the page view
|
||||
kpxcIcons.deleteHiddenIcons = function() {
|
||||
kpxcUsernameIcons.deleteHiddenIcons();
|
||||
kpxcPasswordIcons.deleteHiddenIcons();
|
||||
kpxcTOTPIcons.deleteHiddenIcons();
|
||||
};
|
||||
|
||||
// Initializes all icons needed to be shown
|
||||
kpxcIcons.initIcons = async function(combinations = []) {
|
||||
if (combinations.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const form of kpxcForm.savedForms) {
|
||||
await kpxcIcons.addIconsFromForm(form);
|
||||
}
|
||||
|
||||
// Check for other combinations that are not in any form,
|
||||
// or there's a form that wasn't present in savedForms (and it's not null)
|
||||
for (const c of combinations) {
|
||||
if (!c.form || (c.form && !kpxcForm.savedForms.some(sf => sf.form === c.form))) {
|
||||
await kpxcIcons.addIconsFromForm(c);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
kpxcIcons.hasIcon = function(field) {
|
||||
return !field ? false : kpxcIcons.icons.some(i => i.field === field);
|
||||
};
|
||||
|
||||
// Sets the icons to corresponding database lock status
|
||||
kpxcIcons.switchIcons = async function() {
|
||||
const uuid = await sendMessage('page_get_login_id');
|
||||
|
||||
kpxcUsernameIcons.switchIcon(kpxc.databaseState, uuid);
|
||||
kpxcPasswordIcons.switchIcon(kpxc.databaseState, uuid);
|
||||
kpxcTOTPIcons.switchIcon(kpxc.databaseState, uuid);
|
||||
};
|
||||
|
|
@ -32,7 +32,7 @@ kpxc.addToSitePreferences = async function(optionName, addWildcard = false) {
|
|||
let site;
|
||||
try {
|
||||
site = trimURL(window.top.location.href);
|
||||
} catch (_err) {
|
||||
} catch (err) {
|
||||
logDebug('Adding to Site Preferences denied from iframe.');
|
||||
return;
|
||||
}
|
||||
|
|
@ -62,10 +62,10 @@ kpxc.addToSitePreferences = async function(optionName, addWildcard = false) {
|
|||
|
||||
await sendMessage('save_settings', kpxc.settings);
|
||||
|
||||
if (optionName === SitePreferences.ALLOW_IFRAMES) {
|
||||
if (optionName === 'allowIframes') {
|
||||
await sendMessage('page_set_allow_iframes', [ true, site ]);
|
||||
await sendMessage('iframe_detected', false);
|
||||
} else if (optionName === SitePreferences.USERNAME_ONLY) {
|
||||
} else if (optionName === 'usernameOnly') {
|
||||
await sendMessage('username_field_detected', false);
|
||||
}
|
||||
};
|
||||
|
|
@ -81,9 +81,8 @@ kpxc.clearAllFromPage = function() {
|
|||
kpxcUserAutocomplete.closeList();
|
||||
}
|
||||
|
||||
// Clear logins from background and switch back to default popup
|
||||
sendMessage('page_clear_logins');
|
||||
sendMessage('get_status', [ true, false, true ]); // This is an internal function call, forceShowDefault
|
||||
// Switch back to default popup
|
||||
sendMessage('get_status', [ true ]); // This is an internal function call
|
||||
};
|
||||
|
||||
// Creates a new combination manually from active element
|
||||
|
|
@ -208,8 +207,6 @@ kpxc.getSite = function(sites) {
|
|||
kpxc.identifyFormInputs = async function() {
|
||||
const forms = [];
|
||||
const documentForms = document.forms; // Cache the value just in case
|
||||
// Used for overlay security detection
|
||||
kpxcFields.discoverOverlays();
|
||||
|
||||
for (const form of documentForms) {
|
||||
if (!kpxcFields.isVisible(form)) {
|
||||
|
|
@ -361,7 +358,7 @@ kpxc.initCredentialFields = async function() {
|
|||
|
||||
// Search all remaining inputs from the page, ignore the previous input fields
|
||||
const pageInputs = await kpxcFields.getAllPageInputs(formInputs);
|
||||
if (formInputs.length === 0 && pageInputs.length === 0) {
|
||||
if (formInputs.length === 0 && pageInputs.length === 0 && !kpxcFields.isCustomLoginFieldsUsed()) {
|
||||
// Run 'redetect_credentials' manually if no fields are found after a page load
|
||||
setTimeout(async function() {
|
||||
if (_called.automaticRedetectCompleted) {
|
||||
|
|
@ -519,7 +516,7 @@ kpxc.prepareCredentials = async function() {
|
|||
kpxc.initAutocomplete();
|
||||
|
||||
if (kpxc.settings.autoFillRelevantCredential) {
|
||||
const pageUuid = await sendMessage('page_get_login_id', false);
|
||||
const pageUuid = await sendMessage('page_get_login_id');
|
||||
if (pageUuid) {
|
||||
const relevantCredential = kpxc.credentials.find(c => c.uuid === pageUuid);
|
||||
const combination = kpxc.combinations?.at(-1);
|
||||
|
|
@ -649,14 +646,11 @@ kpxc.rememberCredentialsFromContextMenu = async function() {
|
|||
// Credential Banner can force the retrieval for reloading new/modified credentials.
|
||||
kpxc.retrieveCredentials = async function(force = false) {
|
||||
if (!await isIframeAllowed()) {
|
||||
return;
|
||||
return [];
|
||||
}
|
||||
|
||||
kpxc.url = document.location.href;
|
||||
|
||||
// Search for first combination that has username or password input set
|
||||
const firstCombination = kpxc.combinations?.find((combination) => combination?.username || combination?.password);
|
||||
kpxc.submitUrl = kpxc.getFormActionUrl(firstCombination);
|
||||
kpxc.submitUrl = kpxc.getFormActionUrl(kpxc.combinations[0]);
|
||||
|
||||
if (kpxc.settings.autoRetrieveCredentials && kpxc.url && kpxc.submitUrl) {
|
||||
await kpxc.retrieveCredentialsCallback(
|
||||
|
|
@ -721,6 +715,52 @@ kpxc.setPasswordFilled = async function(state) {
|
|||
await sendMessage('password_set_filled', state);
|
||||
};
|
||||
|
||||
// Special handling for setting value to select and checkbox elements
|
||||
kpxc.setValue = function(field, value, forced = false) {
|
||||
if (field.matches('select')) {
|
||||
value = value.toLowerCase().trim();
|
||||
const options = field.querySelectorAll('option');
|
||||
|
||||
for (const o of options) {
|
||||
if (o.textContent.toLowerCase().trim() === value) {
|
||||
kpxc.setValueWithChange(field, o.value);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
} else if (field.getLowerCaseAttribute('type') === 'checkbox' && value?.toLowerCase() === 'true') {
|
||||
field.checked = true;
|
||||
}
|
||||
|
||||
kpxc.setValueWithChange(field, value, forced);
|
||||
};
|
||||
|
||||
// Sets a new value to input field and triggers necessary events
|
||||
kpxc.setValueWithChange = function(field, value, forced = false) {
|
||||
if (!forced && field.readOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dispatchLegacyEvent = function(elem, eventName) {
|
||||
const legacyEvent = elem.ownerDocument.createEvent('Event');
|
||||
legacyEvent.initEvent(eventName, true, false);
|
||||
elem.dispatchEvent(legacyEvent);
|
||||
};
|
||||
|
||||
field.focus();
|
||||
field.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, cancelable: false }));
|
||||
field.dispatchEvent(new KeyboardEvent('keypress', { bubbles: true, cancelable: false }));
|
||||
field.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true, cancelable: false }));
|
||||
field.dispatchEvent(new Event('input', { bubbles: true, cancelable: false }));
|
||||
field.dispatchEvent(new Event('change', { bubbles: true, cancelable: false }));
|
||||
field.value = value;
|
||||
|
||||
// Some pages will not accept the value change without dispatching events directly to the document
|
||||
dispatchLegacyEvent(field, 'input');
|
||||
dispatchLegacyEvent(field, 'change');
|
||||
};
|
||||
|
||||
kpxc.showGroupNameInAutocomplete = function() {
|
||||
return !kpxc.settings.useCompactMode
|
||||
|| (kpxc.settings.showGroupNameInAutocomplete && kpxc.getUniqueGroupCount(kpxc.credentials) > 1);
|
||||
|
|
@ -732,7 +772,7 @@ kpxc.siteIgnored = async function(condition) {
|
|||
let currentLocation;
|
||||
try {
|
||||
currentLocation = window.top.location.href;
|
||||
} catch (_err) {
|
||||
} catch (err) {
|
||||
// Cross-domain security error inspecting window.top.location.href.
|
||||
// This catches an error when an iframe is being accessed from another (sub)domain
|
||||
// -> use the iframe URL instead.
|
||||
|
|
@ -853,7 +893,6 @@ const initContentScript = async function() {
|
|||
}
|
||||
|
||||
kpxc.settings = settings;
|
||||
kpxc.isFirefox = await sendMessage('is_firefox');
|
||||
|
||||
if (await kpxc.siteIgnored()) {
|
||||
logDebug('This site is ignored in Site Preferences.');
|
||||
|
|
@ -910,9 +949,9 @@ browser.runtime.onMessage.addListener(async function(req, sender) {
|
|||
if (req.action === 'activated_tab') {
|
||||
kpxc.triggerActivatedTab();
|
||||
} else if (req.action === 'add_allow_iframes_option') {
|
||||
kpxc.addToSitePreferences(SitePreferences.ALLOW_IFRAMES);
|
||||
kpxc.addToSitePreferences('allowIframes');
|
||||
} else if (req.action === 'add_username_only_option') {
|
||||
kpxc.addToSitePreferences(SitePreferences.USERNAME_ONLY, true);
|
||||
kpxc.addToSitePreferences('usernameOnly', true);
|
||||
} else if (req.action === 'check_database_hash' && 'hash' in req) {
|
||||
kpxc.detectDatabaseChange(req);
|
||||
} else if (req.action === 'choose_credential_fields') {
|
||||
|
|
@ -941,14 +980,9 @@ browser.runtime.onMessage.addListener(async function(req, sender) {
|
|||
kpxcFill.fillAttributeToActiveElementWith(req.args);
|
||||
} else if (req.action === 'frame_message') {
|
||||
if (req.args?.[0] === 'frame_request_to_frames' && window.self !== window.top) {
|
||||
// Handle message from top window in iframe
|
||||
kpxcCustomLoginFieldsBanner.handleParentWindowMessage(req.args);
|
||||
} else if (req.args?.[0] === 'frame_request_to_parent' && window.self === window.top) {
|
||||
// Handle message from iframe in top window
|
||||
kpxcCustomLoginFieldsBanner.handleTopWindowMessage(req.args);
|
||||
} else if (req.args?.[0] === 'notification_from_frame' && window.self === window.top) {
|
||||
// Handle notification from iframe in top window
|
||||
kpxcUI.createNotification(req?.args[1], req?.args[2]);
|
||||
}
|
||||
} else if (req.action === 'ignore_site') {
|
||||
kpxc.ignoreSite(req.args);
|
||||
|
|
@ -962,14 +996,9 @@ browser.runtime.onMessage.addListener(async function(req, sender) {
|
|||
kpxc.initCredentialFields();
|
||||
} else if (req.action === 'reload_extension') {
|
||||
sendMessage('reconnect');
|
||||
} else if (req.action === 'reopen_database') {
|
||||
sendMessage(
|
||||
'get_status',
|
||||
[ false, true ] // Set forcePopup to true
|
||||
);
|
||||
} else if (req.action === 'save_credentials') {
|
||||
kpxc.rememberCredentialsFromContextMenu();
|
||||
} else if (req.action === 'retrieve_credentials_forced') {
|
||||
} else if (req.action === 'retrive_credentials_forced') {
|
||||
await kpxc.retrieveCredentials(true);
|
||||
} else if (req.action === 'show_password_generator') {
|
||||
kpxcPasswordGenerator.showPasswordGenerator();
|
||||
|
|
@ -999,18 +1028,11 @@ kpxc.reconnect = async function() {
|
|||
|
||||
const isIframeAllowed = async function() {
|
||||
sendMessage('iframe_detected', false);
|
||||
|
||||
// Don't allow sandboxed iframes
|
||||
if (self.origin === null || self.origin === 'null') {
|
||||
logDebug('Error: Sandboxed iframes are not allowed');
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// Check for Cross-domain security error when inspecting window.top.location.href
|
||||
const currentLocation = window.top.location.href;
|
||||
return true;
|
||||
} catch (_err) {
|
||||
} catch (err) {
|
||||
// Inspect iframe using TLD and the tab's original URL
|
||||
const allowed = await sendMessage('is_iframe_allowed', [ window.location.href, window.location.hostname ]);
|
||||
if (allowed) {
|
||||
|
|
|
|||
|
|
@ -18,18 +18,11 @@ kpxcObserverHelper.ignoredNodeNames = [
|
|||
'A',
|
||||
'HEAD',
|
||||
'HTML',
|
||||
'IMG',
|
||||
'LINK',
|
||||
'SCRIPT',
|
||||
'VIDEO',
|
||||
];
|
||||
|
||||
kpxcObserverHelper.ignoredPartialNodeNames = [
|
||||
'REDDIT-PDP',
|
||||
'RENDER-TEMPLATE',
|
||||
'SHREDDIT',
|
||||
];
|
||||
|
||||
kpxcObserverHelper.ignoredNodeTypes = [
|
||||
Node.ATTRIBUTE_NODE,
|
||||
Node.TEXT_NODE,
|
||||
|
|
@ -79,22 +72,15 @@ kpxcObserverHelper.initObserver = async function() {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (kpxcFields.hasOverlay(mut.target)) {
|
||||
kpxcFields.discoverOverlays();
|
||||
kpxcFields.checkExistingFields();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Cache style mutations. We only need the last style mutation of the target.
|
||||
kpxcObserverHelper.cacheStyle(mut, styleMutations, mutations.length);
|
||||
|
||||
if (mut.type === 'childList') {
|
||||
mut.addedNodes.forEach(function (node) {
|
||||
kpxcObserverHelper.handleObserverAdd(node);
|
||||
});
|
||||
mut.removedNodes.forEach(function (node) {
|
||||
kpxcObserverHelper.handleObserverRemove(node);
|
||||
});
|
||||
if (mut.addedNodes.length > 0) {
|
||||
kpxcObserverHelper.handleObserverAdd(mut.addedNodes[0]);
|
||||
} else if (mut.removedNodes.length > 0) {
|
||||
kpxcObserverHelper.handleObserverRemove(mut.removedNodes[0]);
|
||||
}
|
||||
} else if (mut.type === 'attributes' && (mut.attributeName === 'class' || mut.attributeName === 'style')) {
|
||||
// Only accept targets with forms
|
||||
const forms = matchesWithNodeName(mut.target, 'FORM')
|
||||
|
|
@ -244,31 +230,6 @@ kpxcObserverHelper.findInputsFromShadowDOM = function(target) {
|
|||
return inputFields;
|
||||
};
|
||||
|
||||
// Detects animations and transitions. Triggers handleObserverAdd() again on animationend/transitionend.
|
||||
kpxcObserverHelper.handleTransitions = function(target) {
|
||||
const classList = target?.classList?.toString();
|
||||
const targetHasAnimations = classList?.includes('animate');
|
||||
const targetHasDurations = classList?.includes('duration') || classList?.includes('transform');
|
||||
|
||||
if (targetHasAnimations || targetHasDurations) {
|
||||
const animations = target.getAnimations();
|
||||
const transitionTime = animations[0]?.currentTime ?? 0;
|
||||
|
||||
// Animation found, but transition has not finished
|
||||
if (animations && transitionTime === 0) {
|
||||
target.addEventListener(
|
||||
targetHasAnimations ? 'animationend' : 'transitionend',
|
||||
() => {
|
||||
kpxcObserverHelper.handleObserverAdd(target);
|
||||
},
|
||||
{
|
||||
once: true,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Adds elements to a monitor array. Identifies the input fields.
|
||||
kpxcObserverHelper.handleObserverAdd = async function(target) {
|
||||
if (kpxcObserverHelper.ignoredElement(target)) {
|
||||
|
|
@ -281,8 +242,6 @@ kpxcObserverHelper.handleObserverAdd = async function(target) {
|
|||
return;
|
||||
}
|
||||
|
||||
kpxcObserverHelper.handleTransitions(target);
|
||||
|
||||
const inputs = kpxcObserverHelper.getInputs(target);
|
||||
if (inputs.length === 0) {
|
||||
return;
|
||||
|
|
@ -300,7 +259,7 @@ kpxcObserverHelper.handleObserverAdd = async function(target) {
|
|||
kpxc.prepareCredentials();
|
||||
}
|
||||
|
||||
kpxcIcons.deleteAllHiddenIcons();
|
||||
kpxcIcons.deleteHiddenIcons();
|
||||
};
|
||||
|
||||
// Removes monitored elements
|
||||
|
|
@ -314,7 +273,7 @@ kpxcObserverHelper.handleObserverRemove = function(target) {
|
|||
return;
|
||||
}
|
||||
|
||||
kpxcIcons.deleteAllHiddenIcons();
|
||||
kpxcIcons.deleteHiddenIcons();
|
||||
};
|
||||
|
||||
// Handles CSS transitionend event
|
||||
|
|
@ -346,7 +305,6 @@ kpxcObserverHelper.ignoredNode = function(target) {
|
|||
if (!target
|
||||
|| kpxcObserverHelper.ignoredNodeTypes.some(e => e === target.nodeType)
|
||||
|| kpxcObserverHelper.ignoredNodeNames.some(e => e === target.nodeName)
|
||||
|| kpxcObserverHelper.ignoredPartialNodeNames.some(e => target.nodeName?.includes(e))
|
||||
|| target.nodeName.startsWith('YTMUSIC')
|
||||
|| target.nodeName.startsWith('YT-')) {
|
||||
return true;
|
||||
|
|
@ -363,7 +321,7 @@ const getShadowDOM = function(elem) {
|
|||
|
||||
try {
|
||||
return elem.openOrClosedShadowRoot ? elem.openOrClosedShadowRoot : browser.dom.openOrClosedShadowRoot(elem);
|
||||
} catch (_e) {
|
||||
} catch (e) {
|
||||
return elem.shadowRoot;
|
||||
}
|
||||
};
|
||||
|
|
@ -372,9 +330,7 @@ const getShadowDOM = function(elem) {
|
|||
const treeWalkerFilter = function(node) {
|
||||
return !node ||
|
||||
node?.disabled ||
|
||||
(node instanceof Element
|
||||
&& typeof node?.getAttribute === 'function'
|
||||
&& node?.getLowerCaseAttribute('type') === 'hidden')
|
||||
(typeof node?.getAttribute !== 'undefined' && node?.getLowerCaseAttribute('type') === 'hidden')
|
||||
? NodeFilter.FILTER_REJECT
|
||||
: NodeFilter.FILTER_ACCEPT;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,14 +8,13 @@ const PASSKEYS_WAIT_FOR_LIFETIMER = 30;
|
|||
const enablePasskeys = async function() {
|
||||
const passkeysLogDebug = function(message, extra) {
|
||||
if (kpxcPasskeysUtils.debugLogging) {
|
||||
debugLogMessage(message, extra);
|
||||
debugLogMessage(message, extra);
|
||||
}
|
||||
};
|
||||
|
||||
const passkeys = document.createElement('script');
|
||||
passkeys.src = chrome.runtime.getURL('content/passkeys.js');
|
||||
document.documentElement.appendChild(passkeys);
|
||||
passkeys.remove();
|
||||
|
||||
const startTimer = function(timeout) {
|
||||
return setTimeout(() => {
|
||||
|
|
@ -63,14 +62,6 @@ const enablePasskeys = async function() {
|
|||
}
|
||||
};
|
||||
|
||||
const isSameOriginWithAncestors = function () {
|
||||
try {
|
||||
return window.origin === window.top.origin;
|
||||
} catch (_err) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('kpxc-passkeys-request', async (ev) => {
|
||||
if (!window.isSecureContext) {
|
||||
kpxcUI.createNotification('error', tr('errorMessagePasskeysContextIsNotSecure'));
|
||||
|
|
@ -80,14 +71,14 @@ const enablePasskeys = async function() {
|
|||
if (ev.detail.action === 'passkeys_create') {
|
||||
const publicKey = kpxcPasskeysUtils.buildCredentialCreationOptions(
|
||||
ev.detail.publicKey,
|
||||
isSameOriginWithAncestors(),
|
||||
ev.detail.sameOriginWithAncestors,
|
||||
);
|
||||
passkeysLogDebug('Passkey request', publicKey);
|
||||
await sendResponse('passkeys_register', publicKey);
|
||||
} else if (ev.detail.action === 'passkeys_get') {
|
||||
const publicKey = kpxcPasskeysUtils.buildCredentialRequestOptions(
|
||||
ev.detail.publicKey,
|
||||
isSameOriginWithAncestors(),
|
||||
ev.detail.sameOriginWithAncestors,
|
||||
);
|
||||
passkeysLogDebug('Passkey request', publicKey);
|
||||
await sendResponse('passkeys_get', publicKey);
|
||||
|
|
@ -108,7 +99,7 @@ const initContent = async () => {
|
|||
return;
|
||||
}
|
||||
|
||||
if (await chrome.runtime.sendMessage({ action: 'is_site_ignored', args: [ window.self.location.href, true ] })) {
|
||||
if (await chrome.runtime.sendMessage({ action: 'is_site_ignored', args: window.self.location.href })) {
|
||||
console.log('This site is ignored in Site Preferences.');
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,58 +26,13 @@
|
|||
return kpxcStringToArrayBuffer(window.atob(str?.replaceAll('-', '+').replaceAll('_', '/')));
|
||||
};
|
||||
|
||||
// From ArrayBuffer to URL encoded base64 string
|
||||
const kpxcArrayBufferToBase64 = function(buf) {
|
||||
const str = [ ...new Uint8Array(buf) ].map(c => String.fromCharCode(c)).join('');
|
||||
return window.btoa(str).replaceAll('+', '-').replaceAll('/', '_').replaceAll('=', '');
|
||||
};
|
||||
|
||||
// Returns the PublicKeyCredential as JSON
|
||||
// See: https://w3c.github.io/webauthn/#dom-publickeycredential-tojson
|
||||
const kpxcPublicKeyCredentialJson = function (credential, publicKey) {
|
||||
const clientExtensionResults = credential.getClientExtensionResults();
|
||||
const type = credential.type;
|
||||
const authenticatorAttachment = credential.authenticatorAttachment;
|
||||
let response;
|
||||
|
||||
if (credential.response instanceof AuthenticatorAttestationResponse) {
|
||||
const responsePublicKey = credential.response.getPublicKey();
|
||||
response = {
|
||||
clientDataJSON: publicKey.response.clientDataJSON,
|
||||
authenticatorData: publicKey.response.authenticatorData,
|
||||
transports: credential.response.getTransports(),
|
||||
publicKey: responsePublicKey ? kpxcArrayBufferToBase64(responsePublicKey) : null,
|
||||
publicKeyAlgorithm: credential.response.getPublicKeyAlgorithm(),
|
||||
attestationObject: publicKey.response.attestationObject,
|
||||
};
|
||||
}
|
||||
|
||||
if (credential.response instanceof AuthenticatorAssertionResponse) {
|
||||
response = {
|
||||
clientDataJSON: publicKey.response.clientDataJSON,
|
||||
authenticatorData: publicKey.response.authenticatorData,
|
||||
signature: publicKey.response.signature,
|
||||
userHandle: publicKey.response?.userHandle || undefined,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
id: publicKey.id,
|
||||
rawId: publicKey.id,
|
||||
response,
|
||||
authenticatorAttachment,
|
||||
clientExtensionResults,
|
||||
type,
|
||||
};
|
||||
};
|
||||
|
||||
// Wraps response to AuthenticatorAttestationResponse object
|
||||
const createAttestationResponse = function(publicKey) {
|
||||
const response = {
|
||||
attestationObject: kpxcBase64ToArrayBuffer(publicKey.response.attestationObject),
|
||||
clientDataJSON: kpxcBase64ToArrayBuffer(publicKey.response.clientDataJSON),
|
||||
getAuthenticatorData: () => kpxcBase64ToArrayBuffer(publicKey.response?.authenticatorData),
|
||||
getPublicKey: () => publicKey.response?.publicKey ? publicKey.response?.publicKey : null,
|
||||
getPublicKey: () => null,
|
||||
getPublicKeyAlgorithm: () => publicKey.response?.publicKeyAlgorithm,
|
||||
getTransports: () => [ 'internal' ]
|
||||
};
|
||||
|
|
@ -108,8 +63,7 @@
|
|||
response: authenticatorResponse,
|
||||
type: publicKey.type,
|
||||
clientExtensionResults: () => publicKey?.response?.clientExtensionResults || {},
|
||||
getClientExtensionResults: () => publicKey?.response?.clientExtensionResults || {},
|
||||
toJSON: () => kpxcPublicKeyCredentialJson(publicKeyCredential, publicKey)
|
||||
getClientExtensionResults: () => publicKey?.response?.clientExtensionResults || {}
|
||||
};
|
||||
|
||||
return Object.setPrototypeOf(publicKeyCredential, PublicKeyCredential.prototype);
|
||||
|
|
@ -137,6 +91,14 @@
|
|||
});
|
||||
};
|
||||
|
||||
const isSameOriginWithAncestors = function() {
|
||||
try {
|
||||
return window.self.origin === window.top.origin;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Throws errors to a correct exceptions
|
||||
const throwError = function(errorCode, errorMessage) {
|
||||
if ((!errorCode && !errorMessage) || errorCode === PASSKEYS_REQUEST_CANCELED) {
|
||||
|
|
@ -185,9 +147,11 @@
|
|||
return null;
|
||||
}
|
||||
|
||||
const sameOriginWithAncestors = isSameOriginWithAncestors();
|
||||
const response = await postMessageToExtension({
|
||||
action: 'passkeys_create',
|
||||
publicKey: options.publicKey,
|
||||
sameOriginWithAncestors: sameOriginWithAncestors,
|
||||
});
|
||||
|
||||
if (!response.publicKey) {
|
||||
|
|
@ -208,9 +172,11 @@
|
|||
return originalCredentials.get(options);
|
||||
}
|
||||
|
||||
const sameOriginWithAncestors = isSameOriginWithAncestors();
|
||||
const response = await postMessageToExtension({
|
||||
action: 'passkeys_get',
|
||||
publicKey: options.publicKey,
|
||||
sameOriginWithAncestors: sameOriginWithAncestors,
|
||||
});
|
||||
|
||||
if (!response.publicKey) {
|
||||
|
|
@ -235,14 +201,12 @@
|
|||
// select a software authenticator. This could be removed in the future.
|
||||
try {
|
||||
Object.defineProperty(navigator, 'credentials', { value: passkeysCredentials });
|
||||
if (window.PublicKeyCredential) {
|
||||
Object.defineProperty(window.PublicKeyCredential, 'isConditionalMediationAvailable', {
|
||||
value: isConditionalMediationAvailable,
|
||||
});
|
||||
Object.defineProperty(window.PublicKeyCredential, 'isUserVerifyingPlatformAuthenticatorAvailable', {
|
||||
value: isUserVerifyingPlatformAuthenticatorAvailable,
|
||||
});
|
||||
}
|
||||
Object.defineProperty(window.PublicKeyCredential, 'isConditionalMediationAvailable', {
|
||||
value: isConditionalMediationAvailable,
|
||||
});
|
||||
Object.defineProperty(window.PublicKeyCredential, 'isUserVerifyingPlatformAuthenticatorAvailable', {
|
||||
value: isUserVerifyingPlatformAuthenticatorAvailable,
|
||||
});
|
||||
} catch (err) {
|
||||
console.log('Cannot override navigator.credentials: ', err);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@ kpxcPasswordIcons.switchIcon = function(state) {
|
|||
kpxcPasswordIcons.icons.forEach(u => u.switchIcon(state));
|
||||
};
|
||||
|
||||
kpxcPasswordIcons.deleteHiddenIcons = function() {
|
||||
kpxcUI.deleteHiddenIcons(kpxcPasswordIcons.icons);
|
||||
};
|
||||
|
||||
kpxcPasswordIcons.isValid = function(field) {
|
||||
if (!field
|
||||
|| field.readOnly
|
||||
|
|
@ -30,7 +34,7 @@ class PasswordIcon extends Icon {
|
|||
this.nextFieldExists = false;
|
||||
|
||||
this.initField(field);
|
||||
kpxcIcons.monitorIconPosition(this);
|
||||
kpxcUI.monitorIconPosition(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -45,22 +49,19 @@ PasswordIcon.prototype.initField = function(field) {
|
|||
};
|
||||
|
||||
PasswordIcon.prototype.createIcon = function(field) {
|
||||
const className = getIconClass('key');
|
||||
const size = this.calculateIconSize(field);
|
||||
const className = (isFirefox() ? 'key-moz' : 'key');
|
||||
const size = (field.offsetHeight > 28) ? 24 : 16;
|
||||
const offset = kpxcUI.calculateIconOffset(field, size);
|
||||
|
||||
const icon = kpxcUI.createElement('div', 'kpxc kpxc-pwgen-icon ' + className,
|
||||
{
|
||||
'title': tr('passwordGeneratorGenerateText'),
|
||||
'size': size,
|
||||
'kpxc-pwgen-field-id': field.getAttribute('data-kpxc-id'),
|
||||
'popover': 'manual'
|
||||
'offset': offset,
|
||||
'kpxc-pwgen-field-id': field.getAttribute('data-kpxc-id') // Needed?
|
||||
});
|
||||
|
||||
if (kpxcFields.popoverSupported) {
|
||||
icon.style.margin = 0;
|
||||
} else {
|
||||
icon.style.zIndex = '10000000';
|
||||
}
|
||||
icon.style.zIndex = '10000000';
|
||||
icon.style.width = Pixels(size);
|
||||
icon.style.height = Pixels(size);
|
||||
|
||||
|
|
@ -74,11 +75,7 @@ PasswordIcon.prototype.createIcon = function(field) {
|
|||
}
|
||||
|
||||
if (e.shiftKey) {
|
||||
if (kpxcFields.popoverSupported) {
|
||||
icon.hidePopover();
|
||||
} else {
|
||||
icon.style.display = 'none';
|
||||
}
|
||||
icon.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -89,12 +86,9 @@ PasswordIcon.prototype.createIcon = function(field) {
|
|||
icon.addEventListener('mousedown', ev => ev.stopPropagation());
|
||||
icon.addEventListener('mouseup', ev => ev.stopPropagation());
|
||||
|
||||
kpxcIcons.setIconPosition(icon, field, this.rtl);
|
||||
kpxcUI.setIconPosition(icon, field, this.rtl);
|
||||
this.icon = icon;
|
||||
this.createWrapper('css/pwgen.css');
|
||||
if (kpxcFields.popoverSupported) {
|
||||
icon.showPopover();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -157,7 +151,13 @@ kpxcPasswordGenerator.fill = function(elem, password) {
|
|||
|
||||
const isPasswordGeneratorSupported = async function() {
|
||||
const response = await browser.runtime.sendMessage({
|
||||
action: 'get_features_list'
|
||||
action: 'get_keepassxc_versions'
|
||||
});
|
||||
return response?.passwordGenerator;
|
||||
|
||||
const result = await browser.runtime.sendMessage({
|
||||
action: 'compare_versions',
|
||||
args: [ [ '2.7.0' ], response.current ]
|
||||
});
|
||||
|
||||
return result['2.7.0'] || false;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
const ignoreRegex = /(bank|coupon|post(al)?|user|zip|promo).*code|(en|de)code(d|r)*|comment|author|error/i;
|
||||
const ignoreRegex = /(bank|coupon|postal|user|zip).*code|(en|de)code(d|r)*|comment|author|error/i;
|
||||
const ignoredTypes = [ 'email', 'password', 'username' ];
|
||||
const allowedInputTypes = [ 'number', 'password', 'tel', 'text' ];
|
||||
|
||||
|
|
@ -13,14 +13,12 @@ const acceptedOTPFields = [
|
|||
'idvpin',
|
||||
'mfa',
|
||||
'one_time_password',
|
||||
'otc-confirmation-input',
|
||||
'otp',
|
||||
'token',
|
||||
'twofa',
|
||||
'two-factor',
|
||||
'twofactor',
|
||||
'verification_pin',
|
||||
'mfaCode'
|
||||
'verification_pin'
|
||||
];
|
||||
|
||||
const acceptedParents = [
|
||||
|
|
@ -38,6 +36,10 @@ kpxcTOTPIcons.switchIcon = function(state, uuid) {
|
|||
kpxcTOTPIcons.icons.forEach(u => u.switchIcon(state, uuid));
|
||||
};
|
||||
|
||||
kpxcTOTPIcons.deleteHiddenIcons = function() {
|
||||
kpxcUI.deleteHiddenIcons(kpxcTOTPIcons.icons);
|
||||
};
|
||||
|
||||
kpxcTOTPIcons.autoCompleteIsOneTimeCode = function(field) {
|
||||
if (!field) {
|
||||
return false;
|
||||
|
|
@ -107,7 +109,7 @@ class TOTPFieldIcon extends Icon {
|
|||
super(field, databaseState, segmented);
|
||||
|
||||
this.initField(field, segmented);
|
||||
kpxcIcons.monitorIconPosition(this);
|
||||
kpxcUI.monitorIconPosition(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -137,21 +139,19 @@ TOTPFieldIcon.prototype.initField = async function(field, segmented) {
|
|||
};
|
||||
|
||||
TOTPFieldIcon.prototype.createIcon = function(field, segmented = false) {
|
||||
const className = getIconClass('kpxc-totp-icon');
|
||||
const size = this.calculateIconSize(field);
|
||||
const className = (isFirefox() ? 'moz' : 'default');
|
||||
|
||||
const icon = kpxcUI.createElement('div', 'kpxc ' + className,
|
||||
// Size the icon dynamically, but not greater than 24 or smaller than 14
|
||||
const size = Math.max(Math.min(24, field.offsetHeight - 4), 14);
|
||||
const offset = kpxcUI.calculateIconOffset(field, size);
|
||||
|
||||
const icon = kpxcUI.createElement('div', 'kpxc kpxc-totp-icon ' + className,
|
||||
{
|
||||
'title': tr('totpFieldText'),
|
||||
'size': size,
|
||||
'popover': 'manual'
|
||||
'offset': offset
|
||||
});
|
||||
|
||||
if (kpxcFields.popoverSupported) {
|
||||
icon.style.margin = 0;
|
||||
} else {
|
||||
icon.style.zIndex = '10000000';
|
||||
}
|
||||
icon.style.zIndex = '10000000';
|
||||
icon.style.width = Pixels(size);
|
||||
icon.style.height = Pixels(size);
|
||||
|
||||
|
|
@ -167,11 +167,7 @@ TOTPFieldIcon.prototype.createIcon = function(field, segmented = false) {
|
|||
}
|
||||
|
||||
if (e.shiftKey) {
|
||||
if (kpxcFields.popoverSupported) {
|
||||
icon.hidePopover();
|
||||
} else {
|
||||
icon.style.display = 'none';
|
||||
}
|
||||
icon.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -183,10 +179,7 @@ TOTPFieldIcon.prototype.createIcon = function(field, segmented = false) {
|
|||
icon.addEventListener('mousedown', ev => ev.stopPropagation());
|
||||
icon.addEventListener('mouseup', ev => ev.stopPropagation());
|
||||
|
||||
kpxcIcons.setIconPosition(icon, field, this.rtl, segmented);
|
||||
kpxcUI.setIconPosition(icon, field, this.rtl, segmented);
|
||||
this.icon = icon;
|
||||
this.createWrapper('css/totp.css');
|
||||
if (kpxcFields.popoverSupported) {
|
||||
icon.showPopover();
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -13,14 +13,6 @@ const ORANGE_BUTTON = 'kpxc-button kpxc-orange-button';
|
|||
const RED_BUTTON = 'kpxc-button kpxc-red-button';
|
||||
const GRAY_BUTTON_CLASS = 'kpxc-gray-button';
|
||||
|
||||
const ALLOWED_OBSERVER_NODETYPES = [
|
||||
Node.ELEMENT_NODE,
|
||||
Node.DOCUMENT_NODE,
|
||||
Node.DOCUMENT_FRAGMENT_NODE
|
||||
];
|
||||
|
||||
const OBSERVER_OPTIONS = { attributes: true, attributeFilter: [ 'style' ] };
|
||||
|
||||
const DatabaseState = {
|
||||
DISCONNECTED: 0,
|
||||
LOCKED: 1,
|
||||
|
|
@ -35,15 +27,73 @@ const $ = function(elem) {
|
|||
return document.querySelector(elem);
|
||||
};
|
||||
|
||||
// Returns a string with 'px' for CSS styles
|
||||
const Pixels = function(value) {
|
||||
return String(value) + 'px';
|
||||
};
|
||||
|
||||
// Basic icon class
|
||||
class Icon {
|
||||
constructor(field, databaseState = DatabaseState.DISCONNECTED, segmented = false) {
|
||||
this.databaseState = databaseState;
|
||||
this.icon = null;
|
||||
this.inputField = null;
|
||||
this.rtl = kpxcUI.isRTL(field);
|
||||
this.segmented = segmented;
|
||||
|
||||
try {
|
||||
this.observer = new IntersectionObserver((entries) => {
|
||||
kpxcUI.updateFromIntersectionObserver(this, entries);
|
||||
});
|
||||
} catch (err) {
|
||||
logError(err);
|
||||
}
|
||||
}
|
||||
|
||||
// Creates a wrapper div that has the icon in Shadow DOM
|
||||
createWrapper(styleSheetFilename) {
|
||||
const styleSheet = createStylesheet(styleSheetFilename);
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.style.all = 'unset';
|
||||
wrapper.style.display = 'none';
|
||||
|
||||
// Make sure the wrapper is positioned correctly without CSS styles affecting to it
|
||||
wrapper.style.position = 'absolute';
|
||||
wrapper.style.top = Pixels(0);
|
||||
wrapper.style.left = Pixels(0);
|
||||
|
||||
// Waits for stylesheet to load before displaying the element
|
||||
styleSheet.addEventListener('load', () => wrapper.style.display = 'block');
|
||||
|
||||
this.shadowRoot = wrapper.attachShadow({ mode: 'closed' });
|
||||
this.shadowRoot.append(styleSheet);
|
||||
this.shadowRoot.append(this.icon);
|
||||
document.body.append(wrapper);
|
||||
}
|
||||
|
||||
switchIcon(state, uuid) {
|
||||
if (!this.icon) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (state === DatabaseState.UNLOCKED) {
|
||||
this.icon.style.filter = kpxc.credentials.length === 0 && !uuid ? 'saturate(0%)' : 'saturate(100%)';
|
||||
} else {
|
||||
this.icon.style.filter = 'saturate(0%)';
|
||||
}
|
||||
}
|
||||
|
||||
removeIcon() {
|
||||
this.shadowRoot.removeChild(this.icon);
|
||||
document.body.removeChild(this.shadowRoot.host);
|
||||
}
|
||||
}
|
||||
|
||||
const kpxcUI = {};
|
||||
kpxcUI.mouseDown = false;
|
||||
|
||||
if (document.body) {
|
||||
const bodyRect = document.body.getBoundingClientRect();
|
||||
kpxcUI.bodyRect = {
|
||||
left: bodyRect.left + window.pageXOffset,
|
||||
top: bodyRect.top + window.pageYOffset
|
||||
};
|
||||
kpxcUI.bodyRect = document.body.getBoundingClientRect();
|
||||
kpxcUI.bodyStyle = getComputedStyle(document.body);
|
||||
}
|
||||
|
||||
|
|
@ -71,6 +121,63 @@ kpxcUI.createElement = function(type, classes, attributes, textContent) {
|
|||
return element;
|
||||
};
|
||||
|
||||
kpxcUI.monitorIconPosition = function(iconClass) {
|
||||
// Handle icon position on resize
|
||||
window.addEventListener('resize', function(e) {
|
||||
kpxcUI.updateIconPosition(iconClass);
|
||||
});
|
||||
|
||||
// Handle icon position on scroll
|
||||
window.addEventListener('scroll', function(e) {
|
||||
kpxcUI.updateIconPosition(iconClass);
|
||||
});
|
||||
|
||||
window.addEventListener('transitionend', function(e) {
|
||||
if (matchesWithNodeName(e.target, 'INPUT') || matchesWithNodeName(e.target, 'TEXTAREA')) {
|
||||
kpxcUI.updateIconPosition(iconClass);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
kpxcUI.updateIconPosition = function(iconClass) {
|
||||
if (iconClass.inputField && iconClass.icon) {
|
||||
kpxcUI.setIconPosition(iconClass.icon, iconClass.inputField, iconClass.rtl, iconClass.segmented);
|
||||
}
|
||||
};
|
||||
|
||||
kpxcUI.calculateIconOffset = function(field, size) {
|
||||
const offset = Math.floor((field.offsetHeight / 2) - (size / 2) - 1);
|
||||
return (offset < 0) ? 0 : offset;
|
||||
};
|
||||
|
||||
kpxcUI.setIconPosition = function(icon, field, rtl = false, segmented = false) {
|
||||
const rect = field.getBoundingClientRect();
|
||||
const size = Number(icon.getAttribute('size'));
|
||||
const offset = kpxcUI.calculateIconOffset(field, size);
|
||||
const zoom = kpxcUI.bodyStyle.zoom || 1;
|
||||
let left = kpxcUI.getRelativeLeftPosition(rect) / zoom;
|
||||
let top = kpxcUI.getRelativeTopPosition(rect) / zoom;
|
||||
|
||||
// Add more space for the icon to show it at the right side of the field if TOTP fields are segmented
|
||||
if (segmented) {
|
||||
left += size + 10;
|
||||
}
|
||||
|
||||
// Adjusts the icon offset for certain sites
|
||||
const iconOffset = kpxcSites.iconOffset(left, top, size, field?.getLowerCaseAttribute('type'));
|
||||
if (iconOffset) {
|
||||
left = iconOffset[0];
|
||||
top = iconOffset[1];
|
||||
}
|
||||
|
||||
const scrollTop = kpxcUI.getScrollTop() / zoom;
|
||||
const scrollLeft = kpxcUI.getScrollLeft() / zoom;
|
||||
icon.style.top = Pixels(top + scrollTop + offset + 1);
|
||||
icon.style.left = rtl
|
||||
? Pixels(left + scrollLeft + offset)
|
||||
: Pixels(left + scrollLeft + field.offsetWidth - size - offset);
|
||||
};
|
||||
|
||||
kpxcUI.getScrollTop = function() {
|
||||
return document.defaultView?.scrollY ?? document.scrollingElement?.scrollTop ?? 0;
|
||||
};
|
||||
|
|
@ -87,6 +194,32 @@ kpxcUI.getRelativeTopPosition = function(rect) {
|
|||
return kpxcUI.bodyStyle.position.toLowerCase() === 'relative' ? rect.top - kpxcUI.bodyRect.top : rect.top;
|
||||
};
|
||||
|
||||
kpxcUI.deleteHiddenIcons = function(iconList) {
|
||||
const deletedIcons = [];
|
||||
for (const icon of iconList) {
|
||||
if (icon.inputField && !kpxcFields.isVisible(icon.inputField)) {
|
||||
const index = iconList.indexOf(icon);
|
||||
icon.removeIcon();
|
||||
iconList.splice(index, 1);
|
||||
deletedIcons.push(icon.inputField);
|
||||
|
||||
// Delete the input field from detected fields so the icon can be detected again
|
||||
const inputFieldIndex = kpxc.inputs.indexOf(icon.inputField);
|
||||
if (inputFieldIndex >= 0) {
|
||||
kpxc.inputs.splice(inputFieldIndex, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the same icons from kpxcIcons.icons array
|
||||
for (const input of deletedIcons) {
|
||||
const index = kpxcIcons.icons.findIndex(e => e.field === input);
|
||||
if (index >= 0) {
|
||||
kpxcIcons.icons.splice(index, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
kpxcUI.isRTL = function(field) {
|
||||
if (!field) {
|
||||
return false;
|
||||
|
|
@ -157,29 +290,48 @@ kpxcUI.makeBannerDraggable = function(banner) {
|
|||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Detects if the input field appears or disappears -> show/hide the icon
|
||||
* - boundingClientRect with slightly (< -10) negative values -> hidden
|
||||
* - intersectionRatio === 0 -> hidden
|
||||
* - isIntersecting === false -> hidden
|
||||
* - intersectionRatio > 0 -> shown
|
||||
* - isIntersecting === true -> shown
|
||||
*/
|
||||
kpxcUI.updateFromIntersectionObserver = function(iconClass, entries) {
|
||||
for (const entry of entries) {
|
||||
const rect = DOMRectToArray(entry.boundingClientRect);
|
||||
|
||||
if ((entry.intersectionRatio === 0 && !entry.isIntersecting) || (rect.some(x => x < -10))) {
|
||||
iconClass.icon.style.display = 'none';
|
||||
} else if (entry.intersectionRatio > 0 && entry.isIntersecting) {
|
||||
iconClass.icon.style.display = 'block';
|
||||
|
||||
// Wait for possible DOM animations
|
||||
setTimeout(() => {
|
||||
kpxcUI.setIconPosition(iconClass.icon, entry.target, iconClass.rtl, iconClass.segmented);
|
||||
}, 400);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a self-disappearing notification banner to DOM
|
||||
* @param {string} type Notification type: (success, info, warning, error)
|
||||
* @param {string} message The message shown
|
||||
*/
|
||||
kpxcUI.createNotification = async function(type, message) {
|
||||
kpxcUI.createNotification = function(type, message) {
|
||||
if (!kpxc.settings.showNotifications || !type || !message) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Send the notification to top window from iframe
|
||||
if (window.self !== window.top) {
|
||||
await sendMessage('frame_message', [ 'notification_from_frame', type, message ]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Removes notification from the body element
|
||||
const removeNotification = function() {
|
||||
// Catch cross-domain exception
|
||||
let parentBody;
|
||||
try {
|
||||
parentBody = window.parent.document.body;
|
||||
} catch(_e) {
|
||||
} catch(e) {
|
||||
parentBody = window.document.body;
|
||||
}
|
||||
|
||||
|
|
@ -201,7 +353,7 @@ kpxcUI.createNotification = async function(type, message) {
|
|||
const notification = kpxcUI.createElement('div', 'kpxc-notification kpxc-notification-' + type, {});
|
||||
type = type.charAt(0).toUpperCase() + type.slice(1) + '!';
|
||||
|
||||
const className = getIconClass('kpxc-banner-icon');
|
||||
const className = (isFirefox() ? 'kpxc-banner-icon-moz' : 'kpxc-banner-icon');
|
||||
const icon = kpxcUI.createElement('span', className, { 'alt': 'logo' });
|
||||
const label = kpxcUI.createElement('span', 'kpxc-label', {}, type);
|
||||
const msg = kpxcUI.createElement('span', '', {}, message);
|
||||
|
|
@ -242,39 +394,8 @@ kpxcUI.createButton = function(color, textContent, callback) {
|
|||
return button;
|
||||
};
|
||||
|
||||
// Observe and prevent style changes to wrapper div elements
|
||||
kpxcUI.createWrapperObserver = function() {
|
||||
kpxcUI.wrapperObserver = new MutationObserver(function(mutations, obs) {
|
||||
for (const mut of mutations) {
|
||||
if (mut?.target && mut.target.style?.cssText !== 'all: unset;') {
|
||||
mut.target.removeAttribute('style');
|
||||
mut.target.style.all = 'unset';
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
kpxcUI.observeWrapper = function(elem) {
|
||||
kpxcUI.wrapperObserver?.observe(elem, OBSERVER_OPTIONS);
|
||||
};
|
||||
|
||||
// Observer <html> and <body> style changes
|
||||
kpxcUI.createPageObserver = function() {
|
||||
kpxcUI.pageObserver = new MutationObserver(function(mutations, obs) {
|
||||
for (const mut of mutations) {
|
||||
const currentStyle = getComputedStyle(mut?.target);
|
||||
if (currentStyle.opacity && currentStyle.opacity < MIN_OPACITY) {
|
||||
kpxc.clearAllFromPage();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (document?.documentElement && ALLOWED_OBSERVER_NODETYPES.includes(document.documentElement.nodeType)) {
|
||||
kpxcUI.pageObserver.observe(document.documentElement, OBSERVER_OPTIONS);
|
||||
}
|
||||
if (document?.body && ALLOWED_OBSERVER_NODETYPES.includes(document.body.nodeType)) {
|
||||
kpxcUI.pageObserver.observe(document.body, OBSERVER_OPTIONS);
|
||||
}
|
||||
const DOMRectToArray = function(domRect) {
|
||||
return [ domRect.bottom, domRect.height, domRect.left, domRect.right, domRect.top, domRect.width, domRect.x, domRect.y ];
|
||||
};
|
||||
|
||||
const initColorTheme = function(elem) {
|
||||
|
|
@ -302,11 +423,6 @@ const logDebug = function(message, extra) {
|
|||
}
|
||||
};
|
||||
|
||||
const initObservers = function() {
|
||||
kpxcUI.createWrapperObserver();
|
||||
kpxcUI.createPageObserver();
|
||||
};
|
||||
|
||||
document.addEventListener('mousedown', function(e) {
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
|
|
@ -323,12 +439,6 @@ document.addEventListener('mouseup', function(e) {
|
|||
kpxcUI.mouseDown = false;
|
||||
});
|
||||
|
||||
if (document.readyState === 'complete' || (document.readyState !== 'loading' && !document.documentElement.doScroll)) {
|
||||
initObservers();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', initObservers);
|
||||
}
|
||||
|
||||
HTMLDivElement.prototype.appendMultiple = function(...args) {
|
||||
for (const a of args) {
|
||||
this.append(a);
|
||||
|
|
|
|||
|
|
@ -12,6 +12,10 @@ kpxcUsernameIcons.switchIcon = function(state) {
|
|||
kpxcUsernameIcons.icons.forEach(u => u.switchIcon(state));
|
||||
};
|
||||
|
||||
kpxcUsernameIcons.deleteHiddenIcons = function() {
|
||||
kpxcUI.deleteHiddenIcons(kpxcUsernameIcons.icons);
|
||||
};
|
||||
|
||||
kpxcUsernameIcons.isValid = function(field) {
|
||||
if (!field
|
||||
|| field.offsetWidth < MIN_INPUT_FIELD_OFFSET_WIDTH
|
||||
|
|
@ -30,7 +34,7 @@ class UsernameFieldIcon extends Icon {
|
|||
super(field, databaseState);
|
||||
|
||||
this.initField(field);
|
||||
kpxcIcons.monitorIconPosition(this);
|
||||
kpxcUI.monitorIconPosition(this);
|
||||
}
|
||||
|
||||
switchIcon(state) {
|
||||
|
|
@ -64,7 +68,9 @@ UsernameFieldIcon.prototype.initField = function(field) {
|
|||
|
||||
UsernameFieldIcon.prototype.createIcon = function(field) {
|
||||
const className = getIconClassName(this.databaseState);
|
||||
const size = this.calculateIconSize(field);
|
||||
|
||||
// Size the icon dynamically, but not greater than 24 or smaller than 14
|
||||
const size = Math.max(Math.min(24, field.offsetHeight - 4), 14);
|
||||
|
||||
// Don't create the icon if the input field is too small
|
||||
if (field.offsetWidth < (size * 1.5) || field.offsetHeight < size) {
|
||||
|
|
@ -72,19 +78,16 @@ UsernameFieldIcon.prototype.createIcon = function(field) {
|
|||
return;
|
||||
}
|
||||
|
||||
const offset = kpxcUI.calculateIconOffset(field, size);
|
||||
|
||||
const icon = kpxcUI.createElement('div', 'kpxc kpxc-username-icon ' + className,
|
||||
{
|
||||
'title': getIconText(this.databaseState),
|
||||
'size': size,
|
||||
'kpxc-pwgen-field-id': field.getAttribute('data-kpxc-id'),
|
||||
'popover': 'manual'
|
||||
'offset': offset,
|
||||
'kpxc-pwgen-field-id': field.getAttribute('data-kpxc-id')
|
||||
});
|
||||
|
||||
if (kpxcFields.popoverSupported) {
|
||||
icon.style.margin = 0;
|
||||
} else {
|
||||
icon.style.zIndex = '10000000';
|
||||
}
|
||||
icon.style.zIndex = '10000000';
|
||||
icon.style.width = Pixels(size);
|
||||
icon.style.height = Pixels(size);
|
||||
|
||||
|
|
@ -94,11 +97,7 @@ UsernameFieldIcon.prototype.createIcon = function(field) {
|
|||
}
|
||||
|
||||
if (e.shiftKey) {
|
||||
if (kpxcFields.popoverSupported) {
|
||||
icon.hidePopover();
|
||||
} else {
|
||||
icon.style.display = 'none';
|
||||
}
|
||||
icon.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -109,12 +108,9 @@ UsernameFieldIcon.prototype.createIcon = function(field) {
|
|||
icon.addEventListener('mousedown', ev => ev.stopPropagation());
|
||||
icon.addEventListener('mouseup', ev => ev.stopPropagation());
|
||||
|
||||
kpxcIcons.setIconPosition(icon, field, this.rtl);
|
||||
kpxcUI.setIconPosition(icon, field, this.rtl);
|
||||
this.icon = icon;
|
||||
this.createWrapper('css/username.css');
|
||||
if (kpxcFields.popoverSupported) {
|
||||
icon.showPopover();
|
||||
}
|
||||
};
|
||||
|
||||
const iconClicked = async function(field, icon) {
|
||||
|
|
@ -143,12 +139,12 @@ const iconClicked = async function(field, icon) {
|
|||
|
||||
const getIconClassName = function(state = DatabaseState.UNLOCKED) {
|
||||
if (state === DatabaseState.LOCKED) {
|
||||
return getIconClass('lock');
|
||||
return (isFirefox() ? 'lock-moz' : 'lock');
|
||||
} else if (state === DatabaseState.DISCONNECTED) {
|
||||
return getIconClass('disconnected');
|
||||
return (isFirefox() ? 'disconnected-moz' : 'disconnected');
|
||||
}
|
||||
|
||||
return getIconClass('unlock');
|
||||
return (isFirefox() ? 'unlock-moz' : 'unlock');
|
||||
};
|
||||
|
||||
const getIconText = function(state) {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ div.kpxc-banner {
|
|||
margin: 0 auto !important;
|
||||
min-height: 2em !important;
|
||||
max-height: 4em !important;
|
||||
overflow-x: auto !important;
|
||||
overflow-x: hidden !important;
|
||||
padding: 4px !important;
|
||||
position: fixed !important;
|
||||
width: 100% !important;
|
||||
|
|
@ -49,10 +49,6 @@ div.kpxc-banner .banner-info > span {
|
|||
margin: 4px;
|
||||
}
|
||||
|
||||
div.kpxc-banner .banner-info-text {
|
||||
text-wrap: nowrap;
|
||||
}
|
||||
|
||||
div.kpxc-banner div.banner-info, div.banner-buttons {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
|
@ -81,14 +77,6 @@ div.kpxc-banner .kpxc-banner-icon-moz {
|
|||
background-size: contain;
|
||||
}
|
||||
|
||||
div.kpxc-banner .kpxc-banner-icon-safari {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
overflow: hidden;
|
||||
background: url('safari-web-extension://__MSG_@@extension_id__/icons/keepassxc.svg') right no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
div.kpxc-banner .kpxc-help-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
|
|
@ -105,14 +93,6 @@ div.kpxc-banner .kpxc-help-icon-moz {
|
|||
background-size: contain;
|
||||
}
|
||||
|
||||
div.kpxc-banner .kpxc-help-icon-safari {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
overflow: hidden;
|
||||
background: url('safari-web-extension://__MSG_@@extension_id__/icons/help.svg') right no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.kpxc-separator {
|
||||
border-left: 1px solid #ccc;
|
||||
height: 100% !important;
|
||||
|
|
@ -133,7 +113,6 @@ div.kpxc-banner label {
|
|||
font-size: 12px !important;
|
||||
font-weight: normal !important;
|
||||
margin: 4px !important;
|
||||
text-wrap: nowrap;
|
||||
}
|
||||
|
||||
div.kpxc-banner-dialog {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
height: 28px !important;
|
||||
margin: .2em !important;
|
||||
padding: 1px 7px 2px !important;
|
||||
text-wrap: nowrap;
|
||||
}
|
||||
|
||||
.kpxc-white-button {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
--kpxc-autocomplete-menu-border: 1px solid #ddd;
|
||||
--kpxc-background-color: #fff;
|
||||
--kpxc-box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
|
||||
--kpxc-box-shadow-strong: 0 20px 25px -5px rgb(0 0 0 / 0.8), 0 8px 10px -6px rgb(0 0 0 / 0.8);
|
||||
--kpxc-card-background-color: #fff;
|
||||
--kpxc-card-border-color: rgba(0, 0, 0, .125);
|
||||
--kpxc-card-header-color: #fafafa;
|
||||
|
|
@ -30,6 +29,7 @@
|
|||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
|
||||
:root,
|
||||
:host {
|
||||
--kpxc-autocomplete-footer-color: #2b2a2a;
|
||||
|
|
@ -114,3 +114,4 @@
|
|||
--kpxc-lighter-text-color: rgba(0, 0, 0, 0.8);
|
||||
--kpxc-light-text-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,16 +43,6 @@
|
|||
background-size: contain;
|
||||
}
|
||||
|
||||
.kpxc-notification .kpxc-banner-icon-safari {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding: 10px;
|
||||
margin-right: 4px;
|
||||
overflow: hidden;
|
||||
background: url('safari-web-extension://__MSG_@@extension_id__/icons/keepassxc.svg') right no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.kpxc-notification .kpxc-label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
.kpxc-pwgen-icon {
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
padding: 0 !important;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
|
|
@ -14,8 +12,3 @@
|
|||
background: url('moz-extension://__MSG_@@extension_id__/icons/key.svg') right no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.kpxc-pwgen-icon.key-safari {
|
||||
background: url('safari-web-extension://__MSG_@@extension_id__/icons/key.svg') right no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,14 @@
|
|||
.kpxc-totp-icon {
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
padding: 0 !important;
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.kpxc-totp-icon {
|
||||
.kpxc-totp-icon.default {
|
||||
background: url('chrome-extension://__MSG_@@extension_id__/icons/otp.svg') right no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.kpxc-totp-icon-moz {
|
||||
.kpxc-totp-icon.moz {
|
||||
background: url('moz-extension://__MSG_@@extension_id__/icons/otp.svg') right no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.kpxc-totp-icon.safari {
|
||||
background: url('safari-web-extension://__MSG_@@extension_id__/icons/otp.svg') right no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
.kpxc-username-icon {
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
padding: 0 !important;
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.kpxc-username-icon.disconnected {
|
||||
|
|
@ -15,11 +13,6 @@
|
|||
background-size: contain;
|
||||
}
|
||||
|
||||
.kpxc-username-icon.disconnected-safari {
|
||||
background: url('safari-web-extension://__MSG_@@extension_id__/icons/disconnected.svg') right no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.kpxc-username-icon.lock {
|
||||
background: url('chrome-extension://__MSG_@@extension_id__/icons/locked.svg') right no-repeat;
|
||||
background-size: contain;
|
||||
|
|
@ -30,11 +23,6 @@
|
|||
background-size: contain;
|
||||
}
|
||||
|
||||
.kpxc-username-icon.lock-safari {
|
||||
background: url('safari-web-extension://__MSG_@@extension_id__/icons/locked.svg') right no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.kpxc-username-icon.unlock {
|
||||
background: url('chrome-extension://__MSG_@@extension_id__/icons/keepassxc.svg') right no-repeat;
|
||||
background-size: contain;
|
||||
|
|
@ -44,8 +32,3 @@
|
|||
background: url('moz-extension://__MSG_@@extension_id__/icons/keepassxc.svg') right no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.kpxc-username-icon.unlock-safari {
|
||||
background: url('safari-web-extension://__MSG_@@extension_id__/icons/keepassxc.svg') right no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"manifest_version": 3,
|
||||
"name": "KeePassXC-Browser",
|
||||
"version": "1.10.0.1",
|
||||
"version_name": "1.10.0.1",
|
||||
"minimum_chrome_version": "124",
|
||||
"version": "1.9.9.2",
|
||||
"version_name": "1.9.9.2",
|
||||
"minimum_chrome_version": "93",
|
||||
"description": "__MSG_extensionDescription__",
|
||||
"author": "KeePassXC Team",
|
||||
"icons": {
|
||||
|
|
@ -53,8 +53,7 @@
|
|||
"content/fields.js",
|
||||
"content/fill.js",
|
||||
"content/form.js",
|
||||
"content/icon.js",
|
||||
"content/icon-handler.js",
|
||||
"content/icons.js",
|
||||
"content/keepassxc-browser.js",
|
||||
"content/observer-helper.js",
|
||||
"content/pwgen.js",
|
||||
|
|
@ -119,15 +118,12 @@
|
|||
"choose_credential_fields": {
|
||||
"description": "__MSG_popupChooseCredentialsText__"
|
||||
},
|
||||
"retrieve_credentials_forced": {
|
||||
"description": "__MSG_credentialsRetrieveButton__"
|
||||
"retrive_credentials_forced": {
|
||||
"description": "__MSG_popupReopenButton__"
|
||||
},
|
||||
"request_autotype": {
|
||||
"description": "__MSG_contextMenuRequestGlobalAutoType__"
|
||||
},
|
||||
"reopen_database": {
|
||||
"description": "__MSG_popupReopenButton__"
|
||||
},
|
||||
"reload_extension": {
|
||||
"description": "__MSG_popupReloadButton__"
|
||||
}
|
||||
|
|
@ -170,7 +166,8 @@
|
|||
"tabs",
|
||||
"webNavigation",
|
||||
"webRequest",
|
||||
"webRequestAuthProvider"
|
||||
"webRequestAuthProvider",
|
||||
"webRequestBlocking"
|
||||
],
|
||||
"optional_permissions": [
|
||||
"privacy"
|
||||
|
|
|
|||
|
|
@ -82,10 +82,6 @@ pre {
|
|||
white-space: pre-line;
|
||||
}
|
||||
|
||||
.no-transitions .form-check-input {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
.table > caption {
|
||||
color: var(--kpxc-text-color);
|
||||
}
|
||||
|
|
@ -109,6 +105,12 @@ tbody {
|
|||
color: var(--kpxc-text-color);
|
||||
}
|
||||
|
||||
.table-striped > tbody > tr:nth-of-type(odd) input[type="checkbox"] {
|
||||
background-color: var(--kpxc-card-background-color) !important;
|
||||
border-color: var(--kpxc-input-main-border-color) !important;
|
||||
color: var(--kpxc-text-color) !important;
|
||||
}
|
||||
|
||||
.table-striped > tbody > tr:nth-of-type(odd) .form-check-input:checked {
|
||||
background-color: var(--kpxc-checkbox-background-color) !important;
|
||||
}
|
||||
|
|
@ -134,14 +136,14 @@ table tbody tr.empty:not(:nth-last-child(2)) {
|
|||
display: none;
|
||||
}
|
||||
|
||||
#tab-site-preferences td:nth-of-type(3),
|
||||
#tab-site-preferences td:nth-of-type(3) select {
|
||||
#tab-site-preferences td:nth-of-type(2),
|
||||
#tab-site-preferences td:nth-of-type(2) select {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
#tab-site-preferences td:nth-of-type(2),
|
||||
#tab-site-preferences td:nth-of-type(3),
|
||||
#tab-site-preferences td:nth-of-type(4),
|
||||
#tab-site-preferences td:nth-of-type(5),
|
||||
table td:last-of-type {
|
||||
width: 1px;
|
||||
}
|
||||
|
|
@ -231,26 +233,6 @@ table td:last-of-type {
|
|||
color: var(--kpxc-text-color);
|
||||
}
|
||||
|
||||
.non-clickable {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.settings-dropdown {
|
||||
box-shadow: var(--kpxc-box-shadow-strong);
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.settings-dropdown-body {
|
||||
background-color: var(--bs-table-bg);
|
||||
}
|
||||
|
||||
.dropdown-divider {
|
||||
border-top: 1px solid var(--kpxc-table-odd-color);
|
||||
height: 0.5rem;
|
||||
margin-top: 0.3rem;
|
||||
}
|
||||
|
||||
.site-preferences-input {
|
||||
border: 0;
|
||||
border-bottom-right-radius: 4px !important;
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
<script src="options.js"></script>
|
||||
<script src="../common/translate.js" defer></script>
|
||||
</head>
|
||||
<body class="pt-3 pb-5 no-transitions">
|
||||
<body class="pt-3 pb-5">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<nav class="col-md-3 col-lg-2 sidebar">
|
||||
|
|
@ -31,7 +31,7 @@
|
|||
<ul class="nav flex-column mb-2 text-uppercase">
|
||||
<li class="active nav-item">
|
||||
<a class="nav-link text-light rounded-sm" href="#general-settings">
|
||||
<i class="fa fa-cog fa-lg pe-2" aria-hidden="true"></i>
|
||||
<i class="fa fa-ellipsis-h fa-lg pe-2" aria-hidden="true"></i>
|
||||
<span class="menu-item" data-i18n="optionsMenuGeneral"></span>
|
||||
</a>
|
||||
</li>
|
||||
|
|
@ -49,7 +49,7 @@
|
|||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-light rounded-sm" href="#site-preferences">
|
||||
<i class="fa fa-list-alt fa-lg pe-2" aria-hidden="true"></i>
|
||||
<i class="fa fa-wrench fa-lg pe-2" aria-hidden="true"></i>
|
||||
<span class="menu-item" data-i18n="optionsMenuSitePreferences"></span>
|
||||
</a>
|
||||
</li>
|
||||
|
|
@ -81,12 +81,6 @@
|
|||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<!-- Version warning -->
|
||||
<div class="alert alert-warning mt-3 col-lg-9" role="alert" id="minimumVersionAlert" style="display: none;">
|
||||
<i class="fa fa-exclamation-triangle" aria-hidden="true"></i>
|
||||
<span data-i18n="optionsMinimumKeePassXCVersionRequired" data-i18n-placeholder="2.6.0"></span>
|
||||
</div>
|
||||
|
||||
<!-- Color theme -->
|
||||
<div class="form-group col-sm-3 pb-2">
|
||||
<label for="colorTheme" class="form-label" data-i18n="optionsThemeSelectionHeader"></label>
|
||||
|
|
@ -160,7 +154,7 @@
|
|||
</div>
|
||||
|
||||
<!-- Keyboard shortcuts -->
|
||||
<div class="card my-4 shadow" id="keyboardShortcuts">
|
||||
<div class="card my-4 shadow">
|
||||
<div class="card-header h6 rounded-0">
|
||||
<i class="fa fa-keyboard-o" aria-hidden="true"></i>
|
||||
<span data-i18n="optionsKeyboardShortcutsHeader"></span>
|
||||
|
|
@ -266,7 +260,7 @@
|
|||
<input class="form-check-input" type="checkbox" name="autoFillSingleEntry" id="autoFillSingleEntry" value="true">
|
||||
<label class="form-check-label" for="autoFillSingleEntry" data-i18n="optionsCheckboxAutoFillSingleEntry"></label>
|
||||
<div class="form-text" data-i18n="optionsAutoFillSingleEntryHelpText"></div>
|
||||
|
||||
|
||||
<div class="alert alert-warning mt-3 col-lg-9" role="alert">
|
||||
<i class="fa fa-exclamation-triangle" aria-hidden="true"></i>
|
||||
<span data-i18n="optionsAutoFillSingleEntryWarning"></span>
|
||||
|
|
@ -284,7 +278,7 @@
|
|||
</div>
|
||||
|
||||
<!-- Autofill HTTP Auth dialogs -->
|
||||
<div class="form-group pb-1" id="autoFillHttpAuth">
|
||||
<div class="form-group pb-1">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" name="autoFillAndSend" id="autoFillAndSend" value="true">
|
||||
<label class="form-check-label" for="autoFillAndSend" data-i18n="optionsCheckboxAutoFillAndSend"></label>
|
||||
|
|
@ -358,8 +352,8 @@
|
|||
|
||||
<!-- Number of allowed redirects -->
|
||||
<div class="form-group w-50 pb-2">
|
||||
<label id="redirectAllowanceLabel" class="font-weight-normal" for="redirectAllowance" data-i18n="optionsRedirectAllowance" data-i18n-placeholder="3"></label>
|
||||
<input type="range" class="form-range" id="redirectAllowance" name="redirectAllowance" min="1" max="11" step="1" value="3">
|
||||
<label id="redirectAllowanceLabel" class="font-weight-normal" for="redirectAllowance" data-i18n="optionsRedirectAllowance" data-i18n-placeholder="1"></label>
|
||||
<input type="range" class="form-range" id="redirectAllowance" name="redirectAllowance" min="1" max="11" step="1" value="1">
|
||||
<div class="form-text help-text" data-i18n="optionsRedirectAllowanceHelpText"></div>
|
||||
</div>
|
||||
|
||||
|
|
@ -565,10 +559,6 @@
|
|||
<i class="fa fa-arrow-up" aria-hidden="true"></i>
|
||||
<span data-i18n="optionsButtonExport"></span>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-danger ms-2" id="resetSettingsButton" data-i18n="[title]optionsButtonResetSettings">
|
||||
<i class="fa fa-undo" aria-hidden="true"></i>
|
||||
<span data-i18n="optionsButtonResetSettings"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -596,30 +586,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="dialogResetSettings" class="modal fade" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="myModalLabel" data-i18n="optionsButtonResetSettings"></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p data-i18n="optionsResetSettingsDialogText"></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-sm btn-secondary" data-bs-dismiss="modal" aria-hidden="true">
|
||||
<i class="fa fa-remove" aria-hidden="true"></i>
|
||||
<span data-i18n="optionsButtonCancel"></span>
|
||||
</button>
|
||||
<button class="btn btn-sm yes btn-danger">
|
||||
<i class="fa fa-check" aria-hidden="true"></i>
|
||||
<span data-i18n="optionsButtonReset"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Connected Databases -->
|
||||
|
|
@ -795,14 +761,16 @@
|
|||
<thead>
|
||||
<tr>
|
||||
<th scope="col"><span data-i18n="optionsColumnPageURL"></span></th>
|
||||
<th scope="col"></th>
|
||||
<th scope="col"><span data-i18n="optionsColumnFeatures"></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="optionsColumnImprovedInputFieldDetection"></span></th>
|
||||
<th scope="col"><span data-i18n="optionsColumnAllowIframes"></span></th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="empty">
|
||||
<td colspan="4"><span data-i18n="optionsSitePreferencesNotFound"></span></td>
|
||||
<td colspan="5"><span data-i18n="optionsSitePreferencesNotFound"></span></td>
|
||||
</tr>
|
||||
<tr class="clone d-none">
|
||||
<td class="text-nowrap">
|
||||
|
|
@ -822,20 +790,17 @@
|
|||
</button>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-nowrap">
|
||||
<button type="button" class="btn btn-sm btn-primary container-fluid" id="settings-button">
|
||||
<span class="non-clickable" data-i18n="optionsSitePreferencesSettings"></span>
|
||||
</button>
|
||||
</td>
|
||||
<td>
|
||||
<select class="form-select form-select-sm" id="ignore-select" name="ignore" data-i18n="[title]optionsSitePreferencesSelect">
|
||||
<td>
|
||||
<select class="form-select form-select-sm" name="ignore" data-i18n="[title]optionsSitePreferencesSelect">
|
||||
<option value="ignoreNothing" data-i18n="optionsSelectionNothing"></option>
|
||||
<option value="ignoreNormal" data-i18n="optionsSelectionNormal"></option>
|
||||
<option value="ignoreAutoSubmit" data-i18n="optionsSelectionAutoSubmit"></option>
|
||||
<option value="ignorePasskeys" data-i18n="optionsSelectionPasskeys"></option>
|
||||
<option value="ignoreFull" data-i18n="optionsSelectionFull"></option>
|
||||
</select>
|
||||
</td>
|
||||
<td><input class="form-check-input" type="checkbox" name="usernameOnly" value="false" data-i18n="[title]optionsColumnUsernameOnly"></td>
|
||||
<td><input class="form-check-input" type="checkbox" name="improvedFieldDetection" value="false" data-i18n="[title]optionsColumnImprovedInputFieldDetection"></td>
|
||||
<td><input class="form-check-input" type="checkbox" name="allowIframes" value="false" data-i18n="[title]optionsColumnAllowIframes"></td>
|
||||
<td class="text-nowrap">
|
||||
<button class="btn btn-sm delete btn-danger" data-i18n="[title]removeSitePreferencesButtonTitle">
|
||||
<i class="fa fa-remove" aria-hidden="true"></i>
|
||||
|
|
@ -873,32 +838,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Settings dropdown -->
|
||||
<div class="card settings-dropdown" style="display: none;">
|
||||
<div class="card-body settings-dropdown-body">
|
||||
<!-- Username only detection -->
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="usernameOnly" name="usernameOnly" value="false" data-i18n="[title]optionsColumnUsernameOnly">
|
||||
<label class="form-check-label" for="usernameOnly" data-i18n="optionsColumnUsernameOnly"></label>
|
||||
</div>
|
||||
|
||||
<!-- Improved input field detection -->
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="improvedFieldDetection" name="improvedFieldDetection" value="false" data-i18n="[title]optionsColumnImprovedInputFieldDetection">
|
||||
<label class="form-check-label" for="improvedFieldDetection" data-i18n="optionsColumnImprovedInputFieldDetection"></label>
|
||||
</div>
|
||||
|
||||
<!-- Allow iFrames -->
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="allowIframes" name="allowIframes" value="false" data-i18n="[title]optionsColumnAllowIframes">
|
||||
<label class="form-check-label" for="allowIframes" data-i18n="optionsColumnAllowIframes"></label>
|
||||
</div>
|
||||
|
||||
<!-- Add new options below -->
|
||||
<!--<hr class="dropdown-divider">-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- About -->
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
const options = {};
|
||||
options.dropdownButton = null;
|
||||
options.isFirefox = false;
|
||||
|
||||
const $ = function(elem) {
|
||||
return document.querySelector(elem);
|
||||
|
|
@ -208,7 +206,7 @@ options.initGeneralSettings = async function() {
|
|||
});
|
||||
|
||||
$('#configureCommands').addEventListener('click', function() {
|
||||
if (options.isFirefox) {
|
||||
if (isFirefox()) {
|
||||
if (typeof(browser.commands.openShortcutSettings) === 'function') {
|
||||
browser.commands.openShortcutSettings();
|
||||
} else {
|
||||
|
|
@ -287,7 +285,7 @@ options.initGeneralSettings = async function() {
|
|||
// Verify the import
|
||||
temporarySettings = contents;
|
||||
dialogImportSettingsModal.show();
|
||||
} catch (_err) {
|
||||
} catch (err) {
|
||||
console.log('Error loading JSON settings file.');
|
||||
}
|
||||
};
|
||||
|
|
@ -313,24 +311,6 @@ options.initGeneralSettings = async function() {
|
|||
}
|
||||
});
|
||||
|
||||
// Reset all settings modal
|
||||
const dialogResetSettingsModal = new bootstrap.Modal('#dialogResetSettings',
|
||||
{ keyboard: true, focus: false, backdrop: true });
|
||||
|
||||
$('#dialogResetSettings').addEventListener('shown.bs.modal', function(modalEvent) {
|
||||
modalEvent.currentTarget.querySelector('.modal-footer button.yes').focus();
|
||||
});
|
||||
|
||||
$('#resetSettingsButton').addEventListener('click', function() {
|
||||
dialogResetSettingsModal.show();
|
||||
});
|
||||
|
||||
$('#dialogResetSettings .modal-footer button.yes').addEventListener('click', function(e) {
|
||||
dialogResetSettingsModal.hide();
|
||||
browser.runtime.sendMessage({ action: 'reset_all_settings' });
|
||||
location.reload();
|
||||
});
|
||||
|
||||
$('#copyVersionToClipboard').addEventListener('click', function () {
|
||||
const copyText = document.getElementById('versionInfo').innerText;
|
||||
navigator.clipboard.writeText(copyText);
|
||||
|
|
@ -366,23 +346,38 @@ options.showKeePassXCVersions = async function(response) {
|
|||
$('#tab-about span.kpxcVersion').textContent = response.current;
|
||||
$('#tab-general-settings button.checkUpdateKeePassXC').disabled = false;
|
||||
|
||||
const featureList = await browser.runtime.sendMessage({ action: 'get_features_list' });
|
||||
if (featureList?.requiredKeePassXCVersionFound) {
|
||||
const versionResults = await browser.runtime.sendMessage({
|
||||
action: 'compare_versions',
|
||||
args: [
|
||||
[
|
||||
'2.6.0',
|
||||
'2.7.0',
|
||||
'2.7.7',
|
||||
'2.7.10'
|
||||
],
|
||||
response.current
|
||||
],
|
||||
});
|
||||
|
||||
// Hide/disable certain options with older KeePassXC versions than 2.6.0
|
||||
if (versionResults['2.6.0']) {
|
||||
$('#tab-general-settings #versionRequiredAlert').hide();
|
||||
} else {
|
||||
$('#tab-general-settings #showGroupNameInAutocomplete').disabled = true;
|
||||
$('#tab-general-settings #minimumVersionAlert').show();
|
||||
}
|
||||
|
||||
if (!featureList?.downloadFaviconAfterSave) {
|
||||
// Hide certain options with older KeePassXC versions than 2.7.0
|
||||
if (!versionResults['2.7.0']) {
|
||||
$('#tab-general-settings #downloadFaviconAfterSaveFormGroup').hide();
|
||||
}
|
||||
|
||||
if (!featureList?.passkeys) {
|
||||
// Hide certain options with older KeePassXC versions than 2.7.7
|
||||
if (!versionResults['2.7.7']) {
|
||||
$('#tab-general-settings #passkeysOptionsCard').hide();
|
||||
}
|
||||
|
||||
if (!featureList?.passkeysDefaultGroup) {
|
||||
// Hide passkeys default group option with KeePassXC version < 2.7.10
|
||||
if (!versionResults['2.7.10']) {
|
||||
$('#tab-general-settings #passkeysDefaultGroup').hide();
|
||||
}
|
||||
};
|
||||
|
|
@ -541,43 +536,6 @@ options.initSitePreferences = function() {
|
|||
modalEvent.currentTarget.querySelector('.modal-footer button.yes').focus();
|
||||
});
|
||||
|
||||
const settingsButtonClicked = function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const closestTr = e.target.closest('tr');
|
||||
const url = closestTr.getAttribute('url');
|
||||
const sitePreferences = options.settings['sitePreferences']?.find((pref) => pref?.url === url);
|
||||
const usernameOnly = sitePreferences.usernameOnly;
|
||||
const improvedFieldDetection = sitePreferences.improvedFieldDetection;
|
||||
const allowIframes = sitePreferences.allowIframes;
|
||||
|
||||
const dropdown = $('.settings-dropdown');
|
||||
if (dropdown?.style?.display !== 'block'
|
||||
|| (dropdown?.style?.display === 'block' && e.target !== options.dropdownButton)) {
|
||||
if (options.dropdownButton) {
|
||||
options.dropdownButton.classList.remove('active');
|
||||
// Update number of enabled settings to the old Settings button
|
||||
const checkboxValues = Array.from(dropdown?.querySelectorAll('input[type=checkbox]')).map(c => c.checked);
|
||||
updateSettingsButtonText(options.dropdownButton, checkboxValues);
|
||||
}
|
||||
|
||||
// Apply current settings to the dropdown
|
||||
dropdown.querySelector('#usernameOnly').checked = usernameOnly;
|
||||
dropdown.querySelector('#improvedFieldDetection').checked = improvedFieldDetection;
|
||||
dropdown.querySelector('#allowIframes').checked = allowIframes;
|
||||
|
||||
dropdown?.show();
|
||||
updateDropdownPosition(e, dropdown);
|
||||
options.dropdownButton = e.target;
|
||||
options.dropdownButton.classList.add('active');
|
||||
} else {
|
||||
dropdown?.hide();
|
||||
options.dropdownButton.classList.remove('active');
|
||||
updateSettingsButtonText(options.dropdownButton, [ usernameOnly, improvedFieldDetection, allowIframes ]);
|
||||
options.dropdownButton = null;
|
||||
}
|
||||
};
|
||||
|
||||
const removeButtonClicked = function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
|
|
@ -643,18 +601,18 @@ options.initSitePreferences = function() {
|
|||
}
|
||||
};
|
||||
|
||||
const checkboxClicked = async function(e) {
|
||||
const closestTr = options?.dropdownButton?.closest('tr');
|
||||
const checkboxClicked = function() {
|
||||
const closestTr = this.closest('tr');
|
||||
const url = closestTr.getAttribute('url');
|
||||
|
||||
for (const site of options.settings['sitePreferences']) {
|
||||
if (site.url === url) {
|
||||
if (e.target.name === SitePreferences.USERNAME_ONLY) {
|
||||
site.usernameOnly = e.target.checked;
|
||||
} else if (e.target.name === SitePreferences.IMPROVED_FIELD_DETECTION) {
|
||||
site.improvedFieldDetection = e.target.checked;
|
||||
} else if (e.target.name === SitePreferences.ALLOW_IFRAMES) {
|
||||
site.allowIframes = e.target.checked;
|
||||
if (this.name === 'usernameOnly') {
|
||||
site.usernameOnly = this.checked;
|
||||
} else if (this.name === 'improvedFieldDetection') {
|
||||
site.improvedFieldDetection = this.checked;
|
||||
} else if (this.name === 'allowIframes') {
|
||||
site.allowIframes = this.checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -675,11 +633,6 @@ options.initSitePreferences = function() {
|
|||
options.saveSettings();
|
||||
};
|
||||
|
||||
const dropdown = $('.settings-dropdown');
|
||||
dropdown.querySelector('#usernameOnly').addEventListener('change', checkboxClicked);
|
||||
dropdown.querySelector('#improvedFieldDetection').addEventListener('change',checkboxClicked);
|
||||
dropdown.querySelector('#allowIframes').addEventListener('change', checkboxClicked);
|
||||
|
||||
const addNewRow = function(rowClone, newIndex, url, ignore, usernameOnly, improvedFieldDetection, allowIframes) {
|
||||
const row = rowClone.cloneNode(true);
|
||||
row.setAttribute('url', url);
|
||||
|
|
@ -709,25 +662,19 @@ options.initSitePreferences = function() {
|
|||
saveModifiedUrl(e, row, inputField, editButton, cancelButton, saveButton)
|
||||
);
|
||||
|
||||
// Page URL
|
||||
row.children[0].children[0].children[0].value = url;
|
||||
row.children[0].children[0]?.addEventListener('dblclick', (e) =>
|
||||
row.children[0].children[0]?.addEventListener('dblclick', (e) =>
|
||||
enterEditMode(e, row, inputField, editButton, cancelButton, saveButton)
|
||||
);
|
||||
|
||||
// Settings
|
||||
const settings = row.children[1];
|
||||
updateSettingsButtonText(settings.querySelector('#settings-button'),
|
||||
[ usernameOnly, improvedFieldDetection, allowIframes ]);
|
||||
settings.querySelector('#settings-button').addEventListener('click', (e) => settingsButtonClicked(e));
|
||||
|
||||
// Ignore
|
||||
const ignoreSelect = row.children[2];
|
||||
ignoreSelect.querySelector('#ignore-select').value = ignore;
|
||||
ignoreSelect.querySelector('#ignore-select').addEventListener('change', selectionChanged);
|
||||
|
||||
// Remove button
|
||||
row.children[3].addEventListener('click', removeButtonClicked);
|
||||
row.children[1].children[0].value = ignore;
|
||||
row.children[1].children[0].addEventListener('change', selectionChanged);
|
||||
row.children[2].children['usernameOnly'].checked = usernameOnly;
|
||||
row.children[2].children['usernameOnly'].addEventListener('change', checkboxClicked);
|
||||
row.children[3].children['improvedFieldDetection'].checked = improvedFieldDetection;
|
||||
row.children[3].children['improvedFieldDetection'].addEventListener('change', checkboxClicked);
|
||||
row.children[4].children['allowIframes'].checked = allowIframes;
|
||||
row.children[4].children['allowIframes'].addEventListener('change', checkboxClicked);
|
||||
row.children[5].addEventListener('click', removeButtonClicked);
|
||||
|
||||
$('#tab-site-preferences table tbody').append(row);
|
||||
};
|
||||
|
|
@ -791,11 +738,11 @@ options.initSitePreferences = function() {
|
|||
$('#tab-site-preferences table tbody tr.empty').hide();
|
||||
|
||||
options.settings['sitePreferences'].push({
|
||||
allowIframes: false,
|
||||
ignore: IGNORE_NOTHING,
|
||||
improvedFieldDetection: false,
|
||||
url: value,
|
||||
ignore: IGNORE_NOTHING,
|
||||
usernameOnly: false,
|
||||
improvedFieldDetection: false,
|
||||
allowIframes: false,
|
||||
});
|
||||
options.saveSettings();
|
||||
manualUrl.value = '';
|
||||
|
|
@ -830,7 +777,7 @@ options.initAbout = function() {
|
|||
$('#tab-about span.versionCIP').textContent = version;
|
||||
$('#tab-about span.kpxcbrVersion').textContent = version;
|
||||
$('#tab-about span.kpxcbrOS').textContent = platform;
|
||||
$('#tab-about span.kpxcbrBrowser').textContent = getBrowserId(navigator.userAgent);
|
||||
$('#tab-about span.kpxcbrBrowser').textContent = getBrowserId();
|
||||
};
|
||||
|
||||
options.updateTheme = function() {
|
||||
|
|
@ -858,110 +805,24 @@ options.createWarning = function(elem, text) {
|
|||
}, 5000);
|
||||
};
|
||||
|
||||
options.hideUnsupportedFeatures = function() {
|
||||
if (isSafari()) {
|
||||
$('#tab-general-settings div#keyboardShortcuts').hide();
|
||||
$('#tab-general-settings div#autoFillHttpAuth').hide();
|
||||
}
|
||||
};
|
||||
|
||||
const getBrowserId = function(userAgent) {
|
||||
const browserQueries = [
|
||||
{ findStr: 'Firefox', name: 'Mozilla Firefox' },
|
||||
{ findStr: 'Edg', name: 'Microsoft Edge' },
|
||||
{ findStr: 'OPR', name: 'Opera' },
|
||||
{ findStr: 'Chrome', name: 'Chrome/Chromium' },
|
||||
{ findStr: 'Version/', name: 'Safari' }
|
||||
];
|
||||
|
||||
const getVersion = (agent, findStr) => {
|
||||
const match = agent?.match(new RegExp(`(?:${findStr})\/([\\d.]+)`));
|
||||
return match ? match[1] : 'Unknown version';
|
||||
};
|
||||
|
||||
for (const query of browserQueries) {
|
||||
if (userAgent?.indexOf(query.findStr) > -1) {
|
||||
return `${query.name} ${getVersion(userAgent, query.findStr)}`;
|
||||
}
|
||||
const getBrowserId = function() {
|
||||
if (navigator.userAgent.indexOf('Firefox') > -1) {
|
||||
return 'Mozilla Firefox ' + navigator.userAgent.substr(navigator.userAgent.lastIndexOf('/') + 1);
|
||||
} else if (navigator.userAgent.indexOf('Edg') > -1) {
|
||||
let startPos = navigator.userAgent.indexOf('Edg');
|
||||
startPos = navigator.userAgent.indexOf('/', startPos) + 1;
|
||||
const version = navigator.userAgent.substring(startPos);
|
||||
return 'Microsoft Edge ' + version;
|
||||
} 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';
|
||||
};
|
||||
|
||||
// Update the number of enabled settings to the button text
|
||||
const updateSettingsButtonText = function(buttonElement, enabledOptions = []) {
|
||||
const numberOfEnabledOptions = enabledOptions.filter(o => o === true).length;
|
||||
const buttonText = buttonElement.querySelector('span');
|
||||
|
||||
if (numberOfEnabledOptions > 0) {
|
||||
buttonText.textContent =
|
||||
`${browser.i18n.getMessage('optionsSitePreferencesSettings')} (${numberOfEnabledOptions})`;
|
||||
} else {
|
||||
buttonText.textContent = browser.i18n.getMessage('optionsSitePreferencesSettings');
|
||||
}
|
||||
};
|
||||
|
||||
// Updates settings dropdown menu position
|
||||
const updateDropdownPosition = function(e, dropdown) {
|
||||
if (!dropdown) {
|
||||
dropdown = $('.settings-dropdown');
|
||||
}
|
||||
|
||||
const settingsButton = e?.target ?? options.dropdownButton;
|
||||
const rect = settingsButton?.getClientRects()?.[0];
|
||||
if (!rect) {
|
||||
return;
|
||||
}
|
||||
|
||||
const zoom = getComputedStyle(document.body).zoom || 1;
|
||||
const scrollTop = document.defaultView.scrollY / zoom;
|
||||
const scrollLeft = document.defaultView?.scrollX / zoom;
|
||||
|
||||
// If dropdown does not fit to the bottom of the screen -> show it at the top of the settings button
|
||||
const dropdownRect = dropdown.getBoundingClientRect();
|
||||
const totalHeight = dropdownRect.height + rect.height;
|
||||
const offset = (totalHeight + rect.y) / zoom > window.self.visualViewport.height ? totalHeight / zoom : 0;
|
||||
|
||||
dropdown.style.left = Pixels(rect.left / zoom + scrollLeft);
|
||||
dropdown.style.top = Pixels(rect.bottom / zoom + scrollTop - offset);
|
||||
};
|
||||
|
||||
// Hides the settings dropdown when clicked outside of it
|
||||
document.addEventListener('mouseup', function(e) {
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dropdown = $('.settings-dropdown');
|
||||
if (dropdown?.style?.display !== 'block') {
|
||||
return;
|
||||
}
|
||||
|
||||
const rect = dropdown?.getClientRects()?.[0];
|
||||
if (!rect) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((e.x > rect.right || e.x < rect.x) || (e.y > rect.bottom || e.y < rect.y)
|
||||
&& e.target.nodeName !== 'BUTTON') {
|
||||
dropdown?.hide();
|
||||
const checkboxValues = Array.from(dropdown?.querySelectorAll('input[type=checkbox]')).map(c => c.checked);
|
||||
updateSettingsButtonText(options.dropdownButton, checkboxValues);
|
||||
options.dropdownButton.classList.remove('active');
|
||||
options.dropdownButton = null;
|
||||
}
|
||||
});
|
||||
|
||||
// Handle dropdown position on window resize
|
||||
window.addEventListener('resize', function() {
|
||||
updateDropdownPosition();
|
||||
});
|
||||
|
||||
// Handle dropdown position on scroll
|
||||
window.addEventListener('scroll', function() {
|
||||
updateDropdownPosition();
|
||||
});
|
||||
|
||||
(async() => {
|
||||
try {
|
||||
// We eagerly load the theme here to avoid a white flash
|
||||
|
|
@ -976,20 +837,12 @@ window.addEventListener('scroll', function() {
|
|||
|
||||
const keyRing = await browser.runtime.sendMessage({ action: 'load_keyring' });
|
||||
options.keyRing = keyRing;
|
||||
options.isFirefox = isFirefox();
|
||||
|
||||
options.initMenu();
|
||||
await options.initGeneralSettings();
|
||||
options.initConnectedDatabases();
|
||||
options.initCustomLoginFields();
|
||||
options.initSitePreferences();
|
||||
options.initAbout();
|
||||
options.hideUnsupportedFeatures();
|
||||
|
||||
// The form-switch transitions should complete in 150 ms
|
||||
setTimeout(() => {
|
||||
document.body.classList.remove('no-transitions');
|
||||
}, 200);
|
||||
} catch (err) {
|
||||
console.log('Error loading options page: ' + err);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -153,15 +153,6 @@ code {
|
|||
width: 2.5rem;
|
||||
}
|
||||
|
||||
#choose-custom-login-fields-button-safari {
|
||||
background-image: url('safari-web-extension://__MSG_@@extension_id__/icons/custom_login_fields.svg');
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 70%;
|
||||
height: 31px;
|
||||
width: 2.5rem;
|
||||
}
|
||||
|
||||
#lock-database-button {
|
||||
display: none;
|
||||
width: 2.5rem;
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ const sendMessageToTab = async function(message) {
|
|||
});
|
||||
|
||||
// This does not work with Firefox because of https://bugzilla.mozilla.org/show_bug.cgi?id=1665380
|
||||
await sendMessageToTab('retrieve_credentials_forced');
|
||||
await sendMessageToTab('retrive_credentials_forced');
|
||||
close();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,9 @@ async function initSettings() {
|
|||
});
|
||||
|
||||
const customLoginFieldsButton = document.body.querySelector('#settings #choose-custom-login-fields-button');
|
||||
customLoginFieldsButton.id = getIconClass('choose-custom-login-fields-button');
|
||||
if (isFirefox()) {
|
||||
customLoginFieldsButton.id = 'choose-custom-login-fields-button-moz';
|
||||
}
|
||||
|
||||
customLoginFieldsButton.addEventListener('click', async () => {
|
||||
const tab = await getCurrentTab();
|
||||
|
|
|
|||
1185
package-lock.json
generated
1185
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,13 +1,12 @@
|
|||
{
|
||||
"name": "keepassxc-browser",
|
||||
"version": "1.10.0.1",
|
||||
"version": "1.9.9.2",
|
||||
"description": "KeePassXC-Browser",
|
||||
"main": "build.js",
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.1",
|
||||
"@playwright/test": "^1.56.1",
|
||||
"@playwright/test": "^1.45.1",
|
||||
"@types/node": "^20.14.10",
|
||||
"eslint": "^9.39.1"
|
||||
"eslint": "^8.49.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@npmcli/fs": "^2.1.0"
|
||||
|
|
@ -16,7 +15,7 @@
|
|||
"build": "node build.js",
|
||||
"debug:chromium": "cp dist/manifest_chromium.json keepassxc-browser/manifest.json",
|
||||
"debug:firefox": "cp dist/manifest_firefox.json keepassxc-browser/manifest.json",
|
||||
"lint": "npx eslint --no-warn-ignored keepassxc-browser/**/*.js",
|
||||
"lint": "npx eslint keepassxc-browser/**/*.js",
|
||||
"tests": "npx playwright test"
|
||||
},
|
||||
"repository": {
|
||||
|
|
|
|||
8
tests/.eslintrc
Normal file
8
tests/.eslintrc
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"sourceType": "module"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
import { test, expect } from '@playwright/test';
|
||||
import {
|
||||
compareVersion,
|
||||
elementsOverlap,
|
||||
matchesWithNodeName,
|
||||
siteMatch,
|
||||
slashNeededForUrl,
|
||||
|
|
@ -90,48 +89,3 @@ test('Test trimURL()', async ({ page }) => {
|
|||
expect(trimURL('https://example.com/path/')).toBe('https://example.com/path/');
|
||||
expect(trimURL('https://example.com/path/#extra')).toBe('https://example.com/path/#extra');
|
||||
});
|
||||
|
||||
// Check if different popups/overlays partially covers or touches the input field
|
||||
test('Test elementsOverlap()', async ({ page }) => {
|
||||
const inputRect = { left: 0, top: 5, right: 200, bottom: 28 }
|
||||
|
||||
// Fully covered
|
||||
expect(elementsOverlap(inputRect, { left: -2, top: 0, right: 220, bottom: 40 })).toBe(true);
|
||||
|
||||
// Top side is covered
|
||||
expect(elementsOverlap(inputRect, { left: 0, top: 0, right: 220, bottom: 20 })).toBe(true);
|
||||
|
||||
// Bottom side is covered
|
||||
expect(elementsOverlap(inputRect, { left: -2, top: 25, right: 220, bottom: 40 })).toBe(true);
|
||||
|
||||
// Left side is covered
|
||||
expect(elementsOverlap(inputRect, { left: -2, top: 0, right: 100, bottom: 40 })).toBe(true);
|
||||
|
||||
// Right side is covered
|
||||
expect(elementsOverlap(inputRect, { left: 100, top: 0, right: 220, bottom: 40 })).toBe(true);
|
||||
|
||||
// Top-left corner is covered
|
||||
expect(elementsOverlap(inputRect, { left: -2, top: 0, right: 40, bottom: 10 })).toBe(true);
|
||||
|
||||
// Top-right corner is covered
|
||||
expect(elementsOverlap(inputRect, { left: 180, top: 0, right: 220, bottom: 10 })).toBe(true);
|
||||
|
||||
// Bottom-left corner is covered
|
||||
expect(elementsOverlap(inputRect, { left: -2, top: 10, right: 100, bottom: 40 })).toBe(true);
|
||||
|
||||
// Bottom-right corner is covered
|
||||
expect(elementsOverlap(inputRect, { left: 180, top: 10, right: 220, bottom: 40 })).toBe(true);
|
||||
|
||||
// Input field is covered with identical size
|
||||
expect(elementsOverlap(inputRect, { left: 0, top: 5, right: 200, bottom: 28 })).toBe(true);
|
||||
|
||||
// Overlay is inside the input field
|
||||
expect(elementsOverlap(inputRect, { left: 2, top: 10, right: 180, bottom: 26 })).toBe(true);
|
||||
|
||||
// Overlay is partially inside the input field, comes outside from the left
|
||||
expect(elementsOverlap(inputRect, { left: -2, top: 10, right: 180, bottom: 26 })).toBe(true);
|
||||
|
||||
// Overlay is outside the input field
|
||||
expect(elementsOverlap(inputRect, { left: 210, top: 0, right: 240, bottom: 40 })).toBe(false);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -26,10 +26,10 @@ async function testInputFields() {
|
|||
[ 'basic4', 3 ], // Username/passwd/TOTP fields
|
||||
[ 'div1', 2, '#toggle1' ], // Fields are behind a button that must be pressed
|
||||
[ 'div2', 2, '#toggle2' ], // Fields are behind a button that must be pressed behind a JavaScript
|
||||
//[ 'div3', 2, '#toggle3' ], // Fields are behind a button that must be pressed
|
||||
//[ 'div4', 2, '#toggle4' ], // Fields are behind a button that must be pressed
|
||||
[ 'div3', 2, '#toggle3' ], // Fields are behind a button that must be pressed
|
||||
[ 'div4', 2, '#toggle4' ], // Fields are behind a button that must be pressed
|
||||
[ 'hiddenFields1', 0 ], // Two hidden fields
|
||||
//[ 'hiddenFields2', 1 ], // Two hidden fields with one visible
|
||||
[ 'hiddenFields2', 1 ], // Two hidden fields with one visible
|
||||
];
|
||||
|
||||
for (const div of testDivs) {
|
||||
|
|
@ -60,10 +60,6 @@ async function testTotpFields() {
|
|||
[ '', { id: '2fa', type: 'text', maxLength: '6' }, 'Generic 2FA field', true ],
|
||||
[ '', { id: '2fa', type: 'text', maxLength: '4' }, 'Ignore if field maxLength too small', false ],
|
||||
[ '', { id: '2fa', type: 'text', maxLength: '12' }, 'Ignore if field maxLength too long', false ],
|
||||
[ '', { id: 'promocode', type: 'text', }, 'promocode id is not acceptable', false ],
|
||||
[ '', { id: 'Promotional Code', type: 'text', }, 'Promotional Code id is not acceptable', false ],
|
||||
[ '', { id: 'postcode', type: 'text', }, 'postcode id is not acceptable', false ],
|
||||
[ '', { id: 'postalcode', type: 'text', }, 'postalcode id is not acceptable', false ],
|
||||
[ '', { id: 'encode', type: 'text', }, 'encode id is not acceptable', false ],
|
||||
[ '', { id: 'encodedText', type: 'text', }, 'encodedText is not acceptable', false ],
|
||||
[ '', { id: 'decoder', type: 'text', }, 'decoder id is not acceptable', false ],
|
||||
|
|
|
|||
Loading…
Reference in a new issue